This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Methods to generate XML. |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation; either version 2 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License along |
||
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
18 | * http://www.gnu.org/copyleft/gpl.html |
||
19 | * |
||
20 | * @file |
||
21 | */ |
||
22 | |||
23 | /** |
||
24 | * Module of static functions for generating XML |
||
25 | */ |
||
26 | class Xml { |
||
27 | /** |
||
28 | * Format an XML element with given attributes and, optionally, text content. |
||
29 | * Element and attribute names are assumed to be ready for literal inclusion. |
||
30 | * Strings are assumed to not contain XML-illegal characters; special |
||
31 | * characters (<, >, &) are escaped but illegals are not touched. |
||
32 | * |
||
33 | * @param string $element Element name |
||
34 | * @param array $attribs Name=>value pairs. Values will be escaped. |
||
35 | * @param string $contents Null to make an open tag only; '' for a contentless closed tag (default) |
||
36 | * @param bool $allowShortTag Whether '' in $contents will result in a contentless closed tag |
||
37 | * @return string |
||
38 | */ |
||
39 | public static function element( $element, $attribs = null, $contents = '', |
||
40 | $allowShortTag = true |
||
41 | ) { |
||
42 | $out = '<' . $element; |
||
43 | if ( !is_null( $attribs ) ) { |
||
44 | $out .= self::expandAttributes( $attribs ); |
||
45 | } |
||
46 | if ( is_null( $contents ) ) { |
||
47 | $out .= '>'; |
||
48 | } else { |
||
49 | if ( $allowShortTag && $contents === '' ) { |
||
50 | $out .= ' />'; |
||
51 | } else { |
||
52 | $out .= '>' . htmlspecialchars( $contents ) . "</$element>"; |
||
53 | } |
||
54 | } |
||
55 | return $out; |
||
56 | } |
||
57 | |||
58 | /** |
||
59 | * Given an array of ('attributename' => 'value'), it generates the code |
||
60 | * to set the XML attributes : attributename="value". |
||
61 | * The values are passed to Sanitizer::encodeAttribute. |
||
62 | * Return null if no attributes given. |
||
63 | * @param array $attribs Array of attributes for an XML element |
||
64 | * @throws MWException |
||
65 | * @return null|string |
||
66 | */ |
||
67 | public static function expandAttributes( $attribs ) { |
||
68 | $out = ''; |
||
69 | if ( is_null( $attribs ) ) { |
||
70 | return null; |
||
71 | } elseif ( is_array( $attribs ) ) { |
||
72 | foreach ( $attribs as $name => $val ) { |
||
73 | $out .= " {$name}=\"" . Sanitizer::encodeAttribute( $val ) . '"'; |
||
74 | } |
||
75 | return $out; |
||
76 | } else { |
||
77 | throw new MWException( 'Expected attribute array, got something else in ' . __METHOD__ ); |
||
78 | } |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Format an XML element as with self::element(), but run text through the |
||
83 | * $wgContLang->normalize() validator first to ensure that no invalid UTF-8 |
||
84 | * is passed. |
||
85 | * |
||
86 | * @param string $element |
||
87 | * @param array $attribs Name=>value pairs. Values will be escaped. |
||
88 | * @param string $contents Null to make an open tag only; '' for a contentless closed tag (default) |
||
89 | * @return string |
||
90 | */ |
||
91 | public static function elementClean( $element, $attribs = [], $contents = '' ) { |
||
92 | global $wgContLang; |
||
93 | if ( $attribs ) { |
||
94 | $attribs = array_map( [ 'UtfNormal\Validator', 'cleanUp' ], $attribs ); |
||
95 | } |
||
96 | if ( $contents ) { |
||
97 | $contents = $wgContLang->normalize( $contents ); |
||
98 | } |
||
99 | return self::element( $element, $attribs, $contents ); |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * This opens an XML element |
||
104 | * |
||
105 | * @param string $element Name of the element |
||
106 | * @param array $attribs Array of attributes, see Xml::expandAttributes() |
||
107 | * @return string |
||
108 | */ |
||
109 | public static function openElement( $element, $attribs = null ) { |
||
110 | return '<' . $element . self::expandAttributes( $attribs ) . '>'; |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Shortcut to close an XML element |
||
115 | * @param string $element Element name |
||
116 | * @return string |
||
117 | */ |
||
118 | public static function closeElement( $element ) { |
||
119 | return "</$element>"; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * Same as Xml::element(), but does not escape contents. Handy when the |
||
124 | * content you have is already valid xml. |
||
125 | * |
||
126 | * @param string $element Element name |
||
127 | * @param array $attribs Array of attributes |
||
128 | * @param string $contents Content of the element |
||
129 | * @return string |
||
130 | */ |
||
131 | public static function tags( $element, $attribs = null, $contents ) { |
||
132 | return self::openElement( $element, $attribs ) . $contents . "</$element>"; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Create a date selector |
||
137 | * |
||
138 | * @param string $selected The month which should be selected, default ''. |
||
139 | * @param string $allmonths Value of a special item denoting all month. |
||
140 | * Null to not include (default). |
||
141 | * @param string $id Element identifier |
||
142 | * @return string Html string containing the month selector |
||
143 | */ |
||
144 | public static function monthSelector( $selected = '', $allmonths = null, $id = 'month' ) { |
||
145 | global $wgLang; |
||
146 | $options = []; |
||
147 | $data = new XmlSelect( 'month', $id, $selected ); |
||
148 | if ( is_null( $selected ) ) { |
||
149 | $selected = ''; |
||
0 ignored issues
–
show
|
|||
150 | } |
||
151 | if ( !is_null( $allmonths ) ) { |
||
152 | $options[wfMessage( 'monthsall' )->text()] = $allmonths; |
||
153 | } |
||
154 | for ( $i = 1; $i < 13; $i++ ) { |
||
155 | $options[$wgLang->getMonthName( $i )] = $i; |
||
156 | } |
||
157 | $data->addOptions( $options ); |
||
158 | $data->setAttribute( 'class', 'mw-month-selector' ); |
||
159 | return $data->getHTML(); |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * @param int $year |
||
164 | * @param int $month |
||
165 | * @return string Formatted HTML |
||
166 | */ |
||
167 | public static function dateMenu( $year, $month ) { |
||
168 | # Offset overrides year/month selection |
||
169 | if ( $month && $month !== -1 ) { |
||
170 | $encMonth = intval( $month ); |
||
171 | } else { |
||
172 | $encMonth = ''; |
||
173 | } |
||
174 | if ( $year ) { |
||
175 | $encYear = intval( $year ); |
||
176 | } elseif ( $encMonth ) { |
||
177 | $timestamp = MWTimestamp::getInstance(); |
||
178 | $thisMonth = intval( $timestamp->format( 'n' ) ); |
||
179 | $thisYear = intval( $timestamp->format( 'Y' ) ); |
||
180 | if ( intval( $encMonth ) > $thisMonth ) { |
||
181 | $thisYear--; |
||
182 | } |
||
183 | $encYear = $thisYear; |
||
184 | } else { |
||
185 | $encYear = ''; |
||
186 | } |
||
187 | $inputAttribs = [ 'id' => 'year', 'maxlength' => 4, 'size' => 7 ]; |
||
188 | return self::label( wfMessage( 'year' )->text(), 'year' ) . ' ' . |
||
189 | Html::input( 'year', $encYear, 'number', $inputAttribs ) . ' ' . |
||
190 | self::label( wfMessage( 'month' )->text(), 'month' ) . ' ' . |
||
191 | self::monthSelector( $encMonth, -1 ); |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * Construct a language selector appropriate for use in a form or preferences |
||
196 | * |
||
197 | * @param string $selected The language code of the selected language |
||
198 | * @param bool $customisedOnly If true only languages which have some content are listed |
||
199 | * @param string $inLanguage The ISO code of the language to display the select list in (optional) |
||
200 | * @param array $overrideAttrs Override the attributes of the select tag (since 1.20) |
||
201 | * @param Message|null $msg Label message key (since 1.20) |
||
202 | * @return array Array containing 2 items: label HTML and select list HTML |
||
203 | */ |
||
204 | public static function languageSelector( $selected, $customisedOnly = true, |
||
205 | $inLanguage = null, $overrideAttrs = [], Message $msg = null |
||
206 | ) { |
||
207 | global $wgLanguageCode; |
||
208 | |||
209 | $include = $customisedOnly ? 'mwfile' : 'mw'; |
||
210 | $languages = Language::fetchLanguageNames( $inLanguage, $include ); |
||
211 | |||
212 | // Make sure the site language is in the list; |
||
213 | // a custom language code might not have a defined name... |
||
214 | if ( !array_key_exists( $wgLanguageCode, $languages ) ) { |
||
215 | $languages[$wgLanguageCode] = $wgLanguageCode; |
||
216 | } |
||
217 | |||
218 | ksort( $languages ); |
||
219 | |||
220 | /** |
||
221 | * If a bogus value is set, default to the content language. |
||
222 | * Otherwise, no default is selected and the user ends up |
||
223 | * with Afrikaans since it's first in the list. |
||
224 | */ |
||
225 | $selected = isset( $languages[$selected] ) ? $selected : $wgLanguageCode; |
||
226 | $options = "\n"; |
||
227 | foreach ( $languages as $code => $name ) { |
||
228 | $options .= Xml::option( "$code - $name", $code, $code == $selected ) . "\n"; |
||
229 | } |
||
230 | |||
231 | $attrs = [ 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' ]; |
||
232 | $attrs = array_merge( $attrs, $overrideAttrs ); |
||
233 | |||
234 | if ( $msg === null ) { |
||
235 | $msg = wfMessage( 'yourlanguage' ); |
||
236 | } |
||
237 | return [ |
||
238 | Xml::label( $msg->text(), $attrs['id'] ), |
||
239 | Xml::tags( 'select', $attrs, $options ) |
||
240 | ]; |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * Shortcut to make a span element |
||
245 | * @param string $text Content of the element, will be escaped |
||
246 | * @param string $class Class name of the span element |
||
247 | * @param array $attribs Other attributes |
||
248 | * @return string |
||
249 | */ |
||
250 | public static function span( $text, $class, $attribs = [] ) { |
||
251 | return self::element( 'span', [ 'class' => $class ] + $attribs, $text ); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * Shortcut to make a specific element with a class attribute |
||
256 | * @param string $text Content of the element, will be escaped |
||
257 | * @param string $class Class name of the span element |
||
258 | * @param string $tag Element name |
||
259 | * @param array $attribs Other attributes |
||
260 | * @return string |
||
261 | */ |
||
262 | public static function wrapClass( $text, $class, $tag = 'span', $attribs = [] ) { |
||
263 | return self::tags( $tag, [ 'class' => $class ] + $attribs, $text ); |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Convenience function to build an HTML text input field |
||
268 | * @param string $name Value of the name attribute |
||
269 | * @param int $size Value of the size attribute |
||
270 | * @param mixed $value Value of the value attribute |
||
271 | * @param array $attribs Other attributes |
||
272 | * @return string HTML |
||
273 | */ |
||
274 | public static function input( $name, $size = false, $value = false, $attribs = [] ) { |
||
275 | $attributes = [ 'name' => $name ]; |
||
276 | |||
277 | if ( $size ) { |
||
278 | $attributes['size'] = $size; |
||
279 | } |
||
280 | |||
281 | if ( $value !== false ) { // maybe 0 |
||
282 | $attributes['value'] = $value; |
||
283 | } |
||
284 | |||
285 | return self::element( 'input', |
||
286 | Html::getTextInputAttributes( $attributes + $attribs ) ); |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * Convenience function to build an HTML password input field |
||
291 | * @param string $name Value of the name attribute |
||
292 | * @param int $size Value of the size attribute |
||
293 | * @param mixed $value Value of the value attribute |
||
294 | * @param array $attribs Other attributes |
||
295 | * @return string HTML |
||
296 | */ |
||
297 | public static function password( $name, $size = false, $value = false, |
||
298 | $attribs = [] |
||
299 | ) { |
||
300 | return self::input( $name, $size, $value, |
||
301 | array_merge( $attribs, [ 'type' => 'password' ] ) ); |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * Internal function for use in checkboxes and radio buttons and such. |
||
306 | * |
||
307 | * @param string $name |
||
308 | * @param bool $present |
||
309 | * |
||
310 | * @return array |
||
311 | */ |
||
312 | public static function attrib( $name, $present = true ) { |
||
313 | return $present ? [ $name => $name ] : []; |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Convenience function to build an HTML checkbox |
||
318 | * @param string $name Value of the name attribute |
||
319 | * @param bool $checked Whether the checkbox is checked or not |
||
320 | * @param array $attribs Array other attributes |
||
321 | * @return string HTML |
||
322 | */ |
||
323 | public static function check( $name, $checked = false, $attribs = [] ) { |
||
324 | return self::element( 'input', array_merge( |
||
325 | [ |
||
326 | 'name' => $name, |
||
327 | 'type' => 'checkbox', |
||
328 | 'value' => 1 ], |
||
329 | self::attrib( 'checked', $checked ), |
||
330 | $attribs ) ); |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * Convenience function to build an HTML radio button |
||
335 | * @param string $name Value of the name attribute |
||
336 | * @param string $value Value of the value attribute |
||
337 | * @param bool $checked Whether the checkbox is checked or not |
||
338 | * @param array $attribs Other attributes |
||
339 | * @return string HTML |
||
340 | */ |
||
341 | public static function radio( $name, $value, $checked = false, $attribs = [] ) { |
||
342 | return self::element( 'input', [ |
||
343 | 'name' => $name, |
||
344 | 'type' => 'radio', |
||
345 | 'value' => $value ] + self::attrib( 'checked', $checked ) + $attribs ); |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Convenience function to build an HTML form label |
||
350 | * @param string $label Text of the label |
||
351 | * @param string $id |
||
352 | * @param array $attribs An attribute array. This will usually be |
||
353 | * the same array as is passed to the corresponding input element, |
||
354 | * so this function will cherry-pick appropriate attributes to |
||
355 | * apply to the label as well; only class and title are applied. |
||
356 | * @return string HTML |
||
357 | */ |
||
358 | public static function label( $label, $id, $attribs = [] ) { |
||
359 | $a = [ 'for' => $id ]; |
||
360 | |||
361 | foreach ( [ 'class', 'title' ] as $attr ) { |
||
362 | if ( isset( $attribs[$attr] ) ) { |
||
363 | $a[$attr] = $attribs[$attr]; |
||
364 | } |
||
365 | } |
||
366 | |||
367 | return self::element( 'label', $a, $label ); |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * Convenience function to build an HTML text input field with a label |
||
372 | * @param string $label Text of the label |
||
373 | * @param string $name Value of the name attribute |
||
374 | * @param string $id Id of the input |
||
375 | * @param int|bool $size Value of the size attribute |
||
376 | * @param string|bool $value Value of the value attribute |
||
377 | * @param array $attribs Other attributes |
||
378 | * @return string HTML |
||
379 | */ |
||
380 | public static function inputLabel( $label, $name, $id, $size = false, |
||
381 | $value = false, $attribs = [] |
||
382 | ) { |
||
383 | list( $label, $input ) = self::inputLabelSep( $label, $name, $id, $size, $value, $attribs ); |
||
384 | return $label . ' ' . $input; |
||
385 | } |
||
386 | |||
387 | /** |
||
388 | * Same as Xml::inputLabel() but return input and label in an array |
||
389 | * |
||
390 | * @param string $label |
||
391 | * @param string $name |
||
392 | * @param string $id |
||
393 | * @param int|bool $size |
||
394 | * @param string|bool $value |
||
395 | * @param array $attribs |
||
396 | * |
||
397 | * @return array |
||
398 | */ |
||
399 | public static function inputLabelSep( $label, $name, $id, $size = false, |
||
400 | $value = false, $attribs = [] |
||
401 | ) { |
||
402 | return [ |
||
403 | Xml::label( $label, $id, $attribs ), |
||
404 | self::input( $name, $size, $value, [ 'id' => $id ] + $attribs ) |
||
405 | ]; |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * Convenience function to build an HTML checkbox with a label |
||
410 | * |
||
411 | * @param string $label |
||
412 | * @param string $name |
||
413 | * @param string $id |
||
414 | * @param bool $checked |
||
415 | * @param array $attribs |
||
416 | * |
||
417 | * @return string HTML |
||
418 | */ |
||
419 | public static function checkLabel( $label, $name, $id, $checked = false, $attribs = [] ) { |
||
420 | global $wgUseMediaWikiUIEverywhere; |
||
421 | $chkLabel = self::check( $name, $checked, [ 'id' => $id ] + $attribs ) . |
||
422 | ' ' . |
||
423 | self::label( $label, $id, $attribs ); |
||
424 | |||
425 | if ( $wgUseMediaWikiUIEverywhere ) { |
||
426 | $chkLabel = self::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) . |
||
427 | $chkLabel . self::closeElement( 'div' ); |
||
428 | } |
||
429 | return $chkLabel; |
||
430 | } |
||
431 | |||
432 | /** |
||
433 | * Convenience function to build an HTML radio button with a label |
||
434 | * |
||
435 | * @param string $label |
||
436 | * @param string $name |
||
437 | * @param string $value |
||
438 | * @param string $id |
||
439 | * @param bool $checked |
||
440 | * @param array $attribs |
||
441 | * |
||
442 | * @return string HTML |
||
443 | */ |
||
444 | public static function radioLabel( $label, $name, $value, $id, |
||
445 | $checked = false, $attribs = [] |
||
446 | ) { |
||
447 | return self::radio( $name, $value, $checked, [ 'id' => $id ] + $attribs ) . |
||
448 | ' ' . |
||
449 | self::label( $label, $id, $attribs ); |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * Convenience function to build an HTML submit button |
||
454 | * When $wgUseMediaWikiUIEverywhere is true it will default to a progressive button |
||
455 | * @param string $value Label text for the button |
||
456 | * @param array $attribs Optional custom attributes |
||
457 | * @return string HTML |
||
458 | */ |
||
459 | public static function submitButton( $value, $attribs = [] ) { |
||
460 | global $wgUseMediaWikiUIEverywhere; |
||
461 | $baseAttrs = [ |
||
462 | 'type' => 'submit', |
||
463 | 'value' => $value, |
||
464 | ]; |
||
465 | // Done conditionally for time being as it is possible |
||
466 | // some submit forms |
||
467 | // might need to be mw-ui-destructive (e.g. delete a page) |
||
468 | if ( $wgUseMediaWikiUIEverywhere ) { |
||
469 | $baseAttrs['class'] = 'mw-ui-button mw-ui-progressive'; |
||
470 | } |
||
471 | // Any custom attributes will take precendence of anything in baseAttrs e.g. override the class |
||
472 | $attribs = $attribs + $baseAttrs; |
||
473 | return Html::element( 'input', $attribs ); |
||
474 | } |
||
475 | |||
476 | /** |
||
477 | * Convenience function to build an HTML drop-down list item. |
||
478 | * @param string $text Text for this item. Will be HTML escaped |
||
479 | * @param string $value Form submission value; if empty, use text |
||
480 | * @param bool $selected If true, will be the default selected item |
||
481 | * @param array $attribs Optional additional HTML attributes |
||
482 | * @return string HTML |
||
483 | */ |
||
484 | public static function option( $text, $value = null, $selected = false, |
||
485 | $attribs = [] ) { |
||
486 | if ( !is_null( $value ) ) { |
||
487 | $attribs['value'] = $value; |
||
488 | } |
||
489 | if ( $selected ) { |
||
490 | $attribs['selected'] = 'selected'; |
||
491 | } |
||
492 | return Html::element( 'option', $attribs, $text ); |
||
493 | } |
||
494 | |||
495 | /** |
||
496 | * Build a drop-down box from a textual list. |
||
497 | * |
||
498 | * @param string $name Name and id for the drop-down |
||
499 | * @param string $list Correctly formatted text (newline delimited) to be |
||
500 | * used to generate the options. |
||
501 | * @param string $other Text for the "Other reasons" option |
||
502 | * @param string $selected Option which should be pre-selected |
||
503 | * @param string $class CSS classes for the drop-down |
||
504 | * @param int $tabindex Value of the tabindex attribute |
||
505 | * @return string |
||
506 | */ |
||
507 | public static function listDropDown( $name = '', $list = '', $other = '', |
||
508 | $selected = '', $class = '', $tabindex = null |
||
509 | ) { |
||
510 | $optgroup = false; |
||
511 | |||
512 | $options = self::option( $other, 'other', $selected === 'other' ); |
||
513 | |||
514 | foreach ( explode( "\n", $list ) as $option ) { |
||
515 | $value = trim( $option ); |
||
516 | if ( $value == '' ) { |
||
517 | continue; |
||
518 | } elseif ( substr( $value, 0, 1 ) == '*' && substr( $value, 1, 1 ) != '*' ) { |
||
519 | // A new group is starting ... |
||
520 | $value = trim( substr( $value, 1 ) ); |
||
521 | if ( $optgroup ) { |
||
522 | $options .= self::closeElement( 'optgroup' ); |
||
523 | } |
||
524 | $options .= self::openElement( 'optgroup', [ 'label' => $value ] ); |
||
525 | $optgroup = true; |
||
526 | } elseif ( substr( $value, 0, 2 ) == '**' ) { |
||
527 | // groupmember |
||
528 | $value = trim( substr( $value, 2 ) ); |
||
529 | $options .= self::option( $value, $value, $selected === $value ); |
||
530 | } else { |
||
531 | // groupless reason list |
||
532 | if ( $optgroup ) { |
||
533 | $options .= self::closeElement( 'optgroup' ); |
||
534 | } |
||
535 | $options .= self::option( $value, $value, $selected === $value ); |
||
536 | $optgroup = false; |
||
537 | } |
||
538 | } |
||
539 | |||
540 | if ( $optgroup ) { |
||
541 | $options .= self::closeElement( 'optgroup' ); |
||
542 | } |
||
543 | |||
544 | $attribs = []; |
||
545 | |||
546 | if ( $name ) { |
||
547 | $attribs['id'] = $name; |
||
548 | $attribs['name'] = $name; |
||
549 | } |
||
550 | |||
551 | if ( $class ) { |
||
552 | $attribs['class'] = $class; |
||
553 | } |
||
554 | |||
555 | if ( $tabindex ) { |
||
556 | $attribs['tabindex'] = $tabindex; |
||
557 | } |
||
558 | |||
559 | return Xml::openElement( 'select', $attribs ) |
||
560 | . "\n" |
||
561 | . $options |
||
562 | . "\n" |
||
563 | . Xml::closeElement( 'select' ); |
||
564 | } |
||
565 | |||
566 | /** |
||
567 | * Shortcut for creating fieldsets. |
||
568 | * |
||
569 | * @param string|bool $legend Legend of the fieldset. If evaluates to false, |
||
570 | * legend is not added. |
||
571 | * @param string $content Pre-escaped content for the fieldset. If false, |
||
572 | * only open fieldset is returned. |
||
573 | * @param array $attribs Any attributes to fieldset-element. |
||
574 | * |
||
575 | * @return string |
||
576 | */ |
||
577 | public static function fieldset( $legend = false, $content = false, $attribs = [] ) { |
||
578 | $s = Xml::openElement( 'fieldset', $attribs ) . "\n"; |
||
579 | |||
580 | if ( $legend ) { |
||
581 | $s .= Xml::element( 'legend', null, $legend ) . "\n"; |
||
582 | } |
||
583 | |||
584 | if ( $content !== false ) { |
||
585 | $s .= $content . "\n"; |
||
586 | $s .= Xml::closeElement( 'fieldset' ) . "\n"; |
||
587 | } |
||
588 | |||
589 | return $s; |
||
590 | } |
||
591 | |||
592 | /** |
||
593 | * Shortcut for creating textareas. |
||
594 | * |
||
595 | * @param string $name The 'name' for the textarea |
||
596 | * @param string $content Content for the textarea |
||
597 | * @param int $cols The number of columns for the textarea |
||
598 | * @param int $rows The number of rows for the textarea |
||
599 | * @param array $attribs Any other attributes for the textarea |
||
600 | * |
||
601 | * @return string |
||
602 | */ |
||
603 | public static function textarea( $name, $content, $cols = 40, $rows = 5, $attribs = [] ) { |
||
604 | return self::element( 'textarea', |
||
605 | Html::getTextInputAttributes( |
||
606 | [ |
||
607 | 'name' => $name, |
||
608 | 'id' => $name, |
||
609 | 'cols' => $cols, |
||
610 | 'rows' => $rows |
||
611 | ] + $attribs |
||
612 | ), |
||
613 | $content, false ); |
||
614 | } |
||
615 | |||
616 | /** |
||
617 | * Returns an escaped string suitable for inclusion in a string literal |
||
618 | * for JavaScript source code. |
||
619 | * Illegal control characters are assumed not to be present. |
||
620 | * |
||
621 | * @deprecated since 1.21; use Xml::encodeJsVar() or Xml::encodeJsCall() instead |
||
622 | * @param string $string String to escape |
||
623 | * @return string |
||
624 | */ |
||
625 | public static function escapeJsString( $string ) { |
||
626 | // See ECMA 262 section 7.8.4 for string literal format |
||
627 | $pairs = [ |
||
628 | "\\" => "\\\\", |
||
629 | "\"" => "\\\"", |
||
630 | '\'' => '\\\'', |
||
631 | "\n" => "\\n", |
||
632 | "\r" => "\\r", |
||
633 | |||
634 | # To avoid closing the element or CDATA section |
||
635 | "<" => "\\x3c", |
||
636 | ">" => "\\x3e", |
||
637 | |||
638 | # To avoid any complaints about bad entity refs |
||
639 | "&" => "\\x26", |
||
640 | |||
641 | # Work around https://bugzilla.mozilla.org/show_bug.cgi?id=274152 |
||
642 | # Encode certain Unicode formatting chars so affected |
||
643 | # versions of Gecko don't misinterpret our strings; |
||
644 | # this is a common problem with Farsi text. |
||
645 | "\xe2\x80\x8c" => "\\u200c", // ZERO WIDTH NON-JOINER |
||
646 | "\xe2\x80\x8d" => "\\u200d", // ZERO WIDTH JOINER |
||
647 | ]; |
||
648 | |||
649 | return strtr( $string, $pairs ); |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Encode a variable of arbitrary type to JavaScript. |
||
654 | * If the value is an XmlJsCode object, pass through the object's value verbatim. |
||
655 | * |
||
656 | * @note Only use this function for generating JavaScript code. If generating output |
||
657 | * for a proper JSON parser, just call FormatJson::encode() directly. |
||
658 | * |
||
659 | * @param mixed $value The value being encoded. Can be any type except a resource. |
||
660 | * @param bool $pretty If true, add non-significant whitespace to improve readability. |
||
661 | * @return string|bool String if successful; false upon failure |
||
662 | */ |
||
663 | public static function encodeJsVar( $value, $pretty = false ) { |
||
664 | if ( $value instanceof XmlJsCode ) { |
||
665 | return $value->value; |
||
666 | } |
||
667 | return FormatJson::encode( $value, $pretty, FormatJson::UTF8_OK ); |
||
668 | } |
||
669 | |||
670 | /** |
||
671 | * Create a call to a JavaScript function. The supplied arguments will be |
||
672 | * encoded using Xml::encodeJsVar(). |
||
673 | * |
||
674 | * @since 1.17 |
||
675 | * @param string $name The name of the function to call, or a JavaScript expression |
||
676 | * which evaluates to a function object which is called. |
||
677 | * @param array $args The arguments to pass to the function. |
||
678 | * @param bool $pretty If true, add non-significant whitespace to improve readability. |
||
679 | * @return string|bool String if successful; false upon failure |
||
680 | */ |
||
681 | public static function encodeJsCall( $name, $args, $pretty = false ) { |
||
682 | foreach ( $args as &$arg ) { |
||
683 | $arg = Xml::encodeJsVar( $arg, $pretty ); |
||
684 | if ( $arg === false ) { |
||
685 | return false; |
||
686 | } |
||
687 | } |
||
688 | |||
689 | return "$name(" . ( $pretty |
||
690 | ? ( ' ' . implode( ', ', $args ) . ' ' ) |
||
691 | : implode( ',', $args ) |
||
692 | ) . ");"; |
||
693 | } |
||
694 | |||
695 | /** |
||
696 | * Check if a string is well-formed XML. |
||
697 | * Must include the surrounding tag. |
||
698 | * This function is a DoS vector if an attacker can define |
||
699 | * entities in $text. |
||
700 | * |
||
701 | * @param string $text String to test. |
||
702 | * @return bool |
||
703 | * |
||
704 | * @todo Error position reporting return |
||
705 | */ |
||
706 | private static function isWellFormed( $text ) { |
||
707 | $parser = xml_parser_create( "UTF-8" ); |
||
708 | |||
709 | # case folding violates XML standard, turn it off |
||
710 | xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); |
||
711 | |||
712 | if ( !xml_parse( $parser, $text, true ) ) { |
||
713 | // $err = xml_error_string( xml_get_error_code( $parser ) ); |
||
714 | // $position = xml_get_current_byte_index( $parser ); |
||
715 | // $fragment = $this->extractFragment( $html, $position ); |
||
716 | // $this->mXmlError = "$err at byte $position:\n$fragment"; |
||
717 | xml_parser_free( $parser ); |
||
718 | return false; |
||
719 | } |
||
720 | |||
721 | xml_parser_free( $parser ); |
||
722 | |||
723 | return true; |
||
724 | } |
||
725 | |||
726 | /** |
||
727 | * Check if a string is a well-formed XML fragment. |
||
728 | * Wraps fragment in an \<html\> bit and doctype, so it can be a fragment |
||
729 | * and can use HTML named entities. |
||
730 | * |
||
731 | * @param string $text |
||
732 | * @return bool |
||
733 | */ |
||
734 | public static function isWellFormedXmlFragment( $text ) { |
||
735 | $html = |
||
736 | Sanitizer::hackDocType() . |
||
737 | '<html>' . |
||
738 | $text . |
||
739 | '</html>'; |
||
740 | |||
741 | return Xml::isWellFormed( $html ); |
||
742 | } |
||
743 | |||
744 | /** |
||
745 | * Replace " > and < with their respective HTML entities ( ", |
||
746 | * >, <) |
||
747 | * |
||
748 | * @param string $in Text that might contain HTML tags. |
||
749 | * @return string Escaped string |
||
750 | */ |
||
751 | public static function escapeTagsOnly( $in ) { |
||
752 | return str_replace( |
||
753 | [ '"', '>', '<' ], |
||
754 | [ '"', '>', '<' ], |
||
755 | $in ); |
||
756 | } |
||
757 | |||
758 | /** |
||
759 | * Generate a form (without the opening form element). |
||
760 | * Output optionally includes a submit button. |
||
761 | * @param array $fields Associative array, key is the name of a message that |
||
762 | * contains a description for the field, value is an HTML string |
||
763 | * containing the appropriate input. |
||
764 | * @param string $submitLabel The name of a message containing a label for |
||
765 | * the submit button. |
||
766 | * @param array $submitAttribs The attributes to add to the submit button |
||
767 | * @return string HTML form. |
||
768 | */ |
||
769 | public static function buildForm( $fields, $submitLabel = null, $submitAttribs = [] ) { |
||
770 | $form = ''; |
||
771 | $form .= "<table><tbody>"; |
||
772 | |||
773 | foreach ( $fields as $labelmsg => $input ) { |
||
774 | $id = "mw-$labelmsg"; |
||
775 | $form .= Xml::openElement( 'tr', [ 'id' => $id ] ); |
||
776 | |||
777 | // TODO use a <label> here for accessibility purposes - will need |
||
778 | // to either not use a table to build the form, or find the ID of |
||
779 | // the input somehow. |
||
780 | |||
781 | $form .= Xml::tags( 'td', [ 'class' => 'mw-label' ], wfMessage( $labelmsg )->parse() ); |
||
782 | $form .= Xml::openElement( 'td', [ 'class' => 'mw-input' ] ) |
||
783 | . $input . Xml::closeElement( 'td' ); |
||
784 | $form .= Xml::closeElement( 'tr' ); |
||
785 | } |
||
786 | |||
787 | if ( $submitLabel ) { |
||
788 | $form .= Xml::openElement( 'tr' ); |
||
789 | $form .= Xml::tags( 'td', [], '' ); |
||
790 | $form .= Xml::openElement( 'td', [ 'class' => 'mw-submit' ] ) |
||
791 | . Xml::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs ) |
||
792 | . Xml::closeElement( 'td' ); |
||
793 | $form .= Xml::closeElement( 'tr' ); |
||
794 | } |
||
795 | |||
796 | $form .= "</tbody></table>"; |
||
797 | |||
798 | return $form; |
||
799 | } |
||
800 | |||
801 | /** |
||
802 | * Build a table of data |
||
803 | * @param array $rows An array of arrays of strings, each to be a row in a table |
||
804 | * @param array $attribs An array of attributes to apply to the table tag [optional] |
||
805 | * @param array $headers An array of strings to use as table headers [optional] |
||
806 | * @return string |
||
807 | */ |
||
808 | public static function buildTable( $rows, $attribs = [], $headers = null ) { |
||
809 | $s = Xml::openElement( 'table', $attribs ); |
||
810 | |||
811 | if ( is_array( $headers ) ) { |
||
812 | $s .= Xml::openElement( 'thead', $attribs ); |
||
813 | |||
814 | foreach ( $headers as $id => $header ) { |
||
815 | $attribs = []; |
||
816 | |||
817 | if ( is_string( $id ) ) { |
||
818 | $attribs['id'] = $id; |
||
819 | } |
||
820 | |||
821 | $s .= Xml::element( 'th', $attribs, $header ); |
||
822 | } |
||
823 | $s .= Xml::closeElement( 'thead' ); |
||
824 | } |
||
825 | |||
826 | View Code Duplication | foreach ( $rows as $id => $row ) { |
|
827 | $attribs = []; |
||
828 | |||
829 | if ( is_string( $id ) ) { |
||
830 | $attribs['id'] = $id; |
||
831 | } |
||
832 | |||
833 | $s .= Xml::buildTableRow( $attribs, $row ); |
||
834 | } |
||
835 | |||
836 | $s .= Xml::closeElement( 'table' ); |
||
837 | |||
838 | return $s; |
||
839 | } |
||
840 | |||
841 | /** |
||
842 | * Build a row for a table |
||
843 | * @param array $attribs An array of attributes to apply to the tr tag |
||
844 | * @param array $cells An array of strings to put in <td> |
||
845 | * @return string |
||
846 | */ |
||
847 | public static function buildTableRow( $attribs, $cells ) { |
||
848 | $s = Xml::openElement( 'tr', $attribs ); |
||
849 | |||
850 | View Code Duplication | foreach ( $cells as $id => $cell ) { |
|
851 | $attribs = []; |
||
852 | |||
853 | if ( is_string( $id ) ) { |
||
854 | $attribs['id'] = $id; |
||
855 | } |
||
856 | |||
857 | $s .= Xml::element( 'td', $attribs, $cell ); |
||
858 | } |
||
859 | |||
860 | $s .= Xml::closeElement( 'tr' ); |
||
861 | |||
862 | return $s; |
||
863 | } |
||
864 | } |
||
865 | |||
866 | /** |
||
867 | * A wrapper class which causes Xml::encodeJsVar() and Xml::encodeJsCall() to |
||
868 | * interpret a given string as being a JavaScript expression, instead of string |
||
869 | * data. |
||
870 | * |
||
871 | * Example: |
||
872 | * |
||
873 | * Xml::encodeJsVar( new XmlJsCode( 'a + b' ) ); |
||
874 | * |
||
875 | * Returns "a + b". |
||
876 | * |
||
877 | * @note As of 1.21, XmlJsCode objects cannot be nested inside objects or arrays. The sole |
||
878 | * exception is the $args argument to Xml::encodeJsCall() because Xml::encodeJsVar() is |
||
879 | * called for each individual element in that array. |
||
880 | * |
||
881 | * @since 1.17 |
||
882 | */ |
||
883 | class XmlJsCode { |
||
884 | public $value; |
||
885 | |||
886 | function __construct( $value ) { |
||
887 | $this->value = $value; |
||
888 | } |
||
889 | } |
||
890 |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVar
assignment in line 1 and the$higher
assignment in line 2 are dead. The first because$myVar
is never used and the second because$higher
is always overwritten for every possible time line.