From 387ab2ccb4058eaa491c5e3293eee65790ab91ba Mon Sep 17 00:00:00 2001 From: flashwave Date: Fri, 18 May 2018 03:20:27 +0200 Subject: [PATCH] unfinished forum stuff --- assets/less/mio/classes/forum/listing.less | 7 +- assets/less/mio/classes/forum/topics.less | 3 + assets/less/mio/classes/navigation.less | 10 ++ .../2018_05_16_155840_initial_structure.php | 1 - .../2018_05_17_000055_forum_structure.php | 135 ++++++++++++++++++ public/forum/forum.php | 130 +++++++++++++++++ public/forum/index.php | 73 ++++++++++ public/forum/topic.php | 6 + public/index.php | 1 - public/profile.php | 12 +- views/manage/users/roles.twig | 2 +- views/mio/forum/forum.twig | 20 +++ views/mio/forum/index.twig | 27 ++++ views/mio/forum/macros.twig | 115 +++++++++++++++ views/mio/forum/master.twig | 1 + views/mio/forum/topic.twig | 0 views/mio/macros.twig | 5 +- views/mio/master.twig | 1 + views/mio/user/view.twig | 44 ++++-- 19 files changed, 573 insertions(+), 20 deletions(-) create mode 100644 assets/less/mio/classes/forum/topics.less create mode 100644 database/2018_05_17_000055_forum_structure.php create mode 100644 public/forum/forum.php create mode 100644 public/forum/index.php create mode 100644 public/forum/topic.php create mode 100644 views/mio/forum/forum.twig create mode 100644 views/mio/forum/index.twig create mode 100644 views/mio/forum/macros.twig create mode 100644 views/mio/forum/master.twig create mode 100644 views/mio/forum/topic.twig diff --git a/assets/less/mio/classes/forum/listing.less b/assets/less/mio/classes/forum/listing.less index 7c0a96c1..2a99de33 100644 --- a/assets/less/mio/classes/forum/listing.less +++ b/assets/less/mio/classes/forum/listing.less @@ -5,6 +5,10 @@ margin: 0; } + &__none { + padding: 2px 5px; + } + &__entry { display: flex; padding: 2px 0; @@ -16,8 +20,6 @@ &__icon { width: 50px; - line-height: 50px; - font-size: 2.5em; text-align: center; flex-grow: 0; flex-shrink: 0; @@ -26,6 +28,7 @@ &__info { flex-grow: 1; flex-shrink: 1; + padding-left: 6px; } &__title { diff --git a/assets/less/mio/classes/forum/topics.less b/assets/less/mio/classes/forum/topics.less new file mode 100644 index 00000000..efb03206 --- /dev/null +++ b/assets/less/mio/classes/forum/topics.less @@ -0,0 +1,3 @@ +.forum__topics { + // +} diff --git a/assets/less/mio/classes/navigation.less b/assets/less/mio/classes/navigation.less index 0fb10cb4..5fc05b2e 100644 --- a/assets/less/mio/classes/navigation.less +++ b/assets/less/mio/classes/navigation.less @@ -17,6 +17,16 @@ flex-direction: column; } + &--left { + justify-content: left; + padding-left: 25px; + } + + &--right { + justify-content: right; + padding-right: 25px; + } + &--top { border-top-width: 0; border-bottom-width: 1px; diff --git a/database/2018_05_16_155840_initial_structure.php b/database/2018_05_16_155840_initial_structure.php index 873311b3..519867aa 100644 --- a/database/2018_05_16_155840_initial_structure.php +++ b/database/2018_05_16_155840_initial_structure.php @@ -2,7 +2,6 @@ namespace Misuzu\DatabaseMigrations\InitialStructure; use PDO; -use Misuzu\Database; function migrate_up(PDO $conn): void { diff --git a/database/2018_05_17_000055_forum_structure.php b/database/2018_05_17_000055_forum_structure.php new file mode 100644 index 00000000..1bc51d9b --- /dev/null +++ b/database/2018_05_17_000055_forum_structure.php @@ -0,0 +1,135 @@ +exec(" + CREATE TABLE `msz_forum_categories` ( + `forum_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `forum_order` INT(10) UNSIGNED NOT NULL DEFAULT '1', + `forum_parent` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `forum_name` VARCHAR(255) NOT NULL, + `forum_type` TINYINT(4) NOT NULL DEFAULT '0', + `forum_description` TEXT NULL, + `forum_link` VARCHAR(255) NULL DEFAULT NULL, + `forum_created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `forum_archived` TINYINT(1) NOT NULL DEFAULT '0', + `forum_hidden` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`forum_id`), + INDEX `forums_indices` (`forum_order`, `forum_parent`, `forum_type`) + ) + "); + + $conn->exec(" + CREATE TABLE `msz_forum_topics` ( + `topic_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `forum_id` INT(10) UNSIGNED NOT NULL, + `user_id` INT(10) UNSIGNED NULL DEFAULT NULL, + `topic_title` VARCHAR(255) NOT NULL, + `topic_created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `topic_bumped` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `topic_deleted` TIMESTAMP NULL DEFAULT NULL, + `topic_view_count` INT(10) NOT NULL DEFAULT '0', + `topic_type` TINYINT(4) NOT NULL DEFAULT '0', + `topic_status` TINYINT(4) NOT NULL DEFAULT '0', + PRIMARY KEY (`topic_id`), + INDEX `topics_forum_id_foreign` (`forum_id`), + INDEX `topics_user_id_foreign` (`user_id`), + INDEX `topics_indices` (`topic_bumped`, `topic_type`), + CONSTRAINT `topics_forum_id_foreign` + FOREIGN KEY (`forum_id`) + REFERENCES `msz_forum_categories` (`forum_id`) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT `topics_user_id_foreign` + FOREIGN KEY (`user_id`) + REFERENCES `msz_users` (`user_id`) + ON UPDATE CASCADE + ON DELETE CASCADE + ) + "); + + $conn->exec(" + CREATE TABLE `msz_forum_posts` ( + `post_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `topic_id` INT(10) UNSIGNED NOT NULL, + `forum_id` INT(10) UNSIGNED NOT NULL, + `user_id` INT(10) UNSIGNED NULL DEFAULT NULL, + `post_title` VARCHAR(255) NOT NULL, + `post_ip` BLOB NOT NULL, + `post_created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `post_edited` TIMESTAMP NULL DEFAULT NULL, + `post_deleted` TIMESTAMP NULL DEFAULT NULL, + PRIMARY KEY (`post_id`), + INDEX `posts_topic_id_foreign` (`topic_id`), + INDEX `posts_forum_id_foreign` (`forum_id`), + INDEX `posts_user_id_foreign` (`user_id`), + INDEX `posts_indices` (`post_created`), + CONSTRAINT `posts_topic_id_foreign` + FOREIGN KEY (`topic_id`) + REFERENCES `msz_forum_topics` (`topic_id`) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT `posts_forum_id_foreign` + FOREIGN KEY (`forum_id`) + REFERENCES `msz_forum_categories` (`forum_id`) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT `posts_user_id_foreign` + FOREIGN KEY (`user_id`) + REFERENCES `msz_users` (`user_id`) + ON UPDATE CASCADE + ON DELETE SET NULL + ) + "); + + $conn->exec(" + CREATE TABLE `msz_forum_categories_track` ( + `user_id` INT(10) UNSIGNED NOT NULL, + `forum_id` INT(10) UNSIGNED NOT NULL, + `track_last_read` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX `categories_track_forum_id_foreign` (`forum_id`), + INDEX `categories_track_user_id_foreign` (`user_id`), + CONSTRAINT `categories_track_forum_id_foreign` + FOREIGN KEY (`forum_id`) + REFERENCES `msz_forum_categories` (`forum_id`) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT `categories_track_user_id_foreign` + FOREIGN KEY (`user_id`) + REFERENCES `msz_users` (`user_id`) + ON UPDATE CASCADE + ON DELETE CASCADE + ) + "); + + $conn->exec(" + CREATE TABLE `msz_forum_topics_track` ( + `user_id` INT(10) UNSIGNED NOT NULL, + `topic_id` INT(10) UNSIGNED NOT NULL, + `track_last_read` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX `topics_track_topic_id_foreign` (`topic_id`), + INDEX `topics_track_user_id_foreign` (`user_id`), + CONSTRAINT `topics_track_topic_id_foreign` + FOREIGN KEY (`topic_id`) + REFERENCES `msz_forum_topics` (`topic_id`) + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT `topics_track_user_id_foreign` + FOREIGN KEY (`user_id`) + REFERENCES `msz_users` (`user_id`) + ON UPDATE CASCADE + ON DELETE CASCADE + ) + "); +} + +function migrate_down(PDO $conn): void +{ + $conn->exec('DROP TABLE `msz_forum_topics_track`'); + $conn->exec('DROP TABLE `msz_forum_posts`'); + $conn->exec('DROP TABLE `msz_forum_topics`'); + $conn->exec('DROP TABLE `msz_forum_categories`'); +} diff --git a/public/forum/forum.php b/public/forum/forum.php new file mode 100644 index 00000000..eb4e848b --- /dev/null +++ b/public/forum/forum.php @@ -0,0 +1,130 @@ +getTemplating(); + +if ($forumId > 0) { + $getForum = $db->prepare(' + SELECT + `forum_id`, `forum_name`, `forum_type`, `forum_link`, `forum_parent` + FROM `msz_forum_categories` + WHERE `forum_id` = :forum_id + '); + $getForum->bindValue('forum_id', $forumId); + $forum = $getForum->execute() ? $getForum->fetch() : []; +} + +if (empty($forum) || ($forum['forum_type'] == 2 && empty($forum['forum_link']))) { + http_response_code(404); + echo $templating->render('errors.404'); + return; +} + +if ($forum['forum_type'] == 2) { + header('Location: ' . $forum['forum_link']); + return; +} + +// declare this, templating engine assumes it exists +$topics = []; + +// no need to fetch topics for categories (or links but we're already done with those at this point) +if ($forum['forum_type'] == 0) { + $getTopics = $db->prepare(' + SELECT + t.`topic_id`, t.`topic_title`, t.`topic_view_count`, + au.`user_id` as `author_id`, au.`username` as `author_name`, + COUNT(p.`post_id`) as `topic_post_count`, + MIN(p.`post_id`) as `topic_first_post_id`, + MAX(p.`post_id`) as `topic_last_post_id`, + COALESCE(ar.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `author_colour` + FROM `msz_forum_topics` as t + LEFT JOIN `msz_users` as au + ON t.`user_id` = au.`user_id` + LEFT JOIN `msz_roles` as ar + ON ar.`role_id` = au.`display_role` + LEFT JOIN `msz_forum_posts` as p + ON t.`topic_id` = p.`topic_id` + WHERE t.`forum_id` = :forum_id + AND t.`topic_deleted` IS NULL + GROUP BY t.`topic_id` + ORDER BY t.`topic_type`, t.`topic_bumped` + '); + $getTopics->bindValue('forum_id', $forum['forum_id']); + $topics = $getTopics->execute() ? $getTopics->fetchAll() : $topics; +} + +$getSubforums = $db->prepare(' + SELECT + `forum_id`, `forum_name`, `forum_description`, `forum_type`, `forum_link`, + ( + SELECT COUNT(t.`topic_id`) + FROM `msz_forum_topics` as t + WHERE t.`forum_id` = f.`forum_id` + ) as `forum_topic_count`, + ( + SELECT COUNT(p.`post_id`) + FROM `msz_forum_posts` as p + WHERE p.`forum_id` = f.`forum_id` + ) as `forum_post_count` + FROM `msz_forum_categories` as f + WHERE `forum_parent` = :forum_id + AND `forum_hidden` = false +'); +$getSubforums->bindValue('forum_id', $forum['forum_id']); +$forum['forum_subforums'] = $getSubforums->execute() ? $getSubforums->fetchAll() : []; + +if (count($forum['forum_subforums']) > 0) { + // this really, really needs a better name + $getSubSubs = $db->prepare(' + SELECT `forum_id`, `forum_name` + FROM `msz_forum_categories` + WHERE `forum_parent` = :forum_id + AND `forum_hidden` = false + '); + + foreach ($forum['forum_subforums'] as $skey => $subforum) { + $getSubSubs->bindValue('forum_id', $subforum['forum_id']); + $forum['forum_subforums'][$skey]['forum_subforums'] = $getSubSubs->execute() ? $getSubSubs->fetchAll() : []; + } +} + +$lastParent = $forum['forum_parent']; +$breadcrumbs = [$forum['forum_name'] => '/forum/forum.php?f=' . $forum['forum_id']]; +$getBreadcrumb = $db->prepare(' + SELECT `forum_id`, `forum_name`, `forum_parent` + FROM `msz_forum_categories` + WHERE `forum_id` = :forum_id +'); + +while ($lastParent > 0) { + $getBreadcrumb->bindValue('forum_id', $lastParent); + + if (!$getBreadcrumb->execute()) { + break; + } + + $parentForum = $getBreadcrumb->fetch(); + + $breadcrumbs[$parentForum['forum_name']] = '/forum/forum.php?f=' . $parentForum['forum_id']; + $lastParent = $parentForum['forum_parent']; +} + +$breadcrumbs['Forums'] = '/forum/'; +$breadcrumbs = array_reverse($breadcrumbs); + +echo $app->getTemplating()->render('forum.forum', [ + 'forum_info' => $forum, + 'forum_breadcrumbs' => $breadcrumbs, + 'forum_topics' => $topics, +]); diff --git a/public/forum/index.php b/public/forum/index.php new file mode 100644 index 00000000..083f9e87 --- /dev/null +++ b/public/forum/index.php @@ -0,0 +1,73 @@ +query(' + SELECT + f.`forum_id`, f.`forum_name`, f.`forum_type`, + ( + SELECT COUNT(`forum_id`) + FROM `msz_forum_categories` as sf + WHERE sf.`forum_parent` = f.`forum_id` + ) as `forum_children` + FROM `msz_forum_categories` as f + WHERE f.`forum_parent` = 0 + AND f.`forum_type` = 1 + AND f.`forum_hidden` = false + GROUP BY f.`forum_id` + ORDER BY f.`forum_order` +')->fetchAll(); + +$categories = array_merge([ + [ + 'forum_id' => 0, + 'forum_name' => 'Forums', + 'forum_children' => 0, + 'forum_type' => 1, + ], +], $categories); + +$getSubCategories = $db->prepare(' + SELECT + f.`forum_id`, f.`forum_name`, f.`forum_description`, f.`forum_type`, f.`forum_link`, + ( + SELECT COUNT(t.`topic_id`) + FROM `msz_forum_topics` as t + WHERE t.`forum_id` = f.`forum_id` + ) as `forum_topic_count`, + ( + SELECT COUNT(p.`post_id`) + FROM `msz_forum_posts` as p + WHERE p.`forum_id` = f.`forum_id` + ) as `forum_post_count` + FROM `msz_forum_categories` as f + WHERE f.`forum_parent` = :forum_id + AND f.`forum_hidden` = false + AND ((f.`forum_parent` = 0 AND f.`forum_type` != 1) OR f.`forum_parent` != 0) + ORDER BY f.`forum_order` +'); + +foreach ($categories as $key => $category) { + // replace these magic numbers with a constant later, only categories and discussion forums may have subs + if (!in_array($category['forum_type'], [0, 1]) + && ($category['forum_id'] === 0 || $category['forum_children'] > 0)) { + continue; + } + + $getSubCategories->bindValue('forum_id', $category['forum_id']); + $categories[$key]['forum_subforums'] = $getSubCategories->execute() ? $getSubCategories->fetchAll() : []; + + // one level down more! + foreach ($categories[$key]['forum_subforums'] as $skey => $sub) { + $getSubCategories->bindValue('forum_id', $sub['forum_id']); + $categories[$key]['forum_subforums'][$skey]['forum_subforums'] + = $getSubCategories->execute() ? $getSubCategories->fetchAll() : []; + } +} + +$categories[0]['forum_children'] = count($categories[0]['forum_subforums']); + +echo $app->getTemplating()->render('forum.index', [ + 'forum_categories' => $categories, +]); diff --git a/public/forum/topic.php b/public/forum/topic.php new file mode 100644 index 00000000..cedc4248 --- /dev/null +++ b/public/forum/topic.php @@ -0,0 +1,6 @@ +getTemplating()->render('forum.topic'); diff --git a/public/index.php b/public/index.php index 07f08830..c1921365 100644 --- a/public/index.php +++ b/public/index.php @@ -1,6 +1,5 @@ - Create new Role + Create new Role
diff --git a/views/mio/forum/forum.twig b/views/mio/forum/forum.twig new file mode 100644 index 00000000..e8827c72 --- /dev/null +++ b/views/mio/forum/forum.twig @@ -0,0 +1,20 @@ +{% extends '@mio/forum/master.twig' %} +{% from '@mio/macros.twig' import navigation %} +{% from '@mio/forum/macros.twig' import forum_category_listing, forum_topic_listing %} + +{% set title = forum_info.forum_name %} +{% set canonical_url = '/forum/forum.php?f=' ~ forum_info.forum_id %} + +{% block content %} + {{ navigation(forum_breadcrumbs, forum_breadcrumbs|last, true, null, 'left') }} + + {% if forum_info.forum_subforums|length > 0 or forum_info.forum_type == 1 %} + {{ forum_category_listing(forum_info.forum_subforums, 'Forums') }} + {% endif %} + + {% if forum_info.forum_type == 0 %} + {{ forum_topic_listing(forum_topics) }} + {% endif %} + + {{ navigation(mio_navigation, '/forum/') }} +{% endblock %} diff --git a/views/mio/forum/index.twig b/views/mio/forum/index.twig new file mode 100644 index 00000000..e040a86d --- /dev/null +++ b/views/mio/forum/index.twig @@ -0,0 +1,27 @@ +{% extends '@mio/forum/master.twig' %} +{% from '@mio/macros.twig' import navigation %} +{% from '@mio/forum/macros.twig' import forum_category_listing %} + +{% set title = 'Forum Listing' %} +{% set canonical_url = '/forum/' %} + +{% block content %} + {% for category in forum_categories %} + {% if category.forum_children > 0 %} + {{ forum_category_listing(category.forum_subforums, category.forum_name) }} + {% endif %} + {% endfor %} + +
+
Actions
+
+ + + + +
+
+ + {{ navigation(mio_navigation, '/forum/') }} +{% endblock %} + diff --git a/views/mio/forum/macros.twig b/views/mio/forum/macros.twig new file mode 100644 index 00000000..ef628629 --- /dev/null +++ b/views/mio/forum/macros.twig @@ -0,0 +1,115 @@ +{% macro forum_category_listing(forums, title) %} + {% from _self import forum_category_entry %} + +
+
{{ title }}
+
+ {% if forums|length > 0 %} + {% for forum in forums %} + {{ forum_category_entry(forum) }} + {% endfor %} + {% else %} +
+ This category is empty. +
+ {% endif %} +
+
+{% endmacro %} + +{% macro forum_category_entry(forum, forum_icon) %} + {% set forum_icon = forum_icon|default(null) %} + + {% if forum_icon is null %} + {% if forum.forum_archived is defined and forum.forum_archived %} + {% set forum_icon = 'https://static.flash.moe/images/forum-icons/default-archived-%s.png' %} + {% elseif forum.forum_type is defined %} + {% if forum.forum_type == 2 %} + {% set forum_icon = 'https://static.flash.moe/images/forum-icons/default-link-%s.png' %} + {% elseif forum.forum_type == 1 %} + {% set forum_icon = 'https://static.flash.moe/images/forum-icons/default-category-%s.png' %} + {% endif %} + {% endif %} + + {% set forum_icon = forum_icon|default('https://static.flash.moe/images/forum-icons/default-forum-%s.png') %} + {% endif %} + +
+
+ read +
+ + + +
+
{{ forum.forum_topic_count|number_format }}
+
{{ forum.forum_post_count|number_format }}
+
+ +
+
+ There are no posts in this forum yet. +
+
+
+{% endmacro %} + +{% macro forum_topic_listing(topics) %} + {% from _self import forum_topic_entry %} + +
+
Topics
+
+ {% if topics|length > 0 %} + {% for topic in topics %} + {{ forum_topic_entry(topic) }} + {% endfor %} + {% else %} +
+ There are no topics in this forum. +
+ {% endif %} +
+
+{% endmacro %} + +{% macro forum_topic_entry(topic) %} +
+
+ eek +
+ +
+ + {% if topic.author_id is not null %} + + {% endif %} +
+ +
+
{{ topic.topic_post_count }}
+
{{ topic.topic_view_count }}
+
+ +
+ last post data required here, display "no replies" when only one post is present +
+
+{% endmacro %} diff --git a/views/mio/forum/master.twig b/views/mio/forum/master.twig new file mode 100644 index 00000000..187fe3a8 --- /dev/null +++ b/views/mio/forum/master.twig @@ -0,0 +1 @@ +{% extends '@mio/master.twig' %} diff --git a/views/mio/forum/topic.twig b/views/mio/forum/topic.twig new file mode 100644 index 00000000..e69de29b diff --git a/views/mio/macros.twig b/views/mio/macros.twig index ee077b39..aa746fbc 100644 --- a/views/mio/macros.twig +++ b/views/mio/macros.twig @@ -4,12 +4,13 @@ {% endspaceless %} {% endmacro %} -{% macro navigation(links, current, top, fmt) %} +{% macro navigation(links, current, top, fmt, align) %} {% set top = top|default(false) == true %} + {% set align = align|default('centre') %} {% set current = current|default(null) %} {% set fmt = fmt|default('%s') %} -
- {# if profile.last_seen.timestamp > 0 #} -
-
- Last Seen + {% if profile.last_seen is not null %} +
+
+ Last Seen +
+
+ {#{% if profile.last_seen.addMinute.timestamp >= ''|date('U') %} + Online now + {% else %} + {{ profile.last_seen }}{# .diffForHumans #\} + {% endif %}#} + {{ profile.last_seen }} +
-
- {#{% if profile.last_seen.addMinute.timestamp >= ''|date('U') %} - Online now - {% else %} - {{ profile.last_seen }}{# .diffForHumans #\} - {% endif %}#} - {{ profile.last_seen }} + {% endif %} +
+ +
+
+
+ Topics +
+
+ {{ profile.forum_topic_count }} +
+
+ +
+
+ Posts +
+
+ {{ profile.forum_post_count }}
- {# endif #}