Added separate context class for forum stuff and split up handling of each object type.
This commit is contained in:
parent
c68279add9
commit
163da8b213
24 changed files with 1853 additions and 1714 deletions
|
@ -104,7 +104,10 @@ while(!$restricted && !empty($register)) {
|
|||
|
||||
$users->addRoles($userInfo, $defaultRoleInfo);
|
||||
$config->setString('users.newest', $userInfo->getId());
|
||||
$msz->getPerms()->precalculatePermissions($msz->getForum(), [$userInfo->getId()]);
|
||||
$msz->getPerms()->precalculatePermissions(
|
||||
$msz->getForumContext()->getCategories(),
|
||||
[$userInfo->getId()]
|
||||
);
|
||||
|
||||
url_redirect('auth-login-welcome', ['username' => $userInfo->getName()]);
|
||||
return;
|
||||
|
|
|
@ -4,13 +4,16 @@ namespace Misuzu;
|
|||
use stdClass;
|
||||
use RuntimeException;
|
||||
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$forumCategories = $forumCtx->getCategories();
|
||||
$forumTopics = $forumCtx->getTopics();
|
||||
$forumPosts = $forumCtx->getPosts();
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
|
||||
$categoryId = (int)filter_input(INPUT_GET, 'f', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
try {
|
||||
$categoryInfo = $forum->getCategory(categoryId: $categoryId);
|
||||
$categoryInfo = $forumCategories->getCategory(categoryId: $categoryId);
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -29,7 +32,7 @@ if($usersCtx->hasActiveBan($currentUser))
|
|||
|
||||
if($categoryInfo->isLink()) {
|
||||
if($categoryInfo->hasLinkTarget()) {
|
||||
$forum->incrementCategoryClicks($categoryInfo);
|
||||
$forumCategories->incrementCategoryClicks($categoryInfo);
|
||||
redirect($categoryInfo->getLinkTarget());
|
||||
return;
|
||||
}
|
||||
|
@ -37,7 +40,7 @@ if($categoryInfo->isLink()) {
|
|||
Template::throwError(404);
|
||||
}
|
||||
|
||||
$forumPagination = new Pagination($forum->countTopics(
|
||||
$forumPagination = new Pagination($forumTopics->countTopics(
|
||||
categoryInfo: $categoryInfo,
|
||||
global: true,
|
||||
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false
|
||||
|
@ -50,7 +53,7 @@ $children = [];
|
|||
$topics = [];
|
||||
|
||||
if($categoryInfo->mayHaveChildren()) {
|
||||
$children = $forum->getCategoryChildren($categoryInfo, hidden: false, asTree: true);
|
||||
$children = $forumCategories->getCategoryChildren($categoryInfo, hidden: false, asTree: true);
|
||||
|
||||
foreach($children as $childId => $child) {
|
||||
$childPerms = $authInfo->getPerms('forum', $child->info);
|
||||
|
@ -79,7 +82,7 @@ if($categoryInfo->mayHaveChildren()) {
|
|||
$catIds[] = $greatGrandChildId;
|
||||
}
|
||||
|
||||
$grandChildUnread = $forum->checkCategoryUnread($catIds, $currentUser);
|
||||
$grandChildUnread = $forumCategories->checkCategoryUnread($catIds, $currentUser);
|
||||
if($grandChildUnread)
|
||||
$childUnread = true;
|
||||
}
|
||||
|
@ -98,7 +101,7 @@ if($categoryInfo->mayHaveChildren()) {
|
|||
}
|
||||
|
||||
try {
|
||||
$lastPostInfo = $forum->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
||||
$lastPostInfo = $forumPosts->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
||||
} catch(RuntimeException $ex) {
|
||||
$lastPostInfo = null;
|
||||
}
|
||||
|
@ -106,7 +109,7 @@ if($categoryInfo->mayHaveChildren()) {
|
|||
if($lastPostInfo !== null) {
|
||||
$child->lastPost = new stdClass;
|
||||
$child->lastPost->info = $lastPostInfo;
|
||||
$child->lastPost->topicInfo = $forum->getTopic(postInfo: $lastPostInfo);
|
||||
$child->lastPost->topicInfo = $forumTopics->getTopic(postInfo: $lastPostInfo);
|
||||
|
||||
if($lastPostInfo->hasUserId()) {
|
||||
$child->lastPost->user = $usersCtx->getUserInfo($lastPostInfo->getUserId());
|
||||
|
@ -116,7 +119,7 @@ if($categoryInfo->mayHaveChildren()) {
|
|||
}
|
||||
|
||||
if($child->info->mayHaveTopics() && !$childUnread)
|
||||
$childUnread = $forum->checkCategoryUnread($child->info, $currentUser);
|
||||
$childUnread = $forumCategories->checkCategoryUnread($child->info, $currentUser);
|
||||
|
||||
$child->perms = $childPerms;
|
||||
$child->unread = $childUnread;
|
||||
|
@ -124,7 +127,7 @@ if($categoryInfo->mayHaveChildren()) {
|
|||
}
|
||||
|
||||
if($categoryInfo->mayHaveTopics()) {
|
||||
$topicInfos = $forum->getTopics(
|
||||
$topicInfos = $forumTopics->getTopics(
|
||||
categoryInfo: $categoryInfo,
|
||||
global: true,
|
||||
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false,
|
||||
|
@ -134,8 +137,8 @@ if($categoryInfo->mayHaveTopics()) {
|
|||
foreach($topicInfos as $topicInfo) {
|
||||
$topics[] = $topic = new stdClass;
|
||||
$topic->info = $topicInfo;
|
||||
$topic->unread = $forum->checkTopicUnread($topicInfo, $currentUser);
|
||||
$topic->participated = $forum->checkTopicParticipated($topicInfo, $currentUser);
|
||||
$topic->unread = $forumTopics->checkTopicUnread($topicInfo, $currentUser);
|
||||
$topic->participated = $forumTopics->checkTopicParticipated($topicInfo, $currentUser);
|
||||
$topic->lastPost = new stdClass;
|
||||
|
||||
if($topicInfo->hasUserId()) {
|
||||
|
@ -144,7 +147,7 @@ if($categoryInfo->mayHaveTopics()) {
|
|||
}
|
||||
|
||||
try {
|
||||
$topic->lastPost->info = $lastPostInfo = $forum->getPost(
|
||||
$topic->lastPost->info = $lastPostInfo = $forumPosts->getPost(
|
||||
topicInfo: $topicInfo,
|
||||
getLast: true,
|
||||
deleted: $topicInfo->isDeleted() ? null : false,
|
||||
|
@ -163,8 +166,8 @@ $perms = $perms->checkMany([
|
|||
]);
|
||||
|
||||
Template::render('forum.forum', [
|
||||
'forum_breadcrumbs' => $forum->getCategoryAncestry($categoryInfo),
|
||||
'global_accent_colour' => $forum->getCategoryColour($categoryInfo),
|
||||
'forum_breadcrumbs' => $forumCategories->getCategoryAncestry($categoryInfo),
|
||||
'global_accent_colour' => $forumCategories->getCategoryColour($categoryInfo),
|
||||
'forum_info' => $categoryInfo,
|
||||
'forum_children' => $children,
|
||||
'forum_topics' => $topics,
|
||||
|
|
|
@ -4,7 +4,10 @@ namespace Misuzu;
|
|||
use stdClass;
|
||||
use RuntimeException;
|
||||
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$forumCategories = $forumCtx->getCategories();
|
||||
$forumTopics = $forumCtx->getTopics();
|
||||
$forumPosts = $forumCtx->getPosts();
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
$mode = (string)filter_input(INPUT_GET, 'm');
|
||||
|
||||
|
@ -20,13 +23,13 @@ if($mode === 'mark') {
|
|||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$categoryInfos = $categoryId === null
|
||||
? $forum->getCategories()
|
||||
: $forum->getCategoryChildren(parentInfo: $categoryId, includeSelf: true);
|
||||
? $forumCategories->getCategories()
|
||||
: $forumCategories->getCategoryChildren(parentInfo: $categoryId, includeSelf: true);
|
||||
|
||||
foreach($categoryInfos as $categoryInfo) {
|
||||
$perms = $authInfo->getPerms('forum', $categoryInfo);
|
||||
if($perms->check(Perm::F_CATEGORY_LIST))
|
||||
$forum->updateUserReadCategory($userInfo, $categoryInfo);
|
||||
$forumCategories->updateUserReadCategory($userInfo, $categoryInfo);
|
||||
}
|
||||
|
||||
url_redirect($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId]);
|
||||
|
@ -47,7 +50,7 @@ if($mode === 'mark') {
|
|||
if($mode !== '')
|
||||
Template::throwError(404);
|
||||
|
||||
$categories = $forum->getCategories(hidden: false, asTree: true);
|
||||
$categories = $forumCategories->getCategories(hidden: false, asTree: true);
|
||||
|
||||
foreach($categories as $categoryId => $category) {
|
||||
$perms = $authInfo->getPerms('forum', $category->info);
|
||||
|
@ -87,7 +90,7 @@ foreach($categories as $categoryId => $category) {
|
|||
$catIds[] = $greatGrandChildId;
|
||||
}
|
||||
|
||||
$grandChildUnread = $forum->checkCategoryUnread($catIds, $currentUser);
|
||||
$grandChildUnread = $forumCategories->checkCategoryUnread($catIds, $currentUser);
|
||||
if($grandChildUnread)
|
||||
$childUnread = true;
|
||||
}
|
||||
|
@ -106,7 +109,7 @@ foreach($categories as $categoryId => $category) {
|
|||
}
|
||||
|
||||
try {
|
||||
$lastPostInfo = $forum->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
||||
$lastPostInfo = $forumPosts->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
||||
} catch(RuntimeException $ex) {
|
||||
$lastPostInfo = null;
|
||||
}
|
||||
|
@ -114,7 +117,7 @@ foreach($categories as $categoryId => $category) {
|
|||
if($lastPostInfo !== null) {
|
||||
$child->lastPost = new stdClass;
|
||||
$child->lastPost->info = $lastPostInfo;
|
||||
$child->lastPost->topicInfo = $forum->getTopic(postInfo: $lastPostInfo);
|
||||
$child->lastPost->topicInfo = $forumTopics->getTopic(postInfo: $lastPostInfo);
|
||||
|
||||
if($lastPostInfo->hasUserId()) {
|
||||
$child->lastPost->user = $usersCtx->getUserInfo($lastPostInfo->getUserId());
|
||||
|
@ -125,7 +128,7 @@ foreach($categories as $categoryId => $category) {
|
|||
}
|
||||
|
||||
if($child->info->mayHaveTopics() && !$childUnread) {
|
||||
$childUnread = $forum->checkCategoryUnread($child->info, $currentUser);
|
||||
$childUnread = $forumCategories->checkCategoryUnread($child->info, $currentUser);
|
||||
if($childUnread)
|
||||
$unread = true;
|
||||
}
|
||||
|
@ -135,7 +138,7 @@ foreach($categories as $categoryId => $category) {
|
|||
}
|
||||
|
||||
if($category->info->mayHaveTopics() && !$unread)
|
||||
$unread = $forum->checkCategoryUnread($category->info, $currentUser);
|
||||
$unread = $forumCategories->checkCategoryUnread($category->info, $currentUser);
|
||||
|
||||
if(!$category->info->isListing()) {
|
||||
if(!array_key_exists('0', $categories)) {
|
||||
|
@ -159,7 +162,7 @@ foreach($categories as $categoryId => $category) {
|
|||
}
|
||||
|
||||
try {
|
||||
$lastPostInfo = $forum->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
||||
$lastPostInfo = $forumPosts->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
||||
} catch(RuntimeException $ex) {
|
||||
$lastPostInfo = null;
|
||||
}
|
||||
|
@ -167,7 +170,7 @@ foreach($categories as $categoryId => $category) {
|
|||
if($lastPostInfo !== null) {
|
||||
$category->lastPost = new stdClass;
|
||||
$category->lastPost->info = $lastPostInfo;
|
||||
$category->lastPost->topicInfo = $forum->getTopic(postInfo: $lastPostInfo);
|
||||
$category->lastPost->topicInfo = $forumTopics->getTopic(postInfo: $lastPostInfo);
|
||||
|
||||
if($lastPostInfo->hasUserId()) {
|
||||
$category->lastPost->user = $usersCtx->getUserInfo($lastPostInfo->getUserId());
|
||||
|
|
|
@ -6,7 +6,7 @@ use RuntimeException;
|
|||
if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_FORUM_LEADERBOARD_VIEW))
|
||||
Template::throwError(403);
|
||||
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
$config = $cfg->getValues([
|
||||
['forum_leader.first_year:i', 2018],
|
||||
|
@ -61,7 +61,7 @@ for($i = $currentYear, $j = $currentMonth;;) {
|
|||
break;
|
||||
}
|
||||
|
||||
$rankings = $forum->generatePostRankings($year, $month, $unrankedForums, $unrankedTopics);
|
||||
$rankings = $forumCtx->getPosts()->generatePostRankings($year, $month, $unrankedForums, $unrankedTopics);
|
||||
foreach($rankings as $ranking) {
|
||||
$ranking->user = $ranking->colour = null;
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ namespace Misuzu;
|
|||
|
||||
use RuntimeException;
|
||||
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$forumPosts = $forumCtx->getPosts();
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
|
||||
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
|
||||
|
@ -23,7 +24,7 @@ if($postMode !== '' && $usersCtx->hasActiveBan($currentUser))
|
|||
Template::displayInfo('You have been banned, check your profile for more information.', 403);
|
||||
|
||||
try {
|
||||
$postInfo = $forum->getPost(postId: $postId);
|
||||
$postInfo = $forumPosts->getPost(postId: $postId);
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -56,7 +57,7 @@ switch($postMode) {
|
|||
Template::displayInfo('This post has existed for too long. Ask a moderator to remove if it absolutely necessary.', 403);
|
||||
}
|
||||
|
||||
$originalPostInfo = $forum->getPost(topicInfo: $postInfo->getTopicId());
|
||||
$originalPostInfo = $forumPosts->getPost(topicInfo: $postInfo->getTopicId());
|
||||
if($originalPostInfo->getId() === $postInfo->getId())
|
||||
Template::displayInfo('This is the opening post of the topic it belongs to, it may not be deleted without deleting the entire topic as well.', 403);
|
||||
|
||||
|
@ -79,7 +80,7 @@ switch($postMode) {
|
|||
break;
|
||||
}
|
||||
|
||||
$forum->deletePost($postInfo);
|
||||
$forumPosts->deletePost($postInfo);
|
||||
$msz->createAuditLog('FORUM_POST_DELETE', [$postInfo->getId()]);
|
||||
|
||||
url_redirect('forum-topic', ['topic' => $postInfo->getTopicId()]);
|
||||
|
@ -108,7 +109,7 @@ switch($postMode) {
|
|||
break;
|
||||
}
|
||||
|
||||
$forum->nukePost($postInfo->getId());
|
||||
$forumPosts->nukePost($postInfo->getId());
|
||||
$msz->createAuditLog('FORUM_POST_NUKE', [$postInfo->getId()]);
|
||||
|
||||
url_redirect('forum-topic', ['topic' => $postInfo->getTopicId()]);
|
||||
|
@ -137,7 +138,7 @@ switch($postMode) {
|
|||
break;
|
||||
}
|
||||
|
||||
$forum->restorePost($postInfo->getId());
|
||||
$forumPosts->restorePost($postInfo->getId());
|
||||
$msz->createAuditLog('FORUM_POST_RESTORE', [$postInfo->getId()]);
|
||||
|
||||
url_redirect('forum-topic', ['topic' => $postInfo->getTopicId()]);
|
||||
|
|
|
@ -11,7 +11,10 @@ $authInfo = $msz->getAuthInfo();
|
|||
if(!$authInfo->isLoggedIn())
|
||||
Template::throwError(401);
|
||||
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$forumCategories = $forumCtx->getCategories();
|
||||
$forumTopics = $forumCtx->getTopics();
|
||||
$forumPosts = $forumCtx->getPosts();
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
|
||||
$currentUser = $authInfo->getUserInfo();
|
||||
|
@ -19,8 +22,6 @@ $currentUserId = $currentUser->getId();
|
|||
if($usersCtx->hasActiveBan($currentUser))
|
||||
Template::throwError(403);
|
||||
|
||||
$userPostsCounts = [];
|
||||
|
||||
$forumPostingModes = [
|
||||
'create', 'edit', 'quote', 'preview',
|
||||
];
|
||||
|
@ -63,7 +64,7 @@ if(empty($postId)) {
|
|||
$hasPostInfo = false;
|
||||
} else {
|
||||
try {
|
||||
$postInfo = $forum->getPost(postId: $postId);
|
||||
$postInfo = $forumPosts->getPost(postId: $postId);
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ if(empty($topicId)) {
|
|||
$hasTopicInfo = false;
|
||||
} else {
|
||||
try {
|
||||
$topicInfo = $forum->getTopic(topicId: $topicId);
|
||||
$topicInfo = $forumTopics->getTopic(topicId: $topicId);
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -89,7 +90,7 @@ if(empty($topicId)) {
|
|||
Template::throwError(404);
|
||||
|
||||
$forumId = $topicInfo->getCategoryId();
|
||||
$originalPostInfo = $forum->getPost(topicInfo: $topicInfo);
|
||||
$originalPostInfo = $forumPosts->getPost(topicInfo: $topicInfo);
|
||||
$hasTopicInfo = true;
|
||||
}
|
||||
|
||||
|
@ -97,7 +98,7 @@ if(empty($forumId)) {
|
|||
$hasCategoryInfo = false;
|
||||
} else {
|
||||
try {
|
||||
$categoryInfo = $forum->getCategory(categoryId: $forumId);
|
||||
$categoryInfo = $forumCategories->getCategory(categoryId: $forumId);
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -152,7 +153,7 @@ if(!empty($_POST)) {
|
|||
$postTimeout = $cfg->getInteger('forum.posting.timeout', 5);
|
||||
if($postTimeout > 0) {
|
||||
$postTimeoutThreshold = DateTime::now()->modify(sprintf('-%d seconds', $postTimeout));
|
||||
$lastPostCreatedAt = $forum->getUserLastPostCreatedAt($currentUser);
|
||||
$lastPostCreatedAt = $forumPosts->getUserLastPostCreatedAt($currentUser);
|
||||
|
||||
if($lastPostCreatedAt->isMoreThan($postTimeoutThreshold)) {
|
||||
$waitSeconds = $postTimeout + ($lastPostCreatedAt->getUnixTimeSeconds() - time());
|
||||
|
@ -205,7 +206,7 @@ if(!empty($_POST)) {
|
|||
switch($mode) {
|
||||
case 'create':
|
||||
if(empty($topicInfo)) {
|
||||
$topicInfo = $forum->createTopic(
|
||||
$topicInfo = $forumTopics->createTopic(
|
||||
$categoryInfo,
|
||||
$currentUser,
|
||||
$topicTitle,
|
||||
|
@ -213,11 +214,11 @@ if(!empty($_POST)) {
|
|||
);
|
||||
|
||||
$topicId = $topicInfo->getId();
|
||||
$forum->incrementCategoryTopics($categoryInfo);
|
||||
$forumTopics->incrementCategoryTopics($categoryInfo);
|
||||
} else
|
||||
$forum->bumpTopic($topicInfo);
|
||||
$forumTopics->bumpTopic($topicInfo);
|
||||
|
||||
$postInfo = $forum->createPost(
|
||||
$postInfo = $forumPosts->createPost(
|
||||
$topicId,
|
||||
$currentUser,
|
||||
$_SERVER['REMOTE_ADDR'],
|
||||
|
@ -228,7 +229,7 @@ if(!empty($_POST)) {
|
|||
);
|
||||
|
||||
$postId = $postInfo->getId();
|
||||
$forum->incrementCategoryPosts($categoryInfo);
|
||||
$forumCategories->incrementCategoryPosts($categoryInfo);
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
|
@ -236,7 +237,7 @@ if(!empty($_POST)) {
|
|||
&& $postInfo->shouldMarkAsEdited()
|
||||
&& $postText !== $postInfo->getBody();
|
||||
|
||||
$forum->updatePost(
|
||||
$forumPosts->updatePost(
|
||||
$postId,
|
||||
remoteAddr: $_SERVER['REMOTE_ADDR'],
|
||||
body: $postText,
|
||||
|
@ -246,7 +247,7 @@ if(!empty($_POST)) {
|
|||
);
|
||||
|
||||
if($isEditingTopic && ($topicTitleChanged || $topicTypeChanged))
|
||||
$forum->updateTopic(
|
||||
$forumTopics->updateTopic(
|
||||
$topicId,
|
||||
title: $topicTitle,
|
||||
type: $topicType
|
||||
|
@ -278,11 +279,7 @@ if($mode === 'edit') { // $post is pretty much sure to be populated at this poin
|
|||
if($postInfo->hasUserId()) {
|
||||
$post->user = $usersCtx->getUserInfo($postInfo->getUserId());
|
||||
$post->colour = $usersCtx->getUserColour($post->user);
|
||||
|
||||
$postUserId = $postInfo->getUserId();
|
||||
if(!array_key_exists($postUserId, $userPostsCounts))
|
||||
$userPostsCounts[$postUserId] = $forum->countPosts(userInfo: $post->user, deleted: false);
|
||||
$post->postsCount = $userPostsCounts[$postUserId];
|
||||
$post->postsCount = $forumCtx->countTotalUserPosts($post->user);
|
||||
}
|
||||
|
||||
$post->isOriginalPost = $originalPostInfo->getId() == $postInfo->getId();
|
||||
|
@ -293,18 +290,18 @@ if($mode === 'edit') { // $post is pretty much sure to be populated at this poin
|
|||
}
|
||||
|
||||
try {
|
||||
$lastPostInfo = $forum->getPost(userInfo: $currentUser, getLast: true, deleted: false);
|
||||
$lastPostInfo = $forumPosts->getPost(userInfo: $currentUser, getLast: true, deleted: false);
|
||||
$selectedParser = $lastPostInfo->getParser();
|
||||
} catch(RuntimeException $ex) {
|
||||
$selectedParser = Parser::BBCODE;
|
||||
}
|
||||
|
||||
Template::render('forum.posting', [
|
||||
'posting_breadcrumbs' => $forum->getCategoryAncestry($categoryInfo),
|
||||
'global_accent_colour' => $forum->getCategoryColour($categoryInfo),
|
||||
'posting_breadcrumbs' => $forumCategories->getCategoryAncestry($categoryInfo),
|
||||
'global_accent_colour' => $forumCategories->getCategoryColour($categoryInfo),
|
||||
'posting_user' => $currentUser,
|
||||
'posting_user_colour' => $usersCtx->getUserColour($currentUser),
|
||||
'posting_user_posts_count' => $userPostsCounts[$currentUser->getId()] ?? $forum->countPosts(userInfo: $currentUser, deleted: false),
|
||||
'posting_user_posts_count' => $forumCtx->countTotalUserPosts($currentUser),
|
||||
'posting_user_preferred_parser' => $selectedParser,
|
||||
'posting_forum' => $categoryInfo,
|
||||
'posting_notices' => $notices,
|
||||
|
|
|
@ -4,7 +4,11 @@ namespace Misuzu;
|
|||
use stdClass;
|
||||
use RuntimeException;
|
||||
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$forumCategories = $forumCtx->getCategories();
|
||||
$forumTopics = $forumCtx->getTopics();
|
||||
$forumTopicRedirects = $forumCtx->getTopicRedirects();
|
||||
$forumPosts = $forumCtx->getPosts();
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
|
||||
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
|
||||
|
@ -19,7 +23,7 @@ $currentUserId = $currentUser === null ? '0' : $currentUser->getId();
|
|||
|
||||
if($topicId < 1 && $postId > 0) {
|
||||
try {
|
||||
$postInfo = $forum->getPost(postId: $postId);
|
||||
$postInfo = $forumPosts->getPost(postId: $postId);
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -32,7 +36,7 @@ if($topicId < 1 && $postId > 0) {
|
|||
Template::throwError(404);
|
||||
|
||||
$topicId = $postInfo->getTopicId();
|
||||
$preceedingPostCount = $forum->countPosts(
|
||||
$preceedingPostCount = $forumPosts->countPosts(
|
||||
topicInfo: $topicId,
|
||||
upToPostInfo: $postInfo,
|
||||
deleted: $canDeleteAny ? null : false
|
||||
|
@ -41,7 +45,7 @@ if($topicId < 1 && $postId > 0) {
|
|||
|
||||
try {
|
||||
$topicIsNuked = $topicIsDeleted = $canDeleteAny = false;
|
||||
$topicInfo = $forum->getTopic(topicId: $topicId);
|
||||
$topicInfo = $forumTopics->getTopic(topicId: $topicId);
|
||||
} catch(RuntimeException $ex) {
|
||||
$topicIsNuked = true;
|
||||
}
|
||||
|
@ -60,8 +64,8 @@ if(!$topicIsNuked) {
|
|||
$canDeleteAny = $perms->check(Perm::F_POST_DELETE_ANY);
|
||||
}
|
||||
|
||||
if(($topicIsNuked || $topicIsDeleted) && $forum->hasTopicRedirect($topicId)) {
|
||||
$topicRedirectInfo = $forum->getTopicRedirect($topicId);
|
||||
if(($topicIsNuked || $topicIsDeleted) && $forumTopicRedirects->hasTopicRedirect($topicId)) {
|
||||
$topicRedirectInfo = $forumTopicRedirects->getTopicRedirect($topicId);
|
||||
Template::set('topic_redir_info', $topicRedirectInfo);
|
||||
|
||||
if($topicIsNuked || !$canDeleteAny) {
|
||||
|
@ -80,7 +84,7 @@ if(!$perms->check(Perm::F_CATEGORY_VIEW))
|
|||
// this should be in the config
|
||||
$deletePostThreshold = 1;
|
||||
|
||||
$categoryInfo = $forum->getCategory(topicInfo: $topicInfo);
|
||||
$categoryInfo = $forumCategories->getCategory(topicInfo: $topicInfo);
|
||||
$topicIsLocked = $topicInfo->isLocked();
|
||||
$topicIsArchived = $categoryInfo->isArchived();
|
||||
$topicPostsTotal = $topicInfo->getTotalPostsCount();
|
||||
|
@ -135,7 +139,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
|||
Template::displayInfo('This topic has existed for too long. Ask a moderator to remove if it absolutely necessary.', 403);
|
||||
|
||||
// deleted posts are intentionally included
|
||||
$topicPostCount = $forum->countPosts(topicInfo: $topicInfo);
|
||||
$topicPostCount = $forumPosts->countPosts(topicInfo: $topicInfo);
|
||||
if($topicPostCount > $deletePostThreshold)
|
||||
Template::displayInfo('This topic already has replies, you may no longer delete it. Ask a moderator to remove if it absolutely necessary.', 403);
|
||||
}
|
||||
|
@ -159,7 +163,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
|||
break;
|
||||
}
|
||||
|
||||
$forum->deleteTopic($topicInfo->getId());
|
||||
$forumTopics->deleteTopic($topicInfo->getId());
|
||||
$msz->createAuditLog('FORUM_TOPIC_DELETE', [$topicInfo->getId()]);
|
||||
|
||||
url_redirect('forum-category', [
|
||||
|
@ -189,7 +193,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
|||
break;
|
||||
}
|
||||
|
||||
$forum->restoreTopic($topicInfo->getId());
|
||||
$forumTopics->restoreTopic($topicInfo->getId());
|
||||
$msz->createAuditLog('FORUM_TOPIC_RESTORE', [$topicInfo->getId()]);
|
||||
|
||||
url_redirect('forum-category', [
|
||||
|
@ -219,7 +223,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
|||
break;
|
||||
}
|
||||
|
||||
$forum->nukeTopic($topicInfo->getId());
|
||||
$forumTopics->nukeTopic($topicInfo->getId());
|
||||
$msz->createAuditLog('FORUM_TOPIC_NUKE', [$topicInfo->getId()]);
|
||||
|
||||
url_redirect('forum-category', [
|
||||
|
@ -229,7 +233,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
|||
|
||||
case 'bump':
|
||||
if($canBumpTopic) {
|
||||
$forum->bumpTopic($topicInfo->getId());
|
||||
$forumTopics->bumpTopic($topicInfo->getId());
|
||||
$msz->createAuditLog('FORUM_TOPIC_BUMP', [$topicInfo->getId()]);
|
||||
}
|
||||
|
||||
|
@ -240,7 +244,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
|||
|
||||
case 'lock':
|
||||
if($canLockTopic && !$topicIsLocked) {
|
||||
$forum->lockTopic($topicInfo->getId());
|
||||
$forumTopics->lockTopic($topicInfo->getId());
|
||||
$msz->createAuditLog('FORUM_TOPIC_LOCK', [$topicInfo->getId()]);
|
||||
}
|
||||
|
||||
|
@ -251,7 +255,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
|
|||
|
||||
case 'unlock':
|
||||
if($canLockTopic && $topicIsLocked) {
|
||||
$forum->unlockTopic($topicInfo->getId());
|
||||
$forumTopics->unlockTopic($topicInfo->getId());
|
||||
$msz->createAuditLog('FORUM_TOPIC_UNLOCK', [$topicInfo->getId()]);
|
||||
}
|
||||
|
||||
|
@ -275,7 +279,7 @@ if(isset($preceedingPostCount))
|
|||
if(!$topicPagination->hasValidOffset())
|
||||
Template::throwError(404);
|
||||
|
||||
$postInfos = $forum->getPosts(
|
||||
$postInfos = $forumPosts->getPosts(
|
||||
topicInfo: $topicInfo,
|
||||
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false,
|
||||
pagination: $topicPagination,
|
||||
|
@ -284,9 +288,8 @@ $postInfos = $forum->getPosts(
|
|||
if(empty($postInfos))
|
||||
Template::throwError(404);
|
||||
|
||||
$originalPostInfo = $forum->getPost(topicInfo: $topicInfo);
|
||||
$originalPostInfo = $forumPosts->getPost(topicInfo: $topicInfo);
|
||||
|
||||
$userPostsCounts = [];
|
||||
$posts = [];
|
||||
|
||||
foreach($postInfos as $postInfo) {
|
||||
|
@ -296,11 +299,7 @@ foreach($postInfos as $postInfo) {
|
|||
if($postInfo->hasUserId()) {
|
||||
$post->user = $usersCtx->getUserInfo($postInfo->getUserId());
|
||||
$post->colour = $usersCtx->getUserColour($post->user);
|
||||
|
||||
$postUserId = $postInfo->getUserId();
|
||||
if(!array_key_exists($postUserId, $userPostsCounts))
|
||||
$userPostsCounts[$postUserId] = $forum->countPosts(userInfo: $post->user, deleted: false);
|
||||
$post->postsCount = $userPostsCounts[$postUserId];
|
||||
$post->postsCount = $forumCtx->countTotalUserPosts($post->user);
|
||||
}
|
||||
|
||||
$post->isOriginalPost = $originalPostInfo->getId() == $postInfo->getId();
|
||||
|
@ -310,10 +309,10 @@ foreach($postInfos as $postInfo) {
|
|||
|
||||
$canReply = !$topicIsArchived && !$topicIsLocked && !$topicIsDeleted && $perms->check(Perm::F_POST_CREATE);
|
||||
|
||||
if(!$forum->checkUserHasReadTopic($currentUser, $topicInfo))
|
||||
$forum->incrementTopicViews($topicInfo);
|
||||
if(!$forumTopics->checkUserHasReadTopic($currentUser, $topicInfo))
|
||||
$forumTopics->incrementTopicViews($topicInfo);
|
||||
|
||||
$forum->updateUserReadTopic($currentUser, $topicInfo);
|
||||
$forumTopics->updateUserReadTopic($currentUser, $topicInfo);
|
||||
|
||||
$perms = $perms->checkMany([
|
||||
'can_create_post' => Perm::F_POST_CREATE,
|
||||
|
@ -324,8 +323,8 @@ $perms = $perms->checkMany([
|
|||
]);
|
||||
|
||||
Template::render('forum.topic', [
|
||||
'topic_breadcrumbs' => $forum->getCategoryAncestry($topicInfo),
|
||||
'global_accent_colour' => $forum->getCategoryColour($topicInfo),
|
||||
'topic_breadcrumbs' => $forumCategories->getCategoryAncestry($topicInfo),
|
||||
'global_accent_colour' => $forumCategories->getCategoryColour($topicInfo),
|
||||
'topic_info' => $topicInfo,
|
||||
'category_info' => $categoryInfo,
|
||||
'topic_posts' => $posts,
|
||||
|
|
|
@ -5,7 +5,8 @@ $authInfo = $msz->getAuthInfo();
|
|||
if(!$authInfo->getPerms('global')->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$forumTopicRedirects = $forumCtx->getTopicRedirects();
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if(!CSRF::validateRequest())
|
||||
|
@ -15,7 +16,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
$rTopicURL = trim((string)filter_input(INPUT_POST, 'topic_redir_url'));
|
||||
|
||||
$msz->createAuditLog('FORUM_TOPIC_REDIR_CREATE', [$rTopicId]);
|
||||
$forum->createTopicRedirect($rTopicId, $authInfo->getUserInfo(), $rTopicURL);
|
||||
$forumTopicRedirects->createTopicRedirect($rTopicId, $authInfo->getUserInfo(), $rTopicURL);
|
||||
url_redirect('manage-forum-topic-redirs');
|
||||
return;
|
||||
}
|
||||
|
@ -26,16 +27,16 @@ if(filter_input(INPUT_GET, 'm') === 'explode') {
|
|||
|
||||
$rTopicId = (string)filter_input(INPUT_GET, 't');
|
||||
$msz->createAuditLog('FORUM_TOPIC_REDIR_REMOVE', [$rTopicId]);
|
||||
$forum->deleteTopicRedirect($rTopicId);
|
||||
$forumTopicRedirects->deleteTopicRedirect($rTopicId);
|
||||
url_redirect('manage-forum-topic-redirs');
|
||||
return;
|
||||
}
|
||||
|
||||
$pagination = new Pagination($forum->countTopicRedirects(), 20);
|
||||
$pagination = new Pagination($forumTopicRedirects->countTopicRedirects(), 20);
|
||||
if(!$pagination->hasValidOffset())
|
||||
Template::throwError(404);
|
||||
|
||||
$redirs = $forum->getTopicRedirects(pagination: $pagination);
|
||||
$redirs = $forumTopicRedirects->getTopicRedirects(pagination: $pagination);
|
||||
|
||||
Template::render('manage.forum.redirs', [
|
||||
'manage_redirs' => $redirs,
|
||||
|
|
|
@ -216,7 +216,10 @@ if(CSRF::validateRequest() && $canEdit) {
|
|||
}
|
||||
|
||||
if($permsNeedRecalc)
|
||||
$perms->precalculatePermissions($msz->getForum(), [$userInfo->getId()]);
|
||||
$perms->precalculatePermissions(
|
||||
$msz->getForumContext()->getCategories(),
|
||||
[$userInfo->getId()]
|
||||
);
|
||||
|
||||
url_redirect('manage-user', ['user' => $userInfo->getId()]);
|
||||
return;
|
||||
|
|
|
@ -9,7 +9,7 @@ if(!$authInfo->isLoggedIn())
|
|||
|
||||
// TODO: restore forum-topics and forum-posts orderings
|
||||
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
$users = $usersCtx->getUsers();
|
||||
$roles = $usersCtx->getRoles();
|
||||
|
@ -94,8 +94,8 @@ foreach($userInfos as $userInfo)
|
|||
$userList[] = [
|
||||
'info' => $userInfo,
|
||||
'colour' => $usersCtx->getUserColour($userInfo),
|
||||
'ftopics' => $forum->countTopics(userInfo: $userInfo, deleted: false),
|
||||
'fposts' => $forum->countPosts(userInfo: $userInfo, deleted: false),
|
||||
'ftopics' => $forumCtx->countTotalUserTopics($userInfo),
|
||||
'fposts' => $forumCtx->countTotalUserPosts($userInfo),
|
||||
];
|
||||
|
||||
if(empty($userList))
|
||||
|
|
|
@ -17,7 +17,10 @@ $isEditing = !empty($_GET['edit']) && is_string($_GET['edit']) ? (bool)$_GET['ed
|
|||
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
$users = $usersCtx->getUsers();
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$forumCategories = $forumCtx->getCategories();
|
||||
$forumTopics = $forumCtx->getTopics();
|
||||
$forumPosts = $forumCtx->getPosts();
|
||||
|
||||
$authInfo = $msz->getAuthInfo();
|
||||
$viewerInfo = $authInfo->getUserInfo();
|
||||
|
@ -289,8 +292,8 @@ if($isEditing) {
|
|||
|
||||
// TODO: create user counters so these can be statically kept
|
||||
$profileStats = new stdClass;
|
||||
$profileStats->forum_topic_count = $forum->countTopics(userInfo: $userInfo, deleted: false);
|
||||
$profileStats->forum_post_count = $forum->countPosts(userInfo: $userInfo, deleted: false);
|
||||
$profileStats->forum_topic_count = $forumCtx->countTotalUserTopics($userInfo);
|
||||
$profileStats->forum_post_count = $forumCtx->countTotalUserPosts($userInfo);
|
||||
$profileStats->comments_count = $msz->getComments()->countPosts(userInfo: $userInfo, deleted: false);
|
||||
|
||||
if(!$viewingAsGuest) {
|
||||
|
@ -302,21 +305,21 @@ if(!$viewingAsGuest) {
|
|||
'forum_leader.unranked.topic:a',
|
||||
]);
|
||||
|
||||
$activeCategoryStats = $forum->getMostActiveCategoryInfo(
|
||||
$activeCategoryStats = $forumCategories->getMostActiveCategoryInfo(
|
||||
$userInfo,
|
||||
$unranked['forum_leader.unranked.forum'],
|
||||
$unranked['forum_leader.unranked.topic'],
|
||||
deleted: false
|
||||
);
|
||||
$activeCategoryInfo = $activeCategoryStats->success ? $forum->getCategory(categoryId: $activeCategoryStats->categoryId) : null;
|
||||
$activeCategoryInfo = $activeCategoryStats->success ? $forumCategories->getCategory(categoryId: $activeCategoryStats->categoryId) : null;
|
||||
|
||||
$activeTopicStats = $forum->getMostActiveTopicInfo(
|
||||
$activeTopicStats = $forumTopics->getMostActiveTopicInfo(
|
||||
$userInfo,
|
||||
$unranked['forum_leader.unranked.forum'],
|
||||
$unranked['forum_leader.unranked.topic'],
|
||||
deleted: false
|
||||
);
|
||||
$activeTopicInfo = $activeTopicStats->success ? $forum->getTopic(topicId: $activeTopicStats->topicId) : null;
|
||||
$activeTopicInfo = $activeTopicStats->success ? $forumTopics->getTopic(topicId: $activeTopicStats->topicId) : null;
|
||||
|
||||
$profileFieldValues = $profileFields->getFieldValues($userInfo);
|
||||
$profileFieldInfos = $profileFieldInfos ?? $profileFields->getFields(fieldValueInfos: $isEditing ? null : $profileFieldValues);
|
||||
|
|
|
@ -41,7 +41,10 @@ Template::addFunction('search_merge_query', function($attrs) use (&$searchQueryE
|
|||
if(!empty($searchQuery)) {
|
||||
$usersCtx = $msz->getUsersContext();
|
||||
$users = $usersCtx->getUsers();
|
||||
$forum = $msz->getForum();
|
||||
$forumCtx = $msz->getForumContext();
|
||||
$forumCategories = $forumCtx->getCategories();
|
||||
$forumTopics = $forumCtx->getTopics();
|
||||
$forumPosts = $forumCtx->getPosts();
|
||||
$news = $msz->getNews();
|
||||
$comments = $msz->getComments();
|
||||
|
||||
|
@ -79,18 +82,18 @@ if(!empty($searchQuery)) {
|
|||
$currentUserId = $currentUser === null ? 0 : (int)$currentUser->getId();
|
||||
|
||||
$forumCategoryIds = XArray::where(
|
||||
$forum->getCategories(hidden: false),
|
||||
$forumCategories->getCategories(hidden: false),
|
||||
fn($categoryInfo) => $categoryInfo->mayHaveTopics() && $authInfo->getPerms('forum', $categoryInfo)->check(Perm::F_CATEGORY_VIEW)
|
||||
);
|
||||
|
||||
$forumTopicInfos = $forum->getTopics(categoryInfo: $forumCategoryIds, deleted: false, searchQuery: $searchQueryEvaluated);
|
||||
$forumTopics = [];
|
||||
$forumTopicInfos = $forumTopics->getTopics(categoryInfo: $forumCategoryIds, deleted: false, searchQuery: $searchQueryEvaluated);
|
||||
$ftopics = [];
|
||||
|
||||
foreach($forumTopicInfos as $topicInfo) {
|
||||
$forumTopics[] = $topic = new stdClass;
|
||||
$ftopics[] = $topic = new stdClass;
|
||||
$topic->info = $topicInfo;
|
||||
$topic->unread = $forum->checkTopicUnread($topicInfo, $currentUser);
|
||||
$topic->participated = $forum->checkTopicParticipated($topicInfo, $currentUser);
|
||||
$topic->unread = $forumTopics->checkTopicUnread($topicInfo, $currentUser);
|
||||
$topic->participated = $forumTopics->checkTopicParticipated($topicInfo, $currentUser);
|
||||
$topic->lastPost = new stdClass;
|
||||
|
||||
if($topicInfo->hasUserId()) {
|
||||
|
@ -99,7 +102,7 @@ if(!empty($searchQuery)) {
|
|||
}
|
||||
|
||||
try {
|
||||
$topic->lastPost->info = $lastPostInfo = $forum->getPost(
|
||||
$topic->lastPost->info = $lastPostInfo = $forumPosts->getPost(
|
||||
topicInfo: $topicInfo,
|
||||
getLast: true,
|
||||
deleted: $topicInfo->isDeleted() ? null : false,
|
||||
|
@ -112,17 +115,17 @@ if(!empty($searchQuery)) {
|
|||
} catch(RuntimeException $ex) {}
|
||||
}
|
||||
|
||||
$forumPostInfos = $forum->getPosts(categoryInfo: $forumCategoryIds, searchQuery: $searchQueryEvaluated);
|
||||
$forumPosts = [];
|
||||
$forumPostInfos = $forumPosts->getPosts(categoryInfo: $forumCategoryIds, searchQuery: $searchQueryEvaluated);
|
||||
$fposts = [];
|
||||
|
||||
foreach($forumPostInfos as $postInfo) {
|
||||
$forumPosts[] = $post = new stdClass;
|
||||
$fposts[] = $post = new stdClass;
|
||||
$post->info = $postInfo;
|
||||
|
||||
if($postInfo->hasUserId()) {
|
||||
$post->user = $usersCtx->getUserInfo($postInfo->getUserId(), 'id');
|
||||
$post->colour = $usersCtx->getUserColour($post->user);
|
||||
$post->postsCount = $forum->countPosts(userInfo: $post->user, deleted: false);
|
||||
$post->postsCount = $forumCtx->countTotalUserPosts($post->user);
|
||||
}
|
||||
|
||||
// can't be bothered sorry
|
||||
|
@ -165,15 +168,15 @@ if(!empty($searchQuery)) {
|
|||
$members[] = $member = new stdClass;
|
||||
$member->info = $memberInfo;
|
||||
$member->colour = $usersCtx->getUserColour($memberInfo);
|
||||
$member->ftopics = $forum->countTopics(userInfo: $memberInfo, deleted: false);
|
||||
$member->fposts = $forum->countPosts(userInfo: $memberInfo, deleted: false);
|
||||
$member->ftopics = $forumCtx->countTotalUserTopics($memberInfo);
|
||||
$member->fposts = $forumCtx->countTotalUserPosts($memberInfo);
|
||||
}
|
||||
}
|
||||
|
||||
Template::render('home.search', [
|
||||
'search_query' => $searchQuery,
|
||||
'forum_topics' => $forumTopics ?? [],
|
||||
'forum_posts' => $forumPosts ?? [],
|
||||
'forum_topics' => $ftopics ?? [],
|
||||
'forum_posts' => $fposts ?? [],
|
||||
'members' => $members ?? [],
|
||||
'news_posts' => $newsPosts ?? [],
|
||||
]);
|
||||
|
|
|
@ -37,7 +37,10 @@ if(!$isRestricted && $isVerifiedRequest && !empty($_POST['role'])) {
|
|||
case 'leave':
|
||||
if($roleInfo->isLeavable()) {
|
||||
$users->removeRoles($userInfo, $roleInfo);
|
||||
$msz->getPerms()->precalculatePermissions($msz->getForum(), [$userInfo->getId()]);
|
||||
$msz->getPerms()->precalculatePermissions(
|
||||
$msz->getForumContext()->getCategories(),
|
||||
[$userInfo->getId()]
|
||||
);
|
||||
} else
|
||||
$errors[] = "You're not allow to leave this role, an administrator has to remove it for you.";
|
||||
break;
|
||||
|
|
1571
src/Forum/Forum.php
1571
src/Forum/Forum.php
File diff suppressed because it is too large
Load diff
507
src/Forum/ForumCategories.php
Normal file
507
src/Forum/ForumCategories.php
Normal file
|
@ -0,0 +1,507 @@
|
|||
<?php
|
||||
namespace Misuzu\Forum;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
use Index\Colour\Colour;
|
||||
use Index\Data\DbStatementCache;
|
||||
use Index\Data\DbTools;
|
||||
use Index\Data\IDbConnection;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Users\UserInfo;
|
||||
|
||||
class ForumCategories {
|
||||
private IDbConnection $dbConn;
|
||||
private DbStatementCache $cache;
|
||||
|
||||
public function __construct(IDbConnection $dbConn) {
|
||||
$this->dbConn = $dbConn;
|
||||
$this->cache = new DbStatementCache($dbConn);
|
||||
}
|
||||
|
||||
public static function convertCategoryListToTree(
|
||||
array $catInfos,
|
||||
ForumCategoryInfo|string|null $parentInfo = null,
|
||||
?Colour $colour = null
|
||||
): array {
|
||||
$colour ??= Colour::none();
|
||||
$tree = [];
|
||||
$predicate = $parentInfo
|
||||
? fn($catInfo) => $catInfo->isDirectChildOf($parentInfo)
|
||||
: fn($catInfo) => !$catInfo->hasParent();
|
||||
|
||||
foreach($catInfos as $catInfo) {
|
||||
if(!$predicate($catInfo))
|
||||
continue;
|
||||
|
||||
$tree[$catInfo->getId()] = $item = new stdClass;
|
||||
$item->info = $catInfo;
|
||||
$item->colour = $catInfo->hasColour() ? $catInfo->getColour() : $colour;
|
||||
$item->children = self::convertCategoryListToTree($catInfos, $catInfo, $item->colour);
|
||||
$item->childIds = [];
|
||||
foreach($item->children as $child) {
|
||||
$item->childIds[] = $child->info->getId();
|
||||
$item->childIds += $child->childIds;
|
||||
}
|
||||
}
|
||||
|
||||
return $tree;
|
||||
}
|
||||
|
||||
public function countCategories(
|
||||
ForumCategoryInfo|string|null|false $parentInfo = false,
|
||||
string|int|null $type = null,
|
||||
?bool $hidden = null
|
||||
): int {
|
||||
if($parentInfo instanceof ForumCategoryInfo)
|
||||
$parentInfo = $parentInfo->getId();
|
||||
|
||||
$isRootParent = false;
|
||||
$hasParentInfo = $parentInfo !== false;
|
||||
$hasType = $type !== null;
|
||||
$hasHidden = $hidden !== null;
|
||||
|
||||
$args = 0;
|
||||
$query = 'SELECT COUNT(*) FROM msz_forum_categories';
|
||||
if($hasParentInfo) {
|
||||
++$args;
|
||||
$isRootParent = $parentInfo === null;
|
||||
if($isRootParent) { // make a migration that makes the field DEFAULT NULL and update all 0s to NULL
|
||||
$query .= 'WHERE (forum_parent IS NULL OR forum_parent = 0)';
|
||||
} else {
|
||||
$query .= 'WHERE forum_parent = ?';
|
||||
}
|
||||
}
|
||||
if($hasType) {
|
||||
if(is_string($type)) {
|
||||
if(!array_key_exists($type, ForumCategoryInfo::TYPE_ALIASES))
|
||||
throw new InvalidArgumentException('$type is not a valid alias.');
|
||||
$type = ForumCategoryInfo::TYPE_ALIASES[$type];
|
||||
}
|
||||
|
||||
$query .= sprintf(' %s forum_type = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
}
|
||||
if($hasHidden)
|
||||
$query .= sprintf(' %s forum_hidden %s 0', ++$args > 1 ? 'AND' : 'WHERE', $hidden ? '<>' : '=');
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
if($hasParentInfo && !$isRootParent)
|
||||
$stmt->addParameter(++$args, $parentInfo);
|
||||
if($hasType)
|
||||
$stmt->addParameter(++$args, $type);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
return $result->next() ? $result->getInteger(0) : 0;
|
||||
}
|
||||
|
||||
public function getCategories(
|
||||
ForumCategoryInfo|string|null|false $parentInfo = false,
|
||||
string|int|null $type = null,
|
||||
?bool $hidden = null,
|
||||
bool $asTree = false,
|
||||
?Pagination $pagination = null
|
||||
): array {
|
||||
$isRootParent = false;
|
||||
$hasParentInfo = $parentInfo !== false;
|
||||
$hasType = $type !== null;
|
||||
$hasHidden = $hidden !== null;
|
||||
$hasPagination = $pagination !== null;
|
||||
|
||||
if($hasParentInfo && $asTree)
|
||||
throw new InvalidArgumentException('$asTree can only be used with $parentInfo set to false.');
|
||||
|
||||
$args = 0;
|
||||
$query = 'SELECT forum_id, forum_order, forum_parent, forum_name, forum_type, forum_description, forum_icon, forum_colour, forum_link, forum_link_clicks, UNIX_TIMESTAMP(forum_created), forum_archived, forum_hidden, forum_count_topics, forum_count_posts FROM msz_forum_categories';
|
||||
if($hasParentInfo) {
|
||||
++$args;
|
||||
$isRootParent = $parentInfo === null;
|
||||
if($isRootParent) { // make a migration that makes the field DEFAULT NULL and update all 0s to NULL
|
||||
$query .= ' WHERE (forum_parent IS NULL OR forum_parent = 0)';
|
||||
} else {
|
||||
$query .= ' WHERE forum_parent = ?';
|
||||
}
|
||||
}
|
||||
if($hasType) {
|
||||
if(is_string($type)) {
|
||||
if(!array_key_exists($type, ForumCategoryInfo::TYPE_ALIASES))
|
||||
throw new InvalidArgumentException('$type is not a valid alias.');
|
||||
$type = ForumCategoryInfo::TYPE_ALIASES[$type];
|
||||
}
|
||||
|
||||
$query .= sprintf(' %s forum_type = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
}
|
||||
if($hasHidden)
|
||||
$query .= sprintf(' %s forum_hidden %s 0', ++$args > 1 ? 'AND' : 'WHERE', $hidden ? '<>' : '=');
|
||||
$query .= ' ORDER BY forum_parent, forum_type <> 1, forum_order';
|
||||
if($hasPagination)
|
||||
$query .= ' LIMIT ? OFFSET ?';
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
if($hasParentInfo && !$isRootParent) {
|
||||
if($parentInfo instanceof ForumCategoryInfo)
|
||||
$stmt->addParameter(++$args, $parentInfo->getId());
|
||||
else
|
||||
$stmt->addParameter(++$args, $parentInfo);
|
||||
}
|
||||
if($hasType)
|
||||
$stmt->addParameter(++$args, $type);
|
||||
if($hasPagination) {
|
||||
$stmt->addParameter(++$args, $pagination->getRange());
|
||||
$stmt->addParameter(++$args, $pagination->getOffset());
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$cats = [];
|
||||
|
||||
while($result->next())
|
||||
$cats[] = new ForumCategoryInfo($result);
|
||||
|
||||
if($asTree)
|
||||
$cats = self::convertCategoryListToTree($cats);
|
||||
|
||||
return $cats;
|
||||
}
|
||||
|
||||
public function getCategory(
|
||||
?string $categoryId = null,
|
||||
ForumTopicInfo|string|null $topicInfo = null,
|
||||
ForumPostInfo|string|null $postInfo = null
|
||||
): ForumCategoryInfo {
|
||||
$hasCategoryId = $categoryId !== null;
|
||||
$hasTopicInfo = $topicInfo !== null;
|
||||
$hasPostInfo = $postInfo !== null;
|
||||
|
||||
if(!$hasCategoryId && !$hasTopicInfo && !$hasPostInfo)
|
||||
throw new InvalidArgumentException('You must specify an argument.');
|
||||
if(($hasCategoryId && ($hasTopicInfo || $hasPostInfo))
|
||||
|| ($hasTopicInfo && ($hasCategoryId || $hasPostInfo))
|
||||
|| ($hasPostInfo && ($hasCategoryId || $hasTopicInfo)))
|
||||
throw new InvalidArgumentException('Only one argument may be specified.');
|
||||
|
||||
$value = null;
|
||||
$query = 'SELECT forum_id, forum_order, forum_parent, forum_name, forum_type, forum_description, forum_icon, forum_colour, forum_link, forum_link_clicks, UNIX_TIMESTAMP(forum_created), forum_archived, forum_hidden, forum_count_topics, forum_count_posts FROM msz_forum_categories';
|
||||
if($hasCategoryId) {
|
||||
$query .= ' WHERE forum_id = ?';
|
||||
$value = $categoryId;
|
||||
}
|
||||
if($hasTopicInfo) {
|
||||
if($topicInfo instanceof ForumTopicInfo) {
|
||||
$query .= ' WHERE forum_id = ?';
|
||||
$value = $topicInfo->getCategoryId();
|
||||
} else {
|
||||
$query .= ' WHERE forum_id = (SELECT forum_id FROM msz_forum_topics WHERE topic_id = ?)';
|
||||
$value = $topicInfo;
|
||||
}
|
||||
}
|
||||
if($hasPostInfo) {
|
||||
if($postInfo instanceof ForumPostInfo) {
|
||||
$query .= ' WHERE forum_id = ?';
|
||||
$value = $postInfo->getCategoryId();
|
||||
} else {
|
||||
$query .= ' WHERE forum_id = (SELECT forum_id FROM msz_forum_posts WHERE post_id = ?)';
|
||||
$value = $postInfo;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
$stmt->addParameter(1, $value);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('Forum category info not found.');
|
||||
|
||||
return new ForumCategoryInfo($result);
|
||||
}
|
||||
|
||||
public function updateCategory(
|
||||
ForumCategoryInfo|string $categoryInfo
|
||||
): void {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
}
|
||||
|
||||
public function deleteCategory(ForumCategoryInfo|string $categoryInfo): void {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
}
|
||||
|
||||
public function incrementCategoryClicks(ForumCategoryInfo|string $categoryInfo): void {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
|
||||
// previous implementation also WHERE'd for forum_type = link but i don't think there's any other way to get here anyhow
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_categories SET forum_link_clicks = forum_link_clicks + 1 WHERE forum_id = ? AND forum_link_clicks IS NOT NULL');
|
||||
$stmt->addParameter(1, $categoryInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function incrementCategoryTopics(ForumCategoryInfo|string $categoryInfo): void {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_categories SET forum_count_topics = forum_count_topics + 1 WHERE forum_id = ?');
|
||||
$stmt->addParameter(1, $categoryInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function incrementCategoryPosts(ForumCategoryInfo|string $categoryInfo): void {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_categories SET forum_count_posts = forum_count_posts + 1 WHERE forum_id = ?');
|
||||
$stmt->addParameter(1, $categoryInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function getCategoryAncestry(
|
||||
ForumCategoryInfo|ForumTopicInfo|ForumPostInfo|string $categoryInfo
|
||||
): array {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
elseif($categoryInfo instanceof ForumTopicInfo || $categoryInfo instanceof ForumPostInfo)
|
||||
$categoryInfo = $categoryInfo->getCategoryId();
|
||||
|
||||
$query = 'WITH RECURSIVE msz_cte_ancestry AS ('
|
||||
. 'SELECT forum_id, forum_order, forum_parent, forum_name, forum_type, forum_description, forum_icon, forum_colour, forum_link, forum_link_clicks, UNIX_TIMESTAMP(forum_created), forum_archived, forum_hidden, forum_count_topics, forum_count_posts FROM msz_forum_categories WHERE forum_id = ?'
|
||||
. ' UNION ALL'
|
||||
. ' SELECT fc.forum_id, fc.forum_order, fc.forum_parent, fc.forum_name, fc.forum_type, fc.forum_description, fc.forum_icon, fc.forum_colour, fc.forum_link, fc.forum_link_clicks, UNIX_TIMESTAMP(fc.forum_created), fc.forum_archived, fc.forum_hidden, fc.forum_count_topics, fc.forum_count_posts FROM msz_forum_categories AS fc JOIN msz_cte_ancestry AS ca ON fc.forum_id = ca.forum_parent'
|
||||
. ') SELECT * FROM msz_cte_ancestry';
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
$stmt->addParameter(1, $categoryInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$cats = [];
|
||||
|
||||
while($result->next())
|
||||
$cats[] = new ForumCategoryInfo($result);
|
||||
|
||||
return $cats;
|
||||
}
|
||||
|
||||
public function getCategoryChildren(
|
||||
ForumCategoryInfo|string $parentInfo,
|
||||
bool $includeSelf = false,
|
||||
?bool $hidden = null,
|
||||
bool $asTree = false
|
||||
): array {
|
||||
if($parentInfo instanceof ForumCategoryInfo)
|
||||
$parentInfo = $parentInfo->getId();
|
||||
|
||||
$hasHidden = $hidden !== null;
|
||||
|
||||
$query = 'WITH RECURSIVE msz_cte_children AS ('
|
||||
. 'SELECT forum_id, forum_order, forum_parent, forum_name, forum_type, forum_description, forum_icon, forum_colour, forum_link, forum_link_clicks, UNIX_TIMESTAMP(forum_created), forum_archived, forum_hidden, forum_count_topics, forum_count_posts FROM msz_forum_categories WHERE forum_id = ?'
|
||||
. ' UNION ALL'
|
||||
. ' SELECT fc.forum_id, fc.forum_order, fc.forum_parent, fc.forum_name, fc.forum_type, fc.forum_description, fc.forum_icon, fc.forum_colour, fc.forum_link, fc.forum_link_clicks, UNIX_TIMESTAMP(fc.forum_created), fc.forum_archived, fc.forum_hidden, fc.forum_count_topics, fc.forum_count_posts FROM msz_forum_categories AS fc JOIN msz_cte_children AS cc ON fc.forum_parent = cc.forum_id'
|
||||
. ') SELECT * FROM msz_cte_children';
|
||||
|
||||
$args = 0;
|
||||
if(!$includeSelf) {
|
||||
++$args;
|
||||
$query .= ' WHERE forum_id <> ?';
|
||||
}
|
||||
if($hasHidden)
|
||||
$query .= sprintf(' %s forum_hidden %s 0', ++$args > 1 ? 'AND' : 'WHERE', $hidden ? '<>' : '=');
|
||||
$query .= ' ORDER BY forum_parent, forum_order';
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
$stmt->addParameter(++$args, $parentInfo);
|
||||
if(!$includeSelf)
|
||||
$stmt->addParameter(++$args, $parentInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$cats = [];
|
||||
|
||||
while($result->next())
|
||||
$cats[] = new ForumCategoryInfo($result);
|
||||
|
||||
if($asTree)
|
||||
$cats = self::convertCategoryListToTree($cats, $parentInfo);
|
||||
|
||||
return $cats;
|
||||
}
|
||||
|
||||
public function checkCategoryUnread(
|
||||
ForumCategoryInfo|string|array $categoryInfos,
|
||||
UserInfo|string|null $userInfo
|
||||
): bool {
|
||||
if($userInfo === null)
|
||||
return false;
|
||||
|
||||
if(!is_array($categoryInfos))
|
||||
$categoryInfos = [$categoryInfos];
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get(sprintf(
|
||||
'SELECT COUNT(*) FROM msz_forum_topics AS ft LEFT JOIN msz_forum_topics_track AS ftt ON ftt.topic_id = ft.topic_id AND ftt.user_id = ? WHERE ft.forum_id IN (%s) AND ft.topic_deleted IS NULL AND ft.topic_bumped >= NOW() - INTERVAL 1 MONTH AND (ftt.track_last_read IS NULL OR ftt.track_last_read < ft.topic_bumped)',
|
||||
DbTools::prepareListString($categoryInfos)
|
||||
));
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
foreach($categoryInfos as $categoryInfo) {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$stmt->addParameter(++$args, $categoryInfo->getId());
|
||||
elseif(is_string($categoryInfo) || is_int($categoryInfo))
|
||||
$stmt->addParameter(++$args, $categoryInfo);
|
||||
else
|
||||
throw new InvalidArgumentException('Invalid item in $categoryInfos.');
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
return $result->next() && $result->getInteger(0) > 0;
|
||||
}
|
||||
|
||||
public function updateUserReadCategory(
|
||||
UserInfo|string|null $userInfo,
|
||||
ForumCategoryInfo|string $categoryInfo
|
||||
): void {
|
||||
if($userInfo === null)
|
||||
return;
|
||||
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
if($categoryInfo instanceof $categoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('REPLACE INTO msz_forum_topics_track (user_id, topic_id, forum_id, track_last_read) SELECT ?, topic_id, forum_id, NOW() FROM msz_forum_topics WHERE forum_id = ? AND topic_bumped >= NOW() - INTERVAL 1 MONTH');
|
||||
$stmt->addParameter(1, $userInfo);
|
||||
$stmt->addParameter(2, $categoryInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function getCategoryColour(
|
||||
ForumCategoryInfo|ForumTopicInfo|ForumPostInfo|string $categoryInfo
|
||||
): Colour {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
elseif($categoryInfo instanceof ForumTopicInfo || $categoryInfo instanceof ForumPostInfo)
|
||||
$categoryInfo = $categoryInfo->getCategoryId();
|
||||
|
||||
$query = 'WITH RECURSIVE msz_cte_colours AS ('
|
||||
. 'SELECT forum_id, forum_parent, forum_colour FROM msz_forum_categories WHERE forum_id = ?'
|
||||
. ' UNION ALL'
|
||||
. ' SELECT fc.forum_id, fc.forum_parent, fc.forum_colour FROM msz_forum_categories AS fc JOIN msz_cte_colours AS cc ON fc.forum_id = cc.forum_parent'
|
||||
. ') SELECT forum_colour FROM msz_cte_colours WHERE forum_colour IS NOT NULL';
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
$stmt->addParameter(1, $categoryInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
return $result->next() ? Colour::fromMisuzu($result->getInteger(0)) : Colour::none();
|
||||
}
|
||||
|
||||
public function getMostActiveCategoryInfo(
|
||||
UserInfo|string $userInfo,
|
||||
array $exceptCategoryInfos = [],
|
||||
array $exceptTopicInfos = [],
|
||||
?bool $deleted = null
|
||||
): object {
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$hasExceptCategoryInfos = !empty($exceptCategoryInfos);
|
||||
$hasExceptTopicInfos = !empty($exceptTopicInfos);
|
||||
$hasDeleted = $deleted !== null;
|
||||
|
||||
$query = 'SELECT forum_id, COUNT(*) AS post_count FROM msz_forum_posts WHERE user_id = ?';
|
||||
if($hasDeleted)
|
||||
$query .= sprintf(' AND post_deleted %s NULL', $deleted ? 'IS NOT' : 'IS');
|
||||
if($hasExceptCategoryInfos)
|
||||
$query .= sprintf(' AND forum_id NOT IN (%s)', DbTools::prepareListString($exceptCategoryInfos));
|
||||
if($hasExceptTopicInfos)
|
||||
$query .= sprintf(' AND topic_id NOT IN (%s)', DbTools::prepareListString($exceptTopicInfos));
|
||||
$query .= ' GROUP BY forum_id ORDER BY post_count DESC LIMIT 1';
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
foreach($exceptCategoryInfos as $categoryInfo) {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$stmt->addParameter(++$args, $categoryInfo->getId());
|
||||
elseif(is_string($categoryInfo) || is_int($categoryInfo))
|
||||
$stmt->addParameter(++$args, (string)$categoryInfo);
|
||||
else
|
||||
throw new InvalidArgumentException('$exceptCategoryInfos may only contain string ids or instances of ForumCategoryInfo.');
|
||||
}
|
||||
foreach($exceptTopicInfos as $topicInfo) {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$stmt->addParameter(++$args, $topicInfo->getId());
|
||||
elseif(is_string($topicInfo) || is_int($topicInfo))
|
||||
$stmt->addParameter(++$args, (string)$topicInfo);
|
||||
else
|
||||
throw new InvalidArgumentException('$exceptTopicInfos may only contain string ids or instances of ForumTopicInfo.');
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$info = new stdClass;
|
||||
$info->success = $result->next();
|
||||
if($info->success) {
|
||||
$info->categoryId = $result->getString(0);
|
||||
$info->postCount = $result->getInteger(1);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
public function syncForumCounters(
|
||||
ForumCategoryInfo|string|null $categoryInfo = null,
|
||||
bool $updateCounters = true
|
||||
): object {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
elseif($categoryInfo === null)
|
||||
$categoryInfo = '0';
|
||||
|
||||
$counters = new stdClass;
|
||||
|
||||
$stmt = $this->cache->get('SELECT ? AS target_category_id, (SELECT COUNT(*) FROM msz_forum_topics WHERE forum_id = target_category_id AND topic_deleted IS NULL) AS count_topics, (SELECT COUNT(*) FROM msz_forum_posts WHERE forum_id = target_category_id AND post_deleted IS NULL) AS count_posts');
|
||||
$stmt->addParameter(1, $categoryInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('Failed to fetch forum category counters.');
|
||||
|
||||
$counters->topics = $result->getInteger(1);
|
||||
$counters->posts = $result->getInteger(2);
|
||||
|
||||
$stmt = $this->cache->get('SELECT forum_id FROM msz_forum_categories WHERE forum_parent = ?');
|
||||
$stmt->addParameter(1, $categoryInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$children = [];
|
||||
$result = $stmt->getResult();
|
||||
while($result->next())
|
||||
$children[] = $result->getString(0);
|
||||
|
||||
foreach($children as $childId) {
|
||||
$childCounters = $this->syncForumCounters($childId, $updateCounters);
|
||||
$counters->topics += $childCounters->topics;
|
||||
$counters->posts += $childCounters->posts;
|
||||
}
|
||||
|
||||
if($updateCounters && $categoryInfo !== '0') {
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_categories SET forum_count_topics = ?, forum_count_posts = ? WHERE forum_id = ?');
|
||||
$stmt->addParameter(1, $counters->topics);
|
||||
$stmt->addParameter(2, $counters->posts);
|
||||
$stmt->addParameter(3, $categoryInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
return $counters;
|
||||
}
|
||||
}
|
63
src/Forum/ForumContext.php
Normal file
63
src/Forum/ForumContext.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
namespace Misuzu\Forum;
|
||||
|
||||
use stdClass;
|
||||
use Index\Data\IDbConnection;
|
||||
use Misuzu\Users\UserInfo;
|
||||
|
||||
class ForumContext {
|
||||
private ForumCategories $categories;
|
||||
private ForumTopics $topics;
|
||||
private ForumTopicRedirects $topicRedirects;
|
||||
private ForumPosts $posts;
|
||||
|
||||
private array $totalUserTopics = [];
|
||||
private array $totalUserPosts = [];
|
||||
|
||||
public function __construct(IDbConnection $dbConn) {
|
||||
$this->categories = new ForumCategories($dbConn);
|
||||
$this->topics = new ForumTopics($dbConn);
|
||||
$this->topicRedirects = new ForumTopicRedirects($dbConn);
|
||||
$this->posts = new ForumPosts($dbConn);
|
||||
}
|
||||
|
||||
public function getCategories(): ForumCategories {
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
public function getTopics(): ForumTopics {
|
||||
return $this->topics;
|
||||
}
|
||||
|
||||
public function getTopicRedirects(): ForumTopicRedirects {
|
||||
return $this->topicRedirects;
|
||||
}
|
||||
|
||||
public function getPosts(): ForumPosts {
|
||||
return $this->posts;
|
||||
}
|
||||
|
||||
// should be replaced by a static counter
|
||||
public function countTotalUserTopics(UserInfo|string|null $userInfo): int {
|
||||
if($userInfo === null)
|
||||
return 0;
|
||||
|
||||
$userId = $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo;
|
||||
if(array_key_exists($userId, $this->totalUserTopics))
|
||||
return $this->totalUserTopics[$userId];
|
||||
|
||||
return $this->totalUserTopics[$userId] = $this->topics->countTopics(userInfo: $userInfo, deleted: false);
|
||||
}
|
||||
|
||||
// should be replaced by a static counter
|
||||
public function countTotalUserPosts(UserInfo|string|null $userInfo): int {
|
||||
if($userInfo === null)
|
||||
return 0;
|
||||
|
||||
$userId = $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo;
|
||||
if(array_key_exists($userId, $this->totalUserPosts))
|
||||
return $this->totalUserPosts[$userId];
|
||||
|
||||
return $this->totalUserPosts[$userId] = $this->posts->countPosts(userInfo: $userInfo, deleted: false);
|
||||
}
|
||||
}
|
461
src/Forum/ForumPosts.php
Normal file
461
src/Forum/ForumPosts.php
Normal file
|
@ -0,0 +1,461 @@
|
|||
<?php
|
||||
namespace Misuzu\Forum;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
use Index\DateTime;
|
||||
use Index\Data\DbStatementCache;
|
||||
use Index\Data\DbTools;
|
||||
use Index\Data\IDbConnection;
|
||||
use Index\Net\IPAddress;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Users\UserInfo;
|
||||
|
||||
class ForumPosts {
|
||||
private IDbConnection $dbConn;
|
||||
private DbStatementCache $cache;
|
||||
|
||||
public function __construct(IDbConnection $dbConn) {
|
||||
$this->dbConn = $dbConn;
|
||||
$this->cache = new DbStatementCache($dbConn);
|
||||
}
|
||||
|
||||
public function countPosts(
|
||||
ForumCategoryInfo|string|null $categoryInfo = null,
|
||||
ForumTopicInfo|string|null $topicInfo = null,
|
||||
UserInfo|string|null $userInfo = null,
|
||||
ForumPostInfo|string|null $upToPostInfo = null,
|
||||
?bool $deleted = null
|
||||
): int {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
if($upToPostInfo instanceof ForumPostInfo)
|
||||
$upToPostInfo = $upToPostInfo->getId();
|
||||
|
||||
$hasCategoryInfo = $categoryInfo !== null;
|
||||
$hasTopicInfo = $topicInfo !== null;
|
||||
$hasUserInfo = $userInfo !== null;
|
||||
$hasUpToPostInfo = $upToPostInfo !== null;
|
||||
$hasDeleted = $deleted !== null;
|
||||
|
||||
$args = 0;
|
||||
$query = 'SELECT COUNT(*) FROM msz_forum_posts';
|
||||
if($hasCategoryInfo) {
|
||||
++$args;
|
||||
$query .= ' WHERE forum_id = ?';
|
||||
}
|
||||
if($hasTopicInfo)
|
||||
$query .= sprintf(' %s topic_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasUserInfo)
|
||||
$query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasUpToPostInfo)
|
||||
$query .= sprintf(' %s post_id < ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasDeleted)
|
||||
$query .= sprintf(' %s post_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS');
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
if($hasCategoryInfo)
|
||||
$stmt->addParameter(++$args, $categoryInfo);
|
||||
if($hasTopicInfo)
|
||||
$stmt->addParameter(++$args, $topicInfo);
|
||||
if($hasUserInfo)
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
if($hasUpToPostInfo)
|
||||
$stmt->addParameter(++$args, $upToPostInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
return $result->next() ? $result->getInteger(0) : 0;
|
||||
}
|
||||
|
||||
public function getPosts(
|
||||
ForumCategoryInfo|string|array|null $categoryInfo = null,
|
||||
ForumTopicInfo|string|null $topicInfo = null,
|
||||
UserInfo|string|null $userInfo = null,
|
||||
ForumPostInfo|string|null $upToPostInfo = null,
|
||||
ForumPostInfo|string|null $afterPostInfo = null,
|
||||
?int $newerThanDays = null,
|
||||
?array $searchQuery = null,
|
||||
?bool $deleted = null,
|
||||
?Pagination $pagination = null
|
||||
): array {
|
||||
// remove this hack when search server
|
||||
$hasSearchQuery = $searchQuery !== null;
|
||||
$doSearchOrder = false;
|
||||
if($hasSearchQuery) {
|
||||
if(!empty($searchQuery['type'])
|
||||
&& $searchQuery['type'] !== 'forum'
|
||||
&& $searchQuery['type'] !== 'forum:post')
|
||||
return [];
|
||||
|
||||
$userInfo = null;
|
||||
$deleted = false;
|
||||
$pagination = null;
|
||||
$doSearchOrder = true;
|
||||
$afterPostInfo = null;
|
||||
$newerThanDays = null;
|
||||
|
||||
if(!empty($searchQuery['author']))
|
||||
$userInfo = $searchQuery['author'];
|
||||
|
||||
if(!empty($searchQuery['after']))
|
||||
$afterPostInfo = $searchQuery['after'];
|
||||
|
||||
$searchQuery = $searchQuery['query_string'];
|
||||
$hasSearchQuery = !empty($searchQuery);
|
||||
}
|
||||
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
if($upToPostInfo instanceof ForumPostInfo)
|
||||
$upToPostInfo = $upToPostInfo->getId();
|
||||
if($afterPostInfo instanceof ForumPostInfo)
|
||||
$afterPostInfo = $afterPostInfo->getId();
|
||||
|
||||
$hasCategoryInfo = $categoryInfo !== null;
|
||||
$hasTopicInfo = $topicInfo !== null;
|
||||
$hasUserInfo = $userInfo !== null;
|
||||
$hasUpToPostInfo = $upToPostInfo !== null;
|
||||
$hasAfterPostInfo = $afterPostInfo !== null;
|
||||
$hasNewerThanDays = $newerThanDays !== null;
|
||||
$hasDeleted = $deleted !== null;
|
||||
$hasPagination = $pagination !== null;
|
||||
|
||||
$args = 0;
|
||||
$query = 'SELECT post_id, topic_id, forum_id, user_id, INET6_NTOA(post_ip), post_text, post_parse, post_display_signature, UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_edited), UNIX_TIMESTAMP(post_deleted) FROM msz_forum_posts';
|
||||
if($hasCategoryInfo) {
|
||||
++$args;
|
||||
if(is_array($categoryInfo))
|
||||
$query .= sprintf(' WHERE forum_id IN (%s)', DbTools::prepareListString($categoryInfo));
|
||||
else
|
||||
$query .= ' WHERE forum_id = ?';
|
||||
}
|
||||
if($hasTopicInfo)
|
||||
$query .= sprintf(' %s topic_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasUserInfo)
|
||||
$query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasUpToPostInfo)
|
||||
$query .= sprintf(' %s post_id < ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasAfterPostInfo)
|
||||
$query .= sprintf(' %s post_id > ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasNewerThanDays)
|
||||
$query .= sprintf(' %s post_created > NOW() - INTERVAL ? DAY', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasSearchQuery)
|
||||
$query .= sprintf(' %s MATCH(post_text) AGAINST (? IN NATURAL LANGUAGE MODE)', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasDeleted)
|
||||
$query .= sprintf(' %s post_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS');
|
||||
if($doSearchOrder) {
|
||||
$query .= ' ORDER BY post_id ASC LIMIT 20';
|
||||
} else {
|
||||
$query .= ' ORDER BY post_id ASC';
|
||||
if($hasPagination)
|
||||
$query .= ' LIMIT ? OFFSET ?';
|
||||
}
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
if($hasCategoryInfo) {
|
||||
if(is_array($categoryInfo)) {
|
||||
foreach($categoryInfo as $categoryInfoEntry)
|
||||
$stmt->addParameter(++$args, $categoryInfoEntry instanceof ForumCategoryInfo ? $categoryInfoEntry->getId() : (string)$categoryInfoEntry);
|
||||
} else
|
||||
$stmt->addParameter(++$args, $categoryInfo);
|
||||
}
|
||||
if($hasTopicInfo)
|
||||
$stmt->addParameter(++$args, $topicInfo);
|
||||
if($hasUserInfo)
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
if($hasUpToPostInfo)
|
||||
$stmt->addParameter(++$args, $upToPostInfo);
|
||||
if($hasAfterPostInfo)
|
||||
$stmt->addParameter(++$args, $afterPostInfo);
|
||||
if($hasNewerThanDays)
|
||||
$stmt->addParameter(++$args, $newerThanDays);
|
||||
if($hasSearchQuery)
|
||||
$stmt->addParameter(++$args, $searchQuery);
|
||||
if($hasPagination) {
|
||||
$stmt->addParameter(++$args, $pagination->getRange());
|
||||
$stmt->addParameter(++$args, $pagination->getOffset());
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$posts = [];
|
||||
|
||||
while($result->next())
|
||||
$posts[] = new ForumPostInfo($result);
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
public function getPost(
|
||||
?string $postId = null,
|
||||
ForumTopicInfo|string|null $topicInfo = null,
|
||||
ForumCategoryInfo|string|array|null $categoryInfos = null,
|
||||
UserInfo|string|null $userInfo = null,
|
||||
bool $getLast = false,
|
||||
?bool $deleted = null
|
||||
): ForumPostInfo {
|
||||
$hasPostId = $postId !== null;
|
||||
$hasTopicInfo = $topicInfo !== null;
|
||||
$hasCategoryInfos = $categoryInfos !== null;
|
||||
$hasUserInfo = $userInfo !== null;
|
||||
$hasDeleted = $deleted !== null;
|
||||
|
||||
if(!$hasPostId && !$hasTopicInfo && !$hasCategoryInfos && !$hasUserInfo)
|
||||
throw new InvalidArgumentException('At least one of the four first arguments must be specified.');
|
||||
|
||||
$values = [];
|
||||
$query = 'SELECT post_id, topic_id, forum_id, user_id, INET6_NTOA(post_ip), post_text, post_parse, post_display_signature, UNIX_TIMESTAMP(post_created), UNIX_TIMESTAMP(post_edited), UNIX_TIMESTAMP(post_deleted) FROM msz_forum_posts';
|
||||
if($hasPostId) {
|
||||
$query .= ' WHERE post_id = ?';
|
||||
$values[] = $postId;
|
||||
} elseif($hasUserInfo) {
|
||||
$query .= ' WHERE user_id = ?';
|
||||
$values[] = $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo;
|
||||
$query .= sprintf(' ORDER BY post_id %s', $getLast ? 'DESC' : 'ASC');
|
||||
} elseif($hasTopicInfo) {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$query .= sprintf(' WHERE post_id = (SELECT %s(post_id) FROM msz_forum_posts WHERE topic_id = ?', $getLast ? 'MAX' : 'MIN');
|
||||
if($hasDeleted)
|
||||
$query .= sprintf(' AND post_deleted %s NULL', $deleted ? 'IS NOT' : 'IS');
|
||||
$query .= ')';
|
||||
|
||||
$values[] = $topicInfo;
|
||||
} elseif($hasCategoryInfos) {
|
||||
if(!is_array($categoryInfos))
|
||||
$categoryInfos = [$categoryInfos];
|
||||
|
||||
$query .= sprintf(
|
||||
' WHERE post_id = (SELECT %s(post_id) FROM msz_forum_posts WHERE forum_id IN (%s)',
|
||||
$getLast ? 'MAX' : 'MIN',
|
||||
DbTools::prepareListString($categoryInfos)
|
||||
);
|
||||
if($hasDeleted)
|
||||
$query .= sprintf(' AND post_deleted %s NULL', $deleted ? 'IS NOT' : 'IS');
|
||||
$query .= ')';
|
||||
|
||||
foreach($categoryInfos as $categoryInfo) {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$values[] = $categoryInfo->getId();
|
||||
elseif(is_string($categoryInfo) || is_int($categoryInfo))
|
||||
$values[] = (string)$categoryInfo;
|
||||
else
|
||||
throw new InvalidArgumentException('$categoryInfos contains an invalid item.');
|
||||
}
|
||||
}
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
foreach($values as $value)
|
||||
$stmt->addParameter(++$args, $value);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('Forum post not found.');
|
||||
|
||||
return new ForumPostInfo($result);
|
||||
}
|
||||
|
||||
public function createPost(
|
||||
ForumTopicInfo|string $topicInfo,
|
||||
UserInfo|string|null $userInfo,
|
||||
IPAddress|string $remoteAddr,
|
||||
string $body,
|
||||
int $bodyParser,
|
||||
bool $displaySignature,
|
||||
ForumCategoryInfo|string|null $categoryInfo = null
|
||||
): ForumPostInfo {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
|
||||
if($topicInfo instanceof ForumTopicInfo) {
|
||||
$categoryInfo ??= $topicInfo->getCategoryId();
|
||||
$topicInfo = $topicInfo->getId();
|
||||
} elseif($categoryInfo === null)
|
||||
throw new InvalidArgumentException('$categoryInfo may only be null if $topicInfo is an instance of ForumTopicInfo.');
|
||||
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
if($remoteAddr instanceof IPAddress)
|
||||
$remoteAddr = (string)$remoteAddr;
|
||||
|
||||
$stmt = $this->cache->get('INSERT INTO msz_forum_posts (topic_id, forum_id, user_id, post_ip, post_text, post_parse, post_display_signature) VALUES (?, ?, ?, INET6_ATON(?), ?, ?, ?)');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->addParameter(2, $categoryInfo);
|
||||
$stmt->addParameter(3, $userInfo);
|
||||
$stmt->addParameter(4, $remoteAddr);
|
||||
$stmt->addParameter(5, $body);
|
||||
$stmt->addParameter(6, $bodyParser);
|
||||
$stmt->addParameter(7, $displaySignature ? 1 : 0);
|
||||
$stmt->execute();
|
||||
|
||||
return $this->getPost(postId: (string)$this->dbConn->getLastInsertId());
|
||||
}
|
||||
|
||||
public function updatePost(
|
||||
ForumPostInfo|string $postInfo,
|
||||
IPAddress|string|null $remoteAddr = null,
|
||||
?string $body = null,
|
||||
?int $bodyParser = null,
|
||||
?bool $displaySignature = null,
|
||||
bool $bumpEdited = true
|
||||
): void {
|
||||
if($postInfo instanceof ForumPostInfo)
|
||||
$postInfo = $postInfo->getId();
|
||||
|
||||
$fields = [];
|
||||
$values = [];
|
||||
|
||||
if($remoteAddr !== null) {
|
||||
if($remoteAddr instanceof IPAddress)
|
||||
$remoteAddr = (string)$remoteAddr;
|
||||
|
||||
$fields[] = 'post_ip = INET6_ATON(?)';
|
||||
$values[] = $remoteAddr;
|
||||
}
|
||||
|
||||
if($body !== null) {
|
||||
$fields[] = 'post_text = ?';
|
||||
$values[] = $body;
|
||||
}
|
||||
|
||||
if($bodyParser !== null) {
|
||||
$fields[] = 'post_parse = ?';
|
||||
$values[] = $bodyParser;
|
||||
}
|
||||
|
||||
if($displaySignature !== null) {
|
||||
$fields[] = 'post_display_signature = ?';
|
||||
$values[] = $displaySignature ? 1 : 0;
|
||||
}
|
||||
|
||||
if(empty($fields))
|
||||
return;
|
||||
|
||||
if($bumpEdited)
|
||||
$fields[] = 'post_edited = NOW()';
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get(sprintf('UPDATE msz_forum_posts SET %s WHERE post_id = ?', implode(', ', $fields)));
|
||||
foreach($values as $value)
|
||||
$stmt->addParameter(++$args, $value);
|
||||
$stmt->addParameter(++$args, $postInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function deletePost(ForumPostInfo|string $postInfo): void {
|
||||
if($postInfo instanceof ForumPostInfo)
|
||||
$postInfo = $postInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_posts SET post_deleted = COALESCE(post_deleted, NOW()) WHERE post_id = ?');
|
||||
$stmt->addParameter(1, $postInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function restorePost(ForumPostInfo|string $postInfo): void {
|
||||
if($postInfo instanceof ForumPostInfo)
|
||||
$postInfo = $postInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_posts SET post_deleted = NULL WHERE post_id = ?');
|
||||
$stmt->addParameter(1, $postInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function nukePost(ForumPostInfo|string $postInfo): void {
|
||||
if($postInfo instanceof ForumPostInfo)
|
||||
$postInfo = $postInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('DELETE FROM msz_forum_posts WHERE post_id = ?');
|
||||
$stmt->addParameter(1, $postInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function getUserLastPostCreatedTime(UserInfo|string $userInfo): int {
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
// intentionally including deleted posts
|
||||
$stmt = $this->cache->get('SELECT UNIX_TIMESTAMP(MAX(post_created)) FROM msz_forum_posts WHERE user_id = ?');
|
||||
$stmt->addParameter(1, $userInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
return 0;
|
||||
|
||||
return $result->getInteger(0);
|
||||
}
|
||||
|
||||
public function getUserLastPostCreatedAt(UserInfo|string $userInfo): DateTime {
|
||||
return DateTime::fromUnixTimeSeconds($this->getUserLastPostCreatedTime($userInfo));
|
||||
}
|
||||
|
||||
public function generatePostRankings(
|
||||
int $year = 0,
|
||||
int $month = 0,
|
||||
array $exceptCategoryInfos = [],
|
||||
array $exceptTopicInfos = []
|
||||
): array {
|
||||
$hasYear = $year > 0;
|
||||
$hasMonth = $hasYear && $month > 0;
|
||||
$hasExcludedCategoryInfos = !empty($exceptCategoryInfos);
|
||||
$hasExcludedTopicInfos = !empty($exceptTopicInfos);
|
||||
|
||||
$query = 'SELECT user_id, COUNT(*) AS posts_count FROM msz_forum_posts WHERE post_deleted IS NULL';
|
||||
if($hasYear)
|
||||
$query .= sprintf(
|
||||
' AND DATE(post_created) BETWEEN "%1$04d-%2$02d-01" AND "%1$04d-%3$02d-31"',
|
||||
$year,
|
||||
$hasMonth ? $month : 1,
|
||||
$hasMonth ? $month : 12
|
||||
);
|
||||
if($hasExcludedCategoryInfos)
|
||||
$query .= sprintf(' AND forum_id NOT IN (%s)', DbTools::prepareListString($exceptCategoryInfos));
|
||||
if($hasExcludedTopicInfos)
|
||||
$query .= sprintf(' AND topic_id NOT IN (%s)', DbTools::prepareListString($exceptTopicInfos));
|
||||
$query .= ' GROUP BY user_id HAVING posts_count > 0 ORDER BY posts_count DESC';
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
foreach($exceptCategoryInfos as $exceptCategoryInfo)
|
||||
$stmt->addParameter(++$args, $exceptCategoryInfo instanceof ForumCategoryInfo ? $exceptCategoryInfo->getId() : $exceptCategoryInfo);
|
||||
foreach($exceptTopicInfos as $exceptTopicInfo)
|
||||
$stmt->addParameter(++$args, $exceptTopicInfo instanceof ForumTopicInfo ? $exceptTopicInfo->getId() : $exceptTopicInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$rankings = [];
|
||||
$rankNo = 0;
|
||||
$lastPostsCount = PHP_INT_MAX;
|
||||
|
||||
while($result->next()) {
|
||||
$rankings[] = $ranking = new stdClass;
|
||||
$ranking->userId = $result->getString(0);
|
||||
$ranking->postsCount = $result->getInteger(1);
|
||||
|
||||
if($lastPostsCount > $ranking->postsCount) {
|
||||
++$rankNo;
|
||||
$lastPostsCount = $ranking->postsCount;
|
||||
}
|
||||
|
||||
$ranking->position = $rankNo;
|
||||
}
|
||||
|
||||
return $rankings;
|
||||
}
|
||||
}
|
132
src/Forum/ForumTopicRedirects.php
Normal file
132
src/Forum/ForumTopicRedirects.php
Normal file
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
namespace Misuzu\Forum;
|
||||
|
||||
use RuntimeException;
|
||||
use Index\Data\DbStatementCache;
|
||||
use Index\Data\IDbConnection;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Users\UserInfo;
|
||||
|
||||
class ForumTopicRedirects {
|
||||
private DbStatementCache $cache;
|
||||
|
||||
public function __construct(IDbConnection $dbConn) {
|
||||
$this->cache = new DbStatementCache($dbConn);
|
||||
}
|
||||
|
||||
public function countTopicRedirects(
|
||||
UserInfo|string|null $userInfo = null
|
||||
): int {
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$hasUserInfo = $userInfo !== null;
|
||||
|
||||
$query = 'SELECT COUNT(*) FROM msz_forum_topics_redirects';
|
||||
if($hasUserInfo)
|
||||
$query .= ' WHERE user_id = ?';
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
if($hasUserInfo)
|
||||
$stmt->addParameter(1, $userInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
return $result->next() ? $result->getInteger(0) : 0;
|
||||
}
|
||||
|
||||
public function getTopicRedirects(
|
||||
UserInfo|string|null $userInfo = null,
|
||||
?Pagination $pagination = null
|
||||
): array {
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$hasUserInfo = $userInfo !== null;
|
||||
$hasPagination = $pagination !== null;
|
||||
|
||||
$query = 'SELECT topic_id, user_id, topic_redir_url, UNIX_TIMESTAMP(topic_redir_created) FROM msz_forum_topics_redirects';
|
||||
if($hasUserInfo)
|
||||
$query .= ' WHERE user_id = ?';
|
||||
if($hasPagination)
|
||||
$query .= ' LIMIT ? OFFSET ?';
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
if($hasUserInfo)
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
if($hasPagination) {
|
||||
$stmt->addParameter(++$args, $pagination->getRange());
|
||||
$stmt->addParameter(++$args, $pagination->getOffset());
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$redirs = [];
|
||||
|
||||
while($result->next())
|
||||
$redirs[] = new ForumTopicRedirectInfo($result);
|
||||
|
||||
return $redirs;
|
||||
}
|
||||
|
||||
public function hasTopicRedirect(ForumTopicInfo|string $topicInfo): bool {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('SELECT COUNT(*) FROM msz_forum_topics_redirects WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('Was unable to check if a redirect exists.');
|
||||
|
||||
return $result->getInteger(0) > 0;
|
||||
}
|
||||
|
||||
public function getTopicRedirect(ForumTopicInfo|string $topicInfo): ForumTopicRedirectInfo {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('SELECT topic_id, user_id, topic_redir_url, UNIX_TIMESTAMP(topic_redir_created) FROM msz_forum_topics_redirects WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('Could not find that forum topic redirect.');
|
||||
|
||||
return new ForumTopicRedirectInfo($result);
|
||||
}
|
||||
|
||||
public function createTopicRedirect(
|
||||
ForumTopicInfo|string $topicInfo,
|
||||
UserInfo|string|null $userInfo,
|
||||
string $linkTarget
|
||||
): ForumTopicRedirectInfo {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('INSERT INTO msz_forum_topics_redirects (topic_id, user_id, topic_redir_url) VALUES (?, ?, ?)');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->addParameter(2, $userInfo);
|
||||
$stmt->addParameter(3, $linkTarget);
|
||||
$stmt->execute();
|
||||
|
||||
return $this->getTopicRedirect($topicInfo);
|
||||
}
|
||||
|
||||
public function deleteTopicRedirect(ForumTopicRedirectInfo|ForumTopicInfo|string $topicInfo): void {
|
||||
if($topicInfo instanceof ForumTopicRedirectInfo)
|
||||
$topicInfo = $topicInfo->getTopicId();
|
||||
elseif($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('DELETE FROM msz_forum_topics_redirects WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
529
src/Forum/ForumTopics.php
Normal file
529
src/Forum/ForumTopics.php
Normal file
|
@ -0,0 +1,529 @@
|
|||
<?php
|
||||
namespace Misuzu\Forum;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use stdClass;
|
||||
use Index\Data\DbStatementCache;
|
||||
use Index\Data\DbTools;
|
||||
use Index\Data\IDbConnection;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Users\UserInfo;
|
||||
|
||||
class ForumTopics {
|
||||
private IDbConnection $dbConn;
|
||||
private DbStatementCache $cache;
|
||||
|
||||
public function __construct(IDbConnection $dbConn) {
|
||||
$this->dbConn = $dbConn;
|
||||
$this->cache = new DbStatementCache($dbConn);
|
||||
}
|
||||
|
||||
public function countTopics(
|
||||
ForumCategoryInfo|string|array|null $categoryInfo = null,
|
||||
UserInfo|string|null $userInfo = null,
|
||||
?bool $global = null,
|
||||
?bool $deleted = null
|
||||
): int {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$hasCategoryInfo = $categoryInfo !== null;
|
||||
$hasUserInfo = $userInfo !== null;
|
||||
$hasGlobal = $global !== null;
|
||||
$hasDeleted = $deleted !== null;
|
||||
|
||||
$args = 0;
|
||||
$query = 'SELECT COUNT(*) FROM msz_forum_topics';
|
||||
if($hasCategoryInfo || $hasGlobal) {
|
||||
++$args;
|
||||
|
||||
// wow this sucks
|
||||
$hasGlobalAndCategory = $hasCategoryInfo && $hasGlobal;
|
||||
$query .= ' WHERE ';
|
||||
if($hasGlobalAndCategory)
|
||||
$query .= '(';
|
||||
|
||||
if($hasCategoryInfo) {
|
||||
if(is_array($categoryInfo))
|
||||
$query .= sprintf('forum_id IN (%s)', DbTools::prepareListString($categoryInfo));
|
||||
else
|
||||
$query .= 'forum_id = ?';
|
||||
}
|
||||
|
||||
if($hasGlobalAndCategory)
|
||||
$query .= ' OR ';
|
||||
|
||||
if($hasGlobal) // not sure why you would ever set this to false, but consistency!
|
||||
$query .= sprintf('topic_type %s %d', $global ? '=' : '<>', ForumTopicInfo::TYPE_GLOBAL);
|
||||
|
||||
if($hasGlobalAndCategory)
|
||||
$query .= ')';
|
||||
}
|
||||
if($hasUserInfo)
|
||||
$query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasDeleted)
|
||||
$query .= sprintf(' %s topic_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS');
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
if($hasCategoryInfo) {
|
||||
if(is_array($categoryInfo)) {
|
||||
foreach($categoryInfo as $categoryInfoEntry)
|
||||
$stmt->addParameter(++$args, $categoryInfoEntry instanceof ForumCategoryInfo ? $categoryInfoEntry->getId() : (string)$categoryInfoEntry);
|
||||
} else
|
||||
$stmt->addParameter(++$args, $categoryInfo);
|
||||
}
|
||||
if($hasUserInfo)
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
return $result->next() ? $result->getInteger(0) : 0;
|
||||
}
|
||||
|
||||
public function getTopics(
|
||||
ForumCategoryInfo|string|array|null $categoryInfo = null,
|
||||
UserInfo|string|null $userInfo = null,
|
||||
?array $searchQuery = null,
|
||||
?bool $global = null,
|
||||
?bool $deleted = null,
|
||||
?Pagination $pagination = null
|
||||
): array {
|
||||
// remove this hack when search server
|
||||
$hasSearchQuery = $searchQuery !== null;
|
||||
$hasAfterTopicId = false;
|
||||
$afterTopicId = null;
|
||||
$doSearchOrder = false;
|
||||
if($hasSearchQuery) {
|
||||
if(!empty($searchQuery['type'])
|
||||
&& $searchQuery['type'] !== 'forum'
|
||||
&& $searchQuery['type'] !== 'forum:topic')
|
||||
return [];
|
||||
|
||||
$deleted = false;
|
||||
$pagination = null;
|
||||
$doSearchOrder = true;
|
||||
|
||||
if(!empty($searchQuery['author']))
|
||||
$userInfo = $searchQuery['author'];
|
||||
|
||||
if(!empty($searchQuery['after'])) {
|
||||
$hasAfterTopicId = true;
|
||||
$afterTopicId = $searchQuery['after'];
|
||||
}
|
||||
|
||||
$searchQuery = $searchQuery['query_string'];
|
||||
$hasSearchQuery = !empty($searchQuery);
|
||||
}
|
||||
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$hasCategoryInfo = $categoryInfo !== null;
|
||||
$hasUserInfo = $userInfo !== null;
|
||||
$hasGlobal = $global !== null;
|
||||
$hasDeleted = $deleted !== null;
|
||||
$hasPagination = $pagination !== null;
|
||||
|
||||
$args = 0;
|
||||
$query = 'SELECT topic_id, forum_id, user_id, topic_type, topic_title, topic_count_views, UNIX_TIMESTAMP(topic_created), UNIX_TIMESTAMP(topic_bumped), UNIX_TIMESTAMP(topic_deleted), UNIX_TIMESTAMP(topic_locked), (SELECT COUNT(*) FROM msz_forum_posts WHERE topic_id = ft.topic_id AND post_deleted IS NULL) AS topic_count_posts, (SELECT COUNT(*) FROM msz_forum_posts WHERE topic_id = ft.topic_id AND post_deleted IS NOT NULL) AS topic_count_posts_deleted FROM msz_forum_topics AS ft';
|
||||
if($hasCategoryInfo || $hasGlobal) {
|
||||
++$args;
|
||||
|
||||
// wow this sucks
|
||||
$hasGlobalAndCategory = $hasCategoryInfo && $hasGlobal;
|
||||
$query .= ' WHERE ';
|
||||
if($hasGlobalAndCategory)
|
||||
$query .= '(';
|
||||
|
||||
if($hasCategoryInfo) {
|
||||
if(is_array($categoryInfo))
|
||||
$query .= sprintf('forum_id IN (%s)', DbTools::prepareListString($categoryInfo));
|
||||
else
|
||||
$query .= 'forum_id = ?';
|
||||
}
|
||||
|
||||
if($hasGlobalAndCategory)
|
||||
$query .= ' OR ';
|
||||
|
||||
if($hasGlobal) // not sure why you would ever set this to false, but consistency!
|
||||
$query .= sprintf('topic_type %s %d', $global ? '=' : '<>', ForumTopicInfo::TYPE_GLOBAL);
|
||||
|
||||
if($hasGlobalAndCategory)
|
||||
$query .= ')';
|
||||
}
|
||||
if($hasUserInfo)
|
||||
$query .= sprintf(' %s user_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasAfterTopicId)
|
||||
$query .= sprintf(' %s topic_id > ?', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasSearchQuery)
|
||||
$query .= sprintf(' %s MATCH(topic_title) AGAINST (? IN NATURAL LANGUAGE MODE)', ++$args > 1 ? 'AND' : 'WHERE');
|
||||
if($hasDeleted)
|
||||
$query .= sprintf(' %s topic_deleted %s NULL', ++$args > 1 ? 'AND' : 'WHERE', $deleted ? 'IS NOT' : 'IS');
|
||||
if($doSearchOrder) {
|
||||
$query .= ' ORDER BY topic_id ASC LIMIT 20';
|
||||
} else {
|
||||
$query .= ' ORDER BY topic_type DESC, topic_bumped DESC';
|
||||
if($hasPagination)
|
||||
$query .= ' LIMIT ? OFFSET ?';
|
||||
}
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
if($hasCategoryInfo) {
|
||||
if(is_array($categoryInfo)) {
|
||||
foreach($categoryInfo as $categoryInfoEntry)
|
||||
$stmt->addParameter(++$args, $categoryInfoEntry instanceof ForumCategoryInfo ? $categoryInfoEntry->getId() : (string)$categoryInfoEntry);
|
||||
} else
|
||||
$stmt->addParameter(++$args, $categoryInfo);
|
||||
}
|
||||
if($hasUserInfo)
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
if($hasAfterTopicId)
|
||||
$stmt->addParameter(++$args, $afterTopicId);
|
||||
if($hasSearchQuery)
|
||||
$stmt->addParameter(++$args, $searchQuery);
|
||||
if($hasPagination) {
|
||||
$stmt->addParameter(++$args, $pagination->getRange());
|
||||
$stmt->addParameter(++$args, $pagination->getOffset());
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$topics = [];
|
||||
|
||||
while($result->next())
|
||||
$topics[] = new ForumTopicInfo($result);
|
||||
|
||||
return $topics;
|
||||
}
|
||||
|
||||
public function getTopic(
|
||||
?string $topicId = null,
|
||||
ForumPostInfo|string|null $postInfo = null
|
||||
): ForumTopicInfo {
|
||||
$hasTopicId = $topicId !== null;
|
||||
$hasPostInfo = $postInfo !== null;
|
||||
|
||||
if(!$hasTopicId && !$hasPostInfo)
|
||||
throw new InvalidArgumentException('At least one argument must be specified.');
|
||||
if($hasTopicId && $hasPostInfo)
|
||||
throw new InvalidArgumentException('Only one argument may be specified.');
|
||||
|
||||
$value = null;
|
||||
$query = 'SELECT topic_id, forum_id, user_id, topic_type, topic_title, topic_count_views, UNIX_TIMESTAMP(topic_created), UNIX_TIMESTAMP(topic_bumped), UNIX_TIMESTAMP(topic_deleted), UNIX_TIMESTAMP(topic_locked), (SELECT COUNT(*) FROM msz_forum_posts WHERE topic_id = ft.topic_id AND post_deleted IS NULL) AS topic_count_posts, (SELECT COUNT(*) FROM msz_forum_posts WHERE topic_id = ft.topic_id AND post_deleted IS NOT NULL) AS topic_count_posts_deleted FROM msz_forum_topics AS ft';
|
||||
if($hasTopicId) {
|
||||
$query .= ' WHERE topic_id = ?';
|
||||
$value = $topicId;
|
||||
}
|
||||
if($hasPostInfo) {
|
||||
if($postInfo instanceof ForumPostInfo) {
|
||||
$query .= ' WHERE topic_id = ?';
|
||||
$value = $postInfo->getTopicId();
|
||||
} else {
|
||||
$query .= ' WHERE topic_id = (SELECT topic_id FROM msz_forum_posts WHERE post_id = ?)';
|
||||
$value = $postInfo;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
$stmt->addParameter(1, $value);
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
if(!$result->next())
|
||||
throw new RuntimeException('Forum topic not found.');
|
||||
|
||||
return new ForumTopicInfo($result);
|
||||
}
|
||||
|
||||
public function createTopic(
|
||||
ForumCategoryInfo|string $categoryInfo,
|
||||
UserInfo|string|null $userInfo,
|
||||
string $title,
|
||||
string|int $type = ForumTopicInfo::TYPE_DISCUSSION
|
||||
): ForumTopicInfo {
|
||||
if(is_string($type)) {
|
||||
if(!array_key_exists($type, ForumTopicInfo::TYPE_ALIASES))
|
||||
throw new InvalidArgumentException('$type is not a valid alias.');
|
||||
$type = ForumTopicInfo::TYPE_ALIASES[$type];
|
||||
}
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('INSERT INTO msz_forum_topics (forum_id, user_id, topic_type, topic_title) VALUES (?, ?, ?, ?)');
|
||||
$stmt->addParameter(1, $categoryInfo);
|
||||
$stmt->addParameter(2, $userInfo);
|
||||
$stmt->addParameter(3, $type);
|
||||
$stmt->addParameter(4, $title);
|
||||
$stmt->execute();
|
||||
|
||||
return $this->getTopic(topicId: (string)$this->dbConn->getLastInsertId());
|
||||
}
|
||||
|
||||
public function updateTopic(
|
||||
ForumTopicInfo|string $topicInfo,
|
||||
?string $title = null,
|
||||
string|int|null $type = null
|
||||
): void {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$fields = [];
|
||||
$values = [];
|
||||
|
||||
if($title !== null) {
|
||||
$fields[] = 'topic_title = ?';
|
||||
$values[] = $title;
|
||||
}
|
||||
|
||||
if($type !== null) {
|
||||
if(is_string($type)) {
|
||||
if(!array_key_exists($type, ForumTopicInfo::TYPE_ALIASES))
|
||||
throw new InvalidArgumentException('$type is not a valid type alias.');
|
||||
|
||||
$type = ForumTopicInfo::TYPE_ALIASES[$type];
|
||||
}
|
||||
|
||||
$fields[] = 'topic_type = ?';
|
||||
$values[] = $type;
|
||||
}
|
||||
|
||||
if(empty($fields))
|
||||
return;
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get(sprintf('UPDATE msz_forum_topics SET %s WHERE topic_id = ?', implode(', ', $fields)));
|
||||
foreach($values as $value)
|
||||
$stmt->addParameter(++$args, $value);
|
||||
$stmt->addParameter(++$args, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function incrementTopicViews(ForumTopicInfo|string $topicInfo): void {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_topics SET topic_count_views = topic_count_views + 1 WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function bumpTopic(ForumTopicInfo|string $topicInfo): void {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_topics SET topic_bumped = NOW() WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function lockTopic(ForumTopicInfo|string $topicInfo): void {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_topics SET topic_locked = NOW() WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function unlockTopic(ForumTopicInfo|string $topicInfo): void {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_topics SET topic_locked = NULL WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function deleteTopic(ForumTopicInfo|string $topicInfo): void {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_topics SET topic_deleted = COALESCE(topic_deleted, NOW()) WHERE topic_id = ? AND topic_deleted IS NULL');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_posts AS fp SET post_deleted = (SELECT topic_deleted FROM msz_forum_topics WHERE topic_id = fp.topic_id) WHERE topic_id = ? AND post_deleted = NULL');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function restoreTopic(ForumTopicInfo|string $topicInfo): void {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_posts AS fp SET post_deleted = NULL WHERE topic_id = ? AND post_deleted = (SELECT topic_deleted FROM msz_forum_topics WHERE topic_id = fp.topic_id)');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
|
||||
$stmt = $this->cache->get('UPDATE msz_forum_topics SET topic_deleted = NULL WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function nukeTopic(ForumTopicInfo|string $topicInfo): void {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('DELETE FROM msz_forum_topics WHERE topic_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function checkTopicParticipated(
|
||||
ForumTopicInfo|string $topicInfo,
|
||||
UserInfo|string|null $userInfo
|
||||
): bool {
|
||||
if($userInfo === null)
|
||||
return false;
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$topicInfo = $topicInfo->getId();
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$stmt = $this->cache->get('SELECT COUNT(*) FROM msz_forum_posts WHERE topic_id = ? AND user_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo);
|
||||
$stmt->addParameter(2, $userInfo);
|
||||
$stmt->execute();
|
||||
$result = $stmt->getResult();
|
||||
|
||||
return $result->next() && $result->getInteger(0) > 0;
|
||||
}
|
||||
|
||||
public function checkTopicUnread(
|
||||
ForumTopicInfo|string $topicInfo,
|
||||
UserInfo|string|null $userInfo
|
||||
): bool {
|
||||
if($userInfo === null)
|
||||
return false;
|
||||
|
||||
$topicInfoIsInstance = $topicInfo instanceof ForumTopicInfo;
|
||||
if($topicInfoIsInstance && !$topicInfo->isActive())
|
||||
return false;
|
||||
|
||||
$query = 'SELECT UNIX_TIMESTAMP(track_last_read) FROM msz_forum_topics_track AS ftt WHERE user_id = ? AND topic_id = ?';
|
||||
if(!$topicInfoIsInstance)
|
||||
$query .= ' AND track_last_read = (SELECT topic_bumped FROM msz_forum_topics WHERE topic_id = ftt.topic_id AND topic_bumped >= NOW() - INTERVAL 1 MONTH)';
|
||||
|
||||
$stmt = $this->cache->get($query);
|
||||
$stmt->addParameter(1, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo);
|
||||
$stmt->addParameter(2, $topicInfoIsInstance ? $topicInfo->getId() : $topicInfo);
|
||||
$stmt->execute();
|
||||
$result = $stmt->getResult();
|
||||
|
||||
// user has never read this topic, return unread
|
||||
if(!$result->next())
|
||||
return true;
|
||||
|
||||
return $result->getInteger(0) < $topicInfo->getBumpedTime();
|
||||
}
|
||||
|
||||
public function getMostActiveTopicInfo(
|
||||
UserInfo|string $userInfo,
|
||||
array $exceptCategoryInfos = [],
|
||||
array $exceptTopicInfos = [],
|
||||
?bool $deleted = null
|
||||
): object {
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
$hasExceptCategoryInfos = !empty($exceptCategoryInfos);
|
||||
$hasExceptTopicInfos = !empty($exceptTopicInfos);
|
||||
$hasDeleted = $deleted !== null;
|
||||
|
||||
$query = 'SELECT topic_id, forum_id, COUNT(*) AS post_count FROM msz_forum_posts WHERE user_id = ?';
|
||||
if($hasDeleted)
|
||||
$query .= sprintf(' AND post_deleted %s NULL', $deleted ? 'IS NOT' : 'IS');
|
||||
if($hasExceptCategoryInfos)
|
||||
$query .= sprintf(' AND forum_id NOT IN (%s)', DbTools::prepareListString($exceptCategoryInfos));
|
||||
if($hasExceptTopicInfos)
|
||||
$query .= sprintf(' AND topic_id NOT IN (%s)', DbTools::prepareListString($exceptTopicInfos));
|
||||
$query .= ' GROUP BY topic_id ORDER BY post_count DESC LIMIT 1';
|
||||
|
||||
$args = 0;
|
||||
$stmt = $this->cache->get($query);
|
||||
$stmt->addParameter(++$args, $userInfo);
|
||||
foreach($exceptCategoryInfos as $categoryInfo) {
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$stmt->addParameter(++$args, $categoryInfo->getId());
|
||||
elseif(is_string($categoryInfo) || is_int($categoryInfo))
|
||||
$stmt->addParameter(++$args, (string)$categoryInfo);
|
||||
else
|
||||
throw new InvalidArgumentException('$exceptCategoryInfos may only contain string ids or instances of ForumCategoryInfo.');
|
||||
}
|
||||
foreach($exceptTopicInfos as $topicInfo) {
|
||||
if($topicInfo instanceof ForumTopicInfo)
|
||||
$stmt->addParameter(++$args, $topicInfo->getId());
|
||||
elseif(is_string($topicInfo) || is_int($topicInfo))
|
||||
$stmt->addParameter(++$args, (string)$topicInfo);
|
||||
else
|
||||
throw new InvalidArgumentException('$exceptTopicInfos may only contain string ids or instances of ForumTopicInfo.');
|
||||
}
|
||||
$stmt->execute();
|
||||
|
||||
$result = $stmt->getResult();
|
||||
$info = new stdClass;
|
||||
$info->success = $result->next();
|
||||
if($info->success) {
|
||||
$info->topicId = $result->getString(0);
|
||||
$info->categoryId = $result->getString(1);
|
||||
$info->postCount = $result->getInteger(2);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
public function checkUserHasReadTopic(
|
||||
UserInfo|string|null $userInfo,
|
||||
ForumTopicInfo|string $topicInfo
|
||||
): bool {
|
||||
// this method is primarily used to check if we should increment the view count
|
||||
// guests shouldn't increment it so we just
|
||||
if($userInfo === null)
|
||||
return true;
|
||||
|
||||
$stmt = $this->cache->get('SELECT COUNT(*) FROM msz_forum_topics_track WHERE topic_id = ? AND user_id = ?');
|
||||
$stmt->addParameter(1, $topicInfo instanceof ForumTopicInfo ? $topicInfo->getId() : $topicInfo);
|
||||
$stmt->addParameter(2, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo);
|
||||
$stmt->execute();
|
||||
$result = $stmt->getResult();
|
||||
|
||||
return $result->next() && $result->getInteger(0) > 0;
|
||||
}
|
||||
|
||||
public function updateUserReadTopic(
|
||||
UserInfo|string|null $userInfo,
|
||||
ForumTopicInfo|string $topicInfo,
|
||||
ForumCategoryInfo|string|null $categoryInfo = null
|
||||
): void {
|
||||
if($userInfo === null)
|
||||
return;
|
||||
|
||||
if($userInfo instanceof UserInfo)
|
||||
$userInfo = $userInfo->getId();
|
||||
|
||||
if($topicInfo instanceof ForumTopicInfo) {
|
||||
$categoryInfo = $topicInfo->getCategoryId();
|
||||
$topicInfo = $topicInfo->getId();
|
||||
} else {
|
||||
if($categoryInfo === null)
|
||||
throw new InvalidArgumentException('$categoryInfo must be specified if $topicInfo is not an instance of ForumTopicInfo.');
|
||||
if($categoryInfo instanceof ForumCategoryInfo)
|
||||
$categoryInfo = $categoryInfo->getId();
|
||||
}
|
||||
|
||||
$stmt = $this->cache->get('REPLACE INTO msz_forum_topics_track (user_id, topic_id, forum_id, track_last_read) VALUES (?, ?, ?, NOW())');
|
||||
$stmt->addParameter(1, $userInfo);
|
||||
$stmt->addParameter(2, $topicInfo);
|
||||
$stmt->addParameter(3, $categoryInfo);
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ use Misuzu\Comments\Comments;
|
|||
use Misuzu\Config\IConfig;
|
||||
use Misuzu\Counters\Counters;
|
||||
use Misuzu\Emoticons\Emotes;
|
||||
use Misuzu\Forum\Forum;
|
||||
use Misuzu\Forum\ForumContext;
|
||||
use Misuzu\Home\HomeRoutes;
|
||||
use Misuzu\Info\InfoRoutes;
|
||||
use Misuzu\News\News;
|
||||
|
@ -30,7 +30,6 @@ use Misuzu\Profile\ProfileFields;
|
|||
use Misuzu\Satori\SatoriRoutes;
|
||||
use Misuzu\SharpChat\SharpChatRoutes;
|
||||
use Misuzu\Users\UsersContext;
|
||||
use Misuzu\Users\BanInfo;
|
||||
use Misuzu\Users\UserInfo;
|
||||
use Misuzu\Users\Assets\AssetsRoutes;
|
||||
|
||||
|
@ -58,9 +57,9 @@ class MisuzuContext {
|
|||
|
||||
private AuthContext $authCtx;
|
||||
private UsersContext $usersCtx;
|
||||
private ForumContext $forumCtx;
|
||||
|
||||
private ProfileFields $profileFields;
|
||||
private Forum $forum;
|
||||
|
||||
private Permissions $perms;
|
||||
private AuthInfo $authInfo;
|
||||
|
@ -74,13 +73,13 @@ class MisuzuContext {
|
|||
|
||||
$this->authCtx = new AuthContext($dbConn, $config->scopeTo('auth'));
|
||||
$this->usersCtx = new UsersContext($dbConn);
|
||||
$this->forumCtx = new ForumContext($dbConn);
|
||||
|
||||
$this->auditLog = new AuditLog($dbConn);
|
||||
$this->changelog = new Changelog($dbConn);
|
||||
$this->comments = new Comments($dbConn);
|
||||
$this->counters = new Counters($dbConn);
|
||||
$this->emotes = new Emotes($dbConn);
|
||||
$this->forum = new Forum($dbConn);
|
||||
$this->news = new News($dbConn);
|
||||
$this->profileFields = new ProfileFields($dbConn);
|
||||
}
|
||||
|
@ -138,10 +137,6 @@ class MisuzuContext {
|
|||
return $this->profileFields;
|
||||
}
|
||||
|
||||
public function getForum(): Forum {
|
||||
return $this->forum;
|
||||
}
|
||||
|
||||
public function getPerms(): Permissions {
|
||||
return $this->perms;
|
||||
}
|
||||
|
@ -154,6 +149,10 @@ class MisuzuContext {
|
|||
return $this->usersCtx;
|
||||
}
|
||||
|
||||
public function getForumContext(): ForumContext {
|
||||
return $this->forumCtx;
|
||||
}
|
||||
|
||||
public function getAuthInfo(): AuthInfo {
|
||||
return $this->authInfo;
|
||||
}
|
||||
|
@ -294,8 +293,8 @@ class MisuzuContext {
|
|||
$this->router->register(new SatoriRoutes(
|
||||
$this->config->scopeTo('satori'),
|
||||
$this->usersCtx,
|
||||
$this->profileFields,
|
||||
$this->forum
|
||||
$this->forumCtx,
|
||||
$this->profileFields
|
||||
));
|
||||
|
||||
// below is still only otherwise available as stinky php files
|
||||
|
|
|
@ -10,7 +10,7 @@ use Index\Data\DbStatementCache;
|
|||
use Index\Data\DbTools;
|
||||
use Index\Data\IDbConnection;
|
||||
use Index\Data\IDbStatement;
|
||||
use Misuzu\Forum\Forum;
|
||||
use Misuzu\Forum\ForumCategories;
|
||||
use Misuzu\Forum\ForumCategoryInfo;
|
||||
use Misuzu\Users\RoleInfo;
|
||||
use Misuzu\Users\UserInfo;
|
||||
|
@ -227,7 +227,7 @@ class Permissions {
|
|||
}
|
||||
|
||||
// precalculates all permissions for fast lookups
|
||||
public function precalculatePermissions(Forum $forum, array $userIds = []): void {
|
||||
public function precalculatePermissions(ForumCategories $forumCategories, array $userIds = []): void {
|
||||
$suppliedUsers = !empty($userIds);
|
||||
$doGuest = !$suppliedUsers;
|
||||
|
||||
|
@ -296,7 +296,7 @@ class Permissions {
|
|||
}
|
||||
|
||||
self::precalculatePermissionsLog('Loading list of forum categories...');
|
||||
$forumCats = $forum->getCategories(asTree: true);
|
||||
$forumCats = $forumCategories->getCategories(asTree: true);
|
||||
foreach($forumCats as $forumCat)
|
||||
$this->precalculatePermissionsForForumCategory($insert, $userIds, $forumCat, $doGuest);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use Index\Routing\Route;
|
|||
use Misuzu\Pagination;
|
||||
use Misuzu\Tools;
|
||||
use Misuzu\Config\IConfig;
|
||||
use Misuzu\Forum\Forum;
|
||||
use Misuzu\Forum\ForumContext;
|
||||
use Misuzu\Profile\ProfileFields;
|
||||
use Misuzu\Users\UsersContext;
|
||||
|
||||
|
@ -17,8 +17,8 @@ final class SatoriRoutes implements IRouteHandler {
|
|||
public function __construct(
|
||||
private IConfig $config,
|
||||
private UsersContext $usersCtx,
|
||||
private ProfileFields $profileFields,
|
||||
private Forum $forum
|
||||
private ForumContext $forumCtx,
|
||||
private ProfileFields $profileFields
|
||||
) {}
|
||||
|
||||
public function registerRoutes(IRouter $router): void {
|
||||
|
@ -73,7 +73,7 @@ final class SatoriRoutes implements IRouteHandler {
|
|||
$startId = (string)$request->getParam('start', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$posts = [];
|
||||
$postInfos = $this->forum->getPosts(
|
||||
$postInfos = $this->forumCtx->getPosts()->getPosts(
|
||||
categoryInfo: $categoryIds,
|
||||
afterPostInfo: $startId,
|
||||
newerThanDays: $backlogDays,
|
||||
|
@ -82,9 +82,9 @@ final class SatoriRoutes implements IRouteHandler {
|
|||
);
|
||||
|
||||
foreach($postInfos as $postInfo) {
|
||||
$topicInfo = $this->forum->getTopic(postInfo: $postInfo);
|
||||
$firstPostInfo = $this->forum->getPost(topicInfo: $topicInfo);
|
||||
$categoryInfo = $this->forum->getCategory(topicInfo: $topicInfo);
|
||||
$topicInfo = $this->forumCtx->getTopics()->getTopic(postInfo: $postInfo);
|
||||
$firstPostInfo = $this->forumCtx->getPosts()->getPost(topicInfo: $topicInfo);
|
||||
$categoryInfo = $this->forumCtx->getCategories()->getCategory(topicInfo: $topicInfo);
|
||||
$userInfo = $postInfo->hasUserId() ? $this->usersCtx->getUserInfo($postInfo->getUserId()) : null;
|
||||
$userColour = $this->usersCtx->getUserColour($userInfo);
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ msz_sched_task_sql('Synchronise forum_id.', true,
|
|||
'UPDATE msz_forum_posts AS p INNER JOIN msz_forum_topics AS t ON t.topic_id = p.topic_id SET p.forum_id = t.forum_id');
|
||||
|
||||
msz_sched_task_func('Recount forum topics and posts.', true, function() use ($msz) {
|
||||
$msz->getForum()->syncForumCounters();
|
||||
$msz->getForumContext()->getCategories()->syncForumCounters();
|
||||
});
|
||||
|
||||
msz_sched_task_sql('Clean up expired 2fa tokens.', false,
|
||||
|
@ -146,7 +146,7 @@ msz_sched_task_func('Recalculate permissions (maybe)...', false, function() use
|
|||
return;
|
||||
|
||||
$msz->getConfig()->removeValues('perms.needsRecalc');
|
||||
$msz->getPerms()->precalculatePermissions($msz->getForum());
|
||||
$msz->getPerms()->precalculatePermissions($msz->getForumContext()->getCategories());
|
||||
});
|
||||
|
||||
echo 'Running ' . count($schedTasks) . ' tasks...' . PHP_EOL;
|
||||
|
|
|
@ -5,4 +5,4 @@ namespace Misuzu;
|
|||
require_once __DIR__ . '/../misuzu.php';
|
||||
|
||||
$msz->getConfig()->removeValues('perms.needsRecalc');
|
||||
$msz->getPerms()->precalculatePermissions($msz->getForum());
|
||||
$msz->getPerms()->precalculatePermissions($msz->getForumContext()->getCategories());
|
||||
|
|
Loading…
Reference in a new issue