getForum(); $users = $msz->getUsers(); $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; $moderationMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : ''; $submissionConfirmed = !empty($_GET['confirm']) && is_string($_GET['confirm']) && $_GET['confirm'] === '1'; $currentUser = $msz->getActiveUser(); $currentUserId = $currentUser === null ? '0' : $currentUser->getId(); if($topicId < 1 && $postId > 0) { try { $postInfo = $forum->getPost(postId: $postId); } catch(RuntimeException $ex) { echo render_error(404); return; } $categoryId = $postInfo->getCategoryId(); $perms = forum_perms_get_user($categoryId, $currentUserId)[MSZ_FORUM_PERMS_GENERAL]; $canDeleteAny = !perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST); if($postInfo->isDeleted() && !$canDeleteAny) { echo render_error(404); return; } $topicId = $postInfo->getTopicId(); $preceedingPostCount = $forum->countPosts( topicInfo: $topicId, upToPostInfo: $postInfo, deleted: $canDeleteAny ? null : false ); } try { $topicIsNuked = $topicIsDeleted = $canDeleteAny = false; $topicInfo = $forum->getTopic(topicId: $topicId); } catch(RuntimeException $ex) { $topicIsNuked = true; } if(!$topicIsNuked) { $topicIsDeleted = $topicInfo->isDeleted(); if($categoryId !== (int)$topicInfo->getCategoryId()) { $categoryId = (int)$topicInfo->getCategoryId(); $perms = forum_perms_get_user($categoryId, $currentUserId)[MSZ_FORUM_PERMS_GENERAL]; } if(isset($currentUser) && $msz->hasActiveBan($currentUser)) $perms &= MSZ_FORUM_PERM_LIST_FORUM | MSZ_FORUM_PERM_VIEW_FORUM; $canDeleteAny = perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST); } if(($topicIsNuked || $topicIsDeleted) && $forum->hasTopicRedirect($topicId)) { $topicRedirectInfo = $forum->getTopicRedirect($topicId); Template::set('topic_redir_info', $topicRedirectInfo); if($topicIsNuked || !$canDeleteAny) { if(empty($topicRedirectInfo)) echo render_error(404); else header('Location: ' . $topicRedirectInfo->getLinkTarget()); return; } } if(!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) { echo render_error(403); return; } // Maximum amount of posts a topic may contain to still be deletable by the author // this should be in the config $deletePostThreshold = 1; $categoryInfo = $forum->getCategory(topicInfo: $topicInfo); $topicIsLocked = $topicInfo->isLocked(); $topicIsArchived = $categoryInfo->isArchived(); $topicPostsTotal = $topicInfo->getTotalPostsCount(); $topicIsFrozen = $topicIsArchived || $topicIsDeleted; $canDeleteOwn = !$topicIsFrozen && !$topicIsLocked && perms_check($perms, MSZ_FORUM_PERM_DELETE_POST); $canBumpTopic = !$topicIsFrozen && perms_check($perms, MSZ_FORUM_PERM_BUMP_TOPIC); $canLockTopic = !$topicIsFrozen && perms_check($perms, MSZ_FORUM_PERM_LOCK_TOPIC); $canNukeOrRestore = $canDeleteAny && $topicIsDeleted; $canDelete = !$topicIsDeleted && ( $canDeleteAny || ( $topicPostsTotal > 0 && $topicPostsTotal <= $deletePostThreshold && $canDeleteOwn && $topicInfo->getUserId() === (string)$currentUserId ) ); $validModerationModes = [ 'delete', 'restore', 'nuke', 'bump', 'lock', 'unlock', ]; if(in_array($moderationMode, $validModerationModes, true)) { if(!CSRF::validateRequest()) { echo render_info("Couldn't verify this request, please refresh the page and try again.", 403); return; } if(!$msz->isLoggedIn()) { echo render_info('You must be logged in to manage posts.', 401); return; } if($msz->hasActiveBan()) { echo render_info('You have been banned, check your profile for more information.', 403); return; } switch($moderationMode) { case 'delete': if($canDeleteAny) { if($topicInfo->isDeleted()) { echo render_info('This topic has already been marked as deleted.', 404); return; } } else { if($topicInfo->isDeleted()) { echo render_error(404); return; } if(!$canDeleteOwn) { echo render_info("You aren't allowed to delete topics.", 403); return; } if($topicInfo->getUserId() !== $currentUser->getId()) { echo render_info('You can only delete your own topics.', 403); return; } // topics may only be deleted within a day of creation, this should be a config value $deleteTimeFrame = 60 * 60 * 24; if($topicInfo->getCreatedTime() < time() - $deleteTimeFrame) { echo render_info('This topic has existed for too long. Ask a moderator to remove if it absolutely necessary.', 403); return; } // deleted posts are intentionally included $topicPostCount = $forum->countPosts(topicInfo: $topicInfo); if($topicPostCount > $deletePostThreshold) { echo render_info('This topic already has replies, you may no longer delete it. Ask a moderator to remove if it absolutely necessary.', 403); return; } } 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->getId()), 'params' => [ 't' => $topicInfo->getId(), 'm' => 'delete', ], ]); break; } elseif(!$submissionConfirmed) { url_redirect( 'forum-topic', ['topic' => $topicInfo->getId()] ); break; } $forum->deleteTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_DELETE', [$topicInfo->getId()]); url_redirect('forum-category', [ 'forum' => $categoryInfo->getId(), ]); break; case 'restore': if(!$canNukeOrRestore) { echo render_error(403); break; } 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->getId()), 'params' => [ 't' => $topicInfo->getId(), 'm' => 'restore', ], ]); break; } elseif(!$submissionConfirmed) { url_redirect('forum-topic', [ 'topic' => $topicInfo->getId(), ]); break; } $forum->restoreTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_RESTORE', [$topicInfo->getId()]); url_redirect('forum-category', [ 'forum' => $categoryInfo->getId(), ]); break; case 'nuke': if(!$canNukeOrRestore) { echo render_error(403); break; } 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->getId()), 'params' => [ 't' => $topicInfo->getId(), 'm' => 'nuke', ], ]); break; } elseif(!$submissionConfirmed) { url_redirect('forum-topic', [ 'topic' => $topicInfo->getId(), ]); break; } $forum->nukeTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_NUKE', [$topicInfo->getId()]); url_redirect('forum-category', [ 'forum' => $categoryInfo->getId(), ]); break; case 'bump': if($canBumpTopic) { $forum->bumpTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_BUMP', [$topicInfo->getId()]); } url_redirect('forum-topic', [ 'topic' => $topicInfo->getId(), ]); break; case 'lock': if($canLockTopic && !$topicIsLocked) { $forum->lockTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_LOCK', [$topicInfo->getId()]); } url_redirect('forum-topic', [ 'topic' => $topicInfo->getId(), ]); break; case 'unlock': if($canLockTopic && $topicIsLocked) { $forum->unlockTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_UNLOCK', [$topicInfo->getId()]); } url_redirect('forum-topic', [ 'topic' => $topicInfo->getId(), ]); break; } return; } $topicPosts = $topicInfo->getPostsCount(); if($canDeleteAny) $topicPosts += $topicInfo->getDeletedPostsCount(); $topicPagination = new Pagination($topicPosts, 10, 'page'); if(isset($preceedingPostCount)) $topicPagination->setPage(floor($preceedingPostCount / $topicPagination->getRange()), true); if(!$topicPagination->hasValidOffset()) { echo render_error(404); return; } $postInfos = $forum->getPosts( topicInfo: $topicInfo, deleted: perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST) ? null : false, pagination: $topicPagination, ); if(empty($postInfos)) { echo render_error(404); return; } $originalPostInfo = $forum->getPost(topicInfo: $topicInfo); $userInfos = []; $userColours = []; $userPostsCounts = []; $posts = []; foreach($postInfos as $postInfo) { $posts[] = $post = new stdClass; $post->info = $postInfo; if($postInfo->hasUserId()) { $postUserId = $postInfo->getUserId(); if(!array_key_exists($postUserId, $userInfos)) { $userInfo = $users->getUser($postUserId, 'id'); $userInfos[$postUserId] = $userInfo; $userColours[$postUserId] = $users->getUserColour($userInfo); $userPostsCounts[$postUserId] = $forum->countPosts(userInfo: $userInfo, deleted: false); } $post->user = $userInfos[$postUserId]; $post->colour = $userColours[$postUserId]; $post->postsCount = $userPostsCounts[$postUserId]; } $post->isOriginalPost = $originalPostInfo->getId() == $postInfo->getId(); $post->isOriginalPoster = $originalPostInfo->hasUserId() && $postInfo->hasUserId() && $originalPostInfo->getUserId() === $postInfo->getUserId(); } $canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && perms_check($perms, MSZ_FORUM_PERM_CREATE_POST); if(!$forum->checkUserHasReadTopic($userInfo, $topicInfo)) $forum->incrementTopicView($topicInfo); $forum->updateUserReadTopic($currentUser, $topicInfo); $perms = perms_check_bulk($perms, [ 'can_create_post' => MSZ_FORUM_PERM_CREATE_POST, 'can_edit_post' => MSZ_FORUM_PERM_EDIT_POST, 'can_edit_any_post' => MSZ_FORUM_PERM_EDIT_ANY_POST, 'can_delete_post' => MSZ_FORUM_PERM_DELETE_POST, 'can_delete_any_post' => MSZ_FORUM_PERM_DELETE_ANY_POST, ]); Template::render('forum.topic', [ 'topic_breadcrumbs' => $forum->getCategoryAncestry($topicInfo), 'global_accent_colour' => $forum->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, ]);