03cddb790051ee482e1ac724b372c9fd2163e694
[yaffs-website] / web / core / modules / filter / src / Plugin / Filter / FilterCaption.php
1 <?php
2
3 namespace Drupal\filter\Plugin\Filter;
4
5 use Drupal\Component\Utility\Html;
6 use Drupal\Component\Utility\Unicode;
7 use Drupal\Component\Utility\Xss;
8 use Drupal\filter\FilterProcessResult;
9 use Drupal\filter\Plugin\FilterBase;
10 use Drupal\filter\Render\FilteredMarkup;
11
12 /**
13  * Provides a filter to caption elements.
14  *
15  * When used in combination with the filter_align filter, this must run last.
16  *
17  * @Filter(
18  *   id = "filter_caption",
19  *   title = @Translation("Caption images"),
20  *   description = @Translation("Uses a <code>data-caption</code> attribute on <code>&lt;img&gt;</code> tags to caption images."),
21  *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
22  * )
23  */
24 class FilterCaption extends FilterBase {
25
26   /**
27    * {@inheritdoc}
28    */
29   public function process($text, $langcode) {
30     $result = new FilterProcessResult($text);
31
32     if (stristr($text, 'data-caption') !== FALSE) {
33       $dom = Html::load($text);
34       $xpath = new \DOMXPath($dom);
35       foreach ($xpath->query('//*[@data-caption]') as $node) {
36         // Read the data-caption attribute's value, then delete it.
37         $caption = Html::escape($node->getAttribute('data-caption'));
38         $node->removeAttribute('data-caption');
39
40         // Sanitize caption: decode HTML encoding, limit allowed HTML tags; only
41         // allow inline tags that are allowed by default, plus <br>.
42         $caption = Html::decodeEntities($caption);
43         $caption = FilteredMarkup::create(Xss::filter($caption, ['a', 'em', 'strong', 'cite', 'code', 'br']));
44
45         // The caption must be non-empty.
46         if (Unicode::strlen($caption) === 0) {
47           continue;
48         }
49
50         // Given the updated node and caption: re-render it with a caption, but
51         // bubble up the value of the class attribute of the captioned element,
52         // this allows it to collaborate with e.g. the filter_align filter.
53         $tag = $node->tagName;
54         $classes = $node->getAttribute('class');
55         $node->removeAttribute('class');
56         $node = ($node->parentNode->tagName === 'a') ? $node->parentNode : $node;
57         $filter_caption = [
58           '#theme' => 'filter_caption',
59           // We pass the unsanitized string because this is a text format
60           // filter, and after filtering, we always assume the output is safe.
61           // @see \Drupal\filter\Element\ProcessedText::preRenderText()
62           '#node' => FilteredMarkup::create($node->C14N()),
63           '#tag' => $tag,
64           '#caption' => $caption,
65           '#classes' => $classes,
66         ];
67         $altered_html = \Drupal::service('renderer')->render($filter_caption);
68
69         // Load the altered HTML into a new DOMDocument and retrieve the element.
70         $updated_nodes = Html::load($altered_html)->getElementsByTagName('body')
71           ->item(0)
72           ->childNodes;
73
74         foreach ($updated_nodes as $updated_node) {
75           // Import the updated node from the new DOMDocument into the original
76           // one, importing also the child nodes of the updated node.
77           $updated_node = $dom->importNode($updated_node, TRUE);
78           $node->parentNode->insertBefore($updated_node, $node);
79         }
80         // Finally, remove the original data-caption node.
81         $node->parentNode->removeChild($node);
82       }
83
84       $result->setProcessedText(Html::serialize($dom))
85         ->addAttachments([
86           'library' => [
87             'filter/caption',
88           ],
89         ]);
90     }
91
92     return $result;
93   }
94
95   /**
96    * {@inheritdoc}
97    */
98   public function tips($long = FALSE) {
99     if ($long) {
100       return $this->t('
101         <p>You can caption images, videos, blockquotes, and so on. Examples:</p>
102         <ul>
103             <li><code>&lt;img src="" data-caption="This is a caption" /&gt;</code></li>
104             <li><code>&lt;video src="" data-caption="The Drupal Dance" /&gt;</code></li>
105             <li><code>&lt;blockquote data-caption="Dries Buytaert"&gt;Drupal is awesome!&lt;/blockquote&gt;</code></li>
106             <li><code>&lt;code data-caption="Hello world in JavaScript."&gt;alert("Hello world!");&lt;/code&gt;</code></li>
107         </ul>');
108     }
109     else {
110       return $this->t('You can caption images (<code>data-caption="Text"</code>), but also videos, blockquotes, and so on.');
111     }
112   }
113
114 }