diff --git a/assets/less/forum/topic.less b/assets/less/forum/topic.less index afc26a16..23d8cb5c 100644 --- a/assets/less/forum/topic.less +++ b/assets/less/forum/topic.less @@ -88,6 +88,17 @@ box-shadow: 0 1px 2px #111; pointer-events: initial; } + + &__priority { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + line-height: 30px; + font-size: .9em; + text-align: center; + } } &__details { diff --git a/database/2019_05_07_090631_create_feature_forum_type.php b/database/2019_05_07_090631_create_feature_forum_type.php new file mode 100644 index 00000000..e6637b2f --- /dev/null +++ b/database/2019_05_07_090631_create_feature_forum_type.php @@ -0,0 +1,35 @@ +exec(" + CREATE TABLE `msz_forum_topics_priority` ( + `topic_id` INT(10) UNSIGNED NOT NULL, + `user_id` INT(10) UNSIGNED NOT NULL, + `topic_priority` SMALLINT(6) NOT NULL, + UNIQUE INDEX `forum_topics_priority_unique` (`topic_id`, `user_id`), + INDEX `forum_topics_priority_topic_foreign` (`topic_id`), + INDEX `forum_topics_priority_user_foreign` (`user_id`), + CONSTRAINT `forum_topics_priority_topic_foreign` + FOREIGN KEY (`topic_id`) + REFERENCES `msz_forum_topics` (`topic_id`) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT `forum_topics_priority_user_foreign` + FOREIGN KEY (`user_id`) + REFERENCES `msz_users` (`user_id`) + ON UPDATE CASCADE + ON DELETE CASCADE + ) COLLATE='utf8mb4_bin' ENGINE=InnoDB; + "); +} + +function migrate_down(PDO $conn): void +{ + $conn->exec(" + DROP TABLE ... + "); +} diff --git a/public/forum/forum.php b/public/forum/forum.php index 57a35436..50863b40 100644 --- a/public/forum/forum.php +++ b/public/forum/forum.php @@ -51,7 +51,8 @@ $topics = $forumMayHaveTopics $forumUserId, $topicsOffset, $forumPagination['range'], - perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST) + perms_check($perms, MSZ_FORUM_PERM_DELETE_ANY_POST), + forum_has_priority_voting($forum['forum_type']) ) : []; diff --git a/src/Forum/forum.php b/src/Forum/forum.php index 1b310536..fcb0c464 100644 --- a/src/Forum/forum.php +++ b/src/Forum/forum.php @@ -47,19 +47,27 @@ define( define('MSZ_FORUM_TYPE_DISCUSSION', 0); define('MSZ_FORUM_TYPE_CATEGORY', 1); define('MSZ_FORUM_TYPE_LINK', 2); +define('MSZ_FORUM_TYPE_FEATURE', 3); define('MSZ_FORUM_TYPES', [ MSZ_FORUM_TYPE_DISCUSSION, MSZ_FORUM_TYPE_CATEGORY, MSZ_FORUM_TYPE_LINK, + MSZ_FORUM_TYPE_FEATURE, ]); define('MSZ_FORUM_MAY_HAVE_CHILDREN', [ MSZ_FORUM_TYPE_DISCUSSION, MSZ_FORUM_TYPE_CATEGORY, + MSZ_FORUM_TYPE_FEATURE, ]); define('MSZ_FORUM_MAY_HAVE_TOPICS', [ MSZ_FORUM_TYPE_DISCUSSION, + MSZ_FORUM_TYPE_FEATURE, +]); + +define('MSZ_FORUM_HAS_PRIORITY_VOTING', [ + MSZ_FORUM_TYPE_FEATURE, ]); define('MSZ_FORUM_ROOT', 0); @@ -87,6 +95,11 @@ function forum_may_have_topics(int $forumType): bool return in_array($forumType, MSZ_FORUM_MAY_HAVE_TOPICS); } +function forum_has_priority_voting(int $forumType): bool +{ + return in_array($forumType, MSZ_FORUM_HAS_PRIORITY_VOTING); +} + function forum_get(int $forumId, bool $showDeleted = false): array { $getForum = db_prepare(sprintf( diff --git a/src/Forum/topic.php b/src/Forum/topic.php index 89d64c6e..7ecbc180 100644 --- a/src/Forum/topic.php +++ b/src/Forum/topic.php @@ -185,15 +185,19 @@ function forum_topic_mark_read(int $userId, int $topicId, int $forumId): void } } -function forum_topic_listing(int $forumId, int $userId, int $offset = 0, int $take = 0, bool $showDeleted = false): array -{ +function forum_topic_listing( + int $forumId, int $userId, + int $offset = 0, int $take = 0, + bool $showDeleted = false, bool $sortByPriority = false +): array { $hasPagination = $offset >= 0 && $take > 0; $getTopics = db_prepare(sprintf( ' SELECT :user_id AS `target_user_id`, t.`topic_id`, t.`topic_title`, t.`topic_locked`, t.`topic_type`, t.`topic_created`, - t.`topic_bumped`, t.`topic_deleted`, t.`topic_count_views`, + t.`topic_bumped`, t.`topic_deleted`, t.`topic_count_views`, f.`forum_type`, + COALESCE(SUM(tp.`topic_priority`), 0) AS `topic_priority`, au.`user_id` AS `author_id`, au.`username` AS `author_name`, COALESCE(au.`user_colour`, ar.`role_colour`) AS `author_colour`, lp.`post_id` AS `response_id`, @@ -236,6 +240,10 @@ function forum_topic_listing(int $forumId, int $userId, int $offset = 0, int $ta LIMIT 1 ) AS `topic_participated` FROM `msz_forum_topics` AS t + LEFT JOIN `msz_forum_topics_priority` AS tp + ON tp.`topic_id` = t.`topic_id` + LEFT JOIN `msz_forum_categories` AS f + ON f.`forum_id` = t.`forum_id` LEFT JOIN `msz_users` AS au ON t.`user_id` = au.`user_id` LEFT JOIN `msz_roles` AS ar @@ -258,7 +266,8 @@ function forum_topic_listing(int $forumId, int $userId, int $offset = 0, int $ta OR t.`topic_type` = %3$d ) %1$s - ORDER BY FIELD(t.`topic_type`, %4$s) DESC, t.`topic_bumped` DESC + GROUP BY t.`topic_id` + ORDER BY FIELD(t.`topic_type`, %4$s) DESC, %7$s t.`topic_bumped` DESC %2$s ', $showDeleted ? '' : 'AND t.`topic_deleted` IS NULL', @@ -266,7 +275,8 @@ function forum_topic_listing(int $forumId, int $userId, int $offset = 0, int $ta MSZ_TOPIC_TYPE_GLOBAL_ANNOUNCEMENT, implode(',', array_reverse(MSZ_TOPIC_TYPE_ORDER)), $showDeleted ? '' : 'AND `post_deleted` IS NULL', - MSZ_FORUM_POSTS_PER_PAGE + MSZ_FORUM_POSTS_PER_PAGE, + $sortByPriority ? '`topic_priority` DESC,' : '' )); $getTopics->bindValue('forum_id', $forumId); $getTopics->bindValue('user_id', $userId); diff --git a/src/TwigMisuzu.php b/src/TwigMisuzu.php index d810fa8c..9bf40400 100644 --- a/src/TwigMisuzu.php +++ b/src/TwigMisuzu.php @@ -46,7 +46,9 @@ final class TwigMisuzu extends Twig_Extension new Twig_Function('get_csrf_tokens', 'csrf_get_list'), new Twig_Function('url', 'url'), new Twig_Function('url_list', 'url_list'), - new Twig_Function('changelog_action_name', 'changelog_action_name'), + new Twig_Function('forum_may_have_children', 'forum_may_have_children'), + new Twig_Function('forum_may_have_topics', 'forum_may_have_topics'), + new Twig_Function('forum_has_priority_voting', 'forum_has_priority_voting'), new Twig_Function('startup_time', function (float $time = MSZ_STARTUP) { return microtime(true) - $time; }), diff --git a/templates/forum/macros.twig b/templates/forum/macros.twig index c3d36a1a..8098ff15 100644 --- a/templates/forum/macros.twig +++ b/templates/forum/macros.twig @@ -76,7 +76,7 @@
{% if can_topic %} - New Topic + {{ info.forum_type == constant('MSZ_FORUM_TYPE_FEATURE') ? 'New Request' : 'New Topic' }} {% endif %}
@@ -116,7 +116,9 @@ {% elseif forum.forum_archived is defined and forum.forum_archived %} {% set forum_icon = 'fas fa-archive fa-fw' %} {% elseif forum.forum_type is defined and forum.forum_type != constant('MSZ_FORUM_TYPE_DISCUSSION') %} - {% if forum.forum_type == constant('MSZ_FORUM_TYPE_LINK') %} + {% if forum.forum_type == constant('MSZ_FORUM_TYPE_FEATURE') %} + {% set forum_icon = 'fas fa-star fa-fw' %} + {% elseif forum.forum_type == constant('MSZ_FORUM_TYPE_LINK') %} {% set forum_icon = 'fas fa-link fa-fw' %} {% elseif forum.forum_type == constant('MSZ_FORUM_TYPE_CATEGORY') %} {% set forum_icon = 'fas fa-folder fa-fw' %} @@ -161,14 +163,14 @@
{{ forum.forum_link_clicks|number_format }}
{% endif %} - {% elseif forum.forum_type != constant('MSZ_FORUM_TYPE_CATEGORY') %} + {% elseif forum_may_have_children(forum.forum_type) %}
{{ forum.forum_count_topics|number_format }}
{{ forum.forum_count_posts|number_format }}
{% endif %} - {% if forum.forum_type == constant('MSZ_FORUM_TYPE_DISCUSSION') or forum.forum_link_clicks is not null %} + {% if forum_may_have_topics(forum.forum_type) or forum.forum_link_clicks is not null %} {% endmacro %} -{% macro forum_topic_entry(topic, topic_type, topic_unread) %} - {% set topic_type = topic_type|default(null) %} +{% macro forum_topic_entry(topic, topic_icon, topic_unread) %} {% set topic_unread = topic_unread|default(topic.topic_unread|default(false)) %} {% set topic_important = topic.topic_type == constant('MSZ_TOPIC_TYPE_STICKY') or topic.topic_type == constant('MSZ_TOPIC_TYPE_ANNOUNCEMENT') or topic.topic_type == constant('MSZ_TOPIC_TYPE_GLOBAL_ANNOUNCEMENT') %} - {% if topic_type is null %} + {% if topic_icon is null %} {% if topic.topic_deleted is defined and topic.topic_deleted is not null %} - {% set topic_type = 'fas fa-trash-alt' %} + {% set topic_icon = 'fas fa-trash-alt' %} {% elseif topic.topic_type is defined and topic.topic_type != constant('MSZ_TOPIC_TYPE_DISCUSSION') %} {% if topic.topic_type == constant('MSZ_TOPIC_TYPE_ANNOUNCEMENT') or topic.topic_type == constant('MSZ_TOPIC_TYPE_GLOBAL_ANNOUNCEMENT') %} - {% set topic_type = 'fas fa-bullhorn' %} + {% set topic_icon = 'fas fa-bullhorn' %} {% elseif topic.topic_type == constant('MSZ_TOPIC_TYPE_STICKY') %} - {% set topic_type = 'fas fa-thumbtack' %} + {% set topic_icon = 'fas fa-thumbtack' %} {% endif %} {% elseif topic.topic_locked is defined and topic.topic_locked is not null %} - {% set topic_type = 'fas fa-lock' %} + {% set topic_icon = 'fas fa-lock' %} + {% elseif forum_has_priority_voting(topic.forum_type) %} + {% set topic_icon = 'far fa-star' %} {% else %} - {% set topic_type = (topic_unread ? 'fas' : 'far') ~ ' fa-comment' %} + {% set topic_icon = (topic_unread ? 'fas' : 'far') ~ ' fa-comment' %} {% endif %} {% endif %} @@ -277,7 +280,11 @@
- + + + {% if forum_has_priority_voting(topic.forum_type) %} +
{{ topic.topic_priority|number_format }}
+ {% endif %} {% if topic.topic_participated %}