From 2f7cddde19a6eb5faaab042fff44e710fb78ee79 Mon Sep 17 00:00:00 2001 From: flashwave Date: Tue, 18 Jul 2023 21:48:44 +0000 Subject: [PATCH] Config class overhaul. --- assets/misuzu.css/manage/settings.css | 7 +- misuzu.php | 62 ++-- public/auth/login.php | 26 +- public/auth/password.php | 5 +- public/forum/leaderboard.php | 16 +- public/manage/general/setting-delete.php | 39 +-- public/manage/general/setting.php | 143 ++++---- public/manage/general/settings.php | 19 +- public/settings/account.php | 4 +- src/Config.php | 32 -- src/Config/CfgTools.php | 32 -- src/Config/DbConfig.php | 327 ++++++++++++++++--- src/Config/DbConfigValueInfo.php | 92 ++++++ src/Config/IConfig.php | 38 ++- src/Config/IConfigValueInfo.php | 22 ++ src/Config/ScopedConfig.php | 122 +++++-- src/Config/ScopedConfigValueInfo.php | 72 ++++ src/Forum/forum.php | 2 +- src/Forum/topic.php | 2 +- src/Http/Handlers/ChangelogHandler.php | 7 +- src/Http/Handlers/HomeHandler.php | 27 +- src/Http/Handlers/NewsHandler.php | 5 +- src/Mailer.php | 48 +-- src/SharpChat/SharpChatRoutes.php | 8 +- src/Users/Assets/UserAvatarAsset.php | 8 +- src/Users/Assets/UserBackgroundAsset.php | 11 +- src/Users/Assets/UserImageAsset.php | 4 +- templates/manage/general/setting-delete.twig | 12 +- templates/manage/general/setting.twig | 32 +- templates/manage/general/settings.twig | 10 +- 30 files changed, 838 insertions(+), 396 deletions(-) delete mode 100644 src/Config.php delete mode 100644 src/Config/CfgTools.php create mode 100644 src/Config/DbConfigValueInfo.php create mode 100644 src/Config/IConfigValueInfo.php create mode 100644 src/Config/ScopedConfigValueInfo.php diff --git a/assets/misuzu.css/manage/settings.css b/assets/misuzu.css/manage/settings.css index 4a0a4e8..19e3bc4 100644 --- a/assets/misuzu.css/manage/settings.css +++ b/assets/misuzu.css/manage/settings.css @@ -36,10 +36,13 @@ .manage-list-setting-type--string { --type-colour: #ef8323; } -.manage-list-setting-type--integer { +.manage-list-setting-type--int { --type-colour: #8c90bc; } -.manage-list-setting-type--boolean { +.manage-list-setting-type--float { + --type-colour: #cb09c8; +} +.manage-list-setting-type--bool { --type-colour: #77b34c; } .manage-list-setting-type--array { diff --git a/misuzu.php b/misuzu.php index 6896727..39cd6e9 100644 --- a/misuzu.php +++ b/misuzu.php @@ -5,7 +5,6 @@ use Index\Autoloader; use Index\Environment; use Index\Data\ConnectionFailedException; use Index\Data\DbTools; -use Misuzu\Config\IConfig; use Misuzu\Config\DbConfig; use Misuzu\Users\User; use Misuzu\Users\UserNotFoundException; @@ -47,7 +46,7 @@ set_exception_handler(function(\Throwable $ex) { header('Content-Type: text/plain; charset=utf-8'); echo (string)$ex; } else { - header('Content-Type: text/html; charset-utf-8'); + header('Content-Type: text/html; charset=utf-8'); echo file_get_contents(MSZ_TEMPLATES . '/500.html'); } } @@ -83,24 +82,8 @@ DB::init(DbTools::parse($dbConfig['dsn'])); DB::exec(MSZ_DB_INIT); $cfg = new DbConfig($db); -$cfg->reload(); -Config::init($cfg); - -Mailer::init($cfg->getValue('mail.method', IConfig::T_STR), [ - 'host' => $cfg->getValue('mail.host', IConfig::T_STR), - 'port' => $cfg->getValue('mail.port', IConfig::T_INT, 25), - 'username' => $cfg->getValue('mail.username', IConfig::T_STR), - 'password' => $cfg->getValue('mail.password', IConfig::T_STR), - 'encryption' => $cfg->getValue('mail.encryption', IConfig::T_STR), - 'sender_name' => $cfg->getValue('mail.sender.name', IConfig::T_STR), - 'sender_addr' => $cfg->getValue('mail.sender.address', IConfig::T_STR), -]); - -// replace this with a better storage mechanism -define('MSZ_STORAGE', $cfg->getValue('storage.path', IConfig::T_STR, MSZ_ROOT . '/store')); -if(!is_dir(MSZ_STORAGE)) - mkdir(MSZ_STORAGE, 0775, true); +Mailer::init($cfg->scopeTo('mail')); $msz = new MisuzuContext($db, $cfg); @@ -115,17 +98,12 @@ ob_start(); if(file_exists(MSZ_ROOT . '/.migrating')) { http_response_code(503); if(!isset($_GET['_check'])) { - header('Content-Type: text/html; charset-utf-8'); + header('Content-Type: text/html; charset=utf-8'); echo file_get_contents(MSZ_TEMPLATES . '/503.html'); } exit; } -if(!is_readable(MSZ_STORAGE) || !is_writable(MSZ_STORAGE)) { - echo 'Cannot access storage directory.'; - exit; -} - if(!MSZ_DEBUG) { $twigCacheDirSfx = GitInfo::hash(true); if(empty($twigCacheDirSfx)) @@ -136,16 +114,27 @@ if(!MSZ_DEBUG) { mkdir($twigCache, 0775, true); } +$globals = $cfg->getValues([ + ['site.name:s', 'Misuzu'], + 'site.desc:s', + 'site.url:s', + 'sockChat.chatPath.normal:s', + 'eeprom.path:s', + 'eeprom.app:s', + ['auth.secret:s', 'meow'], + ['csrf.secret:s', 'soup'], +]); + Template::init($msz, $twigCache ?? null, MSZ_DEBUG); Template::set('globals', [ - 'site_name' => $cfg->getValue('site.name', IConfig::T_STR, 'Misuzu'), - 'site_description' => $cfg->getValue('site.desc', IConfig::T_STR), - 'site_url' => $cfg->getValue('site.url', IConfig::T_STR), - 'site_chat' => $cfg->getValue('sockChat.chatPath.normal', IConfig::T_STR), + 'site_name' => $globals['site.name'], + 'site_description' => $globals['site.desc'], + 'site_url' => $globals['site.url'], + 'site_chat' => $globals['sockChat.chatPath.normal'], 'eeprom' => [ - 'path' => $cfg->getValue('eeprom.path', IConfig::T_STR), - 'app' => $cfg->getValue('eeprom.app', IConfig::T_STR), + 'path' => $globals['eeprom.path'], + 'app' => $globals['eeprom.app'], ], ]); @@ -156,7 +145,7 @@ unset($mszAssetsInfo); Template::addPath(MSZ_TEMPLATES); -AuthToken::setSecretKey($cfg->getValue('auth.secret', IConfig::T_STR, 'meow')); +AuthToken::setSecretKey($globals['auth.secret']); if(isset($_COOKIE['msz_uid']) && isset($_COOKIE['msz_sid'])) { $authToken = new AuthToken; @@ -220,22 +209,21 @@ if($authToken->isValid()) { } CSRF::init( - $cfg->getValue('csrf.secret', IConfig::T_STR, 'soup'), + $globals['csrf.secret'], (UserSession::hasCurrent() ? UserSession::getCurrent()->getToken() : ($_SERVER['REMOTE_ADDR'] ?? '::1')) ); function mszLockdown(): void { global $misuzuBypassLockdown, $cfg; - if($cfg->getValue('private.enabled', IConfig::T_BOOL)) { + if($cfg->getBoolean('private.enabled')) { $onLoginPage = $_SERVER['PHP_SELF'] === url('auth-login'); $onPasswordPage = parse_url($_SERVER['PHP_SELF'], PHP_URL_PATH) === url('auth-forgot'); $misuzuBypassLockdown = !empty($misuzuBypassLockdown) || $onLoginPage; if(!$misuzuBypassLockdown) { if(UserSession::hasCurrent()) { - $privatePermCat = $cfg->getValue('private.perm.cat', IConfig::T_STR); - $privatePermVal = $cfg->getValue('private.perm.val', IConfig::T_INT); + ['private.perm.cat' => $privatePermCat, 'private.perm.val' => $privatePermVal] = $cfg->getValues(['private.perm.cat:s', 'private.perm.val:i']); if(!empty($privatePermCat) && $privatePermVal > 0) { if(!perms_check_user($privatePermCat, User::getCurrent()->getId(), $privatePermVal)) { @@ -244,7 +232,7 @@ function mszLockdown(): void { User::unsetCurrent(); } } - } elseif(!$onLoginPage && !($onPasswordPage && $cfg->getValue('private.allow_password_reset', IConfig::T_BOOL, true))) { + } elseif(!$onLoginPage && !($onPasswordPage && $cfg->getBoolean('private.allow_password_reset', true))) { url_redirect('auth-login'); exit; } diff --git a/public/auth/login.php b/public/auth/login.php index 25278bc..a1f8161 100644 --- a/public/auth/login.php +++ b/public/auth/login.php @@ -2,7 +2,6 @@ namespace Misuzu; use Misuzu\AuthToken; -use Misuzu\Config\IConfig; use Misuzu\Users\User; use Misuzu\Users\UserNotFoundException; use Misuzu\Users\UserAuthSession; @@ -43,11 +42,28 @@ if(!empty($_GET['resolve'])) { $notices = []; $ipAddress = $_SERVER['REMOTE_ADDR']; $countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX'; -$siteIsPrivate = $cfg->getValue('private.enable', IConfig::T_BOOL); -$loginPermCat = $siteIsPrivate ? $cfg->getValue('private.perm.cat', IConfig::T_STR) : ''; -$loginPermVal = $siteIsPrivate ? $cfg->getValue('private.perm.val', IConfig::T_INT) : 0; $remainingAttempts = UserLoginAttempt::remaining($ipAddress); +$siteIsPrivate = $cfg->getBoolean('private.enable'); +if($siteIsPrivate) { + [ + 'private.perm.cat' => $loginPermCat, + 'private.perm.val' => $loginPermVal, + 'private.msg' => $sitePrivateMessage, + 'private.allow_password_reset' => $canResetPassword, + ] = $cfg->getValues([ + 'private.perm.cat:s', + 'private.perm.val:i', + 'private.msg:s', + ['private.allow_password_reset:b', true], + ]); +} else { + $loginPermCat = ''; + $loginPermVal = 0; + $sitePrivateMessage = ''; + $canResetPassword = true; +} + while(!empty($_POST['login']) && is_array($_POST['login'])) { if(!CSRF::validateRequest()) { $notices[] = 'Was unable to verify the request, please try again!'; @@ -134,8 +150,6 @@ $loginUsername = !empty($_POST['login']['username']) && is_string($_POST['login' !empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : '' ); $loginRedirect = $welcomeMode ? url('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? url('index'); -$sitePrivateMessage = $siteIsPrivate ? $cfg->getValue('private.msg', IConfig::T_STR) : ''; -$canResetPassword = $siteIsPrivate ? $cfg->getValue('private.allow_password_reset', IConfig::T_BOOL, true) : true; $canRegisterAccount = !$siteIsPrivate; Template::render('auth.login', [ diff --git a/public/auth/password.php b/public/auth/password.php index e7dac88..5f6d1ae 100644 --- a/public/auth/password.php +++ b/public/auth/password.php @@ -1,7 +1,6 @@ 0) $notices = []; $ipAddress = $_SERVER['REMOTE_ADDR']; -$siteIsPrivate = $cfg->getValue('private.enable', IConfig::T_BOOL); -$canResetPassword = $siteIsPrivate ? $cfg->getValue('private.allow_password_reset', IConfig::T_BOOL, true) : true; +$siteIsPrivate = $cfg->getBoolean('private.enable'); +$canResetPassword = $siteIsPrivate ? $cfg->getBoolean('private.allow_password_reset', true) : true; $remainingAttempts = UserLoginAttempt::remaining($ipAddress); while($canResetPassword) { diff --git a/public/forum/leaderboard.php b/public/forum/leaderboard.php index dc6951f..6da7515 100644 --- a/public/forum/leaderboard.php +++ b/public/forum/leaderboard.php @@ -1,7 +1,6 @@ getValue('forum_leader.unranked.forum', IConfig::T_ARR); -$unrankedTopics = !empty($_GET['allow_unranked']) ? [] : $cfg->getValue('forum_leader.unranked.topic', IConfig::T_ARR); +if(empty($_GET['allow_unranked'])) { + [ + 'forum_leader.unranked.forum' => $unrankedForums, + 'forum_leader.unranked.topic' => $unrankedTopics, + ] = $cfg->getValues([ + 'forum_leader.unranked.forum:a', + 'forum_leader.unranked.topic:a', + ]); +} else $unrankedForums = $unrankedTopics = []; + $leaderboards = forum_leaderboard_categories(); $leaderboard = forum_leaderboard_listing($leaderboardYear, $leaderboardMonth, $unrankedForums, $unrankedTopics); @@ -31,9 +38,8 @@ $leaderboardName = 'All Time'; if($leaderboardYear) { $leaderboardName = "Leaderboard {$leaderboardYear}"; - if($leaderboardMonth) { + if($leaderboardMonth) $leaderboardName .= "-{$leaderboardMonth}"; - } } if($leaderboardMode === 'markdown') { diff --git a/public/manage/general/setting-delete.php b/public/manage/general/setting-delete.php index ae40483..a47ea5a 100644 --- a/public/manage/general/setting-delete.php +++ b/public/manage/general/setting-delete.php @@ -1,7 +1,6 @@ hasValue($sName)) - throw new \Exception("Config value does not exist."); - -if($_SERVER['REQUEST_METHOD'] === 'POST') { - if(!CSRF::validateRequest()) - throw new \Exception("Request verification failed."); - - $msz->createAuditLog('CONFIG_DELETE', [$sName]); - $cfg->removeValue($sName); - url_redirect('manage-general-settings'); -} else { - $sValue = $cfg->getValue($sName); - - Template::render('manage.general.setting-delete', [ - 'conf_var' => [ - 'name' => $sName, - 'type' => CfgTools::type($sValue), - 'value' => $sValue, - ], - ]); +$valueName = (string)filter_input(INPUT_GET, 'name'); +$valueInfo = $cfg->getValueInfo($valueName); +if($valueInfo === null) { + echo render_error(404); + return; } + +if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { + $valueName = $valueInfo->getName(); + $msz->createAuditLog('CONFIG_DELETE', [$valueName]); + $cfg->removeValues($valueName); + url_redirect('manage-general-settings'); + return; +} + +Template::render('manage.general.setting-delete', [ + 'config_value' => $valueInfo, +]); diff --git a/public/manage/general/setting.php b/public/manage/general/setting.php index 65c4fc0..3ce6a8c 100644 --- a/public/manage/general/setting.php +++ b/public/manage/general/setting.php @@ -1,9 +1,7 @@ '', - 'type' => '', - 'value' => null, - 'new' => true, -]; - +$isNew = true; $sName = (string)filter_input(INPUT_GET, 'name'); +$sType = (string)filter_input(INPUT_GET, 'type'); +$sValue = null; +$loadValueInfo = fn() => $cfg->getValueInfo($sName); if(!empty($sName)) { - if(!CfgTools::validateName($sName)) - throw new \Exception("Config key name has invalid format."); - - $sVar['name'] = $sName; + $sInfo = $loadValueInfo(); + if($sInfo !== null) { + $isNew = false; + $sName = $sInfo->getName(); + $sType = $sInfo->getType(); + $sValue = $sInfo->getValue(); + } } -$sType = (string)filter_input(INPUT_GET, 'type'); -if(!empty($sType)) { - if(!CfgTools::isValidType($sType)) - throw new \Exception("Specified type is invalid."); +while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) { + if($isNew) { + $sName = trim((string)filter_input(INPUT_POST, 'conf_name')); + if(!DbConfig::validateName($sName)) { + echo 'Name contains invalid characters.'; + break; + } - $sVar['type'] = $sType; - $sVar['value'] = CfgTools::default($sType); -} - -if($_SERVER['REQUEST_METHOD'] === 'POST') { - if(!CSRF::validateRequest()) - throw new \Exception("Request verification failed."); - - if(empty($sName)) { - $sName = (string)filter_input(INPUT_POST, 'conf_name'); - - if(empty($sName) || !CfgTools::validateName($sName)) - throw new \Exception("Config key name has invalid format."); - - $sVar['name'] = $sName; + $sType = trim((string)filter_input(INPUT_POST, 'conf_type')); + if(!in_array($sType, ['string', 'int', 'float', 'bool', 'array'])) { + echo 'Invalid type specified.'; + break; + } } - $sLogAction = 'CONFIG_CREATE'; - - if($cfg->hasValue($sName)) { - $sType = CfgTools::type($cfg->getValue($sName)); - $sVar['new'] = false; - $sLogAction = 'CONFIG_UPDATE'; - } elseif(empty($sType)) { - $sType = (string)filter_input(INPUT_POST, 'conf_type'); - if(empty($sType) || !CfgTools::isValidType($sType)) - throw new \Exception("Specified type is invalid."); - } - - $sVar['type'] = $sType; - - $sValue = CfgTools::default($sType); - if($sType === 'array') { - if(!empty($_POST['conf_value']) && is_array($_POST['conf_value'])) { - foreach($_POST['conf_value'] as $fv) { - $fv = (string)$fv; - - if(str_starts_with($fv, 's:')) { - $fv = substr($fv, 2); - } elseif(str_starts_with($fv, 'i:')) { - $fv = (int)substr($fv, 2); - } elseif(str_starts_with($fv, 'b:')) { - $fv = strtolower(substr($fv, 2)); - $fv = $fv !== 'false' && $fv !== '0' && $fv !== ''; - } - - $sValue[] = $fv; - } + $applyFunc = $cfg->setArray(...); + $sValue = []; + $sRaw = filter_input(INPUT_POST, 'conf_value', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY); + foreach($sRaw as $rValue) { + if(strpos($rValue, ':') === 1) { + $rType = $rValue[0]; + $rValue = substr($rValue, 2); + $sValue[] = match($rType) { + 's' => $rValue, + 'i' => (int)$rValue, + 'd' => (float)$rValue, + 'f' => (float)$rValue, + 'b' => $rValue !== 'false' && $rValue !== '0' && $rValue !== '', + default => '', + }; + } else + $sValue[] = $rValue; } - } elseif($sType === 'boolean') { + } elseif($sType === 'bool') { $sValue = !empty($_POST['conf_value']); + $applyFunc = $cfg->setBoolean(...); } else { - $sValue = (string)filter_input(INPUT_POST, 'conf_value'); - if($sType === 'integer') + $sValue = filter_input(INPUT_POST, 'conf_value'); + if($sType === 'int') { + $applyFunc = $cfg->setInteger(...); $sValue = (int)$sValue; + } elseif($sType === 'float') { + $applyFunc = $cfg->setFloat(...); + $sValue = (float)$sValue; + } else + $applyFunc = $cfg->setString(...); } - $sVar['value'] = $sValue; - - $msz->createAuditLog($sLogAction, [$sName]); - $cfg->setValue($sName, $sValue); + $msz->createAuditLog($isNew ? 'CONFIG_CREATE' : 'CONFIG_UPDATE', [$sName]); + $applyFunc($sName, $sValue); url_redirect('manage-general-settings'); return; } -if($cfg->hasValue($sName)) { - $sVar['new'] = false; - $sValue = $cfg->getValue($sName); - $sVar['type'] = $sType = CfgTools::type($sValue); - - if($sType === IConfig::T_ARR) - foreach($sValue as $fk => $fv) - $sValue[$fk] = ['integer' => 'i', 'string' => 's', 'boolean' => 'b'][gettype($fv)] . ':' . $fv; - - $sVar['value'] = $sValue; -} +if($sType === 'array' && !empty($sValue)) + foreach($sValue as $key => $value) + $sValue[$key] = gettype($value)[0] . ':' . $value; Template::render('manage.general.setting', [ - 'conf_var' => $sVar, + 'config_new' => $isNew, + 'config_name' => $sName, + 'config_type' => $sType, + 'config_value' => $sValue, ]); diff --git a/public/manage/general/settings.php b/public/manage/general/settings.php index 50e7a5c..050323d 100644 --- a/public/manage/general/settings.php +++ b/public/manage/general/settings.php @@ -1,9 +1,6 @@ getValue('settings.hidden', IConfig::T_ARR, []); - -$vars = []; -foreach($cfg->getNames() as $key) { - $var = $cfg->getValue($key); - $vars[] = [ - 'key' => $key, - 'type' => CfgTools::type($var), - 'value' => in_array($key, $hidden) ? '*** hidden ***' : json_encode($var), - ]; -} +$hidden = $cfg->getArray('settings.hidden'); +$vars = $cfg->getAllValueInfos(); Template::render('manage.general.settings', [ - 'conf_vars' => $vars, + 'config_vars' => $vars, + 'config_hidden' => $hidden, ]); diff --git a/public/settings/account.php b/public/settings/account.php index 56656e8..2c6f427 100644 --- a/public/settings/account.php +++ b/public/settings/account.php @@ -1,8 +1,6 @@ hasTOTP() !== (bool)$_POST['tfa']['enable']) { if((bool)$_POST['tfa']['enable']) { $tfaKey = TOTP::generateKey(); - $tfaIssuer = $cfg->getValue('site.name', IConfig::T_STR, 'Misuzu'); + $tfaIssuer = $cfg->getString('site.name', 'Misuzu'); $tfaQrcode = (new QRCode(new QROptions([ 'version' => 5, 'outputType' => QRCode::OUTPUT_IMAGE_JPG, diff --git a/src/Config.php b/src/Config.php deleted file mode 100644 index 1f75986..0000000 --- a/src/Config.php +++ /dev/null @@ -1,32 +0,0 @@ -getNames(); - } - - public static function get(string $key, string $type = IConfig::T_ANY, $default = null): mixed { - return self::$config->getValue($key, $type, $default); - } - - public static function has(string $key): bool { - return self::$config->hasValue($key); - } - - public static function set(string $key, $value, bool $soft = false): void { - self::$config->setValue($key, $value, !$soft); - } - - public static function remove(string $key, bool $soft = false): void { - self::$config->removeValue($key, !$soft); - } -} diff --git a/src/Config/CfgTools.php b/src/Config/CfgTools.php deleted file mode 100644 index a84d3f1..0000000 --- a/src/Config/CfgTools.php +++ /dev/null @@ -1,32 +0,0 @@ - null, - IConfig::T_STR => '', - IConfig::T_INT => 0, - IConfig::T_BOOL => false, - IConfig::T_ARR => [], - ]; - - public static function default(string $type) { - return self::DEFAULTS[$type] ?? null; - } -} diff --git a/src/Config/DbConfig.php b/src/Config/DbConfig.php index 163d37d..40d228e 100644 --- a/src/Config/DbConfig.php +++ b/src/Config/DbConfig.php @@ -1,78 +1,327 @@ dbConn = $dbConn; + $this->cache = new DbStatementCache($dbConn); } - public function reload(): void { - $this->values = []; + public static function validateName(string $name): bool { + // this should better validate the format, this allows for a lot of shittery + return preg_match('#^([a-z][a-zA-Z0-9._]+)$#', $name) === 1; + } - try { - $result = $this->dbConn->query('SELECT config_name, config_value FROM msz_config;'); - while($result->next()) - $this->values[$result->getString(0)] = unserialize($result->getString(1)); - } catch(DataException $ex) { } + private static function whereInList(int $count): string { + return implode(', ', array_fill(0, $count, '?')); + } + + public function reset(): void { + $this->values = []; + } + + public function unload(string|array $names): void { + if(empty($names)) + return; + if(is_string($names)) + $names = [$names]; + + foreach($names as $name) + unset($this->values[$name]); } public function scopeTo(string $prefix): IConfig { return new ScopedConfig($this, $prefix); } - public function getNames(): array { - return array_keys($this->values); + public function hasValues(string|array $names): bool { + if(empty($names)) + return true; + if(is_string($names)) + $names = [$names]; + + $cachedNames = array_keys($this->values); + $names = array_diff($names, $cachedNames); + + if(!empty($names)) { + // array_diff preserves keys, the for() later would fuck up without it + $names = array_values($names); + $nameCount = count($names); + + $stmt = $this->cache->get(sprintf( + 'SELECT COUNT(*) FROM msz_config WHERE config_name IN (%s)', + self::whereInList($nameCount) + )); + for($i = 0; $i < $nameCount; ++$i) + $stmt->addParameter($i + 1, $names[$i]); + $stmt->execute(); + + $result = $stmt->getResult(); + if($result->next()) + return $result->getInteger(0) >= $nameCount; + } + + return true; } - public function getValue(string $name, string $type = IConfig::T_ANY, $default = null): mixed { - $value = $this->values[$name] ?? null; + public function removeValues(string|array $names): void { + if(empty($names)) + return; + if(is_string($names)) + $names = [$names]; - if($type !== IConfig::T_ANY && CfgTools::type($value) !== $type) - $value = null; + foreach($names as $name) + unset($this->values[$name]); - return $value ?? $default ?? CfgTools::default($type); + $nameCount = count($names); + $stmt = $this->cache->get(sprintf( + 'DELETE FROM msz_config WHERE config_name IN (%s)', + self::whereInList($nameCount) + )); + + for($i = 0; $i < $nameCount; ++$i) + $stmt->addParameter($i + 1, $names[$i]); + + $stmt->execute(); } - public function hasValue(string $name): bool { - return array_key_exists($name, $this->values); + public function getAllValueInfos(?Pagination $pagination = null): array { + $this->reset(); + $infos = []; + + $hasPagination = $pagination !== null; + + $query = 'SELECT config_name, config_value FROM msz_config'; + if($hasPagination) + $query .= ' LIMIT ? RANGE ?'; + + $stmt = $this->cache->get($query); + if($hasPagination) { + $stmt->addParameter(1, $pagination->getRange()); + $stmt->addParameter(2, $pagination->getOffset()); + } + $stmt->execute(); + + $result = $stmt->getResult(); + + while($result->next()) { + $name = $result->getString(0); + $infos[] = $this->values[$name] = new DbConfigValueInfo($result); + } + + return $infos; } - public function setValue(string $name, $value, bool $save = true): void { + public function getValueInfos(string|array $names): array { + if(empty($names)) + return []; + if(is_string($names)) + $names = [$names]; + + $infos = []; + $skip = []; + + foreach($names as $name) + if(array_key_exists($name, $this->values)) { + $infos[] = $this->values[$name]; + $skip[] = $name; + } + + $names = array_diff($names, $skip); + + if(!empty($names)) { + // array_diff preserves keys, the for() later would fuck up without it + $names = array_values($names); + $nameCount = count($names); + + $stmt = $this->cache->get(sprintf( + 'SELECT config_name, config_value FROM msz_config WHERE config_name IN (%s)', + self::whereInList($nameCount) + )); + for($i = 0; $i < $nameCount; ++$i) + $stmt->addParameter($i + 1, $names[$i]); + $stmt->execute(); + + $result = $stmt->getResult(); + while($result->next()) { + $name = $result->getString(0); + $infos[] = $this->values[$name] = new DbConfigValueInfo($result); + } + } + + return $infos; + } + + public function getValueInfo(string $name): ?IConfigValueInfo { + $infos = $this->getValueInfos($name); + return empty($infos) ? null : $infos[0]; + } + + public function getValues(array $specs): array { + $names = []; + + foreach($specs as $key => $spec) { + if(is_string($spec)) { + $name = $spec; + $default = null; + } elseif(is_array($spec) && !empty($spec)) { + $name = $spec[0]; + $default = $spec[1] ?? null; + } else + throw new InvalidArgumentException('$specs array contains an invalid entry.'); + + if(($colon = strpos($name, ':')) !== false) { + $type = substr($name, $colon + 1, 1); + $name = substr($name, 0, $colon); + } else $type = ''; + + $names[] = $name; + $specs[$key] = [ + 'name' => $name, + 'type' => $type, + 'default' => $default, + ]; + } + + $infos = $this->getValueInfos($names); + $results = []; + + foreach($specs as $spec) { + foreach($infos as $infoTest) + if($infoTest->getName() === $spec['name']) { + $info = $infoTest; + break; + } + + if(!isset($info)) { + $defaultValue = $spec['default'] ?? null; + if($spec['type'] !== '') + settype($defaultValue, match($spec['type']) { + 's' => 'string', + 'a' => 'array', + 'i' => 'int', + 'b' => 'bool', + 'f' => 'float', + 'd' => 'double', + default => throw new RuntimeException('Invalid type letter encountered.'), + }); + + $results[$spec['name']] = $defaultValue; + continue; + } + + $results[$spec['name']] = match($spec['type']) { + 's' => $info->getString(), + 'a' => $info->getArray(), + 'i' => $info->getInteger(), + 'b' => $info->getBoolean(), + 'f' => $info->getFloat(), + 'd' => $info->getFloat(), + '' => $info->getValue(), + default => throw new RuntimeException('Unknown type encountered in $specs.'), + }; + + unset($info); + } + + return $results; + } + + public function getString(string $name, string $default = ''): string { + $valueInfo = $this->getValueInfo($name); + return $valueInfo?->isString() ? $valueInfo->getString() : $default; + } + + public function getInteger(string $name, int $default = 0): int { + $valueInfo = $this->getValueInfo($name); + return $valueInfo?->isInteger() ? $valueInfo->getInteger() : $default; + } + + public function getFloat(string $name, float $default = 0): float { + $valueInfo = $this->getValueInfo($name); + return $valueInfo?->isFloat() ? $valueInfo->getFloat() : $default; + } + + public function getBoolean(string $name, bool $default = false): bool { + $valueInfo = $this->getValueInfo($name); + return $valueInfo?->isBoolean() ? $valueInfo->getBoolean() : $default; + } + + public function getArray(string $name, array $default = []): array { + $valueInfo = $this->getValueInfo($name); + return $valueInfo?->isArray() ? $valueInfo->getArray() : $default; + } + + private function setValue(string $name, $value): void { $this->values[$name] = $value; + $value = serialize($value); - if($save) { - $value = serialize($value); - - if($this->stmtSet === null) - $this->stmtSet = $this->dbConn->prepare('REPLACE INTO msz_config (config_name, config_value) VALUES (?, ?)'); - - $this->stmtSet->reset(); - $this->stmtSet->addParameter(1, $name); - $this->stmtSet->addParameter(2, $value); - $this->stmtSet->execute(); - } + $stmt = $this->cache->get('REPLACE INTO msz_config (config_name, config_value) VALUES (?, ?)'); + $stmt->addParameter(1, $name); + $stmt->addParameter(2, $value); + $stmt->execute(); } - public function removeValue(string $name, bool $save = true): void { - unset($this->values[$name]); + public function setValues(array $values): void { + if(empty($values)) + return; - if($save) { - if($this->stmtDelete === null) - $this->stmtDelete = $this->dbConn->prepare('DELETE FROM msz_config WHERE config_name = ?'); + $valueCount = count($values); - $this->stmtDelete->reset(); - $this->stmtDelete->addParameter(1, $name); - $this->stmtDelete->execute(); + $stmt = $this->cache->get(sprintf( + 'REPLACE INTO msz_config (config_name, config_value) VALUES %s', + implode(', ', array_fill(0, $valueCount, '(?, ?)')) + )); + + $args = 0; + foreach($values as $name => $value) { + if(!self::validateName($name)) + throw new InvalidArgumentException('Invalid name encountered in $values.'); + + if(is_array($value)) { + foreach($value as $entry) + if(!is_scalar($entry)) + throw new InvalidArgumentException('An array value in $values contains a non-scalar type.'); + } elseif(!is_scalar($value)) + throw new InvalidArgumentException('Invalid value type encountered in $values.'); + + $stmt->addParameter(++$args, $name); + $stmt->addParameter(++$args, serialize($value)); } + + $stmt->execute(); + } + + public function setString(string $name, string $value): void { + $this->setValues([$name => $value]); + } + + public function setInteger(string $name, int $value): void { + $this->setValues([$name => $value]); + } + + public function setFloat(string $name, float $value): void { + $this->setValues([$name => $value]); + } + + public function setBoolean(string $name, bool $value): void { + $this->setValues([$name => $value]); + } + + public function setArray(string $name, array $value): void { + $this->setValues([$name => $value]); } } diff --git a/src/Config/DbConfigValueInfo.php b/src/Config/DbConfigValueInfo.php new file mode 100644 index 0000000..16ec885 --- /dev/null +++ b/src/Config/DbConfigValueInfo.php @@ -0,0 +1,92 @@ +name = $result->getString(0); + $this->value = $result->getString(1); + } + + public function getName(): string { + return $this->name; + } + + public function getType(): string { + return match($this->value[0]) { + 's' => 'string', + 'a' => 'array', + 'i' => 'int', + 'b' => 'bool', + 'd' => 'float', + }; + } + + public function isString(): bool { + return $this->value[0] === 's'; + } + + public function isInteger(): bool { + return $this->value[0] === 'i'; + } + + public function isFloat(): bool { + return $this->value[0] === 'd'; + } + + public function isBoolean(): bool { + return $this->value[0] === 'b'; + } + + public function isArray(): bool { + return $this->value[0] === 'a'; + } + + public function getValue(): mixed { + return unserialize($this->value); + } + + public function getString(): string { + $value = $this->getValue(); + if(!is_string($value)) + throw new RuntimeException('Value is not a string.'); + return $value; + } + + public function getInteger(): int { + $value = $this->getValue(); + if(!is_int($value)) + throw new RuntimeException('Value is not an integer.'); + return $value; + } + + public function getFloat(): float { + $value = $this->getValue(); + if(!is_float($value)) + throw new RuntimeException('Value is not a floating point number.'); + return $value; + } + + public function getBoolean(): bool { + $value = $this->getValue(); + if(!is_bool($value)) + throw new RuntimeException('Value is not a boolean.'); + return $value; + } + + public function getArray(): array { + $value = $this->getValue(); + if(!is_array($value)) + throw new RuntimeException('Value is not an array.'); + return $value; + } + + public function __toString(): string { + return $this->isArray() ? implode(', ', $this->getArray()) : (string)$this->getValue(); + } +} diff --git a/src/Config/IConfig.php b/src/Config/IConfig.php index d175972..c088881 100644 --- a/src/Config/IConfig.php +++ b/src/Config/IConfig.php @@ -1,23 +1,29 @@ config = $config; $this->prefix = $prefix; + $this->prefixLength = strlen($prefix); } - private function getName(string $name): string { + private function prefixNames(string|array $names): array { + if(is_string($names)) + return [$this->prefix . $name]; + + foreach($names as $key => $name) + $names[$key] = $this->prefix . $name; + + return $names; + } + + private function prefixName(string $name): string { return $this->prefix . $name; } public function scopeTo(string $prefix): IConfig { - return $this->config->scopeTo($this->getName($prefix)); + return $this->config->scopeTo($this->prefixName($prefix)); } - public function getNames(): array { - $orig = $this->config->getNames(); - $pfxLength = strlen($this->prefix); - $filter = []; - - foreach($orig as $name) - if(str_starts_with($name, $this->prefix)) - $filter[] = substr($name, $pfxLength); - - return $filter; + public function hasValues(string|array $names): bool { + $this->config->hasValues($this->prefixNames($names)); } - public function getValue(string $name, string $type = IConfig::T_ANY, $default = null): mixed { - return $this->config->getValue($this->getName($name), $type, $default); + public function removeValues(string|array $names): void { + $this->config->removeValues($this->prefixNames($names)); } - public function hasValue(string $name): bool { - return $this->config->hasValue($this->getName($name)); + public function getAllValueInfos(?Pagination $pagination = null): array { + $infos = $this->config->getAllValueInfos($pagination); + foreach($infos as $key => $info) + $infos[$key] = new ScopedConfigValueInfo($info, $this->prefixLength); + return $infos; } - public function setValue(string $name, $value, bool $save = true): void { - $this->config->setValue($this->getName($name), $value, $save); + public function getValueInfos(string|array $names): array { + $infos = $this->config->getValueInfos($this->prefixNames($names)); + foreach($infos as $key => $info) + $infos[$key] = new ScopedConfigValueInfo($info, $this->prefixLength); + return $infos; } - public function removeValue(string $name, bool $save = true): void { - $this->config->removeValue($this->getName($name), $save); + public function getValueInfo(string $name): ?IConfigValueInfo { + $info = $this->config->getValueInfo($this->prefixName($name)); + if($info !== null) + $info = new ScopedConfigValueInfo($info, $this->prefixLength); + return $info; + } + + public function getValues(array $specs): array { + foreach($specs as $key => $spec) { + if(is_string($spec)) + $specs[$key] = $this->prefixName($spec); + elseif(is_array($spec) && !empty($spec)) + $specs[$key][0] = $this->prefixName($spec[0]); + else + throw new InvalidArgumentException('$specs array contains an invalid entry.'); + } + + $results = []; + foreach($this->config->getValues($specs) as $name => $result) + $results[substr($name, $this->prefixLength)] = $result; + + return $results; + } + + public function getString(string $name, string $default = ''): string { + return $this->config->getString($this->prefixName($name), $default); + } + + public function getInteger(string $name, int $default = 0): int { + return $this->config->getInteger($this->prefixName($name), $default); + } + + public function getFloat(string $name, float $default = 0): float { + return $this->config->getFloat($this->prefixName($name), $default); + } + + public function getBoolean(string $name, bool $default = false): bool { + return $this->config->getBoolean($this->prefixName($name), $default); + } + + public function getArray(string $name, array $default = []): array { + return $this->config->getArray($this->prefixName($name), $default); + } + + public function setValues(array $values): void { + if(empty($values)) + return; + + $prefixed = []; + foreach($values as $name => $value) + $prefixed[$this->prefixName($name)] = $value; + $this->config->setValues($values); + } + + public function setString(string $name, string $value): void { + $this->config->setString($this->prefixName($name), $value); + } + + public function setInteger(string $name, int $value): void { + $this->config->setInteger($this->prefixName($name), $value); + } + + public function setFloat(string $name, float $value): void { + $this->config->setFloat($this->prefixName($name), $value); + } + + public function setBoolean(string $name, bool $value): void { + $this->config->setBoolean($this->prefixName($name), $value); + } + + public function setArray(string $name, array $value): void { + $this->config->setArray($this->prefixName($name), $value); } } diff --git a/src/Config/ScopedConfigValueInfo.php b/src/Config/ScopedConfigValueInfo.php new file mode 100644 index 0000000..1eddfdc --- /dev/null +++ b/src/Config/ScopedConfigValueInfo.php @@ -0,0 +1,72 @@ +info = $info; + $this->prefixLength = $prefixLength; + } + + public function getName(): string { + return substr($this->info->getName(), $this->prefixLength); + } + + public function getRealName(): string { + return $this->info->getName(); + } + + public function getType(): string { + return $this->info->getType(); + } + + public function isString(): bool { + return $this->info->isString(); + } + + public function isInteger(): bool { + return $this->info->isInteger(); + } + + public function isFloat(): bool { + return $this->info->isFloat(); + } + + public function isBoolean(): bool { + return $this->info->isBoolean(); + } + + public function isArray(): bool { + return $this->info->isArray(); + } + + public function getValue(): mixed { + return $this->info->getValue(); + } + + public function getString(): string { + return $this->info->getString(); + } + + public function getInteger(): int { + return $this->info->getInteger(); + } + + public function getFloat(): float { + return $this->info->getFloat(); + } + + public function getBoolean(): bool { + return $this->info->getBoolean(); + } + + public function getArray(): array { + return $this->info->getArray(); + } + + public function __toString(): string { + return (string)$this->info; + } +} diff --git a/src/Forum/forum.php b/src/Forum/forum.php index 9cf82f8..6a8ef6c 100644 --- a/src/Forum/forum.php +++ b/src/Forum/forum.php @@ -535,7 +535,7 @@ function forum_get_user_most_active_category_info(int $userId): ?object { $getActiveForum = \Misuzu\DB::prepare(sprintf( 'SELECT forum_id, COUNT(*) AS post_count FROM msz_forum_posts WHERE user_id = :user AND post_deleted IS NULL AND forum_id NOT IN (%s) GROUP BY forum_id ORDER BY post_count DESC LIMIT 1', - implode(',', $cfg->getValue('forum_leader.unranked.forum', \Misuzu\Config\IConfig::T_ARR)) + implode(',', $cfg->getArray('forum_leader.unranked.forum')) )); $getActiveForum->bind('user', $userId); diff --git a/src/Forum/topic.php b/src/Forum/topic.php index b1b0c89..f79999c 100644 --- a/src/Forum/topic.php +++ b/src/Forum/topic.php @@ -711,7 +711,7 @@ function forum_get_user_most_active_topic_info(int $userId): ?object { $getActiveForum = \Misuzu\DB::prepare(sprintf( 'SELECT topic_id, COUNT(*) AS post_count FROM msz_forum_posts WHERE user_id = :user AND post_deleted IS NULL AND forum_id NOT IN (%s) GROUP BY topic_id ORDER BY post_count DESC LIMIT 1', - implode(',', $cfg->getValue('forum_leader.unranked.forum', \Misuzu\Config\IConfig::T_ARR)) + implode(',', $cfg->getArray('forum_leader.unranked.forum')) )); $getActiveForum->bind('user', $userId); diff --git a/src/Http/Handlers/ChangelogHandler.php b/src/Http/Handlers/ChangelogHandler.php index c0a63d5..58721c5 100644 --- a/src/Http/Handlers/ChangelogHandler.php +++ b/src/Http/Handlers/ChangelogHandler.php @@ -3,8 +3,6 @@ namespace Misuzu\Http\Handlers; use ErrorException; use RuntimeException; -use Misuzu\Config; -use Misuzu\Config\IConfig; use Misuzu\Pagination; use Misuzu\Template; use Misuzu\Comments\CommentsEx; @@ -119,11 +117,12 @@ class ChangelogHandler extends Handler { } private function createFeed(string $feedMode): Feed { + $siteName = $this->context->getConfig()->getString('site.name', 'Misuzu'); $changes = $this->context->getChangelog()->getAllChanges(pagination: new Pagination(10)); $feed = (new Feed) - ->setTitle(Config::get('site.name', IConfig::T_STR, 'Misuzu') . ' » Changelog') - ->setDescription('Live feed of changes to ' . Config::get('site.name', IConfig::T_STR, 'Misuzu') . '.') + ->setTitle($siteName . ' » Changelog') + ->setDescription('Live feed of changes to ' . $siteName . '.') ->setContentUrl(url_prefix(false) . url('changelog-index')) ->setFeedUrl(url_prefix(false) . url("changelog-feed-{$feedMode}")); diff --git a/src/Http/Handlers/HomeHandler.php b/src/Http/Handlers/HomeHandler.php index c662b77..d3ff0ce 100644 --- a/src/Http/Handlers/HomeHandler.php +++ b/src/Http/Handlers/HomeHandler.php @@ -2,8 +2,6 @@ namespace Misuzu\Http\Handlers; use RuntimeException; -use Misuzu\Config; -use Misuzu\Config\IConfig; use Misuzu\DB; use Misuzu\Pagination; use Misuzu\Template; @@ -21,13 +19,22 @@ final class HomeHandler extends Handler { } public function landing($response, $request): void { - $linkedData = Config::get('social.embed_linked', IConfig::T_BOOL) - ? [ - 'name' => Config::get('site.name', IConfig::T_STR, 'Misuzu'), - 'url' => Config::get('site.url', IConfig::T_STR), - 'logo' => Config::get('site.ext_logo', IConfig::T_STR), - 'same_as' => Config::get('social.linked', IConfig::T_ARR), - ] : null; + $config = $this->context->getConfig(); + + if($config->getBoolean('social.embed_linked')) { + $ldr = $config->getValues([ + ['site.name:s', 'Misuzu'], + 'site.url:s', + 'site.ext_logo:s', + 'social.linked:a' + ]); + $linkedData = [ + 'name' => $ldr['site.name'], + 'url' => $ldr['site.url'], + 'logo' => $ldr['site.ext_logo'], + 'same_as' => $ldr['social.linked'], + ]; + } else $linkedData = null; $featuredNews = $this->context->getNews()->getAllPosts( onlyFeatured: true, @@ -55,7 +62,7 @@ final class HomeHandler extends Handler { )->fetchAll(); // TODO: don't hardcode forum ids - $featuredForums = Config::get('landing.forum_categories', IConfig::T_ARR); + $featuredForums = $config->getArray('landing.forum_categories'); $popularTopics = []; $activeTopics = []; diff --git a/src/Http/Handlers/NewsHandler.php b/src/Http/Handlers/NewsHandler.php index 25d129f..651dde6 100644 --- a/src/Http/Handlers/NewsHandler.php +++ b/src/Http/Handlers/NewsHandler.php @@ -2,13 +2,11 @@ namespace Misuzu\Http\Handlers; use RuntimeException; -use Misuzu\Config; use Misuzu\DB; use Misuzu\Pagination; use Misuzu\Template; use Misuzu\Comments\CommentsCategory; use Misuzu\Comments\CommentsEx; -use Misuzu\Config\IConfig; use Misuzu\Feeds\Feed; use Misuzu\Feeds\FeedItem; use Misuzu\Feeds\AtomFeedSerializer; @@ -154,9 +152,10 @@ final class NewsHandler extends Handler { private function createFeed(string $feedMode, ?NewsCategoryInfo $categoryInfo, array $posts): Feed { $hasCategory = $categoryInfo !== null; + $siteName = $this->context->getConfig()->getString('site.name', 'Misuzu'); $feed = (new Feed) - ->setTitle(Config::get('site.name', IConfig::T_STR, 'Misuzu') . ' » ' . ($hasCategory ? $categoryInfo->getName() : 'Featured News')) + ->setTitle($siteName . ' » ' . ($hasCategory ? $categoryInfo->getName() : 'Featured News')) ->setDescription($hasCategory ? $categoryInfo->getDescription() : 'A live featured news feed.') ->setContentUrl(url_prefix(false) . ($hasCategory ? url('news-category', ['category' => $categoryInfo->getId()]) : url('news-index'))) ->setFeedUrl(url_prefix(false) . ($hasCategory ? url("news-category-feed-{$feedMode}", ['category' => $categoryInfo->getId()]) : url("news-feed-{$feedMode}"))); diff --git a/src/Mailer.php b/src/Mailer.php index 366dde7..896ddbe 100644 --- a/src/Mailer.php +++ b/src/Mailer.php @@ -2,6 +2,7 @@ namespace Misuzu; use InvalidArgumentException; +use Misuzu\Config\IConfig; use Symfony\Component\Mime\Email as SymfonyMessage; use Symfony\Component\Mime\Address as SymfonyAddress; use Symfony\Component\Mailer\Mailer as SymfonyMailer; @@ -11,25 +12,29 @@ use Symfony\Component\Mailer\Exception\TransportExceptionInterface; final class Mailer { private const TEMPLATE_PATH = MSZ_ROOT . '/config/emails/%s.txt'; - private static $dsn = 'null://null'; + private static IConfig $config; private static $transport = null; private static string $senderName = 'Flashii'; private static string $senderAddr = 'sys@flashii.net'; - public static function init(string $method, array $config): void { - if($method !== 'smtp') { - self::$dsn = 'null://null'; - return; - } + public static function init(IConfig $config): void { + self::$config = $config; + } - $dsn = $method; + private static function createDsn(): string { + $config = self::$config->getValues([ + 'method:s', + 'host:s', + ['port:i', 25], + 'username:s', + 'password:s', + ]); - // guess this is applied automatically based on the port? - //if(!empty($config['encryption'])) - // $dsn .= 't'; + if($config['method'] !== 'smtp') + return 'null://null'; - $dsn .= '://'; + $dsn = $config['method'] . '://'; if(!empty($config['username'])) { $dsn .= $config['username']; @@ -44,18 +49,18 @@ final class Mailer { $dsn .= ':'; $dsn .= $config['port'] ?? 25; - self::$dsn = $dsn; + if(!empty($config['sender.name'])) + self::$senderName = $config['sender.name']; - if(!empty($config['sender_name'])) - self::$senderName = $config['sender_name']; + if(!empty($config['sender.address'])) + self::$senderAddr = $config['sender.address']; - if(!empty($config['sender_addr'])) - self::$senderAddr = $config['sender_addr']; + return $dsn; } public static function getTransport() { if(self::$transport === null) - self::$transport = SymfonyTransport::fromDsn(self::$dsn); + self::$transport = SymfonyTransport::fromDsn(self::createDsn()); return self::$transport; } @@ -65,11 +70,16 @@ final class Mailer { break; } + $config = self::$config->getValues([ + 'sender.name:s', + 'sender.address:s', + ]); + $message = new SymfonyMessage; $message->from(new SymfonyAddress( - self::$senderAddr, - self::$senderName + $config['sender.address'], + $config['sender.name'] )); if($bcc) diff --git a/src/SharpChat/SharpChatRoutes.php b/src/SharpChat/SharpChatRoutes.php index 2266611..4804626 100644 --- a/src/SharpChat/SharpChatRoutes.php +++ b/src/SharpChat/SharpChatRoutes.php @@ -24,10 +24,10 @@ final class SharpChatRoutes { $this->config = $config; $this->emotes = $emotes; - $hashKey = $this->config->getValue('hashKey', IConfig::T_STR, ''); + $hashKey = $this->config->getString('hashKey', ''); if(empty($hashKey)) { - $hashKeyPath = $this->config->getValue('hashKeyPath', IConfig::T_STR, ''); + $hashKeyPath = $this->config->getString('hashKeyPath', ''); if(is_file($hashKeyPath)) $this->hashKey = file_get_contents($hashKeyPath); } else { @@ -96,7 +96,7 @@ final class SharpChatRoutes { public function getLogin($response, $request): void { $currentUser = User::getCurrent(); $configKey = $request->hasParam('legacy') ? 'chatPath.legacy' : 'chatPath.normal'; - $chatPath = $this->config->getValue($configKey, IConfig::T_STR, '/'); + $chatPath = $this->config->getString($configKey, '/'); $response->redirect( $currentUser === null @@ -111,7 +111,7 @@ final class SharpChatRoutes { $originHost = strtolower(parse_url($origin, PHP_URL_HOST) ?? ''); if(!empty($originHost) && $originHost !== $host) { - $whitelist = $this->config->getValue('origins', IConfig::T_ARR, []); + $whitelist = $this->config->getArray('origins', []); if(!in_array($originHost, $whitelist)) return 403; diff --git a/src/Users/Assets/UserAvatarAsset.php b/src/Users/Assets/UserAvatarAsset.php index 474d382..6dbc4cd 100644 --- a/src/Users/Assets/UserAvatarAsset.php +++ b/src/Users/Assets/UserAvatarAsset.php @@ -1,8 +1,6 @@ getInteger('avatar.max_res', self::MAX_RES); } public function getMaxHeight(): int { return $this->getMaxWidth(); } public function getMaxBytes(): int { - return Config::get('avatar.max_size', IConfig::T_INT, self::MAX_BYTES); + global $cfg; + return $cfg->getInteger('avatar.max_size', self::MAX_BYTES); } public function getUrl(): string { diff --git a/src/Users/Assets/UserBackgroundAsset.php b/src/Users/Assets/UserBackgroundAsset.php index 0cf3acf..0165b7e 100644 --- a/src/Users/Assets/UserBackgroundAsset.php +++ b/src/Users/Assets/UserBackgroundAsset.php @@ -2,8 +2,6 @@ namespace Misuzu\Users\Assets; use InvalidArgumentException; -use Misuzu\Config; -use Misuzu\Config\IConfig; use Misuzu\Users\User; // attachment and attributes are to be stored in the same byte @@ -49,13 +47,16 @@ class UserBackgroundAsset extends UserImageAsset { } public function getMaxWidth(): int { - return Config::get('background.max_width', IConfig::T_INT, self::MAX_WIDTH); + global $cfg; + return $cfg->getInteger('background.max_width', self::MAX_WIDTH); } public function getMaxHeight(): int { - return Config::get('background.max_height', IConfig::T_INT, self::MAX_HEIGHT); + global $cfg; + return $cfg->getInteger('background.max_height', self::MAX_HEIGHT); } public function getMaxBytes(): int { - return Config::get('background.max_size', IConfig::T_INT, self::MAX_BYTES); + global $cfg; + return $cfg->getInteger('background.max_size', self::MAX_BYTES); } public function getUrl(): string { diff --git a/src/Users/Assets/UserImageAsset.php b/src/Users/Assets/UserImageAsset.php index d3a8911..c943170 100644 --- a/src/Users/Assets/UserImageAsset.php +++ b/src/Users/Assets/UserImageAsset.php @@ -1,8 +1,6 @@ @@ -12,7 +12,7 @@ Are you sure you want to delete this setting? It cannot be recovered. -
+ {{ input_csrf() }}
@@ -20,13 +20,13 @@ -
{{ conf_var.name }}
+
{{ config_value.name }}
- -
{{ conf_var.type }}
+ +
{{ config_value.type }}
-
{{ conf_var.value|json_encode }}
+
{{ config_value }}
diff --git a/templates/manage/general/setting.twig b/templates/manage/general/setting.twig index 2b07787..176557e 100644 --- a/templates/manage/general/setting.twig +++ b/templates/manage/general/setting.twig @@ -2,44 +2,44 @@ {% from 'macros.twig' import container_title %} {% from '_layout/input.twig' import input_csrf, input_text, input_checkbox, input_file, input_select, input_colour %} -{% set title = conf_var.name is empty ? 'Adding a new setting' : ((conf_var.new ? 'Adding ' : 'Editing ') ~ ' setting ' ~ conf_var.name) %} +{% set title = config_new ? 'Adding a new setting' : ((config_new ? 'Adding ' : 'Editing ') ~ ' setting ' ~ config_name) %} {% block manage_content %}
{{ container_title(' ' ~ title) }} - + {{ input_csrf() }} - {% if conf_var.new %} + {% if config_new %} {% endif %} - {% if conf_var.type is not empty %} + {% if config_type is not empty %}