diff --git a/plugins/system/languagefilter/src/Extension/LanguageFilter.php b/plugins/system/languagefilter/src/Extension/LanguageFilter.php index 19d969e324d3..95d3eace3bf2 100644 --- a/plugins/system/languagefilter/src/Extension/LanguageFilter.php +++ b/plugins/system/languagefilter/src/Extension/LanguageFilter.php @@ -243,6 +243,7 @@ public function onAfterInitialise(AfterInitialiseEvent $event): void // Attach parse rule. $router->attachParseRule([$this, 'parseRule'], Router::PROCESS_BEFORE); + $router->attachParseRule([$this, 'setLanguageApplicationState'], Router::PROCESS_BEFORE); } /** @@ -304,7 +305,6 @@ public function buildRule(&$router, &$uri) if ( !$this->params->get('remove_default_prefix', 0) || $lang !== $this->default_lang - || $lang !== $this->current_lang ) { $uri->setPath($uri->getPath() . '/' . $sef . '/'); } @@ -356,179 +356,135 @@ public function postprocessNonSEFBuildRule(&$router, &$uri) */ public function parseRule(&$router, &$uri) { - $app = $this->getApplication(); - - // Did we find the current and existing language yet? - $found = false; - // Are we in SEF mode or not? if ($this->mode_sef) { $path = $uri->getPath(); $parts = explode('/', $path); - - $sef = StringHelper::strtolower($parts[0]); - - // Do we have a URL Language Code ? - if (!isset($this->sefs[$sef])) { - // Check if remove default URL language code is set - if ($this->params->get('remove_default_prefix', 0)) { - if ($parts[0]) { - // We load a default site language page - $lang_code = $this->default_lang; - } else { - // We check for an existing language cookie - $lang_code = $this->getLanguageCookie(); - } - } else { - $lang_code = $this->getLanguageCookie(); - } - - // No language code. Try using browser settings or default site language - if (!$lang_code && $this->params->get('detect_browser', 0) == 1) { - $lang_code = LanguageHelper::detectLanguage(); - } - - if (!$lang_code) { - $lang_code = $this->default_lang; - } - - if ($lang_code === $this->default_lang && $this->params->get('remove_default_prefix', 0)) { - $found = true; + $sef = StringHelper::strtolower($parts[0]); + $lang = $uri->getVar('lang'); + + if (isset($this->sefs[$sef])) { + // We found a matching language to the lang code + $uri->setVar('lang', $this->sefs[$sef]->lang_code); + array_shift($parts); + $uri->setPath(implode('/', $parts)); + + // We were called with the default language code and want to redirect + if ($this->params->get('remove_default_prefix', 0) && $uri->getVar('lang') == $this->default_lang) { + $router->setTainted(); } + } elseif ($this->params->get('remove_default_prefix', 0)) { + // We don't have a prefix for the default language + $uri->setVar('lang', $this->default_lang); } else { - // We found our language - $found = true; - $lang_code = $this->sefs[$sef]->lang_code; - - // If we found our language, but it's the default language and we don't want a prefix for that, we are on a wrong URL. - // Or we try to change the language back to the default language. We need a redirect to the proper URL for the default language. - if ($lang_code === $this->default_lang && $this->params->get('remove_default_prefix', 0)) { - // Create a cookie. - $this->setLanguageCookie($lang_code); - - $found = false; - array_shift($parts); - $path = implode('/', $parts); - } - - // We have found our language and the first part of our URL is the language prefix - if ($found) { - array_shift($parts); - - // Empty parts array when "index.php" is the only part left. - if (\count($parts) === 1 && $parts[0] === 'index.php') { - $parts = []; - } - - $uri->setPath(implode('/', $parts)); - } + // No language is set, so we want to redirect to the right language + $router->setTainted(); } - } else { - // We are not in SEF mode - $lang_code = $this->getLanguageCookie(); - if (!$lang_code && $this->params->get('detect_browser', 1)) { - $lang_code = LanguageHelper::detectLanguage(); + // The language was set both per SEF path and per query parameter. Query parameter takes precedence + if ($lang) { + $uri->setVar('lang', $lang); + $router->setTainted(); } + } elseif ($uri->hasVar('lang')) { + // We are not in SEF mode. Do we have a language set? + $lang_code = $uri->getVar('lang'); - if (!isset($this->lang_codes[$lang_code])) { - $lang_code = $this->default_lang; + if (isset($this->sefs[$lang_code])) { + // We found a matching language to the lang code + $uri->setVar('lang', $this->sefs[$lang_code]->lang_code); + } else { + // The language is not installed on our site + $uri->delVar('lang'); } } + } - $lang = $uri->getVar('lang', $lang_code); + /** + * Parse rule to set the applications language state. + * This rule is removed after being executed the first time, since + * it does redirects and thus disallows parsing more than one URL per page call + * + * @param Router &$router Router object. + * @param Uri &$uri Uri object. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function setLanguageApplicationState(&$router, &$uri) + { + // We check if the parseRule is still attached to keep this b/c + if (!\in_array([$this, 'parseRule'], $router->getRules()['parsepreprocess'])) { + $router->detachRule('parse', [$this, 'setLanguageApplicationState'], $router::PROCESS_BEFORE); - if (isset($this->sefs[$lang])) { - // We found our language - $found = true; - $lang_code = $this->sefs[$lang]->lang_code; + return; } - // We are called via POST or the nolangfilter url parameter was set. We don't care about the language - // and simply set the default language as our current language. - if ( - $app->getInput()->getMethod() === 'POST' - || $app->getInput()->get('nolangfilter', 0) == 1 - || \count($app->getInput()->post) > 0 - || \count($app->getInput()->files) > 0 - ) { - $found = true; - - if (!isset($lang_code)) { - $lang_code = $this->getLanguageCookie(); - } - - if (!$lang_code && $this->params->get('detect_browser', 1)) { - $lang_code = LanguageHelper::detectLanguage(); - } + $lang_code = false; - if (!isset($this->lang_codes[$lang_code])) { + // Our parse rule discovered a language + if ($uri->hasVar('lang')) { + $lang_code = $uri->getVar('lang'); + } else { + /** + * We don't know the language yet and want to discover it. + * If we remove the default prefix, call by POST or have nolangfilter set, + * we simply take the default language. + */ + if ( + $this->params->get('remove_default_prefix', 0) + || $this->getApplication()->getInput()->getMethod() === 'POST' + || $this->getApplication()->getInput()->get('nolangfilter', 0) == 1 + || \count($this->getApplication()->getInput()->post) > 0 + || \count($this->getApplication()->getInput()->files) > 0 + ) { $lang_code = $this->default_lang; - } - } - - // We have not found the language and thus need to redirect - if (!$found) { - // Lets find the default language for this user - if (!isset($lang_code) || !isset($this->lang_codes[$lang_code])) { - $lang_code = false; + } else { + $lang_code = $this->getLanguageCookie(); - if ($this->params->get('detect_browser', 1)) { + // No language code. Try using browser settings or default site language + if (!$lang_code && $this->params->get('detect_browser', 0) == 1) { $lang_code = LanguageHelper::detectLanguage(); - - if (!isset($this->lang_codes[$lang_code])) { - $lang_code = false; - } } if (!$lang_code) { $lang_code = $this->default_lang; } - } - - if ($this->mode_sef) { - // Use the current language sef or the default one. - if ( - $lang_code !== $this->default_lang - || !$this->params->get('remove_default_prefix', 0) - ) { - $path = $this->lang_codes[$lang_code]->sef . '/' . $path; - } - $uri->setPath($path); + if (!$this->params->get('remove_default_prefix', 0) && $uri->getPath() == '') { + if ($this->mode_sef) { + $path = $this->lang_codes[$lang_code]->sef . '/' . $uri->getPath(); - if (!$app->get('sef_rewrite')) { - $uri->setPath('index.php/' . $uri->getPath()); - } - - $redirectUri = $uri->base() . $uri->toString(['path', 'query', 'fragment']); - } else { - $uri->setVar('lang', $this->lang_codes[$lang_code]->sef); - $redirectUri = $uri->base() . 'index.php?' . $uri->getQuery(); - } + if (!$this->getApplication()->get('sef_rewrite')) { + $path = 'index.php/' . $path; + } - // Set redirect HTTP code to "302 Found". - $redirectHttpCode = 302; + $uri->setPath($path); + } else { + $uri->setPath('index.php'); + $uri->setVar('lang', $this->lang_codes[$lang_code]->sef); + } + $redirectHttpCode = 301; + $redirectUri = $uri->base() . $uri->toString(['path', 'query', 'fragment']); - // If selected language is the default language redirect code is "301 Moved Permanently". - if ($lang_code === $this->default_lang) { - $redirectHttpCode = 301; + // We cannot cache this redirect in browser. 301 is cacheable by default so we need to force to not cache it in browsers. + $this->getApplication()->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true); + $this->getApplication()->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true); + $this->getApplication()->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate', false); + $this->getApplication()->sendHeaders(); - // We cannot cache this redirect in browser. 301 is cacheable by default so we need to force to not cache it in browsers. - $app->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true); - $app->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true); - $app->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate', false); - $app->sendHeaders(); + // Redirect to language. + $this->getApplication()->redirect($redirectUri, $redirectHttpCode); + } } - - // Redirect to language. - $app->redirect($redirectUri, $redirectHttpCode); } // We have found our language and now need to set the cookie and the language value in our system $this->current_lang = $lang_code; // Set the request var. + $app = $this->getApplication(); $app->getInput()->set('language', $lang_code); $app->set('language', $lang_code); $language = $app->getLanguage(); @@ -554,9 +510,9 @@ public function parseRule(&$router, &$uri) } // Create a cookie. - if ($this->getLanguageCookie() !== $lang_code) { - $this->setLanguageCookie($lang_code); - } + $this->setLanguageCookie($lang_code); + + $router->detachRule('parse', [$this, 'setLanguageApplicationState'], $router::PROCESS_BEFORE); } /** diff --git a/plugins/system/sef/src/Extension/Sef.php b/plugins/system/sef/src/Extension/Sef.php index 0480cd978a64..5f8ae8b5826b 100644 --- a/plugins/system/sef/src/Extension/Sef.php +++ b/plugins/system/sef/src/Extension/Sef.php @@ -14,6 +14,7 @@ use Joomla\CMS\Event\Application\AfterInitialiseEvent; use Joomla\CMS\Event\Application\AfterRenderEvent; use Joomla\CMS\Event\Application\AfterRouteEvent; +use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Router\Route; use Joomla\CMS\Router\Router; @@ -339,6 +340,15 @@ public function enforceSuffix() return; } + // We don't force a suffix for the language homepage + $segments = explode('/', $route); + $last = array_pop($segments); + $sefs = LanguageHelper::getLanguages('sef'); + + if ($this->getApplication()->getLanguageFilter() && isset($sefs[$last])) { + return; + } + $suffix = pathinfo($route, PATHINFO_EXTENSION); $nonSEFSuffix = $origUri->getVar('format');