core/lib/Drupal/Core/Cache/Cache.php | 20 ++++++++++++++++++++ .../FieldFormatter/EntityReferenceLabelFormatter.php | 12 +++++++++++- .../Field/FieldFormatter/LanguageFormatter.php | 3 +++ .../Field/FieldFormatter/CommentDefaultFormatter.php | 2 ++ .../Plugin/Field/FieldFormatter/LinkFormatter.php | 6 ++++++ .../Plugin/Field/FieldFormatter/PlainFormatter.php | 3 +++ .../Field/FieldFormatter/TaxonomyFormatterBase.php | 18 ++++++++++++++++-- .../Field/FieldFormatter/TextDefaultFormatter.php | 2 ++ .../Field/FieldFormatter/TextTrimmedFormatter.php | 2 ++ 9 files changed, 65 insertions(+), 3 deletions(-) diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php index 4e0124f..91cc4a8 100644 --- a/core/lib/Drupal/Core/Cache/Cache.php +++ b/core/lib/Drupal/Core/Cache/Cache.php @@ -22,6 +22,26 @@ class Cache { const PERMANENT = CacheBackendInterface::CACHE_PERMANENT; /** + * Merges arrays of cache contexts and removes duplicates. + * + * @param string[] … + * Arrays of cache contexts to merge. + * + * @return string[] + * The merged array of cache contexts. + */ + public static function mergeContexts() { + $cache_context_arrays = func_get_args(); + $cache_contexts = []; + foreach ($cache_context_arrays as $contexts) { + $cache_contexts = array_merge($cache_contexts, $contexts); + } + $cache_contexts = array_unique($cache_contexts); + sort($cache_contexts); + return $cache_contexts; + } + + /** * Merges arrays of cache tags and removes duplicates. * * The cache tags array is returned in a format that is valid for diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php index dbc8958..0651bbc 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php @@ -64,7 +64,16 @@ public function viewElements(FieldItemListInterface $items) { $elements = array(); $output_as_link = $this->getSetting('link'); - foreach ($this->getEntitiesToView($items) as $delta => $entity) { + $entities = $this->getEntitiesToView($items); + + // Cache contexts: if one entity is translatable, then they all are. + $cache_contexts = []; + $first_entity = reset($entities); + if (count($first_entity->getTranslationLanguages()) > 1) { + $cache_contexts[] = 'language'; + } + + foreach ($entities as $delta => $entity) { $label = $entity->label(); // If the link is to be displayed and the entity has a uri, display a // link. @@ -100,6 +109,7 @@ public function viewElements(FieldItemListInterface $items) { else { $elements[$delta] = array('#markup' => String::checkPlain($label)); } + $elements[$delta]['#cache']['contexts'] = $cache_contexts; $elements[$delta]['#cache']['tags'] = $entity->getCacheTags(); } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/LanguageFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/LanguageFormatter.php index d936b89..ba730a6 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/LanguageFormatter.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/LanguageFormatter.php @@ -30,6 +30,9 @@ class LanguageFormatter extends FormatterBase { public function viewElements(FieldItemListInterface $items) { $elements = array(); + // The 'language' cache context is not necessary, because + // \Drupal\Core\Language\LanguageInterface::getName() always returns the + // human-readable English name. foreach ($items as $delta => $item) { $elements[$delta] = array('#markup' => $item->language ? String::checkPlain($item->language->getName()) : ''); } diff --git a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php index 02c3107..251dce0 100644 --- a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php +++ b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php @@ -157,6 +157,7 @@ public function viewElements(FieldItemListInterface $items) { // Unpublished comments are not included in // $entity->get($field_name)->comment_count, but unpublished comments // should display if the user is an administrator. + $output['comments']['#cache']['contexts'][] = 'user.roles'; if ($this->currentUser->hasPermission('access comments') || $this->currentUser->hasPermission('administer comments')) { // This is a listing of Comment entities, so associate its list cache // tag for correct invalidation. @@ -182,6 +183,7 @@ public function viewElements(FieldItemListInterface $items) { // display below the entity. Do not show the form for the print view mode. if ($status == CommentItemInterface::OPEN && $comment_settings['form_location'] == CommentItemInterface::FORM_BELOW && $this->viewMode != 'print') { // Only show the add comment form if the user has permission. + $output['comments']['#cache']['contexts'][] = 'user.roles'; if ($this->currentUser->hasPermission('post comments')) { // All users in the "anonymous" role can use the same form: it is fine // for this form to be stored in the render cache. diff --git a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php index d94e2bf..847e1fd 100644 --- a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php +++ b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php @@ -34,6 +34,9 @@ public function viewElements(FieldItemListInterface $items) { foreach ($items as $delta => $item) { if ($item->hasNewEntity()) { $elements[$delta] = array( + '#cache' => [ + 'contexts' => $item->_cacheContexts, + ], '#markup' => String::checkPlain($item->entity->label()), ); } @@ -41,6 +44,9 @@ public function viewElements(FieldItemListInterface $items) { /** @var $term \Drupal\taxonomy\TermInterface */ $term = $item->entity; $elements[$delta] = array( + '#cache' => [ + 'contexts' => $item->_cacheContexts, + ], '#type' => 'link', '#title' => $term->getName(), '#url' => $term->urlInfo(), diff --git a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/PlainFormatter.php b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/PlainFormatter.php index 1bf4ab4..0161791 100644 --- a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/PlainFormatter.php +++ b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/PlainFormatter.php @@ -31,6 +31,9 @@ public function viewElements(FieldItemListInterface $items) { foreach ($items as $delta => $item) { $elements[$delta] = array( + '#cache' => [ + 'contexts' => $item->_cacheContexts, + ], '#markup' => String::checkPlain($item->entity->label()), ); } diff --git a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/TaxonomyFormatterBase.php b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/TaxonomyFormatterBase.php index 6109674..43273b1 100644 --- a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/TaxonomyFormatterBase.php +++ b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/TaxonomyFormatterBase.php @@ -7,6 +7,8 @@ namespace Drupal\taxonomy\Plugin\Field\FieldFormatter; +use Drupal\Core\Cache\Cache; +use Drupal\Core\Cache\CacheableInterface; use Drupal\Core\Field\FormatterBase; /** @@ -21,7 +23,8 @@ * unsets values for invalid terms that do not exist. */ public function prepareView(array $entities_items) { - $terms = array(); + $terms = []; + $cache_contexts_per_term = []; // Collect every possible term attached to any of the fieldable entities. /* @var \Drupal\Core\Field\EntityReferenceFieldItemList $items */ @@ -31,12 +34,21 @@ public function prepareView(array $entities_items) { $active_langcode = $parent->language()->getId(); /* @var \Drupal\taxonomy\Entity\Term $term */ foreach ($items->referencedEntities() as $term) { + $cache_contexts = []; + if (count($term->getTranslationLanguages()) > 1) { + $cache_contexts[] = 'language'; + } if ($term->hasTranslation($active_langcode)) { $translated_term = $term->getTranslation($active_langcode); - if ($translated_term->access('view')) { + $access_result = $translated_term->access('view', NULL, TRUE); + if ($access_result instanceof CacheableInterface) { + $cache_contexts = Cache::mergeContexts($cache_contexts, $access_result->getCacheContexts()); + } + if ($access_result->isAllowed()) { $term = $translated_term; } } + $cache_contexts_per_term[$term->id()] = $cache_contexts; if (!$term->isNew()) { $terms[$term->id()] = $term; } @@ -53,10 +65,12 @@ public function prepareView(array $entities_items) { if (isset($terms[$item->target_id])) { // Replace the instance value with the term data. $item->entity = $terms[$item->target_id]; + $item->_cacheContexts = $cache_contexts_per_term[$item->target_id]; } // Terms to be created are not in $terms, but are still legitimate. elseif ($item->hasNewEntity()) { // Leave the item in place. + $item->_cacheContexts = $cache_contexts_per_term[$item->target_id]; } // Otherwise, unset the instance value, since the term does not exist. else { diff --git a/core/modules/text/src/Plugin/Field/FieldFormatter/TextDefaultFormatter.php b/core/modules/text/src/Plugin/Field/FieldFormatter/TextDefaultFormatter.php index efc4c0d..623c808 100644 --- a/core/modules/text/src/Plugin/Field/FieldFormatter/TextDefaultFormatter.php +++ b/core/modules/text/src/Plugin/Field/FieldFormatter/TextDefaultFormatter.php @@ -34,6 +34,8 @@ class TextDefaultFormatter extends FormatterBase { public function viewElements(FieldItemListInterface $items) { $elements = array(); + // The ProcessedText element already handles cache context & tag bubbling. + // @see \Drupal\filter\Element\ProcessedText::preRenderText() foreach ($items as $delta => $item) { $elements[$delta] = array( '#type' => 'processed_text', diff --git a/core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php b/core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php index ed1c8c3..105bfd5 100644 --- a/core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php +++ b/core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php @@ -81,6 +81,8 @@ public function viewElements(FieldItemListInterface $items) { $element['#text_summary_trim_length'] = $this->getSetting('trim_length'); }; + // The ProcessedText element already handles cache context & tag bubbling. + // @see \Drupal\filter\Element\ProcessedText::preRenderText() foreach ($items as $delta => $item) { $elements[$delta] = array( '#type' => 'processed_text',