Skip to content

Commit fe22ff2

Browse files
authored
[6.0] Languagefilter: Refactoring of routing (joomla#43858)
* Languagefilter: Refactoring of routing * Update plugins/system/languagefilter/src/Extension/LanguageFilter.php * Redirect when no prefix * Codestyle * Use the new tainted flag * Prevent suffix for homepages * Fix merge conflict
1 parent da7b953 commit fe22ff2

File tree

2 files changed

+106
-140
lines changed

2 files changed

+106
-140
lines changed

plugins/system/languagefilter/src/Extension/LanguageFilter.php

Lines changed: 96 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ public function onAfterInitialise(AfterInitialiseEvent $event): void
243243

244244
// Attach parse rule.
245245
$router->attachParseRule([$this, 'parseRule'], Router::PROCESS_BEFORE);
246+
$router->attachParseRule([$this, 'setLanguageApplicationState'], Router::PROCESS_BEFORE);
246247
}
247248

248249
/**
@@ -304,7 +305,6 @@ public function buildRule(&$router, &$uri)
304305
if (
305306
!$this->params->get('remove_default_prefix', 0)
306307
|| $lang !== $this->default_lang
307-
|| $lang !== $this->current_lang
308308
) {
309309
$uri->setPath($uri->getPath() . '/' . $sef . '/');
310310
}
@@ -356,179 +356,135 @@ public function postprocessNonSEFBuildRule(&$router, &$uri)
356356
*/
357357
public function parseRule(&$router, &$uri)
358358
{
359-
$app = $this->getApplication();
360-
361-
// Did we find the current and existing language yet?
362-
$found = false;
363-
364359
// Are we in SEF mode or not?
365360
if ($this->mode_sef) {
366361
$path = $uri->getPath();
367362
$parts = explode('/', $path);
368-
369-
$sef = StringHelper::strtolower($parts[0]);
370-
371-
// Do we have a URL Language Code ?
372-
if (!isset($this->sefs[$sef])) {
373-
// Check if remove default URL language code is set
374-
if ($this->params->get('remove_default_prefix', 0)) {
375-
if ($parts[0]) {
376-
// We load a default site language page
377-
$lang_code = $this->default_lang;
378-
} else {
379-
// We check for an existing language cookie
380-
$lang_code = $this->getLanguageCookie();
381-
}
382-
} else {
383-
$lang_code = $this->getLanguageCookie();
384-
}
385-
386-
// No language code. Try using browser settings or default site language
387-
if (!$lang_code && $this->params->get('detect_browser', 0) == 1) {
388-
$lang_code = LanguageHelper::detectLanguage();
389-
}
390-
391-
if (!$lang_code) {
392-
$lang_code = $this->default_lang;
393-
}
394-
395-
if ($lang_code === $this->default_lang && $this->params->get('remove_default_prefix', 0)) {
396-
$found = true;
363+
$sef = StringHelper::strtolower($parts[0]);
364+
$lang = $uri->getVar('lang');
365+
366+
if (isset($this->sefs[$sef])) {
367+
// We found a matching language to the lang code
368+
$uri->setVar('lang', $this->sefs[$sef]->lang_code);
369+
array_shift($parts);
370+
$uri->setPath(implode('/', $parts));
371+
372+
// We were called with the default language code and want to redirect
373+
if ($this->params->get('remove_default_prefix', 0) && $uri->getVar('lang') == $this->default_lang) {
374+
$router->setTainted();
397375
}
376+
} elseif ($this->params->get('remove_default_prefix', 0)) {
377+
// We don't have a prefix for the default language
378+
$uri->setVar('lang', $this->default_lang);
398379
} else {
399-
// We found our language
400-
$found = true;
401-
$lang_code = $this->sefs[$sef]->lang_code;
402-
403-
// 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.
404-
// Or we try to change the language back to the default language. We need a redirect to the proper URL for the default language.
405-
if ($lang_code === $this->default_lang && $this->params->get('remove_default_prefix', 0)) {
406-
// Create a cookie.
407-
$this->setLanguageCookie($lang_code);
408-
409-
$found = false;
410-
array_shift($parts);
411-
$path = implode('/', $parts);
412-
}
413-
414-
// We have found our language and the first part of our URL is the language prefix
415-
if ($found) {
416-
array_shift($parts);
417-
418-
// Empty parts array when "index.php" is the only part left.
419-
if (\count($parts) === 1 && $parts[0] === 'index.php') {
420-
$parts = [];
421-
}
422-
423-
$uri->setPath(implode('/', $parts));
424-
}
380+
// No language is set, so we want to redirect to the right language
381+
$router->setTainted();
425382
}
426-
} else {
427-
// We are not in SEF mode
428-
$lang_code = $this->getLanguageCookie();
429383

430-
if (!$lang_code && $this->params->get('detect_browser', 1)) {
431-
$lang_code = LanguageHelper::detectLanguage();
384+
// The language was set both per SEF path and per query parameter. Query parameter takes precedence
385+
if ($lang) {
386+
$uri->setVar('lang', $lang);
387+
$router->setTainted();
432388
}
389+
} elseif ($uri->hasVar('lang')) {
390+
// We are not in SEF mode. Do we have a language set?
391+
$lang_code = $uri->getVar('lang');
433392

434-
if (!isset($this->lang_codes[$lang_code])) {
435-
$lang_code = $this->default_lang;
393+
if (isset($this->sefs[$lang_code])) {
394+
// We found a matching language to the lang code
395+
$uri->setVar('lang', $this->sefs[$lang_code]->lang_code);
396+
} else {
397+
// The language is not installed on our site
398+
$uri->delVar('lang');
436399
}
437400
}
401+
}
438402

439-
$lang = $uri->getVar('lang', $lang_code);
403+
/**
404+
* Parse rule to set the applications language state.
405+
* This rule is removed after being executed the first time, since
406+
* it does redirects and thus disallows parsing more than one URL per page call
407+
*
408+
* @param Router &$router Router object.
409+
* @param Uri &$uri Uri object.
410+
*
411+
* @return void
412+
*
413+
* @since __DEPLOY_VERSION__
414+
*/
415+
public function setLanguageApplicationState(&$router, &$uri)
416+
{
417+
// We check if the parseRule is still attached to keep this b/c
418+
if (!\in_array([$this, 'parseRule'], $router->getRules()['parsepreprocess'])) {
419+
$router->detachRule('parse', [$this, 'setLanguageApplicationState'], $router::PROCESS_BEFORE);
440420

441-
if (isset($this->sefs[$lang])) {
442-
// We found our language
443-
$found = true;
444-
$lang_code = $this->sefs[$lang]->lang_code;
421+
return;
445422
}
446423

447-
// We are called via POST or the nolangfilter url parameter was set. We don't care about the language
448-
// and simply set the default language as our current language.
449-
if (
450-
$app->getInput()->getMethod() === 'POST'
451-
|| $app->getInput()->get('nolangfilter', 0) == 1
452-
|| \count($app->getInput()->post) > 0
453-
|| \count($app->getInput()->files) > 0
454-
) {
455-
$found = true;
456-
457-
if (!isset($lang_code)) {
458-
$lang_code = $this->getLanguageCookie();
459-
}
460-
461-
if (!$lang_code && $this->params->get('detect_browser', 1)) {
462-
$lang_code = LanguageHelper::detectLanguage();
463-
}
424+
$lang_code = false;
464425

465-
if (!isset($this->lang_codes[$lang_code])) {
426+
// Our parse rule discovered a language
427+
if ($uri->hasVar('lang')) {
428+
$lang_code = $uri->getVar('lang');
429+
} else {
430+
/**
431+
* We don't know the language yet and want to discover it.
432+
* If we remove the default prefix, call by POST or have nolangfilter set,
433+
* we simply take the default language.
434+
*/
435+
if (
436+
$this->params->get('remove_default_prefix', 0)
437+
|| $this->getApplication()->getInput()->getMethod() === 'POST'
438+
|| $this->getApplication()->getInput()->get('nolangfilter', 0) == 1
439+
|| \count($this->getApplication()->getInput()->post) > 0
440+
|| \count($this->getApplication()->getInput()->files) > 0
441+
) {
466442
$lang_code = $this->default_lang;
467-
}
468-
}
469-
470-
// We have not found the language and thus need to redirect
471-
if (!$found) {
472-
// Lets find the default language for this user
473-
if (!isset($lang_code) || !isset($this->lang_codes[$lang_code])) {
474-
$lang_code = false;
443+
} else {
444+
$lang_code = $this->getLanguageCookie();
475445

476-
if ($this->params->get('detect_browser', 1)) {
446+
// No language code. Try using browser settings or default site language
447+
if (!$lang_code && $this->params->get('detect_browser', 0) == 1) {
477448
$lang_code = LanguageHelper::detectLanguage();
478-
479-
if (!isset($this->lang_codes[$lang_code])) {
480-
$lang_code = false;
481-
}
482449
}
483450

484451
if (!$lang_code) {
485452
$lang_code = $this->default_lang;
486453
}
487-
}
488-
489-
if ($this->mode_sef) {
490-
// Use the current language sef or the default one.
491-
if (
492-
$lang_code !== $this->default_lang
493-
|| !$this->params->get('remove_default_prefix', 0)
494-
) {
495-
$path = $this->lang_codes[$lang_code]->sef . '/' . $path;
496-
}
497454

498-
$uri->setPath($path);
455+
if (!$this->params->get('remove_default_prefix', 0) && $uri->getPath() == '') {
456+
if ($this->mode_sef) {
457+
$path = $this->lang_codes[$lang_code]->sef . '/' . $uri->getPath();
499458

500-
if (!$app->get('sef_rewrite')) {
501-
$uri->setPath('index.php/' . $uri->getPath());
502-
}
503-
504-
$redirectUri = $uri->base() . $uri->toString(['path', 'query', 'fragment']);
505-
} else {
506-
$uri->setVar('lang', $this->lang_codes[$lang_code]->sef);
507-
$redirectUri = $uri->base() . 'index.php?' . $uri->getQuery();
508-
}
459+
if (!$this->getApplication()->get('sef_rewrite')) {
460+
$path = 'index.php/' . $path;
461+
}
509462

510-
// Set redirect HTTP code to "302 Found".
511-
$redirectHttpCode = 302;
463+
$uri->setPath($path);
464+
} else {
465+
$uri->setPath('index.php');
466+
$uri->setVar('lang', $this->lang_codes[$lang_code]->sef);
467+
}
468+
$redirectHttpCode = 301;
469+
$redirectUri = $uri->base() . $uri->toString(['path', 'query', 'fragment']);
512470

513-
// If selected language is the default language redirect code is "301 Moved Permanently".
514-
if ($lang_code === $this->default_lang) {
515-
$redirectHttpCode = 301;
471+
// We cannot cache this redirect in browser. 301 is cacheable by default so we need to force to not cache it in browsers.
472+
$this->getApplication()->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true);
473+
$this->getApplication()->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
474+
$this->getApplication()->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate', false);
475+
$this->getApplication()->sendHeaders();
516476

517-
// We cannot cache this redirect in browser. 301 is cacheable by default so we need to force to not cache it in browsers.
518-
$app->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true);
519-
$app->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
520-
$app->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate', false);
521-
$app->sendHeaders();
477+
// Redirect to language.
478+
$this->getApplication()->redirect($redirectUri, $redirectHttpCode);
479+
}
522480
}
523-
524-
// Redirect to language.
525-
$app->redirect($redirectUri, $redirectHttpCode);
526481
}
527482

528483
// We have found our language and now need to set the cookie and the language value in our system
529484
$this->current_lang = $lang_code;
530485

531486
// Set the request var.
487+
$app = $this->getApplication();
532488
$app->getInput()->set('language', $lang_code);
533489
$app->set('language', $lang_code);
534490
$language = $app->getLanguage();
@@ -554,9 +510,9 @@ public function parseRule(&$router, &$uri)
554510
}
555511

556512
// Create a cookie.
557-
if ($this->getLanguageCookie() !== $lang_code) {
558-
$this->setLanguageCookie($lang_code);
559-
}
513+
$this->setLanguageCookie($lang_code);
514+
515+
$router->detachRule('parse', [$this, 'setLanguageApplicationState'], $router::PROCESS_BEFORE);
560516
}
561517

562518
/**

plugins/system/sef/src/Extension/Sef.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Joomla\CMS\Event\Application\AfterInitialiseEvent;
1515
use Joomla\CMS\Event\Application\AfterRenderEvent;
1616
use Joomla\CMS\Event\Application\AfterRouteEvent;
17+
use Joomla\CMS\Language\LanguageHelper;
1718
use Joomla\CMS\Plugin\CMSPlugin;
1819
use Joomla\CMS\Router\Route;
1920
use Joomla\CMS\Router\Router;
@@ -339,6 +340,15 @@ public function enforceSuffix()
339340
return;
340341
}
341342

343+
// We don't force a suffix for the language homepage
344+
$segments = explode('/', $route);
345+
$last = array_pop($segments);
346+
$sefs = LanguageHelper::getLanguages('sef');
347+
348+
if ($this->getApplication()->getLanguageFilter() && isset($sefs[$last])) {
349+
return;
350+
}
351+
342352
$suffix = pathinfo($route, PATHINFO_EXTENSION);
343353
$nonSEFSuffix = $origUri->getVar('format');
344354

0 commit comments

Comments
 (0)