Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Configurator.php
001 <?php
002
003 /**
004 * @package s9e\TextFormatter
005 * @copyright Copyright (c) 2010-2022 The s9e authors
006 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
007 */
008 namespace s9e\TextFormatter\Plugins\HTMLElements;
009
010 use InvalidArgumentException;
011 use RuntimeException;
012 use s9e\TextFormatter\Configurator\Helpers\RegexpBuilder;
013 use s9e\TextFormatter\Configurator\Items\Tag;
014 use s9e\TextFormatter\Configurator\Items\UnsafeTemplate;
015 use s9e\TextFormatter\Configurator\JavaScript\Dictionary;
016 use s9e\TextFormatter\Configurator\Validators\AttributeName;
017 use s9e\TextFormatter\Configurator\Validators\TagName;
018 use s9e\TextFormatter\Plugins\ConfiguratorBase;
019
020 class Configurator extends ConfiguratorBase
021 {
022 /**
023 * @var array 2D array using HTML element names as keys, each value being an associative array
024 * using HTML attribute names as keys and their alias as values. A special empty entry
025 * is used to store the HTML element's alias
026 */
027 protected $aliases = [];
028
029 /**
030 * @var array Default filter of a few known attributes
031 *
032 * It doesn't make much sense to try to declare every known HTML attribute here. Validation is
033 * not the purpose of this plugin. It does make sense however to declare URL attributes as such,
034 * so that they are subject to our constraints (disallowed hosts, etc...)
035 *
036 * @see scripts/patchHTMLElementConfigurator.php
037 */
038 protected $attributeFilters = [
039 'action' => '#url',
040 'cite' => '#url',
041 'data' => '#url',
042 'formaction' => '#url',
043 'href' => '#url',
044 'icon' => '#url',
045 'itemtype' => '#url',
046 'longdesc' => '#url',
047 'manifest' => '#url',
048 'ping' => '#url',
049 'poster' => '#url',
050 'src' => '#url'
051 ];
052
053 /**
054 * @var array Hash of allowed HTML elements. Element names are lowercased and used as keys for
055 * this array
056 */
057 protected $elements = [];
058
059 /**
060 * @var string Namespace prefix of the tags produced by this plugin's parser
061 */
062 protected $prefix = 'html';
063
064 /**
065 * {@inheritdoc}
066 */
067 protected $quickMatch = '<';
068
069 /**
070 * @var array Blacklist of elements that are considered unsafe
071 */
072 protected $unsafeElements = [
073 'base',
074 'embed',
075 'frame',
076 'iframe',
077 'meta',
078 'object',
079 'script'
080 ];
081
082 /**
083 * @var array Blacklist of attributes that are considered unsafe, in addition of any attribute
084 * whose name starts with "on" such as "onmouseover"
085 */
086 protected $unsafeAttributes = [
087 'style',
088 'target'
089 ];
090
091 /**
092 * Alias the HTML attribute of given HTML element to a given attribute name
093 *
094 * NOTE: will *not* create the target attribute
095 *
096 * @param string $elName Name of the HTML element
097 * @param string $attrName Name of the HTML attribute
098 * @param string $alias Alias
099 * @return void
100 */
101 public function aliasAttribute($elName, $attrName, $alias)
102 {
103 $elName = $this->normalizeElementName($elName);
104 $attrName = $this->normalizeAttributeName($attrName);
105
106 $this->aliases[$elName][$attrName] = AttributeName::normalize($alias);
107 }
108
109 /**
110 * Alias an HTML element to a given tag name
111 *
112 * NOTE: will *not* create the target tag
113 *
114 * @param string $elName Name of the HTML element
115 * @param string $tagName Name of the tag
116 * @return void
117 */
118 public function aliasElement($elName, $tagName)
119 {
120 $elName = $this->normalizeElementName($elName);
121
122 $this->aliases[$elName][''] = TagName::normalize($tagName);
123 }
124
125 /**
126 * Allow an HTML element to be used
127 *
128 * @param string $elName Name of the element
129 * @return Tag Tag that represents this element
130 */
131 public function allowElement($elName)
132 {
133 return $this->allowElementWithSafety($elName, false);
134 }
135
136 /**
137 * Allow an unsafe HTML element to be used
138 *
139 * @param string $elName Name of the element
140 * @return Tag Tag that represents this element
141 */
142 public function allowUnsafeElement($elName)
143 {
144 return $this->allowElementWithSafety($elName, true);
145 }
146
147 /**
148 * Allow a (potentially unsafe) HTML element to be used
149 *
150 * @param string $elName Name of the element
151 * @param bool $allowUnsafe Whether to allow unsafe elements
152 * @return Tag Tag that represents this element
153 */
154 protected function allowElementWithSafety($elName, $allowUnsafe)
155 {
156 $elName = $this->normalizeElementName($elName);
157 $tagName = $this->prefix . ':' . $elName;
158
159 if (!$allowUnsafe && in_array($elName, $this->unsafeElements))
160 {
161 throw new RuntimeException("'" . $elName . "' elements are unsafe and are disabled by default. Please use " . __CLASS__ . '::allowUnsafeElement() to bypass this security measure');
162 }
163
164 // Retrieve or create the tag
165 $tag = ($this->configurator->tags->exists($tagName))
166 ? $this->configurator->tags->get($tagName)
167 : $this->configurator->tags->add($tagName);
168
169 // Rebuild this tag's template
170 $this->rebuildTemplate($tag, $elName, $allowUnsafe);
171
172 // Record the element name
173 $this->elements[$elName] = 1;
174
175 return $tag;
176 }
177
178 /**
179 * Allow an attribute to be used in an HTML element
180 *
181 * @param string $elName Name of the element
182 * @param string $attrName Name of the attribute
183 * @return \s9e\Configurator\Items\Attribute
184 */
185 public function allowAttribute($elName, $attrName)
186 {
187 return $this->allowAttributeWithSafety($elName, $attrName, false);
188 }
189
190 /**
191 * Allow an unsafe attribute to be used in an HTML element
192 *
193 * @param string $elName Name of the element
194 * @param string $attrName Name of the attribute
195 * @return \s9e\Configurator\Items\Attribute
196 */
197 public function allowUnsafeAttribute($elName, $attrName)
198 {
199 return $this->allowAttributeWithSafety($elName, $attrName, true);
200 }
201
202 /**
203 * Allow a (potentially unsafe) attribute to be used in an HTML element
204 *
205 * @param string $elName Name of the element
206 * @param string $attrName Name of the attribute
207 * @param bool $allowUnsafe
208 * @return \s9e\Configurator\Items\Attribute
209 */
210 protected function allowAttributeWithSafety($elName, $attrName, $allowUnsafe)
211 {
212 $elName = $this->normalizeElementName($elName);
213 $attrName = $this->normalizeAttributeName($attrName);
214 $tagName = $this->prefix . ':' . $elName;
215
216 if (!isset($this->elements[$elName]))
217 {
218 throw new RuntimeException("Element '" . $elName . "' has not been allowed");
219 }
220
221 if (!$allowUnsafe)
222 {
223 if (substr($attrName, 0, 2) === 'on'
224 || in_array($attrName, $this->unsafeAttributes))
225 {
226 throw new RuntimeException("'" . $attrName . "' attributes are unsafe and are disabled by default. Please use " . __CLASS__ . '::allowUnsafeAttribute() to bypass this security measure');
227 }
228 }
229
230 $tag = $this->configurator->tags->get($tagName);
231 if (!isset($tag->attributes[$attrName]))
232 {
233 $attribute = $tag->attributes->add($attrName);
234 $attribute->required = false;
235
236 if (isset($this->attributeFilters[$attrName]))
237 {
238 $filterName = $this->attributeFilters[$attrName];
239 $filter = $this->configurator->attributeFilters->get($filterName);
240
241 $attribute->filterChain->append($filter);
242 }
243 }
244
245 // Rebuild this tag's template
246 $this->rebuildTemplate($tag, $elName, $allowUnsafe);
247
248 return $tag->attributes[$attrName];
249 }
250
251 /**
252 * Validate and normalize an element name
253 *
254 * Accepts any name that would be valid, regardless of whether this element exists in HTML5.
255 * Might be slightly off as the HTML5 specs don't seem to require it to start with a letter but
256 * our implementation does.
257 *
258 * @link http://dev.w3.org/html5/spec/syntax.html#syntax-tag-name
259 *
260 * @param string $elName Original element name
261 * @return string Normalized element name, in lowercase
262 */
263 protected function normalizeElementName($elName)
264 {
265 if (!preg_match('#^[a-z][a-z0-9]*$#Di', $elName))
266 {
267 throw new InvalidArgumentException("Invalid element name '" . $elName . "'");
268 }
269
270 return strtolower($elName);
271 }
272
273 /**
274 * Validate and normalize an attribute name
275 *
276 * More restrictive than the specs but allows all HTML5 attributes and more.
277 *
278 * @param string $attrName Original attribute name
279 * @return string Normalized attribute name, in lowercase
280 */
281 protected function normalizeAttributeName($attrName)
282 {
283 if (!preg_match('#^[a-z][-\\w]*$#Di', $attrName))
284 {
285 throw new InvalidArgumentException("Invalid attribute name '" . $attrName . "'");
286 }
287
288 return strtolower($attrName);
289 }
290
291 /**
292 * Rebuild a tag's template
293 *
294 * @param Tag $tag Source tag
295 * @param string $elName Name of the HTML element created by the template
296 * @param bool $allowUnsafe Whether to allow unsafe markup
297 * @return void
298 */
299 protected function rebuildTemplate(Tag $tag, $elName, $allowUnsafe)
300 {
301 $template = '<' . $elName . '>';
302 foreach ($tag->attributes as $attrName => $attribute)
303 {
304 $template .= '<xsl:copy-of select="@' . $attrName . '"/>';
305 }
306 $template .= '<xsl:apply-templates/></' . $elName . '>';
307
308 if ($allowUnsafe)
309 {
310 $template = new UnsafeTemplate($template);
311 }
312
313 $tag->setTemplate($template);
314 }
315
316 /**
317 * Generate this plugin's config
318 *
319 * @return array|null
320 */
321 public function asConfig()
322 {
323 if (empty($this->elements) && empty($this->aliases))
324 {
325 return;
326 }
327
328 /**
329 * Regexp used to match an attributes definition (name + value if applicable)
330 *
331 * @link http://dev.w3.org/html5/spec/syntax.html#attributes-0
332 */
333 $attrRegexp = '[a-z][-a-z0-9]*(?>\\s*=\\s*(?>"[^"]*"|\'[^\']*\'|[^\\s"\'=<>`]+))?';
334 $tagRegexp = RegexpBuilder::fromList(array_merge(
335 array_keys($this->aliases),
336 array_keys($this->elements)
337 ));
338
339 $endTagRegexp = '/(' . $tagRegexp . ')';
340 $startTagRegexp = '(' . $tagRegexp . ')((?>\\s+' . $attrRegexp . ')*+)\\s*/?';
341
342 $regexp = '#<(?>' . $endTagRegexp . '|' . $startTagRegexp . ')\\s*>#i';
343
344 $config = [
345 'quickMatch' => $this->quickMatch,
346 'prefix' => $this->prefix,
347 'regexp' => $regexp
348 ];
349
350 if (!empty($this->aliases))
351 {
352 // Preserve the aliases array's keys in JavaScript
353 $config['aliases'] = new Dictionary;
354 foreach ($this->aliases as $elName => $aliases)
355 {
356 $config['aliases'][$elName] = new Dictionary($aliases);
357 }
358 }
359
360 return $config;
361 }
362
363 /**
364 * {@inheritdoc}
365 */
366 public function getJSHints()
367 {
368 return ['HTMLELEMENTS_HAS_ALIASES' => (int) !empty($this->aliases)];
369 }
370 }