diff -u b/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
--- b/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -279,10 +279,18 @@
/**
* Prepares variables for the module uninstall template.
*
- * @todo
* @param $variables
* An associative array containing:
- * - form: A render element representing the form.
+ * - form: A render element representing the form. Child elements of the form
+ * are individual modules. Each module is an associative array containing
+ * the following elements:
+ * - #module_name: The name of the module as a string.
+ * - name: The name of the module in a renderable array.
+ * - description: A description of the module.
+ * - #required_by: (optional) A list of modules that require the module.
+ * - #validation_reasons: (optional) Additional reasons why the module
+ * cannot be disabled.
+ * - #attributes: A list of attributes for the module wrapper.
*
* @ingroup themeable
*/
@@ -292,25 +300,25 @@
$variables['empty'] = t('No modules are available to uninstall.');
- // Table headers.
- $variables['header'] = array(
- 'uninstall' => t('Uninstall'),
- 'name' => t('Name'),
- 'description' => t('Description'),
- );
-
- // No theming for the confirm form.
- if (isset($form['confirm'])) {
- return drupal_render($form);
- }
-
// Iterate through all the modules, which are children of this element.
foreach (Element::children($form['modules']) as $key) {
$module = $form['modules'][$key];
-
+ $module['plain_name'] = $module['#module_name'];
$module['checkbox'] = $form['uninstall'][$key];
$module['checkbox_id'] = $form['uninstall'][$key]['#id'];
+ if (!empty($module['#validation_reasons'])) {
+ $module['validation_reasons'] = $module['#validation_reasons'];
+ $module['reasons_count'] = count($module['validation_reasons']);
+ } else {
+ $module['reasons_count'] = 0;
+ }
+ if (!empty($module['#required_by'])) {
+ $module['required_by'] = $module['#required_by'];
+ $module['reasons_count'] = $module['reasons_count'] + 1;
+ }
+/*
+ krumo($module);
// Add the modules requiring the module in question as a validation reason.
if (!empty($module['#required_by'])) {
$module['#validation_reasons'][] = \Drupal::translation()
@@ -321,12 +329,8 @@
'#theme' => 'item_list',
'#items' => $module['#validation_reasons'],
];
- $module['disabled_header'] = \Drupal::translation()->formatPlural(count($module['#validation_reasons']),
- 'The following reason prevents @module from being uninstalled:',
- 'The following reasons prevents @module from being uninstalled:',
- array('@module' => $module['#module_name']));
- }
+ }*/
$module['attributes'] = new Attribute($module['#attributes']);
$variables['modules'][] = $module;
}
diff -u b/core/modules/system/templates/system-modules-uninstall.html.twig b/core/modules/system/templates/system-modules-uninstall.html.twig
--- b/core/modules/system/templates/system-modules-uninstall.html.twig
+++ b/core/modules/system/templates/system-modules-uninstall.html.twig
@@ -4,23 +4,14 @@
* Default theme implementation for the modules uninstall page.
*
* Available variables:
- * - form:
- * - filters:
- * - actions:
- * - header: Table header containing the following cells:
- * - uninstall: A localized string for the title of the 'uninstall' column.
- * - name: A localized string for the title of the 'name' column.
- * - description: A localized string for the title of the 'description'
- * column.
+ * - form: The modules uninstall form.
* - modules: Contains multiple module instances. Each module contains:
* - attributes: Attributes on the row.
+ * - plain_name: The plain-text name of the module.
* - checkbox: A checkbox for uninstalling the module.
+ * - checkbox_id: A unique id for interacting with the checkbox element.
* - name: The human-readable name of the module.
- * - id: A unqiue id for interacting with the details element.
- * - uninstall_id: A unique id for interacting with the checkbox element.
* - description: The description of the module.
- * - machine_name: The module's machine name.
- * - disabled_header: (optional) A localized string for .
* - disabled_reasons: (optional) A list of reasons why this module cannot be
* uninstalled.
*
@@ -36,13 +27,13 @@
+ {% trans %}
+ The following reason prevents {{ module.plain_name }} from being uninstalled:
+ {% plural module.reasons_count %}
+ The following reasons prevent {{ module.plain_name }} from being uninstalled:
+ {% endtrans %}
+
@@ -73,5 +80,5 @@
{{ form|without(
- 'filters',
- 'modules',
- 'uninstall'
+ 'filters',
+ 'modules',
+ 'uninstall'
) }}
only in patch2:
unchanged:
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1450,6 +1450,7 @@ services:
calls:
- [setUrlGenerator, ['@url_generator']]
- [setThemeManager, ['@theme.manager']]
+ - [setDateFormatter, ['@date.formatter']]
# @todo Figure out what to do about debugging functions.
# @see https://www.drupal.org/node/1804998
twig.extension.debug:
only in patch2:
unchanged:
--- a/core/lib/Drupal/Component/Plugin/Context/Context.php
+++ b/core/lib/Drupal/Component/Plugin/Context/Context.php
@@ -31,20 +31,16 @@ class Context implements ContextInterface {
protected $contextDefinition;
/**
- * Sets the contextDefinition for us without needing to call the setter.
+ * Create a context object.
*
* @param \Drupal\Component\Plugin\Context\ContextDefinitionInterface $context_definition
* The context definition.
+ * @param mixed|null $context_value
+ * The value of the context.
*/
- public function __construct(ContextDefinitionInterface $context_definition) {
+ public function __construct(ContextDefinitionInterface $context_definition, $context_value = NULL) {
$this->contextDefinition = $context_definition;
- }
-
- /**
- * Implements \Drupal\Component\Plugin\Context\ContextInterface::setContextValue().
- */
- public function setContextValue($value) {
- $this->contextValue = $value;
+ $this->contextValue = $context_value;
}
/**
@@ -75,13 +71,6 @@ public function hasContextValue() {
}
/**
- * {@inheritdoc}
- */
- public function setContextDefinition(ContextDefinitionInterface $context_definition) {
- $this->contextDefinition = $context_definition;
- }
-
- /**
* Implements \Drupal\Component\Plugin\Context\ContextInterface::getContextDefinition().
*/
public function getContextDefinition() {
only in patch2:
unchanged:
--- a/core/lib/Drupal/Component/Plugin/Context/ContextInterface.php
+++ b/core/lib/Drupal/Component/Plugin/Context/ContextInterface.php
@@ -13,16 +13,6 @@
interface ContextInterface {
/**
- * Sets the context value.
- *
- * @param mixed $value
- * The value of this context, matching the context definition.
- *
- * @see \Drupal\Component\Plugin\Context\ContextInterface::setContextDefinition().
- */
- public function setContextValue($value);
-
- /**
* Gets the context value.
*
* @return mixed
@@ -39,15 +29,6 @@ public function getContextValue();
public function hasContextValue();
/**
- * Sets the definition that the context must conform to.
- *
- * @param \Drupal\Component\Plugin\Context\ContextDefinitionInterface $context_definition
- * A defining characteristic representation of the context against which
- * that context can be validated.
- */
- public function setContextDefinition(ContextDefinitionInterface $context_definition);
-
- /**
* Gets the provided definition that the context must conform to.
*
* @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface
only in patch2:
unchanged:
--- a/core/lib/Drupal/Component/Plugin/ContextAwarePluginBase.php
+++ b/core/lib/Drupal/Component/Plugin/ContextAwarePluginBase.php
@@ -41,17 +41,30 @@
* The plugin implementation definition.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
- $context = array();
- if (isset($configuration['context'])) {
- $context = $configuration['context'];
- unset($configuration['context']);
- }
+ $context_configuration = isset($configuration['context']) ? $configuration['context'] : [];
+ unset($configuration['context']);
+
parent::__construct($configuration, $plugin_id, $plugin_definition);
- foreach ($context as $key => $value) {
+
+ $this->contexts = $this->createContextFromConfiguration($context_configuration);
+ }
+
+ /**
+ * Creates context objects from any context mappings in configuration.
+ *
+ * @param array $context_configuration
+ * An associative array of context names and values.
+ *
+ * @return \Drupal\Component\Plugin\Context\ContextInterface[]
+ * An array of context objects.
+ */
+ protected function createContextFromConfiguration(array $context_configuration) {
+ $contexts = [];
+ foreach ($context_configuration as $key => $value) {
$context_definition = $this->getContextDefinition($key);
- $this->context[$key] = new Context($context_definition);
- $this->context[$key]->setContextValue($value);
+ $contexts[$key] = new Context($context_definition, $value);
}
+ return $contexts;
}
/**
@@ -124,7 +137,7 @@ public function getContextValue($name) {
* {@inheritdoc}
*/
public function setContextValue($name, $value) {
- $this->getContext($name)->setContextValue($value);
+ $this->context[$name] = new Context($this->getContextDefinition($name), $value);
return $this;
}
only in patch2:
unchanged:
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/Definition/PluginDefinitionInterface.php
@@ -0,0 +1,40 @@
+getClass()) {
+ throw new PluginException($missing_class_message);
+ }
- $class = $plugin_definition['class'];
+ $class = $plugin_definition->getClass();
+ }
+ else {
+ $plugin_definition_type = is_object($plugin_definition) ? get_class($plugin_definition) : gettype($plugin_definition);
+ throw new PluginException(sprintf('%s can only handle plugin definitions that are arrays or that implement %s, but %s given.', __CLASS__, PluginDefinitionInterface::class, $plugin_definition_type));
+ }
if (!class_exists($class)) {
throw new PluginException(sprintf('Plugin (%s) instance class "%s" does not exist.', $plugin_id, $class));
}
- if ($required_interface && !is_subclass_of($plugin_definition['class'], $required_interface)) {
- throw new PluginException(sprintf('Plugin "%s" (%s) must implement interface %s.', $plugin_id, $plugin_definition['class'], $required_interface));
+ if ($required_interface && !is_subclass_of($class, $required_interface)) {
+ throw new PluginException(sprintf('Plugin "%s" (%s) must implement interface %s.', $plugin_id, $class, $required_interface));
}
return $class;
only in patch2:
unchanged:
--- /dev/null
+++ b/core/lib/Drupal/Component/Utility/FormattableString.php
@@ -0,0 +1,92 @@
+string = (string) $string;
+ $this->arguments = $arguments;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __toString() {
+ return static::placeholderFormat($this->string, $this->arguments);
+ }
+
+ /**
+ * Returns the string length.
+ *
+ * @return int
+ * The length of the string.
+ */
+ public function count() {
+ return Unicode::strlen($this->string);
+ }
+
+ /**
+ * Returns a representation of the object for use in JSON serialization.
+ *
+ * @return string
+ * The safe string content.
+ */
+ public function jsonSerialize() {
+ return $this->__toString();
+ }
+
+}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Component/Utility/Html.php
+++ b/core/lib/Drupal/Component/Utility/Html.php
@@ -292,14 +292,16 @@ public static function serialize(\DOMDocument $document) {
$body_node = $document->getElementsByTagName('body')->item(0);
$html = '';
- foreach ($body_node->getElementsByTagName('script') as $node) {
- static::escapeCdataElement($node);
- }
- foreach ($body_node->getElementsByTagName('style') as $node) {
- static::escapeCdataElement($node, '/*', '*/');
- }
- foreach ($body_node->childNodes as $node) {
- $html .= $document->saveXML($node);
+ if ($body_node !== NULL) {
+ foreach ($body_node->getElementsByTagName('script') as $node) {
+ static::escapeCdataElement($node);
+ }
+ foreach ($body_node->getElementsByTagName('style') as $node) {
+ static::escapeCdataElement($node, '/*', '*/');
+ }
+ foreach ($body_node->childNodes as $node) {
+ $html .= $document->saveXML($node);
+ }
}
return $html;
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
+++ b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
@@ -15,18 +15,62 @@
/**
* Formats a string by replacing variable placeholders.
*
+ * This trait is not intended for passing arbitrary user input into any HTML
+ * attribute value, as only URL attributes such as "src" and "href" are
+ * supported (using ":variable"). Never use this method on unsafe HTML
+ * attributes such as "on*" and "style" and take care when using this with
+ * unsupported attributes such as "title" or "alt" as this can lead to
+ * unexpected and unsafe output.
+ *
* @param string $string
* A string containing placeholders.
* @param array $args
- * An associative array of replacements to make.
+ * An associative array of replacements to make. Occurrences in $string of
+ * any key in $args are replaced with the corresponding value, after
+ * optional sanitization and formatting. The type of sanitization and
+ * formatting depends on the first character of the key:
+ * - @variable: Escaped to HTML using Html::escape() unless the value is
+ * already HTML-safe. Use this as the default choice for anything
+ * displayed on a page on the site, but not within HTML attributes.
+ * - %variable: Escaped to HTML just like @variable, but also wrapped in
+ * tags, which makes the following HTML code:
+ * @code
+ * text output here.
+ * @endcode
+ * As with @variable, do not use this within HTML attributes.
+ * - :variable: Escaped to HTML using Html::escape() and filtered for
+ * dangerous protocols using UrlHelper::stripDangerousProtocols(). Use
+ * this when passing in a URL, such as when using the "src" or "href"
+ * attributes, ensuring the value is always wrapped in quotes:
+ * - Secure: @variable
+ * - Insecure: @variable
+ * When ":variable" comes from arbitrary user input, the result is secure,
+ * but not guaranteed to be a valid URL (which means the resulting output
+ * could fail HTML validation). To guarantee a valid URL, use
+ * Url::fromUri($user_input)->toString() (which either throws an exception
+ * or returns a well-formed URL) before passing the result into a
+ * ":variable" placeholder.
+ * - !variable: Inserted as is, with no sanitization or formatting. Only
+ * use this when the resulting string is being generated for one of:
+ * - Non-HTML usage, such as a plain-text email.
+ * - Non-direct HTML output, such as a plain-text variable that will be
+ * printed as an HTML attribute value and therefore formatted with
+ * self::checkPlain() as part of that.
+ * - Some other special reason for suppressing sanitization.
* @param bool &$safe
* A boolean indicating whether the string is safe or not (optional).
*
* @return string
* The string with the placeholders replaced.
*
- * @see \Drupal\Component\Utility\SafeMarkup::format()
- * @see \Drupal\Core\StringTranslation\TranslatableString::render()
+ * @ingroup sanitization
+ *
+ * @see \Drupal\Component\Utility\FormattableString
+ * @see \Drupal\Core\StringTranslation\TranslatableString
+ * @see \Drupal\Core\StringTranslation\PluralTranslatableString
+ * @see \Drupal\Component\Utility\Html::escape()
+ * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
+ * @see \Drupal\Core\Url::fromUri()
*/
protected static function placeholderFormat($string, array $args, &$safe = TRUE) {
// Transform arguments before inserting them.
only in patch2:
unchanged:
--- a/core/lib/Drupal/Component/Utility/SafeMarkup.php
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -162,79 +162,41 @@ public static function checkPlain($text) {
/**
* Formats a string for HTML display by replacing variable placeholders.
*
- * This method replaces variable placeholders in a string with the requested
- * values and escapes the values so they can be safely displayed as HTML. It
- * should be used on any unknown text that is intended to be printed to an
- * HTML page (especially text that may have come from untrusted users, since
- * in that case it prevents cross-site scripting and other security problems).
- *
- * This method is not intended for passing arbitrary user input into any
- * HTML attribute value, as only URL attributes such as "src" and "href" are
- * supported (using ":variable"). Never use this method on unsafe HTML
- * attributes such as "on*" and "style" and take care when using this with
- * unsupported attributes such as "title" or "alt" as this can lead to
- * unexpected output.
- *
- * In most cases, you should use t() rather than calling this function
- * directly, since it will translate the text (on non-English-only sites) in
- * addition to formatting it.
- *
* @param string $string
- * A string containing placeholders. The string itself is not escaped, any
- * unsafe content must be in $args and inserted via placeholders.
+ * A string containing placeholders. The string itself will not be escaped,
+ * any unsafe content must be in $args and inserted via placeholders.
* @param array $args
- * An associative array of replacements to make. Occurrences in $string of
- * any key in $args are replaced with the corresponding value, after
- * optional sanitization and formatting. The type of sanitization and
- * formatting depends on the first character of the key:
- * - @variable: Escaped to HTML using Html::escape() unless the value is
- * already HTML-safe. Use this as the default choice for anything
- * displayed on a page on the site, but not within HTML attributes.
- * - %variable: Escaped to HTML just like @variable, but also wrapped in
- * tags, which makes the following HTML code:
- * @code
- * text output here.
- * @endcode
- * As with @variable, do not use this within HTML attributes.
- * - :variable: Escaped to HTML using Html::escape() and filtered for
- * dangerous protocols using UrlHelper::stripDangerousProtocols(). Use
- * this when passing in a URL, such as when using the "src" or "href"
- * attributes, ensuring the value is always wrapped in quotes:
- * - Secure: @variable
- * - Insecure: @variable
- * When ":variable" comes from arbitrary user input, the result is secure,
- * but not guaranteed to be a valid URL (which means the resulting output
- * could fail HTML validation). To guarantee a valid URL, use
- * Url::fromUri($user_input)->toString() (which either throws an exception
- * or returns a well-formed URL) before passing the result into a
- * ":variable" placeholder.
- * - !variable: Inserted as is, with no sanitization or formatting. Only
- * use this when the resulting string is being generated for one of:
- * - Non-HTML usage, such as a plain-text email.
- * - Non-direct HTML output, such as a plain-text variable that will be
- * printed as an HTML attribute value and therefore formatted with
- * self::checkPlain() as part of that.
- * - Some other special reason for suppressing sanitization.
+ * An array with placeholder replacements, keyed by placeholder. See
+ * \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
+ * additional information about placeholders.
*
- * @return string
- * The formatted string, which is marked as safe unless sanitization of an
- * unsafe argument was suppressed (see above).
+ * @return string|\Drupal\Component\Utility\SafeStringInterface
+ * The formatted string, which is an instance of SafeStringInterface unless
+ * sanitization of an unsafe argument was suppressed (see above).
*
* @ingroup sanitization
*
- * @see t()
- * @see \Drupal\Component\Utility\Html::escape()
- * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
- * @see \Drupal\Core\Url::fromUri()
+ * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
+ * @see \Drupal\Component\Utility\FormattableString
+ *
+ * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
+ * Use \Drupal\Component\Utility\FormattableString.
*/
public static function format($string, array $args) {
+ // If the string has arguments that start with '!' we consider it unsafe
+ // and return a string instead of an object for backward compatibility
+ // purposes.
+ // @todo https://www.drupal.org/node/2571695 remove this temporary
+ // workaround.
$safe = TRUE;
- $output = static::placeholderFormat($string, $args, $safe);
- if ($safe) {
- static::$safeStrings[$output]['html'] = TRUE;
+ foreach ($args as $key => $value) {
+ if ($key[0] == '!' && !static::isSafe($value)) {
+ $safe = FALSE;
+ }
}
- return $output;
+ $safe_string = new FormattableString($string, $args);
+ return $safe ? $safe_string : (string) $safe_string;
}
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Config/Entity/ConfigDependencyDeleteFormTrait.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyDeleteFormTrait.php
@@ -53,8 +53,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C
'#type' => 'details',
'#title' => $this->t('Configuration updates'),
'#description' => $this->t('The listed configuration will be updated.'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
+ '#open' => TRUE,
'#access' => FALSE,
);
@@ -72,7 +71,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C
'#items' => array(),
);
}
- $form['entity_updates'][$entity_type_id]['#items'][] = $entity->label() ?: $entity->id();
+ $form['entity_updates'][$entity_type_id]['#items'][$entity->id()] = $entity->label() ?: $entity->id();
}
if (!empty($dependent_entities['update'])) {
$form['entity_updates']['#access'] = TRUE;
@@ -83,7 +82,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C
foreach ($entity_types as $entity_type_id => $label) {
$form['entity_updates'][$entity_type_id]['#weight'] = $weight;
// Sort the list of entity labels alphabetically.
- sort($form['entity_updates'][$entity_type_id]['#items'], SORT_FLAG_CASE);
+ ksort($form['entity_updates'][$entity_type_id]['#items'], SORT_FLAG_CASE);
$weight++;
}
}
@@ -92,8 +91,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C
'#type' => 'details',
'#title' => $this->t('Configuration deletions'),
'#description' => $this->t('The listed configuration will be deleted.'),
- '#collapsible' => TRUE,
- '#collapsed' => TRUE,
+ '#open' => TRUE,
'#access' => FALSE,
);
@@ -110,7 +108,7 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C
'#items' => array(),
);
}
- $form['entity_deletes'][$entity_type_id]['#items'][] = $entity->label() ?: $entity->id();
+ $form['entity_deletes'][$entity_type_id]['#items'][$entity->id()] = $entity->label() ?: $entity->id();
}
if (!empty($dependent_entities['delete'])) {
$form['entity_deletes']['#access'] = TRUE;
@@ -119,10 +117,12 @@ protected function addDependencyListsToForm(array &$form, $type, array $names, C
asort($entity_types, SORT_FLAG_CASE);
$weight = 0;
foreach ($entity_types as $entity_type_id => $label) {
- $form['entity_deletes'][$entity_type_id]['#weight'] = $weight;
- // Sort the list of entity labels alphabetically.
- sort($form['entity_deletes'][$entity_type_id]['#items'], SORT_FLAG_CASE);
- $weight++;
+ if (isset($form['entity_deletes'][$entity_type_id])) {
+ $form['entity_deletes'][$entity_type_id]['#weight'] = $weight;
+ // Sort the list of entity labels alphabetically.
+ ksort($form['entity_deletes'][$entity_type_id]['#items'], SORT_FLAG_CASE);
+ $weight++;
+ }
}
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
@@ -249,7 +249,7 @@ public function buildMultiple(array $entities) {
$items = $grouped_items[$id];
/** @var \Drupal\Core\Access\AccessResultInterface $field_access */
$field_access = $items->access('view', NULL, TRUE);
- $build_list[$id][$name] = $field_access->isAllowed() ? $formatter->view($items) : [];
+ $build_list[$id][$name] = $field_access->isAllowed() ? $formatter->view($items, $entity->language()->getId()) : [];
// Apply the field access cacheability metadata to the render array.
$this->renderer->addCacheableDependency($build_list[$id][$name], $field_access);
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Entity/EntityDeleteForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityDeleteForm.php
@@ -34,7 +34,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
if (!($entity instanceof ConfigEntityInterface)) {
return $form;
}
- $this->addDependencyListsToForm($form, $entity->getConfigDependencyKey(), [$entity->getConfigDependencyName()], $this->getConfigManager(), $this->entityManager);
+ $this->addDependencyListsToForm($form, $entity->getConfigDependencyKey(), $this->getConfigNamesToDelete($entity), $this->getConfigManager(), $this->entityManager);
return $form;
}
@@ -49,4 +49,17 @@ protected function getConfigManager() {
return \Drupal::service('config.manager');
}
+ /**
+ * Returns config names to delete for the deletion confirmation form.
+ *
+ * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
+ * The entity being deleted.
+ *
+ * @return string[]
+ * A list of configuration names that will be deleted by this form.
+ */
+ protected function getConfigNamesToDelete(ConfigEntityInterface $entity) {
+ return [$entity->getConfigDependencyName()];
+ }
+
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -7,6 +7,8 @@
namespace Drupal\Core\Entity;
+use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;
+
/**
* Provides an interface for an entity type and its metadata.
*
@@ -15,7 +17,7 @@
* implemented to alter existing data and fill-in defaults. Module-specific
* properties should be documented in the hook implementations defining them.
*/
-interface EntityTypeInterface {
+interface EntityTypeInterface extends PluginDefinitionInterface {
/**
* The maximum length of ID, in characters.
@@ -67,14 +69,6 @@ public function id();
public function getProvider();
/**
- * Gets the name of the entity type class.
- *
- * @return string
- * The name of the entity type class.
- */
- public function getClass();
-
- /**
* Gets the name of the original entity type class.
*
* In case the class name was changed with setClass(), this will return
@@ -171,16 +165,6 @@ public function isRenderCacheable();
public function isPersistentlyCacheable();
/**
- * Sets the name of the entity type class.
- *
- * @param string $class
- * The name of the entity type class.
- *
- * @return $this
- */
- public function setClass($class);
-
- /**
* Determines if there is a handler for a given type.
*
* @param string $handler_type
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/FormatterBase.php
+++ b/core/lib/Drupal/Core/Field/FormatterBase.php
@@ -8,6 +8,7 @@
namespace Drupal\Core\Field;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Render\Element;
/**
@@ -76,8 +77,12 @@ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInter
/**
* {@inheritdoc}
*/
- public function view(FieldItemListInterface $items) {
- $elements = $this->viewElements($items);
+ public function view(FieldItemListInterface $items, $langcode = NULL) {
+ // Default the language to the current content language.
+ if (empty($langcode)) {
+ $langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
+ }
+ $elements = $this->viewElements($items, $langcode);
// If there are actual renderable children, use #theme => field, otherwise,
// let access cacheability metadata pass through for correct bubbling.
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/FormatterInterface.php
+++ b/core/lib/Drupal/Core/Field/FormatterInterface.php
@@ -69,23 +69,28 @@ public function prepareView(array $entities_items);
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values to be rendered.
+ * @param string $langcode
+ * (optional) The language that should be used to render the field. Defaults
+ * to the current content language.
*
* @return array
* A renderable array for a themed field with its label and all its values.
*/
- public function view(FieldItemListInterface $items);
+ public function view(FieldItemListInterface $items, $langcode = NULL);
/**
* Builds a renderable array for a field value.
*
* @param \Drupal\Core\Field\FieldItemListInterface $items
* The field values to be rendered.
+ * @param string $langcode
+ * The language that should be used to render the field.
*
* @return array
* A renderable array for $items, as an array of child elements keyed by
* consecutive numeric indexes starting from 0.
*/
- public function viewElements(FieldItemListInterface $items);
+ public function viewElements(FieldItemListInterface $items, $langcode);
/**
* Returns if the formatter can be used for the provided field.
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/BasicStringFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/BasicStringFormatter.php
@@ -7,7 +7,6 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
-use Drupal\Component\Utility\Html;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
@@ -31,13 +30,17 @@ class BasicStringFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
- $elements = array();
+ public function viewElements(FieldItemListInterface $items, $langcode) {
+ $elements = [];
foreach ($items as $delta => $item) {
// The text value has no text format assigned to it, so the user input
// should equal the output, including newlines.
- $elements[$delta] = array('#markup' => nl2br(Html::escape($item->value)));
+ $elements[$delta] = [
+ '#type' => 'inline_template',
+ '#template' => '{{ value|nl2br }}',
+ '#context' => ['value' => $item->value],
+ ];
}
return $elements;
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/BooleanFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/BooleanFormatter.php
@@ -116,7 +116,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$formats = $this->getOutputFormats();
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php
@@ -10,6 +10,7 @@
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -119,11 +120,11 @@ public function settingsSummary() {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$view_mode = $this->getSetting('view_mode');
$elements = array();
- foreach ($this->getEntitiesToView($items) as $delta => $entity) {
+ foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
// Protect ourselves from recursive rendering.
static $depth = 0;
$depth++;
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFormatterBase.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFormatterBase.php
@@ -36,24 +36,25 @@
*
* @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items
* The item list.
+ * @param string $langcode
+ * The language code of the referenced entities to display.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* The array of referenced entities to display, keyed by delta.
*
* @see ::prepareView()
*/
- protected function getEntitiesToView(EntityReferenceFieldItemListInterface $items) {
+ protected function getEntitiesToView(EntityReferenceFieldItemListInterface $items, $langcode) {
$entities = array();
- $parent_entity_langcode = $items->getEntity()->language()->getId();
foreach ($items as $delta => $item) {
// Ignore items where no entity could be loaded in prepareView().
if (!empty($item->_loaded)) {
$entity = $item->entity;
// Set the entity in the correct language for display.
- if ($entity instanceof TranslatableInterface && $entity->hasTranslation($parent_entity_langcode)) {
- $entity = $entity->getTranslation($parent_entity_langcode);
+ if ($entity instanceof TranslatableInterface) {
+ $entity = \Drupal::entityManager()->getTranslationFromContext($entity, $langcode);
}
$access = $this->checkAccess($entity);
@@ -76,8 +77,8 @@ protected function getEntitiesToView(EntityReferenceFieldItemListInterface $item
* @see ::prepareView()
* @see ::getEntitiestoView()
*/
- public function view(FieldItemListInterface $items) {
- $elements = parent::view($items);
+ public function view(FieldItemListInterface $items, $langcode = NULL) {
+ $elements = parent::view($items, $langcode);
$field_level_access_cacheability = new CacheableMetadata();
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceIdFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceIdFormatter.php
@@ -8,6 +8,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Language\LanguageInterface;
/**
* Plugin implementation of the 'entity reference ID' formatter.
@@ -26,10 +27,10 @@ class EntityReferenceIdFormatter extends EntityReferenceFormatterBase {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
- foreach ($this->getEntitiesToView($items) as $delta => $entity) {
+ foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
if ($entity->id()) {
$elements[$delta] = array(
'#plain_text' => $entity->id(),
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
@@ -59,11 +59,11 @@ public function settingsSummary() {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$output_as_link = $this->getSetting('link');
- foreach ($this->getEntitiesToView($items) as $delta => $entity) {
+ foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
$label = $entity->label();
// If the link is to be displayed and the entity has a uri, display a
// link.
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php
@@ -27,7 +27,7 @@ class MailToFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericFormatterBase.php
@@ -66,7 +66,7 @@ public function settingsSummary() {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$settings = $this->getFieldSettings();
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericUnformattedFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/NumericUnformattedFormatter.php
@@ -28,7 +28,7 @@ class NumericUnformattedFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/StringFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/StringFormatter.php
@@ -7,9 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
-use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\FormatterBase;
@@ -119,7 +117,7 @@ public function settingsSummary() {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$url = NULL;
if ($this->getSetting('link_to_entity')) {
@@ -137,7 +135,7 @@ public function viewElements(FieldItemListInterface $items) {
];
}
else {
- $elements[$delta] = is_array($view_value) ? $view_value : ['#markup' => $view_value];
+ $elements[$delta] = $view_value;
}
}
return $elements;
@@ -156,7 +154,9 @@ protected function viewValue(FieldItemInterface $item) {
// The text value has no text format assigned to it, so the user input
// should equal the output, including newlines.
return [
- '#markup' => nl2br(Html::escape($item->value))
+ '#type' => 'inline_template',
+ '#template' => '{{ value|nl2br }}',
+ '#context' => ['value' => $item->value],
];
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
@@ -153,7 +153,7 @@ public function settingsSummary() {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php
@@ -165,7 +165,7 @@ public function settingsSummary() {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$date_format = $this->getSetting('date_format');
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
@@ -27,7 +27,7 @@ class UriLinkFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -679,6 +679,15 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
$form['#method'] = 'get';
}
+ // GET forms should not use a CSRF token.
+ if (isset($form['#method']) && $form['#method'] === 'get') {
+ // Merges in a default, this means if you've explicitly set #token to the
+ // the $form_id on a GET form, which we don't recommend, it will work.
+ $form += [
+ '#token' => FALSE,
+ ];
+ }
+
// Generate a new #build_id for this form, if none has been set already.
// The form_build_id is used as key to cache a particular build of the form.
// For multi-step forms, this allows the user to go back to an earlier
@@ -1307,7 +1316,11 @@ protected function buttonWasClicked($element, FormStateInterface &$form_state) {
// long as $form['#name'] puts the value at the top level of the tree of
// \Drupal::request()->request data.
$input = $form_state->getUserInput();
- if (isset($input[$element['#name']]) && $input[$element['#name']] == $element['#value']) {
+ // The input value attribute is treated as CDATA by browsers. This means
+ // that they replace character entities with characters. Therefore, we need
+ // to decode the value in $element['#value']. For more details see
+ // http://www.w3.org/TR/html401/types.html#type-cdata.
+ if (isset($input[$element['#name']]) && $input[$element['#name']] == Html::decodeEntities($element['#value'])) {
return TRUE;
}
// When image buttons are clicked, browsers do NOT pass the form element
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Language/ContextProvider/CurrentLanguageContext.php
+++ b/core/lib/Drupal/Core/Language/ContextProvider/CurrentLanguageContext.php
@@ -57,8 +57,7 @@ public function getRuntimeContexts(array $unqualified_context_ids) {
$result = [];
foreach ($language_types as $type_key) {
if (isset($info[$type_key]['name'])) {
- $context = new Context(new ContextDefinition('language', $info[$type_key]['name']));
- $context->setContextValue($this->languageManager->getCurrentLanguage($type_key));
+ $context = new Context(new ContextDefinition('language', $info[$type_key]['name']), $this->languageManager->getCurrentLanguage($type_key));
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['languages:' . $type_key]);
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Menu/menu.api.php
+++ b/core/lib/Drupal/Core/Menu/menu.api.php
@@ -428,7 +428,7 @@ function hook_contextual_links_plugins_alter(array &$contextual_links) {
*/
function hook_system_breadcrumb_alter(\Drupal\Core\Breadcrumb\Breadcrumb &$breadcrumb, \Drupal\Core\Routing\RouteMatchInterface $route_match, array $context) {
// Add an item to the end of the breadcrumb.
- $breadcrumb->addLink(Drupal::l(t('Text'), 'example_route_name'));
+ $breadcrumb->addLink(\Drupal\Core\Link::createFromRoute(t('Text'), 'example_route_name'));
}
/**
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Plugin/Context/Context.php
+++ b/core/lib/Drupal/Core/Plugin/Context/Context.php
@@ -43,11 +43,19 @@ class Context extends ComponentContext implements ContextInterface {
protected $cacheabilityMetadata;
/**
- * {@inheritdoc}
+ * Create a context object.
+ *
+ * @param \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition
+ * The context definition.
+ * @param mixed $context_value|NULL
+ * The context value object.
*/
- public function __construct(ContextDefinitionInterface $context_definition) {
- parent::__construct($context_definition);
+ public function __construct(ContextDefinitionInterface $context_definition, $context_value = NULL) {
+ parent::__construct($context_definition, NULL);
$this->cacheabilityMetadata = new CacheableMetadata();
+ if (!is_null($context_value)) {
+ $this->setContextValue($context_value);
+ }
}
/**
@@ -80,19 +88,22 @@ public function hasContextValue() {
}
/**
- * {@inheritdoc}
+ * Sets the context value.
+ *
+ * @param mixed $value
+ * The value of this context, matching the context definition.
*/
- public function setContextValue($value) {
+ protected function setContextValue($value) {
// Add the value as a cacheable dependency only if implements the interface
// to prevent it from disabling caching with a max-age 0.
if ($value instanceof CacheableDependencyInterface) {
$this->addCacheableDependency($value);
}
if ($value instanceof TypedDataInterface) {
- return $this->setContextData($value);
+ $this->contextData = $value;
}
else {
- return $this->setContextData($this->getTypedDataManager()->create($this->contextDefinition->getDataDefinition(), $value));
+ $this->contextData = $this->getTypedDataManager()->create($this->contextDefinition->getDataDefinition(), $value);
}
}
@@ -119,13 +130,6 @@ public function getContextData() {
return $this->contextData;
}
- /**
- * {@inheritdoc}
- */
- public function setContextData(TypedDataInterface $data) {
- $this->contextData = $data;
- return $this;
- }
/**
* {@inheritdoc}
@@ -170,4 +174,16 @@ public function getCacheMaxAge() {
return $this->cacheabilityMetadata->getCacheMaxAge();
}
+ /**
+ * {@inheritdoc}
+ */
+ public static function createFromContext(ContextInterface $old_context, $value) {
+ $context = new static($old_context->getContextDefinition(), $value);
+ $context->addCacheableDependency($old_context);
+ if (method_exists($old_context, 'getTypedDataManager')) {
+ $context->setTypedDataManager($old_context->getTypedDataManager());
+ }
+ return $context;
+ }
+
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php
@@ -72,6 +72,7 @@ public function getMatchingContexts(array $contexts, ContextDefinitionInterface
* {@inheritdoc}
*/
public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = array()) {
+ /** @var $contexts \Drupal\Core\Plugin\Context\ContextInterface[] */
$mappings += $plugin->getContextMapping();
// Loop through each of the expected contexts.
@@ -94,7 +95,7 @@ public function applyContextMapping(ContextAwarePluginInterface $plugin, $contex
// Pass the value to the plugin if there is one.
if ($contexts[$context_id]->hasContextValue()) {
- $plugin->setContextValue($plugin_context_id, $contexts[$context_id]->getContextValue());
+ $plugin->setContextValue($plugin_context_id, $contexts[$context_id]->getContextData());
}
elseif ($plugin_context_definition->isRequired()) {
// Collect required contexts that exist but are missing a value.
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Plugin/Context/ContextHandlerInterface.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandlerInterface.php
@@ -68,7 +68,7 @@ public function getMatchingContexts(array $contexts, ContextDefinitionInterface
*
* @param \Drupal\Core\Plugin\ContextAwarePluginInterface $plugin
* A plugin about to be evaluated.
- * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
+ * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
* An array of contexts to set on the plugin. They will only be set if they
* match the plugin's context definitions.
* @param array $mappings
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Plugin/Context/ContextInterface.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextInterface.php
@@ -9,7 +9,6 @@
use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterface;
use Drupal\Core\Cache\CacheableDependencyInterface;
-use Drupal\Core\TypedData\TypedDataInterface;
/**
* Interface for context.
@@ -17,21 +16,18 @@
interface ContextInterface extends ComponentContextInterface, CacheableDependencyInterface {
/**
- * Gets the context value as typed data object.
+ * {@inheritdoc}
*
- * @return \Drupal\Core\TypedData\TypedDataInterface
+ * @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface
*/
- public function getContextData();
+ public function getContextDefinition();
/**
- * Sets the context value as typed data object.
- *
- * @param \Drupal\Core\TypedData\TypedDataInterface $data
- * The context value as a typed data object.
+ * Gets the context value as typed data object.
*
- * @return $this
+ * @return \Drupal\Core\TypedData\TypedDataInterface
*/
- public function setContextData(TypedDataInterface $data);
+ public function getContextData();
/**
* Adds a dependency on an object: merges its cacheability metadata.
@@ -51,4 +47,17 @@ public function setContextData(TypedDataInterface $data);
*/
public function addCacheableDependency($dependency);
+ /**
+ * Creates a new context with a different value.
+ *
+ * @param \Drupal\Core\Plugin\Context\ContextInterface $old_context
+ * The context object used to create a new object. Cacheability metadata
+ * will be copied over.
+ * @param mixed $value
+ * The value of the new context object.
+ *
+ * @return static
+ */
+ public static function createFromContext(ContextInterface $old_context, $value);
+
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Plugin/Context/ContextProviderInterface.php
+++ b/core/lib/Drupal/Core/Plugin/Context/ContextProviderInterface.php
@@ -32,8 +32,7 @@
* $node = ...
*
* // Set that specific node as the value of the 'node' context.
- * $context = new Context(new ContextDefinition('entity:node'));
- * $context->setContextValue($node);
+ * $context = new Context(new ContextDefinition('entity:node'), $node);
* return ['node' => $context];
* @endcode
*
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
+++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php
@@ -30,6 +30,25 @@
/**
* {@inheritdoc}
*
+ * @return \Drupal\Core\Plugin\Context\ContextInterface[]
+ */
+ protected function createContextFromConfiguration(array $context_configuration) {
+ // This method is overridden so that it will use
+ // \Drupal\Core\Plugin\Context\Context instead.
+ $contexts = [];
+ foreach ($context_configuration as $key => $value) {
+ $context_definition = $this->getContextDefinition($key);
+ $contexts[$key] = new Context($context_definition, $value);
+ }
+ return $contexts;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return \Drupal\Core\Plugin\Context\ContextInterface
+ * The context object.
+ *
* This code is identical to the Component in order to pick up a different
* Context class.
*/
@@ -55,6 +74,14 @@ public function setContext($name, ComponentContextInterface $context) {
/**
* {@inheritdoc}
*/
+ public function setContextValue($name, $value) {
+ $this->context[$name] = Context::createFromContext($this->getContext($name), $value);
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function getContextMapping() {
$configuration = $this instanceof ConfigurablePluginInterface ? $this->getConfiguration() : $this->configuration;
return isset($configuration['context_mapping']) ? $configuration['context_mapping'] : [];
@@ -85,6 +112,15 @@ public function getContextDefinitions() {
}
/**
+ * {@inheritdoc}
+ *
+ * @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface
+ */
+ public function getContextDefinition($name) {
+ return parent::getContextDefinition($name);
+ }
+
+ /**
* Wraps the context handler.
*
* @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php
+++ b/core/lib/Drupal/Core/StringTranslation/PluralTranslatableString.php
@@ -63,18 +63,18 @@ class PluralTranslatableString extends TranslatableString {
* ease translation. Use @count in place of the item count, as in
* "@count new comments".
* @param array $args
- * (optional) An associative array of replacements to make after
- * translation. Instances of any key in this array are replaced with the
- * corresponding value. Based on the first character of the key, the value
- * is escaped and/or themed. See
- * \Drupal\Component\Utility\SafeMarkup::format(). Note that you do not need
- * to include @count in this array; this replacement is done automatically
+ * (optional) An array with placeholder replacements, keyed by placeholder.
+ * See \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
+ * additional information about placeholders. Note that you do not need to
+ * include @count in this array; this replacement is done automatically
* for the plural cases.
* @param array $options
* (optional) An associative array of additional options. See t() for
* allowed keys.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* (optional) The string translation service.
+ *
+ * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
*/
public function __construct($count, $singular, $plural, array $args = [], array $options = [], TranslationInterface $string_translation = NULL) {
$this->count = $count;
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/StringTranslation/TranslatableString.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslatableString.php
@@ -73,10 +73,14 @@ class TranslatableString implements SafeStringInterface {
* The string that is to be translated.
* @param array $arguments
* (optional) An array with placeholder replacements, keyed by placeholder.
+ * See \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat() for
+ * additional information about placeholders.
* @param array $options
* (optional) An array of additional options.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* (optional) The string translation service.
+ *
+ * @see \Drupal\Component\Utility\PlaceholderTrait::placeholderFormat()
*/
public function __construct($string, array $arguments = array(), array $options = array(), TranslationInterface $string_translation = NULL) {
$this->string = $string;
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Template/Attribute.php
+++ b/core/lib/Drupal/Core/Template/Attribute.php
@@ -7,6 +7,8 @@
namespace Drupal\Core\Template;
+use Drupal\Component\Utility\PlainTextOutput;
+use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\SafeStringInterface;
/**
@@ -52,7 +54,18 @@
* // Produces
* @endcode
*
+ * The attribute values are considered plain text and are treated as such. If a
+ * safe HTML string is detected, it is converted to plain text with
+ * PlainTextOutput::renderFromHtml() before being escaped. For example:
+ * @code
+ * $value = t('Highlight the @tag tag', ['@tag' => '']);
+ * $attributes = new Attribute(['value' => $value]);
+ * echo '';
+ * // Produces
+ * @endcode
+ *
* @see \Drupal\Component\Utility\Html::escape()
+ * @see \Drupal\Component\Utility\PlainTextOutput::renderFromHtml()
* @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
*/
class Attribute implements \ArrayAccess, \IteratorAggregate, SafeStringInterface {
@@ -111,17 +124,27 @@ protected function createAttributeValue($name, $value) {
}
// An array value or 'class' attribute name are forced to always be an
// AttributeArray value for consistency.
- if (is_array($value) || $name == 'class') {
+ if ($name == 'class' && !is_array($value)) {
+ // Cast the value to string in case it implements SafeStringInterface.
+ $value = [(string) $value];
+ }
+ if (is_array($value)) {
// Cast the value to an array if the value was passed in as a string.
// @todo Decide to fix all the broken instances of class as a string
// in core or cast them.
- $value = new AttributeArray($name, (array) $value);
+ $value = new AttributeArray($name, $value);
}
elseif (is_bool($value)) {
$value = new AttributeBoolean($name, $value);
}
// As a development aid, we allow the value to be a safe string object.
- elseif (!is_object($value) || $value instanceof SafeStringInterface) {
+ elseif (SafeMarkup::isSafe($value)) {
+ // Attributes are not supposed to display HTML markup, so we just convert
+ // the value to plain text.
+ $value = PlainTextOutput::renderFromHtml($value);
+ $value = new AttributeString($name, $value);
+ }
+ elseif (!is_object($value)) {
$value = new AttributeString($name, $value);
}
return $value;
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Template/TwigExtension.php
+++ b/core/lib/Drupal/Core/Template/TwigExtension.php
@@ -15,6 +15,7 @@
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\SafeStringInterface;
+use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Render\RenderableInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\UrlGeneratorInterface;
@@ -52,6 +53,13 @@ class TwigExtension extends \Twig_Extension {
protected $themeManager;
/**
+ * The date formatter.
+ *
+ * @var \Drupal\Core\Datetime\DateFormatter
+ */
+ protected $dateFormatter;
+
+ /**
* Constructs \Drupal\Core\Template\TwigExtension.
*
* @param \Drupal\Core\Render\RendererInterface $renderer
@@ -103,6 +111,19 @@ public function setThemeManager(ThemeManagerInterface $theme_manager) {
}
/**
+ * Sets the date formatter.
+ *
+ * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
+ * The date formatter.
+ *
+ * @return $this
+ */
+ public function setDateFormatter(DateFormatter $date_formatter) {
+ $this->dateFormatter = $date_formatter;
+ return $this;
+ }
+
+ /**
* {@inheritdoc}
*/
public function getFunctions() {
@@ -152,6 +173,7 @@ public function getFilters() {
new \Twig_SimpleFilter('clean_id', '\Drupal\Component\Utility\Html::getId'),
// This filter will render a renderable array to use the string results.
new \Twig_SimpleFilter('render', array($this, 'renderVar')),
+ new \Twig_SimpleFilter('format_date', array($this->dateFormatter, 'format')),
);
}
only in patch2:
unchanged:
--- a/core/lib/Drupal/Core/Updater/Updater.php
+++ b/core/lib/Drupal/Core/Updater/Updater.php
@@ -235,9 +235,6 @@ public function update(&$filetransfer, $overrides = array()) {
// Make sure the installation parent directory exists and is writable.
$this->prepareInstallDirectory($filetransfer, $args['install_dir']);
- // Note: If the project is installed in the top-level, it will not be
- // deleted. It will be installed in sites/default as that will override
- // the top-level reference and not break other sites which are using it.
if (is_dir($args['install_dir'] . '/' . $this->name)) {
// Remove the existing installed file.
$filetransfer->removeDirectory($args['install_dir'] . '/' . $this->name);
only in patch2:
unchanged:
--- a/core/misc/states.js
+++ b/core/misc/states.js
@@ -607,7 +607,8 @@
$(document).on('state:required', function (e) {
if (e.trigger) {
if (e.value) {
- var $label = $(e.target).attr({'required': 'required', 'aria-required': 'aria-required'}).closest('.js-form-item, .js-form-wrapper').find('label');
+ var label = 'label' + (e.target.id ? '[for=' + e.target.id + ']' : '');
+ var $label = $(e.target).attr({'required': 'required', 'aria-required': 'aria-required'}).closest('.js-form-item, .js-form-wrapper').find(label);
// Avoids duplicate required markers on initialization.
if (!$label.hasClass('js-form-required').length) {
$label.addClass('js-form-required form-required');
only in patch2:
unchanged:
--- a/core/modules/aggregator/src/Plugin/Field/FieldFormatter/AggregatorTitleFormatter.php
+++ b/core/modules/aggregator/src/Plugin/Field/FieldFormatter/AggregatorTitleFormatter.php
@@ -54,7 +54,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
if ($items->getEntity()->getEntityTypeId() == 'aggregator_feed') {
only in patch2:
unchanged:
--- a/core/modules/aggregator/src/Plugin/Field/FieldFormatter/AggregatorXSSFormatter.php
+++ b/core/modules/aggregator/src/Plugin/Field/FieldFormatter/AggregatorXSSFormatter.php
@@ -30,7 +30,7 @@ class AggregatorXSSFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
foreach ($items as $delta => $item) {
only in patch2:
unchanged:
--- a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
+++ b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
@@ -135,7 +135,7 @@ public function challengeException(Request $request, \Exception $previous) {
$challenge = SafeMarkup::format('Basic realm="@realm"', array(
'@realm' => !empty($site_name) ? $site_name : 'Access restricted',
));
- return new UnauthorizedHttpException($challenge, 'No authentication credentials provided.', $previous);
+ return new UnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
}
}
only in patch2:
unchanged:
--- a/core/modules/block/src/Plugin/migrate/process/BlockRegion.php
+++ b/core/modules/block/src/Plugin/migrate/process/BlockRegion.php
@@ -7,30 +7,68 @@
namespace Drupal\block\Plugin\migrate\process;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @MigrateProcessPlugin(
* id = "block_region"
* )
*/
-class BlockRegion extends ProcessPluginBase {
+class BlockRegion extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
- * {@inheritdoc}
+ * List of regions, keyed by theme.
+ *
+ * @var array[]
+ */
+ protected $regions;
+
+ /**
+ * Constructs a BlockRegion plugin instance.
*
- * Set the destination block region, based on the source region and theme as
- * well as the current destination default theme.
+ * @param array $configuration
+ * The plugin configuration.
+ * @param string $plugin_id
+ * The plugin ID.
+ * @param mixed $plugin_definition
+ * The plugin definition.
+ * @param array $regions
+ * Array of region maps, keyed by theme.
+ */
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, array $regions) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+ $this->regions = $regions;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ $regions = array();
+ foreach ($container->get('theme_handler')->listInfo() as $key => $theme) {
+ $regions[$key] = $theme->info['regions'];
+ }
+ return new static($configuration, $plugin_id, $plugin_definition, $regions);
+ }
+
+ /**
+ * {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+ // Set the destination region, based on the source region and theme as well
+ // as the current destination default theme.
list($region, $source_theme, $destination_theme) = $value;
- // Theme is the same on both source and destination, we will assume they
- // have the same regions.
+ // Theme is the same on both source and destination, so ensure that the
+ // region exists in the destination theme.
if (strtolower($source_theme) == strtolower($destination_theme)) {
- return $region;
+ if (isset($this->regions[$destination_theme][$region])) {
+ return $region;
+ }
}
// If the source and destination theme are different, try to use the
only in patch2:
unchanged:
--- a/core/modules/block/src/Tests/Migrate/d6/MigrateBlockTest.php
+++ b/core/modules/block/src/Tests/Migrate/d6/MigrateBlockTest.php
@@ -154,7 +154,7 @@ public function testBlockMigration() {
$visibility['request_path']['id'] = 'request_path';
$visibility['request_path']['negate'] = FALSE;
$visibility['request_path']['pages'] = '/node';
- $this->assertEntity('block_1', $visibility, 'right', 'bluemarine', -4);
+ $this->assertEntity('block_1', $visibility, 'sidebar_second', 'bluemarine', -4);
$visibility = [];
$this->assertEntity('block_2', $visibility, 'right', 'test_theme', -7);
only in patch2:
unchanged:
--- a/core/modules/block/src/Tests/Migrate/d7/MigrateBlockTest.php
+++ b/core/modules/block/src/Tests/Migrate/d7/MigrateBlockTest.php
@@ -23,7 +23,7 @@ class MigrateBlockTest extends MigrateDrupal7TestBase {
*
* @var array
*/
- static $modules = array(
+ static $modules = [
'block',
'views',
'comment',
@@ -33,7 +33,7 @@ class MigrateBlockTest extends MigrateDrupal7TestBase {
'text',
'filter',
'user',
- );
+ ];
/**
* {@inheritdoc}
@@ -50,7 +50,7 @@ protected function setUp() {
$config->save();
// Install one of D8's test themes.
- \Drupal::service('theme_handler')->install(array('test_theme'));
+ \Drupal::service('theme_handler')->install(['bartik']);
$this->executeMigration('d7_filter_format');
$this->executeMigration('d7_user_role');
@@ -117,7 +117,7 @@ public function testBlockMigration() {
// Assert that disabled blocks (or enabled blocks whose plugin IDs could
// be resolved) did not migrate.
- $non_existent_blocks = array(
+ $non_existent_blocks = [
'bartik_system_navigation',
'bartik_system_help',
'seven_user_new',
@@ -155,7 +155,7 @@ public function testBlockMigration() {
'seven_menu_menu-test-menu',
'seven_statistics_popular',
'seven_block_1',
- );
+ ];
$this->assertTrue(empty(Block::loadMultiple($non_existent_blocks)));
}
only in patch2:
unchanged:
--- a/core/modules/block/tests/modules/block_test/src/ContextProvider/MultipleStaticContext.php
+++ b/core/modules/block/tests/modules/block_test/src/ContextProvider/MultipleStaticContext.php
@@ -52,11 +52,9 @@ public function __construct(AccountInterface $account, EntityManagerInterface $e
public function getRuntimeContexts(array $unqualified_context_ids) {
$current_user = $this->userStorage->load($this->account->id());
- $context1 = new Context(new ContextDefinition('entity:user', 'User 1'));
- $context1->setContextValue($current_user);
+ $context1 = new Context(new ContextDefinition('entity:user', 'User 1'), $current_user);
- $context2 = new Context(new ContextDefinition('entity:user', 'User 2'));
- $context2->setContextValue($current_user);
+ $context2 = new Context(new ContextDefinition('entity:user', 'User 2'), $current_user);
$cacheability = new CacheableMetadata();
$cacheability->setCacheContexts(['user']);
only in patch2:
unchanged:
--- /dev/null
+++ b/core/modules/block/tests/src/Unit/Plugin/migrate/process/BlockRegionTest.php
@@ -0,0 +1,69 @@
+prophesize(MigrateExecutableInterface::class)->reveal();
+ if (empty($row)) {
+ $row = $this->prophesize(Row::class)->reveal();
+ }
+
+ $regions = array(
+ 'bartik' => array(
+ 'triptych_first' => 'Triptych first',
+ 'triptych_second' => 'Triptych second',
+ 'triptych_third' => 'Triptych third',
+ ),
+ );
+ $plugin = new BlockRegion(['region_map' => []], 'block_region', [], $regions);
+ return $plugin->transform($value, $executable, $row, 'foo');
+ }
+
+ /**
+ * If the source and destination themes are identical, the region should only
+ * be passed through if it actually exists in the destination theme.
+ *
+ * @covers ::transform
+ */
+ public function testTransformSameThemeRegionExists() {
+ $this->assertSame('triptych_second', $this->transform(['triptych_second', 'bartik', 'bartik']));
+ }
+
+ /**
+ * If the source and destination themes are identical, the region should be
+ * changed to 'content' if it doesn't exist in the destination theme.
+ *
+ * @covers ::transform
+ */
+ public function testTransformSameThemeRegionNotExists() {
+ $this->assertSame('content', $this->transform(['footer', 'bartik', 'bartik']));
+ }
+
+}
only in patch2:
unchanged:
--- a/core/modules/comment/src/Plugin/Field/FieldFormatter/AuthorNameFormatter.php
+++ b/core/modules/comment/src/Plugin/Field/FieldFormatter/AuthorNameFormatter.php
@@ -28,7 +28,7 @@ class AuthorNameFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
foreach ($items as $delta => $item) {
only in patch2:
unchanged:
--- a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
+++ b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php
@@ -137,7 +137,7 @@ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInter
/**
* {@inheritdoc}
*/
- public function viewElements(FieldItemListInterface $items) {
+ public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = array();
$output = array();
only in patch2:
unchanged:
--- a/core/modules/config/src/Form/ConfigSingleExportForm.php
+++ b/core/modules/config/src/Form/ConfigSingleExportForm.php
@@ -105,7 +105,6 @@ public function buildForm(array $form, FormStateInterface $form_state, $config_t
'#type' => 'select',
'#options' => $this->findConfiguration($default_type),
'#default_value' => $config_name,
- '#required' => TRUE,
'#prefix' => '