You are here

public function FilterCaption::process in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/filter/src/Plugin/Filter/FilterCaption.php \Drupal\filter\Plugin\Filter\FilterCaption::process()

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

core/modules/filter/src/Plugin/Filter/FilterCaption.php, line 66

Class

FilterCaption
Provides a filter to caption elements.

Namespace

Drupal\filter\Plugin\Filter

Code

public function process($text, $langcode) {
  $result = new FilterProcessResult($text);
  if (stristr($text, 'data-caption') !== FALSE) {
    $dom = Html::load($text);
    $xpath = new \DOMXPath($dom);
    $html_filter = $this->filterManager
      ->createInstance('filter_html', [
      'settings' => [
        'allowed_html' => '<a href hreflang target rel> <em> <strong> <cite> <code> <br>',
        'filter_html_help' => FALSE,
        'filter_html_nofollow' => FALSE,
      ],
    ]);
    foreach ($xpath
      ->query('//*[@data-caption]') as $node) {

      // Read the data-caption attribute's value, then delete it.
      $caption = Html::escape($node
        ->getAttribute('data-caption'));
      $node
        ->removeAttribute('data-caption');

      // Sanitize caption: decode HTML encoding, limit allowed HTML tags; only
      // allow inline tags that are allowed by default, plus <br>.
      $caption = Html::decodeEntities($caption);
      $raw_caption = $caption;
      $filtered_caption = $html_filter
        ->process($caption, $langcode);
      $result
        ->addCacheableDependency($filtered_caption);
      $caption = FilteredMarkup::create($filtered_caption
        ->getProcessedText());

      // The caption must be non-empty - however the Media Embed CKEditor
      // plugin uses a single space to represent a newly added caption. The
      // HTML filter will transform this into an empty string and prevent the
      // content editor from adding a new caption. To allow for this we treat
      // a raw caption value of ' ' as valid and adding the wrapping figure
      // element.
      // @see core/modules/media/js/plugins/drupalmedia/plugin.es6.js
      if (mb_strlen($caption) === 0 && $raw_caption !== ' ') {
        continue;
      }

      // Given the updated node and caption: re-render it with a caption, but
      // bubble up the value of the class attribute of the captioned element,
      // this allows it to collaborate with e.g. the filter_align filter.
      $tag = $node->tagName;
      $classes = $node
        ->getAttribute('class');
      $node
        ->removeAttribute('class');
      $node = $node->parentNode->tagName === 'a' ? $node->parentNode : $node;
      $filter_caption = [
        '#theme' => 'filter_caption',
        // We pass the unsanitized string because this is a text format
        // filter, and after filtering, we always assume the output is safe.
        // @see \Drupal\filter\Element\ProcessedText::preRenderText()
        '#node' => FilteredMarkup::create($node
          ->C14N()),
        '#tag' => $tag,
        '#caption' => $caption,
        '#classes' => $classes,
      ];
      $altered_html = \Drupal::service('renderer')
        ->render($filter_caption);

      // Load the altered HTML into a new DOMDocument and retrieve the element.
      $updated_nodes = Html::load($altered_html)
        ->getElementsByTagName('body')
        ->item(0)->childNodes;
      foreach ($updated_nodes as $updated_node) {

        // Import the updated node from the new DOMDocument into the original
        // one, importing also the child nodes of the updated node.
        $updated_node = $dom
          ->importNode($updated_node, TRUE);
        $node->parentNode
          ->insertBefore($updated_node, $node);
      }

      // Finally, remove the original data-caption node.
      $node->parentNode
        ->removeChild($node);
    }
    $result
      ->setProcessedText(Html::serialize($dom))
      ->addAttachments([
      'library' => [
        'filter/caption',
      ],
    ]);
  }
  return $result;
}