Update to Drupal 8.0.0 beta 14. For more information, see https://drupal.org/node/2544542

This commit is contained in:
Pantheon Automation 2015-08-27 12:03:05 -07:00 committed by Greg Anderson
parent 3b2511d96d
commit 81ccda77eb
2155 changed files with 54307 additions and 46870 deletions

View file

@ -33,7 +33,7 @@ class Error {
/**
* Decodes an exception and retrieves the correct caller.
*
* @param \Exception|\BaseException $exception
* @param \Exception|\Throwable $exception
* The exception object that was thrown.
*
* @return array
@ -82,7 +82,7 @@ class Error {
/**
* Renders an exception error message without further exceptions.
*
* @param \Exception|\BaseException $exception
* @param \Exception|\Throwable $exception
* The exception object that was thrown.
*
* @return string

View file

@ -9,10 +9,12 @@ namespace Drupal\Core\Utility;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\SafeStringInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\GeneratedLink;
use Drupal\Core\Link;
use Drupal\Core\Path\AliasManagerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
@ -36,6 +38,13 @@ class LinkGenerator implements LinkGeneratorInterface {
*/
protected $moduleHandler;
/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* Constructs a LinkGenerator instance.
*
@ -43,17 +52,20 @@ class LinkGenerator implements LinkGeneratorInterface {
* The url generator.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
*/
public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler) {
public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler, RendererInterface $renderer) {
$this->urlGenerator = $url_generator;
$this->moduleHandler = $module_handler;
$this->renderer = $renderer;
}
/**
* {@inheritdoc}
*/
public function generateFromLink(Link $link, $collect_cacheability_metadata = FALSE) {
return $this->generate($link->getText(), $link->getUrl(), $collect_cacheability_metadata);
public function generateFromLink(Link $link, $collect_bubbleable_metadata = FALSE) {
return $this->generate($link->getText(), $link->getUrl(), $collect_bubbleable_metadata);
}
/**
@ -68,15 +80,14 @@ class LinkGenerator implements LinkGeneratorInterface {
*
* @see system_page_attachments()
*/
public function generate($text, Url $url, $collect_cacheability_metadata = FALSE) {
public function generate($text, Url $url, $collect_bubbleable_metadata = FALSE) {
// Performance: avoid Url::toString() needing to retrieve the URL generator
// service from the container.
$url->setUrlGenerator($this->urlGenerator);
// Start building a structured representation of our link to be altered later.
$variables = array(
// @todo Inject the service when drupal_render() is converted to one.
'text' => is_array($text) ? drupal_render($text) : $text,
'text' => is_array($text) ? $this->renderer->render($text) : $text,
'url' => $url,
'options' => $url->getOptions(),
);
@ -96,6 +107,13 @@ class LinkGenerator implements LinkGeneratorInterface {
$variables['options']['attributes']['hreflang'] = $variables['options']['language']->getId();
}
// Ensure that query values are strings.
array_walk($variables['options']['query'], function(&$value) {
if ($value instanceof SafeStringInterface) {
$value = (string) $value;
}
});
// Set the "active" class if the 'set_active_class' option is not empty.
if (!empty($variables['options']['set_active_class']) && !$url->isExternal()) {
// Add a "data-drupal-link-query" attribute to let the
@ -131,11 +149,11 @@ class LinkGenerator implements LinkGeneratorInterface {
unset($variables['options']['attributes']);
$url->setOptions($variables['options']);
if (!$collect_cacheability_metadata) {
$url_string = $url->toString($collect_cacheability_metadata);
if (!$collect_bubbleable_metadata) {
$url_string = $url->toString($collect_bubbleable_metadata);
}
else {
$generated_url = $url->toString($collect_cacheability_metadata);
$generated_url = $url->toString($collect_bubbleable_metadata);
$url_string = $generated_url->getGeneratedUrl();
$generated_link = GeneratedLink::createFromObject($generated_url);
}
@ -145,7 +163,7 @@ class LinkGenerator implements LinkGeneratorInterface {
$result = SafeMarkup::format('<a@attributes>@text</a>', array('@attributes' => new Attribute($attributes), '@text' => $variables['text']));
return $collect_cacheability_metadata ? $generated_link->setGeneratedLink($result) : $result;
return $collect_bubbleable_metadata ? $generated_link->setGeneratedLink($result) : $result;
}
}

View file

@ -60,14 +60,14 @@ interface LinkGeneratorInterface {
* class will be applied to the link. It is important to use this
* sparingly since it is usually unnecessary and requires extra
* processing.
* @param bool $collect_cacheability_metadata
* @param bool $collect_bubbleable_metadata
* (optional) Defaults to FALSE. When TRUE, both the generated link and its
* associated cacheability metadata are returned.
* associated bubbleable metadata are returned.
*
* @return string|\Drupal\Core\GeneratedLink
* An HTML string containing a link to the given route and parameters.
* When $collect_cacheability_metadata is TRUE, a GeneratedLink object is
* returned, containing the generated link plus cacheability metadata.
* When $collect_bubbleable_metadata is TRUE, a GeneratedLink object is
* returned, containing the generated link plus bubbleable metadata.
*
* @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
* Thrown when the named route doesn't exist.
@ -77,22 +77,22 @@ interface LinkGeneratorInterface {
* Thrown when a parameter value for a placeholder is not correct because it
* does not match the requirement.
*/
public function generate($text, Url $url, $collect_cacheability_metadata = FALSE);
public function generate($text, Url $url, $collect_bubbleable_metadata = FALSE);
/**
* Renders a link from a link object.
*
* @param \Drupal\Core\Link $link
* A link object to convert to a string.
* @param bool $collect_cacheability_metadata
* @param bool $collect_bubbleable_metadata
* (optional) Defaults to FALSE. When TRUE, both the generated link and its
* associated cacheability metadata are returned.
* associated bubbleable metadata are returned.
*
* @return string|\Drupal\Core\GeneratedLink
* An HTML string containing a link to the given route and parameters.
* When $collect_cacheability_metadata is TRUE, a GeneratedLink object is
* returned, containing the generated link plus cacheability metadata.
* When $collect_bubbleable_metadata is TRUE, a GeneratedLink object is
* returned, containing the generated link plus bubbleable metadata.
*/
public function generateFromLink(Link $link, $collect_cacheability_metadata = FALSE);
public function generateFromLink(Link $link, $collect_bubbleable_metadata = FALSE);
}

View file

@ -8,11 +8,15 @@
namespace Drupal\Core\Utility;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\AttachmentsInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RendererInterface;
/**
* Drupal placeholder/token replacement system.
@ -104,6 +108,13 @@ class Token {
*/
protected $cacheTagsInvalidator;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* Constructs a new class instance.
*
@ -115,12 +126,15 @@ class Token {
* The language manager.
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_invalidator
* The cache tags invalidator.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
*/
public function __construct(ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, CacheTagsInvalidatorInterface $cache_tags_invalidator) {
public function __construct(ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, CacheTagsInvalidatorInterface $cache_tags_invalidator, RendererInterface $renderer) {
$this->cache = $cache;
$this->languageManager = $language_manager;
$this->moduleHandler = $module_handler;
$this->cacheTagsInvalidator = $cache_tags_invalidator;
$this->renderer = $renderer;
}
/**
@ -152,19 +166,39 @@ class Token {
* \Drupal\Component\Utility\Xss::filter(),
* \Drupal\Component\Utility\SafeMarkup::checkPlain() or other appropriate
* scrubbing functions before displaying data to users.
* @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata|null
* (optional) An object to which static::generate() and the hooks and
* functions that it invokes will add their required bubbleable metadata.
*
* To ensure that the metadata associated with the token replacements gets
* attached to the same render array that contains the token-replaced text,
* callers of this method are encouraged to pass in a BubbleableMetadata
* object and apply it to the corresponding render array. For example:
* @code
* $bubbleable_metadata = new BubbleableMetadata();
* $build['#markup'] = $token_service->replace('Tokens: [node:nid] [current-user:uid]', ['node' => $node], [], $bubbleable_metadata);
* $bubbleable_metadata->applyTo($build);
* @endcode
*
* When the caller does not pass in a BubbleableMetadata object, this
* method creates a local one, and applies the collected metadata to the
* Renderer's currently active render context.
*
* @return string
* Text with tokens replaced.
*/
public function replace($text, array $data = array(), array $options = array()) {
public function replace($text, array $data = array(), array $options = array(), BubbleableMetadata $bubbleable_metadata = NULL) {
$text_tokens = $this->scan($text);
if (empty($text_tokens)) {
return $text;
}
$bubbleable_metadata_is_passed_in = (bool) $bubbleable_metadata;
$bubbleable_metadata = $bubbleable_metadata ?: new BubbleableMetadata();
$replacements = array();
foreach ($text_tokens as $type => $tokens) {
$replacements += $this->generate($type, $tokens, $data, $options);
$replacements += $this->generate($type, $tokens, $data, $options, $bubbleable_metadata);
if (!empty($options['clear'])) {
$replacements += array_fill_keys($tokens, '');
}
@ -173,12 +207,20 @@ class Token {
// Optionally alter the list of replacement values.
if (!empty($options['callback'])) {
$function = $options['callback'];
$function($replacements, $data, $options);
$function($replacements, $data, $options, $bubbleable_metadata);
}
$tokens = array_keys($replacements);
$values = array_values($replacements);
// If a local $bubbleable_metadata object was created, apply the metadata
// it collected to the renderer's currently active render context.
if (!$bubbleable_metadata_is_passed_in && $this->renderer->hasRenderContext()) {
$build = [];
$bubbleable_metadata->applyTo($build);
$this->renderer->render($build);
}
return str_replace($tokens, $values, $text);
}
@ -226,14 +268,14 @@ class Token {
* An array of tokens to be replaced, keyed by the literal text of the token
* as it appeared in the source text.
* @param array $data
* (optional) An array of keyed objects. For simple replacement scenarios
* 'node', 'user', and others are common keys, with an accompanying node or
* user object being the value. Some token types, like 'site', do not require
* An array of keyed objects. For simple replacement scenarios: 'node',
* 'user', and others are common keys, with an accompanying node or user
* object being the value. Some token types, like 'site', do not require
* any explicit information from $data and can be replaced even if it is
* empty.
* @param array $options
* (optional) A keyed array of settings and flags to control the token
* replacement process. Supported options are:
* A keyed array of settings and flags to control the token replacement
* process. Supported options are:
* - langcode: A language code to be used when generating locale-sensitive
* tokens.
* - callback: A callback function that will be used to post-process the
@ -245,6 +287,9 @@ class Token {
* responsibility for running \Drupal\Component\Utility\Xss::filter(),
* \Drupal\Component\Utility\SafeMarkup::checkPlain() or other appropriate
* scrubbing functions before displaying data to users.
* @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata
* The bubbleable metadata. This is passed to the token replacement
* implementations so that they can attach their metadata.
*
* @return array
* An associative array of replacement values, keyed by the original 'raw'
@ -254,9 +299,16 @@ class Token {
* @see hook_tokens()
* @see hook_tokens_alter()
*/
public function generate($type, array $tokens, array $data = array(), array $options = array()) {
public function generate($type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
$options += array('sanitize' => TRUE);
$replacements = $this->moduleHandler->invokeAll('tokens', array($type, $tokens, $data, $options));
foreach ($data as $object) {
if ($object instanceof CacheableDependencyInterface || $object instanceof AttachmentsInterface) {
$bubbleable_metadata->addCacheableDependency($object);
}
}
$replacements = $this->moduleHandler->invokeAll('tokens', [$type, $tokens, $data, $options, $bubbleable_metadata]);
// Allow other modules to alter the replacements.
$context = array(
@ -265,7 +317,7 @@ class Token {
'data' => $data,
'options' => $options,
);
$this->moduleHandler->alter('tokens', $replacements, $context);
$this->moduleHandler->alter('tokens', $replacements, $context, $bubbleable_metadata);
return $replacements;
}

View file

@ -7,7 +7,6 @@
namespace Drupal\Core\Utility;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\GeneratedUrl;
@ -40,14 +39,13 @@ class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
*
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* A request stack object.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
* The config factory.
* @param \Drupal\Core\PathProcessor\OutboundPathProcessorInterface $path_processor
* The output path processor.
* @param string[] $filter_protocols
* (optional) An array of protocols allowed for URL generation.
*/
public function __construct(RequestStack $request_stack, ConfigFactoryInterface $config, OutboundPathProcessorInterface $path_processor) {
$allowed_protocols = $config->get('system.filter')->get('protocols') ?: ['http', 'https'];
UrlHelper::setAllowedProtocols($allowed_protocols);
public function __construct(RequestStack $request_stack, OutboundPathProcessorInterface $path_processor, array $filter_protocols = ['http', 'https']) {
UrlHelper::setAllowedProtocols($filter_protocols);
$this->requestStack = $request_stack;
$this->pathProcessor = $path_processor;
}
@ -58,24 +56,24 @@ class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
* This is a helper function that calls buildExternalUrl() or buildLocalUrl()
* based on a check of whether the path is a valid external URL.
*/
public function assemble($uri, array $options = [], $collect_cacheability_metadata = FALSE) {
public function assemble($uri, array $options = [], $collect_bubbleable_metadata = FALSE) {
// Note that UrlHelper::isExternal will return FALSE if the $uri has a
// disallowed protocol. This is later made safe since we always add at
// least a leading slash.
if (parse_url($uri, PHP_URL_SCHEME) === 'base') {
return $this->buildLocalUrl($uri, $options, $collect_cacheability_metadata);
return $this->buildLocalUrl($uri, $options, $collect_bubbleable_metadata);
}
elseif (UrlHelper::isExternal($uri)) {
// UrlHelper::isExternal() only returns true for safe protocols.
return $this->buildExternalUrl($uri, $options, $collect_cacheability_metadata);
return $this->buildExternalUrl($uri, $options, $collect_bubbleable_metadata);
}
throw new \InvalidArgumentException(SafeMarkup::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base: for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.', ['@uri' => $uri]));
throw new \InvalidArgumentException("The URI '$uri' is invalid. You must use a valid URI scheme. Use base: for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.");
}
/**
* {@inheritdoc}
*/
protected function buildExternalUrl($uri, array $options = [], $collect_cacheability_metadata = FALSE) {
protected function buildExternalUrl($uri, array $options = [], $collect_bubbleable_metadata = FALSE) {
$this->addOptionDefaults($options);
// Split off the fragment.
if (strpos($uri, '#') !== FALSE) {
@ -100,14 +98,14 @@ class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
}
// Reassemble.
$url = $uri . $options['fragment'];
return $collect_cacheability_metadata ? (new GeneratedUrl())->setGeneratedUrl($url) : $url;
return $collect_bubbleable_metadata ? (new GeneratedUrl())->setGeneratedUrl($url) : $url;
}
/**
* {@inheritdoc}
*/
protected function buildLocalUrl($uri, array $options = [], $collect_cacheability_metadata = FALSE) {
$generated_url = $collect_cacheability_metadata ? new GeneratedUrl() : NULL;
protected function buildLocalUrl($uri, array $options = [], $collect_bubbleable_metadata = FALSE) {
$generated_url = $collect_bubbleable_metadata ? new GeneratedUrl() : NULL;
$this->addOptionDefaults($options);
$request = $this->requestStack->getCurrentRequest();
@ -149,7 +147,7 @@ class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
else {
$base = $current_base_url;
}
if ($collect_cacheability_metadata) {
if ($collect_bubbleable_metadata) {
$generated_url->addCacheContexts(['url.site']);
}
}
@ -162,7 +160,7 @@ class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
$uri = str_replace('%2F', '/', rawurlencode($prefix . $uri));
$query = $options['query'] ? ('?' . UrlHelper::buildQuery($options['query'])) : '';
$url = $base . $options['script'] . $uri . $query . $options['fragment'];
return $collect_cacheability_metadata ? $generated_url->setGeneratedUrl($url) : $url;
return $collect_bubbleable_metadata ? $generated_url->setGeneratedUrl($url) : $url;
}
/**

View file

@ -44,18 +44,18 @@ interface UnroutedUrlAssemblerInterface {
* - '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.
* @param bool $collect_cacheability_metadata
* @param bool $collect_bubbleable_metadata
* (optional) Defaults to FALSE. When TRUE, both the generated URL and its
* associated cacheability metadata are returned.
* associated bubbleable metadata are returned.
*
* @return string|\Drupal\Core\GeneratedUrl
* A string containing a relative or absolute URL.
* When $collect_cacheability_metadata is TRUE, a GeneratedUrl object is
* returned, containing the generated URL plus cacheability metadata.
* When $collect_bubbleable_metadata is TRUE, a GeneratedUrl object is
* returned, containing the generated URL plus bubbleable metadata.
*
* @throws \InvalidArgumentException
* Thrown when the passed in path has no scheme.
*/
public function assemble($uri, array $options = array(), $collect_cacheability_metadata = FALSE);
public function assemble($uri, array $options = array(), $collect_bubbleable_metadata = FALSE);
}

View file

@ -34,22 +34,43 @@ use Drupal\user\Entity\User;
* An array of tokens to be replaced. The keys are the machine-readable token
* names, and the values are the raw [type:token] strings that appeared in the
* original text.
* @param $data
* (optional) An associative array of data objects to be used when generating
* replacement values, as supplied in the $data parameter to
* \Drupal\Core\Utility\Token::replace().
* @param $options
* (optional) An associative array of options for token replacement; see
* @param array $data
* An associative array of data objects to be used when generating replacement
* values, as supplied in the $data parameter to
* \Drupal\Core\Utility\Token::replace().
* @param array $options
* An associative array of options for token replacement; see
* \Drupal\Core\Utility\Token::replace() for possible values.
* @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata
* The bubbleable metadata. Prior to invoking this hook,
* \Drupal\Core\Utility\Token::generate() collects metadata for all of the
* data objects in $data. For any data sources not in $data, but that are
* used by the token replacement logic, such as global configuration (e.g.,
* 'system.site') and related objects (e.g., $node->getOwner()),
* implementations of this hook must add the corresponding metadata.
* For example:
* @code
* $bubbleable_metadata->addCacheableDependency(\Drupal::config('system.site'));
* $bubbleable_metadata->addCacheableDependency($node->getOwner());
* @endcode
*
* @return
* Additionally, implementations of this hook, must forward
* $bubbleable_metadata to the chained tokens that they invoke.
* For example:
* @code
* if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) {
* $replacements = $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options, $bubbleable_metadata);
* }
* @endcode
*
* @return array
* An associative array of replacement values, keyed by the raw [type:token]
* strings from the original text.
*
* @see hook_token_info()
* @see hook_tokens_alter()
*/
function hook_tokens($type, $tokens, array $data = array(), array $options = array()) {
function hook_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
$token_service = \Drupal::token();
$url_options = array('absolute' => TRUE);
@ -87,6 +108,7 @@ function hook_tokens($type, $tokens, array $data = array(), array $options = arr
case 'author':
$account = $node->getOwner() ? $node->getOwner() : User::load(0);
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($account->label()) : $account->label();
$bubbleable_metadata->addCacheableDependency($account);
break;
case 'created':
@ -96,11 +118,11 @@ function hook_tokens($type, $tokens, array $data = array(), array $options = arr
}
if ($author_tokens = $token_service->findWithPrefix($tokens, 'author')) {
$replacements = $token_service->generate('user', $author_tokens, array('user' => $node->getOwner()), $options);
$replacements = $token_service->generate('user', $author_tokens, array('user' => $node->getOwner()), $options, $bubbleable_metadata);
}
if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) {
$replacements = $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options);
$replacements = $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options, $bubbleable_metadata);
}
}
@ -120,10 +142,14 @@ function hook_tokens($type, $tokens, array $data = array(), array $options = arr
* - 'tokens'
* - 'data'
* - 'options'
* @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata
* The bubbleable metadata. In case you alter an existing token based upon
* a data source that isn't in $context['data'], you must add that
* dependency to $bubbleable_metadata.
*
* @see hook_tokens()
*/
function hook_tokens_alter(array &$replacements, array $context) {
function hook_tokens_alter(array &$replacements, array $context, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
$options = $context['options'];
if (isset($options['langcode'])) {