diff --git a/misuzu.php b/misuzu.php index 5561d70..d2aec85 100644 --- a/misuzu.php +++ b/misuzu.php @@ -22,8 +22,6 @@ Environment::setDebug(MSZ_DEBUG); mb_internal_encoding('utf-8'); date_default_timezone_set('utc'); -require_once MSZ_SOURCE . '/url.php'; - $dbConfig = parse_ini_file(MSZ_CONFIG . '/config.ini', true, INI_SCANNER_TYPED); if(empty($dbConfig)) { diff --git a/public-legacy/auth/index.php b/public-legacy/auth/index.php index d11f371..413c472 100644 --- a/public-legacy/auth/index.php +++ b/public-legacy/auth/index.php @@ -1,4 +1,4 @@ getURLs()->format('auth-login')); diff --git a/public-legacy/auth/login.php b/public-legacy/auth/login.php index 42ff74c..48d8a22 100644 --- a/public-legacy/auth/login.php +++ b/public-legacy/auth/login.php @@ -4,9 +4,10 @@ namespace Misuzu; use Exception; use Misuzu\Auth\AuthTokenCookie; +$urls = $msz->getURLs(); $authInfo = $msz->getAuthInfo(); if($authInfo->isLoggedIn()) { - url_redirect('index'); + Tools::redirect($urls->format('index')); return; } @@ -25,7 +26,7 @@ if(!empty($_GET['resolve'])) { echo json_encode([ 'id' => 0, 'name' => '', - 'avatar' => url('user-avatar', ['res' => 200, 'user' => 0]), + 'avatar' => $urls->format('user-avatar', ['res' => 200, 'user' => 0]), ]); return; } @@ -33,7 +34,7 @@ if(!empty($_GET['resolve'])) { echo json_encode([ 'id' => (int)$userInfo->getId(), 'name' => $userInfo->getName(), - 'avatar' => url('user-avatar', ['user' => $userInfo->getId(), 'res' => 200]), + 'avatar' => $urls->format('user-avatar', ['user' => $userInfo->getId(), 'res' => 200]), ]); return; } @@ -123,9 +124,7 @@ while(!empty($_POST['login']) && is_array($_POST['login'])) { if($userInfo->hasTOTPKey()) { $tfaToken = $authCtx->getTwoFactorAuthSessions()->createToken($userInfo); - url_redirect('auth-two-factor', [ - 'token' => $tfaToken, - ]); + Tools::redirect($urls->format('auth-two-factor', ['token' => $tfaToken])); return; } @@ -146,10 +145,10 @@ while(!empty($_POST['login']) && is_array($_POST['login'])) { AuthTokenCookie::apply($tokenPacker->pack($tokenInfo)); - if(!is_local_url($loginRedirect)) - $loginRedirect = url('index'); + if(!Tools::isLocalURL($loginRedirect)) + $loginRedirect = $urls->format('index'); - redirect($loginRedirect); + Tools::redirect($loginRedirect); return; } @@ -157,7 +156,7 @@ $welcomeMode = !empty($_GET['welcome']); $loginUsername = !empty($_POST['login']['username']) && is_string($_POST['login']['username']) ? $_POST['login']['username'] : ( !empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : '' ); -$loginRedirect = $welcomeMode ? url('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? url('index'); +$loginRedirect = $welcomeMode ? $urls->format('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? $urls->format('index'); $canRegisterAccount = !$siteIsPrivate; Template::render('auth.login', [ diff --git a/public-legacy/auth/logout.php b/public-legacy/auth/logout.php index 5437029..195f714 100644 --- a/public-legacy/auth/logout.php +++ b/public-legacy/auth/logout.php @@ -24,4 +24,4 @@ if($authInfo->isLoggedIn()) { AuthTokenCookie::apply($tokenPacker->pack($tokenInfo)); } -url_redirect('index'); +Tools::redirect($msz->getURLs()->format('index'));; diff --git a/public-legacy/auth/password.php b/public-legacy/auth/password.php index 6964d19..444b2db 100644 --- a/public-legacy/auth/password.php +++ b/public-legacy/auth/password.php @@ -4,9 +4,10 @@ namespace Misuzu; use RuntimeException; use Misuzu\Users\User; +$urls = $msz->getURLs(); $authInfo = $msz->getAuthInfo(); if($authInfo->isLoggedIn()) { - url_redirect('settings-account'); + Tools::redirect($urls->format('settings-account')); return; } @@ -25,7 +26,7 @@ if($userId > 0) try { $userInfo = $users->getUser((string)$userId, 'id'); } catch(RuntimeException $ex) { - url_redirect('auth-forgot'); + Tools::redirect($urls->format('auth-forgot')); return; } @@ -80,7 +81,7 @@ while($canResetPassword) { $recoveryTokens->invalidateToken($tokenInfo); - url_redirect('auth-login', ['redirect' => '/']); + Tools::redirect($urls->format('auth-login', ['redirect' => '/'])); return; } @@ -133,7 +134,7 @@ while($canResetPassword) { } } - url_redirect('auth-reset', ['user' => $forgotUser->getId()]); + Tools::redirect($urls->format('auth-reset', ['user' => $forgotUser->getId()])); return; } diff --git a/public-legacy/auth/register.php b/public-legacy/auth/register.php index 3e57bd6..85b95ca 100644 --- a/public-legacy/auth/register.php +++ b/public-legacy/auth/register.php @@ -4,9 +4,10 @@ namespace Misuzu; use RuntimeException; use Misuzu\Users\User; +$urls = $msz->getURLs(); $authInfo = $msz->getAuthInfo(); if($authInfo->isLoggedIn()) { - url_redirect('index'); + Tools::redirect($urls->format('index')); return; } @@ -109,7 +110,7 @@ while(!$restricted && !empty($register)) { [$userInfo->getId()] ); - url_redirect('auth-login-welcome', ['username' => $userInfo->getName()]); + Tools::redirect($urls->format('auth-login-welcome', ['username' => $userInfo->getName()])); return; } diff --git a/public-legacy/auth/revert.php b/public-legacy/auth/revert.php index 920a20a..5d97a0d 100644 --- a/public-legacy/auth/revert.php +++ b/public-legacy/auth/revert.php @@ -3,6 +3,7 @@ namespace Misuzu; use Misuzu\Auth\AuthTokenCookie; +$urls = $msz->getURLs(); if(CSRF::validateRequest()) { $tokenInfo = $msz->getAuthInfo()->getTokenInfo(); @@ -14,9 +15,9 @@ if(CSRF::validateRequest()) { $tokenInfo = $tokenBuilder->toInfo(); AuthTokenCookie::apply($tokenPacker->pack($tokenInfo)); - url_redirect('manage-user', ['user' => $impUserId]); + Tools::redirect($urls->format('manage-user', ['user' => $impUserId])); return; } } -url_redirect('index'); +Tools::redirect($urls->format('index')); diff --git a/public-legacy/auth/twofactor.php b/public-legacy/auth/twofactor.php index 125aabd..56bcc0a 100644 --- a/public-legacy/auth/twofactor.php +++ b/public-legacy/auth/twofactor.php @@ -5,9 +5,10 @@ use RuntimeException; use Misuzu\TOTPGenerator; use Misuzu\Auth\AuthTokenCookie; +$urls = $msz->getURLs(); $authInfo = $msz->getAuthInfo(); if($authInfo->isLoggedIn()) { - url_redirect('index'); + Tools::redirect($urls->format('index')); return; } @@ -31,7 +32,7 @@ $tokenString = !empty($_GET['token']) && is_string($_GET['token']) ? $_GET['toke $tokenUserId = $tfaSessions->getTokenUserId($tokenString); if(empty($tokenUserId)) { - url_redirect('auth-login'); + Tools::redirect($urls->format('auth-login')); return; } @@ -40,7 +41,7 @@ $userInfo = $users->getUser($tokenUserId, 'id'); // checking user_totp_key specifically because there's a fringe chance that // there's a token present, but totp is actually disabled if(!$userInfo->hasTOTPKey()) { - url_redirect('auth-login'); + Tools::redirect($urls->format('auth-login')); return; } @@ -94,16 +95,16 @@ while(!empty($twofactor)) { AuthTokenCookie::apply($tokenPacker->pack($tokenInfo)); - if(!is_local_url($redirect)) - $redirect = url('index'); + if(!Tools::isLocalURL($redirect)) + $redirect = $urls->format('index'); - redirect($redirect); + Tools::redirect($redirect); return; } Template::render('auth.twofactor', [ 'twofactor_notices' => $notices, - 'twofactor_redirect' => !empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : url('index'), + 'twofactor_redirect' => !empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : $urls->format('index'), 'twofactor_attempts_remaining' => $remainingAttempts, 'twofactor_token' => $tokenString, ]); diff --git a/public-legacy/comments.php b/public-legacy/comments.php index 00782b9..7781f60 100644 --- a/public-legacy/comments.php +++ b/public-legacy/comments.php @@ -4,9 +4,9 @@ namespace Misuzu; use RuntimeException; $usersCtx = $msz->getUsersContext(); -$redirect = filter_input(INPUT_GET, 'return') ?? $_SERVER['HTTP_REFERER'] ?? url('index'); +$redirect = filter_input(INPUT_GET, 'return') ?? $_SERVER['HTTP_REFERER'] ?? $msz->getURLs()->format('index'); -if(!is_local_url($redirect)) +if(!Tools::isLocalURL($redirect)) Template::displayInfo('Possible request forgery detected.', 403); if(!CSRF::validateRequest()) @@ -67,7 +67,7 @@ switch($commentMode) { $comments->unpinPost($commentInfo); } - redirect($redirect . '#comment-' . $commentInfo->getId()); + Tools::redirect($redirect . '#comment-' . $commentInfo->getId()); break; case 'vote': @@ -84,7 +84,7 @@ switch($commentMode) { else $comments->removePostVote($commentInfo, $currentUserInfo); - redirect($redirect . '#comment-' . $commentInfo->getId()); + Tools::redirect($redirect . '#comment-' . $commentInfo->getId()); break; case 'delete': @@ -117,7 +117,7 @@ switch($commentMode) { $msz->createAuditLog('COMMENT_ENTRY_DELETE', [$commentInfo->getId()]); } - redirect($redirect); + Tools::redirect($redirect); break; case 'restore': @@ -135,7 +135,7 @@ switch($commentMode) { '', ]); - redirect($redirect . '#comment-' . $commentInfo->getId()); + Tools::redirect($redirect . '#comment-' . $commentInfo->getId()); break; case 'create': @@ -201,7 +201,7 @@ switch($commentMode) { $commentPin ); - redirect($redirect . '#comment-' . $commentInfo->getId()); + Tools::redirect($redirect . '#comment-' . $commentInfo->getId()); break; default: diff --git a/public-legacy/forum/forum.php b/public-legacy/forum/forum.php index fed93f8..9cf92eb 100644 --- a/public-legacy/forum/forum.php +++ b/public-legacy/forum/forum.php @@ -33,7 +33,7 @@ if($usersCtx->hasActiveBan($currentUser)) if($categoryInfo->isLink()) { if($categoryInfo->hasLinkTarget()) { $forumCategories->incrementCategoryClicks($categoryInfo); - redirect($categoryInfo->getLinkTarget()); + Tools::redirect($categoryInfo->getLinkTarget()); return; } diff --git a/public-legacy/forum/index.php b/public-legacy/forum/index.php index d12cce9..5ae28c2 100644 --- a/public-legacy/forum/index.php +++ b/public-legacy/forum/index.php @@ -32,14 +32,14 @@ if($mode === 'mark') { $forumCategories->updateUserReadCategory($userInfo, $categoryInfo); } - url_redirect($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId]); + Tools::redirect($msz->getURLs()->format($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId])); return; } Template::render('confirm', [ 'title' => 'Mark forum as read', 'message' => 'Are you sure you want to mark ' . ($categoryId < 1 ? 'the entire' : 'this') . ' forum as read?', - 'return' => url($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId]), + 'return' => $msz->getURLs()->format($categoryId ? 'forum-category' : 'forum-index', ['forum' => $categoryId]), 'params' => [ 'forum' => $categoryId, ] diff --git a/public-legacy/forum/leaderboard.php b/public-legacy/forum/leaderboard.php index d8f6e26..b4a0337 100644 --- a/public-legacy/forum/leaderboard.php +++ b/public-legacy/forum/leaderboard.php @@ -93,7 +93,9 @@ MD; $totalPostsCount += $ranking->postsCount; $markdown .= sprintf("| %s | [%s](%s%s) | %s |\r\n", $ranking->position, $ranking->user?->getName() ?? 'Deleted User', - url_prefix(false), url('user-profile', ['user' => $ranking->userId]), number_format($ranking->postsCount)); + $msz->getSiteInfo()->getURL(), + $msz->getURLs()->format('user-profile', ['user' => $ranking->userId]), + number_format($ranking->postsCount)); } $markdown .= sprintf("\r\nIn total %s posts were made!\r\n", number_format($totalPostsCount)); diff --git a/public-legacy/forum/post.php b/public-legacy/forum/post.php index 667f37e..4b071a7 100644 --- a/public-legacy/forum/post.php +++ b/public-legacy/forum/post.php @@ -3,6 +3,7 @@ namespace Misuzu; use RuntimeException; +$urls = $msz->getURLs(); $forumCtx = $msz->getForumContext(); $forumPosts = $forumCtx->getPosts(); $usersCtx = $msz->getUsersContext(); @@ -62,10 +63,7 @@ switch($postMode) { 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); if($postRequestVerified && !$submissionConfirmed) { - url_redirect('forum-post', [ - 'post' => $postInfo->getId(), - 'post_fragment' => 'p' . $postInfo->getId(), - ]); + Tools::redirect($urls->format('forum-post', ['post' => $postInfo->getId()])); break; } elseif(!$postRequestVerified) { Template::render('forum.confirm', [ @@ -83,7 +81,7 @@ switch($postMode) { $forumPosts->deletePost($postInfo); $msz->createAuditLog('FORUM_POST_DELETE', [$postInfo->getId()]); - url_redirect('forum-topic', ['topic' => $postInfo->getTopicId()]); + Tools::redirect($urls->format('forum-topic', ['topic' => $postInfo->getTopicId()])); break; case 'nuke': @@ -91,10 +89,7 @@ switch($postMode) { Template::throwError(403); if($postRequestVerified && !$submissionConfirmed) { - url_redirect('forum-post', [ - 'post' => $postInfo->getId(), - 'post_fragment' => 'p' . $postInfo->getId(), - ]); + Tools::redirect($urls->format('forum-post', ['post' => $postInfo->getId()])); break; } elseif(!$postRequestVerified) { Template::render('forum.confirm', [ @@ -112,7 +107,7 @@ switch($postMode) { $forumPosts->nukePost($postInfo->getId()); $msz->createAuditLog('FORUM_POST_NUKE', [$postInfo->getId()]); - url_redirect('forum-topic', ['topic' => $postInfo->getTopicId()]); + Tools::redirect($urls->format('forum-topic', ['topic' => $postInfo->getTopicId()])); break; case 'restore': @@ -120,10 +115,7 @@ switch($postMode) { Template::throwError(403); if($postRequestVerified && !$submissionConfirmed) { - url_redirect('forum-post', [ - 'post' => $postInfo->getId(), - 'post_fragment' => 'p' . $postInfo->getId(), - ]); + Tools::redirect($urls->format('forum-post', ['post' => $postInfo->getId()])); break; } elseif(!$postRequestVerified) { Template::render('forum.confirm', [ @@ -141,13 +133,10 @@ switch($postMode) { $forumPosts->restorePost($postInfo->getId()); $msz->createAuditLog('FORUM_POST_RESTORE', [$postInfo->getId()]); - url_redirect('forum-topic', ['topic' => $postInfo->getTopicId()]); + Tools::redirect($urls->format('forum-topic', ['topic' => $postInfo->getTopicId()])); break; default: // function as an alt for topic.php?p= by default - url_redirect('forum-post', [ - 'post' => $postInfo->getId(), - 'post_fragment' => 'p' . $postInfo->getId(), - ]); + Tools::redirect($urls->format('forum-post', ['post' => $postInfo->getId()]); break; } diff --git a/public-legacy/forum/posting.php b/public-legacy/forum/posting.php index bf2a0b7..94b6913 100644 --- a/public-legacy/forum/posting.php +++ b/public-legacy/forum/posting.php @@ -257,12 +257,11 @@ if(!empty($_POST)) { if(empty($notices)) { // does this ternary ever return forum-topic? - $redirect = url(empty($topicInfo) ? 'forum-topic' : 'forum-post', [ + $redirect = $msz->getURLs()->format(empty($topicInfo) ? 'forum-topic' : 'forum-post', [ 'topic' => $topicId ?? 0, 'post' => $postId ?? 0, - 'post_fragment' => 'p' . ($postId ?? 0), ]); - redirect($redirect); + Tools::redirect($redirect); return; } } diff --git a/public-legacy/forum/topic.php b/public-legacy/forum/topic.php index c214c7d..bd3e727 100644 --- a/public-legacy/forum/topic.php +++ b/public-legacy/forum/topic.php @@ -4,6 +4,7 @@ namespace Misuzu; use stdClass; use RuntimeException; +$urls = $msz->getURLs(); $forumCtx = $msz->getForumContext(); $forumCategories = $forumCtx->getCategories(); $forumTopics = $forumCtx->getTopics(); @@ -156,19 +157,19 @@ if(in_array($moderationMode, $validModerationModes, true)) { ]); break; } elseif(!$submissionConfirmed) { - url_redirect( + Tools::redirect($urls->format( 'forum-topic', ['topic' => $topicInfo->getId()] - ); + )); break; } $forumTopics->deleteTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_DELETE', [$topicInfo->getId()]); - url_redirect('forum-category', [ + Tools::redirect($urls->format('forum-category', [ 'forum' => $categoryInfo->getId(), - ]); + ])); break; case 'restore': @@ -187,18 +188,18 @@ if(in_array($moderationMode, $validModerationModes, true)) { ]); break; } elseif(!$submissionConfirmed) { - url_redirect('forum-topic', [ + Tools::redirect($urls->format('forum-topic', [ 'topic' => $topicInfo->getId(), - ]); + ])); break; } $forumTopics->restoreTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_RESTORE', [$topicInfo->getId()]); - url_redirect('forum-category', [ + Tools::redirect($urls->format('forum-category', [ 'forum' => $categoryInfo->getId(), - ]); + ])); break; case 'nuke': @@ -217,18 +218,18 @@ if(in_array($moderationMode, $validModerationModes, true)) { ]); break; } elseif(!$submissionConfirmed) { - url_redirect('forum-topic', [ + Tools::redirect($urls->format('forum-topic', [ 'topic' => $topicInfo->getId(), - ]); + ])); break; } $forumTopics->nukeTopic($topicInfo->getId()); $msz->createAuditLog('FORUM_TOPIC_NUKE', [$topicInfo->getId()]); - url_redirect('forum-category', [ + Tools::redirect($urls->format('forum-category', [ 'forum' => $categoryInfo->getId(), - ]); + ])); break; case 'bump': @@ -237,9 +238,9 @@ if(in_array($moderationMode, $validModerationModes, true)) { $msz->createAuditLog('FORUM_TOPIC_BUMP', [$topicInfo->getId()]); } - url_redirect('forum-topic', [ + Tools::redirect($urls->format('forum-topic', [ 'topic' => $topicInfo->getId(), - ]); + ])); break; case 'lock': @@ -248,9 +249,9 @@ if(in_array($moderationMode, $validModerationModes, true)) { $msz->createAuditLog('FORUM_TOPIC_LOCK', [$topicInfo->getId()]); } - url_redirect('forum-topic', [ + Tools::redirect($urls->format('forum-topic', [ 'topic' => $topicInfo->getId(), - ]); + ])); break; case 'unlock': @@ -259,9 +260,9 @@ if(in_array($moderationMode, $validModerationModes, true)) { $msz->createAuditLog('FORUM_TOPIC_UNLOCK', [$topicInfo->getId()]); } - url_redirect('forum-topic', [ + Tools::redirect($urls->format('forum-topic', [ 'topic' => $topicInfo->getId(), - ]); + ])); break; } return; diff --git a/public-legacy/manage/changelog/change.php b/public-legacy/manage/changelog/change.php index f1c8368..a006f42 100644 --- a/public-legacy/manage/changelog/change.php +++ b/public-legacy/manage/changelog/change.php @@ -15,6 +15,7 @@ $changeActions = []; foreach(Changelog::ACTIONS as $action) $changeActions[$action] = Changelog::actionText($action); +$urls = $msz->getURLs(); $changelog = $msz->getChangelog(); $changeId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT); $changeInfo = null; @@ -38,7 +39,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $changelog->deleteChange($changeInfo); $msz->createAuditLog('CHANGELOG_ENTRY_DELETE', [$changeInfo->getId()]); - url_redirect('manage-changelog-changes'); + Tools::redirect($urls->format('manage-changelog-changes')); return; } @@ -104,7 +105,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { [$changeInfo->getId()] ); - url_redirect('manage-changelog-change', ['change' => $changeInfo->getId()]); + Tools::redirect($urls->format('manage-changelog-change', ['change' => $changeInfo->getId()])); return; } diff --git a/public-legacy/manage/changelog/tag.php b/public-legacy/manage/changelog/tag.php index 8e9d104..1e4ae07 100644 --- a/public-legacy/manage/changelog/tag.php +++ b/public-legacy/manage/changelog/tag.php @@ -6,6 +6,7 @@ use RuntimeException; if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_CL_TAGS_MANAGE)) Template::throwError(403); +$urls = $msz->getURLs(); $changelog = $msz->getChangelog(); $tagId = (string)filter_input(INPUT_GET, 't', FILTER_SANITIZE_NUMBER_INT); $loadTagInfo = fn() => $changelog->getTag($tagId); @@ -26,7 +27,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $changelog->deleteTag($tagInfo); $msz->createAuditLog('CHANGELOG_TAG_DELETE', [$tagInfo->getId()]); - url_redirect('manage-changelog-tags'); + Tools::redirect($urls->format('manage-changelog-tags')); return; } @@ -55,7 +56,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { ); if($isNew) { - url_redirect('manage-changelog-tag', ['tag' => $tagInfo->getId()]); + Tools::redirect($urls->format('manage-changelog-tag', ['tag' => $tagInfo->getId()])); return; } else $tagInfo = $loadTagInfo(); break; diff --git a/public-legacy/manage/forum/redirs.php b/public-legacy/manage/forum/redirs.php index 9993c9f..06c8a38 100644 --- a/public-legacy/manage/forum/redirs.php +++ b/public-legacy/manage/forum/redirs.php @@ -5,6 +5,7 @@ $authInfo = $msz->getAuthInfo(); if(!$authInfo->getPerms('global')->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE)) Template::throwError(403); +$urls = $msz->getURLs(); $forumCtx = $msz->getForumContext(); $forumTopicRedirects = $forumCtx->getTopicRedirects(); @@ -17,7 +18,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') { $msz->createAuditLog('FORUM_TOPIC_REDIR_CREATE', [$rTopicId]); $forumTopicRedirects->createTopicRedirect($rTopicId, $authInfo->getUserInfo(), $rTopicURL); - url_redirect('manage-forum-topic-redirs'); + Tools::redirect($urls->format('manage-forum-topic-redirs')); return; } @@ -28,7 +29,7 @@ if(filter_input(INPUT_GET, 'm') === 'explode') { $rTopicId = (string)filter_input(INPUT_GET, 't'); $msz->createAuditLog('FORUM_TOPIC_REDIR_REMOVE', [$rTopicId]); $forumTopicRedirects->deleteTopicRedirect($rTopicId); - url_redirect('manage-forum-topic-redirs'); + Tools::redirect($urls->format('manage-forum-topic-redirs')); return; } diff --git a/public-legacy/manage/general/emoticon.php b/public-legacy/manage/general/emoticon.php index b50b794..8613db1 100644 --- a/public-legacy/manage/general/emoticon.php +++ b/public-legacy/manage/general/emoticon.php @@ -95,7 +95,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { [$emoteInfo->getId()] ); - url_redirect('manage-general-emoticon', ['emote' => $emoteInfo->getId()]); + Tools::redirect($msz->getURLs()->format('manage-general-emoticon', ['emote' => $emoteInfo->getId()])); return; } diff --git a/public-legacy/manage/general/emoticons.php b/public-legacy/manage/general/emoticons.php index 8f5a33e..d4d59ad 100644 --- a/public-legacy/manage/general/emoticons.php +++ b/public-legacy/manage/general/emoticons.php @@ -37,7 +37,7 @@ if(CSRF::validateRequest() && !empty($_GET['emote'])) { } } - url_redirect('manage-general-emoticons'); + Tools::redirect($msz->getURLs()->format('manage-general-emoticons')); return; } diff --git a/public-legacy/manage/general/setting-delete.php b/public-legacy/manage/general/setting-delete.php index 40995da..7f93a60 100644 --- a/public-legacy/manage/general/setting-delete.php +++ b/public-legacy/manage/general/setting-delete.php @@ -15,7 +15,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { $valueName = $valueInfo->getName(); $msz->createAuditLog('CONFIG_DELETE', [$valueName]); $cfg->removeValues($valueName); - url_redirect('manage-general-settings'); + Tools::redirect($msz->getURLs()->format('manage-general-settings')); return; } diff --git a/public-legacy/manage/general/setting.php b/public-legacy/manage/general/setting.php index 05c8617..2364ea1 100644 --- a/public-legacy/manage/general/setting.php +++ b/public-legacy/manage/general/setting.php @@ -73,7 +73,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { $msz->createAuditLog($isNew ? 'CONFIG_CREATE' : 'CONFIG_UPDATE', [$sName]); $applyFunc($sName, $sValue); - url_redirect('manage-general-settings'); + Tools::redirect($msz->getURLs()->format('manage-general-settings')); return; } diff --git a/public-legacy/manage/index.php b/public-legacy/manage/index.php index e842be6..1fb3a11 100644 --- a/public-legacy/manage/index.php +++ b/public-legacy/manage/index.php @@ -1,4 +1,4 @@ getURLs()->format('manage-general-overview')); diff --git a/public-legacy/manage/news/category.php b/public-legacy/manage/news/category.php index 6e186d7..6cb7077 100644 --- a/public-legacy/manage/news/category.php +++ b/public-legacy/manage/news/category.php @@ -6,6 +6,7 @@ use RuntimeException; if(!$msz->getAuthInfo()->getPerms('global')->check(Perm::G_NEWS_CATEGORIES_MANAGE)) Template::throwError(403); +$urls = $msz->getURLs(); $news = $msz->getNews(); $categoryId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT); $loadCategoryInfo = fn() => $news->getCategory(categoryId: $categoryId); @@ -26,7 +27,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $news->deleteCategory($categoryInfo); $msz->createAuditLog('NEWS_CATEGORY_DELETE', [$categoryInfo->getId()]); - url_redirect('manage-news-categories'); + Tools::redirect($urls->format('manage-news-categories')); return; } @@ -55,7 +56,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { ); if($isNew) { - url_redirect('manage-news-category', ['category' => $categoryInfo->getId()]); + Tools::redirect($urls->format('manage-news-category', ['category' => $categoryInfo->getId()])); return; } else $categoryInfo = $loadCategoryInfo(); break; diff --git a/public-legacy/manage/news/index.php b/public-legacy/manage/news/index.php index a983e91..6f38668 100644 --- a/public-legacy/manage/news/index.php +++ b/public-legacy/manage/news/index.php @@ -1,4 +1,4 @@ getURLs()->format('manage-news-categories')); diff --git a/public-legacy/manage/news/post.php b/public-legacy/manage/news/post.php index f94b118..faeef96 100644 --- a/public-legacy/manage/news/post.php +++ b/public-legacy/manage/news/post.php @@ -7,6 +7,7 @@ $authInfo = $msz->getAuthInfo(); if(!$authInfo->getPerms('global')->check(Perm::G_NEWS_POSTS_MANAGE)) Template::throwError(403); +$urls = $msz->getURLs(); $news = $msz->getNews(); $postId = (string)filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT); $loadPostInfo = fn() => $news->getPost($postId); @@ -27,7 +28,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $news->deletePost($postInfo); $msz->createAuditLog('NEWS_POST_DELETE', [$postInfo->getId()]); - url_redirect('manage-news-posts'); + Tools::redirect($urls->format('manage-news-posts')); return; } @@ -63,7 +64,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { // Twitter integration used to be here, replace with Railgun Pulse integration } - url_redirect('manage-news-post', ['post' => $postInfo->getId()]); + Tools::redirect($urls->format('manage-news-post', ['post' => $postInfo->getId()])); return; } else $postInfo = $loadPostInfo(); break; diff --git a/public-legacy/manage/news/posts.php b/public-legacy/manage/news/posts.php index 2285d32..93643fd 100644 --- a/public-legacy/manage/news/posts.php +++ b/public-legacy/manage/news/posts.php @@ -13,7 +13,7 @@ $pagination = new Pagination($news->countPosts( if(!$pagination->hasValidOffset()) Template::throwError(404); -$posts = $news->getAllPosts( +$posts = $news->getPosts( includeScheduled: true, includeDeleted: true, pagination: $pagination diff --git a/public-legacy/manage/users/ban.php b/public-legacy/manage/users/ban.php index 23ca5f2..3cea458 100644 --- a/public-legacy/manage/users/ban.php +++ b/public-legacy/manage/users/ban.php @@ -9,6 +9,7 @@ $authInfo = $msz->getAuthInfo(); if(!$authInfo->getPerms('user')->check(Perm::U_BANS_MANAGE)) Template::throwError(403); +$urls = $msz->getURLs(); $usersCtx = $msz->getUsersContext(); $bans = $usersCtx->getBans(); @@ -24,7 +25,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && filter_has_var(INPUT_GET, 'delete')) $bans->deleteBans($banInfo); $msz->createAuditLog('BAN_DELETE', [$banInfo->getId(), $banInfo->getUserId()]); - url_redirect('manage-users-bans', ['user' => $banInfo->getUserId()]); + Tools::redirect($urls->format('manage-users-bans', ['user' => $banInfo->getUserId()])); return; } @@ -69,7 +70,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { ); $msz->createAuditLog('BAN_CREATE', [$banInfo->getId(), $userInfo->getId()]); - url_redirect('manage-users-bans', ['user' => $userInfo->getId()]); + Tools::redirect($urls->format('manage-users-bans', ['user' => $userInfo->getId()])); return; } diff --git a/public-legacy/manage/users/note.php b/public-legacy/manage/users/note.php index 9aabe71..a69e1dc 100644 --- a/public-legacy/manage/users/note.php +++ b/public-legacy/manage/users/note.php @@ -13,6 +13,7 @@ $hasUserId = filter_has_var(INPUT_GET, 'u'); if((!$hasNoteId && !$hasUserId) || ($hasNoteId && $hasUserId)) Template::throwError(400); +$urls = $msz->getURLs(); $usersCtx = $msz->getUsersContext(); $modNotes = $usersCtx->getModNotes(); @@ -41,7 +42,7 @@ if($hasUserId) { $modNotes->deleteNotes($noteInfo); $msz->createAuditLog('MOD_NOTE_DELETE', [$noteInfo->getId(), $noteInfo->getUserId()]); - url_redirect('manage-users-notes', ['user' => $noteInfo->getUserId()]); + Tools::redirect($urls->format('manage-users-notes', ['user' => $noteInfo->getUserId()])); return; } @@ -71,7 +72,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { ); // this is easier - url_redirect('manage-users-note', ['note' => $noteInfo->getId()]); + Tools::redirect($urls->format('manage-users-note', ['note' => $noteInfo->getId()])); return; } diff --git a/public-legacy/manage/users/role.php b/public-legacy/manage/users/role.php index 31c8783..81402ab 100644 --- a/public-legacy/manage/users/role.php +++ b/public-legacy/manage/users/role.php @@ -135,7 +135,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { $msz->getConfig()->setBoolean('perms.needsRecalc', true); } - url_redirect('manage-role', ['role' => $roleInfo->getId()]); + Tools::redirect($msz->getURLs()->format('manage-role', ['role' => $roleInfo->getId()])); return; } diff --git a/public-legacy/manage/users/user.php b/public-legacy/manage/users/user.php index cad3847..c71387c 100644 --- a/public-legacy/manage/users/user.php +++ b/public-legacy/manage/users/user.php @@ -12,6 +12,7 @@ $viewerPerms = $authInfo->getPerms('user'); if(!$authInfo->isLoggedIn()) Template::throwError(403); +$urls = $msz->getURLs(); $usersCtx = $msz->getUsersContext(); $users = $usersCtx->getUsers(); $roles = $usersCtx->getRoles(); @@ -72,7 +73,7 @@ if(CSRF::validateRequest() && $canEdit) { $tokenInfo = $tokenBuilder->toInfo(); AuthTokenCookie::apply($tokenPacker->pack($tokenInfo)); - url_redirect('index'); + Tools::redirect($urls->format('index')); return; } else $notices[] = 'You aren\'t allowed to impersonate this user.'; } @@ -221,7 +222,7 @@ if(CSRF::validateRequest() && $canEdit) { [$userInfo->getId()] ); - url_redirect('manage-user', ['user' => $userInfo->getId()]); + Tools::redirect($urls->format('manage-user', ['user' => $userInfo->getId()])); return; } diff --git a/public-legacy/manage/users/warning.php b/public-legacy/manage/users/warning.php index 8c2c990..7fc19e7 100644 --- a/public-legacy/manage/users/warning.php +++ b/public-legacy/manage/users/warning.php @@ -7,6 +7,7 @@ $authInfo = $msz->getAuthInfo(); if(!$authInfo->getPerms('user')->check(Perm::U_WARNINGS_MANAGE)) Template::throwError(403); +$urls = $msz->getURLs(); $usersCtx = $msz->getUsersContext(); $users = $usersCtx->getUsers(); $warns = $usersCtx->getWarnings(); @@ -23,7 +24,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && filter_has_var(INPUT_GET, 'delete')) $warns->deleteWarnings($warnInfo); $msz->createAuditLog('WARN_DELETE', [$warnInfo->getId(), $warnInfo->getUserId()]); - url_redirect('manage-users-warnings', ['user' => $warnInfo->getUserId()]); + Tools::redirect($urls->format('manage-users-warnings', ['user' => $warnInfo->getUserId()])); return; } @@ -44,7 +45,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { ); $msz->createAuditLog('WARN_CREATE', [$warnInfo->getId(), $userInfo->getId()]); - url_redirect('manage-users-warnings', ['user' => $userInfo->getId()]); + Tools::redirect($urls->format('manage-users-warnings', ['user' => $userInfo->getId()])); return; } diff --git a/public-legacy/profile.php b/public-legacy/profile.php index bf81317..c5bd9e9 100644 --- a/public-legacy/profile.php +++ b/public-legacy/profile.php @@ -15,6 +15,7 @@ $userId = !empty($_GET['u']) && is_string($_GET['u']) ? trim($_GET['u']) : 0; $profileMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : ''; $isEditing = !empty($_GET['edit']) && is_string($_GET['edit']) ? (bool)$_GET['edit'] : !empty($_POST) && is_array($_POST); +$urls = $msz->getURLs(); $usersCtx = $msz->getUsersContext(); $users = $usersCtx->getUsers(); $forumCtx = $msz->getForumContext(); @@ -54,11 +55,11 @@ switch($profileMode) { Template::throwError(404); case 'forum-topics': - url_redirect('search-query', ['query' => sprintf('type:forum:topic author:%s', $userInfo->getName()), 'section' => 'topics']); + Tools::redirect($urls->format('search-query', ['query' => sprintf('type:forum:topic author:%s', $userInfo->getName()), 'section' => 'topics'])); return; case 'forum-posts': - url_redirect('search-query', ['query' => sprintf('type:forum:post author:%s', $userInfo->getName()), 'section' => 'posts']); + Tools::redirect($urls->format('search-query', ['query' => sprintf('type:forum:post author:%s', $userInfo->getName()), 'section' => 'posts'])); return; case '': diff --git a/public-legacy/settings/account.php b/public-legacy/settings/account.php index 839eacd..f814a49 100644 --- a/public-legacy/settings/account.php +++ b/public-legacy/settings/account.php @@ -53,7 +53,7 @@ if($isVerifiedRequest && isset($_POST['tfa']['enable']) && $userInfo->hasTOTPKey if((bool)$_POST['tfa']['enable']) { $totpKey = TOTPGenerator::generateKey(); - $totpIssuer = $cfg->getString('site.name', 'Misuzu'); + $totpIssuer = $msz->getSiteInfo()->getName(); $totpQrcode = (new QRCode(new QROptions([ 'version' => 5, 'outputType' => QRCode::OUTPUT_IMAGE_JPG, diff --git a/public-legacy/settings/index.php b/public-legacy/settings/index.php index bcf8f4c..0bf9bb1 100644 --- a/public-legacy/settings/index.php +++ b/public-legacy/settings/index.php @@ -5,4 +5,4 @@ $authInfo = $msz->getAuthInfo(); if(!$authInfo->isLoggedIn()) Template::throwError(401); -url_redirect('settings-account'); +Tools::redirect($msz->getURLs()->format('settings-account')); diff --git a/public-legacy/settings/sessions.php b/public-legacy/settings/sessions.php index 7abf502..cc5be10 100644 --- a/public-legacy/settings/sessions.php +++ b/public-legacy/settings/sessions.php @@ -37,7 +37,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { } if($activeSessionKilled) { - url_redirect('index'); + Tools::redirect($msz->getURLs()->format('index')); return; } else break; } diff --git a/src/Changelog/ChangelogRoutes.php b/src/Changelog/ChangelogRoutes.php index 6a3a9dc..22fc78d 100644 --- a/src/Changelog/ChangelogRoutes.php +++ b/src/Changelog/ChangelogRoutes.php @@ -6,20 +6,22 @@ use RuntimeException; use Index\Routing\Route; use Index\Routing\RouteHandler; use Misuzu\Pagination; +use Misuzu\SiteInfo; use Misuzu\Template; use Misuzu\Auth\AuthInfo; use Misuzu\Comments\Comments; use Misuzu\Comments\CommentsEx; -use Misuzu\Config\IConfig; use Misuzu\Feeds\Feed; use Misuzu\Feeds\FeedItem; use Misuzu\Feeds\AtomFeedSerializer; use Misuzu\Feeds\RssFeedSerializer; +use Misuzu\URLs\URLRegistry; use Misuzu\Users\UsersContext; final class ChangelogRoutes extends RouteHandler { public function __construct( - private IConfig $config, + private SiteInfo $siteInfo, + private URLRegistry $urls, private Changelog $changelog, private UsersContext $usersCtx, private AuthInfo $authInfo, @@ -116,18 +118,18 @@ final class ChangelogRoutes extends RouteHandler { } private function createFeed(string $feedMode): Feed { - $siteName = $this->config->getString('site.name', 'Misuzu'); + $siteName = $this->siteInfo->getName(); $changes = $this->changelog->getChanges(pagination: new Pagination(10)); $feed = (new Feed) ->setTitle($siteName . ' » Changelog') ->setDescription('Live feed of changes to ' . $siteName . '.') - ->setContentUrl(url_prefix(false) . url('changelog-index')) - ->setFeedUrl(url_prefix(false) . url("changelog-feed-{$feedMode}")); + ->setContentUrl($this->siteInfo->getURL() . $this->urls->format('changelog-index')) + ->setFeedUrl($this->siteInfo->getURL() . $this->urls->format("changelog-feed-{$feedMode}")); foreach($changes as $change) { - $changeUrl = url_prefix(false) . url('changelog-change', ['change' => $change->getId()]); - $commentsUrl = url_prefix(false) . url('changelog-change-comments', ['change' => $change->getId()]); + $changeUrl = $this->siteInfo->getURL() . $this->urls->format('changelog-change', ['change' => $change->getId()]); + $commentsUrl = $this->siteInfo->getURL() . $this->urls->format('changelog-change-comments', ['change' => $change->getId()]); $feedItem = (new FeedItem) ->setTitle($change->getActionText() . ': ' . $change->getSummary()) @@ -153,18 +155,4 @@ final class ChangelogRoutes extends RouteHandler { $response->setContentType('application/atom+xml; charset=utf-8'); return (new AtomFeedSerializer)->serializeFeed($this->createFeed('atom')); } - - #[Route('GET', '/changelog.php')] - public static function getChangelogPHP($response, $request) { - $changeId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT); - if($changeId) { - $response->redirect(url('changelog-change', ['change' => $changeId]), true); - return; - } - - $response->redirect(url('changelog-index', [ - 'date' => $request->getParam('d'), - 'user' => $request->getParam('u', FILTER_SANITIZE_NUMBER_INT), - ]), true); - } } diff --git a/src/Home/HomeRoutes.php b/src/Home/HomeRoutes.php index b748ea1..6af05cf 100644 --- a/src/Home/HomeRoutes.php +++ b/src/Home/HomeRoutes.php @@ -8,6 +8,7 @@ use Index\Data\IDbConnection; use Index\Routing\Route; use Index\Routing\RouteHandler; use Misuzu\Pagination; +use Misuzu\SiteInfo; use Misuzu\Template; use Misuzu\Auth\AuthInfo; use Misuzu\Changelog\Changelog; @@ -21,6 +22,7 @@ class HomeRoutes extends RouteHandler { public function __construct( private IConfig $config, private IDbConnection $dbConn, + private SiteInfo $siteInfo, private AuthInfo $authInfo, private Changelog $changelog, private Comments $comments, @@ -202,9 +204,6 @@ class HomeRoutes extends RouteHandler { $config = $this->config->getValues([ ['social.embed_linked:b'], ['landing.forum_categories:a'], - ['site.name:s', 'Misuzu'], - 'site.url:s', - 'site.ext_logo:s', 'social.linked:a' ]); @@ -212,9 +211,9 @@ class HomeRoutes extends RouteHandler { $linkedData = [ '@context' => 'http://schema.org', '@type' => 'Organization', - 'name' => $config['site.name'], - 'url' => $config['site.url'], - 'logo' => $config['site.ext_logo'], + 'name' => $this->siteInfo->getName(), + 'url' => $this->siteInfo->getURL(), + 'logo' => $this->siteInfo->getExternalLogo(), 'same_as' => $config['social.linked'], ]; } else $linkedData = null; @@ -236,9 +235,4 @@ class HomeRoutes extends RouteHandler { 'forum_active' => $activeTopics, ]); } - - #[Route('GET', '/index.php')] - public static function getIndexPHP($response) { - $response->redirect(url('index'), true); - } } diff --git a/src/Info/InfoRoutes.php b/src/Info/InfoRoutes.php index 971ba5c..8177978 100644 --- a/src/Info/InfoRoutes.php +++ b/src/Info/InfoRoutes.php @@ -127,19 +127,4 @@ class InfoRoutes extends RouteHandler { ], ]); } - - #[Route('GET', '/info.php')] - public static function getInfoPHP($response) { - $response->redirect(url('info'), true); - } - - #[Route('GET', '/info.php/:name')] - public static function getInfoDocsPHP($response, $request, string $name) { - $response->redirect(url('info', ['title' => $name]), true); - } - - #[Route('GET', '/info.php/:project/:name')] - public static function getInfoProjectPHP($response, $request, string $project, string $name) { - $response->redirect(url('info', ['title' => $project . '/' . $name]), true); - } } diff --git a/src/LegacyRoutes.php b/src/LegacyRoutes.php new file mode 100644 index 0000000..372d3f9 --- /dev/null +++ b/src/LegacyRoutes.php @@ -0,0 +1,136 @@ +redirect($this->urls->format('index'), true); + } + + #[Route('GET', '/info.php')] + public function getInfoPHP($response): void { + $response->redirect($this->urls->format('info'), true); + } + + #[Route('GET', '/info.php/:name')] + public function getInfoDocsPHP($response, $request, string $name): void { + $response->redirect($this->urls->format('info', ['title' => $name]), true); + } + + #[Route('GET', '/info.php/:project/:name')] + public function getInfoProjectPHP($response, $request, string $project, string $name): void { + $response->redirect($this->urls->format('info', ['title' => $project . '/' . $name]), true); + } + + #[Route('GET', '/news.php')] + public function getNewsPHP($response, $request): void { + $postId = (int)($request->getParam('n', FILTER_SANITIZE_NUMBER_INT) ?? $request->getParam('p', FILTER_SANITIZE_NUMBER_INT)); + + if($postId > 0) + $location = $this->urls->format('news-post', ['post' => $postId]); + else { + $catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT); + $pageId = $request->getParam('page', FILTER_SANITIZE_NUMBER_INT); + $location = $this->urls->format($catId > 0 ? 'news-category' : 'news-index', ['category' => $catId, 'page' => $pageId]); + } + + $response->redirect($location, true); + } + + #[Route('GET', '/news.php/rss')] + public function getNewsRssPHP($response, $request): void { + $catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT); + $location = $this->urls->format($catId > 0 ? 'news-category-feed-rss' : 'news-feed-rss', ['category' => $catId]); + $response->redirect($location, true); + } + + #[Route('GET', '/news.php/atom')] + public function getNewsAtomPHP($response, $request): void { + $catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT); + $location = $this->urls->format($catId > 0 ? 'news-category-feed-atom' : 'news-feed-atom', ['category' => $catId]); + $response->redirect($location, true); + } + + #[Route('GET', '/news/index.php')] + public function getNewsIndexPHP($response, $request): void { + $response->redirect($this->urls->format('news-index', [ + 'page' => $request->getParam('page', FILTER_SANITIZE_NUMBER_INT), + ]), true); + } + + #[Route('GET', '/news/category.php')] + public function getNewsCategoryPHP($response, $request): void { + $response->redirect($this->urls->format('news-category', [ + 'category' => $request->getParam('c', FILTER_SANITIZE_NUMBER_INT), + 'page' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT), + ]), true); + } + + #[Route('GET', '/news/post.php')] + public function getNewsPostPHP($response, $request): void { + $response->redirect($this->urls->format('news-post', [ + 'post' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT), + ]), true); + } + + #[Route('GET', '/news/feed.php')] + public function getNewsFeedPHP($response, $request): int { + return 400; + } + + #[Route('GET', '/news/feed.php/rss')] + public function getNewsFeedRssPHP($response, $request): void { + $catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT); + $response->redirect($this->urls->format( + $catId > 0 ? 'news-category-feed-rss' : 'news-feed-rss', + ['category' => $catId] + ), true); + } + + #[Route('GET', '/news/feed.php/atom')] + public function getNewsFeedAtomPHP($response, $request): void { + $catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT); + $response->redirect($this->urls->format( + $catId > 0 ? 'news-category-feed-atom' : 'news-feed-atom', + ['category' => $catId] + ), true); + } + + #[Route('GET', '/changelog.php')] + public function getChangelogPHP($response, $request): void { + $changeId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT); + if($changeId) { + $response->redirect($this->urls->format('changelog-change', ['change' => $changeId]), true); + return; + } + + $response->redirect($this->urls->format('changelog-index', [ + 'date' => $request->getParam('d'), + 'user' => $request->getParam('u', FILTER_SANITIZE_NUMBER_INT), + ]), true); + } + + #[Route('GET', '/auth.php')] + public function getAuthPHP($response, $request): void { + $response->redirect($this->urls->format(match($request->getParam('m')) { + 'logout' => 'auth-logout', + 'reset' => 'auth-reset', + 'forgot' => 'auth-forgot', + 'register' => 'auth-register', + default => 'auth-login', + }), true); + } + + #[Route('GET', '/settings.php')] + public function getSettingsPHP($response): void { + $response->redirect($this->urls->format('settings-index'), true); + } +} diff --git a/src/MisuzuContext.php b/src/MisuzuContext.php index 3840a8a..2c12d75 100644 --- a/src/MisuzuContext.php +++ b/src/MisuzuContext.php @@ -29,6 +29,7 @@ use Misuzu\Perms\Permissions; use Misuzu\Profile\ProfileFields; use Misuzu\Satori\SatoriRoutes; use Misuzu\SharpChat\SharpChatRoutes; +use Misuzu\URLs\URLRegistry; use Misuzu\Users\UsersContext; use Misuzu\Users\UserInfo; use Misuzu\Users\Assets\AssetsRoutes; @@ -44,6 +45,7 @@ class MisuzuContext { private IConfig $config; private HttpFx $router; private SasaeEnvironment $templating; + private URLRegistry $urls; private AuditLog $auditLog; private Counters $counters; @@ -63,13 +65,18 @@ class MisuzuContext { private Permissions $perms; private AuthInfo $authInfo; + private SiteInfo $siteInfo; public function __construct(IDbConnection $dbConn, IConfig $config) { $this->dbConn = $dbConn; $this->config = $config; + $this->urls = new URLRegistry; + $this->registerURLs(); + $this->perms = new Permissions($dbConn); $this->authInfo = new AuthInfo($this->perms); + $this->siteInfo = new SiteInfo($config->scopeTo('site')); $this->authCtx = new AuthContext($dbConn, $config->scopeTo('auth')); $this->usersCtx = new UsersContext($dbConn); @@ -101,6 +108,10 @@ class MisuzuContext { return new FsDbMigrationRepo(MSZ_MIGRATIONS); } + public function getURLs(): URLRegistry { + return $this->urls; + } + public function getConfig(): IConfig { return $this->config; } @@ -157,6 +168,10 @@ class MisuzuContext { return $this->authInfo; } + public function getSiteInfo(): SiteInfo { + return $this->siteInfo; + } + public function createAuditLog(string $action, array $params = [], UserInfo|string|null $userInfo = null): void { if($userInfo === null && $this->authInfo->isLoggedIn()) $userInfo = $this->authInfo->getUserInfo(); @@ -190,16 +205,14 @@ class MisuzuContext { public function startTemplating(): void { $globals = $this->config->getValues([ - ['site.name:s', 'Misuzu', 'site_name'], - ['site.desc:s', '', 'site_description'], - ['site.url:s', '', 'site_url'], ['eeprom.path:s', '', 'eeprom_path'], ['eeprom.app:s', '', 'eeprom_app'], ]); $isDebug = Environment::isDebug(); - $globals['assets'] = $this->getWebAssetInfo(); + $globals['site_info'] = $this->siteInfo; $globals['auth_info'] = $this->authInfo; + $globals['assets'] = $this->getWebAssetInfo(); $globals['active_ban_info'] = $this->usersCtx->tryGetActiveBan($this->authInfo->getUserInfo()); $globals['display_timings_info'] = $isDebug || $this->authInfo->getPerms('global')->check(Perm::G_TIMINGS_VIEW); @@ -250,6 +263,7 @@ class MisuzuContext { $this->router->register(new HomeRoutes( $this->config, $this->dbConn, + $this->siteInfo, $this->authInfo, $this->changelog, $this->comments, @@ -260,21 +274,24 @@ class MisuzuContext { $this->router->register(new AssetsRoutes( $this->authInfo, + $this->urls, $this->usersCtx )); $this->router->register(new InfoRoutes); $this->router->register(new NewsRoutes( - $this->config, + $this->siteInfo, $this->authInfo, + $this->urls, $this->news, $this->usersCtx, $this->comments )); $this->router->register(new ChangelogRoutes( - $this->config, + $this->siteInfo, + $this->urls, $this->changelog, $this->usersCtx, $this->authInfo, @@ -283,6 +300,7 @@ class MisuzuContext { $this->router->register(new SharpChatRoutes( $this->config->scopeTo('sockChat'), + $this->urls, $this->usersCtx, $this->authCtx, $this->emotes, @@ -297,19 +315,141 @@ class MisuzuContext { $this->profileFields )); - // below is still only otherwise available as stinky php files + $this->router->register(new LegacyRoutes($this->urls)); + } - $this->router->get('/auth.php', function($response, $request) { - $response->redirect(url([ - 'logout' => 'auth-logout', - 'reset' => 'auth-reset', - 'forgot' => 'auth-forgot', - 'register' => 'auth-register', - ][$request->getParam('m')] ?? 'auth-login'), true); - }); + public function registerURLs(): void { + // eventually this should be handled by context classes + $this->urls->register('index', '/'); + $this->urls->register('info', '/info/'); - $this->router->get('/settings.php', function($response) { - $response->redirect(url('settings-index'), true); - }); + $this->urls->register('search-index', '/search.php'); + $this->urls->register('search-query', '/search.php', ['q' => '<query>'], '<section>'); + + $this->urls->register('auth-login', '/auth/login.php', ['username' => '<username>', 'redirect' => '<redirect>']); + $this->urls->register('auth-login-welcome', '/auth/login.php', ['welcome' => '1', 'username' => '<username>']); + $this->urls->register('auth-register', '/auth/register.php'); + $this->urls->register('auth-forgot', '/auth/password.php'); + $this->urls->register('auth-reset', '/auth/password.php', ['user' => '<user>']); + $this->urls->register('auth-logout', '/auth/logout.php', ['csrf' => '<csrf>']); + $this->urls->register('auth-resolve-user', '/auth/login.php', ['resolve' => '1', 'name' => '<username>']); + $this->urls->register('auth-two-factor', '/auth/twofactor.php', ['token' => '<token>']); + $this->urls->register('auth-revert', '/auth/revert.php', ['csrf' => '<csrf>']); + + $this->urls->register('changelog-index', '/changelog', ['date' => '<date>', 'user' => '<user>', 'tags' => '<tags>', 'p' => '<page>']); + $this->urls->register('changelog-feed-rss', '/changelog.rss'); + $this->urls->register('changelog-feed-atom', '/changelog.atom'); + $this->urls->register('changelog-change', '/changelog/change/<change>'); + $this->urls->register('changelog-change-comments', '/changelog/change/<change>', fragment: 'comments'); + + $this->urls->register('news-index', '/news', ['p' => '<page>']); + $this->urls->register('news-category', '/news/<category>', ['p' => '<page>']); + $this->urls->register('news-post', '/news/post/<post>'); + $this->urls->register('news-post-comments', '/news/post/<post>', fragment: 'comments'); + $this->urls->register('news-feed-rss', '/news.rss'); + $this->urls->register('news-category-feed-rss', '/news/<category>.rss'); + $this->urls->register('news-feed-atom', '/news.atom'); + $this->urls->register('news-category-feed-atom', '/news/<category>.atom'); + + $this->urls->register('forum-index', '/forum'); + $this->urls->register('forum-leaderboard', '/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']); + $this->urls->register('forum-mark-global', '/forum/index.php', ['m' => 'mark']); + $this->urls->register('forum-mark-single', '/forum/index.php', ['m' => 'mark', 'f' => '<forum>']); + $this->urls->register('forum-topic-new', '/forum/posting.php', ['f' => '<forum>']); + $this->urls->register('forum-reply-new', '/forum/posting.php', ['t' => '<topic>']); + $this->urls->register('forum-category', '/forum/forum.php', ['f' => '<forum>', 'p' => '<page>']); + $this->urls->register('forum-category-root', '/forum/index.php', fragment: '<forum>'); + $this->urls->register('forum-topic', '/forum/topic.php', ['t' => '<topic>', 'page' => '<page>']); + $this->urls->register('forum-topic-create', '/forum/posting.php', ['f' => '<forum>']); + $this->urls->register('forum-topic-bump', '/forum/topic.php', ['t' => '<topic>', 'm' => 'bump', 'csrf' => '<csrf>']); + $this->urls->register('forum-topic-lock', '/forum/topic.php', ['t' => '<topic>', 'm' => 'lock', 'csrf' => '<csrf>']); + $this->urls->register('forum-topic-unlock', '/forum/topic.php', ['t' => '<topic>', 'm' => 'unlock', 'csrf' => '<csrf>']); + $this->urls->register('forum-topic-delete', '/forum/topic.php', ['t' => '<topic>', 'm' => 'delete', 'csrf' => '<csrf>']); + $this->urls->register('forum-topic-restore', '/forum/topic.php', ['t' => '<topic>', 'm' => 'restore', 'csrf' => '<csrf>']); + $this->urls->register('forum-topic-nuke', '/forum/topic.php', ['t' => '<topic>', 'm' => 'nuke', 'csrf' => '<csrf>']); + $this->urls->register('forum-post', '/forum/topic.php', ['p' => '<post>'], 'p<post>'); + $this->urls->register('forum-post-create', '/forum/posting.php', ['t' => '<topic>']); + $this->urls->register('forum-post-delete', '/forum/post.php', ['p' => '<post>', 'm' => 'delete']); + $this->urls->register('forum-post-restore', '/forum/post.php', ['p' => '<post>', 'm' => 'restore']); + $this->urls->register('forum-post-nuke', '/forum/post.php', ['p' => '<post>', 'm' => 'nuke']); + $this->urls->register('forum-post-quote', '/forum/posting.php', ['q' => '<post>']); + $this->urls->register('forum-post-edit', '/forum/posting.php', ['p' => '<post>', 'm' => 'edit']); + + $this->urls->register('user-list', '/members.php', ['r' => '<role>', 'ss' => '<sort>', 'sd' => '<direction>', 'p' => '<page>']); + + $this->urls->register('user-profile', '/profile.php', ['u' => '<user>']); + $this->urls->register('user-profile-forum-topics', '/profile.php', ['u' => '<user>', 'm' => 'forum-topics']); + $this->urls->register('user-profile-forum-posts', '/profile.php', ['u' => '<user>', 'm' => 'forum-posts']); + $this->urls->register('user-profile-edit', '/profile.php', ['u' => '<user>', 'edit' => '1']); + $this->urls->register('user-account-standing', '/profile.php', ['u' => '<user>'], 'account-standing'); + + $this->urls->register('user-avatar', '/assets/avatar/<user>', ['res' => '<res>']); + $this->urls->register('user-background', '/assets/profile-background/<user>'); + + $this->urls->register('settings-index', '/settings'); + $this->urls->register('settings-account', '/settings/account.php'); + $this->urls->register('settings-sessions', '/settings/sessions.php', ['p' => '<page>']); + $this->urls->register('settings-logs', '/settings/logs.php'); + $this->urls->register('settings-logs-logins', '/settings/logs.php', ['ap' => '<account-page>', 'hp' => '<page>'], 'login-history'); + $this->urls->register('settings-logs-account', '/settings/logs.php', ['hp' => '<logins-page>', 'ap' => '<page>'], 'account-log'); + $this->urls->register('settings-data', '/settings/data.php'); + + $this->urls->register('comment-create', '/comments.php', ['m' => 'create', 'return' => '<return>']); + $this->urls->register('comment-vote', '/comments.php', ['c' => '<comment>', 'csrf' => '<csrf>', 'm' => 'vote', 'v' => '<vote>', 'return' => '<return>']); + $this->urls->register('comment-delete', '/comments.php', ['c' => '<comment>', 'csrf' => '<csrf>', 'm' => 'delete', 'return' => '<return>']); + $this->urls->register('comment-restore', '/comments.php', ['c' => '<comment>', 'csrf' => '<csrf>', 'm' => 'restore', 'return' => '<return>']); + $this->urls->register('comment-pin', '/comments.php', ['c' => '<comment>', 'csrf' => '<csrf>', 'm' => 'pin', 'return' => '<return>']); + $this->urls->register('comment-unpin', '/comments.php', ['c' => '<comment>', 'csrf' => '<csrf>', 'm' => 'unpin', 'return' => '<return>']); + + $this->urls->register('manage-index', '/manage'); + + $this->urls->register('manage-general-overview', '/manage/general'); + $this->urls->register('manage-general-logs', '/manage/general/logs.php', ['p' => '<page>']); + + $this->urls->register('manage-general-emoticons', '/manage/general/emoticons.php'); + $this->urls->register('manage-general-emoticon', '/manage/general/emoticon.php', ['e' => '<emote>']); + $this->urls->register('manage-general-emoticon-order-up', '/manage/general/emoticons.php', ['emote' => '<emote>', 'order' => 'd', 'csrf' => '<csrf>']); + $this->urls->register('manage-general-emoticon-order-down', '/manage/general/emoticons.php', ['emote' => '<emote>', 'order' => 'i', 'csrf' => '<csrf>']); + $this->urls->register('manage-general-emoticon-delete', '/manage/general/emoticons.php', ['emote' => '<emote>', 'delete' => '1', 'csrf' => '<csrf>']); + $this->urls->register('manage-general-emoticon-alias', '/manage/general/emoticons.php', ['emote' => '<emote>', 'alias' => '<string>', 'csrf' => '<csrf>']); + + $this->urls->register('manage-general-settings', '/manage/general/settings.php'); + $this->urls->register('manage-general-setting', '/manage/general/setting.php', ['name' => '<name>', 'type' => '<type>']); + $this->urls->register('manage-general-setting-delete', '/manage/general/setting-delete.php', ['name' => '<name>']); + + $this->urls->register('manage-forum-categories', '/manage/forum/index.php'); + $this->urls->register('manage-forum-category', '/manage/forum/category.php', ['f' => '<forum>']); + $this->urls->register('manage-forum-topic-redirs', '/manage/forum/redirs.php', ['p' => '<page>']); + $this->urls->register('manage-forum-topic-redirs-create', '/manage/forum/redirs.php'); + $this->urls->register('manage-forum-topic-redirs-nuke', '/manage/forum/redirs.php', ['m' => 'explode', 't' => '<topic>', 'csrf' => '<csrf>']); + + $this->urls->register('manage-changelog-changes', '/manage/changelog', ['p' => '<page>']); + $this->urls->register('manage-changelog-change', '/manage/changelog/change.php', ['c' => '<change>']); + $this->urls->register('manage-changelog-change-delete', '/manage/changelog/change.php', ['c' => '<change>', 'delete' => '1', 'csrf' => '<csrf>']); + $this->urls->register('manage-changelog-tags', '/manage/changelog/tags.php'); + $this->urls->register('manage-changelog-tag', '/manage/changelog/tag.php', ['t' => '<tag>']); + $this->urls->register('manage-changelog-tag-delete', '/manage/changelog/tag.php', ['t' => '<tag>', 'delete' => '1', 'csrf' => '<csrf>']); + + $this->urls->register('manage-news-categories', '/manage/news/categories.php', ['p' => '<page>']); + $this->urls->register('manage-news-category', '/manage/news/category.php', ['c' => '<category>']); + $this->urls->register('manage-news-category-delete', '/manage/news/category.php', ['c' => '<category>', 'delete' => '1', 'csrf' => '<csrf>']); + $this->urls->register('manage-news-posts', '/manage/news/posts.php', ['p' => '<page>']); + $this->urls->register('manage-news-post', '/manage/news/post.php', ['p' => '<post>']); + $this->urls->register('manage-news-post-delete', '/manage/news/post.php', ['p' => '<post>', 'delete' => '1', 'csrf' => '<csrf>']); + + $this->urls->register('manage-users', '/manage/users', ['p' => '<page>']); + $this->urls->register('manage-user', '/manage/users/user.php', ['u' => '<user>']); + $this->urls->register('manage-users-warnings', '/manage/users/warnings.php', ['u' => '<user>', 'p' => '<page>']); + $this->urls->register('manage-users-warning', '/manage/users/warning.php', ['u' => '<user>']); + $this->urls->register('manage-users-warning-delete', '/manage/users/warning.php', ['w' => '<warning>', 'delete' => '1', 'csrf' => '<csrf>']); + $this->urls->register('manage-users-notes', '/manage/users/notes.php', ['u' => '<user>', 'p' => '<page>']); + $this->urls->register('manage-users-note', '/manage/users/note.php', ['n' => '<note>', 'u' => '<user>']); + $this->urls->register('manage-users-note-delete', '/manage/users/note.php', ['n' => '<note>', 'delete' => '1', 'csrf' => '<csrf>']); + $this->urls->register('manage-users-bans', '/manage/users/bans.php', ['u' => '<user>', 'p' => '<page>']); + $this->urls->register('manage-users-ban', '/manage/users/ban.php', ['u' => '<user>']); + $this->urls->register('manage-users-ban-delete', '/manage/users/ban.php', ['b' => '<ban>', 'delete' => '1', 'csrf' => '<csrf>']); + + $this->urls->register('manage-roles', '/manage/users/roles.php', ['p' => '<page>']); + $this->urls->register('manage-role', '/manage/users/role.php', ['r' => '<role>']); } } diff --git a/src/MisuzuSasaeExtension.php b/src/MisuzuSasaeExtension.php index 8c53b99..8da2a99 100644 --- a/src/MisuzuSasaeExtension.php +++ b/src/MisuzuSasaeExtension.php @@ -26,8 +26,7 @@ final class MisuzuSasaeExtension extends AbstractExtension { public function getFunctions() { return [ - new TwigFunction('url_construct', 'url_construct'), - new TwigFunction('url', 'url'), + new TwigFunction('url', $this->ctx->getURLs()->format(...)), new TwigFunction('csrf_token', CSRF::token(...)), new TwigFunction('git_commit_hash', GitInfo::hash(...)), new TwigFunction('git_tag', GitInfo::tag(...)), @@ -37,6 +36,7 @@ final class MisuzuSasaeExtension extends AbstractExtension { new TwigFunction('msz_header_menu', $this->getHeaderMenu(...)), new TwigFunction('msz_user_menu', $this->getUserMenu(...)), new TwigFunction('msz_manage_menu', $this->getManageMenu(...)), + new TwigFunction('msz_pagination_url', $this->paginationUrlHelper(...)) ]; } @@ -68,50 +68,51 @@ final class MisuzuSasaeExtension extends AbstractExtension { public function getHeaderMenu(): array { $menu = []; + $urls = $this->ctx->getURLs(); $authInfo = $this->ctx->getAuthInfo(); $home = [ 'title' => 'Home', - 'url' => url('index'), + 'url' => $urls->format('index'), 'menu' => [], ]; if($authInfo->isLoggedIn()) $home['menu'][] = [ 'title' => 'Members', - 'url' => url('user-list'), + 'url' => $urls->format('user-list'), ]; $home['menu'][] = [ 'title' => 'Changelog', - 'url' => url('changelog-index'), + 'url' => $urls->format('changelog-index'), ]; $home['menu'][] = [ 'title' => 'Contact', - 'url' => url('info', ['title' => 'contact']), + 'url' => $urls->format('info', ['title' => 'contact']), ]; $home['menu'][] = [ 'title' => 'Rules', - 'url' => url('info', ['title' => 'rules']), + 'url' => $urls->format('info', ['title' => 'rules']), ]; $menu[] = $home; $menu[] = [ 'title' => 'News', - 'url' => url('news-index'), + 'url' => $urls->format('news-index'), ]; $forum = [ 'title' => 'Forum', - 'url' => url('forum-index'), + 'url' => $urls->format('forum-index'), 'menu' => [], ]; if($authInfo->getPerms('global')->check(Perm::G_FORUM_LEADERBOARD_VIEW)) $forum['menu'][] = [ 'title' => 'Leaderboard', - 'url' => url('forum-leaderboard'), + 'url' => $urls->format('forum-leaderboard'), ]; $menu[] = $forum; @@ -128,6 +129,7 @@ final class MisuzuSasaeExtension extends AbstractExtension { public function getUserMenu(bool $inBroomCloset, string $manageUrl = ''): array { $menu = []; + $urls = $this->ctx->getURLs(); $authInfo = $this->ctx->getAuthInfo(); $usersCtx = $this->ctx->getUsersContext(); @@ -136,17 +138,17 @@ final class MisuzuSasaeExtension extends AbstractExtension { $menu[] = [ 'title' => 'Profile', - 'url' => url('user-profile', ['user' => $userInfo->getId()]), + 'url' => $urls->format('user-profile', ['user' => $userInfo->getId()]), 'icon' => 'fas fa-user fa-fw', ]; $menu[] = [ 'title' => 'Settings', - 'url' => url('settings-index'), + 'url' => $urls->format('settings-index'), 'icon' => 'fas fa-cog fa-fw', ]; $menu[] = [ 'title' => 'Search', - 'url' => url('search-index'), + 'url' => $urls->format('search-index'), 'icon' => 'fas fa-search fa-fw', ]; @@ -156,31 +158,31 @@ final class MisuzuSasaeExtension extends AbstractExtension { if($inBroomCloset) $menu[] = [ 'title' => 'Exit Broom Closet', - 'url' => $manageUrl === '' ? url('index') : $manageUrl, + 'url' => $manageUrl === '' ? $urls->format('index') : $manageUrl, 'icon' => 'fas fa-door-open fa-fw', ]; else $menu[] = [ 'title' => 'Enter Broom Closet', - 'url' => $manageUrl === '' ? url('manage-index') : $manageUrl, + 'url' => $manageUrl === '' ? $urls->format('manage-index') : $manageUrl, 'icon' => 'fas fa-door-closed fa-fw', ]; } $menu[] = [ 'title' => 'Log out', - 'url' => url('auth-logout'), + 'url' => $urls->format('auth-logout'), 'icon' => 'fas fa-sign-out-alt fa-fw', ]; } else { $menu[] = [ 'title' => 'Register', - 'url' => url('auth-register'), + 'url' => $urls->format('auth-register'), 'icon' => 'fas fa-user-plus fa-fw', ]; $menu[] = [ 'title' => 'Log in', - 'url' => url('auth-login'), + 'url' => $urls->format('auth-login'), 'icon' => 'fas fa-sign-in-alt fa-fw', ]; } @@ -189,6 +191,7 @@ final class MisuzuSasaeExtension extends AbstractExtension { } public function getManageMenu(): array { + $urls = $this->ctx->getURLs(); $authInfo = $this->ctx->getAuthInfo(); $globalPerms = $authInfo->getPerms('global'); if(!$authInfo->isLoggedIn() || !$globalPerms->check(Perm::G_IS_JANITOR)) @@ -196,44 +199,48 @@ final class MisuzuSasaeExtension extends AbstractExtension { $menu = [ 'General' => [ - 'Overview' => url('manage-general-overview'), + 'Overview' => $urls->format('manage-general-overview'), ], ]; if($globalPerms->check(Perm::G_LOGS_VIEW)) - $menu['General']['Logs'] = url('manage-general-logs'); + $menu['General']['Logs'] = $urls->format('manage-general-logs'); if($globalPerms->check(Perm::G_EMOTES_MANAGE)) - $menu['General']['Emoticons'] = url('manage-general-emoticons'); + $menu['General']['Emoticons'] = $urls->format('manage-general-emoticons'); if($globalPerms->check(Perm::G_CONFIG_MANAGE)) - $menu['General']['Settings'] = url('manage-general-settings'); + $menu['General']['Settings'] = $urls->format('manage-general-settings'); $userPerms = $authInfo->getPerms('user'); if($userPerms->check(Perm::U_USERS_MANAGE)) - $menu['Users & Roles']['Users'] = url('manage-users'); + $menu['Users & Roles']['Users'] = $urls->format('manage-users'); if($userPerms->check(Perm::U_ROLES_MANAGE)) - $menu['Users & Roles']['Roles'] = url('manage-roles'); + $menu['Users & Roles']['Roles'] = $urls->format('manage-roles'); if($userPerms->check(Perm::U_NOTES_MANAGE)) - $menu['Users & Roles']['Notes'] = url('manage-users-notes'); + $menu['Users & Roles']['Notes'] = $urls->format('manage-users-notes'); if($userPerms->check(Perm::U_WARNINGS_MANAGE)) - $menu['Users & Roles']['Warnings'] = url('manage-users-warnings'); + $menu['Users & Roles']['Warnings'] = $urls->format('manage-users-warnings'); if($userPerms->check(Perm::U_BANS_MANAGE)) - $menu['Users & Roles']['Bans'] = url('manage-users-bans'); + $menu['Users & Roles']['Bans'] = $urls->format('manage-users-bans'); if($globalPerms->check(Perm::G_NEWS_POSTS_MANAGE)) - $menu['News']['Posts'] = url('manage-news-posts'); + $menu['News']['Posts'] = $urls->format('manage-news-posts'); if($globalPerms->check(Perm::G_NEWS_CATEGORIES_MANAGE)) - $menu['News']['Categories'] = url('manage-news-categories'); + $menu['News']['Categories'] = $urls->format('manage-news-categories'); if($globalPerms->check(Perm::G_FORUM_CATEGORIES_MANAGE)) - $menu['Forum']['Permission Calculator'] = url('manage-forum-categories'); + $menu['Forum']['Permission Calculator'] = $urls->format('manage-forum-categories'); if($globalPerms->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE)) - $menu['Forum']['Topic Redirects'] = url('manage-forum-topic-redirs'); + $menu['Forum']['Topic Redirects'] = $urls->format('manage-forum-topic-redirs'); if($globalPerms->check(Perm::G_CL_CHANGES_MANAGE)) - $menu['Changelog']['Changes'] = url('manage-changelog-changes'); + $menu['Changelog']['Changes'] = $urls->format('manage-changelog-changes'); if($globalPerms->check(Perm::G_CL_TAGS_MANAGE)) - $menu['Changelog']['Tags'] = url('manage-changelog-tags'); + $menu['Changelog']['Tags'] = $urls->format('manage-changelog-tags'); return $menu; } + + public function paginationUrlHelper(): string { + // + } } diff --git a/src/News/NewsRoutes.php b/src/News/NewsRoutes.php index 01b4954..97074d7 100644 --- a/src/News/NewsRoutes.php +++ b/src/News/NewsRoutes.php @@ -8,25 +8,25 @@ use Index\Data\IDbConnection; use Index\Routing\Route; use Index\Routing\RouteHandler; use Misuzu\Pagination; +use Misuzu\SiteInfo; use Misuzu\Template; use Misuzu\Auth\AuthInfo; use Misuzu\Comments\Comments; use Misuzu\Comments\CommentsCategory; use Misuzu\Comments\CommentsEx; -use Misuzu\Config\IConfig; use Misuzu\Feeds\Feed; use Misuzu\Feeds\FeedItem; use Misuzu\Feeds\AtomFeedSerializer; use Misuzu\Feeds\RssFeedSerializer; -use Misuzu\News\News; -use Misuzu\News\NewsCategoryInfo; -use Misuzu\Users\UsersContext; use Misuzu\Parsers\Parser; +use Misuzu\URLs\URLRegistry; +use Misuzu\Users\UsersContext; class NewsRoutes extends RouteHandler { public function __construct( - private IConfig $config, + private SiteInfo $siteInfo, private AuthInfo $authInfo, + private URLRegistry $urls, private News $news, private UsersContext $usersCtx, private Comments $comments @@ -193,7 +193,7 @@ class NewsRoutes extends RouteHandler { private function getFeed($response, string $feedType, ?NewsCategoryInfo $categoryInfo = null) { $hasCategory = $categoryInfo !== null; - $siteName = $this->config->getString('site.name', 'Misuzu'); + $siteName = $this->siteInfo->getName(); $posts = $this->getNewsPostsForFeed($categoryInfo); $serialiser = match($feedType) { @@ -206,8 +206,8 @@ class NewsRoutes extends RouteHandler { $feed = (new Feed) ->setTitle($siteName . ' » ' . ($hasCategory ? $categoryInfo->getName() : 'Featured News')) ->setDescription($hasCategory ? $categoryInfo->getDescription() : 'A live featured news feed.') - ->setContentUrl(url_prefix(false) . ($hasCategory ? url('news-category', ['category' => $categoryInfo->getId()]) : url('news-index'))) - ->setFeedUrl(url_prefix(false) . ($hasCategory ? url("news-category-feed-{$feedType}", ['category' => $categoryInfo->getId()]) : url("news-feed-{$feedType}"))); + ->setContentUrl($this->siteInfo->getURL() . ($hasCategory ? $this->urls->format('news-category', ['category' => $categoryInfo->getId()]) : $this->urls->format('news-index'))) + ->setFeedUrl($this->siteInfo->getURL() . ($hasCategory ? $this->urls->format("news-category-feed-{$feedType}", ['category' => $categoryInfo->getId()]) : $this->urls->format("news-feed-{$feedType}"))); foreach($posts as $post) { $postInfo = $post['post']; @@ -220,9 +220,9 @@ class NewsRoutes extends RouteHandler { $userName = $userInfo->getName(); } - $postUrl = url_prefix(false) . url('news-post', ['post' => $postInfo->getId()]); - $commentsUrl = url_prefix(false) . url('news-post-comments', ['post' => $postInfo->getId()]); - $authorUrl = url_prefix(false) . url('user-profile', ['user' => $userId]); + $postUrl = $this->siteInfo->getURL() . $this->urls->format('news-post', ['post' => $postInfo->getId()]); + $commentsUrl = $this->siteInfo->getURL() . $this->urls->format('news-post-comments', ['post' => $postInfo->getId()]); + $authorUrl = $this->siteInfo->getURL() . $this->urls->format('user-profile', ['user' => $userId]); $feedItem = (new FeedItem) ->setTitle($postInfo->getTitle()) @@ -243,74 +243,4 @@ class NewsRoutes extends RouteHandler { return $serialiser->serializeFeed($feed); } - - #[Route('GET', '/news.php')] - public static function getNewsPHP($response, $request) { - $postId = $request->getParam('n', FILTER_SANITIZE_NUMBER_INT) - ?? $request->getParam('p', FILTER_SANITIZE_NUMBER_INT); - - if($postId > 0) - $location = url('news-post', ['post' => $postId]); - else { - $catId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT); - $pageId = $request->getParam('page', FILTER_SANITIZE_NUMBER_INT); - $location = url($catId > 0 ? 'news-category' : 'news-index', ['category' => $catId, 'page' => $pageId]); - } - - $response->redirect($location, true); - } - - #[Route('GET', '/news.php/rss')] - public static function getNewsRssPHP($response, $request) { - $catId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT); - $location = url($catId > 0 ? 'news-category-feed-rss' : 'news-feed-rss', ['category' => $catId]); - $response->redirect($location, true); - } - - #[Route('GET', '/news.php/atom')] - public static function getNewsAtomPHP($response, $request) { - $catId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT); - $location = url($catId > 0 ? 'news-category-feed-atom' : 'news-feed-atom', ['category' => $catId]); - $response->redirect($location, true); - } - - #[Route('GET', '/news/index.php')] - public static function getNewsIndexPHP($response, $request) { - $response->redirect(url('news-index', [ - 'page' => $request->getParam('page', FILTER_SANITIZE_NUMBER_INT), - ]), true); - } - - #[Route('GET', '/news/category.php')] - public static function getNewsCategoryPHP($response, $request) { - $response->redirect(url('news-category', [ - 'category' => $request->getParam('c', FILTER_SANITIZE_NUMBER_INT), - 'page' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT), - ]), true); - } - - #[Route('GET', '/news/post.php')] - public static function getNewsPostPHP($response, $request) { - $response->redirect(url('news-post', [ - 'post' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT), - ]), true); - } - - #[Route('GET', '/news/feed.php/rss')] - public static function getNewsFeedRssPHP($response, $request) { - $catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT); - $response->redirect(url( - $catId > 0 ? 'news-category-feed-rss' : 'news-feed-rss', - ['category' => $catId] - ), true); - } - - #[Route('GET', '/news/feed.php/atom')] - public static function getNewsFeedAtomPHP($response, $request) { - $catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT); - $response->redirect(url( - $catId > 0 ? 'news-category-feed-atom' : 'news-feed-atom', - ['category' => $catId] - ), true); - } } diff --git a/src/SharpChat/SharpChatRoutes.php b/src/SharpChat/SharpChatRoutes.php index b8f7874..bd5b5f3 100644 --- a/src/SharpChat/SharpChatRoutes.php +++ b/src/SharpChat/SharpChatRoutes.php @@ -13,6 +13,7 @@ use Misuzu\Auth\Sessions; use Misuzu\Config\IConfig; use Misuzu\Emoticons\Emotes; use Misuzu\Perms\Permissions; +use Misuzu\URLs\URLRegistry; use Misuzu\Users\Bans; use Misuzu\Users\UsersContext; @@ -21,6 +22,7 @@ final class SharpChatRoutes implements IRouteHandler { public function __construct( private IConfig $config, + private URLRegistry $urls, private UsersContext $usersCtx, private AuthContext $authCtx, private Emotes $emotes, @@ -62,7 +64,7 @@ final class SharpChatRoutes implements IRouteHandler { #[Route('GET', '/_sockchat/login')] public function getLogin($response, $request) { if(!$this->authInfo->isLoggedIn()) { - $response->redirect(url('auth-login')); + $response->redirect($this->urls->format('auth-login')); return; } diff --git a/src/SiteInfo.php b/src/SiteInfo.php new file mode 100644 index 0000000..cef0273 --- /dev/null +++ b/src/SiteInfo.php @@ -0,0 +1,53 @@ +<?php +namespace Misuzu; + +use Misuzu\Config\IConfig; + +class SiteInfo { + private bool $loaded = false; + private array $props; + + public function __construct( + private IConfig $config + ) {} + + public function load(): void { + if($this->loaded) + return; + $this->loaded = true; + $this->props = $this->config->getValues([ + ['name:s', 'Misuzu'], + 'desc:s', + 'url:s', + 'ext_logo:s', + ]); + } + + public function getName(): string { + $this->load(); + return $this->props['name']; + } + + public function getDescription(): string { + $this->load(); + return $this->props['desc']; + } + + public function hasURL(): bool { + return $this->getURL() !== ''; + } + + public function getURL(): string { + $this->load(); + return rtrim($this->props['url'], '/'); + } + + public function hasExternalLogo(): bool { + return $this->getExternalLogo() !== ''; + } + + public function getExternalLogo(): string { + $this->load(); + return $this->props['ext_logo']; + } +} diff --git a/src/Tools.php b/src/Tools.php index 67c8995..a5bc94c 100644 --- a/src/Tools.php +++ b/src/Tools.php @@ -5,6 +5,46 @@ use Index\Routing\IRouter; use Index\Http\HttpFx; final class Tools { + public static function isLocalURL( + string $url, + ?bool $isSecure = null, + ?string $host = null + ): bool { + $parsed = parse_url($url); + if($parsed === false) + return false; + + // immediately discard if components that will never be used are present + if(isset($parsed['port']) || isset($parsed['user']) || isset($parsed['pass'])) + return false; + + if(isset($parsed['scheme'])) { + $isSecure ??= filter_has_var(INPUT_SERVER, 'HTTPS'); + + // only allow https when secure + if($isSecure && $parsed['scheme'] !== 'https') + return false; + + // allow https and http on insecure + if(!$isSecure && ($parsed['scheme'] !== 'https' && $parsed['scheme'] !== 'http')) + return false; + } + + if(isset($parsed['host'])) { + $host ??= filter_input(INPUT_SERVER, 'HTTP_HOST'); + + // ensure host is identical + if($parsed['host'] !== $host) + return false; + } + + return true; + } + + public static function redirect(string $target): void { + header(sprintf('Location: %s', $target)); + } + public static function isLeapYear(int $year): bool { return (($year % 4) === 0 && ($year % 100) !== 0) || ($year % 400) === 0; diff --git a/src/URLs/URLInfo.php b/src/URLs/URLInfo.php new file mode 100644 index 0000000..b82734d --- /dev/null +++ b/src/URLs/URLInfo.php @@ -0,0 +1,35 @@ +<?php +namespace Misuzu\URLs; + +class URLInfo { + public function __construct( + private string $name, + private string $path, + private array $query, + private string $fragment + ) {} + + public function getName(): string { + return $this->name; + } + + public function getPath(): string { + return $this->path; + } + + public function hasQuery(): bool { + return !empty($this->query); + } + + public function getQuery(): array { + return $this->query; + } + + public function hasFragment(): bool { + return $this->fragment !== ''; + } + + public function getFragment(): string { + return $this->fragment; + } +} diff --git a/src/URLs/URLRegistry.php b/src/URLs/URLRegistry.php new file mode 100644 index 0000000..e031b06 --- /dev/null +++ b/src/URLs/URLRegistry.php @@ -0,0 +1,97 @@ +<?php +namespace Misuzu\URLs; + +use InvalidArgumentException; + +class URLRegistry { + private array $infos = []; + + public function register(string $name, string $path, array $query = [], string $fragment = ''): void { + if(array_key_exists($name, $this->infos)) + throw new InvalidArgumentException('A URL with $name has already been registered.'); + if($path === '') + throw new InvalidArgumentException('$path may not be empty.'); + if($path[0] !== '/') + throw new InvalidArgumentException('$path must begin with /.'); + + $this->infos[$name] = new URLInfo($name, $path, $query, $fragment); + } + + public function format(string $name, array $vars = [], bool $spacesAsPlus = false): string { + if(!array_key_exists($name, $this->infos)) + return ''; + + $urlInfo = $this->infos[$name]; + $urlStr = self::replaceVariables($urlInfo->getPath(), $vars); + + if($urlInfo->hasQuery()) { + $query = []; + $queryParts = $urlInfo->getQuery(); + foreach($queryParts as $name => $value) { + $value = self::replaceVariables($value, $vars); + if($value !== '' && !($name === 'page' && (int)$value <= 1)) + $query[$name] = $value; + } + + if(!empty($query)) + $urlStr .= '?' . http_build_query($query, '', '&', $spacesAsPlus ? PHP_QUERY_RFC1738 : PHP_QUERY_RFC3986); + } + + if($urlInfo->hasFragment()) { + $fragment = self::replaceVariables($urlInfo->getFragment(), $vars); + if($fragment !== '') + $urlStr .= '#' . $fragment; + } + + return $urlStr; + } + + public static function replaceVariables(string $str, array $vars): string { + // i: have no idea what i'm doing + $out = ''; + $varName = ''; + $inVarName = false; + + $length = strlen($str); + for($i = 0; $i < $length; ++$i) { + $char = $str[$i]; + + if($inVarName) { + if($char === '>') { + $inVarName = false; + if(array_key_exists($varName, $vars)) { + $varValue = $vars[$varName]; + if(is_array($varValue)) + $varValue = empty($varValue) ? '' : implode(',', $varValue); + elseif(is_int($varValue)) + $varValue = $varValue < ($varName === 'page' ? 2 : 1) ? '' : (string)$varValue; + else + $varValue = (string)$varValue; + } else + $varValue = ''; + + $out .= $varValue; + $varName = ''; + continue; + } + + if($char === '<') { + $out .= '<' . $varName; + $varName = ''; + continue; + } + + $varName .= $char; + } else + $inVarName = $char === '<'; + + if(!$inVarName) + $out .= $char; + } + + if($varName !== '') + $out .= $varName; + + return $out; + } +} diff --git a/src/Users/Assets/AssetsRoutes.php b/src/Users/Assets/AssetsRoutes.php index f80fdac..296caf9 100644 --- a/src/Users/Assets/AssetsRoutes.php +++ b/src/Users/Assets/AssetsRoutes.php @@ -7,20 +7,23 @@ use Index\Routing\Route; use Index\Routing\RouteHandler; use Misuzu\Perm; use Misuzu\Auth\AuthInfo; +use Misuzu\URLs\URLRegistry; use Misuzu\Users\UsersContext; use Misuzu\Users\UserInfo; class AssetsRoutes extends RouteHandler { public function __construct( private AuthInfo $authInfo, + private URLRegistry $urls, private UsersContext $usersCtx ) {} private function canViewAsset($request, UserInfo $assetUser): bool { if($this->usersCtx->getBans()->countActiveBans($assetUser)) // allow staff viewing profile to still see banned user assets + // should change the Referer check with some query param only applied when needed return $this->authInfo->getPerms('user')->check(Perm::U_USERS_MANAGE) - && parse_url($request->getHeaderFirstLine('Referer'), PHP_URL_PATH) === url('user-profile'); + && parse_url($request->getHeaderFirstLine('Referer'), PHP_URL_PATH) === $this->urls->format('user-profile'); return true; } diff --git a/src/Users/Assets/UserAvatarAsset.php b/src/Users/Assets/UserAvatarAsset.php index 961fa4b..d47cd81 100644 --- a/src/Users/Assets/UserAvatarAsset.php +++ b/src/Users/Assets/UserAvatarAsset.php @@ -28,13 +28,6 @@ class UserAvatarAsset extends UserImageAsset implements UserAssetScalableInterfa return $cfg->getInteger('avatar.max_size', self::MAX_BYTES); } - public function getUrl(): string { - return url('user-avatar', ['user' => $this->getUserId()]); - } - public function getScaledUrl(int $dims): string { - return url('user-avatar', ['user' => $this->getUserId(), 'res' => $dims]); - } - public static function clampDimensions(int $dimensions): int { $closest = null; foreach(self::DIMENSIONS as $dims) diff --git a/src/Users/Assets/UserBackgroundAsset.php b/src/Users/Assets/UserBackgroundAsset.php index 39c8562..fac6a1e 100644 --- a/src/Users/Assets/UserBackgroundAsset.php +++ b/src/Users/Assets/UserBackgroundAsset.php @@ -70,10 +70,6 @@ class UserBackgroundAsset extends UserImageAsset { return $cfg->getInteger('background.max_size', self::MAX_BYTES); } - public function getUrl(): string { - return url('user-background', ['user' => $this->getUserId()]); - } - public function getFileName(): string { return sprintf('background-%1$d.%2$s', $this->getUserId(), $this->getFileExtension()); } diff --git a/src/url.php b/src/url.php deleted file mode 100644 index 08075fb..0000000 --- a/src/url.php +++ /dev/null @@ -1,242 +0,0 @@ -<?php -// URL FORMATTING -// [0] => Path part of URL. -// [1] => Query part of URL. -// [2] => Fragment part of URL. -// -// text surrounded by < and > will be replaced accordingly to an array of variables supplied to the format function -// text surrounded by [ and ] will be replaced by the constant/define of that name -// text surrounded by { and } will be replaced by a CSRF token with the given text as its realm, this will have no effect in a sessionless environment -define('MSZ_URLS', [ - 'index' => ['/'], - 'info' => ['/info/<title>'], - - 'search-index' => ['/search.php'], - 'search-query' => ['/search.php', ['q' => '<query>'], '<section>'], - - 'auth-login' => ['/auth/login.php', ['username' => '<username>', 'redirect' => '<redirect>']], - 'auth-login-welcome' => ['/auth/login.php', ['welcome' => '1', 'username' => '<username>']], - 'auth-register' => ['/auth/register.php'], - 'auth-forgot' => ['/auth/password.php'], - 'auth-reset' => ['/auth/password.php', ['user' => '<user>']], - 'auth-logout' => ['/auth/logout.php', ['csrf' => '{csrf}']], - 'auth-resolve-user' => ['/auth/login.php', ['resolve' => '1', 'name' => '<username>']], - 'auth-two-factor' => ['/auth/twofactor.php', ['token' => '<token>']], - 'auth-revert' => ['/auth/revert.php', ['csrf' => '{csrf}']], - - 'changelog-index' => ['/changelog', ['date' => '<date>', 'user' => '<user>', 'tags' => '<tags>', 'p' => '<page>']], - 'changelog-feed-rss' => ['/changelog.rss'], - 'changelog-feed-atom' => ['/changelog.atom'], - 'changelog-change' => ['/changelog/change/<change>'], - 'changelog-change-comments' => ['/changelog/change/<change>', [], 'comments'], - - 'news-index' => ['/news', ['p' => '<page>']], - 'news-category' => ['/news/<category>', ['p' => '<page>']], - 'news-post' => ['/news/post/<post>'], - 'news-post-comments' => ['/news/post/<post>', [], 'comments'], - 'news-feed-rss' => ['/news.rss'], - 'news-category-feed-rss' => ['/news/<category>.rss'], - 'news-feed-atom' => ['/news.atom'], - 'news-category-feed-atom' => ['/news/<category>.atom'], - - 'forum-index' => ['/forum'], - 'forum-leaderboard' => ['/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']], - 'forum-mark-global' => ['/forum/index.php', ['m' => 'mark']], - 'forum-mark-single' => ['/forum/index.php', ['m' => 'mark', 'f' => '<forum>']], - 'forum-topic-new' => ['/forum/posting.php', ['f' => '<forum>']], - 'forum-reply-new' => ['/forum/posting.php', ['t' => '<topic>']], - 'forum-category' => ['/forum/forum.php', ['f' => '<forum>', 'p' => '<page>']], - 'forum-category-root' => ['/forum/index.php', [], '<forum>'], - 'forum-topic' => ['/forum/topic.php', ['t' => '<topic>', 'page' => '<page>']], - 'forum-topic-create' => ['/forum/posting.php', ['f' => '<forum>']], - 'forum-topic-bump' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'bump', 'csrf' => '{csrf}']], - 'forum-topic-lock' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'lock', 'csrf' => '{csrf}']], - 'forum-topic-unlock' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'unlock', 'csrf' => '{csrf}']], - 'forum-topic-delete' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'delete', 'csrf' => '{csrf}']], - 'forum-topic-restore' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'restore', 'csrf' => '{csrf}']], - 'forum-topic-nuke' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'nuke', 'csrf' => '{csrf}']], - 'forum-post' => ['/forum/topic.php', ['p' => '<post>'], '<post_fragment>'], - 'forum-post-create' => ['/forum/posting.php', ['t' => '<topic>']], - 'forum-post-delete' => ['/forum/post.php', ['p' => '<post>', 'm' => 'delete']], - 'forum-post-restore' => ['/forum/post.php', ['p' => '<post>', 'm' => 'restore']], - 'forum-post-nuke' => ['/forum/post.php', ['p' => '<post>', 'm' => 'nuke']], - 'forum-post-quote' => ['/forum/posting.php', ['q' => '<post>']], - 'forum-post-edit' => ['/forum/posting.php', ['p' => '<post>', 'm' => 'edit']], - - 'user-list' => ['/members.php', ['r' => '<role>', 'ss' => '<sort>', 'sd' => '<direction>', 'p' => '<page>']], - - 'user-profile' => ['/profile.php', ['u' => '<user>']], - 'user-profile-forum-topics' => ['/profile.php', ['u' => '<user>', 'm' => 'forum-topics']], - 'user-profile-forum-posts' => ['/profile.php', ['u' => '<user>', 'm' => 'forum-posts']], - 'user-profile-edit' => ['/profile.php', ['u' => '<user>', 'edit' => '1']], - 'user-account-standing' => ['/profile.php', ['u' => '<user>'], 'account-standing'], - - 'user-avatar' => ['/assets/avatar/<user>', ['res' => '<res>']], - 'user-background' => ['/assets/profile-background/<user>'], - - 'settings-index' => ['/settings'], - 'settings-account' => ['/settings/account.php'], - 'settings-sessions' => ['/settings/sessions.php'], - 'settings-logs' => ['/settings/logs.php'], - 'settings-data' => ['/settings/data.php'], - - 'comment-create' => ['/comments.php', ['m' => 'create', 'return' => '<return>']], - 'comment-vote' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'vote', 'v' => '<vote>', 'return' => '<return>']], - 'comment-delete' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'delete', 'return' => '<return>']], - 'comment-restore' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'restore', 'return' => '<return>']], - 'comment-pin' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'pin', 'return' => '<return>']], - 'comment-unpin' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'unpin', 'return' => '<return>']], - - 'manage-index' => ['/manage'], - - 'manage-general-overview' => ['/manage/general'], - 'manage-general-logs' => ['/manage/general/logs.php'], - - 'manage-general-emoticons' => ['/manage/general/emoticons.php'], - 'manage-general-emoticon' => ['/manage/general/emoticon.php', ['e' => '<emote>']], - 'manage-general-emoticon-order-up' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'order' => 'd', 'csrf' => '{token}']], - 'manage-general-emoticon-order-down'=> ['/manage/general/emoticons.php', ['emote' => '<emote>', 'order' => 'i', 'csrf' => '{token}']], - 'manage-general-emoticon-delete' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'delete' => '1', 'csrf' => '{token}']], - 'manage-general-emoticon-alias' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'alias' => '<string>', 'csrf' => '{token}']], - - 'manage-general-settings' => ['/manage/general/settings.php'], - 'manage-general-setting' => ['/manage/general/setting.php', ['name' => '<name>', 'type' => '<type>']], - 'manage-general-setting-delete' => ['/manage/general/setting-delete.php', ['name' => '<name>']], - - 'manage-forum-categories' => ['/manage/forum/index.php'], - 'manage-forum-category' => ['/manage/forum/category.php', ['f' => '<forum>']], - 'manage-forum-topic-redirs' => ['/manage/forum/redirs.php'], - 'manage-forum-topic-redirs-create' => ['/manage/forum/redirs.php'], - 'manage-forum-topic-redirs-nuke' => ['/manage/forum/redirs.php', ['m' => 'explode', 't' => '<topic>', 'csrf' => '{csrf}']], - - 'manage-changelog-changes' => ['/manage/changelog'], - 'manage-changelog-change' => ['/manage/changelog/change.php', ['c' => '<change>']], - 'manage-changelog-change-delete' => ['/manage/changelog/change.php', ['c' => '<change>', 'delete' => '1', 'csrf' => '{token}']], - 'manage-changelog-tags' => ['/manage/changelog/tags.php'], - 'manage-changelog-tag' => ['/manage/changelog/tag.php', ['t' => '<tag>']], - 'manage-changelog-tag-delete' => ['/manage/changelog/tag.php', ['t' => '<tag>', 'delete' => '1', 'csrf' => '{token}']], - - 'manage-news-categories' => ['/manage/news/categories.php'], - 'manage-news-category' => ['/manage/news/category.php', ['c' => '<category>']], - 'manage-news-category-delete' => ['/manage/news/category.php', ['c' => '<category>', 'delete' => '1', 'csrf' => '{token}']], - 'manage-news-posts' => ['/manage/news/posts.php'], - 'manage-news-post' => ['/manage/news/post.php', ['p' => '<post>']], - 'manage-news-post-delete' => ['/manage/news/post.php', ['p' => '<post>', 'delete' => '1', 'csrf' => '{token}']], - - 'manage-users' => ['/manage/users'], - 'manage-user' => ['/manage/users/user.php', ['u' => '<user>']], - 'manage-users-warnings' => ['/manage/users/warnings.php', ['u' => '<user>']], - 'manage-users-warning' => ['/manage/users/warning.php', ['u' => '<user>']], - 'manage-users-warning-delete' => ['/manage/users/warning.php', ['w' => '<warning>', 'delete' => '1', 'csrf' => '{csrf}']], - 'manage-users-notes' => ['/manage/users/notes.php', ['u' => '<user>']], - 'manage-users-note' => ['/manage/users/note.php', ['n' => '<note>', 'u' => '<user>']], - 'manage-users-note-delete' => ['/manage/users/note.php', ['n' => '<note>', 'delete' => '1', 'csrf' => '{csrf}']], - 'manage-users-bans' => ['/manage/users/bans.php', ['u' => '<user>']], - 'manage-users-ban' => ['/manage/users/ban.php', ['u' => '<user>']], - 'manage-users-ban-delete' => ['/manage/users/ban.php', ['b' => '<ban>', 'delete' => '1', 'csrf' => '{csrf}']], - - 'manage-roles' => ['/manage/users/roles.php'], - 'manage-role' => ['/manage/users/role.php', ['r' => '<role>']], -]); - -function url(string $name, array $variables = []): string { - if(!array_key_exists($name, MSZ_URLS)) - return ''; - - $info = MSZ_URLS[$name]; - $splitUrl = explode('/', $info[0]); - - for($i = 0; $i < count($splitUrl); $i++) - $splitUrl[$i] = url_variable($splitUrl[$i], $variables); - - $url = implode('/', $splitUrl); - - if(!empty($info[1])) { - $url .= '?'; - - foreach($info[1] as $key => $value) { - $value = url_variable($value, $variables); - - if(empty($value) || ($key === 'page' && $value < 2)) - continue; - - $url .= sprintf('%s=%s&', $key, $value); - } - - $url = trim($url, '?&'); - } - - if(!empty($info[2])) - $url .= rtrim(sprintf('#%s', url_variable($info[2], $variables)), '#'); - - return $url; -} - -function redirect(string $url): void { - header('Location: ' . $url); -} - -function url_redirect(string $name, array $variables = []): void { - redirect(url($name, $variables)); -} - -function url_variable(string $value, array $variables): string { - if(str_starts_with($value, '<') && str_ends_with($value, '>')) { - $value = $variables[trim($value, '<>')] ?? ''; - if(is_array($value)) - $value = implode(',', $value); - return (string)$value; - } - - if(str_starts_with($value, '[') && str_ends_with($value, ']')) - return ''; - - if(str_starts_with($value, '{') && str_ends_with($value, '}')) - return \Misuzu\CSRF::token(); - - // Hack that allows variables with file extensions - $pathInfo = pathinfo($value); - if($value !== $pathInfo['filename']) { - $fallback = url_variable($pathInfo['filename'], $variables); - if($fallback !== $pathInfo['filename']) - return $fallback . '.' . $pathInfo['extension']; - } - - return $value; -} - -function url_construct(string $url, array $query = [], ?string $fragment = null): string { - if(count($query)) { - $url .= mb_strpos($url, '?') !== false ? '&' : '?'; - - foreach($query as $key => $value) { - if($value) { - $url .= rawurlencode($key) . '=' . rawurlencode($value) . '&'; - } - } - - $url = mb_substr($url, 0, -1); - } - - if(!empty($fragment)) { - $url .= "#{$fragment}"; - } - - return $url; -} - -function url_prefix(bool $trailingSlash = true): string { - return 'http' . (empty($_SERVER['HTTPS']) ? '' : 's') . '://' . $_SERVER['HTTP_HOST'] . ($trailingSlash ? '/' : ''); -} - -function is_local_url(string $url): bool { - $length = mb_strlen($url); - - if($length < 1) - return false; - - if($url[0] === '/' && ($length > 1 ? $url[1] !== '/' : true)) - return true; - - return str_starts_with($url, url_prefix()); -} diff --git a/templates/_layout/header.twig b/templates/_layout/header.twig index bdbc4d7..f268489 100644 --- a/templates/_layout/header.twig +++ b/templates/_layout/header.twig @@ -26,8 +26,8 @@ <div class="header__background"></div> <div class="header__desktop"> - <a class="header__desktop__logo" href="{{ url('index') }}" title="{{ globals.site_name }}"> - {{ globals.site_name }} + <a class="header__desktop__logo" href="{{ url('index') }}" title="{{ globals.site_info.name }}"> + {{ globals.site_info.name }} </a> <div class="header__desktop__menus"> @@ -76,7 +76,7 @@ </label> <a class="header__mobile__logo header__mobile__icon" href="{{ url('index') }}"> - {{ globals.site_name }} + {{ globals.site_info.name }} </a> <label class="header__mobile__icon header__mobile__avatar" for="toggle-mobile-header"> diff --git a/templates/_layout/meta.twig b/templates/_layout/meta.twig index b4fb0d2..4df6057 100644 --- a/templates/_layout/meta.twig +++ b/templates/_layout/meta.twig @@ -1,16 +1,16 @@ {% apply spaceless %} - {% set description = description|default(globals.site_description) %} + {% set description = description|default(globals.site_info.description) %} {% if title is defined %} - {% set browser_title = title ~ ' :: ' ~ globals.site_name %} + {% set browser_title = title ~ ' :: ' ~ globals.site_info.name %} {% else %} - {% set browser_title = globals.site_name %} + {% set browser_title = globals.site_info.name %} {% endif %} <title>{{ browser_title }} - - + + {% if description|length > 0 %} @@ -21,11 +21,7 @@ {% if image is defined %} {% if image|slice(0, 1) == '/' %} - {% if globals.site_url is not defined or globals.site_url|length < 1 %} - {% set image = '' %} - {% else %} - {% set image = globals.site_url|trim('/') ~ image %} - {% endif %} + {% set image = globals.site_info.hasURL ? (globals.site_info.url ~ image) : '' %} {% endif %} {% if image|length > 0 %} @@ -35,11 +31,7 @@ {% if canonical_url is defined %} {% if canonical_url|slice(0, 1) == '/' %} - {% if globals.site_url is not defined or globals.site_url|length < 1 %} - {% set canonical_url = '' %} - {% else %} - {% set canonical_url = globals.site_url|trim('/') ~ canonical_url %} - {% endif %} + {% set canonical_url = globals.site_info.hasURL ? (globals.site_info.url ~ canonical_url) : '' %} {% endif %} {% if canonical_url|length > 0 %} diff --git a/templates/changelog/index.twig b/templates/changelog/index.twig index 6b1e5d9..6c44932 100644 --- a/templates/changelog/index.twig +++ b/templates/changelog/index.twig @@ -50,7 +50,7 @@ {% if not is_date %}
- {{ pagination(changelog_pagination, url('changelog-index'), null, {'date':changelog_date_fmt, 'user':changelog_user.id|default(0)})}} + {{ pagination(changelog_pagination, 'changelog-index', {'date': changelog_date_fmt, 'user': changelog_user.id|default(0)}) }}
{% endif %} diff --git a/templates/forum/macros.twig b/templates/forum/macros.twig index b662330..78fb450 100644 --- a/templates/forum/macros.twig +++ b/templates/forum/macros.twig @@ -94,7 +94,7 @@ {% set is_locked = is_archived %} {% set can_topic = not is_locked and perms.can_create_topic %} - {% set pag = pagination(pagination_info, url('forum-category'), null, {'f': forum_id}) %} + {% set pag = pagination(pagination_info, 'forum-category', {'forum': forum_id}) %} {% if can_topic or pag|trim|length > 0 %}
@@ -114,7 +114,7 @@ {% macro forum_topic_tools(info, pagination_info, can_reply) %} {% from 'macros.twig' import pagination %} - {% set pag = pagination(pagination_info, url('forum-topic'), null, {'t': info.id}, 'page') %} + {% set pag = pagination(pagination_info, 'forum-topic', {'topic': info.id}) %} {% if can_reply or pag|trim|length > 0 %}
diff --git a/templates/home/landing.twig b/templates/home/landing.twig index 26459d7..c4af728 100644 --- a/templates/home/landing.twig +++ b/templates/home/landing.twig @@ -44,7 +44,7 @@
diff --git a/templates/macros.twig b/templates/macros.twig index a7eb60e..b0cce7d 100644 --- a/templates/macros.twig +++ b/templates/macros.twig @@ -11,11 +11,10 @@ {% endmacro %} -{% macro pagination(info, path, page_range, params, page_param, url_fragment) %} +{% macro pagination(info, name, params, range) %} {% if info.page is defined and info.pages > 1 %} - {% set params = params is iterable ? params : [] %} - {% set page_param = page_param|default('p') %} - {% set page_range = page_range|default(5) %} + {% set params = params is iterable ? params : {} %} + {% set range = range|default(3) %}
- {% set p_start = max(info.page - page_range, 1) %} - {% set p_stop = min(info.page + page_range, info.pages) %} + {% set p_start = max(info.page - range, 1) %} + {% set p_stop = min(info.page + range, info.pages) %} {% for i in p_start..p_stop %} - + {{ i }} {% endfor %} @@ -56,10 +55,10 @@
{% else %} - - + {% endif %} diff --git a/templates/manage/changelog/changes.twig b/templates/manage/changelog/changes.twig index 40099b0..81d20cf 100644 --- a/templates/manage/changelog/changes.twig +++ b/templates/manage/changelog/changes.twig @@ -3,7 +3,7 @@ {% from 'changelog/macros.twig' import changelog_listing %} {% block manage_content %} - {% set changelog_pagination = pagination(changelog_pagination, url('manage-changelog-changes')) %} + {% set changelog_pagination = pagination(changelog_pagination, 'manage-changelog-changes') %}
diff --git a/templates/manage/forum/redirs.twig b/templates/manage/forum/redirs.twig index 5df1dca..4c2c71b 100644 --- a/templates/manage/forum/redirs.twig +++ b/templates/manage/forum/redirs.twig @@ -2,7 +2,7 @@ {% from 'macros.twig' import pagination, container_title %} {% from '_layout/input.twig' import input_csrf, input_text %} -{% set redirs_pagination = pagination(manage_redirs_pagination, url('manage-forum-topic-redirs')) %} +{% set redirs_pagination = pagination(manage_redirs_pagination, 'manage-forum-topic-redirs') %} {% block manage_content %}
diff --git a/templates/manage/general/logs.twig b/templates/manage/general/logs.twig index 14f30b2..2563637 100644 --- a/templates/manage/general/logs.twig +++ b/templates/manage/general/logs.twig @@ -5,7 +5,7 @@ {% block manage_content %}
{{ container_title(' Global Log') }} - {% set glp = pagination(global_logs_pagination, url('manage-general-logs'), null, {'v': 'logs'}) %} + {% set glp = pagination(global_logs_pagination, 'manage-general-logs') %} {% endblock %} diff --git a/templates/manage/users/bans.twig b/templates/manage/users/bans.twig index 0896962..5148d0c 100644 --- a/templates/manage/users/bans.twig +++ b/templates/manage/users/bans.twig @@ -1,7 +1,7 @@ {% extends 'manage/users/master.twig' %} {% from 'macros.twig' import pagination, container_title, avatar %} -{% set bans_pagination = pagination(manage_bans_pagination, url('manage-users-bans', {'user': manage_bans_filter_user.id|default(0)})) %} +{% set bans_pagination = pagination(manage_bans_pagination, 'manage-users-bans', {'user': manage_bans_filter_user.id|default(0)}) %} {% set bans_filtering = manage_bans_filter_user is not null %} {% block manage_content %} diff --git a/templates/manage/users/notes.twig b/templates/manage/users/notes.twig index 75b123b..96da1de 100644 --- a/templates/manage/users/notes.twig +++ b/templates/manage/users/notes.twig @@ -1,7 +1,7 @@ {% extends 'manage/users/master.twig' %} {% from 'macros.twig' import pagination, container_title, avatar %} -{% set notes_pagination = pagination(manage_notes_pagination, url('manage-users-notes', {'user': manage_notes_filter_user.id|default(0)})) %} +{% set notes_pagination = pagination(manage_notes_pagination, 'manage-users-notes', {'user': manage_notes_filter_user.id|default(0)}) %} {% set notes_filtering = manage_notes_filter_user is not null %} {% block manage_content %} diff --git a/templates/manage/users/roles.twig b/templates/manage/users/roles.twig index c6982dc..742dd24 100644 --- a/templates/manage/users/roles.twig +++ b/templates/manage/users/roles.twig @@ -1,7 +1,7 @@ {% extends 'manage/users/master.twig' %} {% from 'macros.twig' import pagination, container_title %} -{% set roles_pagination = pagination(manage_roles_pagination, url('manage-roles')) %} +{% set roles_pagination = pagination(manage_roles_pagination, 'manage-roles') %} {% block manage_content %}
diff --git a/templates/manage/users/users.twig b/templates/manage/users/users.twig index 4e2317d..aef435b 100644 --- a/templates/manage/users/users.twig +++ b/templates/manage/users/users.twig @@ -1,7 +1,7 @@ {% extends 'manage/users/master.twig' %} {% from 'macros.twig' import pagination, container_title, avatar %} -{% set users_pagination = pagination(manage_users_pagination, url('manage-users')) %} +{% set users_pagination = pagination(manage_users_pagination, 'manage-users') %} {% block manage_content %}
diff --git a/templates/manage/users/warnings.twig b/templates/manage/users/warnings.twig index b2ee2d5..6ceefb2 100644 --- a/templates/manage/users/warnings.twig +++ b/templates/manage/users/warnings.twig @@ -1,7 +1,7 @@ {% extends 'manage/users/master.twig' %} {% from 'macros.twig' import pagination, container_title, avatar %} -{% set warns_pagination = pagination(manage_warns_pagination, url('manage-users-warnings', {'user': manage_warns_filter_user.id|default(0)})) %} +{% set warns_pagination = pagination(manage_warns_pagination, 'manage-users-warnings', {'user': manage_warns_filter_user.id|default(0)}) %} {% set warns_filtering = manage_warns_filter_user is not null %} {% block manage_content %} diff --git a/templates/master.twig b/templates/master.twig index f64e8df..96681b5 100644 --- a/templates/master.twig +++ b/templates/master.twig @@ -12,7 +12,7 @@ :root { --background-width: {{ site_background.width }}px; --background-height: {{ site_background.height }}px; - --background-image: url('{{ site_background.url|raw }}'); + --background-image: url('{{ site_background_url|raw }}'); } {% endif %} diff --git a/templates/news/category.twig b/templates/news/category.twig index 19581f9..5654909 100644 --- a/templates/news/category.twig +++ b/templates/news/category.twig @@ -30,7 +30,7 @@ {% endfor %}
- {{ pagination(news_pagination, url('news-category', {'category': news_category.id})) }} + {{ pagination(news_pagination, 'news-category', {'category': news_category.id}) }}
diff --git a/templates/news/index.twig b/templates/news/index.twig index dcf4840..7904d68 100644 --- a/templates/news/index.twig +++ b/templates/news/index.twig @@ -29,7 +29,7 @@ {% endfor %}
- {{ pagination(news_pagination, url('news-index')) }} + {{ pagination(news_pagination, 'news-index') }}
diff --git a/templates/profile/master.twig b/templates/profile/master.twig index 7a896bb..2523bf5 100644 --- a/templates/profile/master.twig +++ b/templates/profile/master.twig @@ -5,6 +5,7 @@ {% set manage_link = url('manage-user', {'user': profile_user.id}) %} {% if (not profile_is_banned or profile_can_edit) %} {% set site_background = profile_background_info %} + {% set site_background_url = url('user-background', {'user': profile_user.id}) %} {% endif %} {% set stats = [ { diff --git a/templates/settings/logs.twig b/templates/settings/logs.twig index 87fe395..86cd44d 100644 --- a/templates/settings/logs.twig +++ b/templates/settings/logs.twig @@ -7,9 +7,9 @@ {% block settings_content %}
{{ container_title(' Login History') }} - {% set lhpagination = pagination(login_history_pagination, url('settings-logs'), null, { - 'ap': account_log_pagination.page > 1 ? account_log_pagination.page : 0, - }, 'hp', 'login-history') %} + {% set lhpagination = pagination(login_history_pagination, 'settings-logs-logins', { + 'account-page': account_log_pagination.page > 1 ? account_log_pagination.page : 0, + }) %}

These are all the login attempts to your account. If any attempt that you don't recognise is marked as successful your account may be compromised, ask a staff member for advice in this case.

@@ -38,9 +38,9 @@
{{ container_title(' Account Log') }} - {% set alpagination = pagination(account_log_pagination, url('settings-logs'), null, { - 'hp': login_history_pagination.page > 1 ? login_history_pagination.page : 0, - }, 'ap', 'account-log') %} + {% set alpagination = pagination(account_log_pagination, 'settings-logs-account', { + 'logins-page': login_history_pagination.page > 1 ? login_history_pagination.page : 0, + }) %}

This is a log of all "important" actions that have been done using your account for your review. If you notice anything strange, please alert the staff.

diff --git a/templates/settings/sessions.twig b/templates/settings/sessions.twig index ee5a23b..47a368d 100644 --- a/templates/settings/sessions.twig +++ b/templates/settings/sessions.twig @@ -9,7 +9,7 @@
{{ container_title(' Sessions') }} - {% set spagination = pagination(session_pagination, url('settings-sessions')) %} + {% set spagination = pagination(session_pagination, 'settings-sessions') %}

These are the active logins to your account, clicking the Kill button will force a logout on that session. Your current login is highlighted with a different colour so you don't accidentally force yourself to logout.

diff --git a/templates/user/listing.twig b/templates/user/listing.twig index c975b17..2ed857e 100644 --- a/templates/user/listing.twig +++ b/templates/user/listing.twig @@ -40,7 +40,7 @@
- {{ pagination(users_pagination, url('user-list'), null, {'r': url_role, 'ss': url_sort, 'sd': url_direction}) }} + {{ pagination(users_pagination, 'user-list', null, {'r': url_role, 'ss': url_sort, 'sd': url_direction}) }}
{% endmacro %}