FilterCaption.php 3.89 KB
Newer Older
1
2
3
4
5
6
7
8
9
<?php

/**
 * @file
 * Contains \Drupal\filter\Plugin\Filter\FilterCaption.
 */

namespace Drupal\filter\Plugin\Filter;

10
use Drupal\Component\Utility\Html;
11
use Drupal\Component\Utility\SafeMarkup;
12
13
14
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\Xss;
15
use Drupal\filter\FilterProcessResult;
16
17
18
use Drupal\filter\Plugin\FilterBase;

/**
19
20
21
 * Provides a filter to caption elements.
 *
 * When used in combination with the filter_align filter, this must run last.
22
23
24
 *
 * @Filter(
 *   id = "filter_caption",
25
26
 *   title = @Translation("Caption images"),
 *   description = @Translation("Uses a <code>data-caption</code> attribute on <code>&lt;img&gt;</code> tags to caption images."),
27
 *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
28
29
30
31
32
33
34
 * )
 */
class FilterCaption extends FilterBase {

  /**
   * {@inheritdoc}
   */
35
36
  public function process($text, $langcode) {
    $result = new FilterProcessResult($text);
37

38
    if (stristr($text, 'data-caption') !== FALSE) {
39
      $dom = Html::load($text);
40
      $xpath = new \DOMXPath($dom);
41
42
43
44
      foreach ($xpath->query('//*[@data-caption]') as $node) {
        // Read the data-caption attribute's value, then delete it.
        $caption = String::checkPlain($node->getAttribute('data-caption'));
        $node->removeAttribute('data-caption');
45

46
47
48
49
        // Sanitize caption: decode HTML encoding, limit allowed HTML tags; only
        // allow inline tags that are allowed by default, plus <br>.
        $caption = String::decodeEntities($caption);
        $caption = Xss::filter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br'));
50

51
52
        // The caption must be non-empty.
        if (Unicode::strlen($caption) === 0) {
53
54
55
          continue;
        }

56
57
58
59
60
        // 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.
        $classes = $node->getAttribute('class');
        $node->removeAttribute('class');
61
62
        $filter_caption = array(
          '#theme' => 'filter_caption',
63
          '#node' => SafeMarkup::set($node->C14N()),
64
65
          '#tag' => $node->tagName,
          '#caption' => $caption,
66
          '#classes' => $classes,
67
68
        );
        $altered_html = drupal_render($filter_caption);
69
70

        // Load the altered HTML into a new DOMDocument and retrieve the element.
71
        $updated_node = Html::load($altered_html)->getElementsByTagName('body')
72
73
74
75
76
77
78
79
80
81
82
          ->item(0)
          ->childNodes
          ->item(0);

        // 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);
        // Finally, replace the original image node with the new image node!
        $node->parentNode->replaceChild($updated_node, $node);
      }

83
84
      $result->setProcessedText(Html::serialize($dom))
        ->addAssets(array(
85
86
87
88
          'library' => array(
            'filter/caption',
          ),
        ));
89
90
    }

91
    return $result;
92
93
94
95
96
97
98
  }

  /**
   * {@inheritdoc}
   */
  public function tips($long = FALSE) {
    if ($long) {
99
      return $this->t('
100
        <p>You can caption images, videos, blockquotes, and so on. Examples:</p>
101
        <ul>
102
103
104
105
            <li><code>&lt;img src="" data-caption="This is a caption" /&gt;</code></li>
            <li><code>&lt;video src="" data-caption="The Drupal Dance" /&gt;</code></li>
            <li><code>&lt;blockquote data-caption="Dries Buytaert"&gt;Drupal is awesome!&lt;/blockquote&gt;</code></li>
            <li><code>&lt;code data-caption="Hello world in JavaScript."&gt;alert("Hello world!");&lt;/code&gt;</code></li>
106
107
108
        </ul>');
    }
    else {
109
      return $this->t('You can caption images (<code>data-caption="Text"</code>), but also videos, blockquotes, and so on.');
110
111
    }
  }
112

113
}