diff --git a/core/modules/layout_builder/layout_builder.services.yml b/core/modules/layout_builder/layout_builder.services.yml index 6e94ed74d2..11e6adfa68 100644 --- a/core/modules/layout_builder/layout_builder.services.yml +++ b/core/modules/layout_builder/layout_builder.services.yml @@ -42,7 +42,7 @@ services: arguments: ['@tempstore.shared', '@entity_type.manager'] layout_builder.render_block_component_subscriber: class: Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray - arguments: ['@current_user'] + arguments: ['@current_user', '@renderer'] tags: - { name: event_subscriber } logger.channel.layout_builder: diff --git a/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php index e1a9773959..1505721200 100644 --- a/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php +++ b/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php @@ -3,10 +3,12 @@ namespace Drupal\layout_builder\EventSubscriber; use Drupal\block_content\Access\RefinableDependentAccessInterface; +use Drupal\Component\Utility\Html; use Drupal\Core\Access\AccessResult; use Drupal\Core\Block\BlockPluginInterface; use Drupal\Core\Render\Element; use Drupal\Core\Render\PreviewFallbackInterface; +use Drupal\Core\Render\RendererInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed; @@ -32,14 +34,24 @@ class BlockComponentRenderArray implements EventSubscriberInterface { */ protected $currentUser; + /** + * The core renderer service. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + /** * Creates a BlockComponentRenderArray object. * * @param \Drupal\Core\Session\AccountInterface $current_user * The current user. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The core renderer service. */ - public function __construct(AccountInterface $current_user) { + public function __construct(AccountInterface $current_user, RendererInterface $renderer) { $this->currentUser = $current_user; + $this->renderer = $renderer; } /** @@ -108,6 +120,9 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) { if ($is_content_empty && !$is_placeholder_ready) { return; } + if ($event->inPreview()) { + $this->convertFormsToDiv($content); + } $build = [ // @todo Move this to BlockBase in https://www.drupal.org/node/2931040. @@ -138,4 +153,46 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) { } } + /** + * Prevent nested forms from generating form tokens. + * + * @param array $content + * The block render array. + * + * @return array + * A render array where nested forms will not generate tokens due to + * #theme_wrappers being removed. + */ + protected function detokenizeNestedForms(array $content) { + if (is_array($content)) { + if (isset($content['#type']) && $content['#type'] === 'form') { + unset($content['#theme_wrappers']); + } + foreach ($content as $key => $child_content) { + if (is_array($child_content)) { + $content[$key] = $this->detokenizeNestedForms($child_content); + } + + } + } + return $content; + } + + /** + * Convert form tags to div when displayed in the Layout Builder UI form. + * + * @param array $content + * The render array of the block. + */ + protected function convertFormsToDiv(array &$content) { + $markup = $this->renderer->render($content); + $html = Html::load((string) $markup); + + // This step is only necessary if forms are present in the markup. + if ($html->getElementsByTagName('form')->length > 0) { + $new_markup = preg_replace('/