292 lines
10 KiB
PHP
292 lines
10 KiB
PHP
<?php
|
|
namespace Misuzu\Forum;
|
|
|
|
use RuntimeException;
|
|
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
|
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
|
use Index\Http\Routing\Processors\Before;
|
|
use Index\Http\Routing\Routes\PatternRoute;
|
|
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
|
use Misuzu\Perm;
|
|
use Misuzu\Auth\AuthInfo;
|
|
use Misuzu\Logs\LogsContext;
|
|
|
|
class ForumPostsRoutes implements RouteHandler, UrlSource {
|
|
use RouteHandlerCommon, UrlSourceCommon;
|
|
|
|
public function __construct(
|
|
private UrlRegistry $urls,
|
|
private ForumContext $forumCtx,
|
|
private LogsContext $logsCtx,
|
|
private AuthInfo $authInfo,
|
|
) {}
|
|
|
|
#[PatternRoute('GET', '/forum/posts/([0-9]+)')]
|
|
#[Before('authz:cookie')]
|
|
#[UrlFormat('forum-post', '/forum/posts/<post>')]
|
|
public function getPost(HttpResponseBuilder $response, HttpRequest $request, string $postId): mixed {
|
|
try {
|
|
$post = $this->forumCtx->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->forumCtx->posts->countPosts(
|
|
topicInfo: $post->topicId,
|
|
upToPostInfo: $post,
|
|
deleted: $canDeleteAny ? null : false
|
|
);
|
|
$pageNumber = ((int)floor($postsCount / 10)) + 1; // epic magic number
|
|
|
|
$response->redirect($this->urls->format('forum-topic', [
|
|
'topic' => $post->topicId,
|
|
'page' => $pageNumber,
|
|
'topic_fragment' => sprintf('p%s', $post->id),
|
|
]));
|
|
|
|
return 302;
|
|
}
|
|
|
|
#[PatternRoute('DELETE', '/forum/posts/([0-9]+)')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
#[UrlFormat('forum-post-delete', '/forum/posts/<post>')]
|
|
public function deletePost(HttpResponseBuilder $response, HttpRequest $request, string $postId): mixed {
|
|
try {
|
|
$post = $this->forumCtx->posts->getPost(
|
|
postId: $postId,
|
|
deleted: false,
|
|
);
|
|
} catch(RuntimeException $ex) {
|
|
$response->statusCode = 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->statusCode = 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->forumCtx->topics->getTopic(postInfo: $post);
|
|
if($topic->deleted) {
|
|
$response->statusCode = 404;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:post:none',
|
|
'text' => "Couldn't find that forum post.",
|
|
],
|
|
];
|
|
}
|
|
|
|
if($topic->locked) {
|
|
$response->statusCode = 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->statusCode = 403;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:post:delete:access',
|
|
'text' => "You aren't allowed to delete that post.",
|
|
],
|
|
];
|
|
}
|
|
|
|
if($post->userId !== $this->authInfo->userId) {
|
|
$response->statusCode = 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->statusCode = 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->forumCtx->posts->getPost(topicInfo: $post->topicId);
|
|
if($originalPost->id === $post->id) {
|
|
$response->statusCode = 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->forumCtx->categories->getCategory(postInfo: $post);
|
|
if($category->archived) {
|
|
$response->statusCode = 400;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:topic:archived',
|
|
'text' => "The forum category this topic belongs to is archived.",
|
|
],
|
|
];
|
|
}
|
|
|
|
$this->forumCtx->posts->deletePost($post);
|
|
$this->logsCtx->createAuthedLog('FORUM_POST_DELETE', [$post->id]);
|
|
|
|
return 204;
|
|
}
|
|
|
|
#[PatternRoute('POST', '/forum/posts/([0-9]+)/nuke')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
#[UrlFormat('forum-post-nuke', '/forum/posts/<post>/nuke')]
|
|
public function postPostNuke(HttpResponseBuilder $response, HttpRequest $request, string $postId): mixed {
|
|
try {
|
|
$post = $this->forumCtx->posts->getPost(
|
|
postId: $postId,
|
|
deleted: true,
|
|
);
|
|
} catch(RuntimeException $ex) {
|
|
$response->statusCode = 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->statusCode = 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->statusCode = 403;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:post:nuke:access',
|
|
'text' => "You aren't allowed to nuke that post.",
|
|
],
|
|
];
|
|
}
|
|
|
|
$category = $this->forumCtx->categories->getCategory(postInfo: $post);
|
|
if($category->archived) {
|
|
$response->statusCode = 400;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:post:archived',
|
|
'text' => "The forum category this post belongs to is archived.",
|
|
],
|
|
];
|
|
}
|
|
|
|
$this->forumCtx->posts->nukePost($post);
|
|
$this->logsCtx->createAuthedLog('FORUM_POST_NUKE', [$post->id]);
|
|
|
|
return 204;
|
|
}
|
|
|
|
#[PatternRoute('POST', '/forum/posts/([0-9]+)/restore')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
#[UrlFormat('forum-post-restore', '/forum/posts/<post>/restore')]
|
|
public function postPostRestore(HttpResponseBuilder $response, HttpRequest $request, string $postId): mixed {
|
|
try {
|
|
$post = $this->forumCtx->posts->getPost(
|
|
postId: $postId,
|
|
deleted: true,
|
|
);
|
|
} catch(RuntimeException $ex) {
|
|
$response->statusCode = 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->statusCode = 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->statusCode = 403;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:post:restore:access',
|
|
'text' => "You aren't allowed to restore that post.",
|
|
],
|
|
];
|
|
}
|
|
|
|
$category = $this->forumCtx->categories->getCategory(postInfo: $post);
|
|
if($category->archived) {
|
|
$response->statusCode = 400;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:post:archived',
|
|
'text' => "The forum category this post belongs to is archived.",
|
|
],
|
|
];
|
|
}
|
|
|
|
$this->forumCtx->posts->restorePost($post);
|
|
$this->logsCtx->createAuthedLog('FORUM_POST_RESTORE', [$post->id]);
|
|
|
|
return 204;
|
|
}
|
|
}
|