Unverified Commit 00a96de9 authored by Mateu Aguiló Bosch's avatar Mateu Aguiló Bosch Committed by Mateu Aguiló Bosch
Browse files

Issue #3309314 by e0ipso: Custom render element should support Twig blocks

parent e1a82682
Loading
Loading
Loading
Loading
+96 −5
Original line number Diff line number Diff line
@@ -2,7 +2,12 @@

namespace Drupal\cl_components\Element;

use Drupal\cl_components\Component\Component;
use Drupal\cl_components\Component\ComponentDiscovery;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element\RenderElement;

/**
@@ -41,18 +46,104 @@ class ComponentElement extends RenderElement {
   */
  public static function preRenderComponent(array $element): array {
    $variant = $element['#variant'] ?? '';
    $component = \Drupal::service(ComponentDiscovery::class)->find($element['#component']);
    $id = $component->getId();
    $embed_id = $variant ? $id . '--' . $variant : $id;
    $inline_template = '{% include "' . $embed_id . '" %}';
    $context_alter_callback = $element['#context_alter_callback'] ?? NULL;
    $blocks_alter_callback = $element['#blocks_alter_callback'] ?? NULL;
    $discovery = \Drupal::service(ComponentDiscovery::class);
    assert($discovery instanceof ComponentDiscovery);
    $component = $discovery->find($element['#component']);
    $context = $element['#context'] ?? [];
    $bubbleable_metadata = new BubbleableMetadata();
    $twig_blocks = $element['#twig_blocks'] ?? [];
    $trusted_blocks = $element['#trusted_blocks'] ?? FALSE;
    $inline_template = static::generateComponentTemplate(
      $component,
      $variant,
      $twig_blocks,
      $context,
      $context_alter_callback,
      $blocks_alter_callback,
      $bubbleable_metadata,
      $trusted_blocks,
    );
    $element['inline-template'] = [
      '#type' => 'inline_template',
      '#template' => $inline_template,
      '#context' => $element['#context'] ?? [],
      '#context' => $context,
    ];
    $bubbleable_metadata->applyTo($element['inline-template']);
    return $element;
  }

  /**
   * Generates the template to render the component.
   *
   * @param \Drupal\cl_components\Component\Component $component
   *   The component.
   * @param mixed $variant
   *   The variant.
   * @param array $twig_blocks
   *   The contents of any potential embed blocks.
   * @param array $context
   *   The context data.
   * @param callable|null $context_alter_callback
   *   The potential callable for altering context.
   * @param \Drupal\Core\Cache\CacheableMetadata $bubbleable_metadata
   *   The cacheable metadata.
   * @param bool $trusted_blocks
   *   Set to TRUE to render the twig blocks verbatim instead of filtering them.
   *
   * @return string
   *   The template.
   */
  private static function generateComponentTemplate(
    Component $component,
    string $variant,
    array $twig_blocks,
    array $context,
    mixed $context_alter_callback,
    mixed $blocks_alter_callback,
    CacheableMetadata $bubbleable_metadata,
    bool $trusted_blocks = FALSE,
  ): string {
    $id = $component->getId();
    $embed_id = $variant ? $id . '--' . $variant : $id;
    $template = '{# This template was dynamically generated by cl_block #}' . PHP_EOL;
    // If there is a context alter callback, execute it here.
    if (is_callable($context_alter_callback)) {
      $context = call_user_func($context_alter_callback, $context, $bubbleable_metadata);
    }
    $template .= empty($context)
      ? sprintf('{%% embed \'%s\' %%}', $embed_id)
      : sprintf('{%% embed \'%s\' with %s %%}', $embed_id, Json::encode($context));
    $template .= PHP_EOL;
    foreach ($twig_blocks as $block_name => $block_value) {
      $bl_val = is_callable($blocks_alter_callback)
        ? call_user_func($blocks_alter_callback, $block_value, $bubbleable_metadata)
        : $block_value;
      $block_build = $trusted_blocks
        ? ['#markup' => Xss::filterAdmin($bl_val)]
        : [
          '#type' => 'processed_text',
          '#text' => $bl_val,
          '#format' => is_array($block_value) ? $block_value['format'] : NULL,
        ];
      try {
        $value = \Drupal::service('renderer')->render($block_build);
      }
      catch (\Exception $e) {
        $value = $bl_val;
      }
      // Save the collected attachments before we do the stringification.
      $twig_block_metadata = BubbleableMetadata::createFromRenderArray($block_build);
      $bubbleable_metadata = $bubbleable_metadata->merge($twig_block_metadata);
      $template .= "  {% block $block_name %}" . PHP_EOL
        . "    $value" . PHP_EOL
        . "  {% endblock %}" . PHP_EOL;
    }
    $template .= '{% endembed %}' . PHP_EOL;
    return $template;
  }

  /**
   * {@inheritdoc}
   */