Moved topic listing into the router.

This commit is contained in:
Pachira 2024-12-20 00:29:14 +00:00
parent 504154ac38
commit 8569d2b65d
5 changed files with 141 additions and 183 deletions

View file

@ -1,174 +0,0 @@
<?php
namespace Misuzu;
use stdClass;
use RuntimeException;
if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
die('Script must be called through the Misuzu route dispatcher.');
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
$topicId = !empty($_GET['t']) && is_string($_GET['t']) ? (int)$_GET['t'] : 0;
$categoryId = null;
$currentUser = $msz->authInfo->userInfo;
$currentUserId = $currentUser === null ? '0' : $currentUser->id;
if($topicId < 1 && $postId > 0) {
try {
$postInfo = $msz->forumCtx->posts->getPost(postId: (string)$postId);
} catch(RuntimeException $ex) {
Template::throwError(404);
}
$categoryId = (int)$postInfo->categoryId;
$perms = $msz->authInfo->getPerms('forum', $postInfo->categoryId);
$canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY);
if($postInfo->deleted && !$canDeleteAny)
Template::throwError(404);
$topicId = $postInfo->topicId;
$preceedingPostCount = $msz->forumCtx->posts->countPosts(
topicInfo: $topicId,
upToPostInfo: $postInfo,
deleted: $canDeleteAny ? null : false
);
}
try {
$topicIsNuked = $topicIsDeleted = $canDeleteAny = false;
$topicInfo = $msz->forumCtx->topics->getTopic(topicId: $topicId);
} catch(RuntimeException $ex) {
$topicIsNuked = true;
}
if(!$topicIsNuked) {
$topicIsDeleted = $topicInfo->deleted;
if($categoryId !== (int)$topicInfo->categoryId) {
$categoryId = (int)$topicInfo->categoryId;
$perms = $msz->authInfo->getPerms('forum', $topicInfo->categoryId);
}
if($msz->usersCtx->hasActiveBan($currentUser))
$perms = $perms->apply(fn($calc) => $calc & (Perm::F_CATEGORY_LIST | Perm::F_CATEGORY_VIEW));
$canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY);
}
if($topicIsNuked || $topicIsDeleted) {
if($msz->forumCtx->topicRedirects->hasTopicRedirect($topicId)) {
$topicRedirectInfo = $msz->forumCtx->topicRedirects->getTopicRedirect($topicId);
Template::set('topic_redir_info', $topicRedirectInfo);
if($topicIsNuked || !$canDeleteAny) {
header('Location: ' . $topicRedirectInfo->linkTarget);
return;
}
}
if(empty($topicRedirectInfo) && !$canDeleteAny)
Template::throwError(404);
}
if(!$perms->check(Perm::F_CATEGORY_VIEW))
Template::throwError(403);
// Maximum amount of posts a topic may contain to still be deletable by the author
// this should be in the config
$deletePostThreshold = 1;
$categoryInfo = $msz->forumCtx->categories->getCategory(topicInfo: $topicInfo);
$topicIsLocked = $topicInfo->locked;
$topicIsArchived = $categoryInfo->archived;
$topicPostsTotal = $topicInfo->totalPostsCount;
$topicIsFrozen = $topicIsArchived || $topicIsDeleted;
$canDeleteOwn = !$topicIsFrozen && !$topicIsLocked && $perms->check(Perm::F_POST_DELETE_OWN);
$canBumpTopic = !$topicIsFrozen && $perms->check(Perm::F_TOPIC_BUMP);
$canLockTopic = !$topicIsFrozen && $perms->check(Perm::F_TOPIC_LOCK);
$canNukeOrRestore = $canDeleteAny && $topicIsDeleted;
$canDelete = !$topicIsDeleted && (
$canDeleteAny || (
$topicPostsTotal > 0
&& $topicPostsTotal <= $deletePostThreshold
&& $canDeleteOwn
&& $topicInfo->userId === (string)$currentUserId
)
);
$topicPosts = $topicInfo->postsCount;
if($canDeleteAny)
$topicPosts += $topicInfo->deletedPostsCount;
if(isset($preceedingPostCount))
$pagination = Pagination::fromPage($topicPosts, (int)floor($preceedingPostCount / 10), 10, 0);
else
$pagination = Pagination::fromInput($topicPosts, 10, 'page');
if(!$pagination->validOffset)
Template::throwError(404);
$postInfos = $msz->forumCtx->posts->getPosts(
topicInfo: $topicInfo,
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false,
pagination: $pagination,
);
if(empty($postInfos))
Template::throwError(404);
try {
$originalPostInfo = $msz->forumCtx->posts->getPost(topicInfo: $topicInfo);
} catch(RuntimeException $ex) {
Template::throwError(404);
}
$posts = [];
foreach($postInfos as $postInfo) {
$posts[] = $post = new stdClass;
$post->info = $postInfo;
if($postInfo->userId !== null) {
$post->user = $msz->usersCtx->getUserInfo($postInfo->userId);
$post->colour = $msz->usersCtx->getUserColour($post->user);
$post->postsCount = $msz->forumCtx->countTotalUserPosts($post->user);
}
$post->isOriginalPost = $originalPostInfo->id == $postInfo->id;
$post->isOriginalPoster = $originalPostInfo->userId !== null && $postInfo->userId !== null
&& $originalPostInfo->userId === $postInfo->userId;
}
$canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && $perms->check(Perm::F_POST_CREATE);
if(!$msz->forumCtx->topics->checkUserHasReadTopic($currentUser, $topicInfo))
$msz->forumCtx->topics->incrementTopicViews($topicInfo);
$msz->forumCtx->topics->updateUserReadTopic($currentUser, $topicInfo);
$perms = $perms->checkMany([
'can_create_post' => Perm::F_POST_CREATE,
'can_edit_post' => Perm::F_POST_EDIT_OWN,
'can_edit_any_post' => Perm::F_POST_EDIT_ANY,
'can_delete_post' => Perm::F_POST_DELETE_OWN,
'can_delete_any_post' => Perm::F_POST_DELETE_ANY,
]);
Template::render('forum.topic', [
'topic_breadcrumbs' => iterator_to_array($msz->forumCtx->categories->getCategoryAncestry($topicInfo)),
'global_accent_colour' => $msz->forumCtx->categories->getCategoryColour($topicInfo),
'topic_info' => $topicInfo,
'category_info' => $categoryInfo,
'topic_posts' => $posts,
'can_reply' => $canReply,
'topic_pagination' => $pagination,
'topic_can_delete' => $canDelete,
'topic_can_delete_any' => $canDeleteAny,
'topic_can_nuke_or_restore' => $canNukeOrRestore,
'topic_can_bump' => $canBumpTopic,
'topic_can_lock' => $canLockTopic,
'topic_user_id' => $currentUserId,
'topic_perms' => $perms,
]);

View file

@ -314,10 +314,6 @@ class ForumCategoriesRoutes implements RouteHandler, UrlSource {
} }
} }
$perms = $perms->checkMany([
'can_create_topic' => Perm::F_TOPIC_CREATE,
]);
return Template::renderRaw('forum.forum', [ return Template::renderRaw('forum.forum', [
'forum_breadcrumbs' => iterator_to_array($this->forum->categories->getCategoryAncestry($category)), 'forum_breadcrumbs' => iterator_to_array($this->forum->categories->getCategoryAncestry($category)),
'global_accent_colour' => $this->forum->categories->getCategoryColour($category), 'global_accent_colour' => $this->forum->categories->getCategoryColour($category),
@ -326,7 +322,9 @@ class ForumCategoriesRoutes implements RouteHandler, UrlSource {
'forum_topics' => $topics, 'forum_topics' => $topics,
'forum_pagination' => $pagination, 'forum_pagination' => $pagination,
'forum_show_mark_as_read' => $this->authInfo->isLoggedIn, 'forum_show_mark_as_read' => $this->authInfo->isLoggedIn,
'forum_perms' => $perms, 'forum_perms' => $perms->checkMany([
'can_create_topic' => Perm::F_TOPIC_CREATE,
]),
]); ]);
} }

View file

@ -22,6 +22,7 @@ class ForumPostsRoutes implements RouteHandler, UrlSource {
) {} ) {}
#[HttpGet('/forum/posts/([0-9]+)')] #[HttpGet('/forum/posts/([0-9]+)')]
#[UrlFormat('forum-post', '/forum/posts/<post>')]
public function getPost(HttpResponseBuilder $response, HttpRequest $request, string $postId) { public function getPost(HttpResponseBuilder $response, HttpRequest $request, string $postId) {
try { try {
$post = $this->forum->posts->getPost(postId: $postId); $post = $this->forum->posts->getPost(postId: $postId);

View file

@ -1,11 +1,12 @@
<?php <?php
namespace Misuzu\Forum; namespace Misuzu\Forum;
use stdClass;
use RuntimeException; use RuntimeException;
use Index\Http\{HttpRequest,HttpResponseBuilder}; use Index\Http\{HttpRequest,HttpResponseBuilder};
use Index\Http\Routing\{HttpDelete,HttpGet,HttpPost,RouteHandler,RouteHandlerTrait}; use Index\Http\Routing\{HttpDelete,HttpGet,HttpPost,RouteHandler,RouteHandlerTrait};
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceTrait}; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceTrait};
use Misuzu\{CSRF,Perm}; use Misuzu\{CSRF,Pagination,Perm,Template};
use Misuzu\AuditLog\AuditLog; use Misuzu\AuditLog\AuditLog;
use Misuzu\Auth\AuthInfo; use Misuzu\Auth\AuthInfo;
use Misuzu\Users\UsersContext; use Misuzu\Users\UsersContext;
@ -22,8 +23,129 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
) {} ) {}
#[HttpGet('/forum/topics/([0-9]+)')] #[HttpGet('/forum/topics/([0-9]+)')]
#[UrlFormat('forum-topic', '/forum/topics/<topic>', ['page' => '<page>'], '<topic_fragment>')]
public function getTopic(HttpResponseBuilder $response, HttpRequest $request, string $topicId) { public function getTopic(HttpResponseBuilder $response, HttpRequest $request, string $topicId) {
$response->redirect($this->urls->format('forum-topic', ['topic' => $topicId])); try {
$isNuked = $deleted = $canDeleteAny = false;
$topic = $this->forum->topics->getTopic(topicId: $topicId);
} catch(RuntimeException $ex) {
$isNuked = true;
}
if(!$isNuked) {
$deleted = $topic->deleted;
$perms = $this->authInfo->getPerms('forum', $topic->categoryId);
if($this->usersCtx->hasActiveBan($this->authInfo->userInfo))
$perms = $perms->apply(fn($calc) => $calc & (Perm::F_CATEGORY_LIST | Perm::F_CATEGORY_VIEW));
$canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY);
}
if($isNuked || $deleted) {
if($this->forum->topicRedirects->hasTopicRedirect($topicId)) {
$redirect = $this->forum->topicRedirects->getTopicRedirect($topicId);
Template::set('topic_redir_info', $redirect);
if($isNuked || !$canDeleteAny) {
$response->redirect($redirect->linkTarget);
return;
}
}
if(empty($redirect) && !$canDeleteAny)
return 404;
}
if(!$perms->check(Perm::F_CATEGORY_VIEW))
return 403;
$postsCount = $topic->postsCount;
if($canDeleteAny)
$postsCount += $topic->deletedPostsCount;
$pagination = Pagination::fromRequest($request, $postsCount, 10);
if(!$pagination->validOffset)
return 404;
// Maximum amount of posts a topic may contain to still be deletable by the author
// this should be in the config
$deletePostThreshold = 1;
$categoryInfo = $this->forum->categories->getCategory(topicInfo: $topic);
$isFrozen = $categoryInfo->archived || $deleted;
$canDeleteOwn = !$isFrozen && !$topic->locked && $perms->check(Perm::F_POST_DELETE_OWN);
$canBump = !$isFrozen && $perms->check(Perm::F_TOPIC_BUMP);
$canLock = !$isFrozen && $perms->check(Perm::F_TOPIC_LOCK);
$canNukeOrRestore = $canDeleteAny && $deleted;
$canReply = !$isFrozen && !$topic->locked && $perms->check(Perm::F_POST_CREATE);
$canDelete = !$deleted && (
$canDeleteAny || (
$topic->totalPostsCount > 0
&& $topic->totalPostsCount <= $deletePostThreshold
&& $canDeleteOwn
&& $topic->userId === $this->authInfo->userId
)
);
$postInfos = $this->forum->posts->getPosts(
topicInfo: $topic,
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false,
pagination: $pagination,
);
if(empty($postInfos))
return 404;
try {
$originalPostInfo = $this->forum->posts->getPost(topicInfo: $topic);
} catch(RuntimeException $ex) {
return 404;
}
$posts = [];
foreach($postInfos as $postInfo) {
$posts[] = $post = new stdClass;
$post->info = $postInfo;
if($postInfo->userId !== null) {
$post->user = $this->usersCtx->getUserInfo($postInfo->userId);
$post->colour = $this->usersCtx->getUserColour($post->user);
$post->postsCount = $this->forum->countTotalUserPosts($post->user);
}
$post->isOriginalPost = $originalPostInfo->id == $postInfo->id;
$post->isOriginalPoster = $originalPostInfo->userId !== null && $postInfo->userId !== null
&& $originalPostInfo->userId === $postInfo->userId;
}
if(!$this->forum->topics->checkUserHasReadTopic($this->authInfo->userInfo, $topic))
$this->forum->topics->incrementTopicViews($topic);
$this->forum->topics->updateUserReadTopic($this->authInfo->userInfo, $topic);
return Template::renderRaw('forum.topic', [
'topic_breadcrumbs' => iterator_to_array($this->forum->categories->getCategoryAncestry($topic)),
'global_accent_colour' => $this->forum->categories->getCategoryColour($topic),
'topic_info' => $topic,
'category_info' => $categoryInfo,
'topic_posts' => $posts,
'can_reply' => $canReply,
'topic_pagination' => $pagination,
'topic_can_delete' => $canDelete,
'topic_can_delete_any' => $canDeleteAny,
'topic_can_nuke_or_restore' => $canNukeOrRestore,
'topic_can_bump' => $canBump,
'topic_can_lock' => $canLock,
'topic_user_id' => $this->authInfo->userId,
'topic_perms' => $perms->checkMany([
'can_create_post' => Perm::F_POST_CREATE,
'can_edit_post' => Perm::F_POST_EDIT_OWN,
'can_edit_any_post' => Perm::F_POST_EDIT_ANY,
'can_delete_post' => Perm::F_POST_DELETE_OWN,
'can_delete_any_post' => Perm::F_POST_DELETE_ANY,
]),
]);
} }
#[HttpDelete('/forum/topics/([0-9]+)')] #[HttpDelete('/forum/topics/([0-9]+)')]

View file

@ -32,9 +32,7 @@ class LegacyRoutes implements RouteHandler, UrlSource {
$urls->register('forum-leaderboard', '/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']); $urls->register('forum-leaderboard', '/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']);
$urls->register('forum-topic-new', '/forum/posting.php', ['f' => '<forum>']); $urls->register('forum-topic-new', '/forum/posting.php', ['f' => '<forum>']);
$urls->register('forum-reply-new', '/forum/posting.php', ['t' => '<topic>']); $urls->register('forum-reply-new', '/forum/posting.php', ['t' => '<topic>']);
$urls->register('forum-topic', '/forum/topic.php', ['t' => '<topic>', 'page' => '<page>'], '<topic_fragment>');
$urls->register('forum-topic-create', '/forum/posting.php', ['f' => '<forum>']); $urls->register('forum-topic-create', '/forum/posting.php', ['f' => '<forum>']);
$urls->register('forum-post', '/forum/topic.php', ['p' => '<post>'], 'p<post>');
$urls->register('forum-post-create', '/forum/posting.php', ['t' => '<topic>']); $urls->register('forum-post-create', '/forum/posting.php', ['t' => '<topic>']);
$urls->register('forum-post-quote', '/forum/posting.php', ['q' => '<post>']); $urls->register('forum-post-quote', '/forum/posting.php', ['q' => '<post>']);
$urls->register('forum-post-edit', '/forum/posting.php', ['p' => '<post>', 'm' => 'edit']); $urls->register('forum-post-edit', '/forum/posting.php', ['p' => '<post>', 'm' => 'edit']);
@ -195,6 +193,19 @@ class LegacyRoutes implements RouteHandler, UrlSource {
]), true); ]), true);
} }
#[HttpGet('/forum/topic.php')]
public function getForumTopicPHP(HttpResponseBuilder $response, HttpRequest $request): void {
if($request->hasParam('p'))
$response->redirect($this->urls->format('forum-post', [
'post' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT),
]), true);
else
$response->redirect($this->urls->format('forum-topic', [
'topic' => $request->getParam('t', FILTER_SANITIZE_NUMBER_INT),
'page' => $request->getParam('page', FILTER_SANITIZE_NUMBER_INT),
]), true);
}
#[HttpGet('/forum/post.php')] #[HttpGet('/forum/post.php')]
public function getForumPostPHP(HttpResponseBuilder $response, HttpRequest $request): void { public function getForumPostPHP(HttpResponseBuilder $response, HttpRequest $request): void {
$response->redirect($this->urls->format('forum-post', [ $response->redirect($this->urls->format('forum-post', [