diff --git a/core/lib/Drupal/Core/Cache/CacheDependencyBubbleTrait.php b/core/lib/Drupal/Core/Cache/ConditionalCacheabilityMetadataBubblingTrait.php similarity index 61% rename from core/lib/Drupal/Core/Cache/CacheDependencyBubbleTrait.php rename to core/lib/Drupal/Core/Cache/ConditionalCacheabilityMetadataBubblingTrait.php index 6e735aa..bc7ee20 100644 --- a/core/lib/Drupal/Core/Cache/CacheDependencyBubbleTrait.php +++ b/core/lib/Drupal/Core/Cache/ConditionalCacheabilityMetadataBubblingTrait.php @@ -4,11 +4,19 @@ /** * Provides bubble function to apply cache dependency to render context. + * + * This trait should be used with great care. It should only be used by classes + * that may be used both inside and outside of a render context. + * For example: + * - Generating URLs for CLI vs for HTTP responses. + * - Serializing/normalizing data for scripts vs for HTTP responses. */ -trait CacheDependencyBubbleTrait { +trait ConditionalCacheabilityMetadataBubblingTrait { /** - * Bubbles the bubbleable metadata to the current render context. + * Bubbles the bubbleable cacheability metadata to the current render context. + * + * This method does not bubble attachments. * * @param \Drupal\Core\Cache\CacheableDependencyInterface $object * The cacheable dependency object. diff --git a/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php b/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php index e2746e0..ad23a04 100644 --- a/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php +++ b/core/modules/serialization/tests/src/Kernel/EntitySerializationTest.php @@ -61,11 +61,11 @@ protected function setUp() { // User create needs sequence table. $this->installSchema('system', array('sequences')); - // Create a text format because it is needed for text normalization. + // Create a text format because it is needed for TextItemBase normalization. // @see \Drupal\text\Normalizer\TextItemBaseNormalizer::normalize(). FilterFormat::create([ - 'format' => 'full_html', - 'name' => 'Full HTML', + 'format' => 'my_text_format', + 'name' => 'My Text Format', 'filters' => [ 'filter_html' => [ 'module' => 'filter', @@ -99,7 +99,7 @@ protected function setUp() { 'user_id' => $this->user->id(), 'field_test_text' => array( 'value' => $test_text_value, - 'format' => 'full_html', + 'format' => 'my_text_format', ), ); $this->entity = EntityTestMulRev::create($this->values); diff --git a/core/modules/text/src/Normalizer/TextItemBaseNormalizer.php b/core/modules/text/src/Normalizer/TextItemBaseNormalizer.php index 101d95a..613b5ec 100644 --- a/core/modules/text/src/Normalizer/TextItemBaseNormalizer.php +++ b/core/modules/text/src/Normalizer/TextItemBaseNormalizer.php @@ -3,10 +3,9 @@ namespace Drupal\text\Normalizer; use Drupal\Core\Cache\CacheableMetadata; -use Drupal\Core\Cache\CacheDependencyBubbleTrait; +use Drupal\Core\Cache\ConditionalCacheabilityMetadataBubblingTrait; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Render\RendererInterface; -use Drupal\filter\Entity\FilterFormat; use Drupal\serialization\Normalizer\ComplexDataNormalizer; use Drupal\text\Plugin\Field\FieldType\TextItemBase; @@ -19,7 +18,7 @@ */ class TextItemBaseNormalizer extends ComplexDataNormalizer { - use CacheDependencyBubbleTrait; + use ConditionalCacheabilityMetadataBubblingTrait; /** * The renderer. @@ -60,6 +59,26 @@ public function normalize($field_item, $format = NULL, array $context = []) { $attributes = parent::normalize($field_item, $format, $context); /** @var \Drupal\text\Plugin\Field\FieldType\TextItemBase $field_item */ $value = $field_item->getValue(); + + $attributes['processed'] = $this->normalizeProcessed($value, $format, $context); + + return $attributes; + } + + /** + * Normalizes the processed attribute. + * + * @param array $value + * The field item value. + * @param string $format + * The format the normalization result will be encoded as. + * @param array $context + * The context options for the normalizer. + * + * @return string + * The normalized 'processed' attribute. + */ + protected function normalizeProcessed($value, $format, $context) { if (empty($value['format'])) { // The fallback filter format. $filter_format_id = $this->config->get('filter.settings')->get('fallback_format'); @@ -67,20 +86,20 @@ public function normalize($field_item, $format = NULL, array $context = []) { else { $filter_format_id = $value['format']; } - $build =[ + $build = [ '#type' => 'processed_text', '#text' => $value['value'], '#format' => $filter_format_id, '#filter_types_to_skip' => [], '#langcode' => '', ]; - $attributes['processed'] = $this->serializer->normalize($this->renderer->renderPlain($build), $format, $context); + // The rendering process needs to be applied to caching. New cacheability + // metadata can be introduced in the process. + // @see \Drupal\editor\Plugin\Filter\EditorFileReference::process(). + $processed = $this->serializer->normalize($this->renderer->renderPlain($build), $format, $context); $this->bubble(CacheableMetadata::createFromRenderArray($build)); - if ($text_format = FilterFormat::load($filter_format_id)) { - $this->bubble($text_format); - } - return $attributes; + return $processed; } } diff --git a/core/modules/text/tests/src/Kernel/Normalizer/TextItemBaseNormalizerTest.php b/core/modules/text/tests/src/Kernel/Normalizer/TextItemBaseNormalizerTest.php index 16f4ff9..8d6504a 100644 --- a/core/modules/text/tests/src/Kernel/Normalizer/TextItemBaseNormalizerTest.php +++ b/core/modules/text/tests/src/Kernel/Normalizer/TextItemBaseNormalizerTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\text\Kernel\Normalizer; use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\RenderContext; use Drupal\entity_test\Entity\EntityTest; @@ -72,14 +73,17 @@ protected function setUp() { 'status' => TRUE, ], 'filter_html' => [ - 'status' => 1, + 'status' => TRUE, 'settings' => [ 'allowed_html' => '', ], ], - // Include this test filter because it bubbles metadata. + // Include this test filter because it bubbles cache tags. 'filter_test_cache_tags' => [ - 'module' => 'filter', + 'status' => TRUE, + ], + // Include this test filter because it bubbles cache contexts. + 'filter_test_cache_contexts' => [ 'status' => TRUE, ], ], @@ -91,7 +95,7 @@ protected function setUp() { * * @dataProvider testNormalizeProvider */ - public function testNormalize($text_item, array $expected, array $filter_config_update = [], $updated_processed = '') { + public function testNormalize($text_item, array $expected, CacheableMetadata $extra_cacheability, array $filter_config_update = [], $updated_processed = '') { $original_entity = EntityTest::create(['field_text' => $text_item]); $original_entity->save(); $text_format = FilterFormat::load(empty($text_item['format']) ? static::$fallbackFormatId : $text_item['format']); @@ -102,15 +106,14 @@ public function testNormalize($text_item, array $expected, array $filter_config_ ->executeInRenderContext($context, function () use ($entity) { return $this->serializer->normalize($entity); }); - $tags = $text_format->getCacheTags(); - if ($text_format->id() == 'my_text_format') { - // Add tags that are hardcoded FilterTestCacheTags::process(). - $tags = array_merge($tags, ['foo:bar', 'foo:baz']); - } + $cacheability = new BubbleableMetadata(); - $cacheability->setCacheTags($tags); - $require_contexts = $this->container->getParameter('renderer.config')['required_cache_contexts']; - $cacheability->setCacheContexts($require_contexts); + $cacheability->setCacheTags($text_format->getCacheTags()); + $contexts = $this->container->getParameter('renderer.config')['required_cache_contexts']; + $cacheability->setCacheContexts($contexts); + // Merge the CacheableMetadata that is specific to this test. + $cacheability = $cacheability->merge($extra_cacheability); + $this->assertEquals($cacheability, $context->pop()); $this->assertEquals($expected, $data['field_text'][0]); @@ -139,6 +142,7 @@ public function testNormalize($text_item, array $expected, array $filter_config_ * Data provider for testNormalize(). */ public function testNormalizeProvider() { + $cacheability = new CacheableMetadata(); $test_cases['no_format_specified'] = [ 'text item', [ @@ -146,8 +150,13 @@ public function testNormalizeProvider() { 'processed' => "

text item

\n", 'format' => NULL, ], + $cacheability, ]; - + $cacheability = new CacheableMetadata(); + // Add the tags for the 'filter_test_cache_tags' filter. + $cacheability->setCacheTags(['foo:bar', 'foo:baz']); + // Add the contexts for the 'filter_test_cache_contexts' filter. + $cacheability->setCacheContexts(['languages:' . LanguageInterface::TYPE_CONTENT]); $test_cases['my_text_format'] = [ [ 'value' => 'This is important.', @@ -158,9 +167,10 @@ public function testNormalizeProvider() { 'format' => 'my_text_format', 'processed' => "

This is important.

\n", ], + $cacheability, [ 'filter_html' => [ - 'status' => 1, + 'status' => TRUE, 'settings' => [ 'allowed_html' => ' ', ],