')] 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/', ['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' => '', 'rec' => ''])] 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; } }