misuzu/src/Forum/ForumPostsRoutes.php

350 lines
12 KiB
PHP
Raw Normal View History

<?php
namespace Misuzu\Forum;
use RuntimeException;
use Index\Http\{HttpRequest,HttpResponseBuilder};
use Index\Http\Routing\{HttpDelete,HttpGet,HttpPost,RouteHandler,RouteHandlerTrait};
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceTrait};
use Misuzu\{CSRF,Perm};
use Misuzu\AuditLog\AuditLog;
use Misuzu\Auth\AuthInfo;
use Misuzu\Users\UsersContext;
class ForumPostsRoutes implements RouteHandler, UrlSource {
use RouteHandlerTrait, UrlSourceTrait;
public function __construct(
private UrlRegistry $urls,
private ForumContext $forum,
private UsersContext $usersCtx,
private AuditLog $auditLog,
private AuthInfo $authInfo,
) {}
#[HttpGet('/forum/posts/([0-9]+)')]
2024-12-20 00:29:14 +00:00
#[UrlFormat('forum-post', '/forum/posts/<post>')]
public function getPost(HttpResponseBuilder $response, HttpRequest $request, string $postId) {
try {
$post = $this->forum->posts->getPost(postId: $postId);
} catch(RuntimeException $ex) {
return 404;
}
$perms = $this->authInfo->getPerms('forum', $post->categoryId);
if(!$perms->check(Perm::F_CATEGORY_VIEW))
return 403;
$canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY);
if($post->deleted && !$canDeleteAny)
return 404;
$postsCount = $this->forum->posts->countPosts(
topicInfo: $post->topicId,
upToPostInfo: $post,
deleted: $canDeleteAny ? null : false
);
2024-12-19 01:22:26 +00:00
$pageNumber = ((int)floor($postsCount / 10)) + 1; // epic magic number
return $response->redirect($this->urls->format('forum-topic', [
'topic' => $post->topicId,
'page' => $pageNumber,
2024-12-19 01:22:26 +00:00
'topic_fragment' => sprintf('p%s', $post->id),
]));
}
#[HttpDelete('/forum/posts/([0-9]+)')]
#[UrlFormat('forum-post-delete', '/forum/posts/<post>')]
public function deletePost(HttpResponseBuilder $response, HttpRequest $request, string $postId) {
if(!$this->authInfo->isLoggedIn)
return 401;
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
return 403;
$response->setHeader('X-CSRF-Token', CSRF::token());
if($this->usersCtx->hasActiveBan($this->authInfo->authInfo)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'user:banned',
'text' => "You aren't allowed to do that while banned.",
],
];
}
try {
$post = $this->forum->posts->getPost(
postId: $postId,
deleted: false,
);
} catch(RuntimeException $ex) {
$response->setStatusCode(404);
return [
'error' => [
'name' => 'forum:post:none',
'text' => "Couldn't find that forum post.",
],
];
}
$perms = $this->authInfo->getPerms('forum', $post->categoryId);
if(!$perms->check(Perm::F_CATEGORY_VIEW)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:access',
'text' => "You aren't allowed to access that post.",
],
];
}
if(!$perms->check(Perm::F_POST_DELETE_ANY)) {
$topic = $this->forum->topics->getTopic(postInfo: $post);
if($topic->deleted) {
$response->setStatusCode(404);
return [
'error' => [
'name' => 'forum:post:none',
'text' => "Couldn't find that forum post.",
],
];
}
if($topic->locked) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:delete:lock',
'text' => "The forum topic that post belongs is locked.",
],
];
}
if(!$perms->check(Perm::F_POST_DELETE_OWN)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:delete:access',
'text' => "You aren't allowed to delete that post.",
],
];
}
if($post->userId !== $this->authInfo->userId) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:delete:own',
'text' => "You aren't allowed to delete posts made by other people.",
],
];
}
// posts may only be deleted within a week of creation, this should be a config value
$deleteTimeFrame = 60 * 60 * 24 * 7;
if($post->createdTime < time() - $deleteTimeFrame) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:delete:age',
'text' => "This post has existed for too long. Ask a moderator to remove if it absolutely necessary.",
],
];
}
}
$originalPost = $this->forum->posts->getPost(topicInfo: $post->topicId);
if($originalPost->id === $post->id) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:delete:opening',
'text' => "This is the opening post of the topic it belongs to, it may not be deleted without deleting the entire topic as well.",
],
];
}
$category = $this->forum->categories->getCategory(postInfo: $post);
if($category->archived) {
$response->setStatusCode(400);
return [
'error' => [
'name' => 'forum:topic:archived',
'text' => "The forum category this topic belongs to is archived.",
],
];
}
$this->forum->posts->deletePost($post);
$this->auditLog->createLog(
$this->authInfo->userInfo,
'FORUM_POST_DELETE',
[$post->id],
$request->getRemoteAddress(),
$request->getCountryCode()
);
return 204;
}
#[HttpPost('/forum/posts/([0-9]+)/nuke')]
#[UrlFormat('forum-post-nuke', '/forum/posts/<post>/nuke')]
public function postPostNuke(HttpResponseBuilder $response, HttpRequest $request, string $postId) {
if(!$this->authInfo->isLoggedIn)
return 401;
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
return 403;
$response->setHeader('X-CSRF-Token', CSRF::token());
if($this->usersCtx->hasActiveBan($this->authInfo->authInfo)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'user:banned',
'text' => "You aren't allowed to do that while banned.",
],
];
}
try {
$post = $this->forum->posts->getPost(
postId: $postId,
deleted: true,
);
} catch(RuntimeException $ex) {
$response->setStatusCode(404);
return [
'error' => [
'name' => 'forum:post:none',
'text' => "Couldn't find that forum post.",
],
];
}
$perms = $this->authInfo->getPerms('forum', $post->categoryId);
if(!$perms->check(Perm::F_CATEGORY_VIEW)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:access',
'text' => "You aren't allowed to access that post.",
],
];
}
if(!$perms->check(Perm::F_POST_DELETE_ANY)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:nuke:access',
'text' => "You aren't allowed to nuke that post.",
],
];
}
$category = $this->forum->categories->getCategory(postInfo: $post);
if($category->archived) {
$response->setStatusCode(400);
return [
'error' => [
'name' => 'forum:post:archived',
'text' => "The forum category this post belongs to is archived.",
],
];
}
$this->forum->posts->nukePost($post);
$this->auditLog->createLog(
$this->authInfo->userInfo,
'FORUM_POST_NUKE',
[$post->id],
$request->getRemoteAddress(),
$request->getCountryCode()
);
return 204;
}
#[HttpPost('/forum/posts/([0-9]+)/restore')]
#[UrlFormat('forum-post-restore', '/forum/posts/<post>/restore')]
public function postPostRestore(HttpResponseBuilder $response, HttpRequest $request, string $postId) {
if(!$this->authInfo->isLoggedIn)
return 401;
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
return 403;
$response->setHeader('X-CSRF-Token', CSRF::token());
if($this->usersCtx->hasActiveBan($this->authInfo->authInfo)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'user:banned',
'text' => "You aren't allowed to do that while banned.",
],
];
}
try {
$post = $this->forum->posts->getPost(
postId: $postId,
deleted: true,
);
} catch(RuntimeException $ex) {
$response->setStatusCode(404);
return [
'error' => [
'name' => 'forum:post:none',
'text' => "Couldn't find that forum post.",
],
];
}
$perms = $this->authInfo->getPerms('forum', $post->categoryId);
if(!$perms->check(Perm::F_CATEGORY_VIEW)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:access',
'text' => "You aren't allowed to access that post.",
],
];
}
if(!$perms->check(Perm::F_POST_DELETE_ANY)) {
$response->setStatusCode(403);
return [
'error' => [
'name' => 'forum:post:restore:access',
'text' => "You aren't allowed to restore that post.",
],
];
}
$category = $this->forum->categories->getCategory(postInfo: $post);
if($category->archived) {
$response->setStatusCode(400);
return [
'error' => [
'name' => 'forum:post:archived',
'text' => "The forum category this post belongs to is archived.",
],
];
}
$this->forum->posts->restorePost($post);
$this->auditLog->createLog(
$this->authInfo->userInfo,
'FORUM_POST_RESTORE',
[$post->id],
$request->getRemoteAddress(),
$request->getCountryCode()
);
return 204;
}
}