Rewrote URL registry.

This commit is contained in:
Pachira 2023-09-08 20:40:48 +00:00
parent 163da8b213
commit c3bed1c0e3
75 changed files with 750 additions and 599 deletions

View file

@ -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)) {

View file

@ -1,4 +1,4 @@
<?php
namespace Misuzu;
url_redirect('auth-login');
Tools::redirect($msz->getURLs()->format('auth-login'));

View file

@ -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', [

View file

@ -24,4 +24,4 @@ if($authInfo->isLoggedIn()) {
AuthTokenCookie::apply($tokenPacker->pack($tokenInfo));
}
url_redirect('index');
Tools::redirect($msz->getURLs()->format('index'));;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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'));

View file

@ -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,
]);

View file

@ -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) {
'<username>',
]);
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:

View file

@ -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;
}

View file

@ -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,
]

View file

@ -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));

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -37,7 +37,7 @@ if(CSRF::validateRequest() && !empty($_GET['emote'])) {
}
}
url_redirect('manage-general-emoticons');
Tools::redirect($msz->getURLs()->format('manage-general-emoticons'));
return;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -1,4 +1,4 @@
<?php
namespace Misuzu;
url_redirect('manage-general-overview');
Tools::redirect($msz->getURLs()->format('manage-general-overview'));

View file

@ -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;

View file

@ -1,4 +1,4 @@
<?php
namespace Misuzu;
url_redirect('manage-news-categories');
Tools::redirect($msz->getURLs()->format('manage-news-categories'));

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 '':

View file

@ -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,

View file

@ -5,4 +5,4 @@ $authInfo = $msz->getAuthInfo();
if(!$authInfo->isLoggedIn())
Template::throwError(401);
url_redirect('settings-account');
Tools::redirect($msz->getURLs()->format('settings-account'));

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

136
src/LegacyRoutes.php Normal file
View file

@ -0,0 +1,136 @@
<?php
namespace Misuzu;
use Index\Routing\Route;
use Index\Routing\RouteHandler;
use Misuzu\URLs\URLRegistry;
class LegacyRoutes extends RouteHandler {
public function __construct(
private URLRegistry $urls
) {}
#[Route('GET', '/index.php')]
public function getIndexPHP($response): void {
$response->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);
}
}

View file

@ -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/<title>');
$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>']);
}
}

View file

@ -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 {
//
}
}

View file

@ -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);
}
}

View file

@ -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;
}

53
src/SiteInfo.php Normal file
View file

@ -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'];
}
}

View file

@ -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;

35
src/URLs/URLInfo.php Normal file
View file

@ -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;
}
}

97
src/URLs/URLRegistry.php Normal file
View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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)

View file

@ -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());
}

View file

@ -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());
}

View file

@ -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">

View file

@ -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 }}</title>
<meta property="og:title" content="{{ title|default(globals.site_name) }}">
<meta property="og:site_name" content="{{ globals.site_name }}">
<meta property="og:title" content="{{ title|default(globals.site_info.name) }}">
<meta property="og:site_name" content="{{ globals.site_info.name }}">
{% if description|length > 0 %}
<meta name="description" content="{{ description }}">
@ -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 %}

View file

@ -50,7 +50,7 @@
{% if not is_date %}
<div class="changelog__pagination">
{{ 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)}) }}
</div>
{% endif %}
</div>

View file

@ -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 %}
<div class="container forum__actions">
@ -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 %}
<div class="container forum__actions">

View file

@ -44,7 +44,7 @@
<div class="landingv2-header-content">
<div class="landingv2-welcome">
<a href="{{ url('index') }}">
<img src="/images/landing-logo.png" alt="{{ globals.site_name }}">
<img src="/images/landing-logo.png" alt="{{ globals.site_info.name }}">
</a>
</div>

View file

@ -11,11 +11,10 @@
</ul>
{% 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) %}
<div class="pagination">
<div class="pagination__section pagination__section--first">
@ -27,21 +26,21 @@
<i class="fas fa-angle-left"></i>
</div>
{% else %}
<a href="{{ url_construct(path, params, url_fragment) }}" class="pagination__link pagination__link--first" rel="first">
<a href="{{ url(name, params) }}" class="pagination__link pagination__link--first" rel="first">
<i class="fas fa-angle-double-left"></i>
</a>
<a href="{{ url_construct(path, info.page <= 2 ? params : params|merge({(page_param): info.page - 1}), url_fragment) }}" class="pagination__link pagination__link--prev" rel="prev">
<a href="{{ url(name, params|merge({'page': info.page - 1})) }}" class="pagination__link pagination__link--prev" rel="prev">
<i class="fas fa-angle-left"></i>
</a>
{% endif %}
</div>
<div class="pagination__section pagination__section--pages">
{% 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 %}
<a href="{{ url_construct(path, i <= 1 ? params : params|merge({(page_param): i}), url_fragment) }}" class="pagination__link{{ info.page == i ? ' pagination__link--current' : '' }}">
<a href="{{ url(name, params|merge({'page': i})) }}" class="pagination__link{{ info.page == i ? ' pagination__link--current' : '' }}">
{{ i }}
</a>
{% endfor %}
@ -56,10 +55,10 @@
<i class="fas fa-angle-double-right"></i>
</div>
{% else %}
<a href="{{ url_construct(path, params|merge({(page_param): info.page + 1}), url_fragment) }}" class="pagination__link pagination__link--next" rel="next">
<a href="{{ url(name, params|merge({'page': info.page + 1})) }}" class="pagination__link pagination__link--next" rel="next">
<i class="fas fa-angle-right"></i>
</a>
<a href="{{ url_construct(path, params|merge({(page_param): info.pages}), url_fragment) }}" class="pagination__link pagination__link--last" rel="last">
<a href="{{ url(name, params|merge({'page': info.pages})) }}" class="pagination__link pagination__link--last" rel="last">
<i class="fas fa-angle-double-right"></i>
</a>
{% endif %}

View file

@ -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') %}
<div class="container">

View file

@ -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 %}
<div class="container">

View file

@ -5,7 +5,7 @@
{% block manage_content %}
<div class="container settings__container">
{{ container_title('<i class="fas fa-file-alt fa-fw"></i> Global Log') }}
{% set glp = pagination(global_logs_pagination, url('manage-general-logs'), null, {'v': 'logs'}) %}
{% set glp = pagination(global_logs_pagination, 'manage-general-logs') %}
<div class="settings__account-logs">
<div class="settings__account-logs__pagination">

View file

@ -16,6 +16,6 @@
</p>
{% endfor %}
{{ pagination(categories_pagination, url('manage-news-categories')) }}
{{ pagination(categories_pagination, 'manage-news-categories') }}
</div>
{% endblock %}

View file

@ -22,6 +22,6 @@
</p>
{% endfor %}
{{ pagination(posts_pagination, url('manage-news-posts')) }}
{{ pagination(posts_pagination, 'manage-news-posts') }}
</div>
{% endblock %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}
<div class="container manage__roles">

View file

@ -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 %}
<div class="container manage__users">

View file

@ -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 %}

View file

@ -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 }}');
}
</style>
{% endif %}

View file

@ -30,7 +30,7 @@
{% endfor %}
<div class="container" style="padding: 4px; display: {{ news_pagination.pages > 1 ? 'block' : 'none' }}">
{{ pagination(news_pagination, url('news-category', {'category': news_category.id})) }}
{{ pagination(news_pagination, 'news-category', {'category': news_category.id}) }}
</div>
</div>

View file

@ -29,7 +29,7 @@
{% endfor %}
<div class="container" style="padding: 4px; display: {{ news_pagination.pages > 1 ? 'block' : 'none' }}">
{{ pagination(news_pagination, url('news-index')) }}
{{ pagination(news_pagination, 'news-index') }}
</div>
</div>

View file

@ -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 = [
{

View file

@ -7,9 +7,9 @@
{% block settings_content %}
<div class="container settings__container" id="login-history">
{{ container_title('<i class="fas fa-user-lock fa-fw"></i> 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,
}) %}
<div class="settings__description">
<p>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.</p>
@ -38,9 +38,9 @@
<div class="container settings__container" id="account-log">
{{ container_title('<i class="fas fa-file-alt fa-fw"></i> 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,
}) %}
<div class="settings__description">
<p>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.</p>

View file

@ -9,7 +9,7 @@
<div class="container settings__container">
{{ container_title('<i class="fas fa-key fa-fw"></i> Sessions') }}
{% set spagination = pagination(session_pagination, url('settings-sessions')) %}
{% set spagination = pagination(session_pagination, 'settings-sessions') %}
<div class="settings__description">
<p>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.</p>

View file

@ -40,7 +40,7 @@
</form>
<div class="userlist__pagination">
{{ 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}) }}
</div>
</div>
{% endmacro %}