MediaWiki master
Xml.php
Go to the documentation of this file.
1<?php
29
33class Xml {
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
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
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
115 public static function openElement( $element, $attribs = null ) {
116 return '<' . $element . self::expandAttributes( $attribs ) . '>';
117 }
118
124 public static function closeElement( $element ) {
125 return "</$element>";
126 }
127
141 public static function tags( $element, $attribs, $contents ) {
142 return self::openElement( $element, $attribs ) . $contents . "</$element>";
143 }
144
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
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
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
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
275 public static function span( $text, $class, $attribs = [] ) {
276 return self::element( 'span', [ 'class' => $class ] + $attribs, $text );
277 }
278
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
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
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
346 public static function attrib( $name, $present = true ) {
347 return $present ? [ $name => $name ] : [];
348 }
349
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
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
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
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
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
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
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
499 public static function submitButton( $value, $attribs = [] ) {
500 $attribs += [
501 'type' => 'submit',
502 'value' => $value,
503 ];
504 return Html::element( 'input', $attribs );
505 }
506
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
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
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
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
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
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
710 public static function encodeJsVar( $value, $pretty = false ) {
711 return Html::encodeJsVar( $value, $pretty );
712 }
713
730 public static function encodeJsCall( $name, $args, $pretty = false ) {
731 return Html::encodeJsCall( $name, $args, $pretty );
732 }
733
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
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
790 public static function escapeTagsOnly( $in ) {
791 return str_replace(
792 [ '"', '>', '<' ],
793 [ '&quot;', '&gt;', '&lt;' ],
794 $in );
795 }
796
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
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
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}
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(!defined( 'MW_NO_SESSION') &&MW_ENTRY_POINT !=='cli' $wgLang
Definition Setup.php:536
array $params
The job parameters.
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
A service that provides utilities to do with language names and codes.
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:46
Library for creating and parsing MW-style timestamps.
Class for generating HTML <select> or <datalist> elements.
Definition XmlSelect.php:28
Module of static functions for generating XML.
Definition Xml.php:33
static password( $name, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML password input field.
Definition Xml.php:329
static closeElement( $element)
Shortcut to close an XML element.
Definition Xml.php:124
static textarea( $name, $content, $cols=40, $rows=5, $attribs=[])
Shortcut for creating textareas.
Definition Xml.php:684
static inputLabelSep( $label, $name, $id, $size=false, $value=false, $attribs=[])
Same as Xml::inputLabel() but return input and label in an array.
Definition Xml.php:442
static encodeJsVar( $value, $pretty=false)
Encode a variable of arbitrary type to JavaScript.
Definition Xml.php:710
static isWellFormedXmlFragment( $text)
Check if a string is a well-formed XML fragment.
Definition Xml.php:773
static check( $name, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox.
Definition Xml.php:359
static dateMenu( $year, $month)
Definition Xml.php:183
static buildForm( $fields, $submitLabel=null, $submitAttribs=[])
Generate a form (without the opening form element).
Definition Xml.php:810
static listDropdownOptionsOoui( $options)
Convert options for a drop-down box into a format accepted by OOUI\DropdownInputWidget etc.
Definition Xml.php:628
static listDropdown( $name='', $list='', $other='', $selected='', $class='', $tabindex=null)
Build a drop-down box from a textual list.
Definition Xml.php:543
static attrib( $name, $present=true)
Internal function for use in checkboxes and radio buttons and such.
Definition Xml.php:346
static label( $label, $id, $attribs=[])
Convenience function to build an HTML form label.
Definition Xml.php:398
static openElement( $element, $attribs=null)
This opens an XML element.
Definition Xml.php:115
static input( $name, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML text input field.
Definition Xml.php:305
static wrapClass( $text, $class, $tag='span', $attribs=[])
Shortcut to make a specific element with a class attribute.
Definition Xml.php:290
static buildTable( $rows, $attribs=[], $headers=null)
Definition Xml.php:850
static listDropdownOptions( $list, $params=[])
Build options for a drop-down box from a textual list.
Definition Xml.php:576
static submitButton( $value, $attribs=[])
Convenience function to build an HTML submit button.
Definition Xml.php:499
static option( $text, $value=null, $selected=false, $attribs=[])
Convenience function to build an HTML drop-down list item.
Definition Xml.php:517
static checkLabel( $label, $name, $id, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox with a label.
Definition Xml.php:463
static inputLabel( $label, $name, $id, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML text input field with a label.
Definition Xml.php:422
static languageSelector( $selected, $customisedOnly=true, $inLanguage=null, $overrideAttrs=[], Message $msg=null)
Construct a language selector appropriate for use in a form or preferences.
Definition Xml.php:223
static span( $text, $class, $attribs=[])
Shortcut to make a span element.
Definition Xml.php:275
static escapeTagsOnly( $in)
Replace " > and < with their respective HTML entities ( ", >, <)
Definition Xml.php:790
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
Definition Xml.php:141
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition Xml.php:50
static radio( $name, $value, $checked=false, $attribs=[])
Convenience function to build an HTML radio button.
Definition Xml.php:379
static buildTableRow( $attribs, $cells)
Build a row for a table.
Definition Xml.php:891
static radioLabel( $label, $name, $value, $id, $checked=false, $attribs=[])
Convenience function to build an HTML radio button with a label.
Definition Xml.php:482
static encodeJsCall( $name, $args, $pretty=false)
Create a call to a JavaScript function.
Definition Xml.php:730
static expandAttributes(?array $attribs)
Given an array of ('attributename' => 'value'), it generates the code to set the XML attributes : att...
Definition Xml.php:75
static monthSelector( $selected='', $allmonths=null, $id='month')
Create a date selector.
Definition Xml.php:156
static fieldset( $legend=false, $content=false, $attribs=[])
Shortcut for creating fieldsets.
Definition Xml.php:657
static elementClean( $element, $attribs=[], $contents='')
Format an XML element as with self::element(), but run text through the content language's normalize(...
Definition Xml.php:97
$header