diff --git a/database/2023_04_30_001226_create_topic_redirs_table.php b/database/2023_04_30_001226_create_topic_redirs_table.php new file mode 100644 index 0000000..adea1b9 --- /dev/null +++ b/database/2023_04_30_001226_create_topic_redirs_table.php @@ -0,0 +1,23 @@ +execute(' + CREATE TABLE msz_forum_topics_redirects ( + topic_id INT(10) UNSIGNED NOT NULL, + user_id INT(10) UNSIGNED NULL DEFAULT NULL, + topic_redir_url VARCHAR(255) NOT NULL, + topic_redir_created TIMESTAMP NOT NULL DEFAULT current_timestamp(), + PRIMARY KEY (topic_id), + KEY topics_redirs_user_foreign (user_id), + CONSTRAINT topics_redirs_user_foreign + FOREIGN KEY (user_id) + REFERENCES msz_users (user_id) + ON DELETE SET NULL + ON UPDATE CASCADE + ) ENGINE=InnoDB COLLATE=utf8mb4_bin; + '); + } +} diff --git a/public/forum/topic.php b/public/forum/topic.php index ca99bbe..f729689 100644 --- a/public/forum/topic.php +++ b/public/forum/topic.php @@ -31,12 +31,21 @@ $perms = $topic if(isset($topicUser) && $topicUser->hasActiveWarning()) $perms &= ~MSZ_FORUM_PERM_SET_WRITE; +$topicIsNuked = empty($topic['topic_id']); $topicIsDeleted = !empty($topic['topic_deleted']); $canDeleteAny = perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST); -if(!$topic || ($topicIsDeleted && !$canDeleteAny)) { - echo render_error(404); - return; +if($topicIsNuked || $topicIsDeleted) { + $topicRedirectInfo = forum_topic_redir_info($topicId); + Template::set('topic_redir_info', $topicRedirectInfo); + + if($topicIsNuked || !$canDeleteAny) { + if(empty($topicRedirectInfo)) + echo render_error(404); + else + header('Location: ' . $topicRedirectInfo->topic_redir_url); + return; + } } if(!perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM)) { diff --git a/public/manage/forum/redirs.php b/public/manage/forum/redirs.php new file mode 100644 index 0000000..5fa62fa --- /dev/null +++ b/public/manage/forum/redirs.php @@ -0,0 +1,51 @@ +getId(), MSZ_PERM_FORUM_TOPIC_REDIRS)) { + echo render_error(403); + return; +} + +if($_SERVER['REQUEST_METHOD'] === 'POST') { + if(!CSRF::validateRequest()) + throw new \Exception("Request verification failed."); + + $rTopicId = (int)filter_input(INPUT_POST, 'topic_redir_id'); + $rTopicURL = trim((string)filter_input(INPUT_POST, 'topic_redir_url')); + + if($rTopicId < 1) + throw new \Exception("Invalid topic id."); + + AuditLog::create($_SERVER['REMOTE_ADDR'], AuditLog::FORUM_TOPIC_REDIR_CREATE, [$rTopicId]); + forum_topic_redir_create($rTopicId, User::getCurrent()->getId(), $rTopicURL); + url_redirect('manage-forum-topic-redirs'); + return; +} + +if(filter_input(INPUT_GET, 'm') === 'explode') { + if(!CSRF::validateRequest()) + throw new \Exception("Request verification failed."); + + $rTopicId = (int)filter_input(INPUT_GET, 't'); + AuditLog::create($_SERVER['REMOTE_ADDR'], AuditLog::FORUM_TOPIC_REDIR_REMOVE, [$rTopicId]); + forum_topic_redir_remove($rTopicId); + url_redirect('manage-forum-topic-redirs'); + return; +} + +$pagination = new Pagination(forum_topic_redir_count(), 20); +if(!$pagination->hasValidOffset()) { + echo render_error(404); + return; +} + +$redirs = forum_topic_redir_all($pagination->getOffset(), $pagination->getRange()); + +Template::render('manage.forum.redirs', [ + 'manage_redirs' => $redirs, + 'manage_redirs_pagination' => $pagination, +]); diff --git a/src/AuditLog.php b/src/AuditLog.php index 583755f..ffaa2f0 100644 --- a/src/AuditLog.php +++ b/src/AuditLog.php @@ -40,6 +40,8 @@ class AuditLog { public const FORUM_TOPIC_BUMP = 'FORUM_TOPIC_BUMP'; public const FORUM_TOPIC_LOCK = 'FORUM_TOPIC_LOCK'; public const FORUM_TOPIC_UNLOCK = 'FORUM_TOPIC_UNLOCK'; + public const FORUM_TOPIC_REDIR_CREATE = 'FORUM_TOPIC_REDIR_CREATE'; + public const FORUM_TOPIC_REDIR_REMOVE = 'FORUM_TOPIC_REDIR_REMOVE'; public const FORUM_POST_EDIT = 'FORUM_POST_EDIT'; public const FORUM_POST_DELETE = 'FORUM_POST_DELETE'; @@ -88,6 +90,8 @@ class AuditLog { self::FORUM_TOPIC_BUMP => 'Manually bumped forum topic #%d.', self::FORUM_TOPIC_LOCK => 'Locked forum topic #%d.', self::FORUM_TOPIC_UNLOCK => 'Unlocked forum topic #%d.', + self::FORUM_TOPIC_REDIR_CREATE => 'Created redirect for topic #%d.', + self::FORUM_TOPIC_REDIR_REMOVE => 'Removed redirect for topic #%d.', self::CONFIG_CREATE => 'Created config value with name "%s".', self::CONFIG_UPDATE => 'Updated config value with name "%s".', diff --git a/src/Forum/topic.php b/src/Forum/topic.php index 181998a..b1b0c89 100644 --- a/src/Forum/topic.php +++ b/src/Forum/topic.php @@ -108,6 +108,56 @@ function forum_topic_get(int $topicId, bool $allowDeleted = false): array { return $getTopic->fetch(); } +function forum_topic_redir_info(int $topicId): ?object { + $getTopicRedir = \Misuzu\DB::prepare(' + SELECT topic_id, user_id, topic_redir_url, + UNIX_TIMESTAMP(topic_redir_created) AS topic_redir_created + FROM msz_forum_topics_redirects + WHERE topic_id = :topic_id + '); + $getTopicRedir->bind('topic_id', $topicId); + return $getTopicRedir->fetchObject(); +} + +function forum_topic_redir_count(): int { + return \Misuzu\DB::query('SELECT COUNT(*) FROM msz_forum_topics_redirects')->fetchColumn() ?? 0; +} + +function forum_topic_redir_all(int $offset, int $take): array { + $getTopicRedirs = \Misuzu\DB::prepare(' + SELECT topic_id, user_id, topic_redir_url, + UNIX_TIMESTAMP(topic_redir_created) AS topic_redir_created + FROM msz_forum_topics_redirects + LIMIT :offset, :take + '); + $getTopicRedirs->bind('offset', $offset); + $getTopicRedirs->bind('take', $take); + return $getTopicRedirs->fetchObjects(); +} + +function forum_topic_redir_create(int $topicId, int $userId, string $url): void { + if($topicId < 1 || empty($url)) return; + if($userId < 1) $userId = null; + + $createTopicRedir = \Misuzu\DB::prepare(' + INSERT INTO msz_forum_topics_redirects (topic_id, user_id, topic_redir_url) + VALUES (:topic_id, :user_id, :redir_url) + '); + $createTopicRedir->bind('topic_id', $topicId); + $createTopicRedir->bind('user_id', $userId); + $createTopicRedir->bind('redir_url', $url); + $createTopicRedir->execute(); +} + +function forum_topic_redir_remove(int $topicId): void { + $removeTopicRedir = \Misuzu\DB::prepare(' + DELETE FROM msz_forum_topics_redirects + WHERE topic_id = :topic_id + '); + $removeTopicRedir->bind('topic_id', $topicId); + $removeTopicRedir->execute(); +} + function forum_topic_bump(int $topicId): bool { $bumpTopic = \Misuzu\DB::prepare(' UPDATE `msz_forum_topics` diff --git a/src/manage.php b/src/manage.php index 6a19ac6..b042eec 100644 --- a/src/manage.php +++ b/src/manage.php @@ -30,6 +30,8 @@ function manage_get_menu(int $userId): array { if(perms_check_user(MSZ_PERMS_FORUM, $userId, MSZ_PERM_FORUM_MANAGE_FORUMS)) $menu['Forum']['Categories'] = url('manage-forum-categories'); + if(perms_check_user(MSZ_PERMS_FORUM, $userId, MSZ_PERM_FORUM_TOPIC_REDIRS)) + $menu['Forum']['Topic Redirects'] = url('manage-forum-topic-redirs'); if(perms_check_user(MSZ_PERMS_CHANGELOG, $userId, MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) $menu['Changelog']['Changes'] = url('manage-changelog-changes'); @@ -226,6 +228,11 @@ function manage_perms_list(array $rawPerms): array { 'title' => 'Can view the forum leaderboard live.', 'perm' => MSZ_PERM_FORUM_VIEW_LEADERBOARD, ], + [ + 'section' => 'topic-redirs', + 'title' => 'Can create redirects for deleted topics.', + 'perm' => MSZ_PERM_FORUM_TOPIC_REDIRS, + ], ], ], [ diff --git a/src/perms.php b/src/perms.php index 1c9c1dc..af83f10 100644 --- a/src/perms.php +++ b/src/perms.php @@ -34,6 +34,7 @@ define('MSZ_PERM_NEWS_MANAGE_CATEGORIES', 0x00000002); define('MSZ_PERMS_FORUM', 'forum'); define('MSZ_PERM_FORUM_MANAGE_FORUMS', 0x00000001); define('MSZ_PERM_FORUM_VIEW_LEADERBOARD', 0x00000002); +define('MSZ_PERM_FORUM_TOPIC_REDIRS', 0x00000004); define('MSZ_PERMS_COMMENTS', 'comments'); define('MSZ_PERM_COMMENTS_CREATE', 0x00000001); diff --git a/src/url.php b/src/url.php index dc00a4f..e117ffc 100644 --- a/src/url.php +++ b/src/url.php @@ -103,6 +103,9 @@ define('MSZ_URLS', [ 'manage-forum-categories' => ['/manage/forum/index.php'], 'manage-forum-category' => ['/manage/forum/category.php', ['f' => '']], + 'manage-forum-topic-redirs' => ['/manage/forum/redirs.php'], + 'manage-forum-topic-redirs-create' => ['/manage/forum/redirs.php'], + 'manage-forum-topic-redirs-nuke' => ['/manage/forum/redirs.php', ['m' => 'explode', 't' => '', 'csrf' => '{csrf}']], 'manage-changelog-changes' => ['/manage/changelog'], 'manage-changelog-change' => ['/manage/changelog/change.php', ['c' => '']], diff --git a/templates/forum/macros.twig b/templates/forum/macros.twig index 4f85af7..7444b04 100644 --- a/templates/forum/macros.twig +++ b/templates/forum/macros.twig @@ -209,24 +209,42 @@ {% endmacro %} -{% macro forum_topic_locked(locked, archived) %} - {% if locked is not null or archived %} -
-
-
- -
-
- {% if archived %} - This topic has been archived. - {% else %} - This topic was locked - . - {% endif %} -
+{% macro forum_topic_notice(icon, body) %} +
+
+
+
+
+ {{ body|raw }} +
+
+{% endmacro %} + +{% macro forum_topic_redirect(redirect) %} + {% from _self import forum_topic_notice %} + {% if redirect is not empty %} + {% set body %} + This topic redirects to {{ redirect.topic_redir_url }}. + {% endset %} + {{ forum_topic_notice('share', body) }} + {% endif %} +{% endmacro %} + +{% macro forum_topic_locked(locked, archived) %} + {% from _self import forum_topic_notice %} + {% if locked is not null or archived %} + {% set body %} + {% if archived %} + This topic has been archived. + {% else %} + This topic was locked + . + {% endif %} + {% endset %} + {{ forum_topic_notice(archived ? 'archive' : 'lock', body) }} {% endif %} {% endmacro %} diff --git a/templates/forum/topic.twig b/templates/forum/topic.twig index f5b01b1..f8ccf70 100644 --- a/templates/forum/topic.twig +++ b/templates/forum/topic.twig @@ -7,7 +7,8 @@ forum_topic_buttons, forum_topic_locked, forum_header, - forum_topic_tools + forum_topic_tools, + forum_topic_redirect %} {% set title = topic_info.topic_title %} @@ -18,7 +19,7 @@ {% set forum_post_csrf = csrf_token() %} {% 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_notice = forum_topic_locked(topic_info.topic_locked, topic_info.topic_archived) ~ forum_topic_redirect(topic_redir_info|default(null)) %} {% set topic_actions = [ { 'html': ' Delete', @@ -54,10 +55,10 @@ {% block content %} {{ forum_header(topic_info.topic_title, topic_breadcrumbs, false, canonical_url, topic_actions) }} - {{ topic_notice }} + {{ topic_notice|raw }} {{ topic_tools }} {{ forum_post_listing(topic_posts, current_user.id|default(0), topic_perms) }} {{ topic_tools }} - {{ topic_notice }} + {{ topic_notice|raw }} {{ forum_header('', topic_breadcrumbs) }} {% endblock %} diff --git a/templates/manage/forum/redirs.twig b/templates/manage/forum/redirs.twig new file mode 100644 index 0000000..370b339 --- /dev/null +++ b/templates/manage/forum/redirs.twig @@ -0,0 +1,84 @@ +{% extends 'manage/users/master.twig' %} +{% from 'macros.twig' import pagination, container_title %} +{% from '_layout/input.twig' import input_csrf, input_text %} + +{% set redirs_pagination = pagination(manage_redirs_pagination, url('manage-forum-topic-redirs')) %} + +{% block manage_content %} +
+ {{ container_title(' Topic Redirects') }} + +
+ Allows you to set redirects for deleted forum topics. +
+ +
+ {{ input_csrf() }} + + + + + +
+ +
+
+ + {% if redirs_pagination|trim|length > 0 %} +
+ {{ redirs_pagination }} +
+ {% endif %} + +
+ + + + + + + + + + + + {% for redir in manage_redirs %} + + + + + + + + {% endfor %} + +
Topic #User #Target URLCreatedOptions
+
{{ redir.topic_id }}
+
+
{{ redir.user_id }}
+
+
{{ redir.topic_redir_url }}
+
+
+ +
+
+ +
+
+ + {% if redirs_pagination|trim|length > 0 %} +
+ {{ redirs_pagination }} +
+ {% endif %} +
+{% endblock %}