diff --git a/core/src/Core.php b/core/src/Core.php
index 1d2061e943..f47a870cee 100644
--- a/core/src/Core.php
+++ b/core/src/Core.php
@@ -19,6 +19,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
@@ -6560,6 +6561,44 @@ public function getSettings()
$this->getUserSettings();
$this->setConfig('site_timezone', $siteTimezone);
$this->invokeEvent('OnLoadSettings', ['config' => &$this->config]);
+
+ // The factory defaults read the manager lexicon (core/factory/settings.php), so
+ // ManagerTheme is already built from the system theme and language by the time the
+ // user settings above are merged. Realign it so a per-user manager_theme /
+ // manager_language (and anything OnLoadSettings changed) reaches the manager UI.
+ $this->syncManagerTheme();
+ }
+
+ /**
+ * Realign the already resolved ManagerTheme with the current manager_theme and
+ * manager_language. A different theme needs a new instance (the theme name drives the
+ * view namespaces, the style and the theme snippets/chunks); a different language only
+ * needs the lexicon read again.
+ */
+ protected function syncManagerTheme(): void
+ {
+ if (!$this->isBackend() || !$this->resolved('ManagerTheme')) {
+ return;
+ }
+
+ $managerTheme = $this['ManagerTheme'];
+ if (!$managerTheme instanceof ManagerTheme) {
+ return;
+ }
+
+ $theme = (string) $this->getConfig('manager_theme', 'default');
+ if ($theme !== '' && $theme !== $managerTheme->getTheme()) {
+ // Dropped, not rebuilt: the next call resolves it again from the merged config.
+ $this->forgetInstance('ManagerTheme');
+ Facade::clearResolvedInstance('ManagerTheme');
+
+ return;
+ }
+
+ $language = (string) $this->getConfig('manager_language');
+ if ($language !== '' && $language !== $managerTheme->getLangName()) {
+ $managerTheme->reloadLang($language);
+ }
}
/**
diff --git a/core/src/ManagerTheme.php b/core/src/ManagerTheme.php
index 25e6799b2b..bf5fcb16ad 100644
--- a/core/src/ManagerTheme.php
+++ b/core/src/ManagerTheme.php
@@ -205,6 +205,20 @@ public function __construct(CoreInterface $core, string $theme)
}
}
+ /**
+ * Re-read the lexicon for another language.
+ *
+ * The theme is built while the configuration still only holds the system settings,
+ * so the core calls this once the per-user manager_language has been merged in.
+ *
+ * @param string $lang
+ * @return string
+ */
+ public function reloadLang(string $lang): string
+ {
+ return $this->loadLang($lang);
+ }
+
protected function loadLang($lang = 'english')
{
$_lang = [];
diff --git a/core/tests/Unit/ManagerPerUserLanguageTest.php b/core/tests/Unit/ManagerPerUserLanguageTest.php
new file mode 100644
index 0000000000..3355624122
--- /dev/null
+++ b/core/tests/Unit/ManagerPerUserLanguageTest.php
@@ -0,0 +1,69 @@
+isPublic());
+ self::assertSame(1, $method->getNumberOfRequiredParameters());
+ }
+
+ public function testCoreRefreshesTheManagerThemeAfterMergingUserSettings(): void
+ {
+ $mergePosition = mb_strpos(self::$coreSource, '$this->getUserSettings();');
+ $syncPosition = mb_strpos(self::$coreSource, '$this->syncManagerTheme();');
+
+ self::assertIsInt($mergePosition);
+ self::assertIsInt($syncPosition);
+ self::assertGreaterThan($mergePosition, $syncPosition);
+ }
+
+ public function testManagerThemeRefreshOnlyTouchesAnAlreadyBuiltBackendTheme(): void
+ {
+ $sync = mb_substr(
+ self::$coreSource,
+ (int) mb_strpos(self::$coreSource, 'protected function syncManagerTheme')
+ );
+ $sync = mb_substr($sync, 0, (int) mb_strpos($sync, "\n }"));
+
+ self::assertStringContainsString('$this->isBackend()', $sync);
+ self::assertStringContainsString("\$this->resolved('ManagerTheme')", $sync);
+ self::assertStringContainsString('reloadLang(', $sync);
+ // A different theme cannot be patched in place - the instance has to go.
+ self::assertStringContainsString("forgetInstance('ManagerTheme')", $sync);
+ self::assertStringContainsString("Facade::clearResolvedInstance('ManagerTheme')", $sync);
+ }
+}
diff --git a/manager/includes/user_settings.inc.php b/manager/includes/user_settings.inc.php
deleted file mode 100755
index f895e2a250..0000000000
--- a/manager/includes/user_settings.inc.php
+++ /dev/null
@@ -1,31 +0,0 @@
-INCLUDE_ORDERING_ERROR
Please use the EVO Content Manager instead of accessing this file directly.");
-}
-
-// START HACK
-if (isset ($modx)) {
- $user_id = EvolutionCMS()->getLoginUserID();
-} else {
- $user_id = $_SESSION['mgrInternalKey'];
-}
-// END HACK
-
-if (!empty($user_id)) {
- // Raymond: grab the user settings from the database.
- $userSettings = \EvolutionCMS\Models\UserSetting::query()
- ->select('setting_name', 'setting_value')
- ->where('user', EvolutionCMS()->getLoginUserID())->get()->toArray();
-
- $which_browser_default = $which_browser;
- foreach ($userSettings as $row) {
- if ($row['setting_name'] == 'which_browser' && $row['setting_value'] == 'default') {
- $row['setting_value'] = $which_browser_default;
- }
- $settings[$row['setting_name']] = $row['setting_value'];
- if (isset(EvolutionCMS()->config)) {
- EvolutionCMS()->config[$row['setting_name']] = $row['setting_value'];
- }
- }
- extract($settings, EXTR_OVERWRITE);
-}