0b9ac91e4bc92d5c70770847ab5756512bc5dd7b
[yaffs-website] / web / core / lib / Drupal / Core / Render / Element / HtmlTag.php
1 <?php
2
3 namespace Drupal\Core\Render\Element;
4
5 use Drupal\Component\Render\MarkupInterface;
6 use Drupal\Component\Utility\Html as HtmlUtility;
7 use Drupal\Core\Render\Markup;
8 use Drupal\Component\Utility\Xss;
9 use Drupal\Core\Template\Attribute;
10
11 /**
12  * Provides a render element for any HTML tag, with properties and value.
13  *
14  * Properties:
15  * - #tag: The tag name to output.
16  * - #attributes: (array, optional) HTML attributes to apply to the tag. The
17  *   attributes are escaped, see \Drupal\Core\Template\Attribute.
18  * - #value: (string, optional) A string containing the textual contents of
19  *   the tag.
20  * - #noscript: (bool, optional) When set to TRUE, the markup
21  *   (including any prefix or suffix) will be wrapped in a <noscript> element.
22  *
23  * Usage example:
24  * @code
25  * $build['hello'] = [
26  *   '#type' => 'html_tag',
27  *   '#tag' => 'p',
28  *   '#value' => $this->t('Hello World'),
29  * ];
30  * @endcode
31  *
32  * @RenderElement("html_tag")
33  */
34 class HtmlTag extends RenderElement {
35
36   /**
37    * Void elements do not contain values or closing tags.
38    * @see http://www.w3.org/TR/html5/syntax.html#syntax-start-tag
39    * @see http://www.w3.org/TR/html5/syntax.html#void-elements
40    */
41   static protected $voidElements = [
42     'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
43     'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr',
44   ];
45
46   /**
47    * {@inheritdoc}
48    */
49   public function getInfo() {
50     $class = get_class($this);
51     return [
52       '#pre_render' => [
53         [$class, 'preRenderConditionalComments'],
54         [$class, 'preRenderHtmlTag'],
55       ],
56       '#attributes' => [],
57       '#value' => NULL,
58     ];
59   }
60
61   /**
62    * Pre-render callback: Renders a generic HTML tag with attributes into #markup.
63    *
64    * @param array $element
65    *   An associative array containing:
66    *   - #tag: The tag name to output. Typical tags added to the HTML HEAD:
67    *     - meta: To provide meta information, such as a page refresh.
68    *     - link: To refer to stylesheets and other contextual information.
69    *     - script: To load JavaScript.
70    *     The value of #tag is escaped.
71    *   - #attributes: (optional) An array of HTML attributes to apply to the
72    *     tag. The attributes are escaped, see \Drupal\Core\Template\Attribute.
73    *   - #value: (optional) A string containing tag content, such as inline
74    *     CSS. The value of #value will be XSS admin filtered if it is not safe.
75    *   - #noscript: (optional) If TRUE, the markup (including any prefix or
76    *     suffix) will be wrapped in a <noscript> element. (Note that passing
77    *     any non-empty value here will add the <noscript> tag.)
78    *
79    * @return array
80    */
81   public static function preRenderHtmlTag($element) {
82     $attributes = isset($element['#attributes']) ? new Attribute($element['#attributes']) : '';
83
84     // An HTML tag should not contain any special characters. Escape them to
85     // ensure this cannot be abused.
86     $escaped_tag = HtmlUtility::escape($element['#tag']);
87     $markup = '<' . $escaped_tag . $attributes;
88     // Construct a void element.
89     if (in_array($element['#tag'], self::$voidElements)) {
90       $markup .= " />\n";
91     }
92     // Construct all other elements.
93     else {
94       $markup .= '>';
95       $markup .= $element['#value'] instanceof MarkupInterface ? $element['#value'] : Xss::filterAdmin($element['#value']);
96       $markup .= '</' . $escaped_tag . ">\n";
97     }
98     if (!empty($element['#noscript'])) {
99       $markup = "<noscript>$markup</noscript>";
100     }
101     $element['#markup'] = Markup::create($markup);
102     return $element;
103   }
104
105   /**
106    * Pre-render callback: Renders #browsers into #prefix and #suffix.
107    *
108    * @param array $element
109    *   A render array with a '#browsers' property. The '#browsers' property can
110    *   contain any or all of the following keys:
111    *   - 'IE': If FALSE, the element is not rendered by Internet Explorer. If
112    *     TRUE, the element is rendered by Internet Explorer. Can also be a string
113    *     containing an expression for Internet Explorer to evaluate as part of a
114    *     conditional comment. For example, this can be set to 'lt IE 7' for the
115    *     element to be rendered in Internet Explorer 6, but not in Internet
116    *     Explorer 7 or higher. Defaults to TRUE.
117    *   - '!IE': If FALSE, the element is not rendered by browsers other than
118    *     Internet Explorer. If TRUE, the element is rendered by those browsers.
119    *     Defaults to TRUE.
120    *   Examples:
121    *   - To render an element in all browsers, '#browsers' can be left out or set
122    *     to array('IE' => TRUE, '!IE' => TRUE).
123    *   - To render an element in Internet Explorer only, '#browsers' can be set
124    *     to array('!IE' => FALSE).
125    *   - To render an element in Internet Explorer 6 only, '#browsers' can be set
126    *     to array('IE' => 'lt IE 7', '!IE' => FALSE).
127    *   - To render an element in Internet Explorer 8 and higher and in all other
128    *     browsers, '#browsers' can be set to array('IE' => 'gte IE 8').
129    *
130    * @return array
131    *   The passed-in element with markup for conditional comments potentially
132    *   added to '#prefix' and '#suffix'.
133    */
134   public static function preRenderConditionalComments($element) {
135     $browsers = isset($element['#browsers']) ? $element['#browsers'] : [];
136     $browsers += [
137       'IE' => TRUE,
138       '!IE' => TRUE,
139     ];
140
141     // If rendering in all browsers, no need for conditional comments.
142     if ($browsers['IE'] === TRUE && $browsers['!IE']) {
143       return $element;
144     }
145
146     // Determine the conditional comment expression for Internet Explorer to
147     // evaluate.
148     if ($browsers['IE'] === TRUE) {
149       $expression = 'IE';
150     }
151     elseif ($browsers['IE'] === FALSE) {
152       $expression = '!IE';
153     }
154     else {
155       // The IE expression might contain some user input data.
156       $expression = Xss::filterAdmin($browsers['IE']);
157     }
158
159     // If the #prefix and #suffix properties are used, wrap them with
160     // conditional comment markup. The conditional comment expression is
161     // evaluated by Internet Explorer only. To control the rendering by other
162     // browsers, use either the "downlevel-hidden" or "downlevel-revealed"
163     // technique. See http://wikipedia.org/wiki/Conditional_comment
164     // for details.
165
166     // Ensure what we are dealing with is safe.
167     // This would be done later anyway in drupal_render().
168     $prefix = isset($element['#prefix']) ? $element['#prefix'] : '';
169     if ($prefix && !($prefix instanceof MarkupInterface)) {
170       $prefix = Xss::filterAdmin($prefix);
171     }
172     $suffix = isset($element['#suffix']) ? $element['#suffix'] : '';
173     if ($suffix && !($suffix instanceof MarkupInterface)) {
174       $suffix = Xss::filterAdmin($suffix);
175     }
176
177     // We ensured above that $expression is either a string we created or is
178     // admin XSS filtered, and that $prefix and $suffix are also admin XSS
179     // filtered if they are unsafe. Thus, all these strings are safe.
180     if (!$browsers['!IE']) {
181       // "downlevel-hidden".
182       $element['#prefix'] = Markup::create("\n<!--[if $expression]>\n" . $prefix);
183       $element['#suffix'] = Markup::create($suffix . "<![endif]-->\n");
184     }
185     else {
186       // "downlevel-revealed".
187       $element['#prefix'] = Markup::create("\n<!--[if $expression]><!-->\n" . $prefix);
188       $element['#suffix'] = Markup::create($suffix . "<!--<![endif]-->\n");
189     }
190
191     return $element;
192   }
193
194 }