You are here

public function EntityEmbedFilter::process in Entity Embed 8

Performs the filter processing.

Parameters

string $text: The text string to be filtered.

string $langcode: The language code of the text to be filtered.

Return value

\Drupal\filter\FilterProcessResult The filtered text, wrapped in a FilterProcessResult object, and possibly with associated assets, cacheability metadata and placeholders.

Overrides FilterInterface::process

See also

\Drupal\filter\FilterProcessResult

File

src/Plugin/Filter/EntityEmbedFilter.php, line 126

Class

EntityEmbedFilter
Provides a filter to display embedded entities based on data attributes.

Namespace

Drupal\entity_embed\Plugin\Filter

Code

public function process($text, $langcode) {
  $result = new FilterProcessResult($text);
  if (strpos($text, 'data-entity-type') !== FALSE && (strpos($text, 'data-entity-embed-display') !== FALSE || strpos($text, 'data-view-mode') !== FALSE)) {
    $dom = Html::load($text);
    $xpath = new \DOMXPath($dom);
    foreach ($xpath
      ->query('//drupal-entity[@data-entity-type and (@data-entity-uuid or @data-entity-id) and (@data-entity-embed-display or @data-view-mode)]') as $node) {

      /** @var \DOMElement $node */
      $entity_type = $node
        ->getAttribute('data-entity-type');
      $entity = NULL;
      $entity_output = '';

      // data-entity-embed-settings is deprecated, make sure we convert it to
      // data-entity-embed-display-settings.
      if (($settings = $node
        ->getAttribute('data-entity-embed-settings')) && !$node
        ->hasAttribute('data-entity-embed-display-settings')) {
        $node
          ->setAttribute('data-entity-embed-display-settings', $settings);
        $node
          ->removeAttribute('data-entity-embed-settings');
      }
      $entity = NULL;
      try {

        // Load the entity either by UUID (preferred) or ID.
        $id = NULL;
        if ($id = $node
          ->getAttribute('data-entity-uuid')) {
          $entity = $this->entityTypeManager
            ->getStorage($entity_type)
            ->loadByProperties([
            'uuid' => $id,
          ]);
          $entity = current($entity);
        }
        else {
          $id = $node
            ->getAttribute('data-entity-id');
          $entity = $this->entityTypeManager
            ->getStorage($entity_type)
            ->load($id);
        }
        if (!$entity instanceof EntityInterface) {
          $missing_text = $this
            ->t('Missing @type.', [
            '@type' => $this->entityTypeManager
              ->getDefinition($entity_type)
              ->getSingularLabel(),
          ]);
          $entity_output = '<img src="' . file_url_transform_relative(file_create_url('core/modules/media/images/icons/no-thumbnail.png')) . '" width="180" height="180" alt="' . $missing_text . '" title="' . $missing_text . '"/>';
          throw new EntityNotFoundException(sprintf('Unable to load embedded %s entity %s.', $entity_type, $id));
        }
      } catch (EntityNotFoundException $e) {
        watchdog_exception('entity_embed', $e);
      }
      if ($entity instanceof EntityInterface) {

        // If a UUID was not used, but is available, add it to the HTML.
        if (!$node
          ->getAttribute('data-entity-uuid') && ($uuid = $entity
          ->uuid())) {
          $node
            ->setAttribute('data-entity-uuid', $uuid);
        }
        $context = $this
          ->getNodeAttributesAsArray($node);
        $context += [
          'data-langcode' => $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 that takes into account all the
        // relevant information about this field and the referenced entity
        // that is being rendered.
        // @see \Drupal\filter\Element\ProcessedText::preRenderText()
        $recursive_render_id = $entity
          ->uuid() . json_encode($context);
        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 embedded entity %entity_type: %entity_id. Aborting rendering.', [
            '%entity_type' => $entity
              ->getEntityTypeId(),
            '%entity_id' => $entity
              ->id(),
          ]);
          $entity_output = '';
        }
        else {
          $build = $this->builder
            ->buildEntityEmbed($entity, $context);

          // We need to render the embedded entity:
          // - without replacing placeholders, so that the placeholders are
          //   only replaced at the last possible moment. Hence we cannot use
          //   either renderPlain() or renderRoot(), so we must use render().
          // - without bubbling beyond this filter, because filters must
          //   ensure that the bubbleable metadata for the changes they make
          //   when filtering text makes it onto the FilterProcessResult
          //   object that they return ($result). To prevent that bubbling, we
          //   must wrap the call to render() in a render context.
          $entity_output = $this->renderer
            ->executeInRenderContext(new RenderContext(), function () use (&$build) {
            return $this->renderer
              ->render($build);
          });
          $result = $result
            ->merge(BubbleableMetadata::createFromRenderArray($build));
        }
      }
      $this
        ->replaceNodeContent($node, $entity_output);
    }
    $result
      ->setProcessedText(Html::serialize($dom));
  }
  return $result;
}