diff --git a/.env.example b/.env.example index 7626471c..7d6bd0e3 100644 --- a/.env.example +++ b/.env.example @@ -23,3 +23,15 @@ DATABASE_DSN="null:" # But to make things more understandable, the value for Flashii is also included #DOMAIN_ROLES="flashii.net=main; fii.moe=redirect" DOMAIN_ROLES="localhost=main,redirect:/go" + +# Local storage path +# This determines where uploaded files are stored. If left unset, the storage folder in the project tree will be used. +#STORAGE_PATH_LOCAL="/path/to/storage" + +# Remote storage path +# Path on which the storage folder is exposed to the web for NGINX. +#STORAGE_PATH_REMOTE="/_storage" + +# Template cache directory +# Writeable directory path to which template files are cached. +#TEMPLATE_CACHE="/tmp/msz-tpl-cache" diff --git a/composer.json b/composer.json index 5f575a6a..611ea43e 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "require": { "php": ">=8.4", "ext-mbstring": "*", - "flashwave/index": "^0.2503", + "flashwave/index": "^0.2504", "erusev/parsedown": "~1.7", "chillerlan/php-qrcode": "~5.0", "symfony/mailer": "~7.2", diff --git a/composer.lock b/composer.lock index 33fef3ce..b6a42d3b 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": "cd96df7b2981a653c33b100a8b3837cd", + "content-hash": "600e14fc77ff00755761d6e9b6add998", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -501,11 +501,11 @@ }, { "name": "flashwave/index", - "version": "v0.2503.260138", + "version": "v0.2504.21040", "source": { "type": "git", "url": "https://patchii.net/flash/index.git", - "reference": "ea549dd0eb7cc7e7348bfcfb0e95da880dd2c039" + "reference": "42adbe5da8325d66a996e5d69dbb01def80743fc" }, "require": { "ext-mbstring": "*", @@ -554,7 +554,7 @@ ], "description": "Composer package for the common library for my projects.", "homepage": "https://railgun.sh/index", - "time": "2025-03-26T01:40:42+00:00" + "time": "2025-04-02T10:41:10+00:00" }, { "name": "graham-campbell/result-type", @@ -620,16 +620,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.2", + "version": "7.9.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", "shasum": "" }, "require": { @@ -726,7 +726,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + "source": "https://github.com/guzzle/guzzle/tree/7.9.3" }, "funding": [ { @@ -742,20 +742,20 @@ "type": "tidelift" } ], - "time": "2024-07-24T11:22:20+00:00" + "time": "2025-03-27T13:37:11+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.4", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", "shasum": "" }, "require": { @@ -809,7 +809,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.4" + "source": "https://github.com/guzzle/promises/tree/2.2.0" }, "funding": [ { @@ -825,20 +825,20 @@ "type": "tidelift" } ], - "time": "2024-10-17T10:06:22+00:00" + "time": "2025-03-27T13:27:01+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", "shasum": "" }, "require": { @@ -925,7 +925,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.0" + "source": "https://github.com/guzzle/psr7/tree/2.7.1" }, "funding": [ { @@ -941,7 +941,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T11:15:46+00:00" + "time": "2025-03-27T12:30:47+00:00" }, { "name": "jean85/pretty-package-versions", @@ -1129,16 +1129,16 @@ }, { "name": "nesbot/carbon", - "version": "3.8.6", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd" + "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ff2f20cf83bd4d503720632ce8a426dc747bf7fd", - "reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6d16a8a015166fe54e22c042e0805c5363aef50d", + "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d", "shasum": "" }, "require": { @@ -1231,7 +1231,7 @@ "type": "tidelift" } ], - "time": "2025-02-20T17:33:38+00:00" + "time": "2025-03-27T12:57:33+00:00" }, { "name": "paragonie/constant_time_encoding", diff --git a/misuzu.php b/misuzu.php index 9853af83..0211a960 100644 --- a/misuzu.php +++ b/misuzu.php @@ -27,4 +27,5 @@ $msz = new MisuzuContext( $_ENV['DOMAIN_ROLES'] ?? 'localhost=main,redirect,storage', $_ENV['STORAGE_PATH_LOCAL'] ?? Misuzu::PATH_STORAGE, $_ENV['STORAGE_PATH_REMOTE'] ?? '/_storage', + $_ENV['TEMPLATE_CACHE'] ?? '', ); diff --git a/package-lock.json b/package-lock.json index 05ba9dda..9b03128f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,14 +80,14 @@ } }, "node_modules/@swc/core": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.17.tgz", - "integrity": "sha512-FXZx7jHpiwz4fTuuueWwsvN7VFLSoeS3mcxCTPUNOHs/K2ecaBO+slh5T5Xvt/KGuD2I/2T8G6Zts0maPkt2lQ==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.16.tgz", + "integrity": "sha512-wgjrJqVUss8Lxqilg0vkiE0tkEKU3mZkoybQM1Ehy+PKWwwB6lFAwKi20cAEFlSSWo8jFR8hRo19ZELAoLDowg==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.17" + "@swc/types": "^0.1.21" }, "engines": { "node": ">=10" @@ -97,16 +97,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.10.17", - "@swc/core-darwin-x64": "1.10.17", - "@swc/core-linux-arm-gnueabihf": "1.10.17", - "@swc/core-linux-arm64-gnu": "1.10.17", - "@swc/core-linux-arm64-musl": "1.10.17", - "@swc/core-linux-x64-gnu": "1.10.17", - "@swc/core-linux-x64-musl": "1.10.17", - "@swc/core-win32-arm64-msvc": "1.10.17", - "@swc/core-win32-ia32-msvc": "1.10.17", - "@swc/core-win32-x64-msvc": "1.10.17" + "@swc/core-darwin-arm64": "1.11.16", + "@swc/core-darwin-x64": "1.11.16", + "@swc/core-linux-arm-gnueabihf": "1.11.16", + "@swc/core-linux-arm64-gnu": "1.11.16", + "@swc/core-linux-arm64-musl": "1.11.16", + "@swc/core-linux-x64-gnu": "1.11.16", + "@swc/core-linux-x64-musl": "1.11.16", + "@swc/core-win32-arm64-msvc": "1.11.16", + "@swc/core-win32-ia32-msvc": "1.11.16", + "@swc/core-win32-x64-msvc": "1.11.16" }, "peerDependencies": { "@swc/helpers": "*" @@ -118,9 +118,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.17.tgz", - "integrity": "sha512-LSQhSjESleTc0c45BnVKRacp9Nl4zhJMlV/nmhpFCOv/CqHI5YBDX5c9bPk9jTRNHIf0QH92uTtswt8yN++TCQ==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.16.tgz", + "integrity": "sha512-l6uWMU+MUdfLHCl3dJgtVEdsUHPskoA4BSu0L1hh9SGBwPZ8xeOz8iLIqZM27lTuXxL4KsYH6GQR/OdQ/vhLtg==", "cpu": [ "arm64" ], @@ -134,9 +134,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.17.tgz", - "integrity": "sha512-TTaZFS4jLuA3y6+D2HYv4yVGhmjkOGG6KyAwBiJEeoUaazX5MYOyQwaZBPhRGtzHZFrzi4t4jNix4kAkMajPkQ==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.16.tgz", + "integrity": "sha512-TH0IW8Ao1WZ4ARFHIh29dAQHYBEl4YnP74n++rjppmlCjY+8v3s5nXMA7IqxO3b5LVHyggWtU4+46DXTyMJM7g==", "cpu": [ "x64" ], @@ -150,9 +150,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.17.tgz", - "integrity": "sha512-8P+ESJyGnVdJi0nUcQfxkbTiB/7hnu6N3U72KbvHFBcuroherwzW4DId1XD4RTU2Cjsh1dztZoCcOLY8W9RW1Q==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.16.tgz", + "integrity": "sha512-2IxD9t09oNZrbv37p4cJ9cTHMUAK6qNiShi9s2FJ9LcqSnZSN4iS4hvaaX6KZuG54d58vWnMU7yycjkdOTQcMg==", "cpu": [ "arm" ], @@ -166,9 +166,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.17.tgz", - "integrity": "sha512-zT21jDQCe+IslzOtw+BD/9ElO/H4qU4fkkOeVQ68PcxuqYS2gwyDxWqa9IGwpzWexYM+Lzi1rAbl/1BM6nGW8Q==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.16.tgz", + "integrity": "sha512-AYkN23DOiPh1bf3XBf/xzZQDKSsgZTxlbyTyUIhprLJpAAAT0ZCGAUcS5mHqydk0nWQ13ABUymodvHoroutNzw==", "cpu": [ "arm64" ], @@ -182,9 +182,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.17.tgz", - "integrity": "sha512-C2jaW1X+93HscVcesKYgSuZ9GaKqKcQvwvD+q+4JZkaKF4Zopt/aguc6Tmn/nuavRk0WV8yVCpHXoP7lz/2akA==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.16.tgz", + "integrity": "sha512-n/nWXDRCIhM51dDGELfBcTMNnCiFatE7LDvsbYxb7DJt1HGjaCNvHHCKURb/apJTh/YNtWfgFap9dbsTgw8yPA==", "cpu": [ "arm64" ], @@ -198,9 +198,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.17.tgz", - "integrity": "sha512-vfyxqV5gddurG2NVJLemR/68s7GTe0QruozrZiDpNqr9V4VX9t3PadDKMDAvQz6jKrtiqMtshNXQTNRKAKlzFw==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.16.tgz", + "integrity": "sha512-xr182YQrF47n7Awxj+/ruI21bYw+xO/B26KFVnb+i3ezF9NOhqoqTX+33RL1ZLA/uFTq8ksPZO/y+ZVS/odtQA==", "cpu": [ "x64" ], @@ -214,9 +214,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.17.tgz", - "integrity": "sha512-8M+nI5MHZGQUnXyfTLsGw85a3oQRXMsFjgMZuOEJO9ZGBIEnYVuWOxENfcP6MmlJmTOW+cJxHnMGhKY+fjcntw==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.16.tgz", + "integrity": "sha512-k2JBfiwWfXCIKrBRjFO9/vEdLSYq0QLJ+iNSLdfrejZ/aENNkbEg8O7O2GKUSb30RBacn6k8HMfJrcPLFiEyCQ==", "cpu": [ "x64" ], @@ -230,9 +230,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.17.tgz", - "integrity": "sha512-iUeIBFM6c/NwsreLFSAH395Dahc+54mSi0Kq//IrZ2Y16VlqCV7VHdOIMrdAyDoBFUvh0jKuLJPWt+jlKGtSLg==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.16.tgz", + "integrity": "sha512-taOb5U+abyEhQgex+hr6cI48BoqSvSdfmdirWcxprIEUBHCxa1dSriVwnJRAJOFI9T+5BEz88by6rgbB9MjbHA==", "cpu": [ "arm64" ], @@ -246,9 +246,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.17.tgz", - "integrity": "sha512-lPXYFvkfYIN8HdNmG6dCnQqgA+rOSTgeAjIhGsYCEyLsYkkhF2FQw34OF6PnWawQ6hOdOE9v6Bw3T4enj3Lb6w==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.16.tgz", + "integrity": "sha512-b7yYggM9LBDiMY+XUt5kYWvs5sn0U3PXSOGvF3CbLufD/N/YQiDcYON2N3lrWHYL8aYnwbuZl45ojmQHSQPcdA==", "cpu": [ "ia32" ], @@ -262,9 +262,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.10.17", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.17.tgz", - "integrity": "sha512-KrnkFEWpBmxSe8LixhAZXeeUwTNDVukrPeXJ1PiG+pmb5nI989I9J9IQVIgBv+JXXaK+rmiWjlcIkphaDJJEAA==", + "version": "1.11.16", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.16.tgz", + "integrity": "sha512-/ibq/YDc3B5AROkpOKPGxVkSyCKOg+ml8k11RxrW7FAPy6a9y5y9KPcWIqV74Ahq4RuaMNslTQqHWAGSm0xJsQ==", "cpu": [ "x64" ], @@ -284,9 +284,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/types": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", - "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz", + "integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==", "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -302,9 +302,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -314,9 +314,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "funding": [ { "type": "opencollective", @@ -333,11 +333,11 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -417,9 +417,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001700", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", - "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "version": "1.0.30001709", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001709.tgz", + "integrity": "sha512-NgL3vUTnDrPCZ3zTahp4fsugQ4dc7EKTSzwQDPEel6DMoMnfH2jhry9n2Zm8onbSR+f/QtKHFOA+iAQu4kbtWA==", "funding": [ { "type": "opencollective", @@ -703,9 +703,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.102", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", - "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", + "version": "1.5.130", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.130.tgz", + "integrity": "sha512-Ou2u7L9j2XLZbhqzyX0jWDj6gA8D3jIfVzt4rikLf3cGBa0VdReuFimBKS9tQJA4+XpeCxj1NoWlfBXzbMa9IA==", "license": "ISC" }, "node_modules/entities": { @@ -803,9 +803,9 @@ "license": "CC0-1.0" }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -884,9 +884,9 @@ "license": "ISC" }, "node_modules/postcss": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", - "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -1524,9 +1524,9 @@ "license": "0BSD" }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", diff --git a/public-legacy/auth/login.php b/public-legacy/auth/login.php index ad278b7a..09715142 100644 --- a/public-legacy/auth/login.php +++ b/public-legacy/auth/login.php @@ -8,7 +8,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext)) die('Script must be called through the Misuzu route dispatcher.'); if($msz->authInfo->loggedIn) { - Tools::redirect($msz->urls->format('index')); + Tools::redirect($msz->routingCtx->urls->format('index')); return; } @@ -27,7 +27,7 @@ if(!empty($_GET['resolve'])) { echo json_encode([ 'id' => 0, 'name' => '', - 'avatar' => $msz->urls->format('user-avatar', ['res' => 200, 'user' => 0]), + 'avatar' => $msz->routingCtx->urls->format('user-avatar', ['res' => 200, 'user' => 0]), ]); return; } @@ -35,7 +35,7 @@ if(!empty($_GET['resolve'])) { echo json_encode([ 'id' => (int)$userInfo->id, 'name' => $userInfo->name, - 'avatar' => $msz->urls->format('user-avatar', ['user' => $userInfo->id, 'res' => 200]), + 'avatar' => $msz->routingCtx->urls->format('user-avatar', ['user' => $userInfo->id, 'res' => 200]), ]); return; } @@ -132,7 +132,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST') { if($msz->usersCtx->totps->hasUserTotp($userInfo)) { $tfaToken = $msz->authCtx->tfaSessions->createToken($userInfo); - Tools::redirect($msz->urls->format('auth-two-factor', ['token' => $tfaToken, 'redirect' => $loginRedirect])); + Tools::redirect($msz->routingCtx->urls->format('auth-two-factor', ['token' => $tfaToken, 'redirect' => $loginRedirect])); return; } @@ -154,7 +154,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST') { AuthTokenCookie::apply($msz->authCtx->createAuthTokenPacker()->pack($tokenInfo)); if(!Tools::isLocalURL($loginRedirect)) - $loginRedirect = $msz->urls->format('index'); + $loginRedirect = $msz->routingCtx->urls->format('index'); Tools::redirect($loginRedirect); return; @@ -165,7 +165,7 @@ $oauth2Mode = !empty($_GET['oauth2']); $loginUsername = !empty($_POST['username']) && is_string($_POST['username']) ? $_POST['username'] : ( !empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : '' ); -$loginRedirect = $welcomeMode ? $msz->urls->format('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? $msz->urls->format('index'); +$loginRedirect = $welcomeMode ? $msz->routingCtx->urls->format('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? $msz->routingCtx->urls->format('index'); $canRegisterAccount = !$siteIsPrivate; Template::render('auth.login', [ diff --git a/public-legacy/auth/logout.php b/public-legacy/auth/logout.php index 5995bc34..a24ca5e4 100644 --- a/public-legacy/auth/logout.php +++ b/public-legacy/auth/logout.php @@ -24,4 +24,4 @@ if($msz->authInfo->loggedIn) { AuthTokenCookie::apply($msz->authCtx->createAuthTokenPacker()->pack($tokenInfo)); } -Tools::redirect($msz->urls->format('index'));; +Tools::redirect($msz->routingCtx->urls->format('index'));; diff --git a/public-legacy/auth/password.php b/public-legacy/auth/password.php index 2787c640..dc793817 100644 --- a/public-legacy/auth/password.php +++ b/public-legacy/auth/password.php @@ -8,7 +8,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext)) die('Script must be called through the Misuzu route dispatcher.'); if($msz->authInfo->loggedIn) { - Tools::redirect($msz->urls->format('settings-account')); + Tools::redirect($msz->routingCtx->urls->format('settings-account')); return; } @@ -21,7 +21,7 @@ if($userId > 0) try { $userInfo = $msz->usersCtx->users->getUser((string)$userId, 'id'); } catch(RuntimeException $ex) { - Tools::redirect($msz->urls->format('auth-forgot')); + Tools::redirect($msz->routingCtx->urls->format('auth-forgot')); return; } @@ -75,7 +75,7 @@ while($canResetPassword) { $msz->authCtx->recoveryTokens->invalidateToken($tokenInfo); - Tools::redirect($msz->urls->format('auth-login', ['redirect' => '/'])); + Tools::redirect($msz->routingCtx->urls->format('auth-login', ['redirect' => '/'])); return; } @@ -106,8 +106,6 @@ while($canResetPassword) { } catch(RuntimeException $ex) { $tokenInfo = $msz->authCtx->recoveryTokens->createToken($forgotUser, $ipAddress); - $msz->initMailer(); - $recoveryMessage = Mailer::template('password-recovery', [ 'username' => $forgotUser->name, 'token' => $tokenInfo->code, @@ -125,7 +123,7 @@ while($canResetPassword) { } } - Tools::redirect($msz->urls->format('auth-reset', ['user' => $forgotUser->id])); + Tools::redirect($msz->routingCtx->urls->format('auth-reset', ['user' => $forgotUser->id])); return; } diff --git a/public-legacy/auth/register.php b/public-legacy/auth/register.php index 286ae01b..4462edbb 100644 --- a/public-legacy/auth/register.php +++ b/public-legacy/auth/register.php @@ -8,7 +8,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext)) die('Script must be called through the Misuzu route dispatcher.'); if($msz->authInfo->loggedIn) { - Tools::redirect($msz->urls->format('index')); + Tools::redirect($msz->routingCtx->urls->format('index')); return; } @@ -86,13 +86,13 @@ while($_SERVER['REQUEST_METHOD'] === 'POST') { } $msz->usersCtx->users->addRoles($userInfo, $defaultRoleInfo); - $msz->config->setString('users.newest', $userInfo->id); + $msz->usersCtx->newestUserId = $userInfo->id; $msz->perms->precalculatePermissions( $msz->forumCtx->categories, [$userInfo->id] ); - Tools::redirect($msz->urls->format('auth-login-welcome', ['username' => $userInfo->name])); + Tools::redirect($msz->routingCtx->urls->format('auth-login-welcome', ['username' => $userInfo->name])); return; } diff --git a/public-legacy/auth/revert.php b/public-legacy/auth/revert.php index a1a0f79c..e48de30b 100644 --- a/public-legacy/auth/revert.php +++ b/public-legacy/auth/revert.php @@ -17,9 +17,9 @@ if($msz->csrfCtx->verifyLegacy()) { $tokenInfo = $tokenBuilder->toInfo(); AuthTokenCookie::apply($msz->authCtx->createAuthTokenPacker()->pack($tokenInfo)); - Tools::redirect($msz->urls->format('manage-user', ['user' => $impUserId])); + Tools::redirect($msz->routingCtx->urls->format('manage-user', ['user' => $impUserId])); return; } } -Tools::redirect($msz->urls->format('index')); +Tools::redirect($msz->routingCtx->urls->format('index')); diff --git a/public-legacy/auth/twofactor.php b/public-legacy/auth/twofactor.php index a396e82c..08af4df4 100644 --- a/public-legacy/auth/twofactor.php +++ b/public-legacy/auth/twofactor.php @@ -9,7 +9,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext)) die('Script must be called through the Misuzu route dispatcher.'); if($msz->authInfo->loggedIn) { - Tools::redirect($msz->urls->format('index')); + Tools::redirect($msz->routingCtx->urls->format('index')); return; } @@ -26,13 +26,13 @@ $tokenString = !empty($_GET['token']) && is_scalar($_GET['token']) ? (string)$_G $tokenUserId = $msz->authCtx->tfaSessions->getTokenUserId($tokenString); if(empty($tokenUserId)) { - Tools::redirect($msz->urls->format('auth-login')); + Tools::redirect($msz->routingCtx->urls->format('auth-login')); return; } $totpInfo = $msz->usersCtx->totps->getUserTotp($tokenUserId); if($totpInfo === null) { - Tools::redirect($msz->urls->format('auth-login')); + Tools::redirect($msz->routingCtx->urls->format('auth-login')); return; } @@ -87,7 +87,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST') { AuthTokenCookie::apply($msz->authCtx->createAuthTokenPacker()->pack($tokenInfo)); if(!Tools::isLocalURL($redirect)) - $redirect = $msz->urls->format('index'); + $redirect = $msz->routingCtx->urls->format('index'); Tools::redirect($redirect); return; @@ -95,7 +95,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST') { Template::render('auth.twofactor', [ 'twofactor_notices' => $notices, - 'twofactor_redirect' => !empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : $msz->urls->format('index'), + 'twofactor_redirect' => !empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : $msz->routingCtx->urls->format('index'), 'twofactor_attempts_remaining' => $remainingAttempts, 'twofactor_token' => $tokenString, ]); diff --git a/public-legacy/forum/leaderboard.php b/public-legacy/forum/leaderboard.php index 68236fc1..09d04ea7 100644 --- a/public-legacy/forum/leaderboard.php +++ b/public-legacy/forum/leaderboard.php @@ -93,9 +93,9 @@ MD; foreach($rankings as $ranking) { $totalPostsCount += $ranking->postsCount; $markdown .= sprintf("| %s | [%s](%s%s) | %s |\r\n", $ranking->position, - $ranking->user?->name ?? 'Deleted User', // @phpstan-ignore-line: no, it can be null + $ranking->user?->name ?? 'Deleted User', // @phpstan-ignore nullsafe.neverNull $msz->siteInfo->url, - $msz->urls->format('user-profile', ['user' => $ranking->userId]), + $msz->routingCtx->urls->format('user-profile', ['user' => $ranking->userId]), number_format($ranking->postsCount)); } diff --git a/public-legacy/forum/posting.php b/public-legacy/forum/posting.php index 5c679b24..fcc8f3b4 100644 --- a/public-legacy/forum/posting.php +++ b/public-legacy/forum/posting.php @@ -168,9 +168,9 @@ if(!empty($_POST)) { } if($isEditingTopic) { - $originalTopicTitle = $topicInfo?->title ?? null; // @phpstan-ignore-line: nope it can be null + $originalTopicTitle = $topicInfo?->title ?? null; // @phpstan-ignore nullsafe.neverNull $topicTitleChanged = $topicTitle !== $originalTopicTitle; - $originalTopicType = $topicInfo?->typeString ?? 'discussion'; // @phpstan-ignore-line: this also + $originalTopicType = $topicInfo?->typeString ?? 'discussion'; // @phpstan-ignore nullsafe.neverNull $topicTypeChanged = $topicType !== null && $topicType !== $originalTopicType; $topicTitleLengths = $msz->config->getValues([ @@ -255,9 +255,9 @@ if(!empty($_POST)) { break; } - if(empty($notices)) { // @phpstan-ignore-line: i'm guessing it gets the type confused at this point + if(empty($notices)) { // @phpstan-ignore empty.variable // does this ternary ever return forum-topic? - $redirect = $msz->urls->format(empty($topicInfo) ? 'forum-topic' : 'forum-post', [ + $redirect = $msz->routingCtx->urls->format(empty($topicInfo) ? 'forum-topic' : 'forum-post', [ 'topic' => $topicId, 'post' => $postId, ]); diff --git a/public-legacy/manage/changelog/change.php b/public-legacy/manage/changelog/change.php index 7d8eb00f..7fda07b9 100644 --- a/public-legacy/manage/changelog/change.php +++ b/public-legacy/manage/changelog/change.php @@ -39,7 +39,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $msz->changelog->deleteChange($changeInfo); $msz->logsCtx->createAuthedLog('CHANGELOG_ENTRY_DELETE', [$changeInfo->id]); - Tools::redirect($msz->urls->format('manage-changelog-changes')); + Tools::redirect($msz->routingCtx->urls->format('manage-changelog-changes')); return; } @@ -105,7 +105,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { [$changeInfo->id], ); - Tools::redirect($msz->urls->format('manage-changelog-change', ['change' => $changeInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-changelog-change', ['change' => $changeInfo->id])); return; } diff --git a/public-legacy/manage/changelog/tag.php b/public-legacy/manage/changelog/tag.php index d0699d9d..032c6b6e 100644 --- a/public-legacy/manage/changelog/tag.php +++ b/public-legacy/manage/changelog/tag.php @@ -28,7 +28,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $msz->changelog->deleteTag($tagInfo); $msz->logsCtx->createAuthedLog('CHANGELOG_TAG_DELETE', [$tagInfo->id]); - Tools::redirect($msz->urls->format('manage-changelog-tags')); + Tools::redirect($msz->routingCtx->urls->format('manage-changelog-tags')); return; } @@ -57,7 +57,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { ); if($isNew) { - Tools::redirect($msz->urls->format('manage-changelog-tag', ['tag' => $tagInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-changelog-tag', ['tag' => $tagInfo->id])); return; } else $tagInfo = $loadTagInfo(); break; diff --git a/public-legacy/manage/forum/redirs.php b/public-legacy/manage/forum/redirs.php index d55ac1fa..7e80903a 100644 --- a/public-legacy/manage/forum/redirs.php +++ b/public-legacy/manage/forum/redirs.php @@ -16,7 +16,7 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') { $msz->logsCtx->createAuthedLog('FORUM_TOPIC_REDIR_CREATE', [$rTopicId]); $msz->forumCtx->topicRedirects->createTopicRedirect($rTopicId, $msz->authInfo->userInfo, $rTopicURL); - Tools::redirect($msz->urls->format('manage-forum-topic-redirs')); + Tools::redirect($msz->routingCtx->urls->format('manage-forum-topic-redirs')); return; } @@ -27,7 +27,7 @@ if(!empty($_GET['m']) && $_GET['m'] === 'explode') { $rTopicId = !empty($_GET['t']) && is_scalar($_GET['t']) ? (string)$_GET['t'] : ''; $msz->logsCtx->createAuthedLog('FORUM_TOPIC_REDIR_REMOVE', [$rTopicId]); $msz->forumCtx->topicRedirects->deleteTopicRedirect($rTopicId); - Tools::redirect($msz->urls->format('manage-forum-topic-redirs')); + Tools::redirect($msz->routingCtx->urls->format('manage-forum-topic-redirs')); return; } diff --git a/public-legacy/manage/general/emoticon.php b/public-legacy/manage/general/emoticon.php index f11bf509..58cf185a 100644 --- a/public-legacy/manage/general/emoticon.php +++ b/public-legacy/manage/general/emoticon.php @@ -99,7 +99,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { [$emoteInfo->id], ); - Tools::redirect($msz->urls->format('manage-general-emoticon', ['emote' => $emoteInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-general-emoticon', ['emote' => $emoteInfo->id])); return; } diff --git a/public-legacy/manage/general/emoticons.php b/public-legacy/manage/general/emoticons.php index a0824d58..a3745a1a 100644 --- a/public-legacy/manage/general/emoticons.php +++ b/public-legacy/manage/general/emoticons.php @@ -38,7 +38,7 @@ if($msz->csrfCtx->verifyLegacy() && !empty($_GET['emote'])) { } } - Tools::redirect($msz->urls->format('manage-general-emoticons')); + Tools::redirect($msz->routingCtx->urls->format('manage-general-emoticons')); return; } diff --git a/public-legacy/manage/general/setting-delete.php b/public-legacy/manage/general/setting-delete.php index 4aaafce9..9df28bd4 100644 --- a/public-legacy/manage/general/setting-delete.php +++ b/public-legacy/manage/general/setting-delete.php @@ -14,7 +14,7 @@ if($valueInfo === null) if($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { $msz->logsCtx->createAuthedLog('CONFIG_DELETE', [$valueInfo->name]); $msz->config->removeValues($valueInfo->name); - Tools::redirect($msz->urls->format('manage-general-settings')); + Tools::redirect($msz->routingCtx->urls->format('manage-general-settings')); return; } diff --git a/public-legacy/manage/general/setting.php b/public-legacy/manage/general/setting.php index 2f797d2e..75b87d55 100644 --- a/public-legacy/manage/general/setting.php +++ b/public-legacy/manage/general/setting.php @@ -76,7 +76,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { $msz->logsCtx->createAuthedLog($isNew ? 'CONFIG_CREATE' : 'CONFIG_UPDATE', [$sName]); $applyFunc($sName, $sValue); - Tools::redirect($msz->urls->format('manage-general-settings')); + Tools::redirect($msz->routingCtx->urls->format('manage-general-settings')); return; } diff --git a/public-legacy/manage/news/category.php b/public-legacy/manage/news/category.php index fa697df2..5eabdfdd 100644 --- a/public-legacy/manage/news/category.php +++ b/public-legacy/manage/news/category.php @@ -28,7 +28,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $msz->news->deleteCategory($categoryInfo); $msz->logsCtx->createAuthedLog('NEWS_CATEGORY_DELETE', [$categoryInfo->id]); - Tools::redirect($msz->urls->format('manage-news-categories')); + Tools::redirect($msz->routingCtx->urls->format('manage-news-categories')); return; } @@ -57,7 +57,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { ); if($isNew) { - Tools::redirect($msz->urls->format('manage-news-category', ['category' => $categoryInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-news-category', ['category' => $categoryInfo->id])); return; } else $categoryInfo = $loadCategoryInfo(); break; diff --git a/public-legacy/manage/news/post.php b/public-legacy/manage/news/post.php index f0969b8c..0f636435 100644 --- a/public-legacy/manage/news/post.php +++ b/public-legacy/manage/news/post.php @@ -29,7 +29,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $msz->news->deletePost($postInfo); $msz->logsCtx->createAuthedLog('NEWS_POST_DELETE', [$postInfo->id]); - Tools::redirect($msz->urls->format('manage-news-posts')); + Tools::redirect($msz->routingCtx->urls->format('manage-news-posts')); return; } @@ -75,7 +75,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { if(isset($session->accessJwt) && is_string($session->accessJwt) && isset($session->did) && is_string($session->did)) { - $url = $msz->siteInfo->url . $msz->urls->format('news-post', ['post' => $postInfo->id]); + $url = $msz->siteInfo->url . $msz->routingCtx->urls->format('news-post', ['post' => $postInfo->id]); $body = sprintf("News :: %s\n", $postInfo->title); $urlStart = strlen($body); $body .= $url; @@ -129,7 +129,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { } catch(RuntimeException $ex) {} } - Tools::redirect($msz->urls->format('manage-news-post', ['post' => $postInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-news-post', ['post' => $postInfo->id])); return; } else $postInfo = $loadPostInfo(); break; diff --git a/public-legacy/manage/users/ban.php b/public-legacy/manage/users/ban.php index cb322d7f..2c9e75a6 100644 --- a/public-legacy/manage/users/ban.php +++ b/public-legacy/manage/users/ban.php @@ -23,7 +23,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $msz->usersCtx->bans->deleteBans($banInfo); $msz->logsCtx->createAuthedLog('BAN_DELETE', [$banInfo->id, $banInfo->userId]); - Tools::redirect($msz->urls->format('manage-users-bans', ['user' => $banInfo->userId])); + Tools::redirect($msz->routingCtx->urls->format('manage-users-bans', ['user' => $banInfo->userId])); return; } @@ -68,7 +68,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { ); $msz->logsCtx->createAuthedLog('BAN_CREATE', [$banInfo->id, $userInfo->id]); - Tools::redirect($msz->urls->format('manage-users-bans', ['user' => $userInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-users-bans', ['user' => $userInfo->id])); return; } diff --git a/public-legacy/manage/users/note.php b/public-legacy/manage/users/note.php index 066cbec2..dad36579 100644 --- a/public-legacy/manage/users/note.php +++ b/public-legacy/manage/users/note.php @@ -40,7 +40,7 @@ if($hasUserId) { $msz->usersCtx->modNotes->deleteNotes($noteInfo); $msz->logsCtx->createAuthedLog('MOD_NOTE_DELETE', [$noteInfo->id, $noteInfo->userId]); - Tools::redirect($msz->urls->format('manage-users-notes', ['user' => $noteInfo->userId])); + Tools::redirect($msz->routingCtx->urls->format('manage-users-notes', ['user' => $noteInfo->userId])); return; } @@ -70,7 +70,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { ); // this is easier - Tools::redirect($msz->urls->format('manage-users-note', ['note' => $noteInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-users-note', ['note' => $noteInfo->id])); return; } diff --git a/public-legacy/manage/users/role.php b/public-legacy/manage/users/role.php index dc2ec42f..28fc8c45 100644 --- a/public-legacy/manage/users/role.php +++ b/public-legacy/manage/users/role.php @@ -162,7 +162,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { $msz->config->setBoolean('perms.needsRecalc', true); } - Tools::redirect($msz->urls->format('manage-role', ['role' => $roleInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-role', ['role' => $roleInfo->id])); return; } diff --git a/public-legacy/manage/users/user.php b/public-legacy/manage/users/user.php index 1f0e99f5..d4d3f142 100644 --- a/public-legacy/manage/users/user.php +++ b/public-legacy/manage/users/user.php @@ -53,26 +53,17 @@ if($msz->csrfCtx->verifyLegacy() && $canEdit) { $notices[] = 'You must be a super user to do this.'; } elseif(!is_string($_POST['impersonate_user']) || $_POST['impersonate_user'] !== 'meow') { $notices[] = "You didn't say the magic word!"; - } else { - $allowToImpersonate = $currentUser->super; + } elseif($msz->usersCtx->canImpersonateUser($currentUser, $userInfo)) { + $msz->logsCtx->createAuthedLog('USER_IMPERSONATE', [$userInfo->id, $userInfo->name]); - if(!$allowToImpersonate) { - $allowImpersonateUsers = $msz->config->getArray(sprintf('impersonate.allow.u%s', $currentUser->id)); - $allowToImpersonate = in_array($userInfo->id, $allowImpersonateUsers, true); - } + $tokenBuilder = $msz->authInfo->tokenInfo->toBuilder(); + $tokenBuilder->setImpersonatedUserId($userInfo->id); + $tokenInfo = $tokenBuilder->toInfo(); - if($allowToImpersonate) { - $msz->logsCtx->createAuthedLog('USER_IMPERSONATE', [$userInfo->id, $userInfo->name]); - - $tokenBuilder = $msz->authInfo->tokenInfo->toBuilder(); - $tokenBuilder->setImpersonatedUserId($userInfo->id); - $tokenInfo = $tokenBuilder->toInfo(); - - AuthTokenCookie::apply($msz->authCtx->createAuthTokenPacker()->pack($tokenInfo)); - Tools::redirect($msz->urls->format('index')); - return; - } else $notices[] = "You aren't allowed to impersonate this user."; - } + AuthTokenCookie::apply($msz->authCtx->createAuthTokenPacker()->pack($tokenInfo)); + Tools::redirect($msz->routingCtx->urls->format('index')); + return; + } else $notices[] = "You aren't allowed to impersonate this user."; } if(!empty($_POST['send_test_email'])) { @@ -81,7 +72,6 @@ if($msz->csrfCtx->verifyLegacy() && $canEdit) { } elseif(!is_string($_POST['send_test_email']) || $_POST['send_test_email'] !== 'yes_send_it') { $notices[] = 'Invalid request thing shut the fuck up.'; } else { - $msz->initMailer(); $testMail = Mailer::sendMessage( [$userInfo->emailAddress => $userInfo->name], 'Flashii Test E-mail', @@ -215,7 +205,7 @@ if($msz->csrfCtx->verifyLegacy() && $canEdit) { [$userInfo->id] ); - Tools::redirect($msz->urls->format('manage-user', ['user' => $userInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-user', ['user' => $userInfo->id])); return; } diff --git a/public-legacy/manage/users/warning.php b/public-legacy/manage/users/warning.php index 4d797e02..1be96d3d 100644 --- a/public-legacy/manage/users/warning.php +++ b/public-legacy/manage/users/warning.php @@ -21,7 +21,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) { $msz->usersCtx->warnings->deleteWarnings($warnInfo); $msz->logsCtx->createAuthedLog('WARN_DELETE', [$warnInfo->id, $warnInfo->userId]); - Tools::redirect($msz->urls->format('manage-users-warnings', ['user' => $warnInfo->userId])); + Tools::redirect($msz->routingCtx->urls->format('manage-users-warnings', ['user' => $warnInfo->userId])); return; } @@ -42,7 +42,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { ); $msz->logsCtx->createAuthedLog('WARN_CREATE', [$warnInfo->id, $userInfo->id]); - Tools::redirect($msz->urls->format('manage-users-warnings', ['user' => $userInfo->id])); + Tools::redirect($msz->routingCtx->urls->format('manage-users-warnings', ['user' => $userInfo->id])); return; } diff --git a/public-legacy/profile.php b/public-legacy/profile.php index 7d0fc2c4..aa65f9f7 100644 --- a/public-legacy/profile.php +++ b/public-legacy/profile.php @@ -30,7 +30,7 @@ try { } catch(RuntimeException $ex) { $userId = $msz->usersCtx->namesHistory->resolvePastUserName($userId); if($userId !== null) { - header(sprintf('Location: %s', $msz->urls->format('user-profile', ['user' => $userId]))); + header(sprintf('Location: %s', $msz->routingCtx->urls->format('user-profile', ['user' => $userId]))); return; } @@ -58,11 +58,11 @@ switch($profileMode) { Template::throwError(404); case 'forum-topics': - Tools::redirect($msz->urls->format('search-query', ['query' => sprintf('type:forum:topic author:%s', $userInfo->name), 'section' => 'topics'])); + Tools::redirect($msz->routingCtx->urls->format('search-query', ['query' => sprintf('type:forum:topic author:%s', $userInfo->name), 'section' => 'topics'])); return; case 'forum-posts': - Tools::redirect($msz->urls->format('search-query', ['query' => sprintf('type:forum:post author:%s', $userInfo->name), 'section' => 'posts'])); + Tools::redirect($msz->routingCtx->urls->format('search-query', ['query' => sprintf('type:forum:post author:%s', $userInfo->name), 'section' => 'posts'])); return; case '': diff --git a/public-legacy/settings/sessions.php b/public-legacy/settings/sessions.php index 0adb7a08..1e396668 100644 --- a/public-legacy/settings/sessions.php +++ b/public-legacy/settings/sessions.php @@ -37,7 +37,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && $msz->csrfCtx->verifyLegacy()) { } if($activeSessionKilled) { - Tools::redirect($msz->urls->format('index')); + Tools::redirect($msz->routingCtx->urls->format('index')); return; } else break; } diff --git a/public/index.php b/public/index.php index 7a2654ba..7145f851 100644 --- a/public/index.php +++ b/public/index.php @@ -27,12 +27,9 @@ if(is_file($msz->dbCtx->getMigrateLockPath())) { } $request = \Index\Http\HttpRequest::fromRequest(); +$msz->registerRequestRoutes($request); -// order for these two currently matters i think: it shouldn't. -$router = $msz->createRouting($request); -$msz->startTemplating(); - -if($msz->domainRoles->hasRole($request->getHeaderLine('Host'), 'main')) { +if($msz->routingCtx->domainRoles->hasRole($request->getHeaderLine('Host'), 'main')) { $mszRequestPath = substr($request->requestTarget, 1); $mszLegacyPathPrefix = Misuzu::PATH_PUBLIC_LEGACY . '/'; $mszLegacyPath = $mszLegacyPathPrefix . $mszRequestPath; @@ -41,7 +38,7 @@ if($msz->domainRoles->hasRole($request->getHeaderLine('Host'), 'main')) { $mszLegacyPathReal = realpath($mszLegacyPath); if($mszLegacyPath === $mszLegacyPathReal || $mszLegacyPath === $mszLegacyPathReal . '/') { // this is here so filters can run... - $router->router->route(RouteInfo::exact( + $msz->routingCtx->router->route(RouteInfo::exact( $request->method, $request->requestTarget, #[Before('authz:cookie')] @@ -50,7 +47,7 @@ if($msz->domainRoles->hasRole($request->getHeaderLine('Host'), 'main')) { return 403; }, )); - $response = $router->router->handle($request); + $response = $msz->routingCtx->router->handle($request); if($response->getBody()->getSize() > 0) { Router::output($response); exit; @@ -112,4 +109,4 @@ if($msz->domainRoles->hasRole($request->getHeaderLine('Host'), 'main')) { } } -$router->dispatch($request); +$msz->routingCtx->dispatch($request); diff --git a/src/AssetInfo.php b/src/AssetInfo.php index 442b58d4..53a7d864 100644 --- a/src/AssetInfo.php +++ b/src/AssetInfo.php @@ -1,7 +1,13 @@ <?php namespace Misuzu; -class AssetInfo { +use Index\Templating\Extension\TplExtensionCommon; +use Twig\TwigFunction; +use Twig\Extension\LastModifiedExtensionInterface; + +class AssetInfo implements LastModifiedExtensionInterface { + use TplExtensionCommon; + public const string CURRENT_PATH = Misuzu::PATH_ASSETS . DIRECTORY_SEPARATOR . 'current.json'; /** @var array<string, string> */ @@ -21,4 +27,10 @@ class AssetInfo { public function getAssetUrl(string $name): string { return array_key_exists($name, $this->assets) ? $this->assets[$name] : ''; } + + public function getFunctions() { + return [ + new TwigFunction('asset', $this->getAssetUrl(...)), + ]; + } } diff --git a/src/Auth/AuthInfo.php b/src/Auth/AuthInfo.php index b5292413..e6b51e38 100644 --- a/src/Auth/AuthInfo.php +++ b/src/Auth/AuthInfo.php @@ -1,14 +1,20 @@ <?php namespace Misuzu\Auth; +use Misuzu\{Misuzu,Perm}; use Misuzu\Apps\AppInfo; use Misuzu\Auth\SessionInfo; use Misuzu\Forum\ForumCategoryInfo; use Misuzu\OAuth2\OAuth2AccessInfo; use Misuzu\Perms\{IPermissionResult,PermissionsData,PermissionResult}; -use Misuzu\Users\UserInfo; +use Misuzu\Users\{BanInfo,UsersContext,UserInfo}; +use Index\Templating\Extension\TplExtensionCommon; +use Twig\TwigFunction; +use Twig\Extension\LastModifiedExtensionInterface; + +class AuthInfo implements LastModifiedExtensionInterface { + use TplExtensionCommon; -class AuthInfo { public private(set) AuthTokenInfo $tokenInfo; public private(set) ?UserInfo $userInfo; public private(set) ?SessionInfo $sessionInfo; @@ -20,7 +26,8 @@ class AuthInfo { public private(set) array $perms; public function __construct( - private PermissionsData $permissions + private PermissionsData $permissions, + private UsersContext $usersCtx, ) { $this->removeInfo(); } @@ -131,4 +138,50 @@ class AuthInfo { return $this->perms[$cacheKey] = $this->permissions->getPermissions($category, $this->userInfo, $forumCategoryInfo); } + + private function getLoggedIn(): bool { + return $this->loggedIn; + } + + private function getImpersonating(): bool { + return $this->impersonating; + } + + private function getInfo(?object $object, ?string $name = null, string|int|null $default = null): object|string|int|null { + if($name === null) + return $object; + + if($object === null || !property_exists($object, $name)) + return $default; + + return $object->{$name} ?? $default; + } + + private function getUserInfo(?string $name = null, string|int|null $default = null): object|string|int|null { + return $this->getInfo($this->userInfo, $name, $default); + } + + private function getRealUserInfo(?string $name = null, string|int|null $default = null): object|string|int|null { + return $this->getInfo($this->realUserInfo ?? $this->userInfo, $name, $default); + } + + private function getBanInfo(): ?BanInfo { + return $this->usersCtx->tryGetActiveBan($this->userInfo); + } + + public function getDisplayTimings(): bool { + return Misuzu::debug() || $this->getPerms('global')->check(Perm::G_TIMINGS_VIEW); + } + + #[\Override] + public function getFunctions() { + return [ + new TwigFunction('auth_logged_in', $this->getLoggedIn(...)), + new TwigFunction('auth_get_user_info', $this->getUserInfo(...)), + new TwigFunction('auth_impersonating', $this->getImpersonating(...)), + new TwigFunction('auth_get_real_user_info', $this->getRealUserInfo(...)), + new TwigFunction('auth_get_ban_info', $this->getBanInfo(...)), + new TwigFunction('auth_display_timings', $this->getDisplayTimings(...)), + ]; + } } diff --git a/src/Auth/AuthProcessors.php b/src/Auth/AuthProcessors.php index afc778bb..04bd7104 100644 --- a/src/Auth/AuthProcessors.php +++ b/src/Auth/AuthProcessors.php @@ -3,7 +3,6 @@ namespace Misuzu\Auth; use RuntimeException; use Carbon\Carbon; -use Index\Config\Config; use Index\Http\{HttpRequest,HttpResponseBuilder}; use Index\Http\Content\FormContent; use Index\Http\Routing\{HandlerContext,RouteHandler,RouteHandlerCommon}; @@ -16,7 +15,6 @@ final class AuthProcessors implements RouteHandler { use RouteHandlerCommon; public function __construct( - private Config $impersonateConfig, private UsersContext $usersCtx, private OAuth2Context $oauth2Ctx, private CsrfContext $csrfCtx, @@ -24,14 +22,6 @@ final class AuthProcessors implements RouteHandler { private AuthInfo $authInfo, ) {} - private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool { - if($impersonator->super) - return true; - - $whitelist = $this->impersonateConfig->getArray(sprintf('allow.u%s', $impersonator->id)); - return in_array($targetId, $whitelist, true); - } - /** @param array<string, string> $error */ private static function applyErrorHeader(HttpResponseBuilder $response, string $method, array $error): void { $parts = []; @@ -184,7 +174,7 @@ final class AuthProcessors implements RouteHandler { if($sessionInfo->shouldBumpExpires) $builder->setEdited(); - if($tokenInfo->hasImpersonatedUserId && $this->canImpersonateUserId($userInfo, $tokenInfo->impersonatedUserId)) { + if($tokenInfo->hasImpersonatedUserId && $this->usersCtx->canImpersonateUser($userInfo, $tokenInfo->impersonatedUserId)) { $userInfoReal = $userInfo; try { @@ -295,7 +285,7 @@ final class AuthProcessors implements RouteHandler { $this->authCtx->sessions->recordSessionActivity(sessionInfo: $sessionInfo, remoteAddr: $request->remoteAddress); $userInfoReal = null; - if($tokenInfo->hasImpersonatedUserId && $this->canImpersonateUserId($userInfo, $tokenInfo->impersonatedUserId)) { + if($tokenInfo->hasImpersonatedUserId && $this->usersCtx->canImpersonateUser($userInfo, $tokenInfo->impersonatedUserId)) { $userInfoReal = $userInfo; try { diff --git a/src/Changelog/ChangelogRoutes.php b/src/Changelog/ChangelogRoutes.php index 8b91a295..633ca032 100644 --- a/src/Changelog/ChangelogRoutes.php +++ b/src/Changelog/ChangelogRoutes.php @@ -4,15 +4,16 @@ namespace Misuzu\Changelog; use ErrorException; use RuntimeException; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Syndication\FeedBuilder; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon}; use Misuzu\{Pagination,SiteInfo,Template}; use Misuzu\Comments\CommentsContext; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\UsersContext; +#[HandlerRoles('main')] final class ChangelogRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/Colours/ColoursApiRoutes.php b/src/Colours/ColoursApiRoutes.php index 6a71763f..4a4675c6 100644 --- a/src/Colours/ColoursApiRoutes.php +++ b/src/Colours/ColoursApiRoutes.php @@ -4,11 +4,12 @@ namespace Misuzu\Colours; use RuntimeException; use Index\XArray; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Misuzu\FieldTransformer; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('main')] final class ColoursApiRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Comments/CommentsRoutes.php b/src/Comments/CommentsRoutes.php index 18ddd0ca..df7df013 100644 --- a/src/Comments/CommentsRoutes.php +++ b/src/Comments/CommentsRoutes.php @@ -5,15 +5,16 @@ use RuntimeException; use Index\XArray; use Index\Http\{FormHttpContent,HttpRequest,HttpResponseBuilder}; use Index\Http\Content\FormContent; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon}; use Misuzu\Perm; use Misuzu\Auth\AuthInfo; use Misuzu\Perms\{PermissionResult,IPermissionResult}; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\{UserInfo,UsersContext,UsersData}; +#[HandlerRoles('main')] class CommentsRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/CsrfContext.php b/src/CsrfContext.php index cbc1509b..bdd2bd88 100644 --- a/src/CsrfContext.php +++ b/src/CsrfContext.php @@ -6,11 +6,12 @@ use Index\Config\Config; use Index\Http\Content\FormContent; use Index\Http\Routing\{HandlerContext,RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Preprocessor; +use Index\Templating\Extension\TplExtensionCommon; use Twig\TwigFunction; -use Twig\Extension\AbstractExtension; +use Twig\Extension\LastModifiedExtensionInterface; -class CsrfContext extends AbstractExtension implements RouteHandler { - use RouteHandlerCommon; +class CsrfContext implements RouteHandler, LastModifiedExtensionInterface { + use RouteHandlerCommon, TplExtensionCommon; private ?CsrfToken $instance = null; diff --git a/src/DatabaseContext.php b/src/DatabaseContext.php index 75420785..4cfe8050 100644 --- a/src/DatabaseContext.php +++ b/src/DatabaseContext.php @@ -6,9 +6,12 @@ use Index\Db\{DbBackends,DbConnection}; use Index\Db\Migration\{DbMigrationManager,DbMigrationRepo,FsDbMigrationRepo}; use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Filters\PrefixFilter; +use Index\Templating\Extension\TplExtensionCommon; +use Twig\TwigFunction; +use Twig\Extension\LastModifiedExtensionInterface; -class DatabaseContext implements RouteHandler { - use RouteHandlerCommon; +class DatabaseContext implements RouteHandler, LastModifiedExtensionInterface { + use RouteHandlerCommon, TplExtensionCommon; public private(set) DbConnection $conn; @@ -46,4 +49,11 @@ class DatabaseContext implements RouteHandler { if(is_file($this->getMigrateLockPath())) return 503; } + + #[\Override] + public function getFunctions() { + return [ + new TwigFunction('sql_query_count', $this->getQueryCount(...)), + ]; + } } diff --git a/src/Emoticons/EmotesApiRoutes.php b/src/Emoticons/EmotesApiRoutes.php index 7c318d7a..248de7d8 100644 --- a/src/Emoticons/EmotesApiRoutes.php +++ b/src/Emoticons/EmotesApiRoutes.php @@ -4,11 +4,12 @@ namespace Misuzu\Emoticons; use RuntimeException; use Index\XArray; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Misuzu\FieldTransformer; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('main')] final class EmotesApiRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Forum/ForumCategoriesRoutes.php b/src/Forum/ForumCategoriesRoutes.php index a6529546..f8863ed1 100644 --- a/src/Forum/ForumCategoriesRoutes.php +++ b/src/Forum/ForumCategoriesRoutes.php @@ -4,14 +4,15 @@ namespace Misuzu\Forum; use stdClass; use RuntimeException; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon}; use Misuzu\{Pagination,Perm,Template}; use Misuzu\Auth\AuthInfo; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\UsersContext; +#[HandlerRoles('main')] class ForumCategoriesRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/Forum/ForumContext.php b/src/Forum/ForumContext.php index 3d6539ab..07995741 100644 --- a/src/Forum/ForumContext.php +++ b/src/Forum/ForumContext.php @@ -5,9 +5,10 @@ use stdClass; use Index\Config\Config; use Index\Db\DbConnection; use Misuzu\Parsers\TextFormat; +use Misuzu\Templating\TemplatingMetaVariableProvider; use Misuzu\Users\UserInfo; -class ForumContext { +class ForumContext implements TemplatingMetaVariableProvider { public private(set) ForumCategoriesData $categories; public private(set) ForumTopicsData $topics; public private(set) ForumTopicRedirectsData $topicRedirects; @@ -38,6 +39,10 @@ class ForumContext { $this->signatures = new ForumSignaturesData($dbConn); } + public function getTemplatingMetaVariables(): array { + return ['forum-storage-pool' => $this->storagePoolName]; + } + // should be replaced by a static counter public function countTotalUserTopics(UserInfo|string|null $userInfo): int { if($userInfo === null) diff --git a/src/Forum/ForumPostsRoutes.php b/src/Forum/ForumPostsRoutes.php index bb436a21..d3f16704 100644 --- a/src/Forum/ForumPostsRoutes.php +++ b/src/Forum/ForumPostsRoutes.php @@ -3,14 +3,15 @@ namespace Misuzu\Forum; use RuntimeException; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\PatternRoute; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon}; use Misuzu\Perm; use Misuzu\Auth\AuthInfo; use Misuzu\Logs\LogsContext; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('main')] class ForumPostsRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/Forum/ForumTopicsRoutes.php b/src/Forum/ForumTopicsRoutes.php index e173500c..7fa250f7 100644 --- a/src/Forum/ForumTopicsRoutes.php +++ b/src/Forum/ForumTopicsRoutes.php @@ -5,15 +5,16 @@ use stdClass; use RuntimeException; use Index\Http\{HttpRequest,HttpResponseBuilder}; use Index\Http\Content\FormContent; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\PatternRoute; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon}; use Misuzu\{Pagination,Perm,Template}; use Misuzu\Auth\AuthInfo; use Misuzu\Logs\LogsContext; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\UsersContext; +#[HandlerRoles('main')] class ForumTopicsRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/Home/HomeRoutes.php b/src/Home/HomeRoutes.php index 96c3d55e..b329e83c 100644 --- a/src/Home/HomeRoutes.php +++ b/src/Home/HomeRoutes.php @@ -7,7 +7,6 @@ use Index\Config\Config; use Index\Colour\Colour; use Index\Db\{DbConnection,DbTools}; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\ExactRoute; use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon}; @@ -17,8 +16,10 @@ use Misuzu\Changelog\ChangelogData; use Misuzu\Comments\CommentsContext; use Misuzu\Counters\CountersData; use Misuzu\News\{NewsData,NewsCategoryInfo,NewsPostInfo}; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\{UsersContext,UserInfo}; +#[HandlerRoles('main')] class HomeRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; @@ -226,14 +227,14 @@ class HomeRoutes implements RouteHandler, UrlSource { $newestMember = []; if(empty($birthdays)) { - $newestMemberId = $this->config->getString('users.newest'); + $newestMemberId = $this->usersCtx->newestUserId; if(!empty($newestMemberId)) try { $newestMember['info'] = $this->usersCtx->getUserInfo($newestMemberId); $newestMember['colour'] = $this->usersCtx->getUserColour($newestMemberId); } catch(RuntimeException $ex) { $newestMember = []; - $this->config->removeValues('users.newest'); + $this->usersCtx->newestUserId = ''; } } diff --git a/src/Info/InfoRoutes.php b/src/Info/InfoRoutes.php index d6c108ab..92f5edb7 100644 --- a/src/Info/InfoRoutes.php +++ b/src/Info/InfoRoutes.php @@ -3,13 +3,14 @@ namespace Misuzu\Info; use Index\Index; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon}; use Misuzu\{Misuzu,Template}; use Misuzu\Parsers\{Parsers,TextFormat}; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('main')] class InfoRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/LegacyRoutes.php b/src/LegacyRoutes.php index 0c4d4b9c..8e68c634 100644 --- a/src/LegacyRoutes.php +++ b/src/LegacyRoutes.php @@ -2,10 +2,11 @@ namespace Misuzu; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource}; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('main')] class LegacyRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon; diff --git a/src/Messages/MessagesContext.php b/src/Messages/MessagesContext.php index 87f10174..d06c7463 100644 --- a/src/Messages/MessagesContext.php +++ b/src/Messages/MessagesContext.php @@ -3,8 +3,9 @@ namespace Misuzu\Messages; use Index\Config\Config; use Index\Db\DbConnection; +use Misuzu\Templating\TemplatingMetaVariableProvider; -class MessagesContext { +class MessagesContext implements TemplatingMetaVariableProvider { public private(set) MessagesData $database; public string $storagePoolName { @@ -17,4 +18,8 @@ class MessagesContext { ) { $this->database = new MessagesData($dbConn); } + + public function getTemplatingMetaVariables(): array { + return ['messages-storage-pool' => $this->storagePoolName]; + } } diff --git a/src/Messages/MessagesRoutes.php b/src/Messages/MessagesRoutes.php index 34c36056..59530874 100644 --- a/src/Messages/MessagesRoutes.php +++ b/src/Messages/MessagesRoutes.php @@ -9,7 +9,6 @@ use Index\Config\Config; use Index\Colour\Colour; use Index\Http\{FormHttpContent,HttpRequest,HttpResponseBuilder}; use Index\Http\Content\FormContent; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\{After,Before}; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon}; @@ -17,8 +16,10 @@ use Misuzu\{Misuzu,Pagination,Perm,Template}; use Misuzu\Auth\AuthInfo; use Misuzu\Parsers\TextFormat; use Misuzu\Perms\PermissionsData; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\{UsersContext,UserInfo}; +#[HandlerRoles('main')] class MessagesRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/MisuzuContext.php b/src/MisuzuContext.php index a6946bed..8d24e4ed 100644 --- a/src/MisuzuContext.php +++ b/src/MisuzuContext.php @@ -4,28 +4,31 @@ namespace Misuzu; use Index\Dependencies; use Index\Config\Config; use Index\Config\Db\DbConfig; -use Index\Db\DbConnection; -use Index\Db\Migration\{DbMigrationManager,DbMigrationRepo,FsDbMigrationRepo}; -use Index\Http\HttpRequest; -use Index\Http\Routing\RouteHandler; +use Index\Http\{HttpRequest,HttpResponseBuilder}; +use Index\Http\Routing\{Router,RouteHandler,RouteHandlerCommon}; +use Index\Http\Routing\Filters\PrefixFilter; use Index\Snowflake\{BinarySnowflake,RandomSnowflake,SnowflakeGenerator}; -use Index\Templating\TplEnvironment; -use Index\Urls\UrlRegistry; -use Misuzu\Routing\RoutingContext; -use Misuzu\Users\UserInfo; -use Twig\Extension\ExtensionInterface; +use Index\Urls\{ArrayUrlRegistry,UrlRegistry,UrlSource}; + +class MisuzuContext implements RouteHandler { + use RouteHandlerCommon; -class MisuzuContext { public private(set) Dependencies $deps; public private(set) Config $config; - public private(set) DomainRoles $domainRoles; - public private(set) TplEnvironment $templating; + public private(set) Router $router; + public private(set) UrlRegistry $urls; public private(set) SnowflakeGenerator $snowflake; public private(set) BinarySnowflake $sfBinary; public private(set) RandomSnowflake $sfRandom; + public private(set) AssetInfo $assetInfo; + public private(set) SiteInfo $siteInfo; + public private(set) Auth\AuthInfo $authInfo; + public private(set) Perms\PermissionsData $perms; + public private(set) WebFinger\WebFingerRegistry $webfinger; + public private(set) Changelog\ChangelogData $changelog; public private(set) Counters\CountersData $counters; public private(set) News\NewsData $news; @@ -42,23 +45,18 @@ class MisuzuContext { public private(set) Messages\MessagesContext $messagesCtx; public private(set) OAuth2\OAuth2Context $oauth2Ctx; public private(set) Profile\ProfileContext $profileCtx; - public private(set) Storage\StorageContext $storageCtx; - public private(set) Users\UsersContext $usersCtx; public private(set) Redirects\RedirectsContext $redirectsCtx; - - public private(set) Perms\PermissionsData $perms; - public private(set) Auth\AuthInfo $authInfo; - public private(set) SiteInfo $siteInfo; - public private(set) AssetInfo $assetInfo; - - // this probably shouldn't be available - public private(set) UrlRegistry $urls; + public private(set) Routing\RoutingContext $routingCtx; + public private(set) Storage\StorageContext $storageCtx; + public private(set) Templating\TemplatingContext $tplCtx; + public private(set) Users\UsersContext $usersCtx; public function __construct( #[\SensitiveParameter] string $dsn, string $domainRoles, string $storageLocalPath, string $storageRemotePath, + string $templateCachePath, ) { $this->deps = new Dependencies; $this->deps->register($this); @@ -71,10 +69,6 @@ class MisuzuContext { DbConfig::class, tableName: 'msz_config', )); - $this->deps->register($this->domainRoles = $this->deps->constructLazy( - DomainRoles::class, - domainRoles: $domainRoles, - )); $this->deps->register($this->snowflake = $this->deps->constructLazy(SnowflakeGenerator::class)); $this->deps->register($this->sfBinary = $this->deps->constructLazy(BinarySnowflake::class)); @@ -90,6 +84,7 @@ class MisuzuContext { AssetInfo::class, pathOrAssets: AssetInfo::CURRENT_PATH, )); + $this->deps->register($this->webfinger = $this->deps->constructLazy(WebFinger\WebFingerRegistry::class)); $this->deps->register($this->appsCtx = $this->deps->constructLazy(Apps\AppsContext::class)); $this->deps->register($this->authCtx = $this->deps->constructLazy( @@ -117,7 +112,14 @@ class MisuzuContext { config: $this->config->scopeTo('oauth2'), )); $this->deps->register($this->profileCtx = $this->deps->constructLazy(Profile\ProfileContext::class)); - $this->deps->register($this->usersCtx = $this->deps->constructLazy(Users\UsersContext::class)); + $this->deps->register($this->tplCtx = $this->deps->constructLazy( + Templating\TemplatingContext::class, + cachePath: $templateCachePath, + )); + $this->deps->register($this->usersCtx = $this->deps->constructLazy( + Users\UsersContext::class, + config: $this->config->scopeTo('users'), + )); $this->deps->register($this->redirectsCtx = $this->deps->constructLazy( Redirects\RedirectsContext::class, config: $this->config->scopeTo('redirects'), @@ -127,15 +129,23 @@ class MisuzuContext { $this->deps->register($this->counters = $this->deps->constructLazy(Counters\CountersData::class)); $this->deps->register($this->news = $this->deps->constructLazy(News\NewsData::class)); + $this->deps->register($this->routingCtx = $this->deps->construct( + Routing\RoutingContext::class, + domainRoles: $domainRoles, + )); $this->deps->register($this->storageCtx = $this->deps->construct( Storage\StorageContext::class, localPath: $storageLocalPath, remotePath: $storageRemotePath, )); + + Mailer::init($this->config->scopeTo('mail')); + Template::init($this); } - public function initMailer(): void { - Mailer::init($this->config->scopeTo('mail')); + #[PrefixFilter('/')] + public function filterPoweredBy(HttpResponseBuilder $response): void { + $response->setPoweredBy(__NAMESPACE__); } private ?bool $hasManageAccess = null; @@ -146,128 +156,75 @@ class MisuzuContext { return $this->hasManageAccess; } - private ?string $chatUrl = null; - public function getChatURL(): string { - $this->chatUrl ??= $this->config->getString('sockChat.chatPath.normal'); - return $this->chatUrl; - } - - public function startTemplating(bool $cache = true): void { - $isDebug = Misuzu::debug(); - $globals = [ - 'site_info' => $this->siteInfo, - 'auth_info' => $this->authInfo, - 'active_ban_info' => $this->usersCtx->tryGetActiveBan($this->authInfo->userInfo), - 'display_timings_info' => $isDebug || $this->authInfo->getPerms('global')->check(Perm::G_TIMINGS_VIEW), - 'meta' => [ - 'forum-storage-pool' => $this->forumCtx->storagePoolName, - 'messages-storage-pool' => $this->messagesCtx->storagePoolName, - ], - ]; - - $this->templating = new TplEnvironment( - Misuzu::PATH_TEMPLATES, - cache: $isDebug || !$cache ? null : ['Misuzu', GitInfo::hash(true)], - debug: $isDebug - ); - - $exts = $this->deps->all(ExtensionInterface::class); - foreach($exts as $ext) - $this->templating->addExtension($ext); - - $this->templating->addExtension($this->deps->construct(TemplatingExtension::class)); - $this->templating->addGlobal('globals', $globals); - - Template::init($this->templating); - } - - public function createRouting(HttpRequest $request): RoutingContext { - $host = $request->getHeaderLine('Host'); - $roles = $this->domainRoles->getRoles($host); - - $routingCtx = $this->deps->construct(RoutingContext::class); - $this->deps->register($this->urls = $routingCtx->urls); + public function registerRequestRoutes(HttpRequest $request): void { + $roles = $this->routingCtx->domainRoles->getRoles($request->getHeaderLine('Host')); $handlers = $this->deps->all(RouteHandler::class); foreach($handlers as $handler) - $routingCtx->register($handler); + if($handler instanceof Routing\RouteHandler) + $handler->registerRoleRoutes($this->routingCtx->router, $roles); + else + $handler->registerRoutes($this->routingCtx->router); - if(in_array('main', $roles)) - $this->registerMainRoutes($routingCtx); + $sources = $this->deps->all(UrlSource::class); + foreach($sources as $source) + $source->registerUrls($this->routingCtx->urls); - if(in_array('redirect', $roles)) - $this->registerRedirectRoutes($routingCtx); - - if(in_array('storage', $roles)) - $this->registerStorageRoutes($routingCtx); - - return $routingCtx; - } - - public function registerMainRoutes( - RoutingContext $routingCtx - ): void { - $this->deps->register($wf = new WebFinger\WebFingerRegistry); - $wf->register($this->deps->constructLazy( - Users\UsersWebFingerResolver::class, - config: $this->config->scopeTo('users') + // needs better registration process + // hijack registerRoutes for WebFingerRoutes to automate this + $this->webfinger->register($this->deps->constructLazy( + Users\UsersWebFingerResolver::class )); - $routingCtx->register($this->deps->constructLazy(WebFinger\WebFingerRoutes::class)); - $routingCtx->register($this->deps->constructLazy( - Auth\AuthProcessors::class, - impersonateConfig: $this->config->scopeTo('impersonate') - )); - $routingCtx->register($this->deps->constructLazy(Colours\ColoursApiRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Emoticons\EmotesApiRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Users\UsersApiRoutes::class)); + $this->routingCtx->register($this->deps->constructLazy(Auth\AuthProcessors::class), $roles); - $routingCtx->register($this->deps->constructLazy(Home\HomeRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Users\Assets\AssetsRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Info\InfoRoutes::class)); - $routingCtx->register($this->deps->constructLazy(News\NewsRoutes::class)); + $this->routingCtx->register($this->deps->constructLazy(WebFinger\WebFingerRoutes::class), $roles); - $routingCtx->register($this->deps->constructLazy(Comments\CommentsRoutes::class)); - $routingCtx->register($this->deps->constructLazy( + $this->routingCtx->register($this->deps->constructLazy(Colours\ColoursApiRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Emoticons\EmotesApiRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Users\UsersApiRoutes::class), $roles); + + $this->routingCtx->register($this->deps->constructLazy(Home\HomeRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Users\Assets\AssetsRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Info\InfoRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(News\NewsRoutes::class), $roles); + + $this->routingCtx->register($this->deps->constructLazy(Comments\CommentsRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy( Messages\MessagesRoutes::class, config: $this->config->scopeTo('messages') - )); + ), $roles); - $routingCtx->register($this->deps->constructLazy(Storage\Tasks\TasksRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Storage\Uploads\UploadsLegacyRoutes::class)); + $this->routingCtx->register($this->deps->constructLazy(Storage\Tasks\TasksRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Storage\Uploads\UploadsLegacyRoutes::class), $roles); - $routingCtx->register($this->deps->constructLazy(OAuth2\OAuth2ApiRoutes::class)); - $routingCtx->register($this->deps->constructLazy(OAuth2\OAuth2WebRoutes::class)); - $routingCtx->register($this->deps->constructLazy(WebFinger\WebFingerRoutes::class)); + $this->routingCtx->register($this->deps->constructLazy(OAuth2\OAuth2ApiRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(OAuth2\OAuth2WebRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(WebFinger\WebFingerRoutes::class), $roles); - $routingCtx->register($this->deps->constructLazy(Forum\ForumCategoriesRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Forum\ForumTopicsRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Forum\ForumPostsRoutes::class)); + $this->routingCtx->register($this->deps->constructLazy(Forum\ForumCategoriesRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Forum\ForumTopicsRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Forum\ForumPostsRoutes::class), $roles); - $routingCtx->register($this->deps->constructLazy(Changelog\ChangelogRoutes::class)); - $routingCtx->register($this->deps->constructLazy( + $this->routingCtx->register($this->deps->constructLazy(Changelog\ChangelogRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy( SharpChat\SharpChatRoutes::class, config: $this->config->scopeTo('sockChat'), - impersonateConfig: $this->config->scopeTo('impersonate') - )); + ), $roles); - $routingCtx->register($this->deps->constructLazy( + $this->routingCtx->register($this->deps->constructLazy( Satori\SatoriRoutes::class, config: $this->config->scopeTo('satori') - )); + ), $roles); - $routingCtx->register($this->deps->constructLazy(LegacyRoutes::class)); - } + $this->routingCtx->register($this->deps->constructLazy(LegacyRoutes::class), $roles); - public function registerRedirectRoutes(RoutingContext $routingCtx): void { - $routingCtx->register($this->deps->constructLazy(Redirects\LandingRedirectsRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Redirects\AliasRedirectsRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Redirects\IncrementalRedirectsRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Redirects\SocialRedirectsRoutes::class)); - $routingCtx->register($this->deps->constructLazy(Redirects\NamedRedirectsRoutes::class)); - } + $this->routingCtx->register($this->deps->constructLazy(Redirects\LandingRedirectsRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Redirects\AliasRedirectsRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Redirects\IncrementalRedirectsRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Redirects\SocialRedirectsRoutes::class), $roles); + $this->routingCtx->register($this->deps->constructLazy(Redirects\NamedRedirectsRoutes::class), $roles); - public function registerStorageRoutes(RoutingContext $routingCtx): void { - $routingCtx->register($this->deps->constructLazy(Storage\Uploads\UploadsViewRoutes::class)); + $this->routingCtx->register($this->deps->constructLazy(Storage\Uploads\UploadsViewRoutes::class), $roles); } } diff --git a/src/News/NewsRoutes.php b/src/News/NewsRoutes.php index bf1a4837..de98bfaa 100644 --- a/src/News/NewsRoutes.php +++ b/src/News/NewsRoutes.php @@ -4,7 +4,6 @@ namespace Misuzu\News; use RuntimeException; use Index\Colour\Colour; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Syndication\FeedBuilder; @@ -12,8 +11,10 @@ use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon}; use Misuzu\{Pagination,SiteInfo,Template}; use Misuzu\Comments\CommentsContext; use Misuzu\Parsers\{Parsers,TextFormat}; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\{UsersContext,UserInfo}; +#[HandlerRoles('main')] class NewsRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; @@ -120,6 +121,7 @@ class NewsRoutes implements RouteHandler, UrlSource { } #[PatternRoute('GET', '/news/([0-9]+)(?:\.(xml|rss|atom))?')] + #[Before('authz:cookie')] #[UrlFormat('news-category', '/news/<category>', ['p' => '<page>'])] public function getCategory(HttpResponseBuilder $response, HttpRequest $request, string $categoryId, string $type = ''): int|string { try { diff --git a/src/OAuth2/OAuth2ApiRoutes.php b/src/OAuth2/OAuth2ApiRoutes.php index ecac8aad..575db8db 100644 --- a/src/OAuth2/OAuth2ApiRoutes.php +++ b/src/OAuth2/OAuth2ApiRoutes.php @@ -6,7 +6,6 @@ use Index\XArray; use Index\Colour\{Colour,ColourRgb}; use Index\Http\{HttpResponseBuilder,HttpRequest}; use Index\Http\Content\FormContent; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; @@ -15,8 +14,10 @@ use Misuzu\SiteInfo; use Misuzu\Apps\AppsContext; use Misuzu\Auth\AuthInfo; use Misuzu\Profile\ProfileContext; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\UsersContext; +#[HandlerRoles('main')] final class OAuth2ApiRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/OAuth2/OAuth2WebRoutes.php b/src/OAuth2/OAuth2WebRoutes.php index 6e944bc9..50db5e1a 100644 --- a/src/OAuth2/OAuth2WebRoutes.php +++ b/src/OAuth2/OAuth2WebRoutes.php @@ -6,14 +6,15 @@ use RuntimeException; use Index\XArray; use Index\Http\{HttpResponseBuilder,HttpRequest}; use Index\Http\Content\FormContent; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon}; use Misuzu\{CsrfContext,Template}; use Misuzu\Auth\AuthInfo; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\UsersContext; +#[HandlerRoles('main')] final class OAuth2WebRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/Redirects/AliasRedirectsRoutes.php b/src/Redirects/AliasRedirectsRoutes.php index 0d120a53..23cdb9f5 100644 --- a/src/Redirects/AliasRedirectsRoutes.php +++ b/src/Redirects/AliasRedirectsRoutes.php @@ -3,9 +3,10 @@ namespace Misuzu\Redirects; use Index\Config\Config; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Routes\PatternRoute; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('redirect')] class AliasRedirectsRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Redirects/IncrementalRedirectsRoutes.php b/src/Redirects/IncrementalRedirectsRoutes.php index 5d75dcfc..a2c32b98 100644 --- a/src/Redirects/IncrementalRedirectsRoutes.php +++ b/src/Redirects/IncrementalRedirectsRoutes.php @@ -5,10 +5,11 @@ use RuntimeException; use Index\XNumber; use Index\Http\HttpResponseBuilder; use Index\Http\Content\FormContent; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('redirect')] class IncrementalRedirectsRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Redirects/LandingRedirectsRoutes.php b/src/Redirects/LandingRedirectsRoutes.php index 01b45e61..d1cd5589 100644 --- a/src/Redirects/LandingRedirectsRoutes.php +++ b/src/Redirects/LandingRedirectsRoutes.php @@ -1,10 +1,11 @@ <?php namespace Misuzu\Redirects; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Routes\ExactRoute; use Misuzu\Template; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('redirect')] class LandingRedirectsRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Redirects/NamedRedirectsRoutes.php b/src/Redirects/NamedRedirectsRoutes.php index d6c6d54d..f4c0a0a3 100644 --- a/src/Redirects/NamedRedirectsRoutes.php +++ b/src/Redirects/NamedRedirectsRoutes.php @@ -3,9 +3,10 @@ namespace Misuzu\Redirects; use RuntimeException; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Routes\PatternRoute; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('redirect')] class NamedRedirectsRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Redirects/SocialRedirectsRoutes.php b/src/Redirects/SocialRedirectsRoutes.php index 173ed7bd..d288fe43 100644 --- a/src/Redirects/SocialRedirectsRoutes.php +++ b/src/Redirects/SocialRedirectsRoutes.php @@ -2,10 +2,11 @@ namespace Misuzu\Redirects; use Index\Http\HttpResponseBuilder; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Routes\PatternRoute; use Misuzu\Template; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('redirect')] class SocialRedirectsRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/DomainRoles.php b/src/Routing/DomainRoles.php similarity index 98% rename from src/DomainRoles.php rename to src/Routing/DomainRoles.php index 40d4090e..dd412853 100644 --- a/src/DomainRoles.php +++ b/src/Routing/DomainRoles.php @@ -1,5 +1,5 @@ <?php -namespace Misuzu; +namespace Misuzu\Routing; use Index\XArray; diff --git a/src/Routing/HandlerRoles.php b/src/Routing/HandlerRoles.php new file mode 100644 index 00000000..6e4089fc --- /dev/null +++ b/src/Routing/HandlerRoles.php @@ -0,0 +1,36 @@ +<?php +namespace Misuzu\Routing; + +use Attribute; +use ReflectionAttribute; +use ReflectionObject; +use Index\Http\Routing\{Router,RouteHandler as IndexRouteHandler}; + +#[Attribute(Attribute::TARGET_CLASS)] +final class HandlerRoles { + /** @var string[] $roles */ + public private(set) array $roles; + + public function __construct(string ...$roles) { + $this->roles = $roles; + } + + public function hasRole(string $role): bool { + return in_array($role, $this->roles); + } + + /** @param string[] $roles */ + public static function register(Router $router, IndexRouteHandler $handler, array $roles): void { + $object = new ReflectionObject($handler); + $attrs = $object->getAttributes(self::class, ReflectionAttribute::IS_INSTANCEOF); + if(empty($attrs)) + return; + + $info = $attrs[0]->newInstance(); + foreach($roles as $role) + if($info->hasRole($role)) { + $handler->registerRoutes($router); + break; + } + } +} diff --git a/src/Routing/RouteHandler.php b/src/Routing/RouteHandler.php new file mode 100644 index 00000000..c239bd34 --- /dev/null +++ b/src/Routing/RouteHandler.php @@ -0,0 +1,9 @@ +<?php +namespace Misuzu\Routing; + +use Index\Http\Routing\{Router,RouteHandler as IndexRouteHandler}; + +interface RouteHandler extends IndexRouteHandler { + /** @param string[] $roles */ + public function registerRoleRoutes(Router $router, array $roles): void; +} diff --git a/src/Routing/RouteHandlerCommon.php b/src/Routing/RouteHandlerCommon.php new file mode 100644 index 00000000..0eb5a08d --- /dev/null +++ b/src/Routing/RouteHandlerCommon.php @@ -0,0 +1,13 @@ +<?php +namespace Misuzu\Routing; + +use Index\Http\Routing\{Router,RouteHandlerCommon as IndexRouteHandlerCommon}; + +trait RouteHandlerCommon { + use IndexRouteHandlerCommon; + + /** @param string[] $roles */ + public function registerRoleRoutes(Router $router, array $roles): void { + HandlerRoles::register($router, $this, $roles); + } +} diff --git a/src/Routing/RoutingContext.php b/src/Routing/RoutingContext.php index bcd718b1..b7849969 100644 --- a/src/Routing/RoutingContext.php +++ b/src/Routing/RoutingContext.php @@ -1,33 +1,59 @@ <?php namespace Misuzu\Routing; +use Index\Dependencies; use Index\Config\Config; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{Router,RouteHandler}; +use Index\Http\Routing\{Router,RouteHandler as IndexRouteHandler}; use Index\Http\Routing\Filters\FilterInfo; +use Index\Templating\Extension\TplExtensionCommon; use Index\Urls\{ArrayUrlRegistry,UrlRegistry,UrlSource}; +use Twig\TwigFunction; +use Twig\Extension\LastModifiedExtensionInterface; + + +class RoutingContext implements LastModifiedExtensionInterface { + use TplExtensionCommon; -class RoutingContext { public private(set) Router $router; public private(set) UrlRegistry $urls; + public private(set) DomainRoles $domainRoles; - public function __construct(Config $config) { - $this->urls = new ArrayUrlRegistry; - $this->router = new Router( - new RoutingErrorHandler, - new RoutingAccessControlHandler($config), - ); - $this->router->filter(FilterInfo::prefix('/', fn(HttpResponseBuilder $response) => $response->setPoweredBy('Misuzu'))); + public function __construct( + Dependencies $deps, + string $domainRoles, + ) { + $deps->register($this->urls = $deps->constructLazy(ArrayUrlRegistry::class)); + $deps->register($this->router = $deps->constructLazy( + Router::class, + errorHandler: $deps->constructLazy(RoutingErrorHandler::class), + accessControlHandler: $deps->constructLazy(RoutingAccessControlHandler::class), + )); + $deps->register($this->domainRoles = $deps->constructLazy( + DomainRoles::class, + domainRoles: $domainRoles, + )); } - public function register(RouteHandler|UrlSource $handler): void { + /** @param string[] $roles */ + public function register(IndexRouteHandler|UrlSource $handler, array $roles): void { if($handler instanceof RouteHandler) - $this->router->register($handler); + $handler->registerRoleRoutes($this->router, $roles); + elseif($handler instanceof IndexRouteHandler) + $handler->registerRoutes($this->router); + if($handler instanceof UrlSource) - $this->urls->register($handler); + $handler->registerUrls($this->urls); } public function dispatch(?HttpRequest $request = null): void { $this->router->dispatch($request); } + + #[\Override] + public function getFunctions() { + return [ + new TwigFunction('url', $this->urls->format(...)), + ]; + } } diff --git a/src/Satori/SatoriRoutes.php b/src/Satori/SatoriRoutes.php index aa133853..eb48e43e 100644 --- a/src/Satori/SatoriRoutes.php +++ b/src/Satori/SatoriRoutes.php @@ -6,7 +6,6 @@ use Index\Colour\Colour; use Index\Config\Config; use Index\Http\{HttpRequest,HttpResponseBuilder}; use Index\Http\Content\FormContent; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Filters\PrefixFilter; use Index\Http\Routing\Processors\Before; @@ -14,8 +13,10 @@ use Index\Http\Routing\Routes\ExactRoute; use Misuzu\Pagination; use Misuzu\Forum\ForumContext; use Misuzu\Profile\ProfileContext; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\UsersContext; +#[HandlerRoles('main')] final class SatoriRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/SharpChat/SharpChatRoutes.php b/src/SharpChat/SharpChatRoutes.php index 7d6de6df..4caf4e1e 100644 --- a/src/SharpChat/SharpChatRoutes.php +++ b/src/SharpChat/SharpChatRoutes.php @@ -6,7 +6,6 @@ use Index\Colour\Colour; use Index\Config\Config; use Index\Http\{HttpRequest,HttpResponseBuilder}; use Index\Http\Content\{FormContent,UrlEncodedFormContent}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\ExactRoute; @@ -16,8 +15,10 @@ use Misuzu\Counters\CountersData; use Misuzu\Emoticons\EmotesContext; use Misuzu\OAuth2\{OAuth2AccessInfoGetField,OAuth2Context}; use Misuzu\Perms\PermissionsData; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\{BansData,UsersContext,UserInfo}; +#[HandlerRoles('main')] final class SharpChatRoutes implements RouteHandler { use RouteHandlerCommon; @@ -25,7 +26,6 @@ final class SharpChatRoutes implements RouteHandler { public function __construct( private Config $config, - private Config $impersonateConfig, // this sucks lol private UrlRegistry $urls, private UsersContext $usersCtx, private AuthContext $authCtx, @@ -77,14 +77,6 @@ final class SharpChatRoutes implements RouteHandler { )); } - private function canImpersonateUserId(UserInfo $impersonator, string $targetId): bool { - if($impersonator->super) - return true; - - $whitelist = $this->impersonateConfig->getArray(sprintf('allow.u%s', $impersonator->id)); - return in_array($targetId, $whitelist, true); - } - /** @return array{ok: false, err: string}|array{ok: true, usr: int, tkn: string} */ #[AccessControl(credentials: true)] #[ExactRoute('GET', '/_sockchat/token')] @@ -106,7 +98,7 @@ final class SharpChatRoutes implements RouteHandler { return ['ok' => false, 'err' => 'user']; $userInfo = $this->usersCtx->users->getUser($sessionInfo->userId, 'id'); - $userId = $tokenInfo->hasImpersonatedUserId && $this->canImpersonateUserId($userInfo, $tokenInfo->impersonatedUserId) + $userId = $tokenInfo->hasImpersonatedUserId && $this->usersCtx->canImpersonateUser($userInfo, $tokenInfo->impersonatedUserId) ? $tokenInfo->impersonatedUserId : $userInfo->id; @@ -227,7 +219,7 @@ final class SharpChatRoutes implements RouteHandler { $this->authCtx->sessions->recordSessionActivity(sessionInfo: $sessionInfo, remoteAddr: $ipAddress); $userInfo = $this->usersCtx->users->getUser($sessionInfo->userId, 'id'); - if($tokenInfo->hasImpersonatedUserId && $this->canImpersonateUserId($userInfo, $tokenInfo->impersonatedUserId)) { + if($tokenInfo->hasImpersonatedUserId && $this->usersCtx->canImpersonateUser($userInfo, $tokenInfo->impersonatedUserId)) { $userInfoReal = $userInfo; try { diff --git a/src/SiteInfo.php b/src/SiteInfo.php index 0024a0f9..bcc08d91 100644 --- a/src/SiteInfo.php +++ b/src/SiteInfo.php @@ -2,48 +2,58 @@ namespace Misuzu; use Index\Config\Config; +use Index\Templating\Extension\TplExtensionCommon; +use Twig\TwigFunction; +use Twig\Extension\LastModifiedExtensionInterface; -class SiteInfo { - /** @var array<string, string> */ - private array $props; +class SiteInfo implements LastModifiedExtensionInterface { + use TplExtensionCommon; - public function __construct(Config $config) { - $this->props = $config->getValues([ - ['name:s', 'Misuzu'], - 'desc:s', - 'domain:s', - 'url:s', - 'email:s', - 'bsky:s', - 'ext_logo:s', - ]); - } + public function __construct( + private Config $config, + ) {} public string $name { - get => $this->props['name']; + get => $this->config->getString('name', 'Misuzu'); } public string $description { - get => $this->props['desc']; + get => $this->config->getString('desc'); } public string $url { - get => rtrim($this->props['url'], '/'); + get => $this->config->getString('url'); } public string $email { - get => $this->props['email']; + get => $this->config->getString('email'); } public string $bsky { - get => $this->props['bsky']; + get => $this->config->getString('bsky'); } public string $domain { - get => $this->props['domain']; + get => $this->config->getString('domain'); } public string $externalLogo { - get => $this->props['ext_logo']; + get => $this->config->getString('ext_logo'); + } + + public function getProperty(string $name): string { + return $this->config->getString($name); + } + + public function hasProperty(string $name): bool { + return $this->config->hasValues($name); + } + + #[\Override] + public function getFunctions() { + return [ + new TwigFunction('site_has_info', $this->getProperty(...)), + new TwigFunction('site_get_info', $this->getProperty(...)), + ]; } } diff --git a/src/Storage/Tasks/TasksRoutes.php b/src/Storage/Tasks/TasksRoutes.php index 154108f8..30efc87f 100644 --- a/src/Storage/Tasks/TasksRoutes.php +++ b/src/Storage/Tasks/TasksRoutes.php @@ -4,10 +4,11 @@ namespace Misuzu\Storage\Tasks; use RuntimeException; use Index\{ByteFormat,XNumber}; use Index\Http\{HttpResponseBuilder,HttpRequest}; -use Index\Http\Routing\{HttpPut,RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Routes\PatternRoute; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('main')] class TasksRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Storage/Uploads/UploadsLegacyRoutes.php b/src/Storage/Uploads/UploadsLegacyRoutes.php index 952daedc..c4ac83ce 100644 --- a/src/Storage/Uploads/UploadsLegacyRoutes.php +++ b/src/Storage/Uploads/UploadsLegacyRoutes.php @@ -6,19 +6,20 @@ use Index\{ByteFormat,XArray,XNumber}; use Index\Http\{HttpResponseBuilder,HttpRequest}; use Index\Http\Content\MultipartFormContent; use Index\Http\Content\Multipart\FileMultipartFormData; -use Index\Http\Routing\{HttpDelete,HttpOptions,HttpPost,RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon}; use Misuzu\Perm; use Misuzu\Auth\AuthInfo; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Storage\Denylist\{DenylistContext,DenylistReason}; use Misuzu\Storage\Pools\{PoolsContext,PoolInfoGetField}; use Misuzu\Storage\Pools\Rules\{ConstrainSizeRule,EnsureVariantRule,EnsureVariantRuleThumb}; use Misuzu\Storage\Files\{FilesContext,FileImportMode,FileInfoGetFileField}; use Misuzu\Users\UsersContext; +#[HandlerRoles('main')] class UploadsLegacyRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/Storage/Uploads/UploadsViewRoutes.php b/src/Storage/Uploads/UploadsViewRoutes.php index c0c373ff..1178a66b 100644 --- a/src/Storage/Uploads/UploadsViewRoutes.php +++ b/src/Storage/Uploads/UploadsViewRoutes.php @@ -4,14 +4,16 @@ namespace Misuzu\Storage\Uploads; use RuntimeException; use Index\{XArray,XNumber}; use Index\Http\{HttpResponseBuilder,HttpRequest}; -use Index\Http\Routing\{Router,RouteHandler,RouteHandlerCommon}; +use Index\Http\Routing\Router; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Routes\PatternRoute; use Misuzu\Storage\Denylist\DenylistContext; use Misuzu\Storage\Pools\PoolsContext; use Misuzu\Storage\Pools\Rules\{EnsureVariantRule,EnsureVariantRuleThumb}; use Misuzu\Storage\Files\FilesContext; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('storage')] class UploadsViewRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Template.php b/src/Template.php index 7ab430ca..655ad104 100644 --- a/src/Template.php +++ b/src/Template.php @@ -12,8 +12,8 @@ final class Template { /** @var array<string, mixed> */ private static array $vars = []; - public static function init(TplEnvironment $env): void { - self::$env = $env; + public static function init(MisuzuContext $env): void { + self::$env = $env->tplCtx->env; } public static function addFunction(string $name, callable $body): void { diff --git a/src/Templating/TemplatingContext.php b/src/Templating/TemplatingContext.php new file mode 100644 index 00000000..67630dc5 --- /dev/null +++ b/src/Templating/TemplatingContext.php @@ -0,0 +1,34 @@ +<?php +namespace Misuzu\Templating; + +use Index\Dependencies; +use Index\Templating\{TplContext,TplEnvironment}; +use Misuzu\Misuzu; +use Twig\Extension\ExtensionInterface; + +class TemplatingContext { + public private(set) TplEnvironment $env; + + public function __construct( + Dependencies $deps, + string $cachePath, + ) { + $this->env = new TplEnvironment( + Misuzu::PATH_TEMPLATES, + cache: Misuzu::cli() || empty($cachePath) ? null : $cachePath, + debug: Misuzu::debug(), + ); + + $deps->register($this->env); + $this->env->addExtension($deps->construct(TemplatingExtension::class)); + + $exts = $deps->all(ExtensionInterface::class); + foreach($exts as $ext) + $this->env->addExtension($ext); + } + + /** @param array<string, mixed> $vars Context local variables to add right away. */ + public function loadTemplate(string $name, array $vars = []): TplContext { + return $this->env->load($name, $vars); + } +} diff --git a/src/TemplatingExtension.php b/src/Templating/TemplatingExtension.php similarity index 58% rename from src/TemplatingExtension.php rename to src/Templating/TemplatingExtension.php index ef81f226..45abaa2b 100644 --- a/src/TemplatingExtension.php +++ b/src/Templating/TemplatingExtension.php @@ -1,18 +1,33 @@ <?php -namespace Misuzu; +namespace Misuzu\Templating; use DateTimeInterface; use Carbon\CarbonImmutable; +use Index\Dependencies; +use Index\Config\Config; +use Index\Templating\Extension\TplExtensionCommon; +use Index\Urls\UrlRegistry; +use Misuzu\{CsrfContext,GitInfo,Perm,SiteInfo,Tools}; +use Misuzu\Auth\AuthInfo; use Misuzu\Parsers\{Parsers,TextFormat}; +use Misuzu\Users\UsersContext; use Twig\{TwigFilter,TwigFunction}; -use Twig\Extension\AbstractExtension; +use Twig\Extension\LastModifiedExtensionInterface; + +final class TemplatingExtension implements LastModifiedExtensionInterface { + use TplExtensionCommon; -final class TemplatingExtension extends AbstractExtension { public function __construct( - private MisuzuContext $ctx, - private AssetInfo $assetInfo + private Config $config, + private Dependencies $deps, + private UrlRegistry $urls, + private CsrfContext $csrfCtx, + private UsersContext $usersCtx, + private AuthInfo $authInfo, + private SiteInfo $siteInfo, ) {} + #[\Override] public function getFilters() { return [ new TwigFilter('country_name', Tools::countryName(...)), @@ -22,15 +37,14 @@ final class TemplatingExtension extends AbstractExtension { ]; } + #[\Override] public function getFunctions() { return [ - new TwigFunction('asset', $this->assetInfo->getAssetUrl(...)), - new TwigFunction('url', $this->ctx->urls->format(...)), + new TwigFunction('get_meta_vars', $this->getMetaVariables(...)), 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->dbCtx->getQueryCount(...)), new TwigFunction('msz_header_menu', $this->getHeaderMenu(...)), new TwigFunction('msz_user_menu', $this->getUserMenu(...)), new TwigFunction('msz_manage_menu', $this->getManageMenu(...)), @@ -44,6 +58,14 @@ final class TemplatingExtension extends AbstractExtension { ]; } + /** @return array<string, string> */ + public function getMetaVariables(): array { + return array_merge(...array_map( + fn($provider) => $provider->getTemplatingMetaVariables(), + $this->deps->all(TemplatingMetaVariableProvider::class), + )); + } + public function timeFormat(DateTimeInterface|string|int|null $dateTime): string { if($dateTime === null) return 'never'; @@ -73,56 +95,56 @@ final class TemplatingExtension extends AbstractExtension { $home = [ 'title' => 'Home', - 'url' => $this->ctx->urls->format('index'), + 'url' => $this->urls->format('index'), 'menu' => [], ]; - if($this->ctx->authInfo->loggedIn) + if($this->authInfo->loggedIn) $home['menu'][] = [ 'title' => 'Members', - 'url' => $this->ctx->urls->format('user-list'), + 'url' => $this->urls->format('user-list'), ]; $home['menu'][] = [ 'title' => 'Changelog', - 'url' => $this->ctx->urls->format('changelog-index'), + 'url' => $this->urls->format('changelog-index'), ]; $home['menu'][] = [ 'title' => 'Contact', - 'url' => $this->ctx->urls->format('info', ['title' => 'contact']), + 'url' => $this->urls->format('info', ['title' => 'contact']), ]; $home['menu'][] = [ 'title' => 'Rules', - 'url' => $this->ctx->urls->format('info', ['title' => 'rules']), + 'url' => $this->urls->format('info', ['title' => 'rules']), ]; - if(!empty($this->ctx->siteInfo->bsky)) + if(!empty($this->siteInfo->bsky)) $home['menu'][] = [ 'title' => 'Bluesky', - 'url' => $this->ctx->siteInfo->bsky, + 'url' => $this->siteInfo->bsky, ]; $menu[] = $home; $menu[] = [ 'title' => 'News', - 'url' => $this->ctx->urls->format('news-index'), + 'url' => $this->urls->format('news-index'), ]; $forum = [ 'title' => 'Forum', - 'url' => $this->ctx->urls->format('forum-index'), + 'url' => $this->urls->format('forum-index'), 'menu' => [], ]; - if($this->ctx->authInfo->getPerms('global')->check(Perm::G_FORUM_LEADERBOARD_VIEW)) + if($this->authInfo->getPerms('global')->check(Perm::G_FORUM_LEADERBOARD_VIEW)) $forum['menu'][] = [ 'title' => 'Leaderboard', - 'url' => $this->ctx->urls->format('forum-leaderboard'), + 'url' => $this->urls->format('forum-leaderboard'), ]; $menu[] = $forum; - $chatPath = $this->ctx->getChatURL(); + $chatPath = $this->config->getString('sockChat.chatPath.normal'); if(!empty($chatPath)) $menu[] = [ 'title' => 'Chat', @@ -142,64 +164,64 @@ final class TemplatingExtension extends AbstractExtension { public function getUserMenu(bool $inBroomCloset, string $manageUrl = ''): array { $menu = []; - if($this->ctx->authInfo->loggedIn) { - $userInfo = $this->ctx->authInfo->userInfo; - $globalPerms = $this->ctx->authInfo->getPerms('global'); + if($this->authInfo->loggedIn) { + $userInfo = $this->authInfo->userInfo; + $globalPerms = $this->authInfo->getPerms('global'); $menu[] = [ 'title' => 'Profile', - 'url' => $this->ctx->urls->format('user-profile', ['user' => $userInfo->id]), + 'url' => $this->urls->format('user-profile', ['user' => $userInfo->id]), 'icon' => 'fas fa-user fa-fw', ]; if($globalPerms->check(Perm::G_MESSAGES_VIEW)) $menu[] = [ 'title' => 'Messages', - 'url' => $this->ctx->urls->format('messages-index'), + 'url' => $this->urls->format('messages-index'), 'icon' => 'fas fa-envelope fa-fw', 'class' => 'js-header-pms-button', ]; $menu[] = [ 'title' => 'Settings', - 'url' => $this->ctx->urls->format('settings-index'), + 'url' => $this->urls->format('settings-index'), 'icon' => 'fas fa-cog fa-fw', ]; $menu[] = [ 'title' => 'Search', - 'url' => $this->ctx->urls->format('search-index'), + 'url' => $this->urls->format('search-index'), 'icon' => 'fas fa-search fa-fw', ]; - if(!$this->ctx->usersCtx->hasActiveBan($userInfo) && $globalPerms->check(Perm::G_IS_JANITOR)) { + if(!$this->usersCtx->hasActiveBan($userInfo) && $globalPerms->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 === '' ? $this->ctx->urls->format('index') : $manageUrl, + 'url' => $manageUrl === '' ? $this->urls->format('index') : $manageUrl, 'icon' => 'fas fa-door-open fa-fw', ]; else $menu[] = [ 'title' => 'Enter Broom Closet', - 'url' => $manageUrl === '' ? $this->ctx->urls->format('manage-index') : $manageUrl, + 'url' => $manageUrl === '' ? $this->urls->format('manage-index') : $manageUrl, 'icon' => 'fas fa-door-closed fa-fw', ]; } $menu[] = [ 'title' => 'Log out', - 'url' => $this->ctx->urls->format('auth-logout', ['csrf' => $this->ctx->csrfCtx->createToken()]), + 'url' => $this->urls->format('auth-logout', ['csrf' => $this->csrfCtx->createToken()]), 'icon' => 'fas fa-sign-out-alt fa-fw', ]; } else { $menu[] = [ 'title' => 'Register', - 'url' => $this->ctx->urls->format('auth-register'), + 'url' => $this->urls->format('auth-register'), 'icon' => 'fas fa-user-plus fa-fw', ]; $menu[] = [ 'title' => 'Log in', - 'url' => $this->ctx->urls->format('auth-login'), + 'url' => $this->urls->format('auth-login'), 'icon' => 'fas fa-sign-in-alt fa-fw', ]; } @@ -209,49 +231,49 @@ final class TemplatingExtension extends AbstractExtension { /** @return array<string, array<string, string>> */ public function getManageMenu(): array { - $globalPerms = $this->ctx->authInfo->getPerms('global'); - if(!$this->ctx->authInfo->loggedIn || !$globalPerms->check(Perm::G_IS_JANITOR)) + $globalPerms = $this->authInfo->getPerms('global'); + if(!$this->authInfo->loggedIn || !$globalPerms->check(Perm::G_IS_JANITOR)) return []; $menu = [ 'General' => [ - 'Overview' => $this->ctx->urls->format('manage-general-overview'), + 'Overview' => $this->urls->format('manage-general-overview'), ], ]; if($globalPerms->check(Perm::G_LOGS_VIEW)) - $menu['General']['Logs'] = $this->ctx->urls->format('manage-general-logs'); + $menu['General']['Logs'] = $this->urls->format('manage-general-logs'); if($globalPerms->check(Perm::G_EMOTES_MANAGE)) - $menu['General']['Emoticons'] = $this->ctx->urls->format('manage-general-emoticons'); + $menu['General']['Emoticons'] = $this->urls->format('manage-general-emoticons'); if($globalPerms->check(Perm::G_CONFIG_MANAGE)) - $menu['General']['Settings'] = $this->ctx->urls->format('manage-general-settings'); + $menu['General']['Settings'] = $this->urls->format('manage-general-settings'); - $userPerms = $this->ctx->authInfo->getPerms('user'); + $userPerms = $this->authInfo->getPerms('user'); if($userPerms->check(Perm::U_USERS_MANAGE)) - $menu['Users & Roles']['Users'] = $this->ctx->urls->format('manage-users'); + $menu['Users & Roles']['Users'] = $this->urls->format('manage-users'); if($userPerms->check(Perm::U_ROLES_MANAGE)) - $menu['Users & Roles']['Roles'] = $this->ctx->urls->format('manage-roles'); + $menu['Users & Roles']['Roles'] = $this->urls->format('manage-roles'); if($userPerms->check(Perm::U_NOTES_MANAGE)) - $menu['Users & Roles']['Notes'] = $this->ctx->urls->format('manage-users-notes'); + $menu['Users & Roles']['Notes'] = $this->urls->format('manage-users-notes'); if($userPerms->check(Perm::U_WARNINGS_MANAGE)) - $menu['Users & Roles']['Warnings'] = $this->ctx->urls->format('manage-users-warnings'); + $menu['Users & Roles']['Warnings'] = $this->urls->format('manage-users-warnings'); if($userPerms->check(Perm::U_BANS_MANAGE)) - $menu['Users & Roles']['Bans'] = $this->ctx->urls->format('manage-users-bans'); + $menu['Users & Roles']['Bans'] = $this->urls->format('manage-users-bans'); if($globalPerms->check(Perm::G_NEWS_POSTS_MANAGE)) - $menu['News']['Posts'] = $this->ctx->urls->format('manage-news-posts'); + $menu['News']['Posts'] = $this->urls->format('manage-news-posts'); if($globalPerms->check(Perm::G_NEWS_CATEGORIES_MANAGE)) - $menu['News']['Categories'] = $this->ctx->urls->format('manage-news-categories'); + $menu['News']['Categories'] = $this->urls->format('manage-news-categories'); if($globalPerms->check(Perm::G_FORUM_CATEGORIES_MANAGE)) - $menu['Forum']['Permission Calculator'] = $this->ctx->urls->format('manage-forum-categories'); + $menu['Forum']['Permission Calculator'] = $this->urls->format('manage-forum-categories'); if($globalPerms->check(Perm::G_FORUM_TOPIC_REDIRS_MANAGE)) - $menu['Forum']['Topic Redirects'] = $this->ctx->urls->format('manage-forum-topic-redirs'); + $menu['Forum']['Topic Redirects'] = $this->urls->format('manage-forum-topic-redirs'); if($globalPerms->check(Perm::G_CL_CHANGES_MANAGE)) - $menu['Changelog']['Changes'] = $this->ctx->urls->format('manage-changelog-changes'); + $menu['Changelog']['Changes'] = $this->urls->format('manage-changelog-changes'); if($globalPerms->check(Perm::G_CL_TAGS_MANAGE)) - $menu['Changelog']['Tags'] = $this->ctx->urls->format('manage-changelog-tags'); + $menu['Changelog']['Tags'] = $this->urls->format('manage-changelog-tags'); return $menu; } diff --git a/src/Templating/TemplatingMetaVariableProvider.php b/src/Templating/TemplatingMetaVariableProvider.php new file mode 100644 index 00000000..c1efec21 --- /dev/null +++ b/src/Templating/TemplatingMetaVariableProvider.php @@ -0,0 +1,7 @@ +<?php +namespace Misuzu\Templating; + +interface TemplatingMetaVariableProvider { + /** @return array<string, string> */ + public function getTemplatingMetaVariables(): array; +} diff --git a/src/Users/Assets/AssetsRoutes.php b/src/Users/Assets/AssetsRoutes.php index 393b5c0b..0df2e89f 100644 --- a/src/Users/Assets/AssetsRoutes.php +++ b/src/Users/Assets/AssetsRoutes.php @@ -4,14 +4,15 @@ namespace Misuzu\Users\Assets; use InvalidArgumentException; use RuntimeException; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\{ExactRoute,PatternRoute}; use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon}; use Misuzu\{Misuzu,Perm}; use Misuzu\Auth\AuthInfo; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\{UsersContext,UserInfo}; +#[HandlerRoles('main')] class AssetsRoutes implements RouteHandler, UrlSource { use RouteHandlerCommon, UrlSourceCommon; diff --git a/src/Users/UsersApiRoutes.php b/src/Users/UsersApiRoutes.php index 70ba2fa0..b2fe0dac 100644 --- a/src/Users/UsersApiRoutes.php +++ b/src/Users/UsersApiRoutes.php @@ -5,16 +5,17 @@ use RuntimeException; use Index\XArray; use Index\Colour\{Colour,ColourRgb}; use Index\Http\{HttpRequest,HttpResponseBuilder}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Processors\Before; use Index\Http\Routing\Routes\ExactRoute; use Index\Urls\UrlRegistry; use Misuzu\{FieldTransformer,SiteInfo}; use Misuzu\Auth\AuthInfo; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; use Misuzu\Users\UserInfo; use Misuzu\Users\Assets\UserAvatarAsset; +#[HandlerRoles('main')] final class UsersApiRoutes implements RouteHandler { use RouteHandlerCommon; diff --git a/src/Users/UsersContext.php b/src/Users/UsersContext.php index 82ffdeb4..74f6c3da 100644 --- a/src/Users/UsersContext.php +++ b/src/Users/UsersContext.php @@ -1,7 +1,10 @@ <?php namespace Misuzu\Users; +use InvalidArgumentException; +use RuntimeException; use Index\Colour\Colour; +use Index\Config\Config; use Index\Db\DbConnection; class UsersContext { @@ -27,7 +30,24 @@ class UsersContext { /** @var array<string, ?BanInfo> */ private array $activeBans = []; - public function __construct(DbConnection $dbConn) { + public string $accountHost { + get => $this->config->getString('accthost'); + } + + public string $newestUserId { + get => $this->config->getString('newest'); + set(string $value) { + if($value === '') + $this->config->removeValues('newest'); + else + $this->config->setString('newest', $value); + } + } + + public function __construct( + DbConnection $dbConn, + private Config $config, + ) { $this->users = new UsersData($dbConn); $this->roles = new RolesData($dbConn); $this->bans = new BansData($dbConn); @@ -94,4 +114,42 @@ class UsersContext { ): bool { return $this->tryGetActiveBan($userInfo, $minimumSeverity) !== null; } + + /** @return string[] */ + public function getAllowedImpersonationIds(UserInfo|string $userInfo): array { + return $this->config->getArray( + sprintf('allow_impersonate.u%s', $userInfo instanceof UserInfo ? $userInfo->id : $userInfo) + ); + } + + /** @param array<UserInfo|string> $users */ + public function setAllowedImpersonationIds(UserInfo|string $userInfo, array $users): void { + foreach($users as $key => $value) + if($value instanceof UserInfo) + $users[$key] = $value->id; + elseif(!is_string($value)) + throw new InvalidArgumentException('$users must be an array of UserInfo instances or id strings'); + + $name = sprintf('allow_impersonate.u%s', $userInfo instanceof UserInfo ? $userInfo->id : $userInfo); + + if(empty($users)) + $this->config->removeValues($name); + else + $this->config->setArray($name, $users); + } + + public function canImpersonateUser(UserInfo|string $actorInfo, UserInfo|string $targetInfo): bool { + if(is_string($actorInfo)) + try { + $actorInfo = $this->getUserInfo($actorInfo); + } catch(RuntimeException $ex) { + return false; + } + + return $actorInfo->super || in_array( + $targetInfo instanceof UserInfo ? $targetInfo->id : $targetInfo, + $this->getAllowedImpersonationIds($actorInfo), + true, + ); + } } diff --git a/src/Users/UsersWebFingerResolver.php b/src/Users/UsersWebFingerResolver.php index 095ec5bf..7f5200ed 100644 --- a/src/Users/UsersWebFingerResolver.php +++ b/src/Users/UsersWebFingerResolver.php @@ -2,7 +2,6 @@ namespace Misuzu\Users; use Exception; -use Index\Config\Config; use Index\Urls\UrlRegistry; use Misuzu\SiteInfo; use Misuzu\WebFinger\{ @@ -15,11 +14,10 @@ class UsersWebFingerResolver implements WebFingerResolver { private UsersContext $usersCtx, private SiteInfo $siteInfo, private UrlRegistry $urls, - private Config $config ) {} public function resolve(string $uri, array $components): ?WebFingerResourceInfo { - $acctHost = $this->config->getString('accthost'); + $acctHost = $this->usersCtx->accountHost; $getValue = null; if(isset($components['scheme']) && isset($components['path'])) { diff --git a/src/WebFinger/WebFingerRoutes.php b/src/WebFinger/WebFingerRoutes.php index 81de66a3..88a054bf 100644 --- a/src/WebFinger/WebFingerRoutes.php +++ b/src/WebFinger/WebFingerRoutes.php @@ -1,17 +1,18 @@ <?php namespace Misuzu\WebFinger; -use Index\XArray; +use Index\{Dependencies,XArray}; use Index\Http\{HttpResponseBuilder,HttpRequest}; -use Index\Http\Routing\{RouteHandler,RouteHandlerCommon}; use Index\Http\Routing\AccessControl\AccessControl; use Index\Http\Routing\Routes\ExactRoute; +use Misuzu\Routing\{HandlerRoles,RouteHandler,RouteHandlerCommon}; +#[HandlerRoles('main')] class WebFingerRoutes implements RouteHandler { use RouteHandlerCommon; public function __construct( - private WebFingerRegistry $registry + private WebFingerRegistry $registry, ) {} /** diff --git a/templates/_layout/footer.twig b/templates/_layout/footer.twig index 37d707d4..74281deb 100644 --- a/templates/_layout/footer.twig +++ b/templates/_layout/footer.twig @@ -12,7 +12,7 @@ <a href="https://patchii.net/flashii/misuzu/src/tag/{{ git_tag }}" target="_blank" rel="noopener" class="footer__link">{{ git_tag }}</a> {% endif %} # <a href="https://patchii.net/flashii/misuzu/commit/{{ git_commit_hash(true) }}" target="_blank" rel="noopener" class="footer__link">{{ git_commit_hash() }}</a> - {% if globals.display_timings_info %} + {% if auth_display_timings() %} / 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 62fe99ce..41bab99f 100644 --- a/templates/_layout/header.twig +++ b/templates/_layout/header.twig @@ -1,14 +1,15 @@ {% from 'macros.twig' import avatar %} {% from '_layout/input.twig' import input_checkbox_raw %} -{% if globals.auth_info.impersonating %} - {% set real_user_info = globals.auth_info.realUserInfo %} +{% if auth_impersonating() %} + {% set real_user_id = auth_get_real_user_info('id') %} + {% set real_user_name = auth_get_real_user_info('name') %} <div class="impersonate"> <div class="impersonate-content"> <div class="impersonate-user"> - You are <a href="{{ url('user-profile', {'user': real_user_info.id}) }}" class="impersonate-user-link"> - <div class="avatar impersonate-user-avatar">{{ avatar(real_user_info.id, 20, real_user_info.name) }}</div> - {{ real_user_info.name }} + You are <a href="{{ url('user-profile', {'user': real_user_id}) }}" class="impersonate-user-link"> + <div class="avatar impersonate-user-avatar">{{ avatar(real_user_id, 20, real_user_name) }}</div> + {{ real_user_name }} </a> </div> <div class="impersonate-options"> @@ -26,8 +27,8 @@ <div class="header__background"></div> <div class="header__desktop"> - <a class="header__desktop__logo" href="{{ url('index') }}" title="{{ globals.site_info.name }}"> - {{ globals.site_info.name }} + <a class="header__desktop__logo" href="{{ url('index') }}" title="{{ site_get_info('name') }}"> + {{ site_get_info('name') }} </a> <div class="header__desktop__menus"> @@ -56,10 +57,11 @@ </a> {% endfor %} - {% if globals.auth_info.loggedIn %} - {% set user_info = globals.auth_info.userInfo %} - <a href="{{ url('user-profile', {'user': user_info.id}) }}" class="avatar header__desktop__user__avatar" title="{{ user_info.name }}"> - {{ avatar(user_info.id, 60, user_info.name) }} + {% if auth_logged_in() %} + {% set header_user_id = auth_get_user_info('id') %} + {% set header_user_name = auth_get_user_info('name') %} + <a href="{{ url('user-profile', {'user': header_user_id}) }}" class="avatar header__desktop__user__avatar" title="{{ header_user_name }}"> + {{ avatar(header_user_id, 60, header_user_name) }} </a> {% else %} <a href="{{ url('auth-login') }}" class="avatar header__desktop__user__avatar"> @@ -76,13 +78,12 @@ </label> <a class="header__mobile__logo header__mobile__icon" href="{{ url('index') }}"> - {{ globals.site_info.name }} + {{ site_get_info('name') }} </a> <label class="header__mobile__icon header__mobile__avatar" for="toggle-mobile-header"> - {% if globals.auth_info.loggedIn %} - {% set user_info = globals.auth_info.userInfo %} - {{ avatar(user_info.id, 40, user_info.name) }} + {% if auth_logged_in() %} + {{ avatar(auth_get_user_info('id'), 40, auth_get_user_info('name')) }} {% else %} {{ avatar(0, 40, 'Log in') }} {% endif %} diff --git a/templates/_layout/meta.twig b/templates/_layout/meta.twig index f25d28f0..9225cf4c 100644 --- a/templates/_layout/meta.twig +++ b/templates/_layout/meta.twig @@ -1,27 +1,29 @@ {% apply spaceless %} - {% set description = description|default(globals.site_info.description) %} + {% set site_name = site_get_info('name') %} + {% set site_description = description|default(site_get_info('desc')) %} + {% set site_url = site_get_info('url') %} {% if title is defined %} - {% set browser_title = title ~ ' :: ' ~ globals.site_info.name %} + {% set browser_title = title ~ ' :: ' ~ site_name %} {% else %} - {% set browser_title = globals.site_info.name %} + {% set browser_title = site_name %} {% endif %} <title>{{ browser_title }}</title> - <meta property="og:title" content="{{ title|default(globals.site_info.name) }}"> - <meta property="og:site_name" content="{{ globals.site_info.name }}"> + <meta property="og:title" content="{{ title|default(site_name) }}"> + <meta property="og:site_name" content="{{ site_name }}"> - {% if description|length > 0 %} - <meta name="description" content="{{ description }}"> - <meta property="og:description" content="{{ description }}"> + {% if site_description|length > 0 %} + <meta name="description" content="{{ site_description }}"> + <meta property="og:description" content="{{ site_description }}"> {% endif %} <meta property="og:type" content="object"> {% if image is defined %} {% if image|slice(0, 1) == '/' %} - {% set image = globals.site_info.url is not empty ? (globals.site_info.url ~ image) : '' %} + {% set image = site_url ~ image %} {% endif %} {% if image|length > 0 %} @@ -31,7 +33,7 @@ {% if canonical_url is defined %} {% if canonical_url|slice(0, 1) == '/' %} - {% set canonical_url = globals.site_info.url is not empty ? (globals.site_info.url ~ canonical_url) : '' %} + {% set canonical_url = site_url ~ canonical_url %} {% endif %} {% if canonical_url|length > 0 %} diff --git a/templates/errors/master.twig b/templates/errors/master.twig index 7d1de4b6..2314deb4 100644 --- a/templates/errors/master.twig +++ b/templates/errors/master.twig @@ -2,7 +2,7 @@ {% from 'macros.twig' import container_title %} {% set error_string = '%03d %s'|format(error_code, error_text) %} -{% set html_title = error_string ~ ' :: ' ~ globals.site_info.name %} +{% set html_title = error_string ~ ' :: ' ~ site_get_info('name') %} {% block html_head %} <meta name="description" content="{{ error_blerb }}"> @@ -20,15 +20,15 @@ <div class="error-wrapper"> <div class="error-container"> <nav class="error-top"> - <div class="error-logo"><a href="{{ globals.site_info.url }}"><img src="/images/logos/imouto-default.png" alt=""></a></div> - <div class="error-home"><a href="{{ globals.site_info.url }}">{{ globals.site_info.name }}</a></div> + <div class="error-logo"><a href="{{ site_get_info('url') }}"><img src="/images/logos/imouto-default.png" alt=""></a></div> + <div class="error-home"><a href="{{ site_get_info('url') }}">{{ site_get_info('name') }}</a></div> {% if error_code >= 500 %} <div class="error-nav"> - {% if globals.site_info.email is not empty %} - <a href="mailto:{{ globals.site_info.email }}" rel="noopener" target="_blank">E-mail</a> + {% if site_has_info('email') %} + <a href="mailto:{{ site_get_info('email') }}" rel="noopener" target="_blank">E-mail</a> {% endif %} - {% if globals.site_info.bsky is not empty %} - <a href="{{ globals.site_info.bsky }}" rel="noopener" target="_blank">Bluesky</a> + {% if site_has_info('bsky') %} + <a href="{{ site_get_info('bsky') }}" rel="noopener" target="_blank">Bluesky</a> {% endif %} </div> {% endif %} diff --git a/templates/home/landing.twig b/templates/home/landing.twig index 59908cad..6142cfb6 100644 --- a/templates/home/landing.twig +++ b/templates/home/landing.twig @@ -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_info.name }}"> + <img src="/images/landing-logo.png" alt="{{ site_get_info('name') }}"> </a> </div> @@ -89,7 +89,7 @@ {% endif %} # <a href="https://github.com/flashwave/misuzu/commit/{{ git_commit_hash(true) }}" target="_blank" rel="noreferrer noopener">{{ git_commit_hash() }}</a> </div> -{% if globals.display_timings_info %} +{% if auth_display_timings() %} <div class="landingv2-footer-copyright-line"> {{ sql_query_count()|number_format }} queries / {{ (startup_time() - startup_time(constant('MSZ_TPL_RENDER')))|number_format(5) }} load / {{ startup_time(constant('MSZ_TPL_RENDER'))|number_format(5) }} template / {{ startup_time()|number_format(5) }} total </div> diff --git a/templates/master.twig b/templates/master.twig index d3d4778f..73f4c1a9 100644 --- a/templates/master.twig +++ b/templates/master.twig @@ -5,7 +5,7 @@ <link href="/vendor/fontawesome/css/all.min.css" rel="stylesheet"> <link href="{{ asset('common.css') }}" rel="stylesheet"> <link href="{{ asset('misuzu.css') }}" rel="stylesheet"> - {% for name, value in globals.meta %} + {% for name, value in get_meta_vars() %} {% if value is not empty %}<meta name="msz-{{ name }}" content="{{ value }}">{% endif %} {% endfor %} {% if main_css_vars is defined and main_css_vars is iterable and main_css_vars is not empty %} @@ -36,12 +36,13 @@ </div> </noscript> - {% if globals.active_ban_info is not null %} + {% set active_ban_info = auth_get_ban_info() %} + {% if active_ban_info is not null %} <div class="warning warning--red"> <div class="warning__content"> - <p>You have been banned {% if globals.active_ban_info.permanent %}<strong>permanently</strong>{% else %}for <strong title="{{ globals.active_ban_info.expiresTime|date('r') }}">{{ globals.active_ban_info.remainingString }}</strong>{% endif %} since <strong><time datetime="{{ globals.active_ban_info.createdTime|date('c') }}" title="{{ globals.active_ban_info.createdTime|date('r') }}">{{ globals.active_ban_info.createdTime|time_format }}</time></strong>.</p> - {% if globals.active_ban_info.publicReason is not empty %} - <p>Reason: {{ globals.active_ban_info.publicReason }}</p> + <p>You have been banned {% if active_ban_info.permanent %}<strong>permanently</strong>{% else %}for <strong title="{{ active_ban_info.expiresTime|date('r') }}">{{ active_ban_info.remainingString }}</strong>{% endif %} since <strong><time datetime="{{ active_ban_info.createdTime|date('c') }}" title="{{ active_ban_info.createdTime|date('r') }}">{{ active_ban_info.createdTime|time_format }}</time></strong>.</p> + {% if active_ban_info.publicReason is not empty %} + <p>Reason: {{ active_ban_info.publicReason }}</p> {% endif %} </div> </div> diff --git a/templates/oauth2/master.twig b/templates/oauth2/master.twig index 342c7a93..20735f5d 100644 --- a/templates/oauth2/master.twig +++ b/templates/oauth2/master.twig @@ -1,6 +1,6 @@ {% extends 'html.twig' %} -{% set html_title = (title is defined ? (title ~ ' :: ') : '') ~ globals.site_info.name %} +{% set html_title = (title is defined ? (title ~ ' :: ') : '') ~ site_get_info('name') %} {% block html_head %} <link href="{{ asset('common.css') }}" rel="stylesheet"> @@ -27,7 +27,7 @@ <div class="oauth2-body"> <div class="oauth2-banner"> <div class="oauth2-banner-text">{{ body_title|default('') }}</div> - <div class="oauth2-banner-logo">{{ globals.site_info.name }}</div> + <div class="oauth2-banner-logo">{{ site_get_info('name') }}</div> </div> {% block body_content %}{% endblock %} diff --git a/templates/redirects/landing.twig b/templates/redirects/landing.twig index 886a408c..9165ed3d 100644 --- a/templates/redirects/landing.twig +++ b/templates/redirects/landing.twig @@ -1,6 +1,6 @@ {% extends 'redirects/master.twig' %} -{% set html_title = globals.site_info.name ~ ' Redirect Service' %} +{% set html_title = site_get_info('name') ~ ' Redirect Service' %} {% block content %} <div class="redir-landing"> @@ -8,7 +8,7 @@ <article class="redir-landing-body"> <p><div class="redir-landing-logo"></div></p> <h1>{{ html_title }}</h1> - <p>Short URL Service for <a href="{{ globals.site_info.url }}" rel="noopener">{{ globals.site_info.name }}</a></p> + <p>Short URL Service for <a href="{{ site_get_info('url') }}/" rel="noopener">{{ site_get_info('name') }}</a></p> </article> <footer class="redir-landing-footer"> <p> diff --git a/tools/render-tpl b/tools/render-tpl index 9207b77a..20e04aa4 100755 --- a/tools/render-tpl +++ b/tools/render-tpl @@ -77,10 +77,11 @@ handleValue: $hostName ??= 'localhost'; // this should really not be necessary, mostly done to make sure the url registry is available -$msz->createRouting(new HttpRequest('1.1', ['Host' => [$hostName]], NullStream::instance(), [], 'GET', HttpUri::createUri('/'), [], [])); -$msz->startTemplating(false); +$msz->registerRequestRoutes( + new HttpRequest('1.1', ['Host' => [$hostName]], NullStream::instance(), [], 'GET', HttpUri::createUri('/'), [], []) +); -$ctx = $msz->templating->load(implode(' ', array_slice($argv, $pathIndex))); +$ctx = $msz->tplCtx->loadTemplate(implode(' ', array_slice($argv, $pathIndex))); foreach($tplArgs as $name => $value) $ctx->setVar($name, $value);