Cleaned up forum index, listing and topic page as well as added read tracking.

This commit is contained in:
flash 2018-05-24 02:38:42 +02:00
parent cd9e72d3c0
commit 8b2e174d40
10 changed files with 412 additions and 288 deletions

View file

@ -63,6 +63,10 @@
&:hover {
text-decoration: underline;
}
&--unread {
font-weight: 700;
}
}
&__stats {
@ -88,6 +92,7 @@
&__activity {
min-width: 270px;
min-height: 50px;
align-items: center;
@media (max-width: @mio-forum-listing-mobile) {

View file

@ -34,7 +34,6 @@ function migrate_up(PDO $conn): void
`topic_bumped` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`topic_deleted` TIMESTAMP NULL DEFAULT NULL,
`topic_locked` TIMESTAMP NULL DEFAULT NULL,
`topic_view_count` INT(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`topic_id`),
INDEX `topics_forum_id_foreign` (`forum_id`),
INDEX `topics_user_id_foreign` (`user_id`),
@ -86,33 +85,16 @@ function migrate_up(PDO $conn): void
)
");
$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,
`forum_id` INT(10) UNSIGNED NOT NULL,
`track_last_read` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE INDEX `topics_track_unique` (`user_id`, `topic_id`),
INDEX `topics_track_topic_id_foreign` (`topic_id`),
INDEX `topics_track_user_id_foreign` (`user_id`),
INDEX `topics_track_forum_id_foreign` (`forum_id`),
CONSTRAINT `topics_track_topic_id_foreign`
FOREIGN KEY (`topic_id`)
REFERENCES `msz_forum_topics` (`topic_id`)
@ -122,6 +104,11 @@ function migrate_up(PDO $conn): void
FOREIGN KEY (`user_id`)
REFERENCES `msz_users` (`user_id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `topics_track_forum_id_foreign`
FOREIGN KEY (`forum_id`)
REFERENCES `msz_forum_categories` (`forum_id`)
ON UPDATE CASCADE
ON DELETE CASCADE
)
");

View file

@ -12,22 +12,8 @@ if ($forumId === 0) {
exit;
}
$db = Database::connection();
$templating = $app->getTemplating();
$getForum = $db->prepare('
SELECT
`forum_id`, `forum_name`, `forum_type`, `forum_link`, `forum_link_clicks`, `forum_parent`,
(
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
) as `forum_topic_count`
FROM `msz_forum_categories` as f
WHERE `forum_id` = :forum_id
');
$getForum->bindValue('forum_id', $forumId);
$forum = $getForum->execute() ? $getForum->fetch() : [];
$forum = forum_fetch($forumId);
if (empty($forum) || ($forum['forum_type'] == MSZ_FORUM_TYPE_LINK && empty($forum['forum_link']))) {
http_response_code(404);
@ -41,114 +27,15 @@ if ($forum['forum_type'] == MSZ_FORUM_TYPE_LINK) {
return;
}
// declare this, templating engine assumes it exists
$topics = [];
$topics = forum_may_have_topics($forum['forum_type'])
? forum_topic_listing($forum['forum_id'], $app->getUserId(), $topicsOffset, $topicsRange)
: [];
// no need to fetch topics for categories (or links but we're already done with those at this point)
if ($forum['forum_type'] == MSZ_FORUM_TYPE_DISCUSSION) {
$getTopics = $db->prepare('
SELECT
t.`topic_id`, t.`topic_title`, t.`topic_view_count`, t.`topic_locked`, t.`topic_type`, t.`topic_created`,
au.`user_id` as `author_id`, au.`username` as `author_name`,
COALESCE(ar.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `author_colour`,
lp.`post_id` as `response_id`,
lp.`post_created` as `response_created`,
lu.`user_id` as `respondent_id`,
lu.`username` as `respondent_name`,
COALESCE(lr.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `respondent_colour`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
) as `topic_post_count`
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 lp
ON lp.`post_id` = (
SELECT `post_id`
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
ORDER BY `post_id` DESC
LIMIT 1
)
LEFT JOIN `msz_users` as lu
ON lu.`user_id` = lp.`user_id`
LEFT JOIN `msz_roles` as lr
ON lr.`role_id` = lu.`display_role`
WHERE t.`forum_id` = :forum_id
AND t.`topic_deleted` IS NULL
ORDER BY t.`topic_type` DESC, t.`topic_bumped` DESC
LIMIT :offset, :take
');
$getTopics->bindValue('forum_id', $forum['forum_id']);
$getTopics->bindValue('offset', $topicsOffset);
$getTopics->bindValue('take', $topicsRange);
$topics = $getTopics->execute() ? $getTopics->fetchAll() : $topics;
}
$forum['forum_subforums'] = forum_get_children($forum['forum_id'], $app->getUserId());
$getSubforums = $db->prepare('
SELECT
f.`forum_id`, f.`forum_name`, f.`forum_description`, f.`forum_type`, f.`forum_link`, f.`forum_archived`,
t.`topic_id` as `recent_topic_id`, p.`post_id` as `recent_post_id`,
t.`topic_title` as `recent_topic_title`,
p.`post_created` as `recent_post_created`,
u.`user_id` as `recent_post_user_id`,
u.`username` as `recent_post_username`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `recent_post_user_colour`,
(
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
LEFT JOIN `msz_forum_topics` as t
ON t.`topic_id` = (
SELECT `topic_id`
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
AND `topic_deleted` IS NULL
ORDER BY `topic_bumped` DESC
LIMIT 1
)
LEFT JOIN `msz_forum_posts` as p
ON p.`post_id` = (
SELECT `post_id`
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
ORDER BY `post_id` DESC
LIMIT 1
)
LEFT JOIN `msz_users` as u
ON u.`user_id` = p.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
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() : [];
}
foreach ($forum['forum_subforums'] as $skey => $subforum) {
$forum['forum_subforums'][$skey]['forum_subforums']
= forum_get_children($subforum['forum_id'], $app->getUserId(), true);
}
echo $app->getTemplating()->render('forum.forum', [

View file

@ -3,100 +3,21 @@ use Misuzu\Database;
require_once __DIR__ . '/../../misuzu.php';
$db = Database::connection();
$categories = $db->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
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`, f.`forum_link_clicks`, f.`forum_archived`,
t.`topic_id` as `recent_topic_id`, p.`post_id` as `recent_post_id`,
t.`topic_title` as `recent_topic_title`,
p.`post_created` as `recent_post_created`,
u.`user_id` as `recent_post_user_id`,
u.`username` as `recent_post_username`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `recent_post_user_colour`,
(
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
) as `forum_topic_count`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `forum_id` = f.`forum_id`
) as `forum_post_count`
FROM `msz_forum_categories` as f
LEFT JOIN `msz_forum_topics` as t
ON t.`topic_id` = (
SELECT `topic_id`
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
AND `topic_deleted` IS NULL
ORDER BY `topic_bumped` DESC
LIMIT 1
)
LEFT JOIN `msz_forum_posts` as p
ON p.`post_id` = (
SELECT `post_id`
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
ORDER BY `post_id` DESC
LIMIT 1
)
LEFT JOIN `msz_users` as u
ON u.`user_id` = p.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
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`
');
$categories = forum_get_root_categories();
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)) {
$categories[$key]['forum_subforums'] = forum_get_children($category['forum_id'], $app->getUserId());
foreach ($categories[$key]['forum_subforums'] as $skey => $sub) {
if (!forum_may_have_children($sub['forum_type'])) {
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() : [];
= forum_get_children($sub['forum_id'], $app->getUserId(), true);
}
}
$categories[0]['forum_children'] = count($categories[0]['forum_subforums']);
echo $app->getTemplating()->render('forum.index', [
'forum_categories' => $categories,
]);

View file

@ -125,6 +125,7 @@ if ($postRequest) {
IPAddress::remote()->getString(),
$postText
);
forum_topic_mark_read($app->getUserId(), $topicId, $forum['forum_id']);
header("Location: /forum/topic.php?p={$postId}#p{$postId}");
return;

View file

@ -1,9 +1,6 @@
<?php
use Misuzu\Database;
require_once __DIR__ . '/../../misuzu.php';
$db = Database::connection();
$templating = $app->getTemplating();
$postId = (int)($_GET['p'] ?? 0);
@ -11,7 +8,6 @@ $topicId = (int)($_GET['t'] ?? 0);
$postsOffset = max((int)($_GET['o'] ?? 0), 0);
$postsRange = max(min((int)($_GET['r'] ?? 10), 25), 5);
// find topic id
if ($topicId < 1 && $postId > 0) {
$postInfo = forum_post_find($postId);
@ -21,28 +17,7 @@ if ($topicId < 1 && $postId > 0) {
}
}
$getTopic = $db->prepare('
SELECT
t.`topic_id`, t.`forum_id`, t.`topic_title`, t.`topic_type`, t.`topic_locked`,
f.`forum_archived` as `topic_archived`,
(
SELECT MIN(`post_id`)
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
) as `topic_first_post_id`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
) as `topic_post_count`
FROM `msz_forum_topics` as t
LEFT JOIN `msz_forum_categories` as f
ON f.`forum_id` = t.`forum_id`
WHERE t.`topic_id` = :topic_id
AND t.`topic_deleted` IS NULL
');
$getTopic->bindValue('topic_id', $topicId);
$topic = $getTopic->execute() ? $getTopic->fetch() : false;
$topic = forum_topic_fetch($topicId);
if (!$topic) {
http_response_code(404);
@ -50,28 +25,7 @@ if (!$topic) {
return;
}
$getPosts = $db->prepare('
SELECT
p.`post_id`, p.`post_text`, p.`post_created`,
p.`topic_id`,
u.`user_id` as `poster_id`,
u.`username` as `poster_name`,
u.`created_at` as `poster_joined`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `poster_colour`
FROM `msz_forum_posts` as p
LEFT JOIN `msz_users` as u
ON u.`user_id` = p.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
WHERE `topic_id` = :topic_id
AND `post_deleted` IS NULL
ORDER BY `post_id`
LIMIT :offset, :take
');
$getPosts->bindValue('topic_id', $topic['topic_id']);
$getPosts->bindValue('offset', $postsOffset);
$getPosts->bindValue('take', $postsRange);
$posts = $getPosts->execute() ? $getPosts->fetchAll() : [];
$posts = forum_post_listing($topic['topic_id'], $postsOffset, $postsRange);
if (!$posts) {
http_response_code(404);
@ -79,6 +33,8 @@ if (!$posts) {
return;
}
forum_topic_mark_read($app->getUserId(), $topic['topic_id'], $topic['forum_id']);
echo $templating->render('forum.topic', [
'topic_breadcrumbs' => forum_get_breadcrumbs($topic['forum_id']),
'topic_info' => $topic,

View file

@ -10,6 +10,81 @@ define('MSZ_FORUM_TYPES', [
MSZ_FORUM_TYPE_LINK,
]);
define('MSZ_FORUM_MAY_HAVE_CHILDREN', [
MSZ_FORUM_TYPE_DISCUSSION,
MSZ_FORUM_TYPE_CATEGORY,
]);
define('MSZ_FORUM_MAY_HAVE_TOPICS', [
MSZ_FORUM_TYPE_DISCUSSION,
]);
define('MSZ_FORUM_ROOT', 0);
define('MSZ_FORUM_ROOT_DATA', [ // should be compatible with the data fetched in forum_get_root_categories
'forum_id' => 0,
'forum_name' => 'Forums',
'forum_children' => 0,
'forum_type' => MSZ_FORUM_TYPE_CATEGORY,
]);
function forum_may_have_children(int $forumType): bool
{
return in_array($forumType, MSZ_FORUM_MAY_HAVE_CHILDREN);
}
function forum_may_have_topics(int $forumType): bool
{
return in_array($forumType, MSZ_FORUM_MAY_HAVE_TOPICS);
}
function forum_fetch(int $forumId): array
{
$getForum = Database::connection()->prepare('
SELECT
`forum_id`, `forum_name`, `forum_type`, `forum_link`, `forum_link_clicks`, `forum_parent`,
(
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
) as `forum_topic_count`
FROM `msz_forum_categories` as f
WHERE `forum_id` = :forum_id
');
$getForum->bindValue('forum_id', $forumId);
return $getForum->execute() ? $getForum->fetch() : [];
}
function forum_get_root_categories(): array
{
$dbc = Database::connection();
$categories = $dbc->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
ORDER BY f.`forum_order`
')->fetchAll();
$categories = array_merge([MSZ_FORUM_ROOT_DATA], $categories);
$categories[0]['forum_children'] = (int)$dbc->query('
SELECT COUNT(`forum_id`)
FROM `msz_forum_categories`
WHERE `forum_parent` = ' . MSZ_FORUM_ROOT . '
')->fetchColumn();
return $categories;
}
function forum_get_breadcrumbs(
int $forumId,
string $linkFormat = '/forum/forum.php?f=%d',
@ -22,7 +97,7 @@ function forum_get_breadcrumbs(
WHERE `forum_id` = :forum_id
');
while ($forumId > 0) {
while ($forumId > MSZ_FORUM_ROOT) {
$getBreadcrumb->bindValue('forum_id', $forumId);
$breadcrumb = $getBreadcrumb->execute() ? $getBreadcrumb->fetch() : [];
@ -43,9 +118,138 @@ function forum_increment_clicks(int $forumId): void
UPDATE `msz_forum_categories`
SET `forum_link_clicks` = `forum_link_clicks` + 1
WHERE `forum_id` = :forum_id
AND `forum_type` = 2
AND `forum_type` = ' . MSZ_FORUM_TYPE_LINK . '
AND `forum_link_clicks` IS NOT NULL
');
$incrementLinkClicks->bindValue('forum_id', $forumId);
$incrementLinkClicks->execute();
}
define('MSZ_FORUM_GET_CHILDREN_QUERY_SMALL', '
SELECT
:user_id as `target_user_id`,
f.`forum_id`, f.`forum_name`,
(
SELECT
`target_user_id` > 0
AND
t.`topic_id` IS NOT NULL
AND
t.`topic_bumped` >= NOW() - INTERVAL 1 MONTH
AND (
SELECT COUNT(ti.`topic_id`) < (
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
AND `topic_bumped` >= NOW() - INTERVAL 1 MONTH
AND `topic_deleted` IS NULL
)
FROM `msz_forum_topics_track` as tt
RIGHT JOIN `msz_forum_topics` as ti
ON ti.`topic_id` = tt.`topic_id`
WHERE ti.`forum_id` = f.`forum_id`
AND tt.`user_id` = `target_user_id`
AND `track_last_read` >= `topic_bumped`
)
) as `forum_unread`
FROM `msz_forum_categories` as f
LEFT JOIN `msz_forum_topics` as t
ON t.`topic_id` = (
SELECT `topic_id`
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
AND `topic_deleted` IS NULL
ORDER BY `topic_bumped` DESC
LIMIT 1
)
WHERE `forum_parent` = :parent_id
AND `forum_hidden` = false
ORDER BY f.`forum_order`
');
define('MSZ_FORUM_GET_CHILDREN_QUERY_STANDARD', '
SELECT
:user_id as `target_user_id`,
f.`forum_id`, f.`forum_name`, f.`forum_description`, f.`forum_type`,
f.`forum_link`, f.`forum_link_clicks`, f.`forum_archived`,
t.`topic_id` as `recent_topic_id`, p.`post_id` as `recent_post_id`,
t.`topic_title` as `recent_topic_title`, t.`topic_bumped` as `recent_topic_bumped`,
p.`post_created` as `recent_post_created`,
u.`user_id` as `recent_post_user_id`,
u.`username` as `recent_post_username`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `recent_post_user_colour`,
(
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
) as `forum_topic_count`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `forum_id` = f.`forum_id`
) as `forum_post_count`,
(
SELECT
`target_user_id` > 0
AND
`recent_topic_id` IS NOT NULL
AND
`recent_topic_bumped` >= NOW() - INTERVAL 1 MONTH
AND (
SELECT COUNT(ti.`topic_id`) < (
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
AND `topic_bumped` >= NOW() - INTERVAL 1 MONTH
AND `topic_deleted` IS NULL
)
FROM `msz_forum_topics_track` as tt
RIGHT JOIN `msz_forum_topics` as ti
ON ti.`topic_id` = tt.`topic_id`
WHERE ti.`forum_id` = f.`forum_id`
AND tt.`user_id` = `target_user_id`
AND `track_last_read` >= `topic_bumped`
)
) as `forum_unread`
FROM `msz_forum_categories` as f
LEFT JOIN `msz_forum_topics` as t
ON t.`topic_id` = (
SELECT `topic_id`
FROM `msz_forum_topics`
WHERE `forum_id` = f.`forum_id`
AND `topic_deleted` IS NULL
ORDER BY `topic_bumped` DESC
LIMIT 1
)
LEFT JOIN `msz_forum_posts` as p
ON p.`post_id` = (
SELECT `post_id`
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
ORDER BY `post_id` DESC
LIMIT 1
)
LEFT JOIN `msz_users` as u
ON u.`user_id` = p.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
WHERE f.`forum_parent` = :parent_id
AND f.`forum_hidden` = false
AND (
(f.`forum_parent` = ' . MSZ_FORUM_ROOT . ' AND f.`forum_type` != ' . MSZ_FORUM_TYPE_CATEGORY . ')
OR f.`forum_parent` != ' . MSZ_FORUM_ROOT . '
)
ORDER BY f.`forum_order`
');
function forum_get_children(int $parentId, int $userId, bool $small = false): array
{
$getListing = Database::connection()->prepare(
$small
? MSZ_FORUM_GET_CHILDREN_QUERY_SMALL
: MSZ_FORUM_GET_CHILDREN_QUERY_STANDARD
);
$getListing->bindValue('user_id', $userId);
$getListing->bindValue('parent_id', $parentId);
return $getListing->execute() ? $getListing->fetchAll() : [];
}

View file

@ -47,3 +47,40 @@ function forum_post_find(int $postId): array
return $getPostInfo->execute() ? $getPostInfo->fetch() : false;
}
define('MSZ_FORUM_POST_LISTING_QUERY_STANDARD', '
SELECT
p.`post_id`, p.`post_text`, p.`post_created`,
p.`topic_id`,
u.`user_id` as `poster_id`,
u.`username` as `poster_name`,
u.`created_at` as `poster_joined`,
COALESCE(r.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `poster_colour`
FROM `msz_forum_posts` as p
LEFT JOIN `msz_users` as u
ON u.`user_id` = p.`user_id`
LEFT JOIN `msz_roles` as r
ON r.`role_id` = u.`display_role`
WHERE `topic_id` = :topic_id
AND `post_deleted` IS NULL
ORDER BY `post_id`
');
define('MSZ_FORUM_POST_LISTING_QUERY_PAGINATED', MSZ_FORUM_POST_LISTING_QUERY_STANDARD . ' LIMIT :offset, :take');
function forum_post_listing(int $topicId, int $offset = 0, int $take = 0): array
{
$hasPagination = $offset >= 0 && $take > 0;
$getPosts = Database::connection()->prepare(
$hasPagination
? MSZ_FORUM_POST_LISTING_QUERY_PAGINATED
: MSZ_FORUM_POST_LISTING_QUERY_STANDARD
);
$getPosts->bindValue('topic_id', $topicId);
if ($hasPagination) {
$getPosts->bindValue('offset', $offset);
$getPosts->bindValue('take', $take);
}
return $getPosts->execute() ? $getPosts->fetchAll() : [];
}

View file

@ -27,6 +27,33 @@ function forum_topic_create(int $forumId, int $userId, string $title): int
return $createTopic->execute() ? (int)$dbc->lastInsertId() : 0;
}
function forum_topic_fetch(int $topicId): array
{
$getTopic = Database::connection()->prepare('
SELECT
t.`topic_id`, t.`forum_id`, t.`topic_title`, t.`topic_type`, t.`topic_locked`,
f.`forum_archived` as `topic_archived`,
(
SELECT MIN(`post_id`)
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
) as `topic_first_post_id`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
) as `topic_post_count`
FROM `msz_forum_topics` as t
LEFT JOIN `msz_forum_categories` as f
ON f.`forum_id` = t.`forum_id`
WHERE t.`topic_id` = :topic_id
AND t.`topic_deleted` IS NULL
');
$getTopic->bindValue('topic_id', $topicId);
return $getTopic->execute() ? $getTopic->fetch() : [];
}
function forum_topic_bump(int $topicId): bool
{
$bumpTopic = Database::connection()->prepare('
@ -37,3 +64,99 @@ function forum_topic_bump(int $topicId): bool
$bumpTopic->bindValue('topic_id', $topicId);
return $bumpTopic->execute();
}
function forum_topic_mark_read(int $userId, int $topicId, int $forumId): void
{
if ($userId < 1) {
return;
}
$markAsRead = Database::connection()->prepare('
REPLACE INTO `msz_forum_topics_track`
(`user_id`, `topic_id`, `forum_id`, `track_last_read`)
VALUES
(:user_id, :topic_id, :forum_id, NOW())
');
$markAsRead->bindValue('user_id', $userId);
$markAsRead->bindValue('topic_id', $topicId);
$markAsRead->bindValue('forum_id', $forumId);
$markAsRead->execute();
}
define('MSZ_TOPIC_LISTING_QUERY_STANDARD', '
SELECT
:user_id as `target_user_id`,
t.`topic_id`, t.`topic_title`, t.`topic_locked`, t.`topic_type`, t.`topic_created`,
au.`user_id` as `author_id`, au.`username` as `author_name`,
COALESCE(ar.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `author_colour`,
lp.`post_id` as `response_id`,
lp.`post_created` as `response_created`,
lu.`user_id` as `respondent_id`,
lu.`username` as `respondent_name`,
COALESCE(lr.`role_colour`, CAST(0x40000000 AS UNSIGNED)) as `respondent_colour`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
) as `topic_post_count`,
(
SELECT COUNT(`user_id`)
FROM `msz_forum_topics_track`
WHERE `topic_id` = t.`topic_id`
) as `topic_view_count`,
(
SELECT
`target_user_id` > 0
AND
t.`topic_bumped` > NOW() - INTERVAL 1 MONTH
AND (
SELECT COUNT(ti.`topic_id`) < 1
FROM `msz_forum_topics_track` as tt
RIGHT JOIN `msz_forum_topics` as ti
ON ti.`topic_id` = tt.`topic_id`
WHERE ti.`topic_id` = t.`topic_id`
AND tt.`user_id` = `target_user_id`
AND `track_last_read` >= `topic_bumped`
)
) as `topic_unread`
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 lp
ON lp.`post_id` = (
SELECT `post_id`
FROM `msz_forum_posts`
WHERE `topic_id` = t.`topic_id`
ORDER BY `post_id` DESC
LIMIT 1
)
LEFT JOIN `msz_users` as lu
ON lu.`user_id` = lp.`user_id`
LEFT JOIN `msz_roles` as lr
ON lr.`role_id` = lu.`display_role`
WHERE t.`forum_id` = :forum_id
AND t.`topic_deleted` IS NULL
ORDER BY t.`topic_type` DESC, t.`topic_bumped` DESC
');
define('MSZ_TOPIC_LISTING_QUERY_PAGINATED', MSZ_TOPIC_LISTING_QUERY_STANDARD . ' LIMIT :offset, :take');
function forum_topic_listing(int $forumId, int $userId, int $offset = 0, int $take = 0): array
{
$hasPagination = $offset >= 0 && $take > 0;
$getTopics = Database::connection()->prepare(
$hasPagination
? MSZ_TOPIC_LISTING_QUERY_PAGINATED
: MSZ_TOPIC_LISTING_QUERY_STANDARD
);
$getTopics->bindValue('forum_id', $forumId);
$getTopics->bindValue('user_id', $userId);
if ($hasPagination) {
$getTopics->bindValue('offset', $offset);
$getTopics->bindValue('take', $take);
}
return $getTopics->execute() ? $getTopics->fetchAll() : [];
}

View file

@ -23,9 +23,9 @@
</div>
{% endmacro %}
{% macro forum_category_entry(forum, forum_type, forum_read, forum_icon) %}
{% macro forum_category_entry(forum, forum_unread, forum_type, forum_icon) %}
{% set forum_type = forum_type|default(null) %}
{% set forum_read = forum_read|default(true) ? 'read' : 'unread' %}
{% set forum_unread = forum_unread|default(forum.forum_unread|default(false)) ? 'unread' : 'read' %}
{% set forum_icon = forum_icon|default('https://static.flash.moe/images/forum-icons/forum-%s-%s.png') %}
{% if forum_type is null %}
@ -43,7 +43,7 @@
{% endif %}
<div class="forum__listing__entry">
<img src="{{ forum_icon|format(forum_type, forum_read) }}" alt="read" class="forum__listing__entry__icon">
<img src="{{ forum_icon|format(forum_type, forum_unread) }}" alt="{{ forum_unread }}" class="forum__listing__entry__icon">
<div class="forum__listing__entry__info">
<div class="forum__listing__entry__title">
@ -57,7 +57,10 @@
{% if forum.forum_subforums is defined and forum.forum_subforums|length > 0 %}
<div class="forum__listing__entry__subforums">
{% for subforum in forum.forum_subforums %}
<a href="/forum/forum.php?f={{ subforum.forum_id }}" class="forum__listing__entry__subforum">{{ subforum.forum_name }}</a>
<a href="/forum/forum.php?f={{ subforum.forum_id }}"
class="forum__listing__entry__subforum{% if subforum.forum_unread %} forum__listing__entry__subforum--unread{% endif %}">
{{ subforum.forum_name }}
</a>
{% endfor %}
</div>
{% endif %}
@ -158,9 +161,9 @@
</div>
{% endmacro %}
{% macro forum_topic_entry(topic, topic_type, topic_read, topic_icon) %}
{% macro forum_topic_entry(topic, topic_type, topic_unread, topic_icon) %}
{% set topic_type = topic_type|default(null) %}
{% set topic_read = topic_read|default(true) ? 'read' : 'unread' %}
{% set topic_unread = topic_unread|default(topic.topic_unread|default(false)) ? 'unread' : 'read' %}
{% set topic_icon = topic_icon|default('https://static.flash.moe/images/topic-icons/topic-%s-%s.png') %}
{% if topic_type is null %}
@ -180,10 +183,10 @@
{% endif %}
<div class="forum__topics__entry forum__topics__entry--{{ topic_type }}">
<img src="{{ topic_icon|format(topic_type, topic_read) }}" alt="read" class="forum__topics__entry__icon">
<img src="{{ topic_icon|format(topic_type, topic_unread) }}" alt="{{ topic_unread }}" class="forum__topics__entry__icon">
<div class="forum__topics__entry__info">
<div class="forum__topics__entry__info__title forum__topics__entry__info__title--{{ topic_read }}">
<div class="forum__topics__entry__info__title forum__topics__entry__info__title--{{ topic_unread }}">
<a href="/forum/topic.php?t={{ topic.topic_id }}" class="forum__topics__entry__info__title__link">{{ topic.topic_title }}</a>
</div>
<div class="forum__topics__entry__info__author">