From 1da647092886cbd8b93645b55a5155ed657346d2 Mon Sep 17 00:00:00 2001 From: flashwave Date: Thu, 31 Aug 2023 21:33:34 +0000 Subject: [PATCH] Switch to Sasae. --- composer.json | 2 +- composer.lock | 202 ++++++++++++++++++++++------- public/index.php | 127 ++---------------- src/Config/DbConfig.php | 9 +- src/MisuzuContext.php | 166 +++++++----------------- src/MisuzuSasaeExtension.php | 238 ++++++++++++++++++++++++++++++++++ src/Template.php | 41 ++---- src/TwigMisuzu.php | 70 ---------- templates/_layout/footer.twig | 2 +- templates/_layout/header.twig | 29 +++-- templates/forum/posting.twig | 4 +- templates/home/landing.twig | 4 +- templates/manage/master.twig | 3 +- templates/master.twig | 12 +- templates/profile/index.twig | 4 +- 15 files changed, 503 insertions(+), 410 deletions(-) create mode 100644 src/MisuzuSasaeExtension.php delete mode 100644 src/TwigMisuzu.php diff --git a/composer.json b/composer.json index 8c22d20..8e4610d 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "prefer-stable": true, "require": { "flashwave/index": "dev-master", - "twig/twig": "^3.0", + "flashwave/sasae": "dev-master", "erusev/parsedown": "~1.6", "chillerlan/php-qrcode": "^4.3", "symfony/mailer": "^6.0", diff --git a/composer.lock b/composer.lock index eaea418..53c50c3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c76eb19b0eb02b6a9fccb4b63f50d165", + "content-hash": "313710f128265bd0f0f9e7687058324b", "packages": [ { "name": "chillerlan/php-qrcode", @@ -348,7 +348,7 @@ "source": { "type": "git", "url": "https://git.flash.moe/flash/index.git", - "reference": "6a38f803f4b3e49296f7472743e7c683c496ec19" + "reference": "1172115e699acf44580ffcdcf86c9e3987d2f969" }, "require": { "ext-mbstring": "*", @@ -386,7 +386,48 @@ ], "description": "Composer package for the common library for my projects.", "homepage": "https://railgun.sh/index", - "time": "2023-08-22T00:04:20+00:00" + "time": "2023-08-28T13:58:51+00:00" + }, + { + "name": "flashwave/sasae", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://git.flash.moe/flash/sasae.git", + "reference": "739669fc8ce7ea862ed2129cb24976ceebd0f4c7" + }, + "require": { + "flashwave/index": "dev-master", + "php": ">=8.2", + "twig/html-extra": "^3.7", + "twig/twig": "^3.7" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.2" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "Sasae\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "bsd-3-clause-clear" + ], + "authors": [ + { + "name": "flashwave", + "email": "packagist@flash.moe", + "homepage": "https://flash.moe", + "role": "mom" + } + ], + "description": "A wrapper for Twig with added common functionality.", + "homepage": "https://railgun.sh/sasae", + "time": "2023-08-24T23:24:45+00:00" }, { "name": "matomo/device-detector", @@ -1049,16 +1090,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -1073,7 +1114,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1111,7 +1152,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -1127,20 +1168,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d", + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d", "shasum": "" }, "require": { @@ -1154,7 +1195,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1198,7 +1239,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0" }, "funding": [ { @@ -1214,20 +1255,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:30:37+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", "shasum": "" }, "require": { @@ -1239,7 +1280,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1282,7 +1323,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" }, "funding": [ { @@ -1298,20 +1339,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -1326,7 +1367,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1365,7 +1406,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -1381,20 +1422,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179", "shasum": "" }, "require": { @@ -1403,7 +1444,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1441,7 +1482,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0" }, "funding": [ { @@ -1457,7 +1498,7 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/service-contracts", @@ -1542,17 +1583,81 @@ "time": "2023-05-23T14:45:45+00:00" }, { - "name": "twig/twig", - "version": "v3.7.0", + "name": "twig/html-extra", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "5cf942bbab3df42afa918caeba947f1b690af64b" + "url": "https://github.com/twigphp/html-extra.git", + "reference": "95ceb36e70fa8d07af08cf5135ecbf5e0bd8f386" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/5cf942bbab3df42afa918caeba947f1b690af64b", - "reference": "5cf942bbab3df42afa918caeba947f1b690af64b", + "url": "https://api.github.com/repos/twigphp/html-extra/zipball/95ceb36e70fa8d07af08cf5135ecbf5e0bd8f386", + "reference": "95ceb36e70fa8d07af08cf5135ecbf5e0bd8f386", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/mime": "^5.4|^6.0", + "twig/twig": "^2.7|^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5.4|^6.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\Html\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for HTML", + "homepage": "https://twig.symfony.com", + "keywords": [ + "html", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/html-extra/tree/v3.7.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-07-29T15:34:56+00:00" + }, + { + "name": "twig/twig", + "version": "v3.7.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", + "reference": "a0ce373a0ca3bf6c64b9e3e2124aca502ba39554", "shasum": "" }, "require": { @@ -1562,7 +1667,7 @@ }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^5.4.9|^6.3" }, "type": "library", "autoload": { @@ -1598,7 +1703,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.7.0" + "source": "https://github.com/twigphp/Twig/tree/v3.7.1" }, "funding": [ { @@ -1610,7 +1715,7 @@ "type": "tidelift" } ], - "time": "2023-07-26T07:16:09+00:00" + "time": "2023-08-28T11:09:02+00:00" } ], "packages-dev": [ @@ -1680,7 +1785,8 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { - "flashwave/index": 20 + "flashwave/index": 20, + "flashwave/sasae": 20 }, "prefer-stable": true, "prefer-lowest": false, diff --git a/public/index.php b/public/index.php index c2a32dd..9c2e9d4 100644 --- a/public/index.php +++ b/public/index.php @@ -5,6 +5,7 @@ use RuntimeException; use Misuzu\Auth\AuthTokenBuilder; use Misuzu\Auth\AuthTokenCookie; use Misuzu\Auth\AuthTokenInfo; +use Sasae\SasaeEnvironment; require_once __DIR__ . '/../misuzu.php'; @@ -38,44 +39,6 @@ if(file_exists(MSZ_ROOT . '/.migrating')) { exit; } -if(!MSZ_DEBUG) { - $twigCacheDirSfx = GitInfo::hash(true); - if(empty($twigCacheDirSfx)) - $twigCacheDirSfx = md5(MSZ_ROOT); - - $twigCache = sys_get_temp_dir() . '/msz-tpl-' . $twigCacheDirSfx; - if(!is_dir($twigCache)) - mkdir($twigCache, 0775, true); -} - -$globals = $cfg->getValues([ - ['site.name:s', 'Misuzu'], - 'site.desc:s', - 'site.url:s', - 'eeprom.path:s', - 'eeprom.app:s', - ['csrf.secret:s', 'soup'], -]); - -Template::init($msz, $twigCache ?? null, MSZ_DEBUG); - -Template::set('globals', [ - 'site_name' => $globals['site.name'], - 'site_description' => $globals['site.desc'], - 'site_url' => $globals['site.url'], - 'eeprom' => [ - 'path' => $globals['eeprom.path'], - 'app' => $globals['eeprom.app'], - ], -]); - -$mszAssetsInfo = json_decode(file_get_contents(MSZ_ASSETS . '/current.json')); -if(!empty($mszAssetsInfo)) - Template::set('assets', $mszAssetsInfo); -unset($mszAssetsInfo); - -Template::addPath(MSZ_TEMPLATES); - $tokenPacker = $msz->createAuthTokenPacker(); if(filter_has_var(INPUT_COOKIE, 'msz_auth')) @@ -156,95 +119,21 @@ if($tokenInfo->hasUserId() && $tokenInfo->hasSessionToken()) { $msz->getAuthInfo()->setInfo($tokenInfo, $userInfo, $sessionInfo, $userInfoReal); -if(!empty($userInfo)) - $userInfo = $users->getUser((string)$userInfo->getId(), 'id'); -if(!empty($userInfoReal)) - $userInfoReal = $users->getUser((string)$userInfoReal->getId(), 'id'); - CSRF::init( - $globals['csrf.secret'], + $cfg->getString('csrf.secret', 'soup'), ($msz->isLoggedIn() ? $sessionInfo->getToken() : $_SERVER['REMOTE_ADDR']) ); -if(!empty($userInfo)) { - Template::set('current_user', $userInfo); - Template::set('current_user_ban_info', $msz->tryGetActiveBan()); -} - -if(!empty($userInfoReal)) { - Template::set('current_user_real', $userInfoReal); - Template::set('current_user_real_colour', $users->getUserColour($userInfoReal)); -} - -$inManageMode = str_starts_with($_SERVER['REQUEST_URI'], '/manage'); - -Template::set('header_menu', $msz->getHeaderMenu($userInfo ?? null)); -Template::set('user_menu', $msz->getUserMenu($userInfo ?? null, $inManageMode)); -Template::set('display_timings_info', MSZ_DEBUG || $msz->getAuthInfo()->getPerms('global')->check(Perm::G_TIMINGS_VIEW)); - -if($inManageMode) { - $hasManageAccess = false; - - if($msz->isLoggedIn() && !$msz->hasActiveBan()) { - $manageUser = $msz->getActiveUser(); - $manageUserId = $manageUser->getId(); - $manageGlobalPerms = $msz->getAuthInfo()->getPerms('global'); - - if($manageGlobalPerms->check(Perm::G_IS_JANITOR)) { - $hasManageAccess = true; - $manageMenu = [ - 'General' => [ - 'Overview' => url('manage-general-overview'), - ], - ]; - - if($manageGlobalPerms->check(Perm::G_LOGS_VIEW)) - $manageMenu['General']['Logs'] = url('manage-general-logs'); - if($manageGlobalPerms->check(Perm::G_EMOTES_MANAGE)) - $manageMenu['General']['Emoticons'] = url('manage-general-emoticons'); - if($manageGlobalPerms->check(Perm::G_CONFIG_MANAGE)) - $manageMenu['General']['Settings'] = url('manage-general-settings'); - - $manageUserPerms = $msz->getAuthInfo()->getPerms('user'); - if($manageUserPerms->check(Perm::U_USERS_MANAGE)) - $manageMenu['Users & Roles']['Users'] = url('manage-users'); - if($manageUserPerms->check(Perm::U_ROLES_MANAGE)) - $manageMenu['Users & Roles']['Roles'] = url('manage-roles'); - if($manageUserPerms->check(Perm::U_NOTES_MANAGE)) - $manageMenu['Users & Roles']['Notes'] = url('manage-users-notes'); - if($manageUserPerms->check(Perm::U_WARNINGS_MANAGE)) - $manageMenu['Users & Roles']['Warnings'] = url('manage-users-warnings'); - if($manageUserPerms->check(Perm::U_BANS_MANAGE)) - $manageMenu['Users & Roles']['Bans'] = url('manage-users-bans'); - - if($manageGlobalPerms->check(Perm::G_NEWS_POSTS_MANAGE)) - $manageMenu['News']['Posts'] = url('manage-news-posts'); - if($manageGlobalPerms->check(Perm::G_NEWS_CATEGORIES_MANAGE)) - $manageMenu['News']['Categories'] = url('manage-news-categories'); - - if($manageGlobalPerms->check(Perm::G_FORUM_CATEGORIES_MANAGE)) - $manageMenu['Forum']['Permission Calculator'] = url('manage-forum-categories'); - if($manageGlobalPerms->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE)) - $manageMenu['Forum']['Topic Redirects'] = url('manage-forum-topic-redirs'); - - if($manageGlobalPerms->check(Perm::G_CL_CHANGES_MANAGE)) - $manageMenu['Changelog']['Changes'] = url('manage-changelog-changes'); - if($manageGlobalPerms->check(Perm::G_CL_TAGS_MANAGE)) - $manageMenu['Changelog']['Tags'] = url('manage-changelog-tags'); - - Template::set('manage_menu', $manageMenu); - } - } - - if(!$hasManageAccess) - Template::throwError(403); -} +$msz->startTemplating(); $mszRequestPath = $request->getPath(); $mszLegacyPathPrefix = MSZ_PUBLIC . '-legacy/'; $mszLegacyPath = realpath($mszLegacyPathPrefix . $mszRequestPath); if(!empty($mszLegacyPath) && str_starts_with($mszLegacyPath, $mszLegacyPathPrefix)) { + if(str_starts_with($mszRequestPath, '/manage') && !$msz->hasManageAccess()) + Template::throwError(403); + if(is_dir($mszLegacyPath)) $mszLegacyPath .= '/index.php'; @@ -254,5 +143,5 @@ if(!empty($mszLegacyPath) && str_starts_with($mszLegacyPath, $mszLegacyPathPrefi } } -$msz->setUpHttp(); -$msz->dispatchHttp($request); +$msz->startRouter(); +$msz->dispatchRouter($request); diff --git a/src/Config/DbConfig.php b/src/Config/DbConfig.php index c2e2459..bf293e4 100644 --- a/src/Config/DbConfig.php +++ b/src/Config/DbConfig.php @@ -172,9 +172,11 @@ class DbConfig implements IConfig { if(is_string($spec)) { $name = $spec; $default = null; + $alias = null; } elseif(is_array($spec) && !empty($spec)) { $name = $spec[0]; $default = $spec[1] ?? null; + $alias = $spec[2] ?? null; } else throw new InvalidArgumentException('$specs array contains an invalid entry.'); @@ -188,6 +190,7 @@ class DbConfig implements IConfig { 'name' => $name, 'type' => $type, 'default' => $default, + 'alias' => $alias, ]; } @@ -201,6 +204,8 @@ class DbConfig implements IConfig { break; } + $resultName = $spec['alias'] ?? $spec['name']; + if(!isset($info)) { $defaultValue = $spec['default'] ?? null; if($spec['type'] !== '') @@ -214,11 +219,11 @@ class DbConfig implements IConfig { default => throw new RuntimeException('Invalid type letter encountered.'), }); - $results[$spec['name']] = $defaultValue; + $results[$resultName] = $defaultValue; continue; } - $results[$spec['name']] = match($spec['type']) { + $results[$resultName] = match($spec['type']) { 's' => $info->getString(), 'a' => $info->getArray(), 'i' => $info->getInteger(), diff --git a/src/MisuzuContext.php b/src/MisuzuContext.php index 0b0a983..ad822f0 100644 --- a/src/MisuzuContext.php +++ b/src/MisuzuContext.php @@ -8,6 +8,7 @@ use Index\Data\Migration\FsDbMigrationRepo; use Index\Http\HttpFx; use Index\Http\HttpRequest; use Index\Routing\Router; +use Sasae\SasaeEnvironment; use Misuzu\Template; use Misuzu\Auth\AuthInfo; use Misuzu\Auth\AuthTokenPacker; @@ -69,6 +70,7 @@ class MisuzuContext { private Forum $forum; private Permissions $perms; private AuthInfo $authInfo; + private SasaeEnvironment $templating; public function __construct(IDbConnection $dbConn, IConfig $config) { $this->dbConn = $dbConn; @@ -218,10 +220,10 @@ class MisuzuContext { } $userId = (string)$userInfo->getId(); - if(array_key_exists($userId, $this->activeBansCache)) - return $this->activeBansCache[$userId]; + if(!array_key_exists($userId, $this->activeBansCache)) + $this->activeBansCache[$userId] = $this->bans->tryGetActiveBan($userId); - return $this->activeBansCache[$userId] = $this->bans->tryGetActiveBan($userId); + return $this->activeBansCache[$userId]; } public function hasActiveBan(UserInfo|string|null $userInfo = null): bool { @@ -241,125 +243,51 @@ class MisuzuContext { ); } - public function getHeaderMenu(?UserInfo $userInfo): array { - $hasUserInfo = $userInfo?->isDeleted() === false; - $menu = []; - - $home = [ - 'title' => 'Home', - 'url' => url('index'), - 'menu' => [], - ]; - - if($hasUserInfo) - $home['menu'][] = [ - 'title' => 'Members', - 'url' => url('user-list'), - ]; - - $home['menu'][] = [ - 'title' => 'Changelog', - 'url' => url('changelog-index'), - ]; - $home['menu'][] = [ - 'title' => 'Contact', - 'url' => url('info', ['title' => 'contact']), - ]; - $home['menu'][] = [ - 'title' => 'Rules', - 'url' => url('info', ['title' => 'rules']), - ]; - - $menu[] = $home; - - $menu[] = [ - 'title' => 'News', - 'url' => url('news-index'), - ]; - - $forum = [ - 'title' => 'Forum', - 'url' => url('forum-index'), - 'menu' => [], - ]; - - if($this->authInfo->getPerms('global')->check(Perm::G_FORUM_LEADERBOARD_VIEW)) - $forum['menu'][] = [ - 'title' => 'Leaderboard', - 'url' => url('forum-leaderboard'), - ]; - - $menu[] = $forum; - - $chatPath = $this->config->getString('sockChat.chatPath.normal'); - if(!empty($chatPath)) - $menu[] = [ - 'title' => 'Chat', - 'url' => $chatPath, - ]; - - return $menu; + private ?bool $hasManageAccess = null; + public function hasManageAccess(): bool { + $this->hasManageAccess ??= $this->authInfo->isLoggedIn() && !$this->hasActiveBan() + && $this->getAuthInfo()->getPerms('global')->check(Perm::G_IS_JANITOR); + return $this->hasManageAccess; } - public function getUserMenu(?UserInfo $userInfo, bool $inBroomCloset): array { - $menu = []; - - if($userInfo === null) { - $menu[] = [ - 'title' => 'Register', - 'url' => url('auth-register'), - 'icon' => 'fas fa-user-plus fa-fw', - ]; - $menu[] = [ - 'title' => 'Log in', - 'url' => url('auth-login'), - 'icon' => 'fas fa-sign-in-alt fa-fw', - ]; - } else { - $menu[] = [ - 'title' => 'Profile', - 'url' => url('user-profile', ['user' => $userInfo->getId()]), - 'icon' => 'fas fa-user fa-fw', - ]; - $menu[] = [ - 'title' => 'Settings', - 'url' => url('settings-index'), - 'icon' => 'fas fa-cog fa-fw', - ]; - $menu[] = [ - 'title' => 'Search', - 'url' => url('search-index'), - 'icon' => 'fas fa-search fa-fw', - ]; - - if(!$this->hasActiveBan($userInfo) && $this->authInfo->getPerms('global')->check(Perm::G_IS_JANITOR)) { - // restore behaviour where clicking this button switches between - // site version and broom version - if($inBroomCloset) - $menu[] = [ - 'title' => 'Exit Broom Closet', - 'url' => url('index'), - 'icon' => 'fas fa-door-open fa-fw', - ]; - else - $menu[] = [ - 'title' => 'Enter Broom Closet', - 'url' => url('manage-index'), - 'icon' => 'fas fa-door-closed fa-fw', - ]; - } - - $menu[] = [ - 'title' => 'Log out', - 'url' => url('auth-logout'), - 'icon' => 'fas fa-sign-out-alt fa-fw', - ]; - } - - return $menu; + public function getWebAssetInfo(): ?object { + return json_decode(file_get_contents(MSZ_ASSETS . '/current.json')); } - public function setUpHttp(): void { + private ?string $chatUrl = null; + public function getChatURL(): string { + $this->chatUrl ??= $this->config->getString('sockChat.chatPath.normal'); + return $this->chatUrl; + } + + 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'], + ]); + + $authInfo = $this->getAuthInfo(); + $globals['assets'] = $this->getWebAssetInfo(); + $globals['auth_info'] = $authInfo; + $globals['active_ban_info'] = $this->tryGetActiveBan(); + $globals['display_timings_info'] = MSZ_DEBUG + || $authInfo->getPerms('global')->check(Perm::G_TIMINGS_VIEW); + + $templating = new SasaeEnvironment( + MSZ_TEMPLATES, + cache: MSZ_DEBUG ? null : ['Misuzu', GitInfo::hash(true)], + debug: MSZ_DEBUG + ); + $templating->addExtension(new MisuzuSasaeExtension($this)); + $templating->addGlobal('globals', $globals); + + Template::init($templating); + } + + public function startRouter(): void { $this->router = new HttpFx; $this->router->use('/', function($response) { $response->setPoweredBy('Misuzu'); @@ -369,7 +297,7 @@ class MisuzuContext { $this->registerHttpRoutes(); } - public function dispatchHttp(?HttpRequest $request = null): void { + public function dispatchRouter(?HttpRequest $request = null): void { $this->router->dispatch($request); } diff --git a/src/MisuzuSasaeExtension.php b/src/MisuzuSasaeExtension.php new file mode 100644 index 0000000..d8ec486 --- /dev/null +++ b/src/MisuzuSasaeExtension.php @@ -0,0 +1,238 @@ +ctx = $ctx; + } + + public function getFilters() { + return [ + new TwigFilter('country_name', Tools::countryName(...)), + new TwigFilter('parse_text', fn(string $text, int $parser): string => Parser::instance($parser)->parseText($text)), + new TwigFilter('time_format', $this->timeFormat(...)), + ]; + } + + public function getFunctions() { + return [ + new TwigFunction('url_construct', 'url_construct'), + new TwigFunction('url', 'url'), + new TwigFunction('csrf_token', CSRF::token(...)), + new TwigFunction('git_commit_hash', GitInfo::hash(...)), + new TwigFunction('git_tag', GitInfo::tag(...)), + new TwigFunction('git_branch', GitInfo::branch(...)), + new TwigFunction('startup_time', fn(float $time = MSZ_STARTUP) => microtime(true) - $time), + new TwigFunction('sql_query_count', $this->ctx->getDbQueryCount(...)), + new TwigFunction('msz_header_menu', $this->getHeaderMenu(...)), + new TwigFunction('msz_user_menu', $this->getUserMenu(...)), + new TwigFunction('msz_manage_menu', $this->getManageMenu(...)), + ]; + } + + public function timeFormat(DateTime|string|int|null $dateTime): string { + if($dateTime === null) + return 'never'; + + if(is_string($dateTime)) + $dateTime = new DateTime($dateTime); + elseif(is_int($dateTime)) + $dateTime = DateTime::fromUnixTimeSeconds($dateTime); + + $string = ''; + $now = DateTime::now(); + + $isDiffYear = $now->getYear() !== $dateTime->getYear(); + if($isDiffYear || $now->getMonth() !== $dateTime->getMonth() || $now->getDay() !== $dateTime->getDay()) { + $string .= $dateTime->format('M jS'); + if($isDiffYear) + $string .= $dateTime->format(' Y'); + $string .= ', '; + } + + $string .= $dateTime->format('G:i '); + $string .= $dateTime->isUTC() ? 'UTC' : $dateTime->format('T'); + + return $string; + } + + public function getHeaderMenu(): array { + $menu = []; + $authInfo = $this->ctx->getAuthInfo(); + + $home = [ + 'title' => 'Home', + 'url' => url('index'), + 'menu' => [], + ]; + + if($authInfo->isLoggedIn()) + $home['menu'][] = [ + 'title' => 'Members', + 'url' => url('user-list'), + ]; + + $home['menu'][] = [ + 'title' => 'Changelog', + 'url' => url('changelog-index'), + ]; + $home['menu'][] = [ + 'title' => 'Contact', + 'url' => url('info', ['title' => 'contact']), + ]; + $home['menu'][] = [ + 'title' => 'Rules', + 'url' => url('info', ['title' => 'rules']), + ]; + + $menu[] = $home; + + $menu[] = [ + 'title' => 'News', + 'url' => url('news-index'), + ]; + + $forum = [ + 'title' => 'Forum', + 'url' => url('forum-index'), + 'menu' => [], + ]; + + if($authInfo->getPerms('global')->check(Perm::G_FORUM_LEADERBOARD_VIEW)) + $forum['menu'][] = [ + 'title' => 'Leaderboard', + 'url' => url('forum-leaderboard'), + ]; + + $menu[] = $forum; + + $chatPath = $this->ctx->getChatURL(); + if(!empty($chatPath)) + $menu[] = [ + 'title' => 'Chat', + 'url' => $chatPath, + ]; + + return $menu; + } + + public function getUserMenu(bool $inBroomCloset, string $manageUrl = ''): array { + $menu = []; + $authInfo = $this->ctx->getAuthInfo(); + + if($authInfo->isLoggedIn()) { + $userInfo = $authInfo->getUserInfo(); + + $menu[] = [ + 'title' => 'Profile', + 'url' => url('user-profile', ['user' => $userInfo->getId()]), + 'icon' => 'fas fa-user fa-fw', + ]; + $menu[] = [ + 'title' => 'Settings', + 'url' => url('settings-index'), + 'icon' => 'fas fa-cog fa-fw', + ]; + $menu[] = [ + 'title' => 'Search', + 'url' => url('search-index'), + 'icon' => 'fas fa-search fa-fw', + ]; + + if(!$this->ctx->hasActiveBan($userInfo) && $authInfo->getPerms('global')->check(Perm::G_IS_JANITOR)) { + // restore behaviour where clicking this button switches between + // site version and broom version + if($inBroomCloset) + $menu[] = [ + 'title' => 'Exit Broom Closet', + 'url' => $manageUrl === '' ? url('index') : $manageUrl, + 'icon' => 'fas fa-door-open fa-fw', + ]; + else + $menu[] = [ + 'title' => 'Enter Broom Closet', + 'url' => $manageUrl === '' ? url('manage-index') : $manageUrl, + 'icon' => 'fas fa-door-closed fa-fw', + ]; + } + + $menu[] = [ + 'title' => 'Log out', + 'url' => url('auth-logout'), + 'icon' => 'fas fa-sign-out-alt fa-fw', + ]; + } else { + $menu[] = [ + 'title' => 'Register', + 'url' => url('auth-register'), + 'icon' => 'fas fa-user-plus fa-fw', + ]; + $menu[] = [ + 'title' => 'Log in', + 'url' => url('auth-login'), + 'icon' => 'fas fa-sign-in-alt fa-fw', + ]; + } + + return $menu; + } + + public function getManageMenu(): array { + $authInfo = $this->ctx->getAuthInfo(); + $globalPerms = $authInfo->getPerms('global'); + if(!$authInfo->isLoggedIn() || !$globalPerms->check(Perm::G_IS_JANITOR)) + return []; + + $menu = [ + 'General' => [ + 'Overview' => url('manage-general-overview'), + ], + ]; + + if($globalPerms->check(Perm::G_LOGS_VIEW)) + $menu['General']['Logs'] = url('manage-general-logs'); + if($globalPerms->check(Perm::G_EMOTES_MANAGE)) + $menu['General']['Emoticons'] = url('manage-general-emoticons'); + if($globalPerms->check(Perm::G_CONFIG_MANAGE)) + $menu['General']['Settings'] = url('manage-general-settings'); + + $userPerms = $authInfo->getPerms('user'); + if($userPerms->check(Perm::U_USERS_MANAGE)) + $menu['Users & Roles']['Users'] = url('manage-users'); + if($userPerms->check(Perm::U_ROLES_MANAGE)) + $menu['Users & Roles']['Roles'] = url('manage-roles'); + if($userPerms->check(Perm::U_NOTES_MANAGE)) + $menu['Users & Roles']['Notes'] = url('manage-users-notes'); + if($userPerms->check(Perm::U_WARNINGS_MANAGE)) + $menu['Users & Roles']['Warnings'] = url('manage-users-warnings'); + if($userPerms->check(Perm::U_BANS_MANAGE)) + $menu['Users & Roles']['Bans'] = url('manage-users-bans'); + + if($globalPerms->check(Perm::G_NEWS_POSTS_MANAGE)) + $menu['News']['Posts'] = url('manage-news-posts'); + if($globalPerms->check(Perm::G_NEWS_CATEGORIES_MANAGE)) + $menu['News']['Categories'] = url('manage-news-categories'); + + if($globalPerms->check(Perm::G_FORUM_CATEGORIES_MANAGE)) + $menu['Forum']['Permission Calculator'] = url('manage-forum-categories'); + if($globalPerms->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE)) + $menu['Forum']['Topic Redirects'] = url('manage-forum-topic-redirs'); + + if($globalPerms->check(Perm::G_CL_CHANGES_MANAGE)) + $menu['Changelog']['Changes'] = url('manage-changelog-changes'); + if($globalPerms->check(Perm::G_CL_TAGS_MANAGE)) + $menu['Changelog']['Tags'] = url('manage-changelog-tags'); + + return $menu; + } +} diff --git a/src/Template.php b/src/Template.php index 2b668d5..95709be 100644 --- a/src/Template.php +++ b/src/Template.php @@ -2,46 +2,30 @@ namespace Misuzu; use InvalidArgumentException; -use Twig\Environment as TwigEnvironment; -use Twig\TwigFunction; -use Twig_Extensions_Extension_Date; -use Twig\Loader\FilesystemLoader as TwigLoaderFilesystem; +use Sasae\SasaeContext; +use Sasae\SasaeEnvironment; use Misuzu\MisuzuContext; final class Template { private const FILE_EXT = '.twig'; - private static $loader; - private static $env; - private static $vars = []; + private static SasaeEnvironment $env; + private static array $vars = []; - public static function init(MisuzuContext $ctx, ?string $cache = null, bool $debug = false): void { - self::$loader = new TwigLoaderFilesystem; - self::$env = new TwigEnvironment(self::$loader, [ - 'cache' => $cache ?? false, - 'strict_variables' => true, - 'auto_reload' => $debug, - 'debug' => $debug, - ]); - self::$env->addExtension(new TwigMisuzu($ctx)); - } - - public static function addPath(string $path): void { - self::$loader->addPath($path); + public static function init(SasaeEnvironment $env): void { + self::$env = $env; } public static function addFunction(string $name, callable $body): void { - self::$env->addFunction(new TwigFunction($name, $body)); + self::$env->addFunction($name, $body); } public static function renderRaw(string $file, array $vars = []): string { - if(!defined('MSZ_TPL_RENDER')) { + if(!defined('MSZ_TPL_RENDER')) define('MSZ_TPL_RENDER', microtime(true)); - } - if(!str_ends_with($file, self::FILE_EXT)) { + if(!str_ends_with($file, self::FILE_EXT)) $file = str_replace('.', DIRECTORY_SEPARATOR, $file) . self::FILE_EXT; - } return self::$env->render($file, array_merge(self::$vars, $vars)); } @@ -51,13 +35,12 @@ final class Template { } public static function set($arrayOrKey, $value = null): void { - if(is_string($arrayOrKey)) { + if(is_string($arrayOrKey)) self::$vars[$arrayOrKey] = $value; - } elseif(is_array($arrayOrKey)) { + elseif(is_array($arrayOrKey)) self::$vars = array_merge(self::$vars, $arrayOrKey); - } else { + else throw new InvalidArgumentException('First parameter must be of type array or string.'); - } } public static function displayInfo(?string $message, int $statusCode, ?string $template = null): never { diff --git a/src/TwigMisuzu.php b/src/TwigMisuzu.php deleted file mode 100644 index 0c26829..0000000 --- a/src/TwigMisuzu.php +++ /dev/null @@ -1,70 +0,0 @@ -ctx = $ctx; - } - - public function getFilters() { - return [ - new TwigFilter('country_name', Tools::countryName(...)), - new TwigFilter('parse_text', fn(string $text, int $parser): string => Parser::instance($parser)->parseText($text)), - new TwigFilter('time_format', $this->timeFormat(...)), - ]; - } - - public function getFunctions() { - return [ - new TwigFunction('url_construct', 'url_construct'), - new TwigFunction('url', 'url'), - new TwigFunction('csrf_token', CSRF::token(...)), - new TwigFunction('git_commit_hash', GitInfo::hash(...)), - new TwigFunction('git_tag', GitInfo::tag(...)), - new TwigFunction('git_branch', GitInfo::branch(...)), - new TwigFunction('startup_time', fn(float $time = MSZ_STARTUP) => microtime(true) - $time), - new TwigFunction('sql_query_count', $this->ctx->getDbQueryCount(...)), - new TwigFunction('ndx_version', Environment::getIndexVersion(...)), - new TwigFunction('byte_symbol', ByteFormat::format(...)), - ]; - } - - public function timeFormat(DateTime|string|int|null $dateTime): string { - if($dateTime === null) - return 'never'; - - if(is_string($dateTime)) - $dateTime = new DateTime($dateTime); - elseif(is_int($dateTime)) - $dateTime = DateTime::fromUnixTimeSeconds($dateTime); - - $string = ''; - $now = DateTime::now(); - - $isDiffYear = $now->getYear() !== $dateTime->getYear(); - if($isDiffYear || $now->getMonth() !== $dateTime->getMonth() || $now->getDay() !== $dateTime->getDay()) { - $string .= $dateTime->format('M jS'); - if($isDiffYear) - $string .= $dateTime->format(' Y'); - $string .= ', '; - } - - $string .= $dateTime->format('G:i '); - $string .= $dateTime->isUTC() ? 'UTC' : $dateTime->format('T'); - - return $string; - } -} diff --git a/templates/_layout/footer.twig b/templates/_layout/footer.twig index b0444b6..358bd94 100644 --- a/templates/_layout/footer.twig +++ b/templates/_layout/footer.twig @@ -13,7 +13,7 @@ {{ git_tag }} {% endif %} # {{ git_commit_hash() }} - {% if display_timings_info %} + {% if globals.display_timings_info %} / Index {{ ndx_version() }} / {{ sql_query_count()|number_format }} queries / {{ (startup_time() - startup_time(constant('MSZ_TPL_RENDER')))|number_format(5) }} load diff --git a/templates/_layout/header.twig b/templates/_layout/header.twig index 72fc56e..bdbc4d7 100644 --- a/templates/_layout/header.twig +++ b/templates/_layout/header.twig @@ -1,13 +1,14 @@ {% from 'macros.twig' import avatar %} {% from '_layout/input.twig' import input_checkbox_raw %} -{% if current_user_real is defined %} +{% if globals.auth_info.isImpersonating %} + {% set real_user_info = globals.auth_info.realUserInfo %}
-
- You are -
{{ avatar(current_user_real.id, 20, current_user_real.name) }}
- {{ current_user_real.name }} +
@@ -17,6 +18,10 @@
{% endif %} +{% set is_in_manage = is_in_manage|default(false) %} +{% set header_menu = msz_header_menu() %} +{% set user_menu = msz_user_menu(is_in_manage, (is_in_manage ? site_link|default('') : manage_link|default(''))) %} +
diff --git a/templates/forum/posting.twig b/templates/forum/posting.twig index e8bb21d..a7c3c0d 100644 --- a/templates/forum/posting.twig +++ b/templates/forum/posting.twig @@ -176,9 +176,9 @@
- {% if globals.eeprom.path is not empty %} + {% if globals.eeprom_path is not empty and globals.eeprom_app is not empty %} {% endif %} {% endblock %} diff --git a/templates/home/landing.twig b/templates/home/landing.twig index 9baf9d8..26459d7 100644 --- a/templates/home/landing.twig +++ b/templates/home/landing.twig @@ -36,6 +36,8 @@ }, ] %} +{% set header_menu = msz_header_menu() %} + {% block main_header %}
@@ -87,7 +89,7 @@ {% endif %} # {{ git_commit_hash() }}
-{% if display_timings_info %} +{% if globals.display_timings_info %} diff --git a/templates/manage/master.twig b/templates/manage/master.twig index 66fdf5d..623a1c6 100644 --- a/templates/manage/master.twig +++ b/templates/manage/master.twig @@ -2,13 +2,14 @@ {% from 'macros.twig' import container_title %} {% from 'manage/macros.twig' import manage_navigation %} +{% set is_in_manage = true %} {% set title = title|default('Broom Closet') %} {% set site_logo = '/images/logos/imouto-broom.png' %} {% block content %}
diff --git a/templates/master.twig b/templates/master.twig index 9caaa16..f64e8df 100644 --- a/templates/master.twig +++ b/templates/master.twig @@ -6,7 +6,7 @@ {% include '_layout/meta.twig' %} - + {% if site_background is defined %}