diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 07ea4bd..e2836f8 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -6,7 +6,7 @@ use Drupal\Component\Utility\String; use Drupal\Component\Utility\Timer; use Drupal\Component\Utility\Unicode; -use Drupal\Component\Utility\UrlValidator; +use Drupal\Component\Utility\Url; use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; use Drupal\Core\DependencyInjection\ContainerBuilder; diff --git a/core/includes/common.inc b/core/includes/common.inc index 54dc70c..fb0abac 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4,7 +4,7 @@ use Drupal\Component\Utility\Json; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Tags; -use Drupal\Component\Utility\UrlValidator; +use Drupal\Component\Utility\Url; use Drupal\Component\Utility\Xss; use Drupal\Core\Cache\Cache; use Drupal\Core\Language\Language; @@ -440,56 +440,14 @@ function drupal_get_feeds($delimiter = "\n") { * * @return * An array containing query parameters, which can be used for url(). + * + * @deprecated as of Drupal 8.0. Use Url::filterQueryParameters() instead. */ function drupal_get_query_parameters(array $query = NULL, array $exclude = array(), $parent = '') { - // Set defaults, if none given. if (!isset($query)) { - $query = $_GET; - } - // If $exclude is empty, there is nothing to filter. - if (empty($exclude)) { - return $query; - } - elseif (!$parent) { - $exclude = array_flip($exclude); - } - - $params = array(); - foreach ($query as $key => $value) { - $string_key = ($parent ? $parent . '[' . $key . ']' : $key); - if (isset($exclude[$string_key])) { - continue; - } - - if (is_array($value)) { - $params[$key] = drupal_get_query_parameters($value, $exclude, $string_key); - } - else { - $params[$key] = $value; - } - } - - return $params; -} - -/** - * Splits a URL-encoded query string into an array. - * - * @param $query - * The query string to split. - * - * @return - * An array of URL decoded couples $param_name => $value. - */ -function drupal_get_query_array($query) { - $result = array(); - if (!empty($query)) { - foreach (explode('&', $query) as $param) { - $param = explode('=', $param); - $result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : ''; - } + $query = Drupal::request()->query->all(); } - return $result; + return Url::filterQueryParameters($query, $exclude, $parent); } /** @@ -497,12 +455,13 @@ function drupal_get_query_array($query) { * * @see \Drupal\Core\Routing\PathBasedGeneratorInterface::httpBuildQuery() * @see drupal_get_query_parameters() - * @deprecated as of Drupal 8.0. Use - * Drupal::urlGenerator()->httpBuildQuery() instead. + * @deprecated as of Drupal 8.0. Use Url::buildQuery() instead. * @ingroup php_wrappers + * + * @deprecated as of Drupal 8.0. Use Url::buildQuery() instead. */ function drupal_http_build_query(array $query, $parent = '') { - return Drupal::urlGenerator()->httpBuildQuery($query, $parent); + return Url::buildQuery($query, $parent); } /** @@ -527,12 +486,13 @@ function drupal_get_destination() { return $destination; } - if (isset($_GET['destination'])) { - $destination = array('destination' => $_GET['destination']); + $query = Drupal::request()->query; + if ($query->has('destination')) { + $destination = array('destination' => $query->get('destination')); } else { $path = current_path(); - $query = Drupal::urlGenerator()->httpBuildQuery(drupal_get_query_parameters()); + $query = Url::buildQuery(Url::filterQueryParameters($query->all())); if ($query != '') { $path .= '?' . $query; } @@ -573,46 +533,11 @@ function drupal_get_destination() { * * @see url() * @ingroup php_wrappers + * + * @deprecated as of Drupal 8.0. Use Url::parse() instead. */ function drupal_parse_url($url) { - $options = array( - 'path' => NULL, - 'query' => array(), - 'fragment' => '', - ); - - // External URLs: not using parse_url() here, so we do not have to rebuild - // the scheme, host, and path without having any use for it. - if (strpos($url, '://') !== FALSE) { - // Split off everything before the query string into 'path'. - $parts = explode('?', $url); - $options['path'] = $parts[0]; - // If there is a query string, transform it into keyed query parameters. - if (isset($parts[1])) { - $query_parts = explode('#', $parts[1]); - parse_str($query_parts[0], $options['query']); - // Take over the fragment, if there is any. - if (isset($query_parts[1])) { - $options['fragment'] = $query_parts[1]; - } - } - } - // Internal URLs. - else { - // parse_url() does not support relative URLs, so make it absolute. E.g. the - // relative URL "foo/bar:1" isn't properly parsed. - $parts = parse_url('http://example.com/' . $url); - // Strip the leading slash that was just added. - $options['path'] = substr($parts['path'], 1); - if (isset($parts['query'])) { - parse_str($parts['query'], $options['query']); - } - if (isset($parts['fragment'])) { - $options['fragment'] = $parts['fragment']; - } - } - - return $options; + return Url::parse($url); } /** @@ -625,9 +550,11 @@ function drupal_parse_url($url) { * * @param $path * The Drupal path to encode. + * + * @deprecated as of Drupal 8.0. Use Url::encodePath() instead. */ function drupal_encode_path($path) { - return str_replace('%2F', '/', rawurlencode($path)); + return Url::encodePath($path); } /** @@ -638,20 +565,11 @@ function drupal_encode_path($path) { * * @return * TRUE if the URL has the same domain and base path. + * + * @deprecated as of Drupal 8.0. Use Url::externalIsLocal() instead. */ function _external_url_is_local($url) { - $url_parts = parse_url($url); - $base_host = parse_url($GLOBALS['base_url'], PHP_URL_HOST); - - if (!isset($url_parts['path'])) { - return ($url_parts['host'] == $base_host); - } - else { - // When comparing base paths, we need a trailing slash to make sure a - // partial URL match isn't occuring. Since base_path() always returns with - // a trailing slash, we don't need to add the trailing slash here. - return ($url_parts['host'] == $base_host && stripos($url_parts['path'], base_path()) === 0); - } + return Url::externalIsLocal($url, base_path()); } /** @@ -705,12 +623,12 @@ function valid_email_address($mail) { * @return * TRUE if the URL is in a valid format. * - * @see \Drupal\Component\Utility\UrlValidator::isValid() + * @see \Drupal\Component\Utility\Url::isValid() * - * @deprecated as of Drupal 8.0. Use UrlValidator::isValid() instead. + * @deprecated as of Drupal 8.0. Use Url::isValid() instead. */ function valid_url($url, $absolute = FALSE) { - return UrlValidator::isValid($url, $absolute); + return Url::isValid($url, $absolute); } /** @@ -795,7 +713,7 @@ function valid_number_step($value, $step, $offset = 0.0) { * @see \Drupal\Component\Utility\Url::stripDangerousProtocols() */ function drupal_strip_dangerous_protocols($uri) { - return UrlValidator::stripDangerousProtocols($uri); + return Url::stripDangerousProtocols($uri); } /** @@ -817,7 +735,7 @@ function drupal_strip_dangerous_protocols($uri) { * @see \Drupal\Component\Utility\String::checkPlain() */ function check_url($uri) { - return String::checkPlain(UrlValidator::stripDangerousProtocols($uri)); + return String::checkPlain(Url::stripDangerousProtocols($uri)); } /** @@ -885,7 +803,7 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', * @see \Drupal\Component\Utility\Url::filterBadProtocol() */ function filter_xss_bad_protocol($string) { - return UrlValidator::filterBadProtocol($string); + return Url::filterBadProtocol($string); } /** @@ -1313,11 +1231,7 @@ function url($path = NULL, array $options = array()) { * Boolean TRUE or FALSE, where TRUE indicates an external path. */ function url_is_external($path) { - $colonpos = strpos($path, ':'); - // Avoid calling drupal_strip_dangerous_protocols() if there is any - // slash (/), hash (#) or question_mark (?) before the colon (:) - // occurrence - if any - as this would clearly mean it is not a URL. - return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path; + return Url::isExternal($path); } /** @@ -4019,7 +3933,7 @@ function _drupal_bootstrap_code() { // of allowed protocols for these cases. $allowed_protocols = array('http', 'https'); } - UrlValidator::setAllowedProtocols($allowed_protocols); + Url::setAllowedProtocols($allowed_protocols); } /** diff --git a/core/includes/theme.inc b/core/includes/theme.inc index a396619..7b46693 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -2215,27 +2215,6 @@ function template_preprocess_tablesort_indicator(&$variables) { } /** - * Returns HTML for a marker for new or updated content. - * - * @param $variables - * An associative array containing: - * - type: Number representing the marker type to display. See MARK_NEW, - * MARK_UPDATED, MARK_READ. - */ -function theme_mark($variables) { - $type = $variables['status']; - global $user; - if ($user->uid) { - if ($type == MARK_NEW) { - return ' ' . t('new') . ''; - } - elseif ($type == MARK_UPDATED) { - return ' ' . t('updated') . ''; - } - } -} - -/** * Preprocesses variables for theme_item_list(). * * @param array $variables @@ -3114,6 +3093,7 @@ function drupal_common_theme() { ), 'mark' => array( 'variables' => array('status' => MARK_NEW), + 'template' => 'mark', ), 'item_list' => array( 'variables' => array('items' => array(), 'title' => '', 'list_type' => 'ul', 'attributes' => array()), diff --git a/core/lib/Drupal/Component/Utility/Url.php b/core/lib/Drupal/Component/Utility/Url.php new file mode 100644 index 0000000..cdc01c8 --- /dev/null +++ b/core/lib/Drupal/Component/Utility/Url.php @@ -0,0 +1,353 @@ + $value) { + $key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key)); + + // Recurse into children. + if (is_array($value)) { + $params[] = static::buildQuery($value, $key); + } + // If a query parameter value is NULL, only append its key. + elseif (!isset($value)) { + $params[] = $key; + } + else { + // For better readability of paths in query strings, we decode slashes. + $params[] = $key . '=' . str_replace('%2F', '/', rawurlencode($value)); + } + } + + return implode('&', $params); + } + + /** + * Filters a URL query parameter array to remove unwanted elements. + * + * @param array $query + * An array to be processed. + * @param array $exclude + * (optional) A list of $query array keys to remove. Use "parent[child]" to + * exclude nested items. + * @param string $parent + * Internal use only. Used to build the $query array key for nested items. + * + * @return + * An array containing query parameters. + */ + public static function filterQueryParameters(array $query, array $exclude = array(), $parent = '') { + // If $exclude is empty, there is nothing to filter. + if (empty($exclude)) { + return $query; + } + elseif (!$parent) { + $exclude = array_flip($exclude); + } + + $params = array(); + foreach ($query as $key => $value) { + $string_key = ($parent ? $parent . '[' . $key . ']' : $key); + if (isset($exclude[$string_key])) { + continue; + } + + if (is_array($value)) { + $params[$key] = static::filterQueryParameters($value, $exclude, $string_key); + } + else { + $params[$key] = $value; + } + } + + return $params; + } + + /** + * Parses a system URL string into an associative array. + * + * This function should only be used for URLs that have been generated by the + * system. It should not be used for URLs that come from external sources, or + * URLs that link to external resources. + * + * The returned array contains a 'path' that may be passed separately to url(). + * For example: + * @code + * $options = Url::parse($_GET['destination']); + * $my_url = url($options['path'], $options); + * $my_link = l('Example link', $options['path'], $options); + * @endcode + * + * @param string $url + * The URL string to parse, f.e. $_GET['destination']. + * + * @return + * An associative array containing the keys: + * - 'path': The path of the URL. If the given $url is external, this includes + * the scheme and host. + * - 'query': An array of query parameters of $url, if existent. + * - 'fragment': The fragment of $url, if existent. + * + * @ingroup php_wrappers + */ + public static function parse($url) { + $options = array( + 'path' => NULL, + 'query' => array(), + 'fragment' => '', + ); + + // External URLs: not using parse_url() here, so we do not have to rebuild + // the scheme, host, and path without having any use for it. + if (strpos($url, '://') !== FALSE) { + // Split off everything before the query string into 'path'. + $parts = explode('?', $url); + $options['path'] = $parts[0]; + // If there is a query string, transform it into keyed query parameters. + if (isset($parts[1])) { + $query_parts = explode('#', $parts[1]); + parse_str($query_parts[0], $options['query']); + // Take over the fragment, if there is any. + if (isset($query_parts[1])) { + $options['fragment'] = $query_parts[1]; + } + } + } + // Internal URLs. + else { + // parse_url() does not support relative URLs, so make it absolute. E.g. the + // relative URL "foo/bar:1" isn't properly parsed. + $parts = parse_url('http://example.com/' . $url); + // Strip the leading slash that was just added. + $options['path'] = substr($parts['path'], 1); + if (isset($parts['query'])) { + parse_str($parts['query'], $options['query']); + } + if (isset($parts['fragment'])) { + $options['fragment'] = $parts['fragment']; + } + } + + return $options; + } + + /** + * Encodes a Drupal path for use in a URL. + * + * For aesthetic reasons slashes are not escaped. + * + * @param string $path + * The Drupal path to encode. + * + * @return string + * The encoded path. + */ + public static function encodePath($path) { + return str_replace('%2F', '/', rawurlencode($path)); + } + + /** + * Returns whether a path is external to Drupal (e.g. http://example.com). + * + * If a path cannot be assessed by Drupal's menu handler, then we must + * treat it as potentially insecure. + * + * @param string $path + * The internal path or external URL being linked to, such as "node/34" or + * "http://example.com/foo". + * + * @return bool + * TRUE or FALSE, where TRUE indicates an external path. + */ + public static function isExternal($path) { + $colonpos = strpos($path, ':'); + // Avoid calling stripDangerousProtocols() if there is any + // slash (/), hash (#) or question_mark (?) before the colon (:) + // occurrence - if any - as this would clearly mean it is not a URL. + return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && static::stripDangerousProtocols($path) == $path; + } + + /** + * Determines if an external URL points to this installation. + * + * @param string $url + * A string containing an external URL, such as "http://example.com/foo". + * @param string $base_url + * The base URL string to check against, such as "http://example.com/" + * + * @return + * TRUE if the URL has the same domain and base path. + */ + public static function externalIsLocal($url, $base_url) { + $url_parts = parse_url($url); + $base_host = parse_url($base_url, PHP_URL_HOST); + + if (!isset($url_parts['path'])) { + return ($url_parts['host'] == $base_host); + } + else { + // When comparing base paths, we need a trailing slash to make sure a + // partial URL match isn't occuring. Since base_path() always returns with + // a trailing slash, we don't need to add the trailing slash here. + return ($url_parts['host'] == $base_host && stripos($url_parts['path'], $base_url) === 0); + } + } + + /** + * Processes an HTML attribute value and strips dangerous protocols from URLs. + * + * @param string $string + * The string with the attribute value. + * + * @return string + * Cleaned up and HTML-escaped version of $string. + */ + public static function filterBadProtocol($string) { + // Get the plain text representation of the attribute value (i.e. its + // meaning). + $string = String::decodeEntities($string); + return String::checkPlain(static::stripDangerousProtocols($string)); + } + + /** + * Sets the allowed protocols. + * + * @param array $protocols + * An array of protocols, for example http, https and irc. + */ + public static function setAllowedProtocols(array $protocols = array()) { + static::$allowedProtocols = $protocols; + } + + /** + * Strips dangerous protocols (e.g. 'javascript:') from a URI. + * + * This function must be called for all URIs within user-entered input prior + * to being output to an HTML attribute value. It is often called as part of + * check_url() or Drupal\Component\Utility\Xss::filter(), but those functions + * return an HTML-encoded string, so this function can be called independently + * when the output needs to be a plain-text string for passing to t(), l(), + * Drupal\Core\Template\Attribute, or another function that will call + * \Drupal\Component\Utility\String::checkPlain() separately. + * + * @param string $uri + * A plain-text URI that might contain dangerous protocols. + * + * @return string + * A plain-text URI stripped of dangerous protocols. As with all plain-text + * strings, this return value must not be output to an HTML page without + * being sanitized first. However, it can be passed to functions + * expecting plain-text strings. + */ + public static function stripDangerousProtocols($uri) { + $allowed_protocols = array_flip(static::$allowedProtocols); + + // Iteratively remove any invalid protocol found. + do { + $before = $uri; + $colonpos = strpos($uri, ':'); + if ($colonpos > 0) { + // We found a colon, possibly a protocol. Verify. + $protocol = substr($uri, 0, $colonpos); + // If a colon is preceded by a slash, question mark or hash, it cannot + // possibly be part of the URL scheme. This must be a relative URL, which + // inherits the (safe) protocol of the base document. + if (preg_match('![/?#]!', $protocol)) { + break; + } + // Check if this is a disallowed protocol. Per RFC2616, section 3.2.3 + // (URI Comparison) scheme comparison must be case-insensitive. + if (!isset($allowed_protocols[strtolower($protocol)])) { + $uri = substr($uri, $colonpos + 1); + } + } + } while ($before != $uri); + + return $uri; + } + + /** + * Verifies the syntax of the given URL. + * + * This function should only be used on actual URLs. It should not be used for + * Drupal menu paths, which can contain arbitrary characters. + * Valid values per RFC 3986. + * + * @param string $url + * The URL to verify. + * @param bool $absolute + * Whether the URL is absolute (beginning with a scheme such as "http:"). + * + * @return bool + * TRUE if the URL is in a valid format, FALSE otherwise. + */ + public static function isValid($url, $absolute = FALSE) { + if ($absolute) { + return (bool) preg_match(" + /^ # Start at the beginning of the text + (?:ftp|https?|feed):\/\/ # Look for ftp, http, https or feed schemes + (?: # Userinfo (optional) which is typically + (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)* # a username or a username and password + (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@ # combination + )? + (?: + (?:[a-z0-9\-\.]|%[0-9a-f]{2})+ # A domain name or a IPv4 address + |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\]) # or a well formed IPv6 address + ) + (?::[0-9]+)? # Server port number (optional) + (?:[\/|\?] + (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional) + *)? + $/xi", $url); + } + else { + return (bool) preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url); + } + } + +} diff --git a/core/lib/Drupal/Component/Utility/UrlValidator.php b/core/lib/Drupal/Component/Utility/UrlValidator.php deleted file mode 100644 index 4772373..0000000 --- a/core/lib/Drupal/Component/Utility/UrlValidator.php +++ /dev/null @@ -1,136 +0,0 @@ - 0) { - // We found a colon, possibly a protocol. Verify. - $protocol = substr($uri, 0, $colonpos); - // If a colon is preceded by a slash, question mark or hash, it cannot - // possibly be part of the URL scheme. This must be a relative URL, which - // inherits the (safe) protocol of the base document. - if (preg_match('![/?#]!', $protocol)) { - break; - } - // Check if this is a disallowed protocol. Per RFC2616, section 3.2.3 - // (URI Comparison) scheme comparison must be case-insensitive. - if (!isset($allowed_protocols[strtolower($protocol)])) { - $uri = substr($uri, $colonpos + 1); - } - } - } while ($before != $uri); - - return $uri; - } - - /** - * Verifies the syntax of the given URL. - * - * This function should only be used on actual URLs. It should not be used for - * Drupal menu paths, which can contain arbitrary characters. - * Valid values per RFC 3986. - * - * @param string $url - * The URL to verify. - * @param bool $absolute - * Whether the URL is absolute (beginning with a scheme such as "http:"). - * - * @return bool - * TRUE if the URL is in a valid format. - */ - public static function isValid($url, $absolute = FALSE) { - if ($absolute) { - return (bool) preg_match(" - /^ # Start at the beginning of the text - (?:ftp|https?|feed):\/\/ # Look for ftp, http, https or feed schemes - (?: # Userinfo (optional) which is typically - (?:(?:[\w\.\-\+!$&'\(\)*\+,;=]|%[0-9a-f]{2})+:)* # a username or a username and password - (?:[\w\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})+@ # combination - )? - (?: - (?:[a-z0-9\-\.]|%[0-9a-f]{2})+ # A domain name or a IPv4 address - |(?:\[(?:[0-9a-f]{0,4}:)*(?:[0-9a-f]{0,4})\]) # or a well formed IPv6 address - ) - (?::[0-9]+)? # Server port number (optional) - (?:[\/|\?] - (?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2}) # The path and query (optional) - *)? - $/xi", $url); - } - else { - return (bool) preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url); - } - } - -} diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php index a1f1956..8d0d257 100644 --- a/core/lib/Drupal/Component/Utility/Xss.php +++ b/core/lib/Drupal/Component/Utility/Xss.php @@ -225,7 +225,7 @@ protected static function attributes($attributes) { case 2: // Attribute value, a URL after href= for instance. if (preg_match('/^"([^"]*)"(\s+|$)/', $attributes, $match)) { - $thisval = UrlValidator::filterBadProtocol($match[1]); + $thisval = Url::filterBadProtocol($match[1]); if (!$skip) { $attributes_array[] = "$attribute_name=\"$thisval\""; @@ -237,7 +237,7 @@ protected static function attributes($attributes) { } if (preg_match("/^'([^']*)'(\s+|$)/", $attributes, $match)) { - $thisval = UrlValidator::filterBadProtocol($match[1]); + $thisval = Url::filterBadProtocol($match[1]); if (!$skip) { $attributes_array[] = "$attribute_name='$thisval'"; @@ -248,7 +248,7 @@ protected static function attributes($attributes) { } if (preg_match("%^([^\s\"']+)(\s+|$)%", $attributes, $match)) { - $thisval = UrlValidator::filterBadProtocol($match[1]); + $thisval = Url::filterBadProtocol($match[1]); if (!$skip) { $attributes_array[] = "$attribute_name=\"$thisval\""; diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php index 04c2c17..972058a 100644 --- a/core/lib/Drupal/Core/Routing/UrlGenerator.php +++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php @@ -16,7 +16,7 @@ use Symfony\Cmf\Component\Routing\ProviderBasedGenerator; use Drupal\Component\Utility\Settings; -use Drupal\Component\Utility\UrlValidator; +use Drupal\Component\Utility\Url; use Drupal\Core\Config\ConfigFactory; use Drupal\Core\PathProcessor\OutboundPathProcessorInterface; @@ -83,7 +83,7 @@ public function __construct(RouteProviderInterface $provider, OutboundPathProces $this->pathProcessor = $path_processor; $this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE); $allowed_protocols = $config->get('system.filter')->get('protocols') ?: array('http', 'https'); - UrlValidator::setAllowedProtocols($allowed_protocols); + Url::setAllowedProtocols($allowed_protocols); } /** @@ -112,7 +112,7 @@ public function generate($name, $parameters = array(), $absolute = FALSE) { if ($name instanceof SymfonyRoute) { $route = $name; } - elseif (null === $route = $this->provider->getRouteByName($name, $parameters)) { + elseif (NULL === $route = $this->provider->getRouteByName($name, $parameters)) { throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name)); } @@ -239,7 +239,7 @@ public function generateFromPath($path = NULL, $options = array()) { // that would require another function call, and performance inside url() is // critical. $colonpos = strpos($path, ':'); - $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && UrlValidator::stripDangerousProtocols($path) == $path); + $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && Url::stripDangerousProtocols($path) == $path); } if (isset($options['fragment']) && $options['fragment'] !== '') { @@ -257,7 +257,7 @@ public function generateFromPath($path = NULL, $options = array()) { } // Append the query. if ($options['query']) { - $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $this->httpBuildQuery($options['query']); + $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . Url::buildQuery($options['query']); } if (isset($options['https']) && $this->mixedModeSessions) { if ($options['https'] === TRUE) { @@ -300,7 +300,7 @@ public function generateFromPath($path = NULL, $options = array()) { $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix']; $path = str_replace('%2F', '/', rawurlencode($prefix . $path)); - $query = $options['query'] ? ('?' . $this->httpBuildQuery($options['query'])) : ''; + $query = $options['query'] ? ('?' . Url::buildQuery($options['query'])) : ''; return $base . $options['script'] . $path . $query . $options['fragment']; } @@ -326,47 +326,6 @@ public function setScriptPath($path) { } /** - * Parses an array into a valid, rawurlencoded query string. - * - * This differs from http_build_query() as we need to rawurlencode() (instead of - * urlencode()) all query parameters. - * - * @param $query - * The query parameter array to be processed, e.g. $_GET. - * @param $parent - * Internal use only. Used to build the $query array key for nested items. - * - * @return - * A rawurlencoded string which can be used as or appended to the URL query - * string. - * - * @see drupal_get_query_parameters() - * @ingroup php_wrappers - */ - public function httpBuildQuery(array $query, $parent = '') { - $params = array(); - - foreach ($query as $key => $value) { - $key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key)); - - // Recurse into children. - if (is_array($value)) { - $params[] = $this->httpBuildQuery($value, $key); - } - // If a query parameter value is NULL, only append its key. - elseif (!isset($value)) { - $params[] = $key; - } - else { - // For better readability of paths in query strings, we decode slashes. - $params[] = $key . '=' . str_replace('%2F', '/', rawurlencode($value)); - } - } - - return implode('&', $params); - } - - /** * Passes the path to a processor manager to allow alterations. */ protected function processPath($path, &$options = array()) { diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module index 75e3ed2..9280a7b 100644 --- a/core/modules/contextual/contextual.module +++ b/core/modules/contextual/contextual.module @@ -347,7 +347,8 @@ function _contextual_id_to_links($id) { foreach ($contexts as $context) { list($module, $parent_path, $path_args, $metadata_raw) = explode(':', $context); $path_args = explode('/', $path_args); - $metadata = drupal_get_query_array($metadata_raw); + $metadata = array(); + parse_str($metadata_raw, $metadata); $contextual_links[$module] = array($parent_path, $path_args, $metadata); } return $contextual_links; diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc index 126ec5a..c4e97b3 100644 --- a/core/modules/language/language.negotiation.inc +++ b/core/modules/language/language.negotiation.inc @@ -500,7 +500,9 @@ function language_url_rewrite_session(&$path, &$options) { // user language preference even with cookies disabled. if ($query_rewrite) { if (is_string($options['query'])) { - $options['query'] = drupal_get_query_array($options['query']); + $query = array(); + parse_str($options['query'], $query); + $options['query'] = $query; } if (!isset($options['query'][$query_param])) { $options['query'][$query_param] = $query_value; diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php index 6064108..8eb4458 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php @@ -227,7 +227,8 @@ public function validate(array $form, array &$form_state) { if (!url_is_external($menu_link->link_path)) { $parsed_link = parse_url($menu_link->link_path); if (isset($parsed_link['query'])) { - $menu_link->options['query'] = drupal_get_query_array($parsed_link['query']); + $menu_link->options['query'] = array(); + parse_str($parsed_link['query'], $menu_link->options['query']); } else { // Use unset() rather than setting to empty string diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php index b6e1ae7..249afd4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php @@ -197,16 +197,6 @@ function testDrupalGetQueryParameters() { } /** - * Tests drupal_http_build_query(). - */ - function testDrupalHttpBuildQuery() { - $this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.'); - $this->assertEqual(drupal_http_build_query(array(' &#//+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.'); - $this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.'); - $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.'); - } - - /** * Tests drupal_parse_url(). */ function testDrupalParseUrl() { diff --git a/core/modules/system/templates/mark.html.twig b/core/modules/system/templates/mark.html.twig new file mode 100644 index 0000000..2798bb0 --- /dev/null +++ b/core/modules/system/templates/mark.html.twig @@ -0,0 +1,22 @@ +{# +/** + * @file + * Default theme implementation for a marker for new or updated content. + * + * Available variables: + * - status: Number representing the marker status to display. Use the constants + * below for comparison: + * - MARK_NEW + * - MARK_UPDATED + * - MARK_READ + * + * @ingroup themeable + */ +#} +{% if logged_in %} + {% if status is constant('MARK_NEW') %} + {{ 'new'|t }} + {% elseif status is constant('MARK_UPDATED') %} + {{ 'updated'|t }} + {% endif %} +{% endif %} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php index b07d763..ac95d5b 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php @@ -9,7 +9,7 @@ use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; -use Drupal\Component\Utility\UrlValidator; +use Drupal\Component\Utility\Url; use Drupal\Component\Utility\Xss; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\PluginBase; @@ -221,7 +221,7 @@ public function sanitizeValue($value, $type = NULL) { $value = Xss::filterAdmin($value); break; case 'url': - $value = String::checkPlain(UrlValidator::stripDangerousProtocols($value)); + $value = String::checkPlain(Url::stripDangerousProtocols($value)); break; default: $value = String::checkPlain($value); diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php index 386eda0..fda023a 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php @@ -1365,7 +1365,8 @@ protected function renderAsLink($alter, $text, $tokens) { if (isset($url['query'])) { $path = strtr($path, array('?' . $url['query'] => '')); - $query = drupal_get_query_array($url['query']); + $query = array(); + parse_str($url['query'], $query); // Remove query parameters that were assigned a query string replacement // token for which there is no value available. foreach ($query as $param => $val) { @@ -1421,7 +1422,9 @@ protected function renderAsLink($alter, $text, $tokens) { // convert back to an array form for l(). $options['query'] = drupal_http_build_query($alter['query']); $options['query'] = strtr($options['query'], $tokens); - $options['query'] = drupal_get_query_array($options['query']); + $query = array(); + parse_str($options['query'], $query); + $options['query'] = $query; } if (isset($alter['alias'])) { // Alias is a boolean field, so no token. diff --git a/core/tests/Drupal/Tests/Core/Common/UrlValidatorTest.php b/core/tests/Drupal/Tests/Component/Utility/UrlTest.php similarity index 70% rename from core/tests/Drupal/Tests/Core/Common/UrlValidatorTest.php rename to core/tests/Drupal/Tests/Component/Utility/UrlTest.php index 105d716..769a10d 100644 --- a/core/tests/Drupal/Tests/Core/Common/UrlValidatorTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/UrlTest.php @@ -2,30 +2,63 @@ /** * @file - * Contains \Drupal\Tests\Core\Common\UrlValidatorTest. + * Contains \Drupal\Tests\Component\Utility\UrlTest. */ -namespace Drupal\Tests\Core\Common; +namespace Drupal\Tests\Component\Utility; + +use Drupal\Component\Utility\Url; use Drupal\Component\Utility\String; -use Drupal\Component\Utility\UrlValidator; use Drupal\Tests\UnitTestCase; /** - * Tests URL validation by valid_url(). + * Tests the http query methods. + * + * @see \Drupal\Component\Utility\Url */ -class UrlValidatorTest extends UnitTestCase { +class UrlTest extends UnitTestCase { public static function getInfo() { return array( - 'name' => 'URL validation', - 'description' => 'Tests URL validation by valid_url()', - 'group' => 'Common', + 'name' => t('Url Tests'), + 'description' => t('Tests the Url utility class.'), + 'group' => t('Path API'), + ); + } + + /** + * Provides test data for testBuildQuery(). + * + * @return array + */ + public function providerTestBuildQuery() { + return array( + array(array('a' => ' &#//+%20@۞'), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.'), + array(array(' &#//+%20@۞' => 'a'), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.'), + array(array('a' => '1', 'b' => '2', 'c' => '3'), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.'), + array(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo'), 'a[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.'), ); } /** - * Data provider for absolute URLs. + * Tests Url::buildQuery(). + * + * @param array $query + * The array of query parameters. + * @param string $expected + * The expected query string. + * @param string $message + * The assertion message. + * + * @dataProvider providerTestBuildQuery + */ + public function testBuildQuery($query, $expected, $message) { + $this->assertEquals(Url::buildQuery($query), $expected, $message); + } + + /** + * Data provider for testValidAbsolute(). */ public function providerTestValidAbsoluteData() { $urls = array( @@ -64,12 +97,12 @@ public function providerTestValidAbsoluteData() { */ public function testValidAbsolute($url, $scheme) { $test_url = $scheme . '://' . $url; - $valid_url = UrlValidator::isValid($test_url, TRUE); + $valid_url = Url::isValid($test_url, TRUE); $this->assertTrue($valid_url, String::format('@url is a valid URL.', array('@url' => $test_url))); } /** - * Provides invalid absolute URLs. + * Provides data for testInvalidAbsolute(). */ public function providerTestInvalidAbsolute() { $data = array( @@ -92,12 +125,12 @@ public function providerTestInvalidAbsolute() { */ public function testInvalidAbsolute($url, $scheme) { $test_url = $scheme . '://' . $url; - $valid_url = UrlValidator::isValid($test_url, TRUE); + $valid_url = Url::isValid($test_url, TRUE); $this->assertFalse($valid_url, String::format('@url is NOT a valid URL.', array('@url' => $test_url))); } /** - * Provides valid relative URLs + * Provides data for testValidRelative(). */ public function providerTestValidRelativeData() { $data = array( @@ -123,12 +156,12 @@ public function providerTestValidRelativeData() { */ public function testValidRelative($url, $prefix) { $test_url = $prefix . $url; - $valid_url = Urlvalidator::isValid($test_url); + $valid_url = Url::isValid($test_url); $this->assertTrue($valid_url, String::format('@url is a valid URL.', array('@url' => $test_url))); } /** - * Provides invalid relative URLs. + * Provides data for testInvalidRelative(). */ public function providerTestInvalidRelativeData() { $data = array( @@ -151,7 +184,7 @@ public function providerTestInvalidRelativeData() { */ public function testInvalidRelative($url, $prefix) { $test_url = $prefix . $url; - $valid_url = UrlValidator::isValid($test_url); + $valid_url = Url::isValid($test_url); $this->assertFalse($valid_url, String::format('@url is NOT a valid URL.', array('@url' => $test_url))); } @@ -195,5 +228,4 @@ protected function dataEnhanceWithPrefix(array $urls) { return $data; } - } diff --git a/core/tests/Drupal/Tests/Component/Utility/XssTest.php b/core/tests/Drupal/Tests/Component/Utility/XssTest.php index e87cd79..2614c5d 100644 --- a/core/tests/Drupal/Tests/Component/Utility/XssTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php @@ -8,7 +8,7 @@ namespace Drupal\Tests\Component\Utility; use Drupal\Component\Utility\String; -use Drupal\Component\Utility\UrlValidator; +use Drupal\Component\Utility\Url; use Drupal\Component\Utility\Xss; use Drupal\Tests\UnitTestCase; @@ -53,7 +53,7 @@ protected function setUp() { 'webcal', 'rtsp', ); - UrlValidator::setAllowedProtocols($allowed_protocols); + Url::setAllowedProtocols($allowed_protocols); } /**