From 8ffc9ecc6a0c8986115cc9a678b92f9aecb945a9 Mon Sep 17 00:00:00 2001 From: Ahmad Nawaz <ahmad.nawaz@paddle.be> Date: Thu, 27 Feb 2025 18:16:01 +0500 Subject: [PATCH 1/2] Can only intentionally re-render an entity with references 20 times --- .../Drupal/Core/Entity/EntityViewBuilder.php | 92 +++++++++++- .../EntityReferenceEntityFormatter.php | 34 ----- .../comment/src/CommentViewBuilder.php | 4 +- .../EntityReferenceAutoCreateTest.php | 50 +++++++ .../EntityReferenceFormatterTest.php | 138 ++++++++++++++++-- .../media/src/Plugin/Filter/MediaEmbed.php | 31 ---- .../tests/src/Kernel/MediaEmbedFilterTest.php | 9 +- core/tests/Drupal/Tests/EntityViewTrait.php | 9 ++ 8 files changed, 283 insertions(+), 84 deletions(-) diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index 149ea3ca32ce..8624460a403e 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -3,12 +3,14 @@ namespace Drupal\Core\Entity; use Drupal\Component\Utility\Crypt; +use Drupal\Component\Utility\Random; use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Field\FieldItemInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Logger\LoggerChannelTrait; use Drupal\Core\Render\Element; use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Theme\Registry; @@ -22,6 +24,8 @@ */ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterface, EntityViewBuilderInterface, TrustedCallbackInterface { + use LoggerChannelTrait; + /** * The type of entities for which this view builder is instantiated. * @@ -80,6 +84,15 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf */ protected $singleFieldDisplays; + /** + * A collection of keys. + * + * It identifies rendering in progress, used to prevent recursion. + * + * @var array + */ + protected array $recursionKeys = []; + /** * Constructs a new EntityViewBuilder. * @@ -107,13 +120,15 @@ public function __construct(EntityTypeInterface $entity_type, EntityRepositoryIn * {@inheritdoc} */ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { - return new static( + $instance = new static( $entity_type, $container->get('entity.repository'), $container->get('language_manager'), $container->get('theme.registry'), $container->get('entity_display.repository') ); + $instance->setLoggerFactory($container->get('logger.factory')); + return $instance; } /** @@ -136,7 +151,12 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N * {@inheritdoc} */ public static function trustedCallbacks() { - return ['build', 'buildMultiple']; + return [ + 'build', + 'buildMultiple', + 'setRecursiveRenderProtection', + 'unsetRecursiveRenderProtection', + ]; } /** @@ -189,6 +209,9 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode) { 'max-age' => $entity->getCacheMaxAge(), ], ]; + // Add callbacks to protect from recursive rendering. + $build['#pre_render'] = [[$this, 'setRecursiveRenderProtection']]; + $build['#post_render'] = [[$this, 'unsetRecursiveRenderProtection']]; // Add the default #theme key if a template exists for it. if ($this->themeRegistry->getRuntime()->has($this->entityTypeId)) { @@ -535,4 +558,69 @@ protected function getSingleFieldDisplay($entity, $field_name, $display_options) return $display; } + /** + * Entity render array #pre_render callback. + */ + public function setRecursiveRenderProtection(array $build): array { + // Checks whether entity render array with matching cache keys is being + // recursively rendered. If not already being rendered, add an entry to track + // that it is. + $recursion_key = $this->getRenderRecursionKey($build); + if ((count($this->recursionKeys) !== 1) && isset($this->recursionKeys[$recursion_key])) { + $this->getLogger('entity') + ->error('Recursive rendering attempt aborted for %key. In progress: %guards', [ + '%key' => $recursion_key, + '%guards' => print_r($this->recursionKeys, TRUE), + ]); + $build['#printed'] = TRUE; + } + else { + $this->recursionKeys[$recursion_key] = $recursion_key; + } + return $build; + } + + /** + * Entity render array #post_render callback. + */ + public function unsetRecursiveRenderProtection(string $renderedEntity, array $build): string { + // Removes rendered entity matching cache keys from recursive render + // tracking, once the entity has been rendered. + $recursion_key = $this->getRenderRecursionKey($build); + unset($this->recursionKeys[$recursion_key]); + + return $renderedEntity; + } + + /** + * Generates a key for an entity render array for recursion protection. + * + * @param array $build + * The entity render array. + * + * @return string + * The key to ID the build array within recursion tracking. + */ + protected function getRenderRecursionKey(array $build): string { + /** @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity = $build['#' . $this->entityTypeId]; + // If entity is new and has no ID, generate a unique random string. + if ($entity->id()) { + $entity_id = $entity->id(); + } + else { + if (!$entity->_tempRecursionRenderId) { + $entity->_tempRecursionRenderId = (new Random())->string(8, TRUE); + } + $entity_id = $entity->_tempRecursionRenderId; + } + // It seems very unlikely that the same entity displayed in the same view + // mode would be recursively nested and meant to be displayed differently, + // so a key made up of the entity type ID, entity ID, and view mode should + // suffice for recursion detection + return $entity->getEntityTypeId() + . $entity_id + . $build['#view_mode']; + } + } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php index b80f036ea38b..de42113ae9ab 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php @@ -160,40 +160,6 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $elements = []; foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) { - // Due to render caching and delayed calls, the viewElements() method - // will be called later in the rendering process through a '#pre_render' - // callback, so we need to generate a counter that takes into account - // all the relevant information about this field and the referenced - // entity that is being rendered. - $recursive_render_id = $items->getFieldDefinition()->getTargetEntityTypeId() - . $items->getFieldDefinition()->getTargetBundle() - . $items->getName() - // We include the referencing entity, so we can render default images - // without hitting recursive protections. - . $items->getEntity()->id() - . $entity->getEntityTypeId() - . $entity->id(); - - if (isset(static::$recursiveRenderDepth[$recursive_render_id])) { - static::$recursiveRenderDepth[$recursive_render_id]++; - } - else { - static::$recursiveRenderDepth[$recursive_render_id] = 1; - } - - // Protect ourselves from recursive rendering. - if (static::$recursiveRenderDepth[$recursive_render_id] > static::RECURSIVE_RENDER_LIMIT) { - $this->loggerFactory->get('entity')->error('Recursive rendering detected when rendering entity %entity_type: %entity_id, using the %field_name field on the %parent_entity_type:%parent_bundle %parent_entity_id entity. Aborting rendering.', [ - '%entity_type' => $entity->getEntityTypeId(), - '%entity_id' => $entity->id(), - '%field_name' => $items->getName(), - '%parent_entity_type' => $items->getFieldDefinition()->getTargetEntityTypeId(), - '%parent_bundle' => $items->getFieldDefinition()->getTargetBundle(), - '%parent_entity_id' => $items->getEntity()->id(), - ]); - return $elements; - } - $view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId()); $elements[$delta] = $view_builder->view($entity, $view_mode, $entity->language()->getId()); diff --git a/core/modules/comment/src/CommentViewBuilder.php b/core/modules/comment/src/CommentViewBuilder.php index 0e4e024dd574..b0a7af4f800e 100644 --- a/core/modules/comment/src/CommentViewBuilder.php +++ b/core/modules/comment/src/CommentViewBuilder.php @@ -61,7 +61,7 @@ public function __construct(EntityTypeInterface $entity_type, EntityRepositoryIn * {@inheritdoc} */ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { - return new static( + $instance = new static( $entity_type, $container->get('entity.repository'), $container->get('language_manager'), @@ -70,6 +70,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI $container->get('entity_display.repository'), $container->get('entity_type.manager') ); + $instance->setLoggerFactory($container->get('logger.factory')); + return $instance; } /** diff --git a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAutoCreateTest.php b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAutoCreateTest.php index 7a982ed1bf43..4893ec4b65ec 100644 --- a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAutoCreateTest.php +++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAutoCreateTest.php @@ -304,4 +304,54 @@ public function testNoBundles(): void { $this->assertEquals($referenced_id, $referencing_node->$field_name->target_id, 'Newly created node is referenced from the referencing entity.'); } + /** + * Test previewing autocreated node in a new node is not flagged as recursion. + * + * Entity render recursion is prevented by pushing/popping entities being + * rendered on to a stack keyed by a string made up of the entity type ID, + * entity ID, and the view mode. In the case of new entities, the entity ID is + * NULL, so this test checks that unique identifiers are used in the stack + * keys for new entities to prevent false positive recursion protection. + */ + public function testAutoCreatedNodeNewNodePreview(): void { + // Set the referenced node to be displayed as a rendered entity in the same + // view mode as the referencing node. + $display_repository = \Drupal::service('entity_display.repository'); + $display_repository->getViewDisplay('node', $this->referencingType) + ->setComponent('test_field', [ + 'type' => 'entity_reference_entity_view', + 'settings' => [ + 'view_mode' => 'full', + ], + ]) + ->save(); + // Remove the links component from the referenced node's display because + // there would be errors rendering links on the referenced entity without an + // ID. + $display_repository->getViewDisplay('node', $this->referencedType) + ->removeComponent('links') + ->save(); + + // Assert referenced node does not exist. + $referenced_title = $this->randomMachineName(); + $result = \Drupal::entityQuery('node') + ->accessCheck(FALSE) + ->condition('type', $this->referencedType) + ->condition('title', $referenced_title) + ->execute(); + $this->assertEmpty($result, 'Referenced node does not exist yet.'); + + // Preview a new referencing node from the node add form. + $edit = [ + 'title[0][value]' => $this->randomMachineName(), + 'test_field[0][target_id]' => $referenced_title, + ]; + $this->drupalGet("node/add/{$this->referencingType}"); + $this->submitForm($edit, 'Preview'); + $this->assertSession()->statusCodeEquals(200); + // Referenced node title should appear if not blocked by recursion + // protection. + $this->assertSession()->pageTextContains($referenced_title); + } + } diff --git a/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php b/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php index 9d4449cc6829..12da08fd6020 100644 --- a/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php +++ b/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php @@ -7,7 +7,6 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Field\FieldStorageDefinitionInterface; -use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter; use Drupal\entity_test\Entity\EntityTest; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; @@ -17,6 +16,9 @@ use Drupal\user\RoleInterface; use Drupal\entity_test\Entity\EntityTestLabel; use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; /** * Tests the formatters functionality. @@ -234,6 +236,44 @@ public function testEntityFormatter(): void { $this->assertSame('default | ' . $this->unsavedReferencedEntity->label() . $expected_rendered_name_field_2 . $expected_rendered_body_field_2, (string) $build[1]['#markup'], sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter)); } + /** + * Tests recursive rendering protection failing over single entity N times. + */ + public function testEntityFormatterRecursiveRenderingFailing(): void { + $request = Request::create('http://example.com', 'POST'); + $request->setSession(new Session(new MockArraySessionStorage())); + \Drupal::requestStack()->push($request); + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + $formatter = 'entity_reference_entity_view'; + $view_builder = $this->entityTypeManager->getViewBuilder($this->entityType); + + // Set the default view mode to use the 'entity_reference_entity_view' + // formatter. + \Drupal::service('entity_display.repository') + ->getViewDisplay($this->entityType, $this->bundle) + ->setComponent($this->fieldName, [ + 'type' => $formatter, + ]) + ->save(); + + $storage = \Drupal::entityTypeManager()->getStorage($this->entityType); + $entity = $storage->create(['name' => $this->randomMachineName()]); + $entity->save(); + $referencing_entity = $storage->create([ + 'name' => $this->randomMachineName(), + $this->fieldName => $entity->id(), + ]); + $referencing_entity->save(); + + $count = 21; + $build = $view_builder->viewMultiple(array_fill(0, $count, $referencing_entity), 'default'); + $output = (string) $renderer->renderRoot($build); + // The title of entity_test entities is printed twice by default, so we have + // to multiply our count by 2. + $this->assertSame($count * 2, substr_count($output, $entity->name->value)); + } + /** * Tests the recursive rendering protection of the entity formatter. */ @@ -260,8 +300,22 @@ public function testEntityFormatterRecursiveRendering(): void { $referencing_entity_1->{$this->fieldName}->entity = $referencing_entity_1; $referencing_entity_1->save(); - // Check that the recursive rendering stops after it reaches the specified - // limit. + // Using a different view mode is not recursion. + $build = $view_builder->view($referencing_entity_1, 'teaser'); + $output = (string) $renderer->renderRoot($build); + // 2 occurrences of the entity title per entity. + $expected_occurrences = 4; + + $actual_occurrences = substr_count($output, $referencing_entity_1->name->value); + $this->assertEquals($expected_occurrences, $actual_occurrences); + + // Self-references should not be rendered. + // entity_1 -> entity_1 + $build = $view_builder->view($referencing_entity_1, 'default'); + $output = (string) $renderer->renderRoot($build); + $expected_occurrences = 2; + $actual_occurrences = substr_count($output, $referencing_entity_1->name->value); + $this->assertEquals($expected_occurrences, $actual_occurrences); $build = $view_builder->view($referencing_entity_1, 'default'); $output = (string) $renderer->renderRoot($build); @@ -270,27 +324,46 @@ public function testEntityFormatterRecursiveRendering(): void { // Additionally, we have to take into account 2 additional occurrences of // the entity title because we're rendering the full entity, not just the // reference field. - $expected_occurrences = EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT * 2 + 2; + // Repetition is not wrongly detected as recursion. + // entity_1 -> entity_1 + $output = (string) $renderer->renderRoot($build); $actual_occurrences = substr_count($output, $referencing_entity_1->label()); $this->assertEquals($expected_occurrences, $actual_occurrences); - // Repeat the process with another entity in order to check that the - // 'recursive_render_id' counter is generated properly. + // Referencing from another entity works fine. + // entity_2 -> entity_1 $referencing_entity_2 = $storage->create(['name' => $this->randomMachineName()]); $referencing_entity_2->save(); - $referencing_entity_2->{$this->fieldName}->entity = $referencing_entity_2; + $referencing_entity_2->{$this->fieldName}->entity = $referencing_entity_1; $referencing_entity_2->save(); $build = $view_builder->view($referencing_entity_2, 'default'); $output = (string) $renderer->renderRoot($build); - $actual_occurrences = substr_count($output, $referencing_entity_2->label()); + $actual_occurrences = substr_count($output, $referencing_entity_1->name->value); $this->assertEquals($expected_occurrences, $actual_occurrences); - // Now render both entities at the same time and check again. + // Referencing from multiple is fine. + // entity_1 -> entity_1 + // entity_2 -> entity_1 $build = $view_builder->viewMultiple([$referencing_entity_1, $referencing_entity_2], 'default'); $output = (string) $renderer->renderRoot($build); + // entity_1 should be seen once as a parent and once as a child of entity_2. + $expected_occurrences = 4; + // entity_2 is seen only once, as a parent. + $expected_occurrences = 2; + $actual_occurrences = substr_count($output, $referencing_entity_2->name->value); + $this->assertEquals($expected_occurrences, $actual_occurrences); + // Indirect recursion is not ok. + // entity_2 -> entity_1 -> entity_2 + $referencing_entity_1->{$this->fieldName}->entity = $referencing_entity_2; + $referencing_entity_1->save(); + $build = $view_builder->view($referencing_entity_2, 'default'); + $output = (string) $renderer->renderRoot($build); + // Each entity should be seen once. + $expected_occurrences = 2; + $actual_occurrences = substr_count($output, $referencing_entity_1->label()); $this->assertEquals($expected_occurrences, $actual_occurrences); @@ -341,6 +414,53 @@ public function testEntityReferenceRecursiveProtectionWithManyRenderedEntities() $this->assertEquals($expected_occurrences, $actual_occurrences); } + /** + * Tests multiple renderings of an entity that references another. + */ + public function testEntityReferenceRecursionProtectionWithRepeatedReferencingEntity(): void { + $request = Request::create('http://example.com', 'POST'); + $request->setSession(new Session(new MockArraySessionStorage())); + \Drupal::requestStack()->push($request); + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + $formatter = 'entity_reference_entity_view'; + $view_builder = $this->entityTypeManager->getViewBuilder($this->entityType); + + // Set the default view mode to use the 'entity_reference_entity_view' + // formatter. + \Drupal::service('entity_display.repository') + ->getViewDisplay($this->entityType, $this->bundle) + ->setComponent($this->fieldName, [ + 'type' => $formatter, + ]) + ->save(); + + $storage = \Drupal::entityTypeManager()->getStorage($this->entityType); + $entity = $storage->create(['name' => $this->randomMachineName()]); + $entity->save(); + $referencing_entity = $storage->create([ + 'name' => $this->randomMachineName(), + $this->fieldName => $entity->id(), + ]); + $referencing_entity->save(); + + // Large-scale repetition within a single render root is not recursion. + $count = 30; + $build = $view_builder->viewMultiple(array_fill(0, $count, $referencing_entity), 'default'); + $output = (string) $renderer->renderRoot($build); + // The title of entity_test entities is printed twice by default, so we have + // to multiply our count by 2. + $this->assertSame($count * 2, substr_count($output, $entity->name->value)); + + // Large-scale repetition across render roots is not recursion. + for ($i = 0; $i < $count; $i++) { + $build = $view_builder->view($referencing_entity, 'default'); + $output = (string) $renderer->renderRoot($build); + // The title of entity_test entities is printed twice by default. + $this->assertSame(2, substr_count($output, $entity->name->value)); + } + } + /** * Tests the label formatter. */ diff --git a/core/modules/media/src/Plugin/Filter/MediaEmbed.php b/core/modules/media/src/Plugin/Filter/MediaEmbed.php index 118422bf2c28..007385db3611 100644 --- a/core/modules/media/src/Plugin/Filter/MediaEmbed.php +++ b/core/modules/media/src/Plugin/Filter/MediaEmbed.php @@ -85,18 +85,6 @@ class MediaEmbed extends FilterBase implements ContainerFactoryPluginInterface, */ protected $loggerFactory; - /** - * An array of counters for the recursive rendering protection. - * - * Each counter takes into account all the relevant information about the - * field and the referenced entity that is being rendered. - * - * @var array - * - * @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter::$recursiveRenderDepth - */ - protected static $recursiveRenderDepth = []; - /** * Constructs a MediaEmbed object. * @@ -213,25 +201,6 @@ public static function validateOptions(array &$element, FormStateInterface $form * A render array. */ protected function renderMedia(MediaInterface $media, $view_mode, $langcode) { - // Due to render caching and delayed calls, filtering happens later - // in the rendering process through a '#pre_render' callback, so we - // need to generate a counter for the media entity that is being embedded. - // @see \Drupal\filter\Element\ProcessedText::preRenderText() - $recursive_render_id = $media->uuid(); - if (isset(static::$recursiveRenderDepth[$recursive_render_id])) { - static::$recursiveRenderDepth[$recursive_render_id]++; - } - else { - static::$recursiveRenderDepth[$recursive_render_id] = 1; - } - // Protect ourselves from recursive rendering: return an empty render array. - if (static::$recursiveRenderDepth[$recursive_render_id] > EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT) { - $this->loggerFactory->get('media')->error('During rendering of embedded media: recursive rendering detected for %entity_id. Aborting rendering.', [ - '%entity_id' => $media->id(), - ]); - return []; - } - $build = $this->entityTypeManager ->getViewBuilder('media') ->view($media, $view_mode, $langcode); diff --git a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php index e283aae92bb1..0af8d0b4ffe3 100644 --- a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php +++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTest.php @@ -381,16 +381,11 @@ public function testRecursionProtection(): void { 'data-entity-uuid' => static::EMBEDDED_ENTITY_UUID, ]); - // Render and verify the presence of the embedded entity 20 times. - for ($i = 0; $i < 20; $i++) { + // Render and verify the presence of the embedded entity 30 times. + for ($i = 0; $i < 30; $i++) { $this->applyFilter($text); $this->assertCount(1, $this->cssSelect('div[data-media-embed-test-view-mode="default"]')); } - - // Render a 21st time, this is exceeding the recursion limit. The entity - // embed markup will be stripped. - $this->applyFilter($text); - $this->assertEmpty($this->getRawContent()); } /** diff --git a/core/tests/Drupal/Tests/EntityViewTrait.php b/core/tests/Drupal/Tests/EntityViewTrait.php index a1568119f8b6..a2a3f2d3476a 100644 --- a/core/tests/Drupal/Tests/EntityViewTrait.php +++ b/core/tests/Drupal/Tests/EntityViewTrait.php @@ -63,6 +63,15 @@ protected function buildEntityView(EntityInterface $entity, $view_mode = 'full', $build = $render_controller->view($entity, $view_mode, $langcode); $ensure_fully_built($build); + // EntityViewBuilder adds a #pre_render hook that tracks entity render array + // to prevent recursive rendering. Since the #pre_render hooks were called + // manually here without rendering actually taking place, unset the tracking + // for the render array so that when it is rendered, it does not trigger + // the recursive protection. + if (method_exists($render_controller, 'unsetRecursiveRenderProtection')) { + $render_controller->unsetRecursiveRenderProtection('', $build); + } + return $build; } -- GitLab From de3fec62390707189a3b59e1b35e931ae51b276d Mon Sep 17 00:00:00 2001 From: Ahmad Nawaz <ahmad.nawaz@paddle.be> Date: Thu, 27 Feb 2025 19:19:42 +0500 Subject: [PATCH 2/2] Removed un-used use statement --- core/modules/media/src/Plugin/Filter/MediaEmbed.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/modules/media/src/Plugin/Filter/MediaEmbed.php b/core/modules/media/src/Plugin/Filter/MediaEmbed.php index 007385db3611..426d429e6256 100644 --- a/core/modules/media/src/Plugin/Filter/MediaEmbed.php +++ b/core/modules/media/src/Plugin/Filter/MediaEmbed.php @@ -7,7 +7,6 @@ use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -- GitLab