Improved templating engine wrapping.

This commit is contained in:
flash 2025-04-03 14:37:19 +00:00
parent e4c3e4c052
commit ceb6bece09
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
86 changed files with 784 additions and 515 deletions
.env.examplecomposer.jsoncomposer.lockmisuzu.phppackage-lock.json
public-legacy
public
src
templates
tools

View file

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

View file

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

54
composer.lock generated
View file

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

View file

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

142
package-lock.json generated
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
<?php
namespace Misuzu;
namespace Misuzu\Routing;
use Index\XArray;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,7 @@
<?php
namespace Misuzu\Templating;
interface TemplatingMetaVariableProvider {
/** @return array<string, string> */
public function getTemplatingMetaVariables(): array;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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