389 lines
17 KiB
PHP
389 lines
17 KiB
PHP
<?php
|
|
namespace Misuzu\Forum;
|
|
|
|
use stdClass;
|
|
use RuntimeException;
|
|
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
|
use Index\Http\Routing\{HttpGet,HttpPost,RouteHandler,RouteHandlerCommon};
|
|
use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon};
|
|
use Misuzu\{CSRF,Pagination,Perm,Template};
|
|
use Misuzu\Auth\AuthInfo;
|
|
use Misuzu\Users\UsersContext;
|
|
|
|
class ForumCategoriesRoutes implements RouteHandler, UrlSource {
|
|
use RouteHandlerCommon, UrlSourceCommon;
|
|
|
|
public function __construct(
|
|
private ForumContext $forum,
|
|
private UsersContext $usersCtx,
|
|
private AuthInfo $authInfo,
|
|
) {}
|
|
|
|
#[HttpGet('/forum')]
|
|
#[UrlFormat('forum-index', '/forum')]
|
|
#[UrlFormat('forum-category-root', '/forum', fragment: '<forum>')]
|
|
public function getIndex(): string {
|
|
$cats = $this->forum->categories->getCategories(hidden: false, asTree: true);
|
|
|
|
// Filtering! This really needs cleaning up...
|
|
foreach($cats as $catId => $category) {
|
|
$perms = $this->authInfo->getPerms('forum', $category->info);
|
|
|
|
// Check if we're allowed to list this category, separate from viewing for some reason...
|
|
if(!$perms->check(Perm::F_CATEGORY_LIST)) {
|
|
unset($cats[$catId]);
|
|
continue;
|
|
}
|
|
|
|
$unread = false;
|
|
|
|
// This is not fully recursive, idk why... probably shove this into ForumContext?
|
|
if($category->info->mayHaveChildren)
|
|
foreach($category->children as $childId => $child) {
|
|
$childPerms = $this->authInfo->getPerms('forum', $child->info);
|
|
if(!$childPerms->check(Perm::F_CATEGORY_LIST)) {
|
|
unset($category->children[$childId]);
|
|
continue;
|
|
}
|
|
|
|
$childUnread = false;
|
|
|
|
if($category->info->isListing) {
|
|
if($child->info->mayHaveChildren) {
|
|
foreach($child->children as $grandChildId => $grandChild) {
|
|
$grandChildPerms = $this->authInfo->getPerms('forum', $grandChild->info);
|
|
if(!$grandChildPerms->check(Perm::F_CATEGORY_LIST)) {
|
|
unset($child->children[$grandChildId]);
|
|
continue;
|
|
}
|
|
|
|
$grandChildUnread = false;
|
|
|
|
if($grandChild->info->mayHaveTopics) {
|
|
$catIds = [$grandChild->info->id];
|
|
foreach($grandChild->childIds as $greatGrandChildId) {
|
|
$greatGrandChildPerms = $this->authInfo->getPerms('forum', $greatGrandChildId);
|
|
if($greatGrandChildPerms->check(Perm::F_CATEGORY_LIST))
|
|
$catIds[] = $greatGrandChildId;
|
|
}
|
|
|
|
$grandChildUnread = $this->forum->categories->checkCategoryUnread($catIds, $this->authInfo->userInfo);
|
|
if($grandChildUnread)
|
|
$childUnread = true;
|
|
}
|
|
|
|
$grandChild->perms = $grandChildPerms;
|
|
$grandChild->unread = $grandChildUnread;
|
|
}
|
|
}
|
|
|
|
if($child->info->mayHaveChildren || $child->info->mayHaveTopics) {
|
|
$catIds = [$child->info->id];
|
|
foreach($child->childIds as $grandChildId) {
|
|
$grandChildPerms = $this->authInfo->getPerms('forum', $grandChildId);
|
|
if($grandChildPerms->check(Perm::F_CATEGORY_LIST))
|
|
$catIds[] = $grandChildId;
|
|
}
|
|
|
|
try {
|
|
$lastPostInfo = $this->forum->posts->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
|
} catch(RuntimeException $ex) {
|
|
$lastPostInfo = null;
|
|
}
|
|
|
|
if($lastPostInfo !== null) {
|
|
$child->lastPost = new stdClass;
|
|
$child->lastPost->info = $lastPostInfo;
|
|
$child->lastPost->topicInfo = $this->forum->topics->getTopic(postInfo: $lastPostInfo);
|
|
|
|
if($lastPostInfo->userId !== null) {
|
|
$child->lastPost->user = $this->usersCtx->getUserInfo($lastPostInfo->userId);
|
|
$child->lastPost->colour = $this->usersCtx->getUserColour($child->lastPost->user);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if($child->info->mayHaveTopics && !$childUnread) {
|
|
$childUnread = $this->forum->categories->checkCategoryUnread($child->info, $this->authInfo->userInfo);
|
|
if($childUnread)
|
|
$unread = true;
|
|
}
|
|
|
|
$child->perms = $childPerms;
|
|
$child->unread = $childUnread;
|
|
}
|
|
|
|
// This is listed second despite being less expensive because the subcategories listed also need to show unread :sob:
|
|
if($category->info->mayHaveTopics)
|
|
$unread = $this->forum->categories->checkCategoryUnread($category->info, $this->authInfo->userInfo);
|
|
|
|
// Create virtual root category if we need to list non-listing categories in root
|
|
if(!$category->info->isListing) {
|
|
if(!array_key_exists('0', $cats)) {
|
|
$cats['0'] = $root = new stdClass;
|
|
$root->info = null;
|
|
$root->perms = 0;
|
|
$root->unread = false;
|
|
$root->colour = null;
|
|
$root->children = [];
|
|
}
|
|
|
|
$cats['0']->children[$category->id] = $category;
|
|
unset($cats[$category->id]);
|
|
|
|
if($category->info->mayHaveChildren || $category->info->mayHaveTopics) {
|
|
$catIds = [$category->info->id];
|
|
foreach($category->childIds as $childId) {
|
|
$childPerms = $this->authInfo->getPerms('forum', $childId);
|
|
if($childPerms->check(Perm::F_CATEGORY_LIST))
|
|
$catIds[] = $childId;
|
|
}
|
|
|
|
try {
|
|
$lastPostInfo = $this->forum->posts->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
|
} catch(RuntimeException $ex) {
|
|
$lastPostInfo = null;
|
|
}
|
|
|
|
if($lastPostInfo !== null) {
|
|
$category->lastPost = new stdClass;
|
|
$category->lastPost->info = $lastPostInfo;
|
|
$category->lastPost->topicInfo = $this->forum->topics->getTopic(postInfo: $lastPostInfo);
|
|
|
|
if($lastPostInfo->userId !== null) {
|
|
$category->lastPost->user = $this->usersCtx->getUserInfo($lastPostInfo->userId);
|
|
$category->lastPost->colour = $this->usersCtx->getUserColour($category->lastPost->user);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$category->perms = $perms;
|
|
$category->unread = $unread;
|
|
}
|
|
|
|
return Template::renderRaw('forum.index', [
|
|
'forum_categories' => $cats,
|
|
'forum_empty' => empty($cats),
|
|
'forum_show_mark_as_read' => $this->authInfo->isLoggedIn,
|
|
]);
|
|
}
|
|
|
|
#[HttpGet('/forum/([0-9]+)')]
|
|
#[UrlFormat('forum-category', '/forum/<forum>', ['page' => '<page>'])]
|
|
public function getCategory(HttpResponseBuilder $response, HttpRequest $request, string $catId): mixed {
|
|
try {
|
|
$category = $this->forum->categories->getCategory(categoryId: $catId);
|
|
} catch(RuntimeException $ex) {
|
|
return 404;
|
|
}
|
|
|
|
$perms = $this->authInfo->getPerms('forum', $category);
|
|
if(!$perms->check(Perm::F_CATEGORY_VIEW))
|
|
return 403;
|
|
|
|
if($category->isLink) {
|
|
$this->forum->categories->incrementCategoryClicks($category);
|
|
$response->redirect($category->linkTarget ?? '/');
|
|
return 301;
|
|
}
|
|
|
|
if($this->usersCtx->hasActiveBan($this->authInfo->userInfo))
|
|
$perms = $perms->apply(fn(int $calc) => $calc & (Perm::F_CATEGORY_LIST | Perm::F_CATEGORY_VIEW));
|
|
|
|
$pagination = Pagination::fromRequest($request, $this->forum->topics->countTopics(
|
|
categoryInfo: $category,
|
|
global: true,
|
|
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false
|
|
), 20);
|
|
if(!$pagination->validOffset)
|
|
return 404;
|
|
|
|
$children = [];
|
|
$topics = [];
|
|
|
|
if($category->mayHaveChildren) {
|
|
$children = $this->forum->categories->getCategoryChildren($category, hidden: false, asTree: true);
|
|
|
|
foreach($children as $childId => $child) {
|
|
$childPerms = $this->authInfo->getPerms('forum', $child->info);
|
|
if(!$childPerms->check(Perm::F_CATEGORY_LIST)) {
|
|
unset($children[$childId]);
|
|
continue;
|
|
}
|
|
|
|
$childUnread = false;
|
|
|
|
if($child->info->mayHaveChildren) {
|
|
foreach($child->children as $grandChildId => $grandChild) {
|
|
$grandChildPerms = $this->authInfo->getPerms('forum', $grandChild->info);
|
|
if(!$grandChildPerms->check(Perm::F_CATEGORY_LIST)) {
|
|
unset($child->children[$grandChildId]);
|
|
continue;
|
|
}
|
|
|
|
$grandChildUnread = false;
|
|
|
|
if($grandChild->info->mayHaveTopics) {
|
|
$catIds = [$grandChild->info->id];
|
|
foreach($grandChild->childIds as $greatGrandChildId) {
|
|
$greatGrandChildPerms = $this->authInfo->getPerms('forum', $greatGrandChildId);
|
|
if(!$greatGrandChildPerms->check(Perm::F_CATEGORY_LIST))
|
|
$catIds[] = $greatGrandChildId;
|
|
}
|
|
|
|
$grandChildUnread = $this->forum->categories->checkCategoryUnread($catIds, $this->authInfo->userInfo);
|
|
if($grandChildUnread)
|
|
$childUnread = true;
|
|
}
|
|
|
|
$grandChild->perms = $grandChildPerms;
|
|
$grandChild->unread = $grandChildUnread;
|
|
}
|
|
}
|
|
|
|
if($child->info->mayHaveChildren || $child->info->mayHaveTopics) {
|
|
$catIds = [$child->info->id];
|
|
foreach($child->childIds as $grandChildId) {
|
|
$grandChildPerms = $this->authInfo->getPerms('forum', $grandChildId);
|
|
if($grandChildPerms->check(Perm::F_CATEGORY_LIST))
|
|
$catIds[] = $grandChildId;
|
|
}
|
|
|
|
try {
|
|
$lastPostInfo = $this->forum->posts->getPost(categoryInfos: $catIds, getLast: true, deleted: false);
|
|
} catch(RuntimeException $ex) {
|
|
$lastPostInfo = null;
|
|
}
|
|
|
|
if($lastPostInfo !== null) {
|
|
$child->lastPost = new stdClass;
|
|
$child->lastPost->info = $lastPostInfo;
|
|
$child->lastPost->topicInfo = $this->forum->topics->getTopic(postInfo: $lastPostInfo);
|
|
|
|
if($lastPostInfo->userId !== null) {
|
|
$child->lastPost->user = $this->usersCtx->getUserInfo($lastPostInfo->userId);
|
|
$child->lastPost->colour = $this->usersCtx->getUserColour($child->lastPost->user);
|
|
}
|
|
}
|
|
}
|
|
|
|
if($child->info->mayHaveTopics && !$childUnread)
|
|
$childUnread = $this->forum->categories->checkCategoryUnread($child->info, $this->authInfo->userInfo);
|
|
|
|
$child->perms = $childPerms;
|
|
$child->unread = $childUnread;
|
|
}
|
|
}
|
|
|
|
if($category->mayHaveTopics) {
|
|
$topicInfos = $this->forum->topics->getTopics(
|
|
categoryInfo: $category,
|
|
global: true,
|
|
deleted: $perms->check(Perm::F_POST_DELETE_ANY) ? null : false,
|
|
pagination: $pagination,
|
|
);
|
|
|
|
foreach($topicInfos as $topicInfo) {
|
|
$topics[] = $topic = new stdClass;
|
|
$topic->info = $topicInfo;
|
|
$topic->unread = $this->forum->topics->checkTopicUnread($topicInfo, $this->authInfo->userInfo);
|
|
$topic->participated = $this->forum->topics->checkTopicParticipated($topicInfo, $this->authInfo->userInfo);
|
|
|
|
if($topicInfo->userId !== null) {
|
|
$topic->user = $this->usersCtx->getUserInfo($topicInfo->userId);
|
|
$topic->colour = $this->usersCtx->getUserColour($topic->user);
|
|
}
|
|
|
|
try {
|
|
$topic->lastPost = new stdClass;
|
|
$topic->lastPost->info = $lastPostInfo = $this->forum->posts->getPost(
|
|
topicInfo: $topicInfo,
|
|
getLast: true,
|
|
deleted: $topicInfo->deleted ? null : false,
|
|
);
|
|
|
|
if($lastPostInfo->userId !== null) {
|
|
$topic->lastPost->user = $this->usersCtx->getUserInfo($lastPostInfo->userId);
|
|
$topic->lastPost->colour = $this->usersCtx->getUserColour($topic->lastPost->user);
|
|
}
|
|
} catch(RuntimeException $ex) {
|
|
$topic->lastPost = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Template::renderRaw('forum.forum', [
|
|
'forum_breadcrumbs' => iterator_to_array($this->forum->categories->getCategoryAncestry($category)),
|
|
'global_accent_colour' => $this->forum->categories->getCategoryColour($category),
|
|
'forum_info' => $category,
|
|
'forum_children' => $children,
|
|
'forum_topics' => $topics,
|
|
'forum_pagination' => $pagination,
|
|
'forum_show_mark_as_read' => $this->authInfo->isLoggedIn,
|
|
'forum_perms' => $perms->checkMany([
|
|
'can_create_topic' => Perm::F_TOPIC_CREATE,
|
|
]),
|
|
]);
|
|
}
|
|
|
|
#[HttpPost('/forum/mark-as-read')]
|
|
#[UrlFormat('forum-mark-as-read', '/forum/mark-as-read', ['cat' => '<category>', 'rec' => '<recursive>'])]
|
|
public function postMarkAsRead(HttpResponseBuilder $response, HttpRequest $request): mixed {
|
|
if(!$this->authInfo->isLoggedIn)
|
|
return 401;
|
|
|
|
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
|
|
return 403;
|
|
$response->setHeader('X-CSRF-Token', CSRF::token());
|
|
|
|
$catId = (string)$request->getParam('cat', FILTER_SANITIZE_NUMBER_INT);
|
|
$recursive = !empty($request->getParam('rec'));
|
|
|
|
if($catId === '') {
|
|
if(!$recursive)
|
|
return 400;
|
|
|
|
$cats = $this->forum->categories->getCategories();
|
|
} elseif($recursive)
|
|
$cats = $this->forum->categories->getCategoryChildren(parentInfo: $catId, includeSelf: true);
|
|
else
|
|
try {
|
|
$cats = [$this->forum->categories->getCategory(categoryId: $catId)];
|
|
} catch(RuntimeException $ex) {
|
|
$cats = [];
|
|
}
|
|
|
|
if(empty($cats)) {
|
|
$response->statusCode = 404;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:category:none',
|
|
'text' => "Couldn't find that forum category.",
|
|
],
|
|
];
|
|
}
|
|
|
|
$success = false;
|
|
foreach($cats as $category) {
|
|
$perms = $this->authInfo->getPerms('forum', $category);
|
|
if($perms->check(Perm::F_CATEGORY_LIST)) {
|
|
$this->forum->categories->updateUserReadCategory($this->authInfo->userInfo, $category);
|
|
$success = true;
|
|
}
|
|
}
|
|
|
|
if(!$success) {
|
|
$response->statusCode = 403;
|
|
return [
|
|
'error' => [
|
|
'name' => 'forum:category:access',
|
|
'text' => "You're not allowed to access this forum category.",
|
|
],
|
|
];
|
|
}
|
|
|
|
return 204;
|
|
}
|
|
}
|