Issue #1888424 by katbailey, steveoliver, twistor, beejeebus, effulgentsia: Change notice: Make Drupal's URL generation logic available to HttpKernel, and minimize code repetition/divergence.
parent
9d73599020
commit
b21943922d
|
@ -198,12 +198,16 @@ services:
|
|||
arguments: ['@router.route_provider']
|
||||
calls:
|
||||
- [setFinalMatcher, ['@router.matcher.final_matcher']]
|
||||
router.generator:
|
||||
url_generator:
|
||||
class: Drupal\Core\Routing\UrlGenerator
|
||||
arguments: ['@router.route_provider', '@path.alias_manager.cached']
|
||||
arguments: ['@router.route_provider', '@path_processor_manager', '@config.factory', '@settings']
|
||||
calls:
|
||||
- [setRequest, ['@?request']]
|
||||
tags:
|
||||
- { name: persist }
|
||||
router.dynamic:
|
||||
class: Symfony\Cmf\Component\Routing\DynamicRouter
|
||||
arguments: ['@router.request_context', '@router.matcher', '@router.generator']
|
||||
arguments: ['@router.request_context', '@router.matcher', '@url_generator']
|
||||
legacy_generator:
|
||||
class: Drupal\Core\Routing\NullGenerator
|
||||
legacy_url_matcher:
|
||||
|
@ -390,11 +394,13 @@ services:
|
|||
class: Drupal\Core\PathProcessor\PathProcessorFront
|
||||
tags:
|
||||
- { name: path_processor_inbound, priority: 200 }
|
||||
- { name: path_processor_outbound, priority: 200 }
|
||||
arguments: ['@config.factory']
|
||||
path_processor_alias:
|
||||
class: Drupal\Core\PathProcessor\PathProcessorAlias
|
||||
tags:
|
||||
- { name: path_processor_inbound, priority: 100 }
|
||||
- { name: path_processor_outbound, priority: 300 }
|
||||
arguments: ['@path.alias_manager']
|
||||
transliteration:
|
||||
class: Drupal\Core\Transliteration\PHPTransliteration
|
||||
|
|
|
@ -13,6 +13,7 @@ use Drupal\Component\Utility\NestedArray;
|
|||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Routing\GeneratorNotInitializedException;
|
||||
use Drupal\Core\SystemListingInfo;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
|
||||
|
@ -498,42 +499,14 @@ function drupal_get_query_array($query) {
|
|||
/**
|
||||
* 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\Core\Routing\PathBasedGeneratorInterface::httpBuildQuery()
|
||||
* @see drupal_get_query_parameters()
|
||||
* @deprecated as of Drupal 8.0. Use
|
||||
* Drupal::urlGenerator()->httpBuildQuery() instead.
|
||||
* @ingroup php_wrappers
|
||||
*/
|
||||
function drupal_http_build_query(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[] = drupal_http_build_query($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);
|
||||
return Drupal::urlGenerator()->httpBuildQuery($query, $parent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -564,7 +537,7 @@ function drupal_get_destination() {
|
|||
}
|
||||
else {
|
||||
$path = current_path();
|
||||
$query = drupal_http_build_query(drupal_get_query_parameters());
|
||||
$query = Drupal::urlGenerator()->httpBuildQuery(drupal_get_query_parameters());
|
||||
if ($query != '') {
|
||||
$path .= '?' . $query;
|
||||
}
|
||||
|
@ -725,8 +698,7 @@ function drupal_goto($path = '', array $options = array(), $http_response_code =
|
|||
// The 'Location' HTTP header must be absolute.
|
||||
$options['absolute'] = TRUE;
|
||||
|
||||
$url = url($path, $options);
|
||||
|
||||
$url = Drupal::urlGenerator()->generateFromPath($path, $options);
|
||||
header('Location: ' . $url, TRUE, $http_response_code);
|
||||
|
||||
// The "Location" header sends a redirect status code to the HTTP daemon. In
|
||||
|
@ -1423,153 +1395,24 @@ function datetime_default_format_type() {
|
|||
* When creating links in modules, consider whether l() could be a better
|
||||
* alternative than url().
|
||||
*
|
||||
* @param $path
|
||||
* (optional) The internal path or external URL being linked to, such as
|
||||
* "node/34" or "http://example.com/foo". The default value is equivalent to
|
||||
* passing in '<front>'. A few notes:
|
||||
* - If you provide a full URL, it will be considered an external URL.
|
||||
* - If you provide only the path (e.g. "node/34"), it will be
|
||||
* considered an internal link. In this case, it should be a system URL,
|
||||
* and it will be replaced with the alias, if one exists. Additional query
|
||||
* arguments for internal paths must be supplied in $options['query'], not
|
||||
* included in $path.
|
||||
* - If you provide an internal path and $options['alias'] is set to TRUE, the
|
||||
* path is assumed already to be the correct path alias, and the alias is
|
||||
* not looked up.
|
||||
* - The special string '<front>' generates a link to the site's base URL.
|
||||
* - If your external URL contains a query (e.g. http://example.com/foo?a=b),
|
||||
* then you can either URL encode the query keys and values yourself and
|
||||
* include them in $path, or use $options['query'] to let this function
|
||||
* URL encode them.
|
||||
* @param $options
|
||||
* (optional) An associative array of additional options, with the following
|
||||
* elements:
|
||||
* - 'query': An array of query key/value-pairs (without any URL-encoding) to
|
||||
* append to the URL.
|
||||
* - 'fragment': A fragment identifier (named anchor) to append to the URL.
|
||||
* Do not include the leading '#' character.
|
||||
* - 'absolute': Defaults to FALSE. Whether to force the output to be an
|
||||
* absolute link (beginning with http:). Useful for links that will be
|
||||
* displayed outside the site, such as in an RSS feed.
|
||||
* - 'alias': Defaults to FALSE. Whether the given path is a URL alias
|
||||
* already.
|
||||
* - 'external': Whether the given path is an external URL.
|
||||
* - 'language': An optional language object. If the path being linked to is
|
||||
* internal to the site, $options['language'] is used to look up the alias
|
||||
* for the URL. If $options['language'] is omitted, the language will be
|
||||
* obtained from language(Language::TYPE_URL).
|
||||
* - 'https': Whether this URL should point to a secure location. If not
|
||||
* defined, the current scheme is used, so the user stays on HTTP or HTTPS
|
||||
* respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can
|
||||
* only be enforced when the variable 'https' is set to TRUE.
|
||||
* - 'base_url': Only used internally, to modify the base URL when a language
|
||||
* dependent URL requires so.
|
||||
* - 'prefix': Only used internally, to modify the path when a language
|
||||
* dependent URL requires so.
|
||||
* - 'script': Added to the URL between the base path and the path prefix.
|
||||
* Defaults to empty string when clean URLs are in effect, and to
|
||||
* 'index.php/' when they are not.
|
||||
* - 'entity_type': The entity type of the object that called url(). Only
|
||||
* set if url() is invoked by Drupal\Core\Entity\Entity::uri().
|
||||
* - 'entity': The entity object (such as a node) for which the URL is being
|
||||
* generated. Only set if url() is invoked by Drupal\Core\Entity\Entity::uri().
|
||||
*
|
||||
* @return
|
||||
* A string containing a URL to the given path.
|
||||
* @see \Drupal\Core\Routing\PathBasedGeneratorInterface::generateFromPath().
|
||||
*/
|
||||
function url($path = NULL, array $options = array()) {
|
||||
// Merge in defaults.
|
||||
$options += array(
|
||||
'fragment' => '',
|
||||
'query' => array(),
|
||||
'absolute' => FALSE,
|
||||
'alias' => FALSE,
|
||||
'prefix' => '',
|
||||
'script' => $GLOBALS['script_path'],
|
||||
);
|
||||
|
||||
if (!isset($options['external'])) {
|
||||
// Return an external link if $path contains an allowed absolute URL. Only
|
||||
// call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
|
||||
// before any / ? or #. Note: we could use url_is_external($path) here, but
|
||||
// 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)) && drupal_strip_dangerous_protocols($path) == $path);
|
||||
$generator = Drupal::urlGenerator();
|
||||
try {
|
||||
$url = $generator->generateFromPath($path, $options);
|
||||
}
|
||||
|
||||
// Preserve the original path before altering or aliasing.
|
||||
$original_path = $path;
|
||||
|
||||
// Allow other modules to alter the outbound URL and options.
|
||||
drupal_alter('url_outbound', $path, $options, $original_path);
|
||||
|
||||
if (isset($options['fragment']) && $options['fragment'] !== '') {
|
||||
$options['fragment'] = '#' . $options['fragment'];
|
||||
catch (GeneratorNotInitializedException $e) {
|
||||
// Fallback to using globals.
|
||||
// @todo Remove this once there is no code that calls url() when there is
|
||||
// no request.
|
||||
global $base_url, $base_path, $script_path;
|
||||
$generator->setBasePath($base_path);
|
||||
$generator->setBaseUrl($base_url . '/');
|
||||
$generator->setScriptPath($script_path);
|
||||
$url = $generator->generateFromPath($path, $options);
|
||||
}
|
||||
|
||||
if ($options['external']) {
|
||||
// Split off the fragment.
|
||||
if (strpos($path, '#') !== FALSE) {
|
||||
list($path, $old_fragment) = explode('#', $path, 2);
|
||||
// If $options contains no fragment, take it over from the path.
|
||||
if (isset($old_fragment) && !$options['fragment']) {
|
||||
$options['fragment'] = '#' . $old_fragment;
|
||||
}
|
||||
}
|
||||
// Append the query.
|
||||
if ($options['query']) {
|
||||
$path .= (strpos($path, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($options['query']);
|
||||
}
|
||||
if (isset($options['https']) && settings()->get('mixed_mode_sessions', FALSE)) {
|
||||
if ($options['https'] === TRUE) {
|
||||
$path = str_replace('http://', 'https://', $path);
|
||||
}
|
||||
elseif ($options['https'] === FALSE) {
|
||||
$path = str_replace('https://', 'http://', $path);
|
||||
}
|
||||
}
|
||||
// Reassemble.
|
||||
return $path . $options['fragment'];
|
||||
}
|
||||
|
||||
global $base_url, $base_secure_url, $base_insecure_url;
|
||||
|
||||
// The base_url might be rewritten from the language rewrite in domain mode.
|
||||
if (!isset($options['base_url'])) {
|
||||
if (isset($options['https']) && settings()->get('mixed_mode_sessions', FALSE)) {
|
||||
if ($options['https'] === TRUE) {
|
||||
$options['base_url'] = $base_secure_url;
|
||||
$options['absolute'] = TRUE;
|
||||
}
|
||||
elseif ($options['https'] === FALSE) {
|
||||
$options['base_url'] = $base_insecure_url;
|
||||
$options['absolute'] = TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$options['base_url'] = $base_url;
|
||||
}
|
||||
}
|
||||
|
||||
// The special path '<front>' links to the default front page.
|
||||
if ($path == '<front>') {
|
||||
$path = '';
|
||||
}
|
||||
elseif (!empty($path) && !$options['alias']) {
|
||||
$langcode = isset($options['language']) && isset($options['language']->langcode) ? $options['language']->langcode : '';
|
||||
$alias = drupal_container()->get('path.alias_manager')->getPathAlias($original_path, $langcode);
|
||||
if ($alias != $original_path) {
|
||||
$path = $alias;
|
||||
}
|
||||
}
|
||||
|
||||
$base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
|
||||
$prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
|
||||
|
||||
$path = drupal_encode_path($prefix . $path);
|
||||
$query = $options['query'] ? ('?' . drupal_http_build_query($options['query'])) : '';
|
||||
return $base . $options['script'] . $path . $query . $options['fragment'];
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1320,7 +1320,8 @@ function drupal_redirect_form($form_state) {
|
|||
$function($form_state['redirect']);
|
||||
}
|
||||
}
|
||||
drupal_goto(current_path(), array('query' => drupal_container()->get('request')->query->all()));
|
||||
$request = Drupal::request();
|
||||
drupal_goto($request->attributes->get('system_path'), array('query' => $request->query->all()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -410,6 +410,8 @@ function install_begin_request(&$install_state) {
|
|||
// Register Twig template engine for use during install.
|
||||
CoreBundle::registerTwig($container);
|
||||
|
||||
$container->register('url_generator', 'Drupal\Core\Routing\NullGenerator');
|
||||
|
||||
Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
|
@ -1948,7 +1950,14 @@ function install_finished(&$install_state) {
|
|||
|
||||
$messages = drupal_set_message();
|
||||
$output = '<p>' . st('Congratulations, you installed @drupal!', array('@drupal' => drupal_install_profile_distribution_name())) . '</p>';
|
||||
$output .= '<p>' . (isset($messages['error']) ? st('Review the messages above before visiting <a href="@url">your new site</a>.', array('@url' => url(''))) : st('<a href="@url">Visit your new site</a>.', array('@url' => url('')))) . '</p>';
|
||||
// Ensure the URL that is generated for the home page does not have 'install.php'
|
||||
// in it.
|
||||
$request = Request::createFromGlobals();
|
||||
$generator = Drupal::urlGenerator();
|
||||
$generator->setBasePath(str_replace('/core', '', $request->getBasePath()) . '/');
|
||||
$generator->setScriptPath('');
|
||||
$url = $generator->generateFromPath('');
|
||||
$output .= '<p>' . (isset($messages['error']) ? st('Review the messages above before visiting <a href="@url">your new site</a>.', array('@url' => $url)) : st('<a href="@url">Visit your new site</a>.', array('@url' => $url))) . '</p>';
|
||||
|
||||
// Run cron to populate update status tables (if available) so that users
|
||||
// will be warned if they've installed an out of date Drupal version.
|
||||
|
|
|
@ -361,4 +361,14 @@ class Drupal {
|
|||
return static::$container->get('token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url generator service.
|
||||
*
|
||||
* @return \Drupal\Core\Routing\UrlGenerator
|
||||
* The url generator service.
|
||||
*/
|
||||
public static function urlGenerator() {
|
||||
return static::$container->get('url_generator');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,9 +27,15 @@ class RegisterPathProcessorsPass implements CompilerPassInterface {
|
|||
return;
|
||||
}
|
||||
$manager = $container->getDefinition('path_processor_manager');
|
||||
// Add inbound path processors.
|
||||
foreach ($container->findTaggedServiceIds('path_processor_inbound') as $id => $attributes) {
|
||||
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
|
||||
$manager->addMethodCall('addInbound', array(new Reference($id), $priority));
|
||||
}
|
||||
// Add outbound path processors.
|
||||
foreach ($container->findTaggedServiceIds('path_processor_outbound') as $id => $attributes) {
|
||||
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
|
||||
$manager->addMethodCall('addOutbound', array(new Reference($id), $priority));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\Core\EventSubscriber;
|
|||
|
||||
use Drupal\Core\CacheDecorator\AliasManagerCacheDecorator;
|
||||
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
||||
use Drupal\Core\Routing\PathBasedGeneratorInterface;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
|
@ -20,7 +21,18 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|||
*/
|
||||
class PathSubscriber extends PathListenerBase implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The alias manager that caches alias lookups based on the request.
|
||||
*
|
||||
* @var \Drupal\Core\CacheDecorator\AliasManagerCacheDecorator
|
||||
*/
|
||||
protected $aliasManager;
|
||||
|
||||
/**
|
||||
* A path processor manager for resolving the system path.
|
||||
*
|
||||
* @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface
|
||||
*/
|
||||
protected $pathProcessor;
|
||||
|
||||
public function __construct(AliasManagerCacheDecorator $alias_manager, InboundPathProcessorInterface $path_processor) {
|
||||
|
@ -39,6 +51,14 @@ class PathSubscriber extends PathListenerBase implements EventSubscriberInterfac
|
|||
$path = trim($request->getPathInfo(), '/');
|
||||
$path = $this->pathProcessor->processInbound($path, $request);
|
||||
$request->attributes->set('system_path', $path);
|
||||
// Also set an attribute that indicates whether we are using clean URLs.
|
||||
$clean_urls = TRUE;
|
||||
$base_url = $request->getBaseUrl();
|
||||
if (!empty($base_url) && strpos($base_url, $request->getScriptName()) !== FALSE) {
|
||||
$clean_urls = FALSE;
|
||||
}
|
||||
$request->attributes->set('clean_urls', $clean_urls);
|
||||
// Set the cache key on the alias manager cache decorator.
|
||||
if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
|
||||
$this->aliasManager->setCacheKey($path);
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ class LanguageManager {
|
|||
* @return bool
|
||||
* TRUE if more than one language is enabled, FALSE otherwise.
|
||||
*/
|
||||
protected function isMultilingual() {
|
||||
public function isMultilingual() {
|
||||
return variable_get('language_count', 1) > 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Core\PathProcessor\OutboundPathProcessorInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\PathProcessor;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Defines an interface for classes that process the outbound path.
|
||||
*/
|
||||
interface OutboundPathProcessorInterface {
|
||||
|
||||
/**
|
||||
* Processes the outbound path.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to process.
|
||||
*
|
||||
* @param array $options
|
||||
* An array of options such as would be passed to the generator's
|
||||
* generateFromPath() method.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HttpRequest object representing the current request.
|
||||
*
|
||||
* @return
|
||||
* The processed path.
|
||||
*/
|
||||
public function processOutbound($path, &$options = array(), Request $request = NULL);
|
||||
|
||||
}
|
|
@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
/**
|
||||
* Processes the inbound path using path alias lookups.
|
||||
*/
|
||||
class PathProcessorAlias implements InboundPathProcessorInterface {
|
||||
class PathProcessorAlias implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
|
||||
|
||||
/**
|
||||
* An alias manager for looking up the system path.
|
||||
|
@ -40,4 +40,12 @@ class PathProcessorAlias implements InboundPathProcessorInterface {
|
|||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound().
|
||||
*/
|
||||
public function processOutbound($path, &$options = array(), Request $request = NULL) {
|
||||
$langcode = isset($options['language']) ? $options['language']->langcode : NULL;
|
||||
$path = $this->aliasManager->getPathAlias($path, $langcode);
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
/**
|
||||
* Processes the inbound path by resolving it to the front page if empty.
|
||||
*/
|
||||
class PathProcessorFront implements InboundPathProcessorInterface {
|
||||
class PathProcessorFront implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
|
||||
|
||||
/**
|
||||
* A config factory for retrieving required config settings.
|
||||
|
@ -45,4 +45,15 @@ class PathProcessorFront implements InboundPathProcessorInterface {
|
|||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound().
|
||||
*/
|
||||
public function processOutbound($path, &$options = array(), Request $request = NULL) {
|
||||
// The special path '<front>' links to the default front page.
|
||||
if ($path == '<front>') {
|
||||
$path = '';
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\PathProcessor;
|
||||
|
||||
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
|
@ -16,10 +15,10 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
* Holds an array of path processor objects and uses them to sequentially process
|
||||
* a path, in order of processor priority.
|
||||
*/
|
||||
class PathProcessorManager implements InboundPathProcessorInterface {
|
||||
class PathProcessorManager implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
|
||||
|
||||
/**
|
||||
* Holds the array of processors to cycle through.
|
||||
* Holds the array of inbound processors to cycle through.
|
||||
*
|
||||
* @var array
|
||||
* An array whose keys are priorities and whose values are arrays of path
|
||||
|
@ -28,13 +27,31 @@ class PathProcessorManager implements InboundPathProcessorInterface {
|
|||
protected $inboundProcessors = array();
|
||||
|
||||
/**
|
||||
* Holds the array of processors, sorted by priority.
|
||||
* Holds the array of inbound processors, sorted by priority.
|
||||
*
|
||||
* @var array
|
||||
* An array of path processor objects.
|
||||
*/
|
||||
protected $sortedInbound = array();
|
||||
|
||||
|
||||
/**
|
||||
* Holds the array of outbound processors to cycle through.
|
||||
*
|
||||
* @var array
|
||||
* An array whose keys are priorities and whose values are arrays of path
|
||||
* processor objects.
|
||||
*/
|
||||
protected $outboundProcessors = array();
|
||||
|
||||
/**
|
||||
* Holds the array of outbound processors, sorted by priority.
|
||||
*
|
||||
* @var array
|
||||
* An array of path processor objects.
|
||||
*/
|
||||
protected $sortedOutbound = array();
|
||||
|
||||
/**
|
||||
* Adds an inbound processor object to the $inboundProcessors property.
|
||||
*
|
||||
|
@ -74,6 +91,46 @@ class PathProcessorManager implements InboundPathProcessorInterface {
|
|||
return $this->sortedInbound;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds an outbound processor object to the $outboundProcessors property.
|
||||
*
|
||||
* @param \Drupal\Core\PathProcessor\OutboundPathProcessorInterface $processor
|
||||
* The processor object to add.
|
||||
*
|
||||
* @param int $priority
|
||||
* The priority of the processor being added.
|
||||
*/
|
||||
public function addOutbound(OutboundPathProcessorInterface $processor, $priority = 0) {
|
||||
$this->outboundProcessors[$priority][] = $processor;
|
||||
$this->sortedOutbound = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound().
|
||||
*/
|
||||
public function processOutbound($path, &$options = array(), Request $request = NULL) {
|
||||
$processors = $this->getOutbound();
|
||||
foreach ($processors as $processor) {
|
||||
$path = $processor->processOutbound($path, $options, $request);
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sorted array of outbound processors.
|
||||
*
|
||||
* @return array
|
||||
* An array of processor objects.
|
||||
*/
|
||||
protected function getOutbound() {
|
||||
if (empty($this->sortedOutbound)) {
|
||||
$this->sortedOutbound = $this->sortProcessors('outboundProcessors');
|
||||
}
|
||||
|
||||
return $this->sortedOutbound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the processors according to priority.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\Core\Routing\GeneratorNotInitializedException.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class for exceptions thrown when the generator has not been initialized.
|
||||
*/
|
||||
class GeneratorNotInitializedException extends Exception { }
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
|
@ -13,17 +14,23 @@ use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
|||
/**
|
||||
* No-op implementation of a Url Generator, needed for backward compatibility.
|
||||
*/
|
||||
class NullGenerator implements UrlGeneratorInterface {
|
||||
class NullGenerator extends UrlGenerator {
|
||||
|
||||
/**
|
||||
* Implements Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate();
|
||||
* Override the parent constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Routing\UrlGenerator::generate();
|
||||
*/
|
||||
public function generate($name, $parameters = array(), $absolute = FALSE) {
|
||||
throw new RouteNotFoundException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Symfony\Component\Routing\RequestContextAwareInterface::setContext();
|
||||
* Overrides Drupal\Core\Routing\UrlGenerator::setContext();
|
||||
*/
|
||||
public function setContext(RequestContext $context) {
|
||||
}
|
||||
|
@ -33,4 +40,11 @@ class NullGenerator implements UrlGeneratorInterface {
|
|||
*/
|
||||
public function getContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Routing\UrlGenerator::processPath().
|
||||
*/
|
||||
protected function processPath($path, &$options = array()) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Core\Routing\PathBasedGeneratorInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Defines an interface for generating a url from a path as opposed to a route.
|
||||
*/
|
||||
interface PathBasedGeneratorInterface {
|
||||
|
||||
/**
|
||||
* Generates an internal or external URL.
|
||||
*
|
||||
* @param $path
|
||||
* (optional) The internal path or external URL being linked to, such as
|
||||
* "node/34" or "http://example.com/foo".
|
||||
*
|
||||
* @param $options
|
||||
* (optional) An associative array of additional options.
|
||||
*
|
||||
* @return
|
||||
* A string containing a URL to the given path.
|
||||
*/
|
||||
public function generateFromPath($path = NULL, $options = array());
|
||||
|
||||
/**
|
||||
* Sets the $request property.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The HttpRequest object representing the current request.
|
||||
*/
|
||||
public function setRequest(Request $request);
|
||||
|
||||
/**
|
||||
* Sets the baseUrl property.
|
||||
*
|
||||
* This property is made up of scheme, host and base_path, e.g.
|
||||
* 'http://www.example.com/mydrupalinstall/'
|
||||
*
|
||||
* The base url is usually set by the request but we allow it to be set
|
||||
* independent of the request so that code that calls url() outside the context
|
||||
* of a request can use the global $base_url variable to set this value.
|
||||
*
|
||||
* @todo Remove this once the url() function no longer supports being called
|
||||
* when there is no request.
|
||||
*
|
||||
* @var string $url
|
||||
* The base url to use for url generation.
|
||||
*/
|
||||
public function setBaseUrl($url);
|
||||
|
||||
/**
|
||||
* Sets the basePath property.
|
||||
*
|
||||
* This will be either '/' or '[subdir]/', where [subdir] is the name of the
|
||||
* subdirectory that Drupal is running in.
|
||||
*
|
||||
* The base path is usually set by the request but we allow it to be set
|
||||
* independent of the request so that code that calls url() outside the context
|
||||
* of a request can use the global $base_url variable to set this value.
|
||||
*
|
||||
* @todo Remove this once the url() function no longer supports being called
|
||||
* when there is no request.
|
||||
*
|
||||
* @var string $path
|
||||
* The base path to use for url generation.
|
||||
*/
|
||||
public function setBasePath($path);
|
||||
|
||||
/**
|
||||
* Sets the scriptPath property.
|
||||
*
|
||||
* The script path is usually set by the request and is either 'index.php' or
|
||||
* the empty string, depending on whether the request path actually contains
|
||||
* the script path or not. We allow it to be set independent of the request so
|
||||
* that code that calls url() outside the context of a request can use the global
|
||||
* $script_path variable to set this value.
|
||||
*
|
||||
* @todo Remove this once the url() function no longer supports being called
|
||||
* when there is no request.
|
||||
*
|
||||
* @var string $path
|
||||
* The script path to use for url generation.
|
||||
*/
|
||||
public function setScriptPath($path);
|
||||
|
||||
}
|
|
@ -7,24 +7,66 @@
|
|||
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
|
||||
use Symfony\Component\Routing\Route as SymfonyRoute;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
|
||||
use Symfony\Cmf\Component\Routing\ProviderBasedGenerator;
|
||||
use Symfony\Cmf\Component\Routing\RouteProviderInterface;
|
||||
|
||||
use Drupal\Core\Path\AliasManagerInterface;
|
||||
use Drupal\Component\Utility\Settings;
|
||||
use Drupal\Component\Utility\UrlValidator;
|
||||
use Drupal\Core\Config\ConfigFactory;
|
||||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
|
||||
/**
|
||||
* A Generator creates URL strings based on a specified route.
|
||||
*/
|
||||
class UrlGenerator extends ProviderBasedGenerator {
|
||||
class UrlGenerator extends ProviderBasedGenerator implements PathBasedGeneratorInterface {
|
||||
|
||||
/**
|
||||
* The alias manager that will be used to alias generated URLs.
|
||||
* A request object.
|
||||
*
|
||||
* @var AliasManagerInterface
|
||||
* @var \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
protected $aliasManager;
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The path processor to convert the system path to one suitable for urls.
|
||||
*
|
||||
* @var \Drupal\Core\PathProcessor\OutboundPathProcessorInterface
|
||||
*/
|
||||
protected $pathProcessor;
|
||||
|
||||
/**
|
||||
* The base path to use for urls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $basePath;
|
||||
|
||||
/**
|
||||
* The base url to use for urls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUrl;
|
||||
|
||||
/**
|
||||
* The script path to use for urls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $scriptPath;
|
||||
|
||||
/**
|
||||
* Whether both secure and insecure session cookies can be used simultaneously.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $mixedModeSessions;
|
||||
|
||||
/**
|
||||
* Constructs a new generator object.
|
||||
|
@ -36,23 +78,324 @@ class UrlGenerator extends ProviderBasedGenerator {
|
|||
* @param \Symfony\Component\HttpKernel\Log\LoggerInterface $logger
|
||||
* An optional logger for recording errors.
|
||||
*/
|
||||
public function __construct(RouteProviderInterface $provider, AliasManagerInterface $alias_manager, LoggerInterface $logger = NULL) {
|
||||
public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, ConfigFactory $config, Settings $settings, LoggerInterface $logger = NULL) {
|
||||
parent::__construct($provider, $logger);
|
||||
|
||||
$this->aliasManager = $alias_manager;
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate();
|
||||
* Implements \Drupal\Core\Routing\PathBasedGeneratorInterface::setRequest().
|
||||
*/
|
||||
public function setRequest(Request $request) {
|
||||
$this->request = $request;
|
||||
// Set some properties, based on the request, that are used during path-based
|
||||
// url generation.
|
||||
$this->basePath = $request->getBasePath() . '/';
|
||||
$this->baseUrl = $request->getSchemeAndHttpHost() . $this->basePath;
|
||||
$this->scriptPath = '';
|
||||
$base_path_with_script = $request->getBaseUrl();
|
||||
$script_name = $request->getScriptName();
|
||||
if (!empty($base_path_with_script) && strpos($base_path_with_script, $script_name) !== FALSE) {
|
||||
$length = strlen($this->basePath);
|
||||
$this->scriptPath = ltrim(substr($script_name, $length), '/') . '/';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Symfony\Component\Routing\Generator\UrlGeneratorInterface::generate().
|
||||
*/
|
||||
public function generate($name, $parameters = array(), $absolute = FALSE) {
|
||||
$path = parent::generate($name, $parameters, $absolute);
|
||||
|
||||
// This method is expected to return a path with a leading /, whereas
|
||||
// the alias manager has no leading /.
|
||||
$path = '/' . $this->aliasManager->getPathAlias(trim($path, '/'));
|
||||
if ($name instanceof SymfonyRoute) {
|
||||
$route = $name;
|
||||
}
|
||||
elseif (null === $route = $this->provider->getRouteByName($name, $parameters)) {
|
||||
throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
// The Route has a cache of its own and is not recompiled as long as it does
|
||||
// not get modified.
|
||||
$compiledRoute = $route->compile();
|
||||
$hostTokens = $compiledRoute->getHostTokens();
|
||||
|
||||
$route_requirements = $route->getRequirements();
|
||||
// We need to bypass the doGenerate() method's handling of absolute URLs as
|
||||
// we handle that ourselves after processing the path.
|
||||
if (isset($route_requirements['_scheme'])) {
|
||||
$scheme_req = $route_requirements['_scheme'];
|
||||
unset($route_requirements['_scheme']);
|
||||
}
|
||||
$path = $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route_requirements, $compiledRoute->getTokens(), $parameters, $name, FALSE, $hostTokens);
|
||||
|
||||
// The URL returned from doGenerate() will include the base path if there is
|
||||
// one (i.e., if running in a subdirectory) so we need to strip that off
|
||||
// before processing the path.
|
||||
$base_url = $this->context->getBaseUrl();
|
||||
if (!empty($base_url) && strpos($path, $base_url) === 0) {
|
||||
$path = substr($path, strlen($base_url));
|
||||
}
|
||||
|
||||
$path = $this->processPath($path);
|
||||
if (!$absolute || !$host = $this->context->getHost()) {
|
||||
return $base_url . $path;
|
||||
}
|
||||
|
||||
// Prepare an absolute URL by getting the correct scheme, host and port from
|
||||
// the request context.
|
||||
$scheme = $this->context->getScheme();
|
||||
if (isset($scheme_req) && ($req = strtolower($scheme_req)) && $scheme !== $req) {
|
||||
$scheme = $req;
|
||||
}
|
||||
$port = '';
|
||||
if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
|
||||
$port = ':' . $this->context->getHttpPort();
|
||||
} elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
|
||||
$port = ':' . $this->context->getHttpsPort();
|
||||
}
|
||||
return $scheme . '://' . $host . $port . $base_url . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Routing\PathBasedGeneratorInterface::generateFromPath().
|
||||
*
|
||||
* @param $path
|
||||
* (optional) The internal path or external URL being linked to, such as
|
||||
* "node/34" or "http://example.com/foo". The default value is equivalent to
|
||||
* passing in '<front>'. A few notes:
|
||||
* - If you provide a full URL, it will be considered an external URL.
|
||||
* - If you provide only the path (e.g. "node/34"), it will be
|
||||
* considered an internal link. In this case, it should be a system URL,
|
||||
* and it will be replaced with the alias, if one exists. Additional query
|
||||
* arguments for internal paths must be supplied in $options['query'], not
|
||||
* included in $path.
|
||||
* - If you provide an internal path and $options['alias'] is set to TRUE, the
|
||||
* path is assumed already to be the correct path alias, and the alias is
|
||||
* not looked up.
|
||||
* - The special string '<front>' generates a link to the site's base URL.
|
||||
* - If your external URL contains a query (e.g. http://example.com/foo?a=b),
|
||||
* then you can either URL encode the query keys and values yourself and
|
||||
* include them in $path, or use $options['query'] to let this method
|
||||
* URL encode them.
|
||||
*
|
||||
* @param $options
|
||||
* (optional) An associative array of additional options, with the following
|
||||
* elements:
|
||||
* - 'query': An array of query key/value-pairs (without any URL-encoding) to
|
||||
* append to the URL.
|
||||
* - 'fragment': A fragment identifier (named anchor) to append to the URL.
|
||||
* Do not include the leading '#' character.
|
||||
* - 'absolute': Defaults to FALSE. Whether to force the output to be an
|
||||
* absolute link (beginning with http:). Useful for links that will be
|
||||
* displayed outside the site, such as in an RSS feed.
|
||||
* - 'alias': Defaults to FALSE. Whether the given path is a URL alias
|
||||
* already.
|
||||
* - 'external': Whether the given path is an external URL.
|
||||
* - 'language': An optional language object. If the path being linked to is
|
||||
* internal to the site, $options['language'] is used to look up the alias
|
||||
* for the URL. If $options['language'] is omitted, the language will be
|
||||
* obtained from language(Language::TYPE_URL).
|
||||
* - 'https': Whether this URL should point to a secure location. If not
|
||||
* defined, the current scheme is used, so the user stays on HTTP or HTTPS
|
||||
* respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can
|
||||
* only be enforced when the variable 'https' is set to TRUE.
|
||||
* - 'base_url': Only used internally, to modify the base URL when a language
|
||||
* dependent URL requires so.
|
||||
* - 'prefix': Only used internally, to modify the path when a language
|
||||
* dependent URL requires so.
|
||||
* - 'script': Added to the URL between the base path and the path prefix.
|
||||
* Defaults to empty string when clean URLs are in effect, and to
|
||||
* 'index.php/' when they are not.
|
||||
* - 'entity_type': The entity type of the object that called url(). Only
|
||||
* set if url() is invoked by Drupal\Core\Entity\Entity::uri().
|
||||
* - 'entity': The entity object (such as a node) for which the URL is being
|
||||
* generated. Only set if url() is invoked by Drupal\Core\Entity\Entity::uri().
|
||||
*
|
||||
* @return
|
||||
* A string containing a URL to the given path.
|
||||
*
|
||||
* @throws \Drupal\Core\Routing\GeneratorNotInitializedException.
|
||||
*/
|
||||
public function generateFromPath($path = NULL, $options = array()) {
|
||||
|
||||
if (!$this->initialized()) {
|
||||
throw new GeneratorNotInitializedException();
|
||||
}
|
||||
|
||||
// Merge in defaults.
|
||||
$options += array(
|
||||
'fragment' => '',
|
||||
'query' => array(),
|
||||
'absolute' => FALSE,
|
||||
'prefix' => '',
|
||||
);
|
||||
|
||||
if (!isset($options['external'])) {
|
||||
// Return an external link if $path contains an allowed absolute URL. Only
|
||||
// call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
|
||||
// before any / ? or #. Note: we could use url_is_external($path) here, but
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (isset($options['fragment']) && $options['fragment'] !== '') {
|
||||
$options['fragment'] = '#' . $options['fragment'];
|
||||
}
|
||||
|
||||
if ($options['external']) {
|
||||
// Split off the fragment.
|
||||
if (strpos($path, '#') !== FALSE) {
|
||||
list($path, $old_fragment) = explode('#', $path, 2);
|
||||
// If $options contains no fragment, take it over from the path.
|
||||
if (isset($old_fragment) && !$options['fragment']) {
|
||||
$options['fragment'] = '#' . $old_fragment;
|
||||
}
|
||||
}
|
||||
// Append the query.
|
||||
if ($options['query']) {
|
||||
$path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $this->httpBuildQuery($options['query']);
|
||||
}
|
||||
if (isset($options['https']) && $this->mixedModeSessions) {
|
||||
if ($options['https'] === TRUE) {
|
||||
$path = str_replace('http://', 'https://', $path);
|
||||
}
|
||||
elseif ($options['https'] === FALSE) {
|
||||
$path = str_replace('https://', 'http://', $path);
|
||||
}
|
||||
}
|
||||
// Reassemble.
|
||||
return $path . $options['fragment'];
|
||||
}
|
||||
else {
|
||||
$path = ltrim($this->processPath($path, $options), '/');
|
||||
}
|
||||
|
||||
if (!isset($options['script'])) {
|
||||
$options['script'] = $this->scriptPath;
|
||||
}
|
||||
// The base_url might be rewritten from the language rewrite in domain mode.
|
||||
if (!isset($options['base_url'])) {
|
||||
if (isset($options['https']) && $this->mixedModeSessions) {
|
||||
if ($options['https'] === TRUE) {
|
||||
$options['base_url'] = str_replace('http://', 'https://', $this->baseUrl);
|
||||
$options['absolute'] = TRUE;
|
||||
}
|
||||
elseif ($options['https'] === FALSE) {
|
||||
$options['base_url'] = str_replace('https://', 'http://', $this->baseUrl);
|
||||
$options['absolute'] = TRUE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$options['base_url'] = $this->baseUrl;
|
||||
}
|
||||
}
|
||||
elseif (rtrim($options['base_url'], '/') == $options['base_url']) {
|
||||
$options['base_url'] .= '/';
|
||||
}
|
||||
$base = $options['absolute'] ? $options['base_url'] : $this->basePath;
|
||||
$prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
|
||||
|
||||
$path = str_replace('%2F', '/', rawurlencode($prefix . $path));
|
||||
$query = $options['query'] ? ('?' . $this->httpBuildQuery($options['query'])) : '';
|
||||
return $base . $options['script'] . $path . $query . $options['fragment'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Routing\PathBasedGeneratorInterface::setBaseUrl().
|
||||
*/
|
||||
public function setBaseUrl($url) {
|
||||
$this->baseUrl = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Routing\PathBasedGeneratorInterface::setBasePath().
|
||||
*/
|
||||
public function setBasePath($path) {
|
||||
$this->basePath = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Routing\PathBasedGeneratorInterface::setScriptPath().
|
||||
*/
|
||||
public function setScriptPath($path) {
|
||||
$this->scriptPath = $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()) {
|
||||
// Router-based paths may have a querystring on them.
|
||||
if ($query_pos = strpos($path, '?')) {
|
||||
// We don't need to do a strict check here because position 0 would mean we
|
||||
// have no actual path to work with.
|
||||
$actual_path = substr($path, 0, $query_pos);
|
||||
$query_string = substr($path, $query_pos);
|
||||
}
|
||||
else {
|
||||
$actual_path = $path;
|
||||
$query_string = '';
|
||||
}
|
||||
$path = '/' . $this->pathProcessor->processOutbound(trim($actual_path, '/'), $options, $this->request);
|
||||
$path .= $query_string;
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the url generator has been initialized.
|
||||
*
|
||||
* @return bool
|
||||
* Returns TRUE if the basePath, baseUrl and scriptPath properties have been
|
||||
* set, FALSE otherwise.
|
||||
*/
|
||||
protected function initialized() {
|
||||
return isset($this->basePath) && isset($this->baseUrl) && isset($this->scriptPath);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
|
||||
namespace Drupal\contextual\Tests;
|
||||
|
||||
use Drupal\simpletest\UnitTestBase;
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
|
||||
/**
|
||||
* Tests _contextual_links_to_id() & _contextual_id_to_links().
|
||||
*/
|
||||
class ContextualUnitTest extends UnitTestBase {
|
||||
class ContextualUnitTest extends DrupalUnitTestBase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Conversion to and from "contextual id"s (for placeholders)',
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\file\Tests;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests for download/file transfer functions.
|
||||
*/
|
||||
|
@ -84,7 +86,6 @@ class DownloadTest extends FileManagedTestBase {
|
|||
* Test file_create_url().
|
||||
*/
|
||||
function testFileCreateUrl() {
|
||||
global $base_url, $script_path;
|
||||
|
||||
// Tilde (~) is excluded from this test because it is encoded by
|
||||
// rawurlencode() in PHP 5.2 but not in PHP 5.3, as per RFC 3986.
|
||||
|
@ -100,12 +101,18 @@ class DownloadTest extends FileManagedTestBase {
|
|||
// generated by url(), whereas private files should be served by Drupal, so
|
||||
// their URLs should be generated by url(). The difference is most apparent
|
||||
// when $script_path is not empty (i.e., when not using clean URLs).
|
||||
$script_path_original = $script_path;
|
||||
foreach (array('', 'index.php/') as $script_path) {
|
||||
$this->checkUrl('public', '', $basename, $base_url . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded);
|
||||
$this->checkUrl('private', '', $basename, $base_url . '/' . $script_path . 'system/files/' . $basename_encoded);
|
||||
$clean_url_settings = array(
|
||||
'clean' => '',
|
||||
'unclean' => 'index.php/',
|
||||
);
|
||||
$generator = $this->container->get('url_generator');
|
||||
foreach ($clean_url_settings as $clean_url_setting => $script_path) {
|
||||
$clean_urls = $clean_url_setting == 'clean';
|
||||
$request = $this->prepareRequestForGenerator($clean_urls);
|
||||
$base_path = $request->getSchemeAndHttpHost() . $request->getBasePath();
|
||||
$this->checkUrl('public', '', $basename, $base_path . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded);
|
||||
$this->checkUrl('private', '', $basename, $base_path . '/' . $script_path . 'system/files/' . $basename_encoded);
|
||||
}
|
||||
$script_path = $script_path_original;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -467,7 +467,7 @@ class ForumTest extends WebTestBase {
|
|||
// Login as the first user.
|
||||
$this->drupalLogin($this->admin_user);
|
||||
// Create a forum container.
|
||||
$this->container = $this->createForum('container');
|
||||
$this->forumContainer = $this->createForum('container');
|
||||
// Create a forum.
|
||||
$this->forum = $this->createForum('forum');
|
||||
// Create a topic.
|
||||
|
|
|
@ -723,12 +723,14 @@ function image_style_flush($style) {
|
|||
* The name of the style to be used with this image.
|
||||
* @param $path
|
||||
* The path to the image.
|
||||
* @param $clean_urls
|
||||
* (optional) Whether clean URLs are in use.
|
||||
* @return
|
||||
* The absolute URL where a style image can be downloaded, suitable for use
|
||||
* in an <img> tag. Requesting the URL will cause the image to be created.
|
||||
* @see image_style_deliver()
|
||||
*/
|
||||
function image_style_url($style_name, $path) {
|
||||
function image_style_url($style_name, $path, $clean_urls = NULL) {
|
||||
$uri = image_style_path($style_name, $path);
|
||||
// The token query is added even if the
|
||||
// 'image.settings:allow_insecure_derivatives' configuration is TRUE, so that
|
||||
|
@ -743,11 +745,22 @@ function image_style_url($style_name, $path) {
|
|||
$token_query = array(IMAGE_DERIVATIVE_TOKEN => image_style_path_token($style_name, file_stream_wrapper_uri_normalize($path)));
|
||||
}
|
||||
|
||||
if ($clean_urls === NULL) {
|
||||
// Assume clean URLs unless the request tells us otherwise.
|
||||
$clean_urls = TRUE;
|
||||
try {
|
||||
$request = Drupal::request();
|
||||
$clean_urls = $request->attributes->get('clean_urls');
|
||||
}
|
||||
catch (ServiceNotFoundException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
// If not using clean URLs, the image derivative callback is only available
|
||||
// with the script path. If the file does not exist, use url() to ensure
|
||||
// that it is included. Once the file exists it's fine to fall back to the
|
||||
// actual file path, this avoids bootstrapping PHP once the files are built.
|
||||
if ($GLOBALS['script_path'] && file_uri_scheme($uri) == 'public' && !file_exists($uri)) {
|
||||
if ($clean_urls === FALSE && file_uri_scheme($uri) == 'public' && !file_exists($uri)) {
|
||||
$directory_path = file_stream_wrapper_get_instance_by_uri($uri)->getDirectoryPath();
|
||||
return url($directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE, 'query' => $token_query));
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\image\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests the functions for generating paths and URLs for image styles.
|
||||
|
@ -104,8 +105,7 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
* Tests image_style_url().
|
||||
*/
|
||||
function _testImageStyleUrlAndPath($scheme, $clean_url = TRUE, $extra_slash = FALSE) {
|
||||
$script_path_original = $GLOBALS['script_path'];
|
||||
$GLOBALS['script_path'] = $clean_url ? '' : 'index.php/';
|
||||
$request = $this->prepareRequestForGenerator($clean_url);
|
||||
|
||||
// Make the default scheme neither "public" nor "private" to verify the
|
||||
// functions work for other than the default scheme.
|
||||
|
@ -129,7 +129,7 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
// Get the URL of a file that has not been generated and try to create it.
|
||||
$generated_uri = image_style_path($this->style_name, $original_uri);
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$generate_url = image_style_url($this->style_name, $original_uri);
|
||||
$generate_url = image_style_url($this->style_name, $original_uri, $clean_url);
|
||||
|
||||
// Ensure that the tests still pass when the file is generated by accessing
|
||||
// a poorly constructed (but still valid) file URL that has an extra slash
|
||||
|
@ -137,11 +137,10 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
if ($extra_slash) {
|
||||
$modified_uri = str_replace('://', ':///', $original_uri);
|
||||
$this->assertNotEqual($original_uri, $modified_uri, 'An extra slash was added to the generated file URI.');
|
||||
$generate_url = image_style_url($this->style_name, $modified_uri);
|
||||
$generate_url = image_style_url($this->style_name, $modified_uri, $clean_url);
|
||||
}
|
||||
|
||||
if ($GLOBALS['script_path']) {
|
||||
$this->assertTrue(strpos($generate_url, $GLOBALS['script_path']) !== FALSE, 'When using non-clean URLS, the system path contains the script name.');
|
||||
if (!$clean_url) {
|
||||
$this->assertTrue(strpos($generate_url, 'index.php/') !== FALSE, 'When using non-clean URLS, the system path contains the script name.');
|
||||
}
|
||||
// Add some extra chars to the token.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
|
||||
|
@ -194,7 +193,7 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
$this->assertNoRaw( chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10), 'No PNG signature found in the response body.');
|
||||
}
|
||||
}
|
||||
elseif (!$GLOBALS['script_path']) {
|
||||
elseif ($clean_url) {
|
||||
// Add some extra chars to the token.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
|
||||
$this->assertResponse(200, 'Existing image was accessible at the URL wih an invalid token.');
|
||||
|
@ -219,11 +218,10 @@ class ImageStylesPathAndUrlTest extends WebTestBase {
|
|||
config('image.settings')->set('suppress_itok_output', TRUE)->save();
|
||||
$generated_uri = image_style_path($this->style_name, $original_uri);
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$generate_url = image_style_url($this->style_name, $original_uri);
|
||||
$generate_url = image_style_url($this->style_name, $original_uri, $clean_url);
|
||||
$this->assertIdentical(strpos($generate_url, IMAGE_DERIVATIVE_TOKEN . '='), FALSE, 'The security token does not appear in the image style URL.');
|
||||
$this->drupalGet($generate_url);
|
||||
$this->assertResponse(200, 'Image was accessible at the URL with a missing token.');
|
||||
|
||||
$GLOBALS['script_path'] = $script_path_original;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -634,7 +634,6 @@ function language_language_negotiation_info() {
|
|||
'callbacks' => array(
|
||||
'negotiation' => 'language_from_url',
|
||||
'language_switch' => 'language_switcher_url',
|
||||
'url_rewrite' => 'language_url_rewrite_url',
|
||||
),
|
||||
'file' => $file,
|
||||
'weight' => -8,
|
||||
|
@ -770,56 +769,6 @@ function language_preprocess_block(&$variables) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_url_outbound_alter().
|
||||
*
|
||||
* Rewrite outbound URLs with language based prefixes.
|
||||
*/
|
||||
function language_url_outbound_alter(&$path, &$options, $original_path) {
|
||||
// Only modify internal URLs.
|
||||
if (!$options['external'] && language_multilingual()) {
|
||||
static $drupal_static_fast;
|
||||
if (!isset($drupal_static_fast)) {
|
||||
$drupal_static_fast['callbacks'] = &drupal_static(__FUNCTION__);
|
||||
}
|
||||
$callbacks = &$drupal_static_fast['callbacks'];
|
||||
|
||||
if (!isset($callbacks)) {
|
||||
$callbacks = array();
|
||||
include_once DRUPAL_ROOT . '/core/includes/language.inc';
|
||||
|
||||
foreach (language_types_get_configurable() as $type) {
|
||||
// Get URL rewriter callbacks only from enabled language methods.
|
||||
$negotiation = variable_get("language_negotiation_$type", array());
|
||||
|
||||
foreach ($negotiation as $method_id => $method) {
|
||||
if (isset($method['callbacks']['url_rewrite'])) {
|
||||
if (isset($method['file'])) {
|
||||
require_once DRUPAL_ROOT . '/' . $method['file'];
|
||||
}
|
||||
// Avoid duplicate callback entries.
|
||||
$callbacks[$method['callbacks']['url_rewrite']] = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$callbacks = array_keys($callbacks);
|
||||
}
|
||||
|
||||
// No language dependent path allowed in this mode.
|
||||
if (empty($callbacks)) {
|
||||
unset($options['language']);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($callbacks as $callback) {
|
||||
if (function_exists($callback)) {
|
||||
$callback($path, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns language mappings between browser and Drupal language codes.
|
||||
*
|
||||
|
|
|
@ -424,83 +424,6 @@ function language_switcher_session($type, $path) {
|
|||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite URLs for the URL language negotiation method.
|
||||
*/
|
||||
function language_url_rewrite_url(&$path, &$options) {
|
||||
static $drupal_static_fast;
|
||||
if (!isset($drupal_static_fast)) {
|
||||
$drupal_static_fast['languages'] = &drupal_static(__FUNCTION__);
|
||||
}
|
||||
$languages = &$drupal_static_fast['languages'];
|
||||
|
||||
if (!isset($languages)) {
|
||||
$languages = language_list();
|
||||
$languages = array_flip(array_keys($languages));
|
||||
}
|
||||
|
||||
// Language can be passed as an option, or we go for current URL language.
|
||||
if (!isset($options['language'])) {
|
||||
$language_url = language(Language::TYPE_URL);
|
||||
$options['language'] = $language_url;
|
||||
}
|
||||
// We allow only enabled languages here.
|
||||
elseif (is_object($options['language']) && !isset($languages[$options['language']->langcode])) {
|
||||
unset($options['language']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($options['language'])) {
|
||||
switch (config('language.negotiation')->get('url.source')) {
|
||||
case LANGUAGE_NEGOTIATION_URL_DOMAIN:
|
||||
$domains = language_negotiation_url_domains();
|
||||
if (is_object($options['language']) && !empty($domains[$options['language']->langcode])) {
|
||||
// Save the original base URL. If it contains a port, we need to
|
||||
// retain it below.
|
||||
if (!empty($options['base_url'])) {
|
||||
// The colon in the URL scheme messes up the port checking below.
|
||||
$normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']);
|
||||
|
||||
}
|
||||
|
||||
// Ask for an absolute URL with our modified base URL.
|
||||
$url_scheme = Drupal::request()->isSecure() ? 'https://' : 'http://';
|
||||
$options['absolute'] = TRUE;
|
||||
$options['base_url'] = $url_scheme . $domains[$options['language']->langcode];
|
||||
|
||||
// In case either the original base URL or the HTTP host contains a
|
||||
// port, retain it.
|
||||
$http_host = $_SERVER['HTTP_HOST'];
|
||||
if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
|
||||
list($host, $port) = explode(':', $normalized_base_url);
|
||||
$options['base_url'] .= ':' . $port;
|
||||
}
|
||||
elseif (strpos($http_host, ':') !== FALSE) {
|
||||
list($host, $port) = explode(':', $http_host);
|
||||
$options['base_url'] .= ':' . $port;
|
||||
}
|
||||
|
||||
if (isset($options['https']) && settings()->get('mixed_mode_sessions', FALSE)) {
|
||||
if ($options['https'] === TRUE) {
|
||||
$options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
|
||||
}
|
||||
elseif ($options['https'] === FALSE) {
|
||||
$options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LANGUAGE_NEGOTIATION_URL_PREFIX:
|
||||
$prefixes = language_negotiation_url_prefixes();
|
||||
if (is_object($options['language']) &&!empty($prefixes[$options['language']->langcode])) {
|
||||
$options['prefix'] = $prefixes[$options['language']->langcode] . '/';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads language prefixes and uses the langcode if no prefix is set.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
services:
|
||||
path_processor_language:
|
||||
class: Drupal\language\HttpKernel\PathProcessorLanguage
|
||||
arguments: ['@config.factory', '@settings', '@language_manager']
|
||||
tags:
|
||||
- { name: path_processor_inbound, priority: 300 }
|
||||
arguments: ['@config.factory']
|
||||
- { name: path_processor_outbound, priority: 100 }
|
||||
|
|
|
@ -7,15 +7,19 @@
|
|||
|
||||
namespace Drupal\language\HttpKernel;
|
||||
|
||||
use Drupal\Component\Utility\Settings;
|
||||
use Drupal\Core\Config\ConfigFactory;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
||||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Processes the inbound path using path alias lookups.
|
||||
*/
|
||||
class PathProcessorLanguage implements InboundPathProcessorInterface {
|
||||
class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
|
||||
|
||||
/**
|
||||
* A config factory for retrieving required config settings.
|
||||
|
@ -24,6 +28,20 @@ class PathProcessorLanguage implements InboundPathProcessorInterface {
|
|||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Whether both secure and insecure session cookies can be used simultaneously.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $mixedModeSessions;
|
||||
|
||||
/**
|
||||
* Language manager for retrieving the url language type.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManager
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* An array of enabled languages.
|
||||
*
|
||||
|
@ -31,6 +49,7 @@ class PathProcessorLanguage implements InboundPathProcessorInterface {
|
|||
*/
|
||||
protected $languages;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a PathProcessorLanguage object.
|
||||
*
|
||||
|
@ -41,8 +60,10 @@ class PathProcessorLanguage implements InboundPathProcessorInterface {
|
|||
* An array of languages, keyed by language code, representing the languages
|
||||
* currently enabled on the site.
|
||||
*/
|
||||
public function __construct(ConfigFactory $config, array $languages = array()) {
|
||||
public function __construct(ConfigFactory $config, Settings $settings, LanguageManager $language_manager, array $languages = array()) {
|
||||
$this->config = $config;
|
||||
$this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE);
|
||||
$this->languageManager = $language_manager;
|
||||
if (empty($languages)) {
|
||||
$languages = language_list();
|
||||
}
|
||||
|
@ -69,4 +90,74 @@ class PathProcessorLanguage implements InboundPathProcessorInterface {
|
|||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processOutbound().
|
||||
*/
|
||||
public function processOutbound($path, &$options = array(), Request $request = NULL) {
|
||||
if (!$this->languageManager->isMultilingual()) {
|
||||
return $path;
|
||||
}
|
||||
$url_scheme = 'http';
|
||||
$port = 80;
|
||||
if ($request) {
|
||||
$url_scheme = $request->getScheme();
|
||||
$port = $request->getPort();
|
||||
}
|
||||
$languages = array_flip(array_keys($this->languages));
|
||||
// Language can be passed as an option, or we go for current URL language.
|
||||
if (!isset($options['language'])) {
|
||||
$language_url = $this->languageManager->getLanguage(Language::TYPE_URL);
|
||||
$options['language'] = $language_url;
|
||||
}
|
||||
// We allow only enabled languages here.
|
||||
elseif (is_object($options['language']) && !isset($languages[$options['language']->langcode])) {
|
||||
return $path;
|
||||
}
|
||||
$url_source = $this->config->get('language.negotiation')->get('url.source');
|
||||
// @todo Go back to using a constant instead of the string 'path_prefix' once we can use a class
|
||||
// constant.
|
||||
if ($url_source == 'path_prefix') {
|
||||
$prefixes = $this->config->get('language.negotiation')->get('url.prefixes');
|
||||
if (is_object($options['language']) && !empty($prefixes[$options['language']->langcode])) {
|
||||
return empty($path) ? $prefixes[$options['language']->langcode] : $prefixes[$options['language']->langcode] . '/' . $path;
|
||||
}
|
||||
}
|
||||
elseif ($url_source == 'domain') {
|
||||
$domains = $this->config->get('language.negotiation')->get('url.domains');
|
||||
if (is_object($options['language']) && !empty($domains[$options['language']->langcode])) {
|
||||
|
||||
// Save the original base URL. If it contains a port, we need to
|
||||
// retain it below.
|
||||
if (!empty($options['base_url'])) {
|
||||
// The colon in the URL scheme messes up the port checking below.
|
||||
$normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']);
|
||||
}
|
||||
|
||||
// Ask for an absolute URL with our modified base URL.
|
||||
$options['absolute'] = TRUE;
|
||||
$options['base_url'] = $url_scheme . '://' . $domains[$options['language']->langcode];
|
||||
|
||||
// In case either the original base URL or the HTTP host contains a
|
||||
// port, retain it.
|
||||
if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
|
||||
list($host, $port) = explode(':', $normalized_base_url);
|
||||
$options['base_url'] .= ':' . $port;
|
||||
}
|
||||
elseif ($port != 80) {
|
||||
$options['base_url'] .= ':' . $port;
|
||||
}
|
||||
|
||||
if (isset($options['https']) && $this->mixedModeSessions) {
|
||||
if ($options['https'] === TRUE) {
|
||||
$options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
|
||||
}
|
||||
elseif ($options['https'] === FALSE) {
|
||||
$options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -452,6 +452,7 @@ class LanguageUILanguageNegotiationTest extends WebTestBase {
|
|||
'domain[it]' => 'it.example.com',
|
||||
);
|
||||
$this->drupalPost('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Build the link we're going to test.
|
||||
$link = 'it.example.com/admin';
|
||||
|
@ -466,17 +467,19 @@ class LanguageUILanguageNegotiationTest extends WebTestBase {
|
|||
|
||||
// Test HTTPS via options.
|
||||
$this->settingsSet('mixed_mode_sessions', TRUE);
|
||||
$this->rebuildContainer();
|
||||
|
||||
$italian_url = url('admin', array('https' => TRUE, 'language' => $languages['it'], 'script' => ''));
|
||||
$correct_link = 'https://' . $link;
|
||||
$this->assertTrue($italian_url == $correct_link, format_string('The url() function returns the right HTTPS URL (via options) (@url) in accordance with the chosen language', array('@url' => $italian_url)));
|
||||
$this->settingsSet('mixed_mode_sessions', FALSE);
|
||||
|
||||
// Test HTTPS via current URL scheme.
|
||||
$temp_https = $this->request->server->get('HTTPS');
|
||||
$this->request->server->set('HTTPS', 'on');
|
||||
$generator = $this->container->get('url_generator');
|
||||
$request = Request::create('', 'GET', array(), array(), array(), array('HTTPS' => 'on'));
|
||||
$generator->setRequest($request);
|
||||
$italian_url = url('admin', array('language' => $languages['it'], 'script' => ''));
|
||||
$correct_link = 'https://' . $link;
|
||||
$this->assertTrue($italian_url == $correct_link, format_string('The url() function returns the right URL (via current URL scheme) (@url) in accordance with the chosen language', array('@url' => $italian_url)));
|
||||
$this->request->server->set('HTTPS', $temp_https);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\language\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Test that URL rewriting works as expected.
|
||||
|
@ -47,8 +48,6 @@ class LanguageUrlRewritingTest extends WebTestBase {
|
|||
|
||||
// Reset static caching.
|
||||
drupal_static_reset('language_list');
|
||||
drupal_static_reset('language_url_outbound_alter');
|
||||
drupal_static_reset('language_url_rewrite_url');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,6 +59,7 @@ class LanguageUrlRewritingTest extends WebTestBase {
|
|||
$non_existing->langcode = $this->randomName();
|
||||
$this->checkUrl($non_existing, 'Path language is ignored if language is not installed.', 'URL language negotiation does not work with non-installed languages');
|
||||
|
||||
$request = $this->prepareRequestForGenerator();
|
||||
// Check that URL rewriting is not applied to subrequests.
|
||||
$this->drupalGet('language_test/subrequest');
|
||||
$this->assertText($this->web_user->name, 'Page correctly retrieved');
|
||||
|
@ -109,6 +109,9 @@ class LanguageUrlRewritingTest extends WebTestBase {
|
|||
'domain[fr]' => $language_domain
|
||||
);
|
||||
$this->drupalPost('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
|
||||
// Rebuild the container so that the new language gets picked up by services
|
||||
// that hold the list of languages.
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Enable domain configuration.
|
||||
config('language.negotiation')
|
||||
|
@ -117,17 +120,12 @@ class LanguageUrlRewritingTest extends WebTestBase {
|
|||
|
||||
// Reset static caching.
|
||||
drupal_static_reset('language_list');
|
||||
drupal_static_reset('language_url_outbound_alter');
|
||||
drupal_static_reset('language_url_rewrite_url');
|
||||
|
||||
// In case index.php is part of the URLs, we need to adapt the asserted
|
||||
// URLs as well.
|
||||
$index_php = strpos(url('', array('absolute' => TRUE)), 'index.php') !== FALSE;
|
||||
|
||||
// Remember current HTTP_HOST.
|
||||
$http_host = $_SERVER['HTTP_HOST'];
|
||||
// Fake a different port.
|
||||
$_SERVER['HTTP_HOST'] .= ':88';
|
||||
$request = $this->prepareRequestForGenerator(TRUE, array('SERVER_PORT' => '88'));
|
||||
|
||||
// Create an absolute French link.
|
||||
$language = language_load('fr');
|
||||
|
@ -137,22 +135,18 @@ class LanguageUrlRewritingTest extends WebTestBase {
|
|||
));
|
||||
|
||||
$expected = $index_php ? 'http://example.fr:88/index.php/' : 'http://example.fr:88/';
|
||||
|
||||
$this->assertEqual($url, $expected, 'The right port is used.');
|
||||
|
||||
// If we set the port explicitly in url(), it should not be overriden.
|
||||
$url = url('', array(
|
||||
'absolute' => TRUE,
|
||||
'language' => $language,
|
||||
'base_url' => $GLOBALS['base_url'] . ':90',
|
||||
'base_url' => $request->getBaseUrl() . ':90',
|
||||
));
|
||||
|
||||
$expected = $index_php ? 'http://example.fr:90/index.php/' : 'http://example.fr:90/';
|
||||
|
||||
$this->assertEqual($url, $expected, 'A given port is not overriden.');
|
||||
|
||||
// Restore HTTP_HOST.
|
||||
$_SERVER['HTTP_HOST'] = $http_host;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -119,5 +119,18 @@ function language_test_menu() {
|
|||
* Page callback. Uses a subrequest to retrieve the 'user' page.
|
||||
*/
|
||||
function language_test_subrequest() {
|
||||
return drupal_container()->get('http_kernel')->handle(Request::create('/user'), HttpKernelInterface::SUB_REQUEST);
|
||||
$request = Request::createFromGlobals();
|
||||
$server = $request->server->all();
|
||||
if (basename($server['SCRIPT_FILENAME']) != basename($server['SCRIPT_NAME'])) {
|
||||
// We need this for when the test is executed by run-tests.sh.
|
||||
// @todo Remove this once run-tests.sh has been converted to use a Request
|
||||
// object.
|
||||
$server['SCRIPT_FILENAME'] = $server['SCRIPT_NAME'];
|
||||
$base_path = ltrim($server['REQUEST_URI'], '/');
|
||||
}
|
||||
else {
|
||||
$base_path = $request->getBasePath();
|
||||
}
|
||||
$subrequest = Request::create($base_path . '/user', 'GET', $request->query->all(), $request->cookies->all(), array(), $server);
|
||||
return Drupal::service('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
|
||||
}
|
||||
|
|
|
@ -851,6 +851,10 @@ function locale_update_8011() {
|
|||
* Renames language_default language negotiation method to language_selected.
|
||||
*/
|
||||
function locale_update_8013() {
|
||||
// @todo We only need language.inc here because LANGUAGE_NEGOTIATION_SELECTED
|
||||
// is defined there. Remove this line once that has been converted to a class
|
||||
// constant.
|
||||
require_once DRUPAL_ROOT . '/core/includes/language.inc';
|
||||
$weight = update_variable_get('language_negotiation_methods_weight_language_interface', NULL);
|
||||
if ($weight !== NULL) {
|
||||
$weight[LANGUAGE_NEGOTIATION_SELECTED] = $weight['language-default'];
|
||||
|
|
|
@ -97,10 +97,10 @@ class PathLanguageTest extends PathTestBase {
|
|||
// Confirm that the alias is returned by url(). Languages are cached on
|
||||
// many levels, and we need to clear those caches.
|
||||
drupal_static_reset('language_list');
|
||||
drupal_static_reset('language_url_outbound_alter');
|
||||
drupal_static_reset('language_url_rewrite_url');
|
||||
$this->rebuildContainer();
|
||||
$languages = language_list();
|
||||
$url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->langcode]));
|
||||
$url = $this->container->get('url_generator')->generateFromPath('node/' . $french_node->nid, array('language' => $languages[$french_node->langcode]));
|
||||
|
||||
$this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.');
|
||||
|
||||
// Confirm that the alias works even when changing language negotiation
|
||||
|
|
|
@ -49,7 +49,7 @@ class BundleSchema extends EntitySchema {
|
|||
*/
|
||||
public function getUri() {
|
||||
$path = str_replace(array('{entity_type}', '{bundle}'), array($this->entityType, $this->bundle), static::$uriPattern);
|
||||
return $this->siteSchema->getUri() . $path;
|
||||
return $this->siteSchema->getUri() . '/' . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,7 +58,7 @@ class EntitySchema extends SchemaTermBase {
|
|||
*/
|
||||
public function getUri() {
|
||||
$path = str_replace('{entity_type}', $this->entityType , static::$uriPattern);
|
||||
return $this->siteSchema->getUri() . $path;
|
||||
return $this->siteSchema->getUri() . '/' . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,7 +47,6 @@ class SiteSchemaTest extends WebTestBase {
|
|||
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => 'http://www.w3.org/2000/01/rdf-schema#class',
|
||||
'http://www.w3.org/2000/01/rdf-schema#subClassOf' => url("$schema_path$entity_type", array('absolute' => TRUE)),
|
||||
);
|
||||
|
||||
$this->assertEqual($bundle_schema->getUri(), $bundle_uri, 'Bundle term URI is generated correctly.');
|
||||
$this->assertEqual($bundle_schema->getProperties(), $bundle_properties, 'Bundle term properties are generated correctly.');
|
||||
}
|
||||
|
|
|
@ -174,6 +174,16 @@ abstract class DrupalUnitTestBase extends UnitTestBase {
|
|||
->setFactoryMethod('get')
|
||||
->addArgument('state');
|
||||
}
|
||||
|
||||
if ($container->hasDefinition('path_processor_alias')) {
|
||||
// Prevent the alias-based path processor, which requires a url_alias db
|
||||
// table, from being registered to the path processor manager. We do this
|
||||
// by removing the tags that the compiler pass looks for. This means the
|
||||
// url generator can safely be used within DUTB tests.
|
||||
$definition = $container->getDefinition('path_processor_alias');
|
||||
$definition->clearTag('path_processor_inbound')->clearTag('path_processor_outbound');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@ use DOMDocument;
|
|||
use DOMXPath;
|
||||
use SimpleXMLElement;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Test case for typical Drupal tests.
|
||||
|
@ -905,6 +906,16 @@ abstract class WebTestBase extends TestBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\simpletest\TestBase::rebuildContainer().
|
||||
*/
|
||||
protected function rebuildContainer() {
|
||||
parent::rebuildContainer();
|
||||
// Make sure the url generator has a request object, otherwise calls to
|
||||
// $this->drupalGet() will fail.
|
||||
$this->prepareRequestForGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all data structures after having enabled new modules.
|
||||
*
|
||||
|
@ -1181,7 +1192,7 @@ abstract class WebTestBase extends TestBase {
|
|||
* @param $path
|
||||
* Drupal path or URL to load into internal browser
|
||||
* @param $options
|
||||
* Options to be forwarded to url().
|
||||
* Options to be forwarded to the url generator.
|
||||
* @param $headers
|
||||
* An array containing additional HTTP request headers, each formatted as
|
||||
* "name: value".
|
||||
|
@ -1194,7 +1205,8 @@ abstract class WebTestBase extends TestBase {
|
|||
// We re-using a CURL connection here. If that connection still has certain
|
||||
// options set, it might change the GET into a POST. Make sure we clear out
|
||||
// previous options.
|
||||
$out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers));
|
||||
$url = $this->container->get('url_generator')->generateFromPath($path, $options);
|
||||
$out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $url, CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers));
|
||||
$this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up.
|
||||
|
||||
// Replace original page output with new output from redirected page(s).
|
||||
|
@ -1311,7 +1323,7 @@ abstract class WebTestBase extends TestBase {
|
|||
* textfield: under these conditions, no button information is added to the
|
||||
* POST data.
|
||||
* @param $options
|
||||
* Options to be forwarded to url().
|
||||
* Options to be forwarded to the url generator.
|
||||
* @param $headers
|
||||
* An array containing additional HTTP request headers, each formatted as
|
||||
* "name: value".
|
||||
|
@ -1436,7 +1448,7 @@ abstract class WebTestBase extends TestBase {
|
|||
* element. In the absence of both the triggering element's Ajax path and
|
||||
* $ajax_path 'system/ajax' will be used.
|
||||
* @param $options
|
||||
* (optional) Options to be forwarded to url().
|
||||
* (optional) Options to be forwarded to the url generator.
|
||||
* @param $headers
|
||||
* (optional) An array containing additional HTTP request headers, each
|
||||
* formatted as "name: value". Forwarded to drupalPost().
|
||||
|
@ -1644,7 +1656,7 @@ abstract class WebTestBase extends TestBase {
|
|||
* @param $path
|
||||
* Drupal path or URL to load into internal browser
|
||||
* @param $options
|
||||
* Options to be forwarded to url().
|
||||
* Options to be forwarded to the url generator.
|
||||
* @param $headers
|
||||
* An array containing additional HTTP request headers, each formatted as
|
||||
* "name: value".
|
||||
|
@ -1653,7 +1665,8 @@ abstract class WebTestBase extends TestBase {
|
|||
*/
|
||||
protected function drupalHead($path, array $options = array(), array $headers = array()) {
|
||||
$options['absolute'] = TRUE;
|
||||
$out = $this->curlExec(array(CURLOPT_NOBODY => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_HTTPHEADER => $headers));
|
||||
$url = $this->container->get('url_generator')->generateFromPath($path, $options);
|
||||
$out = $this->curlExec(array(CURLOPT_NOBODY => TRUE, CURLOPT_URL => $url, CURLOPT_HTTPHEADER => $headers));
|
||||
$this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up.
|
||||
|
||||
if ($this->dumpHeaders) {
|
||||
|
@ -2241,7 +2254,7 @@ abstract class WebTestBase extends TestBase {
|
|||
* @param $path
|
||||
* The expected system path.
|
||||
* @param $options
|
||||
* (optional) Any additional options to pass for $path to url().
|
||||
* (optional) Any additional options to pass for $path to the url generator.
|
||||
* @param $message
|
||||
* (optional) A message to display with the assertion. Do not translate
|
||||
* messages: use format_string() to embed variables in the message text, not
|
||||
|
@ -2258,11 +2271,11 @@ abstract class WebTestBase extends TestBase {
|
|||
protected function assertUrl($path, array $options = array(), $message = '', $group = 'Other') {
|
||||
if (!$message) {
|
||||
$message = t('Current URL is @url.', array(
|
||||
'@url' => var_export(url($path, $options), TRUE),
|
||||
'@url' => var_export($this->container->get('url_generator')->generateFromPath($path, $options), TRUE),
|
||||
));
|
||||
}
|
||||
$options['absolute'] = TRUE;
|
||||
return $this->assertEqual($this->getUrl(), url($path, $options), $message, $group);
|
||||
return $this->assertEqual($this->getUrl(), $this->container->get('url_generator')->generateFromPath($path, $options), $message, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3252,4 +3265,50 @@ abstract class WebTestBase extends TestBase {
|
|||
$this->verbose(t('Email:') . '<pre>' . print_r($mail, TRUE) . '</pre>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mock request and sets it on the generator.
|
||||
*
|
||||
* This is used to manipulate how the generator generates paths during tests.
|
||||
* It also ensures that calls to $this->drupalGet() will work when running
|
||||
* from run-tests.sh because the url generator no longer looks at the global
|
||||
* variables that are set there but relies on getting this information from a
|
||||
* request object.
|
||||
*
|
||||
* @param bool $clean_urls
|
||||
* Whether to mock the request using clean urls.
|
||||
*
|
||||
* @param $override_server_vars
|
||||
* An array of server variables to override.
|
||||
*
|
||||
* @return $request
|
||||
* The mocked request object.
|
||||
*/
|
||||
protected function prepareRequestForGenerator($clean_urls = TRUE, $override_server_vars = array()) {
|
||||
$generator = $this->container->get('url_generator');
|
||||
$request = Request::createFromGlobals();
|
||||
$server = $request->server->all();
|
||||
if (basename($server['SCRIPT_FILENAME']) != basename($server['SCRIPT_NAME'])) {
|
||||
// We need this for when the test is executed by run-tests.sh.
|
||||
// @todo Remove this once run-tests.sh has been converted to use a Request
|
||||
// object.
|
||||
$cwd = getcwd();
|
||||
$server['SCRIPT_FILENAME'] = $cwd . '/' . basename($server['SCRIPT_NAME']);
|
||||
$base_path = rtrim($server['REQUEST_URI'], '/');
|
||||
}
|
||||
else {
|
||||
$base_path = $request->getBasePath();
|
||||
}
|
||||
if ($clean_urls) {
|
||||
$request_path = $base_path ? $base_path . '/user' : 'user';
|
||||
}
|
||||
else {
|
||||
$request_path = $base_path ? $base_path . '/index.php/user' : '/index.php/user';
|
||||
}
|
||||
$server = array_merge($server, $override_server_vars);
|
||||
|
||||
$request = Request::create($request_path, 'GET', array(), array(), array(), $server);
|
||||
$generator->setRequest($request);
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\system\Tests\Common;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests for URL generation functions.
|
||||
|
@ -263,50 +264,6 @@ class UrlTest extends WebTestBase {
|
|||
$this->assertFalse(valid_url($parts['path'], TRUE), 'drupal_parse_url() correctly parsed a forged URL.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests url() functionality.
|
||||
*
|
||||
* Tests url() with/without query, with/without fragment, absolute on/off and
|
||||
* asserts all that works when clean URLs are on and off.
|
||||
*/
|
||||
function testUrl() {
|
||||
global $base_url, $script_path;
|
||||
|
||||
$script_path_original = $script_path;
|
||||
foreach (array('', 'index.php/') as $script_path) {
|
||||
foreach (array(FALSE, TRUE) as $absolute) {
|
||||
// Get the expected start of the path string.
|
||||
$base = ($absolute ? $base_url . '/' : base_path()) . $script_path;
|
||||
$absolute_string = $absolute ? 'absolute' : NULL;
|
||||
|
||||
$url = $base . 'node/123';
|
||||
$result = url('node/123', array('absolute' => $absolute));
|
||||
$this->assertEqual($url, $result, "$url == $result");
|
||||
|
||||
$url = $base . 'node/123#foo';
|
||||
$result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
|
||||
$this->assertEqual($url, $result, "$url == $result");
|
||||
|
||||
$url = $base . 'node/123?foo';
|
||||
$result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
|
||||
$this->assertEqual($url, $result, "$url == $result");
|
||||
|
||||
$url = $base . 'node/123?foo=bar&bar=baz';
|
||||
$result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
|
||||
$this->assertEqual($url, $result, "$url == $result");
|
||||
|
||||
$url = $base . 'node/123?foo#bar';
|
||||
$result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
|
||||
$this->assertEqual($url, $result, "$url == $result");
|
||||
|
||||
$url = $base;
|
||||
$result = url('<front>', array('absolute' => $absolute));
|
||||
$this->assertEqual($url, $result, "$url == $result");
|
||||
}
|
||||
}
|
||||
$script_path = $script_path_original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests external URL handling.
|
||||
*/
|
||||
|
|
|
@ -46,7 +46,8 @@ class UrlAlterFunctionalTest extends WebTestBase {
|
|||
|
||||
// Test that a path always uses its alias.
|
||||
$path = array('source' => "user/$uid/test1", 'alias' => 'alias/test1');
|
||||
drupal_container()->get('path.crud')->save($path['source'], $path['alias']);
|
||||
$this->container->get('path.crud')->save($path['source'], $path['alias']);
|
||||
$this->rebuildContainer();
|
||||
$this->assertUrlInboundAlter('alias/test1', "user/$uid/test1");
|
||||
$this->assertUrlOutboundAlter("user/$uid/test1", 'alias/test1');
|
||||
|
||||
|
@ -100,7 +101,7 @@ class UrlAlterFunctionalTest extends WebTestBase {
|
|||
*/
|
||||
protected function assertUrlOutboundAlter($original, $final) {
|
||||
// Test outbound altering.
|
||||
$result = url($original);
|
||||
$result = $this->container->get('url_generator')->generateFromPath($original);
|
||||
$base_path = base_path() . $GLOBALS['script_path'];
|
||||
$result = substr($result, strlen($base_path));
|
||||
$this->assertIdentical($result, $final, format_string('Altered outbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\system\Tests\Routing\UrlGeneratorTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Routing;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
use Drupal\simpletest\UnitTestBase;
|
||||
|
||||
use Drupal\Core\Routing\UrlGenerator;
|
||||
|
||||
/**
|
||||
* Basic tests for the Route.
|
||||
*/
|
||||
class UrlGeneratorTest extends UnitTestBase {
|
||||
|
||||
protected $generator;
|
||||
|
||||
protected $aliasManager;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'UrlGenerator',
|
||||
'description' => 'Confirm that the UrlGenerator is functioning properly.',
|
||||
'group' => 'Routing',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$routes = new RouteCollection();
|
||||
$routes->add('test_1', new Route('/test/one'));
|
||||
$routes->add('test_2', new Route('/test/two/{narf}'));
|
||||
$provider = new MockRouteProvider($routes);
|
||||
|
||||
$this->aliasManager = new MockAliasManager();
|
||||
$this->aliasManager->addAlias('test/one', 'hello/world');
|
||||
|
||||
$context = new RequestContext();
|
||||
$context->fromRequest(Request::create('/some/path'));
|
||||
|
||||
$generator = new UrlGenerator($provider, $this->aliasManager);
|
||||
$generator->setContext($context);
|
||||
|
||||
$this->generator = $generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that generated routes will have aliased paths.
|
||||
*/
|
||||
public function testAliasGeneration() {
|
||||
$url = $this->generator->generate('test_1');
|
||||
|
||||
$this->assertEqual($url, '/hello/world', 'Correct URL generated including alias.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that generated routes will have aliased paths.
|
||||
*/
|
||||
public function testAliasGenerationWithParameters() {
|
||||
$this->aliasManager->addAlias('test/two/5', 'goodbye/cruel/world');
|
||||
|
||||
$url = $this->generator->generate('test_2', array('narf' => '5'));
|
||||
|
||||
$this->assertEqual($url, '/goodbye/cruel/world', 'Correct URL generated including alias and parameters.');
|
||||
}
|
||||
|
||||
}
|
|
@ -120,6 +120,11 @@ class LanguageUpgradePathTest extends UpgradePathTestBase {
|
|||
$this->assertTrue(isset($current_weights['language-selected']), 'Language-selected is present.');
|
||||
$this->assertFalse(isset($current_weights['language-default']), 'Language-default is not present.');
|
||||
|
||||
// @todo We only need language.inc here because LANGUAGE_NEGOTIATION_SELECTED
|
||||
// is defined there. Remove this line once that has been converted to a class
|
||||
// constant.
|
||||
require_once DRUPAL_ROOT . '/core/includes/language.inc';
|
||||
|
||||
// Check that negotiation callback was added to language_negotiation_language_interface.
|
||||
$language_negotiation_language_interface = update_variable_get('language_negotiation_language_interface', NULL);
|
||||
$this->assertTrue(isset($language_negotiation_language_interface[LANGUAGE_NEGOTIATION_SELECTED]['callbacks']['negotiation']), 'Negotiation callback was added to language_negotiation_language_interface.');
|
||||
|
|
|
@ -9,6 +9,8 @@ namespace Drupal\system\Tests\Upgrade;
|
|||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\DrupalKernel;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
@ -18,6 +20,11 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
*/
|
||||
abstract class UpgradePathTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $configDirectories;
|
||||
|
||||
/**
|
||||
* The file path(s) to the dumped database(s) to load into the child site.
|
||||
*
|
||||
|
@ -103,6 +110,12 @@ abstract class UpgradePathTestBase extends WebTestBase {
|
|||
$conf = array();
|
||||
drupal_static_reset();
|
||||
|
||||
|
||||
// Build a minimal, partially mocked environment for unit tests.
|
||||
$this->containerBuild(drupal_container());
|
||||
// Make sure it survives kernel rebuilds.
|
||||
$conf['container_bundles'][] = 'Drupal\simpletest\TestBundle';
|
||||
|
||||
// Change the database prefix.
|
||||
// All static variables need to be reset before the database prefix is
|
||||
// changed, since Drupal\Core\Utility\CacheArray implementations attempt to
|
||||
|
@ -300,6 +313,27 @@ abstract class UpgradePathTestBase extends WebTestBase {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides some core services for the upgrade tests.
|
||||
*/
|
||||
public function containerBuild(ContainerBuilder $container) {
|
||||
// Keep the container object around for tests.
|
||||
$this->container = $container;
|
||||
|
||||
$container
|
||||
->register('config.storage', 'Drupal\Core\Config\FileStorage')
|
||||
->addArgument($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
|
||||
|
||||
if ($this->container->hasDefinition('path_processor_alias')) {
|
||||
// Prevent the alias-based path processor, which requires a url_alias db
|
||||
// table, from being registered to the path processor manager. We do this
|
||||
// by removing the tags that the compiler pass looks for. This means the
|
||||
// url generator can safely be used within upgrade path tests.
|
||||
$definition = $this->container->getDefinition('path_processor_alias');
|
||||
$definition->clearTag('path_processor_inbound')->clearTag('path_processor_outbound');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets update.php without calling url().
|
||||
*
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
/**
|
||||
* @file
|
||||
* Fake an HTTPS request, for use during testing.
|
||||
*
|
||||
* @todo Fix this to use a new request rather than modifying server variables,
|
||||
* see http.php.
|
||||
*/
|
||||
|
||||
// Set a global variable to indicate a mock HTTPS request.
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\url_alter_test\PathProcessorTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\url_alter_test;
|
||||
|
||||
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
|
||||
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Path processor for url_alter_test.
|
||||
*/
|
||||
class PathProcessorTest implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
|
||||
*/
|
||||
public function processInbound($path, Request $request) {
|
||||
// Rewrite user/username to user/uid.
|
||||
if (preg_match('!^user/([^/]+)(/.*)?!', $path, $matches)) {
|
||||
if ($account = user_load_by_name($matches[1])) {
|
||||
$matches += array(2 => '');
|
||||
$path = 'user/' . $account->uid . $matches[2];
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite community/ to forum/.
|
||||
if ($path == 'community' || strpos($path, 'community/') === 0) {
|
||||
$path = 'forum' . substr($path, 9);
|
||||
}
|
||||
|
||||
if ($path == 'url-alter-test/bar') {
|
||||
$path = 'url-alter-test/foo';
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound().
|
||||
*/
|
||||
public function processOutbound($path, &$options = array(), Request $request = NULL) {
|
||||
// Rewrite user/uid to user/username.
|
||||
if (preg_match('!^user/([0-9]+)(/.*)?!', $path, $matches)) {
|
||||
if ($account = user_load($matches[1])) {
|
||||
$matches += array(2 => '');
|
||||
$path = 'user/' . $account->name . $matches[2];
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite forum/ to community/.
|
||||
if ($path == 'forum' || strpos($path, 'forum/') === 0) {
|
||||
$path = 'community' . substr($path, 5);
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
|
@ -25,21 +25,3 @@ function url_alter_test_foo() {
|
|||
print 'current_path=' . current_path() . ' request_path=' . request_path();
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_url_outbound_alter().
|
||||
*/
|
||||
function url_alter_test_url_outbound_alter(&$path, &$options, $original_path) {
|
||||
// Rewrite user/uid to user/username.
|
||||
if (preg_match('!^user/([0-9]+)(/.*)?!', $path, $matches)) {
|
||||
if ($account = user_load($matches[1])) {
|
||||
$matches += array(2 => '');
|
||||
$path = 'user/' . $account->name . $matches[2];
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite forum/ to community/.
|
||||
if ($path == 'forum' || strpos($path, 'forum/') === 0) {
|
||||
$path = 'community' . substr($path, 5);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
services:
|
||||
url_alter_test.path_subscriber:
|
||||
class: Drupal\url_alter_test\PathProcessor
|
||||
url_alter_test.path_processor:
|
||||
class: Drupal\url_alter_test\PathProcessorTest
|
||||
tags:
|
||||
- { name: path_processor_inbound, priority: 800 }
|
||||
- { name: path_processor_outbound, priority: 100 }
|
||||
|
|
|
@ -281,8 +281,7 @@ class TranslationTest extends WebTestBase {
|
|||
*/
|
||||
function resetCaches() {
|
||||
drupal_static_reset('language_list');
|
||||
drupal_static_reset('language_url_outbound_alter');
|
||||
drupal_static_reset('language_url_rewrite_url');
|
||||
$this->rebuildContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -82,6 +82,10 @@ abstract class EntityTranslationTestBase extends WebTestBase {
|
|||
$this->setupTestFields();
|
||||
|
||||
$this->controller = translation_entity_controller($this->entityType);
|
||||
|
||||
// Rebuild the container so that the new languages are picked up by services
|
||||
// that hold a list of languages.
|
||||
$this->rebuildContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,6 +68,7 @@ class EntityTranslationWorkflowsTest extends EntityTranslationTestBase {
|
|||
$this->drupalLogin($this->translator);
|
||||
$add_translation_path = $this->controller->getBasePath($this->entity) . "/translations/add/$default_langcode/{$this->langcodes[2]}";
|
||||
$this->drupalPost($add_translation_path, array(), t('Save'));
|
||||
$this->rebuildContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -285,8 +285,10 @@ function simpletest_script_init($server_software) {
|
|||
if (!empty($args['url'])) {
|
||||
$parsed_url = parse_url($args['url']);
|
||||
$host = $parsed_url['host'] . (isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '');
|
||||
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
|
||||
|
||||
$path = isset($parsed_url['path']) ? rtrim($parsed_url['path']) : '';
|
||||
if ($path == '/') {
|
||||
$path = '';
|
||||
}
|
||||
// If the passed URL schema is 'https' then setup the $_SERVER variables
|
||||
// properly so that testing will run under HTTPS.
|
||||
if ($parsed_url['scheme'] == 'https') {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Tests\Core\PathProcessor;
|
||||
|
||||
use Drupal\Component\Utility\Settings;
|
||||
use Drupal\Core\PathProcessor\PathProcessorAlias;
|
||||
use Drupal\Core\PathProcessor\PathProcessorDecode;
|
||||
use Drupal\Core\PathProcessor\PathProcessorFront;
|
||||
|
@ -22,6 +23,7 @@ use Drupal\Tests\UnitTestCase;
|
|||
class PathProcessorTest extends UnitTestCase {
|
||||
|
||||
protected $languages;
|
||||
protected $languageManager;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
|
@ -43,6 +45,13 @@ class PathProcessorTest extends UnitTestCase {
|
|||
}
|
||||
$this->languages = $languages;
|
||||
|
||||
// Create a language manager stub.
|
||||
$language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
|
||||
$language_manager->expects($this->any())
|
||||
->method('getLanguage')
|
||||
->will($this->returnValue($languages['en']));
|
||||
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,7 +95,7 @@ class PathProcessorTest extends UnitTestCase {
|
|||
$alias_processor = new PathProcessorAlias($alias_manager);
|
||||
$decode_processor = new PathProcessorDecode();
|
||||
$front_processor = new PathProcessorFront($config_factory_stub);
|
||||
$language_processor = new PathProcessorLanguage($config_factory_stub, $this->languages);
|
||||
$language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager, $this->languages);
|
||||
|
||||
// First, test the processor manager with the processors in the incorrect
|
||||
// order. The alias processor will run before the language processor, meaning
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Tests\Core\Routing\UrlGeneratorTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Routing;
|
||||
|
||||
use Drupal\Component\Utility\Settings;
|
||||
use Drupal\Core\Config\ConfigFactory;
|
||||
use Drupal\Core\Config\NullStorage;
|
||||
use Drupal\Core\Config\Context\ConfigContextFactory;
|
||||
use Drupal\Core\PathProcessor\PathProcessorAlias;
|
||||
use Drupal\Core\PathProcessor\PathProcessorManager;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
use Drupal\Core\Routing\UrlGenerator;
|
||||
|
||||
/**
|
||||
* Basic tests for the Route.
|
||||
*
|
||||
* @group Routing
|
||||
*/
|
||||
class UrlGeneratorTest extends UnitTestCase {
|
||||
|
||||
protected $generator;
|
||||
|
||||
protected $aliasManager;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'UrlGenerator',
|
||||
'description' => 'Confirm that the UrlGenerator is functioning properly.',
|
||||
'group' => 'Routing',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
|
||||
$routes = new RouteCollection();
|
||||
$first_route = new Route('/test/one');
|
||||
$second_route = new Route('/test/two/{narf}');
|
||||
$routes->add('test_1', $first_route);
|
||||
$routes->add('test_2', $second_route);
|
||||
|
||||
// Create a route provider stub.
|
||||
$provider = $this->getMockBuilder('Drupal\Core\Routing\RouteProvider')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$route_name_return_map = array(
|
||||
array('test_1', array(), $first_route),
|
||||
array('test_2', array('narf' => '5'), $second_route),
|
||||
);
|
||||
$provider->expects($this->any())
|
||||
->method('getRouteByName')
|
||||
->will($this->returnValueMap($route_name_return_map));
|
||||
$routes_names_return_map = array(
|
||||
array(array('test_1'), array(), array($first_route)),
|
||||
array(array('test_2'), array('narf' => '5'), array($second_route)),
|
||||
);
|
||||
$provider->expects($this->any())
|
||||
->method('getRoutesByNames')
|
||||
->will($this->returnValueMap($routes_names_return_map));
|
||||
|
||||
// Create an alias manager stub.
|
||||
$alias_manager = $this->getMockBuilder('Drupal\Core\Path\AliasManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$alias_map = array(
|
||||
array('test/one', NULL, 'hello/world'),
|
||||
array('test/two/5', NULL, 'goodbye/cruel/world'),
|
||||
array('node/123', NULL, 'node/123'),
|
||||
);
|
||||
$alias_manager->expects($this->any())
|
||||
->method('getPathAlias')
|
||||
->will($this->returnValueMap($alias_map));
|
||||
|
||||
$this->aliasManager = $alias_manager;
|
||||
|
||||
$context = new RequestContext();
|
||||
$context->fromRequest(Request::create('/some/path'));
|
||||
|
||||
$processor = new PathProcessorAlias($this->aliasManager);
|
||||
$processor_manager = new PathProcessorManager();
|
||||
$processor_manager->addOutbound($processor, 1000);
|
||||
|
||||
$config_factory_stub = $this->getConfigFactoryStub(array('system.filter' => array('protocols' => array('http', 'https'))));
|
||||
|
||||
$generator = new UrlGenerator($provider, $processor_manager, $config_factory_stub, new Settings(array()));
|
||||
$generator->setContext($context);
|
||||
|
||||
$this->generator = $generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that generated routes will have aliased paths.
|
||||
*/
|
||||
public function testAliasGeneration() {
|
||||
$url = $this->generator->generate('test_1');
|
||||
$this->assertEquals('/hello/world', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that generated routes will have aliased paths.
|
||||
*/
|
||||
public function testAliasGenerationWithParameters() {
|
||||
$url = $this->generator->generate('test_2', array('narf' => '5'));
|
||||
$this->assertEquals('/goodbye/cruel/world', $url, 'Correct URL generated including alias and parameters.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that absolute URLs work with generated routes.
|
||||
*/
|
||||
public function testAbsoluteURLGeneration() {
|
||||
$url = $this->generator->generate('test_1', array(), TRUE);
|
||||
$this->assertEquals('http://localhost/hello/world', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests path-based URL generation.
|
||||
*/
|
||||
public function testPathBasedURLGeneration() {
|
||||
$base_path = '/subdir';
|
||||
$base_url = 'http://www.example.com' . $base_path;
|
||||
$this->generator->setBasePath($base_path . '/');
|
||||
$this->generator->setBaseUrl($base_url . '/');
|
||||
foreach (array('', 'index.php/') as $script_path) {
|
||||
$this->generator->setScriptPath($script_path);
|
||||
foreach (array(FALSE, TRUE) as $absolute) {
|
||||
// Get the expected start of the path string.
|
||||
$base = ($absolute ? $base_url . '/' : $base_path . '/') . $script_path;
|
||||
$absolute_string = $absolute ? 'absolute' : NULL;
|
||||
$url = $base . 'node/123';
|
||||
$result = $this->generator->generateFromPath('node/123', array('absolute' => $absolute));
|
||||
$this->assertEquals($url, $result, "$url == $result");
|
||||
|
||||
$url = $base . 'node/123#foo';
|
||||
$result = $this->generator->generateFromPath('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
|
||||
$this->assertEquals($url, $result, "$url == $result");
|
||||
|
||||
$url = $base . 'node/123?foo';
|
||||
$result = $this->generator->generateFromPath('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
|
||||
$this->assertEquals($url, $result, "$url == $result");
|
||||
|
||||
$url = $base . 'node/123?foo=bar&bar=baz';
|
||||
$result = $this->generator->generateFromPath('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
|
||||
$this->assertEquals($url, $result, "$url == $result");
|
||||
|
||||
$url = $base . 'node/123?foo#bar';
|
||||
$result = $this->generator->generateFromPath('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
|
||||
$this->assertEquals($url, $result, "$url == $result");
|
||||
|
||||
$url = $base;
|
||||
$result = $this->generator->generateFromPath('<front>', array('absolute' => $absolute));
|
||||
$this->assertEquals($url, $result, "$url == $result");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue