diff -u b/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php --- b/core/lib/Drupal/Core/Url.php +++ b/core/lib/Drupal/Core/Url.php @@ -199,7 +199,13 @@ * @param string $user_input * User input for a link or path. The first character must be one of the * following characters: - * - '/': A base-relative path for the current site. + * - '/': A path within the current site. This path might be to a Drupal + * route (e.g., '/admin'), to a file (e.g., '/README.txt'), or to + * something processed by a non-Drupal script (e.g., + * '/not/a/drupal/page'). If the path matches a Drupal route, then the + * URL generation will include Drupal's path processors (e.g., + * language-prefixing and aliasing). Otherwise, the URL generation will + * just append the passed-in path to Drupal's base path. * - '?': A query string for the current page or resource. * - '#': A fragment (jump-link) on the current page or resource. * This helps reduce ambiguity for user-entered links and paths, and @@ -221,11 +227,16 @@ * characters: '/', '?', or '#'. */ public static function fromUserInput($user_input, $options = []) { + // Ensuring one of these initial characters also enforces that what is + // passed is a relative URI reference rather than an absolute URI, + // because these are URI reserved characters that a scheme name may not + // start with. if ((strpos($user_input, '/') !== 0) && (strpos($user_input, '#') !== 0) && (strpos($user_input, '?') !== 0)) { throw new \InvalidArgumentException(String::format("The user-entered string @user_input must begin with a '/', '?', or '#'.", ['@user_input' => $user_input])); } - // Appending the scheme below ensures that the user input is treated as a - // relative (not absolute) reference. + + // fromUri() requires an absolute URI, so prepend the appropriate scheme + // name. return static::fromUri('user-path:' . $user_input, $options); } diff -u b/core/modules/path/src/Controller/PathController.php b/core/modules/path/src/Controller/PathController.php --- b/core/modules/path/src/Controller/PathController.php +++ b/core/modules/path/src/Controller/PathController.php @@ -86,7 +86,8 @@ $destination = drupal_get_destination(); foreach ($this->aliasStorage->getAliasesForAdminListing($header, $keys) as $data) { $row = array(); - // @todo Should Path module store leading slashes? Needs followup. + // @todo Should Path module store leading slashes? See + // https://www.drupal.org/node/2430593. $row['data']['alias'] = $this->l(Unicode::truncate($data->alias, 50, FALSE, TRUE), Url::fromUserInput('/' . $data->source, array( 'attributes' => array('title' => $data->alias), ))); diff -u b/core/modules/views/src/Plugin/views/row/RssFields.php b/core/modules/views/src/Plugin/views/row/RssFields.php --- b/core/modules/views/src/Plugin/views/row/RssFields.php +++ b/core/modules/views/src/Plugin/views/row/RssFields.php @@ -160,8 +160,8 @@ $item_guid = $this->getField($row_index, $this->options['guid_field_options']['guid_field']); if ($this->options['guid_field_options']['guid_field_is_permalink']) { $guid_is_permalink_string = 'true'; - // @todo Views should expect and store a leading /. See: - // https://www.drupal.org/node/2423913 + // @todo Enforce GUIDs as system-generated rather than user input? See + // https://www.drupal.org/node/2430589. $item_guid = Url::fromUserInput('/' . $item_guid)->setAbsolute()->toString(); } $item->elements[] = array( diff -u b/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc --- b/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -880,8 +880,8 @@ // Compare the link to the default home page; if it's the default home page, // just use $base_url. $url_string = $url->setOptions($url_options)->toString(); - // @todo Views should expect and store a leading /. See: - // https://www.drupal.org/node/2423913 + // @todo Should page.front be stored with a leading slash? See + // https://www.drupal.org/node/2430595. if ($url_string === Url::fromUserInput('/' . $config->get('page.front'))->toString()) { $url_string = Url::fromRoute('')->setAbsolute()->toString(); }