settingsMetadataGateway->getValue(SettingKeys::APP_TITLE_KEY); $value = $value !== null ? trim($value) : null; return $value !== '' ? $value : null; } public function setAppTitle(?string $title, ?string $description = null): bool { $value = $title !== null ? trim($title) : null; if ($value === '') { $value = null; } $desc = $description ?? 'setting.app_title'; return $this->settingsMetadataGateway->set(SettingKeys::APP_TITLE_KEY, $value, $desc); } public function getAppLocale(): ?string { $value = $this->settingsMetadataGateway->getValue(SettingKeys::APP_LOCALE_KEY); $value = $value !== null ? trim($value) : ''; return $value !== '' ? $value : null; } public function setAppLocale(?string $locale, ?string $description = null): bool { $value = $locale !== null ? trim($locale) : ''; if ($value === '') { $value = null; } else { $allowed = defined('APP_LOCALES') ? APP_LOCALES : []; if ($allowed && !in_array($value, $allowed, true)) { return false; } } $desc = $description ?? 'setting.app_locale'; return $this->settingsMetadataGateway->set(SettingKeys::APP_LOCALE_KEY, $value, $desc); } public function getAppTheme(): ?string { $value = $this->settingsMetadataGateway->getValue(SettingKeys::APP_THEME_KEY); $value = $value !== null ? strtolower(trim($value)) : ''; if (!$this->isAllowedTheme($value)) { return null; } return $value; } public function setAppTheme(?string $theme, ?string $description = null): bool { $value = $theme !== null ? strtolower(trim($theme)) : ''; if ($value === '' || !$this->isAllowedTheme($value)) { $value = null; } $desc = $description ?? 'setting.app_theme'; return $this->settingsMetadataGateway->set(SettingKeys::APP_THEME_KEY, $value, $desc); } /** * @return array */ public function listThemes(): array { return $this->themeConfigService->all(); } public function isAllowedTheme(?string $theme): bool { $value = strtolower(trim((string) $theme)); return $value !== '' && in_array($value, $this->allowedThemes(), true); } public function resolveDefaultTheme(): string { $settingTheme = $this->getAppTheme(); if ($settingTheme !== null) { return $settingTheme; } $envTheme = strtolower(trim((string) (getenv('APP_THEME') ?: ''))); if ($this->isAllowedTheme($envTheme)) { return $envTheme; } $allowedThemes = $this->allowedThemes(); if (in_array('light', $allowedThemes, true)) { return 'light'; } return (string) ($allowedThemes[0] ?? 'light'); } public function normalizeTheme(?string $theme): string { $value = strtolower(trim((string) $theme)); if ($this->isAllowedTheme($value)) { return $value; } return $this->resolveDefaultTheme(); } public function isUserThemeAllowed(): bool { $value = $this->settingsMetadataGateway->getValue(SettingKeys::APP_THEME_USER_KEY); if ($value === null || $value === '') { return true; } return in_array(strtolower($value), ['1', 'true', 'yes', 'on'], true); } public function setUserThemeAllowed(bool $allowed, ?string $description = null): bool { $value = $allowed ? '1' : '0'; $desc = $description ?? 'setting.app_theme_user'; return $this->settingsMetadataGateway->set(SettingKeys::APP_THEME_USER_KEY, $value, $desc); } public function isRegistrationEnabled(): bool { $value = $this->settingsMetadataGateway->getValue(SettingKeys::APP_REGISTRATION_KEY); if ($value === null || $value === '') { return true; } return in_array(strtolower($value), ['1', 'true', 'yes', 'on'], true); } public function setRegistrationEnabled(bool $allowed, ?string $description = null): bool { $value = $allowed ? '1' : '0'; $desc = $description ?? 'setting.app_registration'; return $this->settingsMetadataGateway->set(SettingKeys::APP_REGISTRATION_KEY, $value, $desc); } public function getAppPrimaryColor(): ?string { $value = $this->settingsMetadataGateway->getValue(SettingKeys::APP_PRIMARY_COLOR_KEY); $value = $value !== null ? strtolower(trim($value)) : ''; if ($value === '') { return null; } if (!preg_match('/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i', $value)) { return null; } return $value; } public function setAppPrimaryColor(?string $color, ?string $description = null): bool { $value = $color !== null ? strtolower(trim($color)) : ''; if ($value !== '' && $value[0] !== '#') { if (preg_match('/^[0-9a-f]{3}$/i', $value) || preg_match('/^[0-9a-f]{4}$/i', $value) || preg_match('/^[0-9a-f]{6}$/i', $value) || preg_match('/^[0-9a-f]{8}$/i', $value)) { $value = '#' . $value; } } if ($value === '') { $value = null; } elseif (!preg_match('/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i', $value)) { return false; } $desc = $description ?? 'setting.app_primary_color'; return $this->settingsMetadataGateway->set(SettingKeys::APP_PRIMARY_COLOR_KEY, $value, $desc); } private function allowedThemes(): array { return array_keys($this->listThemes()); } public function normalizeLocale(?string $locale): string { $value = strtolower(trim((string) $locale)); $available = defined('APP_LOCALES') ? APP_LOCALES : [I18n::$defaultLocale]; if ($value === '' || !in_array($value, $available, true)) { return I18n::$locale ?? I18n::$defaultLocale; } return $value; } }