688 lines
26 KiB
PHP
688 lines
26 KiB
PHP
<?php
|
|
namespace Misuzu\Comments;
|
|
|
|
use RuntimeException;
|
|
use Index\XArray;
|
|
use Index\Http\{FormHttpContent,HttpRequest,HttpResponseBuilder};
|
|
use Index\Http\Content\FormContent;
|
|
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
|
use Index\Http\Routing\Processors\Before;
|
|
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
|
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
|
use Misuzu\Perm;
|
|
use Misuzu\Auth\AuthInfo;
|
|
use Misuzu\Perms\{PermissionResult,IPermissionResult};
|
|
use Misuzu\Users\{UserInfo,UsersContext,UsersData};
|
|
|
|
class CommentsRoutes implements RouteHandler, UrlSource {
|
|
use RouteHandlerCommon, UrlSourceCommon;
|
|
|
|
public function __construct(
|
|
private CommentsContext $commentsCtx,
|
|
private UsersContext $usersCtx,
|
|
private UrlRegistry $urls,
|
|
private AuthInfo $authInfo,
|
|
) {}
|
|
|
|
private function getGlobalPerms(): IPermissionResult {
|
|
return $this->authInfo->loggedIn && !$this->usersCtx->hasActiveBan($this->authInfo->userInfo)
|
|
? $this->authInfo->getPerms('global')
|
|
: new PermissionResult(0);
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* id: string,
|
|
* name: string,
|
|
* profile: string,
|
|
* avatar: string,
|
|
* colour?: string,
|
|
* }
|
|
*/
|
|
private function convertUser(UserInfo $userInfo, int $avatarRes = 80): array {
|
|
$user = [
|
|
'id' => $userInfo->id,
|
|
'name' => $userInfo->name,
|
|
'profile' => $this->urls->format('user-profile', ['user' => $userInfo->id]),
|
|
'avatar' => $this->urls->format('user-avatar', ['user' => $userInfo->id, 'res' => $avatarRes]),
|
|
];
|
|
|
|
$userColour = $this->usersCtx->getUserColour($userInfo);
|
|
if(!$userColour->inherits)
|
|
$user['colour'] = (string)$userColour;
|
|
|
|
return $user;
|
|
}
|
|
|
|
/**
|
|
* @param iterable<CommentsPostInfo> $postInfos
|
|
* @return mixed[]
|
|
*/
|
|
private function convertPosts(
|
|
IPermissionResult $perms,
|
|
CommentsCategoryInfo $catInfo,
|
|
iterable $postInfos,
|
|
bool $loadReplies = false
|
|
): array {
|
|
$posts = [];
|
|
|
|
foreach($postInfos as $postInfo) {
|
|
$post = $this->convertPost(
|
|
$perms,
|
|
$catInfo,
|
|
$postInfo,
|
|
$loadReplies ? $this->commentsCtx->posts->getPosts(parentInfo: $postInfo) : null
|
|
);
|
|
if(isset($post['deleted']) && $post['deleted'] === true && empty($post['replies']))
|
|
continue;
|
|
|
|
$posts[] = $post;
|
|
}
|
|
|
|
return $posts;
|
|
}
|
|
|
|
/**
|
|
* @param ?iterable<CommentsPostInfo> $replyInfos
|
|
* @return array{
|
|
* id: string,
|
|
* body?: string,
|
|
* created: string,
|
|
* pinned?: string,
|
|
* edited?: string,
|
|
* deleted?: string|true,
|
|
* user?: array{
|
|
* id: string,
|
|
* name: string,
|
|
* profile: string,
|
|
* avatar: string,
|
|
* colour?: string,
|
|
* },
|
|
* positive?: int,
|
|
* negative?: int,
|
|
* vote?: int,
|
|
* can_edit?: true,
|
|
* can_delete?: true,
|
|
* can_delete_any?: true,
|
|
* replies?: int|mixed[],
|
|
* }
|
|
*/
|
|
private function convertPost(
|
|
IPermissionResult $perms,
|
|
CommentsCategoryInfo $catInfo,
|
|
CommentsPostInfo $postInfo,
|
|
?iterable $replyInfos = null
|
|
): array {
|
|
$canViewDeleted = $perms->check(Perm::G_COMMENTS_DELETE_ANY);
|
|
$isDeleted = $postInfo->deleted && !$canViewDeleted;
|
|
|
|
$post = [
|
|
'id' => $postInfo->id,
|
|
'created' => $postInfo->createdAt->toIso8601ZuluString(),
|
|
];
|
|
if(!$isDeleted) {
|
|
$post['body'] = $postInfo->body;
|
|
if($postInfo->pinned)
|
|
$post['pinned'] = $postInfo->pinnedAt->toIso8601ZuluString();
|
|
if($postInfo->edited)
|
|
$post['edited'] = $postInfo->editedAt->toIso8601ZuluString();
|
|
|
|
if($postInfo->userId !== null)
|
|
try {
|
|
$post['user'] = $this->convertUser(
|
|
$this->usersCtx->getUserInfo($postInfo->userId)
|
|
);
|
|
} catch(RuntimeException $ex) {}
|
|
|
|
$votes = $this->commentsCtx->votes->getVotesAggregate($postInfo);
|
|
$post['positive'] = $votes->positive;
|
|
$post['negative'] = $votes->negative;
|
|
|
|
if($this->authInfo->loggedIn) {
|
|
$voteInfo = $this->commentsCtx->votes->getVote($postInfo, $this->authInfo->userInfo);
|
|
if($voteInfo->weight !== 0)
|
|
$post['vote'] = $voteInfo->weight;
|
|
|
|
$isAuthor = $this->authInfo->userId === $postInfo->userId;
|
|
if($isAuthor && $perms->check(Perm::G_COMMENTS_EDIT_OWN))
|
|
$post['can_edit'] = true;
|
|
if(($isAuthor || $catInfo->ownerId === $this->authInfo->userId) && $perms->check(Perm::G_COMMENTS_DELETE_OWN))
|
|
$post['can_delete'] = true;
|
|
}
|
|
}
|
|
if($postInfo->deleted)
|
|
$post['deleted'] = $canViewDeleted ? $postInfo->deletedAt->toIso8601ZuluString() : true;
|
|
|
|
if($this->authInfo->loggedIn) {
|
|
if($perms->check(Perm::G_COMMENTS_EDIT_ANY))
|
|
$post['can_edit'] = true;
|
|
if($perms->check(Perm::G_COMMENTS_DELETE_ANY))
|
|
$post['can_delete'] = $post['can_delete_any'] = true;
|
|
}
|
|
|
|
if($replyInfos === null) {
|
|
$replies = $this->commentsCtx->posts->countPosts(parentInfo: $postInfo);
|
|
if($replies > 0)
|
|
$post['replies'] = $replies;
|
|
} else {
|
|
$replies = $this->convertPosts($perms, $catInfo, $replyInfos);
|
|
if(!empty($replies))
|
|
$post['replies'] = $replies;
|
|
}
|
|
|
|
return $post;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $extra
|
|
* @return array{error: array{name: string, text: string}}
|
|
*/
|
|
private static function error(HttpResponseBuilder $response, int $code, string $name, string $text, array $extra = []): array {
|
|
$response->statusCode = $code;
|
|
|
|
return [
|
|
'error' => array_merge($extra, [
|
|
'name' => $name,
|
|
'text' => $text,
|
|
]),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* category: array{
|
|
* name: string,
|
|
* created: string,
|
|
* locked?: string,
|
|
* owner?: array{
|
|
* id: string,
|
|
* name: string,
|
|
* profile: string,
|
|
* avatar: string,
|
|
* colour?: string,
|
|
* },
|
|
* },
|
|
* user?: array{
|
|
* id: string,
|
|
* name: string,
|
|
* profile: string,
|
|
* avatar: string,
|
|
* colour?: string,
|
|
* can_create?: true,
|
|
* can_pin?: true,
|
|
* can_vote?: true,
|
|
* can_lock?: true,
|
|
* },
|
|
* posts: mixed[]
|
|
* }|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('GET', '/comments/categories/([A-Za-z0-9-]+)')]
|
|
#[Before('authz:cookie')]
|
|
public function getCategory(HttpResponseBuilder $response, string $categoryName): array {
|
|
try {
|
|
$catInfo = $this->commentsCtx->categories->getCategory(name: $categoryName);
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:category-not-found', 'No comment section with that name exists.');
|
|
}
|
|
|
|
$perms = $this->getGlobalPerms();
|
|
$result = [];
|
|
$category = [
|
|
'name' => $catInfo->name,
|
|
'created' => $catInfo->createdAt->toIso8601ZuluString(),
|
|
];
|
|
|
|
if($catInfo->locked)
|
|
$category['locked'] = $catInfo->lockedAt->toIso8601ZuluString();
|
|
|
|
if($catInfo->ownerId !== null)
|
|
try {
|
|
$category['owner'] = $this->convertUser(
|
|
$this->usersCtx->getUserInfo($catInfo->ownerId)
|
|
);
|
|
} catch(RuntimeException $ex) {}
|
|
|
|
$result['category'] = $category;
|
|
|
|
if($this->authInfo->loggedIn) {
|
|
$user = $this->convertUser($this->authInfo->userInfo, 100);
|
|
|
|
if($perms->check(Perm::G_COMMENTS_CREATE))
|
|
$user['can_create'] = true;
|
|
if($perms->check(Perm::G_COMMENTS_PIN) || $catInfo->ownerId === $this->authInfo->userId)
|
|
$user['can_pin'] = true;
|
|
if($perms->check(Perm::G_COMMENTS_VOTE))
|
|
$user['can_vote'] = true;
|
|
if($perms->check(Perm::G_COMMENTS_LOCK))
|
|
$user['can_lock'] = true;
|
|
|
|
$result['user'] = $user;
|
|
}
|
|
|
|
try {
|
|
$posts = $this->convertPosts($perms, $catInfo, $this->commentsCtx->posts->getPosts(
|
|
categoryInfo: $catInfo,
|
|
replies: false,
|
|
), true);
|
|
} catch(RuntimeException $ex) {
|
|
$posts = [];
|
|
}
|
|
|
|
$result['posts'] = $posts;
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* name: string,
|
|
* locked?: string|false,
|
|
* }|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('POST', '/comments/categories/([A-Za-z0-9-]+)')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
#[Before('input:urlencoded')]
|
|
public function patchCategory(HttpResponseBuilder $response, FormContent $content, string $categoryName): array {
|
|
try {
|
|
$catInfo = $this->commentsCtx->categories->getCategory(name: $categoryName);
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:category-not-found', 'No comment section with that name exists.');
|
|
}
|
|
|
|
$perms = $this->getGlobalPerms();
|
|
$locked = null;
|
|
|
|
if($content->hasParam('lock')) {
|
|
if(!$perms->check(Perm::G_COMMENTS_LOCK))
|
|
return self::error($response, 403, 'comments:lock-not-allowed', 'You are not allowed to lock this comment section.');
|
|
|
|
$locked = !empty($content->getParam('lock'));
|
|
}
|
|
|
|
$this->commentsCtx->categories->updateCategory(
|
|
$catInfo,
|
|
locked: $locked,
|
|
);
|
|
|
|
try {
|
|
$catInfo = $this->commentsCtx->categories->getCategory(categoryId: $catInfo->id);
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:category-not-found', 'No comment section with that name exists.');
|
|
}
|
|
|
|
$result = ['name' => $catInfo->name];
|
|
if($locked !== null)
|
|
$result['locked'] = $catInfo->locked ? $catInfo->lockedAt->toIso8601ZuluString() : false;
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return mixed[]|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[ExactRoute('POST', '/comments/posts')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
#[Before('input:multipart')]
|
|
public function postPost(HttpResponseBuilder $response, FormContent $content): array {
|
|
$perms = $this->getGlobalPerms();
|
|
if(!$perms->check(Perm::G_COMMENTS_CREATE))
|
|
return self::error($response, 403, 'comments:create-not-allowed', 'You are not allowed to post comments.');
|
|
|
|
if(!$content->hasParam('category') || !$content->hasParam('body'))
|
|
return self::error($response, 400, 'comments:missing-fields', 'Required fields are not specified.');
|
|
|
|
$pinned = false;
|
|
$body = preg_replace("/[\r\n]{2,}/", "\n", (string)$content->getParam('body'));
|
|
if(mb_strlen(mb_trim($body)) < 1)
|
|
return self::error($response, 400, 'comments:body-too-short', 'Your comment must be longer.');
|
|
if(mb_strlen($body) > 5000)
|
|
return self::error($response, 400, 'comments:body-too-long', 'Your comment is too long.');
|
|
|
|
try {
|
|
$catInfo = $this->commentsCtx->categories->getCategory(name: (string)$content->getParam('category'));
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:category-not-found', 'No comment section with that name exists.');
|
|
}
|
|
|
|
if($content->hasParam('reply_to')) {
|
|
try {
|
|
$replyToInfo = $this->commentsCtx->posts->getPost((string)$content->getParam('reply_to'));
|
|
if($replyToInfo->deleted)
|
|
return self::error($response, 404, 'comments:parent-not-found', 'The comment you are trying to reply to does not exist.');
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:parent-not-found', 'The comment you are trying to reply to does not exist.');
|
|
}
|
|
} else
|
|
$replyToInfo = null;
|
|
|
|
if($content->hasParam('pin')) {
|
|
if(!$perms->check(Perm::G_COMMENTS_PIN) && $catInfo->ownerId !== $this->authInfo->userId)
|
|
return self::error($response, 403, 'comments:pin-not-allowed', 'You are not allowed to pin comments.');
|
|
if($replyToInfo !== null)
|
|
return self::error($response, 400, 'comments:post-not-root', 'Replies cannot be pinned.');
|
|
|
|
$pinned = !empty($content->getParam('pin'));
|
|
}
|
|
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->createPost(
|
|
$catInfo,
|
|
$replyToInfo,
|
|
$this->authInfo->userInfo,
|
|
$body,
|
|
$pinned
|
|
);
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 500, 'comments:create-failed', 'Failed to create your comment. Please report this as a bug if it persists.');
|
|
}
|
|
|
|
$response->statusCode = 201;
|
|
return $this->convertPost($perms, $catInfo, $postInfo);
|
|
}
|
|
|
|
/**
|
|
* @return mixed[]|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('GET', '/comments/posts/([0-9]+)')]
|
|
#[Before('authz:cookie')]
|
|
public function getPost(HttpResponseBuilder $response, string $commentId): array {
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
|
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
$perms = $this->getGlobalPerms();
|
|
$post = $this->convertPost(
|
|
$perms,
|
|
$catInfo,
|
|
$postInfo,
|
|
$this->commentsCtx->posts->getPosts(parentInfo: $postInfo)
|
|
);
|
|
if(isset($post['deleted']) && $post['deleted'] === true && empty($post['replies']))
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
|
|
return $post;
|
|
}
|
|
|
|
/**
|
|
* @return mixed[]|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('GET', '/comments/posts/([0-9]+)/replies')]
|
|
#[Before('authz:cookie')]
|
|
public function getPostReplies(HttpResponseBuilder $response, string $commentId): array {
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
|
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
return $this->convertPosts(
|
|
$this->getGlobalPerms(),
|
|
$catInfo,
|
|
$this->commentsCtx->posts->getPosts(parentInfo: $postInfo)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* id: string,
|
|
* body?: string,
|
|
* pinned?: string|false,
|
|
* edited?: string,
|
|
* }|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('PATCH', '/comments/posts/([0-9]+)')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
#[Before('input:multipart')]
|
|
public function patchPost(HttpResponseBuilder $response, FormContent $content, string $commentId): array {
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
|
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
$perms = $this->getGlobalPerms();
|
|
if(!$perms->check(Perm::G_COMMENTS_DELETE_ANY) && ($catInfo->locked || $postInfo->deleted))
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
|
|
$body = null;
|
|
$pinned = null;
|
|
$edited = false;
|
|
|
|
if($content->hasParam('pin')) {
|
|
if(!$perms->check(Perm::G_COMMENTS_PIN) && $catInfo->ownerId !== $this->authInfo->userId)
|
|
return self::error($response, 403, 'comments:pin-not-allowed', 'You are not allowed to pin comments.');
|
|
if($postInfo->reply)
|
|
return self::error($response, 400, 'comments:post-not-root', 'Replies cannot be pinned.');
|
|
|
|
$pinned = !empty($content->getParam('pin'));
|
|
}
|
|
|
|
if($content->hasParam('body')) {
|
|
if(!$perms->check(Perm::G_COMMENTS_EDIT_ANY) && !($perms->check(Perm::G_COMMENTS_EDIT_OWN) && $this->authInfo->userId === $postInfo->userId))
|
|
return self::error($response, 403, 'comments:edit-not-allowed', 'You are not allowed to edit comments.');
|
|
|
|
$body = preg_replace("/[\r\n]{2,}/", "\n", (string)$content->getParam('body'));
|
|
if(mb_strlen(mb_trim($body)) < 1)
|
|
return self::error($response, 400, 'comments:body-too-short', 'Your comment must be longer.');
|
|
if(mb_strlen($body) > 5000)
|
|
return self::error($response, 400, 'comments:body-too-long', 'Your comment is too long.');
|
|
|
|
$edited = $body !== $postInfo->body;
|
|
if(!$edited)
|
|
$body = null;
|
|
}
|
|
|
|
$this->commentsCtx->posts->updatePost(
|
|
$postInfo,
|
|
body: $body,
|
|
pinned: $pinned,
|
|
edited: $edited,
|
|
);
|
|
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($postInfo->id);
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
$result = ['id' => $postInfo->id];
|
|
if($body !== null)
|
|
$result['body'] = $postInfo->body;
|
|
if($pinned !== null)
|
|
$result['pinned'] = $postInfo->pinned ? $postInfo->pinnedAt->toIso8601ZuluString() : false;
|
|
if($edited)
|
|
$result['edited'] = $postInfo->editedAt->toIso8601ZuluString();
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return string|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('DELETE', '/comments/posts/([0-9]+)')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
public function deletePost(HttpResponseBuilder $response, string $commentId): array|string {
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
|
if($postInfo->deleted)
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
|
|
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
|
if($catInfo->locked)
|
|
return self::error($response, 403, 'comments:category-locked-delete', 'The comment section this comment is in is locked, it cannot be deleted.');
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
$perms = $this->getGlobalPerms();
|
|
if(!$perms->check(Perm::G_COMMENTS_DELETE_ANY) && !(
|
|
($postInfo->userId === $this->authInfo->userId || $catInfo->ownerId === $this->authInfo->userId)
|
|
&& $perms->check(Perm::G_COMMENTS_DELETE_OWN)
|
|
)) return self::error($response, 403, 'comments:delete-not-allowed', 'You are not allowed to delete this comment.');
|
|
|
|
$this->commentsCtx->posts->deletePost($postInfo);
|
|
|
|
$response->statusCode = 204;
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @return mixed[]|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('POST', '/comments/posts/([0-9]+)/restore')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
public function postPostRestore(HttpResponseBuilder $response, string $commentId): array {
|
|
if(!$this->getGlobalPerms()->check(Perm::G_COMMENTS_DELETE_ANY))
|
|
return self::error($response, 403, 'comments:restore-not-allowed', 'You are not allowed to restore comments.');
|
|
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
|
if(!$postInfo->deleted)
|
|
return self::error($response, 400, 'comments:post-not-deleted', 'This comment is not currently deleted.');
|
|
|
|
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
|
if($catInfo->locked)
|
|
return self::error($response, 403, 'comments:category-locked-restore', 'The comment section this comment is in is locked, it cannot be restored.');
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
$this->commentsCtx->posts->restorePost($postInfo);
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* @return mixed[]|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('POST' ,'/comments/posts/([0-9]+)/nuke')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
public function postPostNuke(HttpResponseBuilder $response, string $commentId): array {
|
|
if(!$this->getGlobalPerms()->check(Perm::G_COMMENTS_DELETE_ANY))
|
|
return self::error($response, 403, 'comments:nuke-not-allowed', 'You are not allowed to permanently delete comments.');
|
|
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
|
if(!$postInfo->deleted)
|
|
return self::error($response, 400, 'comments:post-not-deleted', 'This comment is not currently (soft-)deleted.');
|
|
|
|
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
|
if($catInfo->locked)
|
|
return self::error($response, 403, 'comments:category-locked-nuke', 'The comment section this comment is in is locked, it cannot be permanently deleted.');
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
$this->commentsCtx->posts->nukePost($postInfo);
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* vote: int,
|
|
* positive: int,
|
|
* negative: int,
|
|
* }|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('POST', '/comments/posts/([0-9]+)/vote')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
#[Before('input:urlencoded')]
|
|
public function postPostVote(HttpResponseBuilder $response, FormContent $content, string $commentId): array {
|
|
$vote = (int)$content->getFilteredParam('vote', FILTER_SANITIZE_NUMBER_INT);
|
|
if($vote === 0)
|
|
return self::error($response, 400, 'comments:vote', 'Could not process vote.');
|
|
|
|
if(!$this->getGlobalPerms()->check(Perm::G_COMMENTS_VOTE))
|
|
return self::error($response, 403, 'comments:vote-not-allowed', 'You are not allowed to vote on comments.');
|
|
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
|
if($postInfo->deleted)
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
|
|
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
|
if($catInfo->locked)
|
|
return self::error($response, 403, 'comments:category-locked-vote', 'The comment section this comment is in is locked, you cannot vote on it.');
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
$this->commentsCtx->votes->addVote(
|
|
$postInfo,
|
|
$this->authInfo->userInfo,
|
|
max(-1, min(1, $vote))
|
|
);
|
|
|
|
$voteInfo = $this->commentsCtx->votes->getVote($postInfo, $this->authInfo->userInfo);
|
|
$votes = $this->commentsCtx->votes->getVotesAggregate($postInfo);
|
|
|
|
$response->statusCode = 201;
|
|
return [
|
|
'vote' => $voteInfo->weight,
|
|
'positive' => $votes->positive,
|
|
'negative' => $votes->negative,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* vote: int,
|
|
* positive: int,
|
|
* negative: int,
|
|
* }|array{error: array{name: string, text: string}}
|
|
*/
|
|
#[PatternRoute('DELETE', '/comments/posts/([0-9]+)/vote')]
|
|
#[Before('authz:cookie', type: 'json', required: true)]
|
|
#[Before('csrf:header', type: 'json')]
|
|
#[Before('authz:banned', type: 'json')]
|
|
public function deletePostVote(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array {
|
|
if(!$this->getGlobalPerms()->check(Perm::G_COMMENTS_VOTE))
|
|
return self::error($response, 403, 'comments:vote-not-allowed', 'You are not allowed to vote on comments.');
|
|
|
|
try {
|
|
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
|
if($postInfo->deleted)
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
|
|
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
|
if($catInfo->locked)
|
|
return self::error($response, 403, 'comments:category-locked-vote', 'The comment section this comment is in is locked, you cannot vote on it.');
|
|
} catch(RuntimeException $ex) {
|
|
return self::error($response, 404, 'comments:post-not-found', 'Comment not found.');
|
|
}
|
|
|
|
$this->commentsCtx->votes->removeVote(
|
|
$postInfo,
|
|
$this->authInfo->userInfo
|
|
);
|
|
|
|
$voteInfo = $this->commentsCtx->votes->getVote($postInfo, $this->authInfo->userInfo);
|
|
$votes = $this->commentsCtx->votes->getVotesAggregate($postInfo);
|
|
|
|
return [
|
|
'vote' => $voteInfo->weight,
|
|
'positive' => $votes->positive,
|
|
'negative' => $votes->negative,
|
|
];
|
|
}
|
|
}
|