Skip to content
Snippets Groups Projects
Commit 60ee2692 authored by Derek Wright's avatar Derek Wright
Browse files

Merge branch '2418369-internal-url-handling' into '2.0.x'

Bug #2418369: Internal URL handling (language prefixes, base://, ...) (patch...

See merge request !12
parents 1eb0b8ba c0e6d0a8
No related branches found
No related tags found
No related merge requests found
Pipeline #64028 passed with warnings
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
declare(strict_types=1); declare(strict_types=1);
use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Html;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\Url; use Drupal\Core\Url;
/** /**
...@@ -128,13 +129,14 @@ function _pathologic_replace($matches) { ...@@ -128,13 +129,14 @@ function _pathologic_replace($matches) {
// guess… Note that we don't do the & thing here so that we can modify // guess… Note that we don't do the & thing here so that we can modify
// $cached_settings later and not have the changes be "permanent." // $cached_settings later and not have the changes be "permanent."
$cached_settings = drupal_static('_pathologic_filter'); $cached_settings = drupal_static('_pathologic_filter');
// If it appears the path is a scheme-less URL, prepend a scheme to it. // If it appears the path is a scheme-less URL, prepend a scheme to it.
// parse_url() cannot properly parse scheme-less URLs. Don't worry; if it // parse_url() cannot properly parse scheme-less URLs. Don't worry; if it
// looks like Pathologic can't handle the URL, it will return the scheme-less // looks like Pathologic can't handle the URL, it will return the scheme-less
// original. // original.
// @see https://drupal.org/node/1617944 // @see https://drupal.org/node/1617944
// @see https://drupal.org/node/2030789 // @see https://drupal.org/node/2030789
if (strpos($matches[2], '//') === 0) { if (str_starts_with($matches[2], '//')) {
if (\Drupal::request()->isSecure()) { if (\Drupal::request()->isSecure()) {
$matches[2] = 'https:' . $matches[2]; $matches[2] = 'https:' . $matches[2];
} }
...@@ -180,6 +182,12 @@ function _pathologic_replace($matches) { ...@@ -180,6 +182,12 @@ function _pathologic_replace($matches) {
$parts['path'] = ''; $parts['path'] = '';
} }
// Variable to define whether we need to rewrite/transform the URL through a
// URL object.
$dont_rewrite = FALSE;
// Variable to define whether the given path is a file path.
$is_file = FALSE;
// Check to see if we're dealing with a file. // Check to see if we're dealing with a file.
// @todo Should we still try to do path correction on these files too? // @todo Should we still try to do path correction on these files too?
if (isset($parts['scheme']) && $parts['scheme'] === 'files') { if (isset($parts['scheme']) && $parts['scheme'] === 'files') {
...@@ -194,10 +202,14 @@ function _pathologic_replace($matches) { ...@@ -194,10 +202,14 @@ function _pathologic_replace($matches) {
$new_parts['path'] = rawurldecode($new_parts['path']); $new_parts['path'] = rawurldecode($new_parts['path']);
$parts = $new_parts; $parts = $new_parts;
// Don't do language handling for file paths. // Don't do language handling for file paths.
$cached_settings['is_file'] = TRUE; $is_file = TRUE;
} }
else { // Check to see if instead of a 'files:' scheme we have a normal internal
$cached_settings['is_file'] = FALSE; // file url starting with the public base path.
elseif (str_contains($parts['path'], PublicStream::basePath())) {
// This url should not be turned into a URL object, because we don't want
// language handling for this path.
$dont_rewrite = TRUE;
} }
// Let's also bail out of this doesn't look like a local path. // Let's also bail out of this doesn't look like a local path.
...@@ -291,14 +303,14 @@ function _pathologic_replace($matches) { ...@@ -291,14 +303,14 @@ function _pathologic_replace($matches) {
// If we didn't previously identify this as a file, check to see if the file // If we didn't previously identify this as a file, check to see if the file
// exists now that we have the correct path relative to DRUPAL_ROOT // exists now that we have the correct path relative to DRUPAL_ROOT
if (!$cached_settings['is_file']) { if (!$is_file) {
$cached_settings['is_file'] = !empty($parts['path']) && is_file(DRUPAL_ROOT . '/' . $parts['path']); $is_file = !empty($parts['path']) && is_file(DRUPAL_ROOT . '/' . $parts['path']);
} }
// Okay, deal with language stuff. // Let's see if path has a language prefix, so we can distinguish the target
// Let's see if we can split off a language prefix from the path. // language for this path.
$keep_language_prefix = $cached_settings['current_settings']['keep_language_prefix'] ?? FALSE; $specific_language = NULL;
if ($keep_language_prefix === FALSE && \Drupal::moduleHandler()->moduleExists('language')) { if (\Drupal::moduleHandler()->moduleExists('language')) {
// This logic is based on // This logic is based on
// \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::getLangcode(). // \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::getLangcode().
$languages = \Drupal::languageManager()->getLanguages(); $languages = \Drupal::languageManager()->getLanguages();
...@@ -311,8 +323,10 @@ function _pathologic_replace($matches) { ...@@ -311,8 +323,10 @@ function _pathologic_replace($matches) {
// Search for prefix within added languages. // Search for prefix within added languages.
foreach ($languages as $language) { foreach ($languages as $language) {
if (isset($config['prefixes'][$language->getId()]) && $config['prefixes'][$language->getId()] == $prefix) { if (isset($config['prefixes'][$language->getId()]) && $config['prefixes'][$language->getId()] == $prefix) {
$parts['path'] = implode('/', $path_args); $specific_language = $language;
$parts['language_obj'] = $language; if (($cached_settings['current_settings']['keep_language_prefix'] ?? FALSE) === FALSE) {
$parts['path'] = implode('/', $path_args);
}
break; break;
} }
} }
...@@ -336,7 +350,7 @@ function _pathologic_replace($matches) { ...@@ -336,7 +350,7 @@ function _pathologic_replace($matches) {
'absolute' => $cached_settings['current_settings']['protocol_style'] !== 'path', 'absolute' => $cached_settings['current_settings']['protocol_style'] !== 'path',
// If we seem to have found a language for the path, pass it along to // If we seem to have found a language for the path, pass it along to
// url(). Otherwise, ignore the 'language' parameter. // url(). Otherwise, ignore the 'language' parameter.
'language' => isset($parts['language_obj']) ? $parts['language_obj'] : NULL, 'language' => $specific_language ?? NULL,
// A special parameter not actually used by url(), but we use it to see if // A special parameter not actually used by url(), but we use it to see if
// an alter hook implementation wants us to just pass through the original // an alter hook implementation wants us to just pass through the original
// URL. // URL.
...@@ -361,8 +375,28 @@ function _pathologic_replace($matches) { ...@@ -361,8 +375,28 @@ function _pathologic_replace($matches) {
if ($parts['path'] == '<front>') { if ($parts['path'] == '<front>') {
$url = Url::fromRoute('<front>', [], $url_params['options'])->toString(); $url = Url::fromRoute('<front>', [], $url_params['options'])->toString();
} }
elseif ($dont_rewrite) {
// The URL is just the internal path that doesn't need rewriting.
$url = $parts['path'];
if (!str_starts_with($url, '/')) {
$url = '/' . $url;
}
}
else { else {
$path = (empty($url_params['options']['external']) ? 'base://' : '') . $url_params['path']; // If we've been told this is already an external URL, leave it alone.
if (!empty($url_params['options']['external'])) {
$scheme = '';
}
// If it's a file, use 'base:' so that the path is not re-written.
elseif ($is_file) {
$scheme = 'base:/';
}
// For everything we did not recognize as external or files, we use the
// internal scheme so aliases and language prefixes are set correctly.
else {
$scheme = 'internal:/';
}
$path = $scheme . $url_params['path'];
try { try {
$url = Url::fromUri($path, $url_params['options'])->toString(); $url = Url::fromUri($path, $url_params['options'])->toString();
} }
......
...@@ -9,6 +9,7 @@ use Drupal\Tests\BrowserTestBase; ...@@ -9,6 +9,7 @@ use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait; use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
use Drupal\Tests\node\Traits\NodeCreationTrait; use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\Tests\pathologic\Traits\PathologicFormatTrait; use Drupal\Tests\pathologic\Traits\PathologicFormatTrait;
use Drupal\Tests\Traits\Core\PathAliasTestTrait;
use Drupal\Tests\user\Traits\UserCreationTrait; use Drupal\Tests\user\Traits\UserCreationTrait;
/** /**
...@@ -20,6 +21,7 @@ class PathologicLanguageTest extends BrowserTestBase { ...@@ -20,6 +21,7 @@ class PathologicLanguageTest extends BrowserTestBase {
use ContentTypeCreationTrait; use ContentTypeCreationTrait;
use NodeCreationTrait; use NodeCreationTrait;
use PathAliasTestTrait;
use PathologicFormatTrait; use PathologicFormatTrait;
use UserCreationTrait; use UserCreationTrait;
...@@ -32,6 +34,8 @@ class PathologicLanguageTest extends BrowserTestBase { ...@@ -32,6 +34,8 @@ class PathologicLanguageTest extends BrowserTestBase {
'language', 'language',
'locale', 'locale',
'node', 'node',
'path',
'path_alias',
'pathologic', 'pathologic',
'text', 'text',
'user', 'user',
...@@ -94,7 +98,7 @@ class PathologicLanguageTest extends BrowserTestBase { ...@@ -94,7 +98,7 @@ class PathologicLanguageTest extends BrowserTestBase {
'title' => 'Reference page', 'title' => 'Reference page',
]); ]);
// Add translations for the reference node, and try a whole // Add translations and path aliases for the reference node, and try a whole
// series of possible input texts to see how they are handled. // series of possible input texts to see how they are handled.
$node_to_reference->addTranslation('fr', [ $node_to_reference->addTranslation('fr', [
'title' => 'Page de référence en français', 'title' => 'Page de référence en français',
...@@ -103,6 +107,10 @@ class PathologicLanguageTest extends BrowserTestBase { ...@@ -103,6 +107,10 @@ class PathologicLanguageTest extends BrowserTestBase {
'title' => 'Página de referência em Português', 'title' => 'Página de referência em Português',
])->save(); ])->save();
$this->createPathAlias('/node/' . $node_to_reference->id(), '/reference-en', 'en');
$this->createPathAlias('/node/' . $node_to_reference->id(), '/reference-fr', 'fr');
$this->createPathAlias('/node/' . $node_to_reference->id(), '/referencia-pt', 'pt-br');
global $base_path; global $base_path;
$nid = $node_to_reference->id(); $nid = $node_to_reference->id();
...@@ -115,19 +123,19 @@ class PathologicLanguageTest extends BrowserTestBase { ...@@ -115,19 +123,19 @@ class PathologicLanguageTest extends BrowserTestBase {
"$langcode: file links do not get a language prefix", "$langcode: file links do not get a language prefix",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'node/' . $nid . '">Test node link</a>', '<a href="' . $base_path . 'reference-en">Test node link</a>',
$this->runFilter('<a href="/node/' . $nid . '">Test node link</a>', $langcode), $this->runFilter('<a href="/node/' . $nid . '">Test node link</a>', $langcode),
"$langcode: node/N link should be unchanged" "$langcode: node/N link uses EN alias",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'fr/node/' . $nid . '">Test node link</a>', '<a href="' . $base_path . 'fr/reference-fr">Test node link</a>',
$this->runFilter('<a href="/fr/node/' . $nid . '">Test node link</a>', $langcode), $this->runFilter('<a href="/fr/node/' . $nid . '">Test node link</a>', $langcode),
"$langcode: fr/node/N link should be unchanged", "$langcode: fr/node/N link uses the FR alias",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'pt-br/node/' . $nid . '">Test node link</a>', '<a href="' . $base_path . 'pt-br/referencia-pt">Test node link</a>',
$this->runFilter('<a href="/pt-br/node/' . $nid . '">Test node link</a>', $langcode), $this->runFilter('<a href="/pt-br/node/' . $nid . '">Test node link</a>', $langcode),
"$langcode: pt-br/node/N link should be unchanged", "$langcode: pt-br/node/N link uses the PT-BR alias",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'reference-en">Test node link</a>', '<a href="' . $base_path . 'reference-en">Test node link</a>',
...@@ -161,19 +169,19 @@ class PathologicLanguageTest extends BrowserTestBase { ...@@ -161,19 +169,19 @@ class PathologicLanguageTest extends BrowserTestBase {
"$langcode: file links do not get a language prefix", "$langcode: file links do not get a language prefix",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'node/' . $nid . '">Test node link</a>', '<a href="' . $base_path . 'reference-en">Test node link</a>',
$this->runFilter('<a href="/node/' . $nid . '">Test node link</a>', $langcode), $this->runFilter('<a href="/node/' . $nid . '">Test node link</a>', $langcode),
"$langcode: node/N link is unchanged" "$langcode: node/N link uses EN alias",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'node/' . $nid . '">Test node link</a>', '<a href="' . $base_path . 'fr/reference-fr">Test node link</a>',
$this->runFilter('<a href="/fr/node/' . $nid . '">Test node link</a>', $langcode), $this->runFilter('<a href="/fr/node/' . $nid . '">Test node link</a>', $langcode),
"$langcode: fr/node/N link should have no langcode prefix" "$langcode: fr/node/N link should use the FR alias with langcode, even if prefix is stripped",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'node/' . $nid . '">Test node link</a>', '<a href="' . $base_path . 'pt-br/referencia-pt">Test node link</a>',
$this->runFilter('<a href="/pt-br/node/' . $nid . '">Test node link</a>', $langcode), $this->runFilter('<a href="/pt-br/node/' . $nid . '">Test node link</a>', $langcode),
"$langcode: pt-br/node/N link should have no langcode prefix" "$langcode: pt-br/node/N link should use the PT-BR alias with langcode, even if the prefix is stripped",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'reference-en">Test node link</a>', '<a href="' . $base_path . 'reference-en">Test node link</a>',
...@@ -183,12 +191,12 @@ class PathologicLanguageTest extends BrowserTestBase { ...@@ -183,12 +191,12 @@ class PathologicLanguageTest extends BrowserTestBase {
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'reference-fr">Test node link</a>', '<a href="' . $base_path . 'reference-fr">Test node link</a>',
$this->runFilter('<a href="/fr/reference-fr">Test node link</a>', $langcode), $this->runFilter('<a href="/fr/reference-fr">Test node link</a>', $langcode),
"$langcode: fr/reference-fr link should have no langcode prefix", "$langcode: fr/reference-fr link (no URL processing) uses the FR alias with langcode removed",
); );
$this->assertSame( $this->assertSame(
'<a href="' . $base_path . 'referencia-pt">Test node link</a>', '<a href="' . $base_path . 'referencia-pt">Test node link</a>',
$this->runFilter('<a href="/pt-br/referencia-pt">Test node link</a>', $langcode), $this->runFilter('<a href="/pt-br/referencia-pt">Test node link</a>', $langcode),
"$langcode: pt-br/referencia-pt link should have no langcode prefix", "$langcode: pt-br/referencia-pt link (no URL processing) uses the PT-BR alias with langcode removed",
); );
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment