Added some topic moderation tools.
This commit is contained in:
parent
93357f90e7
commit
9869190b2a
14 changed files with 571 additions and 45 deletions
|
@ -47,4 +47,20 @@
|
|||
font-size: .9em;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__action {
|
||||
margin-right: 10px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: color .2s;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--accent-colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ $topics = $forumMayHaveTopics
|
|||
$forumUserId,
|
||||
$topicsOffset,
|
||||
$forumPagination['range'],
|
||||
perms_check($perms, MSZ_FORUM_PERM_DELETE_TOPIC | MSZ_FORUM_PERM_DELETE_ANY_POST)
|
||||
perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST)
|
||||
)
|
||||
: [];
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ switch ($_GET['m'] ?? '') {
|
|||
$categories[$key]['forum_subforums'] = forum_get_children(
|
||||
$category['forum_id'],
|
||||
user_session_current('user_id', 0),
|
||||
perms_check($category['forum_permissions'], MSZ_FORUM_PERM_DELETE_TOPIC | MSZ_FORUM_PERM_DELETE_ANY_POST)
|
||||
perms_check($category['forum_permissions'], MSZ_FORUM_PERM_DELETE_ANY_POST)
|
||||
);
|
||||
|
||||
foreach ($categories[$key]['forum_subforums'] as $skey => $sub) {
|
||||
|
@ -35,7 +35,7 @@ switch ($_GET['m'] ?? '') {
|
|||
= forum_get_children(
|
||||
$sub['forum_id'],
|
||||
user_session_current('user_id', 0),
|
||||
perms_check($sub['forum_permissions'], MSZ_FORUM_PERM_DELETE_TOPIC | MSZ_FORUM_PERM_DELETE_ANY_POST),
|
||||
perms_check($sub['forum_permissions'], MSZ_FORUM_PERM_DELETE_ANY_POST),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ switch ($postMode) {
|
|||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_OLD:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'This post has existed for too long, ask a moderator to remove if it absolutely necessary.';
|
||||
$canDeleteMsg = 'This post has existed for too long. Ask a moderator to remove if it absolutely necessary.';
|
||||
break;
|
||||
case MSZ_E_FORUM_POST_DELETE_PERM:
|
||||
$responseCode = 401;
|
||||
|
@ -122,9 +122,11 @@ switch ($postMode) {
|
|||
echo tpl_render('forum.confirm', [
|
||||
'title' => 'Confirm post deletion',
|
||||
'class' => 'far fa-trash-alt',
|
||||
'mode' => 'delete',
|
||||
'message' => sprintf('You are about to delete post #%d. Are you sure about that?', $postInfo['post_id']),
|
||||
'post' => $postInfo,
|
||||
'params' => [
|
||||
'p' => $postInfo['post_id'],
|
||||
'm' => 'delete',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
@ -163,9 +165,11 @@ switch ($postMode) {
|
|||
echo tpl_render('forum.confirm', [
|
||||
'title' => 'Confirm post nuke',
|
||||
'class' => 'fas fa-radiation',
|
||||
'mode' => 'nuke',
|
||||
'message' => sprintf('You are about to PERMANENTLY DELETE post #%d. Are you sure about that?', $postInfo['post_id']),
|
||||
'post' => $postInfo,
|
||||
'params' => [
|
||||
'p' => $postInfo['post_id'],
|
||||
'm' => 'nuke',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
@ -199,9 +203,11 @@ switch ($postMode) {
|
|||
echo tpl_render('forum.confirm', [
|
||||
'title' => 'Confirm post restore',
|
||||
'class' => 'fas fa-magic',
|
||||
'mode' => 'restore',
|
||||
'message' => sprintf('You are about to restore post #%d. Are you sure about that?', $postInfo['post_id']),
|
||||
'post' => $postInfo,
|
||||
'params' => [
|
||||
'p' => $postInfo['post_id'],
|
||||
'm' => 'restore',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ if (!empty($postId)) {
|
|||
}
|
||||
|
||||
if (!empty($topicId)) {
|
||||
$topic = forum_topic_fetch($topicId);
|
||||
$topic = forum_topic_get($topicId);
|
||||
|
||||
if (isset($topic['forum_id'])) {
|
||||
$forumId = (int)$topic['forum_id'];
|
||||
|
|
|
@ -14,7 +14,7 @@ if ($topicId < 1 && $postId > 0) {
|
|||
}
|
||||
}
|
||||
|
||||
$topic = forum_topic_fetch($topicId, $topicUserId);
|
||||
$topic = forum_topic_get($topicId, true);
|
||||
$perms = $topic
|
||||
? forum_perms_get_user(MSZ_FORUM_PERMS_GENERAL, $topic['forum_id'], $topicUserId)
|
||||
: 0;
|
||||
|
@ -23,7 +23,10 @@ if (user_warning_check_restriction($topicUserId)) {
|
|||
$perms &= ~MSZ_FORUM_PERM_SET_WRITE;
|
||||
}
|
||||
|
||||
if (!$topic || ($topic['topic_deleted'] !== null && !perms_check($perms, MSZ_FORUM_PERM_DELETE_TOPIC))) {
|
||||
$topicIsDeleted = !empty($topic['topic_deleted']);
|
||||
$canDeleteAny = perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST);
|
||||
|
||||
if (!$topic || ($topicIsDeleted && !$canDeleteAny)) {
|
||||
echo render_error(404);
|
||||
return;
|
||||
}
|
||||
|
@ -33,7 +36,266 @@ if (!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) {
|
|||
return;
|
||||
}
|
||||
|
||||
$topicPagination = pagination_create($topic['topic_post_count'], MSZ_FORUM_POSTS_PER_PAGE);
|
||||
$topicIsLocked = !empty($topic['topic_locked']);
|
||||
$topicIsArchived = !empty($topic['topic_archived']);
|
||||
$topicPostsTotal = (int)($topic['topic_post_count'] + $topic['topic_deleted_post_count']);
|
||||
$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 <= MSZ_FORUM_TOPIC_DELETE_POST_LIMIT
|
||||
&& $canDeleteOwn
|
||||
&& $topic['author_user_id'] === $topicUserId
|
||||
)
|
||||
);
|
||||
|
||||
$moderationMode = (string)($_GET['m'] ?? '');
|
||||
$validModerationModes = [
|
||||
'delete', 'restore', 'nuke',
|
||||
'bump', 'lock', 'unlock',
|
||||
];
|
||||
|
||||
if (in_array($moderationMode, $validModerationModes, true)) {
|
||||
$redirect = !empty($_SERVER['HTTP_REFERER']) && empty($_SERVER['HTTP_X_MISUZU_XHR']) ? $_SERVER['HTTP_REFERER'] : '';
|
||||
$isXHR = !$redirect;
|
||||
|
||||
if ($isXHR) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
} elseif (!is_local_url($redirect)) {
|
||||
echo render_info('Possible request forgery detected.', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!csrf_verify('forum_post', $_GET['csrf'] ?? '') && !csrf_verify('forum_post', csrf_http_header_parse($_SERVER['HTTP_X_MISUZU_CSRF'] ?? '')['token'])) {
|
||||
echo render_info_or_json($isXHR, "Couldn't verify this request, please refresh the page and try again.", 403);
|
||||
return;
|
||||
}
|
||||
|
||||
header(csrf_http_header('forum_post'));
|
||||
|
||||
if (!user_session_active()) {
|
||||
echo render_info_or_json($isXHR, 'You must be logged in to manage posts.', 401);
|
||||
return;
|
||||
}
|
||||
|
||||
if (user_warning_check_expiration($topicUserId, MSZ_WARN_BAN) > 0) {
|
||||
echo render_info_or_json($isXHR, 'You have been banned, check your profile for more information.', 403);
|
||||
return;
|
||||
}
|
||||
if (user_warning_check_expiration($topicUserId, MSZ_WARN_SILENCE) > 0) {
|
||||
echo render_info_or_json($isXHR, 'You have been silenced, check your profile for more information.', 403);
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($_GET['m'] ?? '') {
|
||||
case 'delete':
|
||||
$canDeleteCode = forum_topic_can_delete($topic, $topicUserId);
|
||||
$canDeleteMsg = '';
|
||||
$responseCode = 200;
|
||||
|
||||
switch ($canDeleteCode) {
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_USER:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'You must be logged in to delete topics.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_TOPIC:
|
||||
$responseCode = 404;
|
||||
$canDeleteMsg = "This topic doesn't exist.";
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_DELETED:
|
||||
$responseCode = 404;
|
||||
$canDeleteMsg = 'This topic has already been marked as deleted.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_OWNER:
|
||||
$responseCode = 403;
|
||||
$canDeleteMsg = 'You can only delete your own topics.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_OLD:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'This topic has existed for too long. Ask a moderator to remove if it absolutely necessary.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_PERM:
|
||||
$responseCode = 401;
|
||||
$canDeleteMsg = 'You are not allowed to delete topics.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_POSTS:
|
||||
$responseCode = 403;
|
||||
$canDeleteMsg = 'This topic already has replies, you may no longer delete it. Ask a moderator to remove if it absolutely necessary.';
|
||||
break;
|
||||
case MSZ_E_FORUM_TOPIC_DELETE_OK:
|
||||
break;
|
||||
default:
|
||||
$responseCode = 500;
|
||||
$canDeleteMsg = sprintf('Unknown error \'%d\'', $canDelete);
|
||||
}
|
||||
|
||||
if ($canDeleteCode !== MSZ_E_FORUM_TOPIC_DELETE_OK) {
|
||||
if ($isXHR) {
|
||||
http_response_code($responseCode);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'topic_id' => $topic['topic_id'],
|
||||
'code' => $canDeleteCode,
|
||||
'message' => $canDeleteMsg,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
echo render_info($canDeleteMsg, $responseCode);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$isXHR) {
|
||||
if (isset($_GET['confirm']) && $_GET['confirm'] !== '1') {
|
||||
header("Location: /forum/topic.php?t={$topic['topic_id']}");
|
||||
break;
|
||||
} elseif (!isset($_GET['confirm'])) {
|
||||
echo tpl_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?', $topic['topic_id']),
|
||||
'params' => [
|
||||
't' => $topic['topic_id'],
|
||||
'm' => 'delete',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$deleteTopic = forum_topic_delete($topic['topic_id']);
|
||||
|
||||
if ($isXHR) {
|
||||
echo json_encode([
|
||||
'success' => $deleteTopic,
|
||||
'topic_id' => $topic['topic_id'],
|
||||
'message' => $deleteTopic ? 'Topic deleted!' : 'Failed to delete topic.',
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$deleteTopic) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
header('Location: /forum/forum.php?f=' . $topic['forum_id']);
|
||||
break;
|
||||
|
||||
case 'restore':
|
||||
if (!$canNukeOrRestore) {
|
||||
echo render_error(403);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$isXHR) {
|
||||
if (isset($_GET['confirm']) && $_GET['confirm'] !== '1') {
|
||||
header("Location: /forum/topic.php?t={$topic['topic_id']}");
|
||||
break;
|
||||
} elseif (!isset($_GET['confirm'])) {
|
||||
echo tpl_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?', $topic['topic_id']),
|
||||
'params' => [
|
||||
't' => $topic['topic_id'],
|
||||
'm' => 'restore',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$restoreTopic = forum_topic_restore($topic['topic_id']);
|
||||
|
||||
if (!$restoreTopic) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
http_response_code(204);
|
||||
|
||||
if (!$isXHR) {
|
||||
header('Location: /forum/forum.php?f=' . $topic['forum_id']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'nuke':
|
||||
if (!$canNukeOrRestore) {
|
||||
echo render_error(403);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$isXHR) {
|
||||
if (isset($_GET['confirm']) && $_GET['confirm'] !== '1') {
|
||||
header("Location: /forum/topic.php?t={$topic['topic_id']}");
|
||||
break;
|
||||
} elseif (!isset($_GET['confirm'])) {
|
||||
echo tpl_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?', $topic['topic_id']),
|
||||
'params' => [
|
||||
't' => $topic['topic_id'],
|
||||
'm' => 'nuke',
|
||||
],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$nukeTopic = forum_topic_nuke($topic['topic_id']);
|
||||
|
||||
if (!$nukeTopic) {
|
||||
echo render_error(500);
|
||||
break;
|
||||
}
|
||||
|
||||
http_response_code(204);
|
||||
|
||||
if (!$isXHR) {
|
||||
header('Location: /forum/forum.php?f=' . $topic['forum_id']);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bump':
|
||||
if ($canBumpTopic) {
|
||||
forum_topic_bump($topic['topic_id']);
|
||||
}
|
||||
|
||||
header('Location: /forum/topic.php?t=' . $topic['topic_id']);
|
||||
break;
|
||||
|
||||
case 'lock':
|
||||
if ($canLockTopic && !$topicIsLocked) {
|
||||
forum_topic_lock($topic['topic_id']);
|
||||
}
|
||||
|
||||
header('Location: /forum/topic.php?t=' . $topic['topic_id']);
|
||||
break;
|
||||
|
||||
case 'unlock':
|
||||
if ($canLockTopic && $topicIsLocked) {
|
||||
forum_topic_unlock($topic['topic_id']);
|
||||
}
|
||||
|
||||
header('Location: /forum/topic.php?t=' . $topic['topic_id']);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$topicPosts = $topic['topic_post_count'];
|
||||
|
||||
if ($canDeleteAny) {
|
||||
$topicPosts += $topic['topic_deleted_post_count'];
|
||||
}
|
||||
|
||||
$topicPagination = pagination_create($topicPosts, MSZ_FORUM_POSTS_PER_PAGE);
|
||||
|
||||
if (isset($postInfo['preceeding_post_count'])) {
|
||||
$postsPage = floor($postInfo['preceeding_post_count'] / $topicPagination['range']) + 1;
|
||||
|
@ -60,8 +322,7 @@ if (!$posts) {
|
|||
return;
|
||||
}
|
||||
|
||||
$canReply = empty($topic['topic_archived']) && empty($topic['topic_locked']) && empty($topic['topic_deleted'])
|
||||
&& perms_check($perms, MSZ_FORUM_PERM_CREATE_POST);
|
||||
$canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && perms_check($perms, MSZ_FORUM_PERM_CREATE_POST);
|
||||
|
||||
forum_topic_mark_read($topicUserId, $topic['topic_id'], $topic['forum_id']);
|
||||
|
||||
|
@ -72,4 +333,8 @@ echo tpl_render('forum.topic', [
|
|||
'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,
|
||||
]);
|
||||
|
|
|
@ -5,12 +5,13 @@ define('MSZ_FORUM_PERM_LIST_FORUM', 1); // can see stats, but will get error whe
|
|||
define('MSZ_FORUM_PERM_VIEW_FORUM', 1 << 1);
|
||||
|
||||
define('MSZ_FORUM_PERM_CREATE_TOPIC', 1 << 10);
|
||||
define('MSZ_FORUM_PERM_DELETE_TOPIC', 1 << 11); // how is this different from MSZ_FORUM_PERM_DELETE_ANY_POST?
|
||||
//define('MSZ_FORUM_PERM_DELETE_TOPIC', 1 << 11); // use MSZ_FORUM_PERM_DELETE_ANY_POST instead
|
||||
define('MSZ_FORUM_PERM_MOVE_TOPIC', 1 << 12);
|
||||
define('MSZ_FORUM_PERM_LOCK_TOPIC', 1 << 13);
|
||||
define('MSZ_FORUM_PERM_STICKY_TOPIC', 1 << 14);
|
||||
define('MSZ_FORUM_PERM_ANNOUNCE_TOPIC', 1 << 15);
|
||||
define('MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC', 1 << 16);
|
||||
define('MSZ_FORUM_PERM_BUMP_TOPIC', 1 << 17);
|
||||
|
||||
define('MSZ_FORUM_PERM_CREATE_POST', 1 << 20);
|
||||
define('MSZ_FORUM_PERM_EDIT_POST', 1 << 21);
|
||||
|
@ -23,7 +24,6 @@ define('MSZ_FORUM_PERM_SET_READ', MSZ_FORUM_PERM_LIST_FORUM | MSZ_FORUM_PERM_VIE
|
|||
define(
|
||||
'MSZ_FORUM_PERM_SET_WRITE',
|
||||
MSZ_FORUM_PERM_CREATE_TOPIC
|
||||
| MSZ_FORUM_PERM_DELETE_TOPIC
|
||||
| MSZ_FORUM_PERM_MOVE_TOPIC
|
||||
| MSZ_FORUM_PERM_LOCK_TOPIC
|
||||
| MSZ_FORUM_PERM_STICKY_TOPIC
|
||||
|
@ -34,6 +34,7 @@ define(
|
|||
| MSZ_FORUM_PERM_EDIT_ANY_POST
|
||||
| MSZ_FORUM_PERM_DELETE_POST
|
||||
| MSZ_FORUM_PERM_DELETE_ANY_POST
|
||||
| MSZ_FORUM_PERM_BUMP_TOPIC
|
||||
);
|
||||
|
||||
define('MSZ_FORUM_TYPE_DISCUSSION', 0);
|
||||
|
|
|
@ -72,7 +72,7 @@ function forum_post_find(int $postId, int $userId): array
|
|||
WHERE p.`post_id` = :post_id
|
||||
',
|
||||
forum_perms_get_user_sql(MSZ_FORUM_PERMS_GENERAL, 'p.`forum_id`'),
|
||||
MSZ_FORUM_PERM_DELETE_TOPIC | MSZ_FORUM_PERM_DELETE_ANY_POST
|
||||
MSZ_FORUM_PERM_DELETE_ANY_POST
|
||||
));
|
||||
$getPostInfo->bindValue('post_id', $postId);
|
||||
$getPostInfo->bindValue('perm_user_id_user', $userId);
|
||||
|
@ -183,7 +183,7 @@ define('MSZ_FORUM_POST_DELETE_LIMIT', 60 * 60 * 24 * 7);
|
|||
// $postId can also be a the return value of forum_post_get if you already grabbed it once before
|
||||
function forum_post_can_delete($postId, ?int $userId = null): int
|
||||
{
|
||||
if (($userId !== null && $userId < 1) || $postId < 1) {
|
||||
if ($userId !== null && $userId < 1) {
|
||||
return MSZ_E_FORUM_POST_DELETE_USER;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,37 +69,41 @@ function forum_topic_update(int $topicId, ?string $title, ?int $type = null): bo
|
|||
return $updateTopic->execute();
|
||||
}
|
||||
|
||||
function forum_topic_fetch(int $topicId, int $userId = 0): array
|
||||
function forum_topic_get(int $topicId, bool $allowDeleted = false): array
|
||||
{
|
||||
$getTopic = db_prepare(sprintf(
|
||||
'
|
||||
SELECT
|
||||
t.`topic_id`, t.`forum_id`, t.`topic_title`, t.`topic_type`, t.`topic_locked`, t.`topic_created`,
|
||||
f.`forum_archived` as `topic_archived`, t.`topic_deleted`, t.`topic_bumped`,
|
||||
((%s) & %d) as `can_view_deleted`,
|
||||
(
|
||||
SELECT MIN(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `topic_id` = t.`topic_id`
|
||||
AND (`can_view_deleted` OR `post_deleted` IS NULL)
|
||||
) as `topic_first_post_id`,
|
||||
fp.`topic_id` as `author_post_id`, fp.`user_id` as `author_user_id`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `topic_id` = t.`topic_id`
|
||||
AND (`can_view_deleted` OR `post_deleted` IS NULL)
|
||||
) as `topic_post_count`
|
||||
AND `post_deleted` IS NULL
|
||||
) as `topic_post_count`,
|
||||
(
|
||||
SELECT COUNT(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `topic_id` = t.`topic_id`
|
||||
AND `post_deleted` IS NOT NULL
|
||||
) as `topic_deleted_post_count`
|
||||
FROM `msz_forum_topics` as t
|
||||
LEFT JOIN `msz_forum_categories` as f
|
||||
ON f.`forum_id` = t.`forum_id`
|
||||
LEFT JOIN `msz_forum_posts` as fp
|
||||
ON fp.`post_id` = (
|
||||
SELECT MIN(`post_id`)
|
||||
FROM `msz_forum_posts`
|
||||
WHERE `topic_id` = t.`topic_id`
|
||||
)
|
||||
WHERE t.`topic_id` = :topic_id
|
||||
%s
|
||||
',
|
||||
forum_perms_get_user_sql(MSZ_FORUM_PERMS_GENERAL, 't.`forum_id`'),
|
||||
MSZ_FORUM_PERM_DELETE_TOPIC | MSZ_FORUM_PERM_DELETE_ANY_POST
|
||||
$allowDeleted ? '' : 'AND t.`topic_deleted` IS NULL'
|
||||
));
|
||||
$getTopic->bindValue('topic_id', $topicId);
|
||||
$getTopic->bindValue('perm_user_id_user', $userId);
|
||||
$getTopic->bindValue('perm_user_id_role', $userId);
|
||||
return db_fetch($getTopic);
|
||||
}
|
||||
|
||||
|
@ -224,3 +228,191 @@ function forum_topic_listing(int $forumId, int $userId, int $offset = 0, int $ta
|
|||
|
||||
return db_fetch_all($getTopics);
|
||||
}
|
||||
|
||||
function forum_topic_lock(int $topicId): bool
|
||||
{
|
||||
if ($topicId < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$markLocked = db_prepare('
|
||||
UPDATE `msz_forum_topics`
|
||||
SET `topic_locked` = NOW()
|
||||
WHERE `topic_id` = :topic
|
||||
AND `topic_locked` IS NULL
|
||||
');
|
||||
$markLocked->bindValue('topic', $topicId);
|
||||
|
||||
return $markLocked->execute();
|
||||
}
|
||||
|
||||
function forum_topic_unlock(int $topicId): bool
|
||||
{
|
||||
if ($topicId < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$markUnlocked = db_prepare('
|
||||
UPDATE `msz_forum_topics`
|
||||
SET `topic_locked` = NULL
|
||||
WHERE `topic_id` = :topic
|
||||
AND `topic_locked` IS NOT NULL
|
||||
');
|
||||
$markUnlocked->bindValue('topic', $topicId);
|
||||
|
||||
return $markUnlocked->execute();
|
||||
}
|
||||
|
||||
define('MSZ_E_FORUM_TOPIC_DELETE_OK', 0); // deleting is fine
|
||||
define('MSZ_E_FORUM_TOPIC_DELETE_USER', 1); // invalid user
|
||||
define('MSZ_E_FORUM_TOPIC_DELETE_TOPIC', 2); // topic doesn't exist
|
||||
define('MSZ_E_FORUM_TOPIC_DELETE_DELETED', 3); // topic is already marked as deleted
|
||||
define('MSZ_E_FORUM_TOPIC_DELETE_OWNER', 4); // you may only delete your own topics
|
||||
define('MSZ_E_FORUM_TOPIC_DELETE_OLD', 5); // topic has existed for too long to be deleted
|
||||
define('MSZ_E_FORUM_TOPIC_DELETE_PERM', 6); // you aren't allowed to delete topics
|
||||
define('MSZ_E_FORUM_TOPIC_DELETE_POSTS', 7); // the topic already has replies
|
||||
|
||||
// only allow topics made within a day of posting to be deleted by normal users
|
||||
define('MSZ_FORUM_TOPIC_DELETE_TIME_LIMIT', 60 * 60 * 24);
|
||||
|
||||
// only allow topics with a single post to be deleted, includes soft deleted posts
|
||||
define('MSZ_FORUM_TOPIC_DELETE_POST_LIMIT', 1);
|
||||
|
||||
// set $userId to null for system request, make sure this is NEVER EVER null on user request
|
||||
// $topicId can also be a the return value of forum_topic_get if you already grabbed it once before
|
||||
function forum_topic_can_delete($topicId, ?int $userId = null): int
|
||||
{
|
||||
if ($userId !== null && $userId < 1) {
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_USER;
|
||||
}
|
||||
|
||||
if (is_array($topicId)) {
|
||||
$topic = $topicId;
|
||||
} else {
|
||||
$topic = forum_topic_get((int)$topicId, true);
|
||||
}
|
||||
|
||||
if (empty($topic)) {
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_TOPIC;
|
||||
}
|
||||
|
||||
$isSystemReq = $userId === null;
|
||||
$perms = $isSystemReq ? 0 : forum_perms_get_user(MSZ_FORUM_PERMS_GENERAL, $topic['forum_id'], $userId);
|
||||
$canDeleteAny = $isSystemReq ? true : perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST);
|
||||
$canViewPost = $isSystemReq ? true : perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM);
|
||||
$postIsDeleted = !empty($topic['topic_deleted']);
|
||||
|
||||
if (!$canViewPost) {
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_TOPIC;
|
||||
}
|
||||
|
||||
if ($postIsDeleted) {
|
||||
return $canDeleteAny ? MSZ_E_FORUM_TOPIC_DELETE_DELETED : MSZ_E_FORUM_TOPIC_DELETE_TOPIC;
|
||||
}
|
||||
|
||||
if ($isSystemReq) {
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_OK;
|
||||
}
|
||||
|
||||
if (!$canDeleteAny) {
|
||||
if (!perms_check($perms, MSZ_FORUM_PERM_DELETE_POST)) {
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_PERM;
|
||||
}
|
||||
|
||||
if ($topic['author_user_id'] !== $userId) {
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_OWNER;
|
||||
}
|
||||
|
||||
if (strtotime($topic['topic_created']) <= time() - MSZ_FORUM_TOPIC_DELETE_TIME_LIMIT) {
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_OLD;
|
||||
}
|
||||
|
||||
$totalReplies = $topic['topic_post_count'] + $topic['topic_deleted_post_count'];
|
||||
|
||||
if ($totalReplies > MSZ_E_FORUM_TOPIC_DELETE_POSTS) {
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_POSTS;
|
||||
}
|
||||
}
|
||||
|
||||
return MSZ_E_FORUM_TOPIC_DELETE_OK;
|
||||
}
|
||||
|
||||
function forum_topic_delete(int $topicId): bool
|
||||
{
|
||||
if ($topicId < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$markTopicDeleted = db_prepare('
|
||||
UPDATE `msz_forum_topics`
|
||||
SET `topic_deleted` = NOW()
|
||||
WHERE `topic_id` = :topic
|
||||
AND `topic_deleted` IS NULL
|
||||
');
|
||||
$markTopicDeleted->bindValue('topic', $topicId);
|
||||
|
||||
if (!$markTopicDeleted->execute()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$markPostsDeleted = db_prepare('
|
||||
UPDATE `msz_forum_posts` as p
|
||||
SET p.`post_deleted` = (
|
||||
SELECT `topic_deleted`
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `topic_id` = p.`topic_id`
|
||||
)
|
||||
WHERE p.`topic_id` = :topic
|
||||
AND p.`post_deleted` IS NULL
|
||||
');
|
||||
$markPostsDeleted->bindValue('topic', $topicId);
|
||||
|
||||
return $markPostsDeleted->execute();
|
||||
}
|
||||
|
||||
function forum_topic_restore(int $topicId): bool
|
||||
{
|
||||
if ($topicId < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$markPostsRestored = db_prepare('
|
||||
UPDATE `msz_forum_posts` as p
|
||||
SET p.`post_deleted` = NULL
|
||||
WHERE p.`topic_id` = :topic
|
||||
AND p.`post_deleted` = (
|
||||
SELECT `topic_deleted`
|
||||
FROM `msz_forum_topics`
|
||||
WHERE `topic_id` = p.`topic_id`
|
||||
)
|
||||
');
|
||||
$markPostsRestored->bindValue('topic', $topicId);
|
||||
|
||||
if (!$markPostsRestored->execute()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$markTopicRestored = db_prepare('
|
||||
UPDATE `msz_forum_topics`
|
||||
SET `topic_deleted` = NULL
|
||||
WHERE `topic_id` = :topic
|
||||
AND `topic_deleted` IS NOT NULL
|
||||
');
|
||||
$markTopicRestored->bindValue('topic', $topicId);
|
||||
|
||||
return $markTopicRestored->execute();
|
||||
}
|
||||
|
||||
function forum_topic_nuke(int $topicId): bool
|
||||
{
|
||||
if ($topicId < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$nukeTopic = db_prepare('
|
||||
DELETE FROM `msz_forum_topics`
|
||||
WHERE `topic_id` = :topic
|
||||
');
|
||||
$nukeTopic->bindValue('topic', $topicId);
|
||||
return $nukeTopic->execute();
|
||||
}
|
||||
|
|
|
@ -357,11 +357,6 @@ function manage_forum_perms_list(array $rawPerms): array
|
|||
'title' => 'Can create topics.',
|
||||
'perm' => MSZ_FORUM_PERM_CREATE_TOPIC,
|
||||
],
|
||||
[
|
||||
'section' => 'can-delete-topic',
|
||||
'title' => 'Can delete topics (required a post delete permission).',
|
||||
'perm' => MSZ_FORUM_PERM_DELETE_TOPIC,
|
||||
],
|
||||
[
|
||||
'section' => 'can-move-topic',
|
||||
'title' => 'Can move topics between forums.',
|
||||
|
@ -387,6 +382,11 @@ function manage_forum_perms_list(array $rawPerms): array
|
|||
'title' => 'Can make topics global announcements.',
|
||||
'perm' => MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC,
|
||||
],
|
||||
[
|
||||
'section' => 'can-bump-topic',
|
||||
'title' => 'Can bump topics without posting a reply.',
|
||||
'perm' => MSZ_FORUM_PERM_BUMP_TOPIC,
|
||||
],
|
||||
[
|
||||
'section' => 'can-create-post',
|
||||
'title' => 'Can make posts (reply only, if create topic is disallowed).',
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
{% block content %}
|
||||
<form action="" method="get" class="container forum__confirm">
|
||||
{{ container_title('<i class="' ~ class|default('fas fa-exclamation-circle') ~ ' fa-fw"></i> ' ~ title) }}
|
||||
{{ input_csrf('forum_post') }}
|
||||
<input type="hidden" name="p" value="{{ post.post_id|default(0) }}">
|
||||
<input type="hidden" name="m" value="{{ mode|default('') }}">
|
||||
{{ input_csrf(csrf|default('forum_post')) }}
|
||||
{% for name, value in params %}
|
||||
<input type="hidden" name="{{ name }}" value="{{ value }}">
|
||||
{% endfor %}
|
||||
|
||||
<div class="forum__confirm__message">
|
||||
{{ message|default('Are you sure you w') }}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro forum_header(title, breadcrumbs, omit_last_breadcrumb, title_url) %}
|
||||
{% macro forum_header(title, breadcrumbs, omit_last_breadcrumb, title_url, actions) %}
|
||||
<div class="container forum__header">
|
||||
{% if breadcrumbs is iterable and breadcrumbs|length > 0 %}
|
||||
<div class="forum__header__breadcrumbs">
|
||||
|
@ -50,6 +50,18 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if actions is iterable and actions|length > 0 %}
|
||||
<div class="forum__header__actions">
|
||||
{% for action in actions %}
|
||||
{% if action.display is not defined or action.display %}
|
||||
<a class="forum__header__action{% if action.class is defined %}{{ action.class }}{% endif %}" href="{{ action.url }}">
|
||||
{{ action.html|raw }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
|
|
@ -16,11 +16,44 @@
|
|||
'page': topic_pagination.page > 1 ? topic_pagination.page : 0,
|
||||
}) %}
|
||||
|
||||
{% set forum_post_csrf = csrf_token('forum_post') %}
|
||||
{% set topic_tools = forum_topic_tools(topic_info, topic_pagination, can_reply) %}
|
||||
{% set topic_notice = forum_topic_locked(topic_info.topic_locked, topic_info.topic_archived) %}
|
||||
{% set topic_actions = [
|
||||
{
|
||||
'html': '<i class="far fa-trash-alt fa-fw"></i> Delete',
|
||||
'url': url_construct('/forum/topic.php', {'t': topic_info.topic_id, 'm': 'delete', 'csrf[forum_post]': forum_post_csrf}),
|
||||
'display': topic_can_delete,
|
||||
},
|
||||
{
|
||||
'html': '<i class="fas fa-magic fa-fw"></i> Restore',
|
||||
'url': url_construct('/forum/topic.php', {'t': topic_info.topic_id, 'm': 'restore', 'csrf[forum_post]': forum_post_csrf}),
|
||||
'display': topic_can_nuke_or_restore,
|
||||
},
|
||||
{
|
||||
'html': '<i class="fas fa-radiation-alt fa-fw"></i> Permanently Delete',
|
||||
'url': url_construct('/forum/topic.php', {'t': topic_info.topic_id, 'm': 'nuke', 'csrf[forum_post]': forum_post_csrf}),
|
||||
'display': topic_can_nuke_or_restore,
|
||||
},
|
||||
{
|
||||
'html': '<i class="fas fa-plus-circle fa-fw"></i> Bump',
|
||||
'url': url_construct('/forum/topic.php', {'t': topic_info.topic_id, 'm': 'bump', 'csrf[forum_post]': forum_post_csrf}),
|
||||
'display': topic_can_bump,
|
||||
},
|
||||
{
|
||||
'html': '<i class="fas fa-lock fa-fw"></i> Lock',
|
||||
'url': url_construct('/forum/topic.php', {'t': topic_info.topic_id, 'm': 'lock', 'csrf[forum_post]': forum_post_csrf}),
|
||||
'display': topic_can_lock and topic_info.topic_locked is null,
|
||||
},
|
||||
{
|
||||
'html': '<i class="fas fa-lock-open fa-fw"></i> Unlock',
|
||||
'url': url_construct('/forum/topic.php', {'t': topic_info.topic_id, 'm': 'unlock', 'csrf[forum_post]': forum_post_csrf}),
|
||||
'display': topic_can_lock and topic_info.topic_locked is not null,
|
||||
},
|
||||
] %}
|
||||
|
||||
{% block content %}
|
||||
{{ forum_header(topic_info.topic_title, topic_breadcrumbs, false, canonical_url) }}
|
||||
{{ forum_header(topic_info.topic_title, topic_breadcrumbs, false, canonical_url, topic_actions) }}
|
||||
{{ topic_notice }}
|
||||
{{ topic_tools }}
|
||||
{{ forum_post_listing(topic_posts, current_user.user_id|default(0), topic_perms) }}
|
||||
|
|
|
@ -258,7 +258,7 @@ function render_info_or_json(bool $json, string $message, int $httpCode = 200, s
|
|||
http_response_code($httpCode);
|
||||
|
||||
if ($json) {
|
||||
return json_encode([($error ? 'error' : 'message') => $message]);
|
||||
return json_encode([($error ? 'error' : 'message') => $message, 'success' => $error]);
|
||||
}
|
||||
|
||||
return render_info($message, $httpCode, $template);
|
||||
|
|
Loading…
Add table
Reference in a new issue