2022-09-13 13:14:49 +00:00
|
|
|
<?php
|
|
|
|
namespace Misuzu;
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
use stdClass;
|
|
|
|
use RuntimeException;
|
|
|
|
|
|
|
|
$forum = $msz->getForum();
|
|
|
|
$users = $msz->getUsers();
|
|
|
|
|
2022-09-13 13:14:49 +00:00
|
|
|
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
|
|
|
|
$topicId = !empty($_GET['t']) && is_string($_GET['t']) ? (int)$_GET['t'] : 0;
|
2023-08-28 01:17:34 +00:00
|
|
|
$categoryId = null;
|
2022-09-13 13:14:49 +00:00
|
|
|
$moderationMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
|
|
|
|
$submissionConfirmed = !empty($_GET['confirm']) && is_string($_GET['confirm']) && $_GET['confirm'] === '1';
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
$currentUser = $msz->getActiveUser();
|
|
|
|
$currentUserId = $currentUser === null ? '0' : $currentUser->getId();
|
2022-09-13 13:14:49 +00:00
|
|
|
|
|
|
|
if($topicId < 1 && $postId > 0) {
|
2023-08-28 01:17:34 +00:00
|
|
|
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);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
if($postInfo->isDeleted() && !$canDeleteAny) {
|
|
|
|
echo render_error(404);
|
|
|
|
return;
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
2023-08-28 01:17:34 +00:00
|
|
|
|
|
|
|
$topicId = $postInfo->getTopicId();
|
|
|
|
$preceedingPostCount = $forum->countPosts(
|
|
|
|
topicInfo: $topicId,
|
|
|
|
upToPostInfo: $postInfo,
|
|
|
|
deleted: $canDeleteAny ? null : false
|
|
|
|
);
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
try {
|
|
|
|
$topicIsNuked = $topicIsDeleted = $canDeleteAny = false;
|
|
|
|
$topicInfo = $forum->getTopic(topicId: $topicId);
|
|
|
|
} catch(RuntimeException $ex) {
|
|
|
|
$topicIsNuked = true;
|
|
|
|
}
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
if(!$topicIsNuked) {
|
|
|
|
$topicIsDeleted = $topicInfo->isDeleted();
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
if($categoryId !== (int)$topicInfo->getCategoryId()) {
|
|
|
|
$categoryId = (int)$topicInfo->getCategoryId();
|
|
|
|
$perms = forum_perms_get_user($categoryId, $currentUserId)[MSZ_FORUM_PERMS_GENERAL];
|
|
|
|
}
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
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);
|
2023-04-30 00:18:14 +00:00
|
|
|
Template::set('topic_redir_info', $topicRedirectInfo);
|
|
|
|
|
|
|
|
if($topicIsNuked || !$canDeleteAny) {
|
|
|
|
if(empty($topicRedirectInfo))
|
|
|
|
echo render_error(404);
|
|
|
|
else
|
2023-08-28 01:17:34 +00:00
|
|
|
header('Location: ' . $topicRedirectInfo->getLinkTarget());
|
2023-04-30 00:18:14 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) {
|
|
|
|
echo render_error(403);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
// 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();
|
2022-09-13 13:14:49 +00:00
|
|
|
$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
|
2023-08-28 01:17:34 +00:00
|
|
|
&& $topicPostsTotal <= $deletePostThreshold
|
2022-09-13 13:14:49 +00:00
|
|
|
&& $canDeleteOwn
|
2023-08-28 01:17:34 +00:00
|
|
|
&& $topicInfo->getUserId() === (string)$currentUserId
|
2022-09-13 13:14:49 +00:00
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
$validModerationModes = [
|
|
|
|
'delete', 'restore', 'nuke',
|
|
|
|
'bump', 'lock', 'unlock',
|
|
|
|
];
|
|
|
|
|
|
|
|
if(in_array($moderationMode, $validModerationModes, true)) {
|
|
|
|
if(!CSRF::validateRequest()) {
|
2023-01-02 23:12:23 +00:00
|
|
|
echo render_info("Couldn't verify this request, please refresh the page and try again.", 403);
|
2022-09-13 13:14:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-02 22:12:47 +00:00
|
|
|
if(!$msz->isLoggedIn()) {
|
2023-01-02 23:12:23 +00:00
|
|
|
echo render_info('You must be logged in to manage posts.', 401);
|
2022-09-13 13:14:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-07-26 18:19:46 +00:00
|
|
|
if($msz->hasActiveBan()) {
|
2023-01-02 23:12:23 +00:00
|
|
|
echo render_info('You have been banned, check your profile for more information.', 403);
|
2022-09-13 13:14:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch($moderationMode) {
|
|
|
|
case 'delete':
|
2023-08-28 01:17:34 +00:00
|
|
|
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;
|
|
|
|
}
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
2023-01-02 23:12:23 +00:00
|
|
|
if(!isset($_GET['confirm'])) {
|
|
|
|
Template::render('forum.confirm', [
|
|
|
|
'title' => 'Confirm topic deletion',
|
|
|
|
'class' => 'far fa-trash-alt',
|
2023-08-28 01:17:34 +00:00
|
|
|
'message' => sprintf('You are about to delete topic #%d. Are you sure about that?', $topicInfo->getId()),
|
2023-01-02 23:12:23 +00:00
|
|
|
'params' => [
|
2023-08-28 01:17:34 +00:00
|
|
|
't' => $topicInfo->getId(),
|
2023-01-02 23:12:23 +00:00
|
|
|
'm' => 'delete',
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
break;
|
|
|
|
} elseif(!$submissionConfirmed) {
|
|
|
|
url_redirect(
|
|
|
|
'forum-topic',
|
2023-08-28 01:17:34 +00:00
|
|
|
['topic' => $topicInfo->getId()]
|
2023-01-02 23:12:23 +00:00
|
|
|
);
|
|
|
|
break;
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
$forum->deleteTopic($topicInfo->getId());
|
|
|
|
$msz->createAuditLog('FORUM_TOPIC_DELETE', [$topicInfo->getId()]);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
|
|
|
url_redirect('forum-category', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'forum' => $categoryInfo->getId(),
|
2022-09-13 13:14:49 +00:00
|
|
|
]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'restore':
|
|
|
|
if(!$canNukeOrRestore) {
|
|
|
|
echo render_error(403);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-01-02 23:12:23 +00:00
|
|
|
if(!isset($_GET['confirm'])) {
|
|
|
|
Template::render('forum.confirm', [
|
|
|
|
'title' => 'Confirm topic restore',
|
|
|
|
'class' => 'fas fa-magic',
|
2023-08-28 01:17:34 +00:00
|
|
|
'message' => sprintf('You are about to restore topic #%d. Are you sure about that?', $topicInfo->getId()),
|
2023-01-02 23:12:23 +00:00
|
|
|
'params' => [
|
2023-08-28 01:17:34 +00:00
|
|
|
't' => $topicInfo->getId(),
|
2023-01-02 23:12:23 +00:00
|
|
|
'm' => 'restore',
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
break;
|
|
|
|
} elseif(!$submissionConfirmed) {
|
|
|
|
url_redirect('forum-topic', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'topic' => $topicInfo->getId(),
|
2023-01-02 23:12:23 +00:00
|
|
|
]);
|
|
|
|
break;
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
$forum->restoreTopic($topicInfo->getId());
|
|
|
|
$msz->createAuditLog('FORUM_TOPIC_RESTORE', [$topicInfo->getId()]);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-01-02 23:12:23 +00:00
|
|
|
url_redirect('forum-category', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'forum' => $categoryInfo->getId(),
|
2023-01-02 23:12:23 +00:00
|
|
|
]);
|
2022-09-13 13:14:49 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'nuke':
|
|
|
|
if(!$canNukeOrRestore) {
|
|
|
|
echo render_error(403);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-01-02 23:12:23 +00:00
|
|
|
if(!isset($_GET['confirm'])) {
|
|
|
|
Template::render('forum.confirm', [
|
|
|
|
'title' => 'Confirm topic nuke',
|
|
|
|
'class' => 'fas fa-radiation',
|
2023-08-28 01:17:34 +00:00
|
|
|
'message' => sprintf('You are about to PERMANENTLY DELETE topic #%d. Are you sure about that?', $topicInfo->getId()),
|
2023-01-02 23:12:23 +00:00
|
|
|
'params' => [
|
2023-08-28 01:17:34 +00:00
|
|
|
't' => $topicInfo->getId(),
|
2023-01-02 23:12:23 +00:00
|
|
|
'm' => 'nuke',
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
break;
|
|
|
|
} elseif(!$submissionConfirmed) {
|
|
|
|
url_redirect('forum-topic', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'topic' => $topicInfo->getId(),
|
2023-01-02 23:12:23 +00:00
|
|
|
]);
|
|
|
|
break;
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
$forum->nukeTopic($topicInfo->getId());
|
|
|
|
$msz->createAuditLog('FORUM_TOPIC_NUKE', [$topicInfo->getId()]);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-01-02 23:12:23 +00:00
|
|
|
url_redirect('forum-category', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'forum' => $categoryInfo->getId(),
|
2023-01-02 23:12:23 +00:00
|
|
|
]);
|
2022-09-13 13:14:49 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'bump':
|
2023-08-28 01:17:34 +00:00
|
|
|
if($canBumpTopic) {
|
|
|
|
$forum->bumpTopic($topicInfo->getId());
|
|
|
|
$msz->createAuditLog('FORUM_TOPIC_BUMP', [$topicInfo->getId()]);
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
url_redirect('forum-topic', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'topic' => $topicInfo->getId(),
|
2022-09-13 13:14:49 +00:00
|
|
|
]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'lock':
|
2023-08-28 01:17:34 +00:00
|
|
|
if($canLockTopic && !$topicIsLocked) {
|
|
|
|
$forum->lockTopic($topicInfo->getId());
|
|
|
|
$msz->createAuditLog('FORUM_TOPIC_LOCK', [$topicInfo->getId()]);
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
url_redirect('forum-topic', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'topic' => $topicInfo->getId(),
|
2022-09-13 13:14:49 +00:00
|
|
|
]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'unlock':
|
2023-08-28 01:17:34 +00:00
|
|
|
if($canLockTopic && $topicIsLocked) {
|
|
|
|
$forum->unlockTopic($topicInfo->getId());
|
|
|
|
$msz->createAuditLog('FORUM_TOPIC_UNLOCK', [$topicInfo->getId()]);
|
2022-09-13 13:14:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
url_redirect('forum-topic', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'topic' => $topicInfo->getId(),
|
2022-09-13 13:14:49 +00:00
|
|
|
]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
$topicPosts = $topicInfo->getPostsCount();
|
|
|
|
if($canDeleteAny)
|
|
|
|
$topicPosts += $topicInfo->getDeletedPostsCount();
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
$topicPagination = new Pagination($topicPosts, 10, 'page');
|
2022-09-13 13:14:49 +00:00
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
if(isset($preceedingPostCount))
|
|
|
|
$topicPagination->setPage(floor($preceedingPostCount / $topicPagination->getRange()), true);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
|
|
|
if(!$topicPagination->hasValidOffset()) {
|
|
|
|
echo render_error(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
$postInfos = $forum->getPosts(
|
|
|
|
topicInfo: $topicInfo,
|
|
|
|
deleted: perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST) ? null : false,
|
|
|
|
pagination: $topicPagination,
|
2022-09-13 13:14:49 +00:00
|
|
|
);
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
if(empty($postInfos)) {
|
2022-09-13 13:14:49 +00:00
|
|
|
echo render_error(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
$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();
|
|
|
|
}
|
|
|
|
|
2022-09-13 13:14:49 +00:00
|
|
|
$canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && perms_check($perms, MSZ_FORUM_PERM_CREATE_POST);
|
|
|
|
|
2023-08-28 01:17:34 +00:00
|
|
|
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,
|
|
|
|
]);
|
2022-09-13 13:14:49 +00:00
|
|
|
|
|
|
|
Template::render('forum.topic', [
|
2023-08-28 01:17:34 +00:00
|
|
|
'topic_breadcrumbs' => $forum->getCategoryAncestry($topicInfo),
|
|
|
|
'global_accent_colour' => $forum->getCategoryColour($topicInfo),
|
|
|
|
'topic_info' => $topicInfo,
|
|
|
|
'category_info' => $categoryInfo,
|
2022-09-13 13:14:49 +00:00
|
|
|
'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,
|
2023-08-28 01:17:34 +00:00
|
|
|
'topic_user_id' => $currentUserId,
|
|
|
|
'topic_perms' => $perms,
|
2022-09-13 13:14:49 +00:00
|
|
|
]);
|