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