authInfo->userInfo; $currentUserId = $currentUser === null ? '0' : $currentUser->getId(); if($topicId < 1 && $postId > 0) { try { $postInfo = $msz->forumCtx->posts->getPost(postId: $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)) 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 ) ); $validModerationModes = [ 'delete', 'restore', 'nuke', 'bump', 'lock', 'unlock', ]; if(in_array($moderationMode, $validModerationModes, true)) { if(!CSRF::validateRequest()) Template::displayInfo("Couldn't verify this request, please refresh the page and try again.", 403); if(!$msz->authInfo->isLoggedIn) Template::displayInfo('You must be logged in to manage posts.', 401); if($msz->usersCtx->hasActiveBan($currentUser)) Template::displayInfo('You have been banned, check your profile for more information.', 403); switch($moderationMode) { case 'delete': if($canDeleteAny) { if($topicInfo->deleted) Template::displayInfo('This topic has already been marked as deleted.', 404); } else { if($topicInfo->deleted) Template::throwError(404); if(!$canDeleteOwn) Template::displayInfo("You aren't allowed to delete topics.", 403); if($topicInfo->userId !== $currentUser->getId()) Template::displayInfo('You can only delete your own topics.', 403); // topics may only be deleted within a day of creation, this should be a config value $deleteTimeFrame = 60 * 60 * 24; if($topicInfo->createdTime < time() - $deleteTimeFrame) Template::displayInfo('This topic has existed for too long. Ask a moderator to remove if it absolutely necessary.', 403); // deleted posts are intentionally included $topicPostCount = $msz->forumCtx->posts->countPosts(topicInfo: $topicInfo); if($topicPostCount > $deletePostThreshold) Template::displayInfo('This topic already has replies, you may no longer delete it. Ask a moderator to remove if it absolutely necessary.', 403); } if(!isset($_GET['confirm'])) { Template::render('forum.confirm', [ 'title' => 'Confirm topic deletion', 'class' => 'far fa-trash-alt', 'message' => sprintf('You are about to delete topic #%d. Are you sure about that?', $topicInfo->id), 'params' => [ 't' => $topicInfo->id, 'm' => 'delete', ], ]); break; } elseif(!$submissionConfirmed) { Tools::redirect($msz->urls->format( 'forum-topic', ['topic' => $topicInfo->id] )); break; } $msz->forumCtx->topics->deleteTopic($topicInfo->id); $msz->createAuditLog('FORUM_TOPIC_DELETE', [$topicInfo->id]); Tools::redirect($msz->urls->format('forum-category', [ 'forum' => $categoryInfo->id, ])); break; case 'restore': if(!$canNukeOrRestore) Template::throwError(403); if(!isset($_GET['confirm'])) { Template::render('forum.confirm', [ 'title' => 'Confirm topic restore', 'class' => 'fas fa-magic', 'message' => sprintf('You are about to restore topic #%d. Are you sure about that?', $topicInfo->id), 'params' => [ 't' => $topicInfo->id, 'm' => 'restore', ], ]); break; } elseif(!$submissionConfirmed) { Tools::redirect($msz->urls->format('forum-topic', [ 'topic' => $topicInfo->id, ])); break; } $msz->forumCtx->topics->restoreTopic($topicInfo->id); $msz->createAuditLog('FORUM_TOPIC_RESTORE', [$topicInfo->id]); Tools::redirect($msz->urls->format('forum-category', [ 'forum' => $categoryInfo->id, ])); break; case 'nuke': if(!$canNukeOrRestore) Template::throwError(403); if(!isset($_GET['confirm'])) { Template::render('forum.confirm', [ 'title' => 'Confirm topic nuke', 'class' => 'fas fa-radiation', 'message' => sprintf('You are about to PERMANENTLY DELETE topic #%d. Are you sure about that?', $topicInfo->id), 'params' => [ 't' => $topicInfo->id, 'm' => 'nuke', ], ]); break; } elseif(!$submissionConfirmed) { Tools::redirect($msz->urls->format('forum-topic', [ 'topic' => $topicInfo->id, ])); break; } $msz->forumCtx->topics->nukeTopic($topicInfo->id); $msz->createAuditLog('FORUM_TOPIC_NUKE', [$topicInfo->id]); Tools::redirect($msz->urls->format('forum-category', [ 'forum' => $categoryInfo->id, ])); break; case 'bump': if($canBumpTopic) { $msz->forumCtx->topics->bumpTopic($topicInfo->id); $msz->createAuditLog('FORUM_TOPIC_BUMP', [$topicInfo->id]); } Tools::redirect($msz->urls->format('forum-topic', [ 'topic' => $topicInfo->id, ])); break; case 'lock': if($canLockTopic && !$topicIsLocked) { $msz->forumCtx->topics->lockTopic($topicInfo->id); $msz->createAuditLog('FORUM_TOPIC_LOCK', [$topicInfo->id]); } Tools::redirect($msz->urls->format('forum-topic', [ 'topic' => $topicInfo->id, ])); break; case 'unlock': if($canLockTopic && $topicIsLocked) { $msz->forumCtx->topics->unlockTopic($topicInfo->id); $msz->createAuditLog('FORUM_TOPIC_UNLOCK', [$topicInfo->id]); } Tools::redirect($msz->urls->format('forum-topic', [ 'topic' => $topicInfo->id, ])); break; } return; } $topicPosts = $topicInfo->postsCount; if($canDeleteAny) $topicPosts += $topicInfo->deletedPostsCount; $topicPagination = new Pagination($topicPosts, 10, 'page'); if(isset($preceedingPostCount)) $topicPagination->setPage(floor($preceedingPostCount / $topicPagination->getRange()), true); if(!$topicPagination->hasValidOffset()) Template::throwError(404); $postInfos = $msz->forumCtx->posts->getPosts( topicInfo: $topicInfo, deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false, pagination: $topicPagination, ); 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' => $topicPagination, 'topic_can_delete' => $canDelete, 'topic_can_nuke_or_restore' => $canNukeOrRestore, 'topic_can_bump' => $canBumpTopic, 'topic_can_lock' => $canLockTopic, 'topic_user_id' => $currentUserId, 'topic_perms' => $perms, ]);