diff --git a/core/src/Revolution/modX.php b/core/src/Revolution/modX.php index e17de52e8f7..d1da883e9ef 100644 --- a/core/src/Revolution/modX.php +++ b/core/src/Revolution/modX.php @@ -2889,6 +2889,36 @@ protected function _initSession($options = null) { } } + /** + * Persist manager language preference in a cookie so it survives logout (issue #16072). + * Uses session cookie path and SameSite for consistency with session cookies. + * + * @param string $language Valid language code from lexicon->getLanguageList() (e.g. 'en', 'de') + * @return void + */ + public function setManagerLanguageCookie($language) + { + $cookiePath = $this->getOption('session_cookie_path', null, ''); + if (empty($cookiePath)) { + $cookiePath = $this->getOption('base_url', null, MODX_BASE_URL); + } + if (empty($cookiePath)) { + $cookiePath = '/'; + } + $cookieOptions = [ + 'expires' => time() + 31536000, + 'path' => $cookiePath, + 'domain' => '', + 'secure' => (bool) $this->getOption('session_cookie_secure', null, false), + 'httponly' => false, + ]; + $samesite = $this->getOption('session_cookie_samesite', null, 'Lax'); + if ($samesite !== '') { + $cookieOptions['samesite'] = $samesite; + } + setcookie('modx_manager_language', $language, $cookieOptions); + } + /** * Loads the modX system configuration settings. * diff --git a/manager/assets/modext/core/modx.js b/manager/assets/modext/core/modx.js index bbdf81902b5..dc6d8f529a1 100644 --- a/manager/assets/modext/core/modx.js +++ b/manager/assets/modext/core/modx.js @@ -362,7 +362,12 @@ Ext.extend(MODx,Ext.Component,{ ,listeners: { 'success': {fn:function(r) { if (this.fireEvent('afterLogout',r)) { - location.href = './'; + var lang = Ext.util.Cookies && Ext.util.Cookies.get("modx_manager_language"); + var url = "./"; + if (lang) { + url = "./?manager_language=" + encodeURIComponent(lang); + } + location.href = url; } },scope:this} } diff --git a/manager/controllers/default/language.class.php b/manager/controllers/default/language.class.php index 58c18951c42..3bd00c2e9ea 100644 --- a/manager/controllers/default/language.class.php +++ b/manager/controllers/default/language.class.php @@ -3,7 +3,7 @@ use MODX\Revolution\modParsedManagerController; /** - * Switches the current manger language to requested. + * Switches the current manager language to requested. * * Class LanguageManagerController */ @@ -30,6 +30,7 @@ public function process(array $scriptProperties = []) } } $_SESSION['manager_language'] = $targetLanguage; + $this->modx->setManagerLanguageCookie($targetLanguage); $this->modx->sendRedirect(MODX_MANAGER_URL . (($targetProperties) ? '?' . http_build_query($targetProperties) : '')); } diff --git a/manager/controllers/default/security/login.class.php b/manager/controllers/default/security/login.class.php index 1b0f6cbb1af..c7de170b8b4 100644 --- a/manager/controllers/default/security/login.class.php +++ b/manager/controllers/default/security/login.class.php @@ -205,16 +205,21 @@ public function handleLanguageChange() $ml = $this->modx->sanitizeString($this->modx->getOption('manager_language', $_REQUEST)); if (!$ml || !in_array($ml, $languages)) { - $ml = $this->modx->getOption('manager_language', $_SESSION); - if (!$ml) { - // Try to detect default browser language - $accept_languages = strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']); - preg_match_all('#([\w-]+)(?:[^,\d]+([\d.]+))?#', $accept_languages, $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - $lang = trim(explode('-', $match[1])[0]); - if (in_array($lang, $languages)) { - $ml = $lang; - break; + $ml = isset($_COOKIE['modx_manager_language']) + ? $this->modx->sanitizeString($_COOKIE['modx_manager_language']) + : null; + if (!$ml || !in_array($ml, $languages)) { + $ml = $this->modx->getOption('manager_language', $_SESSION); + if (!$ml) { + // Try to detect default browser language + $accept_languages = strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''); + preg_match_all('#([\w-]+)(?:[^,\d]+([\d.]+))?#', $accept_languages, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $lang = trim(explode('-', $match[1])[0]); + if (in_array($lang, $languages)) { + $ml = $lang; + break; + } } } } @@ -225,6 +230,7 @@ public function handleLanguageChange() } // Save manager language to session $_SESSION['manager_language'] = $ml; + $this->modx->setManagerLanguageCookie($ml); // If user tried to change language - make redirect to hide it from url if (!empty($_GET['manager_language'])) { unset($_GET['manager_language']); diff --git a/manager/controllers/default/security/logout.class.php b/manager/controllers/default/security/logout.class.php index 2fbb2aef3dd..ef6d16bab1c 100644 --- a/manager/controllers/default/security/logout.class.php +++ b/manager/controllers/default/security/logout.class.php @@ -31,8 +31,18 @@ public function loadCustomCssJs() {} * @return mixed */ public function process(array $scriptProperties = []) { + $managerLanguage = null; + if (!empty($_SESSION['manager_language'])) { + $languages = $this->modx->lexicon->getLanguageList('core'); + if (in_array($_SESSION['manager_language'], $languages)) { + $managerLanguage = $_SESSION['manager_language']; + } + } $this->modx->runProcessor('security/logout'); - $url = $this->modx->getOption('manager_url',null,MODX_MANAGER_URL); + $url = $this->modx->getOption('manager_url', null, MODX_MANAGER_URL); + if ($managerLanguage !== null) { + $url .= (strpos($url, '?') !== false ? '&' : '?') . 'manager_language=' . urlencode($managerLanguage); + } $this->modx->sendRedirect($url); }