diff --git a/core/lib/Drupal/Core/Path/AliasManager.php b/core/lib/Drupal/Core/Path/AliasManager.php index 5b43bf6..8c366f1 100644 --- a/core/lib/Drupal/Core/Path/AliasManager.php +++ b/core/lib/Drupal/Core/Path/AliasManager.php @@ -199,7 +199,7 @@ public function getAliasByPath($path, $langcode = NULL) { // Check the path whitelist, if the top-level part before the first / // is not in the list, then there is no need to do anything further, // it is not in the database. - if ($path === '/' || !$this->whitelist->get('/' . strtok(ltrim($path, '/'), '/'))) { + if ($path === '/' || !$this->whitelist->get(strtok(trim($path, '/'), '/'))) { return $path; } diff --git a/core/lib/Drupal/Core/Path/AliasWhitelist.php b/core/lib/Drupal/Core/Path/AliasWhitelist.php index 38ace76..fc26b77 100644 --- a/core/lib/Drupal/Core/Path/AliasWhitelist.php +++ b/core/lib/Drupal/Core/Path/AliasWhitelist.php @@ -107,7 +107,7 @@ public function get($offset) { * {@inheritdoc} */ public function resolveCacheMiss($root) { - $exists = $this->aliasStorage->pathHasMatchingAlias($root); + $exists = $this->aliasStorage->pathHasMatchingAlias('/' . $root); $this->storage[$root] = $exists; $this->persist($root); if ($exists) { diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php index d6e0999..8c65bc3 100644 --- a/core/modules/block/src/Tests/BlockTest.php +++ b/core/modules/block/src/Tests/BlockTest.php @@ -35,7 +35,7 @@ function testBlockVisibility() { ); // Set the block to be hidden on any user path, and to be shown only to // authenticated users. - $edit['visibility[request_path][pages]'] = 'user*'; + $edit['visibility[request_path][pages]'] = '/user*'; $edit['visibility[request_path][negate]'] = TRUE; $edit['visibility[user_role][roles][' . RoleInterface::AUTHENTICATED_ID . ']'] = TRUE; $this->drupalGet('admin/structure/block/add/' . $block_name . '/' . $default_theme); diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php index 95bd739..d8dec6a 100644 --- a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php +++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php @@ -64,7 +64,7 @@ function testPageCacheTags() { $block = $this->drupalPlaceBlock('views_block:comments_recent-block_1', array( 'visibility' => array( 'request_path' => array( - 'pages' => 'node/' . $node_2->id(), + 'pages' => '/node/' . $node_2->id(), ), ), )); diff --git a/core/modules/path/src/Form/PathFormBase.php b/core/modules/path/src/Form/PathFormBase.php index 6618dd9..be16b42 100644 --- a/core/modules/path/src/Form/PathFormBase.php +++ b/core/modules/path/src/Form/PathFormBase.php @@ -13,6 +13,7 @@ use Drupal\Core\Path\AliasManagerInterface; use Drupal\Core\Path\AliasStorageInterface; use Drupal\Core\Path\PathValidatorInterface; +use Drupal\Core\Routing\RequestContext; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -49,6 +50,13 @@ protected $pathValidator; /** + * The request context. + * + * @var \Drupal\Core\Routing\RequestContext + */ + protected $requestContext; + + /** * Constructs a new PathController. * * @param \Drupal\Core\Path\AliasStorageInterface $alias_storage @@ -57,11 +65,14 @@ * The path alias manager. * @param \Drupal\Core\Path\PathValidatorInterface $path_validator * The path validator. + * @param \Drupal\Core\Routing\RequestContext $request_context + * The request context. */ - public function __construct(AliasStorageInterface $alias_storage, AliasManagerInterface $alias_manager, PathValidatorInterface $path_validator) { + public function __construct(AliasStorageInterface $alias_storage, AliasManagerInterface $alias_manager, PathValidatorInterface $path_validator, RequestContext $request_context) { $this->aliasStorage = $alias_storage; $this->aliasManager = $alias_manager; $this->pathValidator = $path_validator; + $this->requestContext = $request_context; } /** @@ -71,7 +82,8 @@ public static function create(ContainerInterface $container) { return new static( $container->get('path.alias_storage'), $container->get('path.alias_manager'), - $container->get('path.validator') + $container->get('path.validator'), + $container->get('router.request_context') ); } @@ -94,8 +106,8 @@ public function buildForm(array $form, FormStateInterface $form_state, $pid = NU '#default_value' => $this->path['source'], '#maxlength' => 255, '#size' => 45, - '#description' => $this->t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1.'), - '#field_prefix' => $this->url('', [], ['absolute' => TRUE]), + '#description' => $this->t('Specify the existing path you wish to alias. For example: /node/28, /forum/1, /taxonomy/term/1.'), + '#field_prefix' => $this->requestContext->getCompleteBaseUrl(), '#required' => TRUE, ); $form['alias'] = array( @@ -104,8 +116,8 @@ public function buildForm(array $form, FormStateInterface $form_state, $pid = NU '#default_value' => $this->path['alias'], '#maxlength' => 255, '#size' => 45, - '#description' => $this->t('Specify an alternative path by which this data can be accessed. For example, type "about" when writing an about page. Use a relative path.'), - '#field_prefix' => $this->url('', [], ['absolute' => TRUE]), + '#description' => $this->t('Specify an alternative path by which this data can be accessed. For example, type "/about" when writing an about page. Use a relative path with a slash in front..'), + '#field_prefix' => $this->requestContext->getCompleteBaseUrl(), '#required' => TRUE, ); diff --git a/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php b/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php index 957484c..07f1712 100644 --- a/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php +++ b/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php @@ -96,7 +96,7 @@ public static function validateFormElement(array &$element, FormStateInterface $ } } - if ($alias[0] !== '/') { + if ($alias && $alias[0] !== '/') { $form_state->setError($element, t('The alias needs to start with a slash.')); } } diff --git a/core/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php index 45418a7b..2d6a5eb 100644 --- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php +++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php @@ -138,11 +138,11 @@ public function build(RouteMatchInterface $route_match) { $exclude[$front] = TRUE; // /user is just a redirect, so skip it. // @todo Find a better way to deal with /user. - $exclude['user'] = TRUE; + $exclude['/user'] = TRUE; while (count($path_elements) > 1) { array_pop($path_elements); // Copy the path elements for up-casting. - $route_request = $this->getRequestForPath(implode('/', $path_elements), $exclude); + $route_request = $this->getRequestForPath('/' . implode('/', $path_elements), $exclude); if ($route_request) { $route_match = RouteMatch::createFromRequest($route_request); $access = $this->accessManager->check($route_match, $this->currentUser); @@ -172,7 +172,7 @@ public function build(RouteMatchInterface $route_match) { * Matches a path in the router. * * @param string $path - * The request path. + * The request path with a leading slash. * @param array $exclude * An array of paths or system paths to skip. * @@ -185,7 +185,7 @@ protected function getRequestForPath($path, array $exclude) { } // @todo Use the RequestHelper once https://www.drupal.org/node/2090293 is // fixed. - $request = Request::create($this->context->getCompleteBaseUrl() . '/' . $path); + $request = Request::create($this->context->getCompleteBaseUrl() . $path); // Performance optimization: set a short accept header to reduce overhead in // AcceptHeaderMatcher when matching the request. $request->headers->set('Accept', 'text/html'); @@ -195,7 +195,7 @@ protected function getRequestForPath($path, array $exclude) { // This resolves to the front page, which we already add. return NULL; } - $this->currentPath->setPath('/' . $processed, $request); + $this->currentPath->setPath($processed, $request); // Attempt to match this path to provide a fully built request. try { $request->attributes->add($this->router->matchRequest($request)); diff --git a/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php b/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php index e9430b0..53f1efb 100644 --- a/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php +++ b/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php @@ -56,10 +56,10 @@ function testContentRouting() { /** @var \Drupal\Core\Path\AliasStorageInterface $path_alias_storage */ $path_alias_storage = $this->container->get('path.alias_storage'); // Alias with extension pointing to no extension/constant content-type. - $path_alias_storage->save('conneg/html', 'alias.html'); + $path_alias_storage->save('/conneg/html', '/alias.html'); // Alias with extension pointing to dynamic extension/linked content-type. - $path_alias_storage->save('conneg/html?_format=json', 'alias.json'); + $path_alias_storage->save('/conneg/html?_format=json', '/alias.json'); $tests = [ // ['path', 'accept', 'content-type'], @@ -102,7 +102,7 @@ function testContentRouting() { $accept_header = $test[1]; $content_type = $test[2]; $message = "Testing path:$path Accept:$accept_header Content-type:$content_type"; - $request = Request::create($path); + $request = Request::create('/' . $path); if ($accept_header) { $request->headers->set('Accept', $accept_header); } @@ -141,7 +141,7 @@ public function testFullNegotiation() { $accept_header = $test[1]; $content_type = $test[2]; $message = "Testing path:$path Accept:$accept_header Content-type:$content_type"; - $request = Request::create($path); + $request = Request::create('/' . $path); $request->headers->set('Accept', $accept_header); /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $kernel */ diff --git a/core/modules/system/src/Tests/System/AccessDeniedTest.php b/core/modules/system/src/Tests/System/AccessDeniedTest.php index 17cb9d5..ebc5eea 100644 --- a/core/modules/system/src/Tests/System/AccessDeniedTest.php +++ b/core/modules/system/src/Tests/System/AccessDeniedTest.php @@ -44,7 +44,7 @@ function testAccessDenied() { // Use a custom 403 page. $this->drupalLogin($this->adminUser); $edit = [ - 'site_403' => 'user/' . $this->adminUser->id(), + 'site_403' => '/user/' . $this->adminUser->id(), ]; $this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration')); @@ -73,7 +73,7 @@ function testAccessDenied() { // Log back in, set the custom 403 page to /user/login and remove the block $this->drupalLogin($this->adminUser); - $this->config('system.site')->set('page.403', 'user/login')->save(); + $this->config('system.site')->set('page.403', '/user/login')->save(); $edit = [ 'region' => -1, ]; diff --git a/core/tests/Drupal/Tests/Core/Path/PathMatcherTest.php b/core/tests/Drupal/Tests/Core/Path/PathMatcherTest.php index 1d1aaf7..e60f8f5 100644 --- a/core/tests/Drupal/Tests/Core/Path/PathMatcherTest.php +++ b/core/tests/Drupal/Tests/Core/Path/PathMatcherTest.php @@ -121,43 +121,43 @@ public function getMatchPathData() { ), array( // Multiple paths with the \n delimiter. - "node/*\nnode/*/edit", + "/node/*\n/node/*/edit", array( - 'node/1' => TRUE, - 'node/view' => TRUE, - 'node/32/edit' => TRUE, - 'node/delete/edit' => TRUE, - 'node/50/delete' => TRUE, - 'test/example' => FALSE, + '/node/1' => TRUE, + '/node/view' => TRUE, + '/node/32/edit' => TRUE, + '/node/delete/edit' => TRUE, + '/node/50/delete' => TRUE, + '/test/example' => FALSE, ), ), array( // Multiple paths with the \r delimiter. - "user/*\rexample/*", + "/user/*\r/example/*", array( - 'user/1' => TRUE, - 'example/1' => TRUE, - 'user/1/example/1' => TRUE, - 'user/example' => TRUE, - 'test/example' => FALSE, - 'user' => FALSE, - 'example' => FALSE, + '/user/1' => TRUE, + '/example/1' => TRUE, + '/user/1/example/1' => TRUE, + '/user/example' => TRUE, + '/test/example' => FALSE, + '/user' => FALSE, + '/example' => FALSE, ), ), array( // Multiple paths with the \r\n delimiter. - "test\r\n", + "/test\r\n", array( - 'test' => TRUE, - 'dummy' => TRUE, - 'example' => FALSE, + '/test' => TRUE, + '/dummy' => TRUE, + '/example' => FALSE, ), ), array( // Test existing regular expressions (should be escaped). '[^/]+?/[0-9]', array( - 'test/1' => FALSE, + '/test/1' => FALSE, '[^/]+?/[0-9]' => TRUE, ), ), diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php index 4560881..79b49a2 100644 --- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php +++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php @@ -103,11 +103,11 @@ function testProcessInbound() { $system_path_map = array( // Set up one proper alias that can be resolved to a system path. - array('foo', NULL, 'user/1'), + array('/foo', NULL, '/user/1'), // Passing in anything else should return the same string. - array('fr/foo', NULL, 'fr/foo'), - array('fr', NULL, 'fr'), - array('user/login', NULL, 'user/login'), + array('/fr/foo', NULL, '/fr/foo'), + array('/fr', NULL, '/fr'), + array('/user/login', NULL, '/user/login'), ); $alias_manager->expects($this->any()) @@ -169,16 +169,16 @@ function testProcessInbound() { } // Test resolving the French homepage using the incorrect processor order. - $test_path = 'fr'; + $test_path = '/fr'; $request = Request::create($test_path); $processed = $processor_manager->processInbound($test_path, $request); - $this->assertEquals('', $processed, 'Processing in the incorrect order fails to resolve the system path from the empty path'); + $this->assertEquals('/', $processed, 'Processing in the incorrect order fails to resolve the system path from the empty path'); // Test resolving an existing alias using the incorrect processor order. - $test_path = 'fr/foo'; + $test_path = '/fr/foo'; $request = Request::create($test_path); $processed = $processor_manager->processInbound($test_path, $request); - $this->assertEquals('foo', $processed, 'Processing in the incorrect order fails to resolve the system path from an alias'); + $this->assertEquals('/foo', $processed, 'Processing in the incorrect order fails to resolve the system path from an alias'); // Now create a new processor manager and add the processors, this time in // the correct order. @@ -194,15 +194,15 @@ function testProcessInbound() { } // Test resolving the French homepage using the correct processor order. - $test_path = 'fr'; + $test_path = '/fr'; $request = Request::create($test_path); $processed = $processor_manager->processInbound($test_path, $request); - $this->assertEquals('user/login', $processed, 'Processing in the correct order resolves the system path from the empty path.'); + $this->assertEquals('/user/login', $processed, 'Processing in the correct order resolves the system path from the empty path.'); // Test resolving an existing alias using the correct processor order. - $test_path = 'fr/foo'; + $test_path = '/fr/foo'; $request = Request::create($test_path); $processed = $processor_manager->processInbound($test_path, $request); - $this->assertEquals('user/1', $processed, 'Processing in the correct order resolves the system path from an alias.'); + $this->assertEquals('/user/1', $processed, 'Processing in the correct order resolves the system path from an alias.'); } }