diff options
Diffstat (limited to 'includes/Xml.php')
-rw-r--r-- | includes/Xml.php | 221 |
1 files changed, 109 insertions, 112 deletions
diff --git a/includes/Xml.php b/includes/Xml.php index 57a4fcf6..ac0539d1 100644 --- a/includes/Xml.php +++ b/includes/Xml.php @@ -38,13 +38,13 @@ class Xml { */ public static function element( $element, $attribs = null, $contents = '', $allowShortTag = true ) { $out = '<' . $element; - if( !is_null( $attribs ) ) { + if ( !is_null( $attribs ) ) { $out .= self::expandAttributes( $attribs ); } - if( is_null( $contents ) ) { + if ( is_null( $contents ) ) { $out .= '>'; } else { - if( $allowShortTag && $contents === '' ) { + if ( $allowShortTag && $contents === '' ) { $out .= ' />'; } else { $out .= '>' . htmlspecialchars( $contents ) . "</$element>"; @@ -64,10 +64,10 @@ class Xml { */ public static function expandAttributes( $attribs ) { $out = ''; - if( is_null( $attribs ) ) { + if ( is_null( $attribs ) ) { return null; - } elseif( is_array( $attribs ) ) { - foreach( $attribs as $name => $val ) { + } elseif ( is_array( $attribs ) ) { + foreach ( $attribs as $name => $val ) { $out .= " {$name}=\"" . Sanitizer::encodeAttribute( $val ) . '"'; } return $out; @@ -88,10 +88,10 @@ class Xml { */ public static function elementClean( $element, $attribs = array(), $contents = '' ) { global $wgContLang; - if( $attribs ) { + if ( $attribs ) { $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs ); } - if( $contents ) { + if ( $contents ) { wfProfileIn( __METHOD__ . '-norm' ); $contents = $wgContLang->normalize( $contents ); wfProfileOut( __METHOD__ . '-norm' ); @@ -115,7 +115,9 @@ class Xml { * @param string $element element name * @return string */ - public static function closeElement( $element ) { return "</$element>"; } + public static function closeElement( $element ) { + return "</$element>"; + } /** * Same as Xml::element(), but does not escape contents. Handy when the @@ -144,11 +146,11 @@ class Xml { wfDeprecated( __METHOD__, '1.19' ); return Html::namespaceSelector( array( 'selected' => $selected, - 'all' => $all, - 'label' => $label, + 'all' => $all, + 'label' => $label, ), array( - 'name' => $element_name, - 'id' => 'namespace', + 'name' => $element_name, + 'id' => 'namespace', 'class' => 'namespaceselector', ) ); } @@ -164,12 +166,15 @@ class Xml { public static function monthSelector( $selected = '', $allmonths = null, $id = 'month' ) { global $wgLang; $options = array(); - if( is_null( $selected ) ) + if ( is_null( $selected ) ) { $selected = ''; - if( !is_null( $allmonths ) ) + } + if ( !is_null( $allmonths ) ) { $options[] = self::option( wfMessage( 'monthsall' )->text(), $allmonths, $selected === $allmonths ); - for( $i = 1; $i < 13; $i++ ) + } + for ( $i = 1; $i < 13; $i++ ) { $options[] = self::option( $wgLang->getMonthName( $i ), $i, $selected === $i ); + } return self::openElement( 'select', array( 'id' => $id, 'name' => 'month', 'class' => 'mw-month-selector' ) ) . implode( "\n", $options ) . self::closeElement( 'select' ); @@ -182,17 +187,18 @@ class Xml { */ public static function dateMenu( $year, $month ) { # Offset overrides year/month selection - if( $month && $month !== -1 ) { + if ( $month && $month !== -1 ) { $encMonth = intval( $month ); } else { $encMonth = ''; } - if( $year ) { + if ( $year ) { $encYear = intval( $year ); - } elseif( $encMonth ) { - $thisMonth = intval( gmdate( 'n' ) ); - $thisYear = intval( gmdate( 'Y' ) ); - if( intval( $encMonth ) > $thisMonth ) { + } elseif ( $encMonth ) { + $timestamp = MWTimestamp::getInstance(); + $thisMonth = intval( $timestamp->format( 'n' ) ); + $thisYear = intval( $timestamp->format( 'Y' ) ); + if ( intval( $encMonth ) > $thisMonth ) { $thisYear--; } $encYear = $thisYear; @@ -200,9 +206,9 @@ class Xml { $encYear = ''; } $inputAttribs = array( 'id' => 'year', 'maxlength' => 4, 'size' => 7 ); - return self::label( wfMessage( 'year' )->text(), 'year' ) . ' '. - Html::input( 'year', $encYear, 'number', $inputAttribs ) . ' '. - self::label( wfMessage( 'month' )->text(), 'month' ) . ' '. + return self::label( wfMessage( 'year' )->text(), 'year' ) . ' ' . + Html::input( 'year', $encYear, 'number', $inputAttribs ) . ' ' . + self::label( wfMessage( 'month' )->text(), 'month' ) . ' ' . self::monthSelector( $encMonth, -1 ); } @@ -224,7 +230,7 @@ class Xml { // Make sure the site language is in the list; // a custom language code might not have a defined name... - if( !array_key_exists( $wgLanguageCode, $languages ) ) { + if ( !array_key_exists( $wgLanguageCode, $languages ) ) { $languages[$wgLanguageCode] = $wgLanguageCode; } @@ -237,14 +243,14 @@ class Xml { */ $selected = isset( $languages[$selected] ) ? $selected : $wgLanguageCode; $options = "\n"; - foreach( $languages as $code => $name ) { - $options .= Xml::option( "$code - $name", $code, ($code == $selected) ) . "\n"; + foreach ( $languages as $code => $name ) { + $options .= Xml::option( "$code - $name", $code, $code == $selected ) . "\n"; } $attrs = array( 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' ); $attrs = array_merge( $attrs, $overrideAttrs ); - if( $msg === null ) { + if ( $msg === null ) { $msg = wfMessage( 'yourlanguage' ); } return array( @@ -288,11 +294,11 @@ class Xml { public static function input( $name, $size = false, $value = false, $attribs = array() ) { $attributes = array( 'name' => $name ); - if( $size ) { + if ( $size ) { $attributes['size'] = $size; } - if( $value !== false ) { // maybe 0 + if ( $value !== false ) { // maybe 0 $attributes['value'] = $value; } @@ -330,7 +336,7 @@ class Xml { * @param array $attribs other attributes * @return string HTML */ - public static function check( $name, $checked = false, $attribs=array() ) { + public static function check( $name, $checked = false, $attribs = array() ) { return self::element( 'input', array_merge( array( 'name' => $name, @@ -369,10 +375,10 @@ class Xml { $a = array( 'for' => $id ); # FIXME avoid copy pasting below: - if( isset( $attribs['class'] ) ) { + if ( isset( $attribs['class'] ) ) { $a['class'] = $attribs['class']; } - if( isset( $attribs['title'] ) ) { + if ( isset( $attribs['title'] ) ) { $a['title'] = $attribs['title']; } @@ -389,7 +395,7 @@ class Xml { * @param array $attribs other attributes * @return string HTML */ - public static function inputLabel( $label, $name, $id, $size=false, $value=false, $attribs = array() ) { + public static function inputLabel( $label, $name, $id, $size = false, $value = false, $attribs = array() ) { list( $label, $input ) = self::inputLabelSep( $label, $name, $id, $size, $value, $attribs ); return $label . ' ' . $input; } @@ -460,18 +466,18 @@ class Xml { /** * Convenience function to build an HTML drop-down list item. - * @param string $text text for this item + * @param string $text text for this item. Will be HTML escaped * @param string $value form submission value; if empty, use text * @param $selected boolean: if true, will be the default selected item * @param array $attribs optional additional HTML attributes * @return string HTML */ - public static function option( $text, $value=null, $selected = false, + public static function option( $text, $value = null, $selected = false, $attribs = array() ) { - if( !is_null( $value ) ) { + if ( !is_null( $value ) ) { $attribs['value'] = $value; } - if( $selected ) { + if ( $selected ) { $attribs['selected'] = 'selected'; } return Html::element( 'option', $attribs, $text ); @@ -488,47 +494,53 @@ class Xml { * @param $tabindex Mixed: Value of the tabindex attribute * @return string */ - public static function listDropDown( $name= '', $list = '', $other = '', $selected = '', $class = '', $tabindex = null ) { + public static function listDropDown( $name = '', $list = '', $other = '', $selected = '', $class = '', $tabindex = null ) { $optgroup = false; $options = self::option( $other, 'other', $selected === 'other' ); - foreach ( explode( "\n", $list ) as $option) { + foreach ( explode( "\n", $list ) as $option ) { $value = trim( $option ); if ( $value == '' ) { continue; - } elseif ( substr( $value, 0, 1) == '*' && substr( $value, 1, 1) != '*' ) { + } elseif ( substr( $value, 0, 1 ) == '*' && substr( $value, 1, 1 ) != '*' ) { // A new group is starting ... $value = trim( substr( $value, 1 ) ); - if( $optgroup ) $options .= self::closeElement( 'optgroup' ); + if ( $optgroup ) { + $options .= self::closeElement( 'optgroup' ); + } $options .= self::openElement( 'optgroup', array( 'label' => $value ) ); $optgroup = true; - } elseif ( substr( $value, 0, 2) == '**' ) { + } elseif ( substr( $value, 0, 2 ) == '**' ) { // groupmember $value = trim( substr( $value, 2 ) ); $options .= self::option( $value, $value, $selected === $value ); } else { // groupless reason list - if( $optgroup ) $options .= self::closeElement( 'optgroup' ); + if ( $optgroup ) { + $options .= self::closeElement( 'optgroup' ); + } $options .= self::option( $value, $value, $selected === $value ); $optgroup = false; } } - if( $optgroup ) $options .= self::closeElement( 'optgroup' ); + if ( $optgroup ) { + $options .= self::closeElement( 'optgroup' ); + } $attribs = array(); - if( $name ) { + if ( $name ) { $attribs['id'] = $name; $attribs['name'] = $name; } - if( $class ) { + if ( $class ) { $attribs['class'] = $class; } - if( $tabindex ) { + if ( $tabindex ) { $attribs['tabindex'] = $tabindex; } @@ -590,6 +602,7 @@ class Xml { * for JavaScript source code. * Illegal control characters are assumed not to be present. * + * @deprecated since 1.21; use Xml::encodeJsVar() or Xml::encodeJsCall() instead * @param string $string to escape * @return String */ @@ -621,72 +634,46 @@ class Xml { } /** - * Encode a variable of unknown type to JavaScript. - * Arrays are converted to JS arrays, objects are converted to JS associative - * arrays (objects). So cast your PHP associative arrays to objects before - * passing them to here. + * Encode a variable of arbitrary type to JavaScript. + * If the value is an XmlJsCode object, pass through the object's value verbatim. * - * @param $value + * @note Only use this function for generating JavaScript code. If generating output + * for a proper JSON parser, just call FormatJson::encode() directly. * - * @return string + * @param mixed $value The value being encoded. Can be any type except a resource. + * @param bool $pretty If true, add non-significant whitespace to improve readability. + * @return string|bool: String if successful; false upon failure */ - public static function encodeJsVar( $value ) { - if ( is_bool( $value ) ) { - $s = $value ? 'true' : 'false'; - } elseif ( is_null( $value ) ) { - $s = 'null'; - } elseif ( is_int( $value ) || is_float( $value ) ) { - $s = strval( $value ); - } elseif ( is_array( $value ) && // Make sure it's not associative. - array_keys( $value ) === range( 0, count( $value ) - 1 ) || - count( $value ) == 0 - ) { - $s = '['; - foreach ( $value as $elt ) { - if ( $s != '[' ) { - $s .= ','; - } - $s .= self::encodeJsVar( $elt ); - } - $s .= ']'; - } elseif ( $value instanceof XmlJsCode ) { - $s = $value->value; - } elseif ( is_object( $value ) || is_array( $value ) ) { - // Objects and associative arrays - $s = '{'; - foreach ( (array)$value as $name => $elt ) { - if ( $s != '{' ) { - $s .= ','; - } - - $s .= '"' . self::escapeJsString( $name ) . '":' . - self::encodeJsVar( $elt ); - } - $s .= '}'; - } else { - $s = '"' . self::escapeJsString( $value ) . '"'; + public static function encodeJsVar( $value, $pretty = false ) { + if ( $value instanceof XmlJsCode ) { + return $value->value; } - return $s; + return FormatJson::encode( $value, $pretty, FormatJson::UTF8_OK ); } /** * Create a call to a JavaScript function. The supplied arguments will be * encoded using Xml::encodeJsVar(). * + * @since 1.17 * @param string $name The name of the function to call, or a JavaScript expression * which evaluates to a function object which is called. - * @param array $args of arguments to pass to the function. - * - * @since 1.17 - * - * @return string + * @param array $args The arguments to pass to the function. + * @param bool $pretty If true, add non-significant whitespace to improve readability. + * @return string|bool: String if successful; false upon failure */ - public static function encodeJsCall( $name, $args ) { + public static function encodeJsCall( $name, $args, $pretty = false ) { foreach ( $args as &$arg ) { - $arg = Xml::encodeJsVar( $arg ); + $arg = Xml::encodeJsVar( $arg, $pretty ); + if ( $arg === false ) { + return false; + } } - return "$name(" . implode( ', ', $args ) . ");\n"; + return "$name(" . ( $pretty + ? ( ' ' . implode( ', ', $args ) . ' ' ) + : implode( ',', $args ) + ) . ");"; } /** @@ -704,7 +691,7 @@ class Xml { # case folding violates XML standard, turn it off xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); - if( !xml_parse( $parser, $text, true ) ) { + if ( !xml_parse( $parser, $text, true ) ) { //$err = xml_error_string( xml_get_error_code( $parser ) ); //$position = xml_get_current_byte_index( $parser ); //$fragment = $this->extractFragment( $html, $position ); @@ -753,26 +740,32 @@ class Xml { /** * Generate a form (without the opening form element). * Output optionally includes a submit button. - * @param array $fields Associative array, key is message corresponding to a description for the field (colon is in the message), value is appropriate input. - * @param string $submitLabel A message containing a label for the submit button. + * @param array $fields Associative array, key is the name of a message that contains a description for the field, value is an HTML string containing the appropriate input. + * @param string $submitLabel The name of a message containing a label for the submit button. + * @param array $submitAttribs The attributes to add to the submit button * @return string HTML form. */ - public static function buildForm( $fields, $submitLabel = null ) { + public static function buildForm( $fields, $submitLabel = null, $submitAttribs = array() ) { $form = ''; $form .= "<table><tbody>"; - foreach( $fields as $labelmsg => $input ) { + foreach ( $fields as $labelmsg => $input ) { $id = "mw-$labelmsg"; $form .= Xml::openElement( 'tr', array( 'id' => $id ) ); + + // TODO use a <label> here for accessibility purposes - will need + // to either not use a table to build the form, or find the ID of + // the input somehow. + $form .= Xml::tags( 'td', array( 'class' => 'mw-label' ), wfMessage( $labelmsg )->parse() ); $form .= Xml::openElement( 'td', array( 'class' => 'mw-input' ) ) . $input . Xml::closeElement( 'td' ); $form .= Xml::closeElement( 'tr' ); } - if( $submitLabel ) { + if ( $submitLabel ) { $form .= Xml::openElement( 'tr' ); $form .= Xml::tags( 'td', array(), '' ); - $form .= Xml::openElement( 'td', array( 'class' => 'mw-submit' ) ) . Xml::submitButton( wfMessage( $submitLabel )->text() ) . Xml::closeElement( 'td' ); + $form .= Xml::openElement( 'td', array( 'class' => 'mw-submit' ) ) . Xml::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs ) . Xml::closeElement( 'td' ); $form .= Xml::closeElement( 'tr' ); } @@ -794,7 +787,7 @@ class Xml { if ( is_array( $headers ) ) { $s .= Xml::openElement( 'thead', $attribs ); - foreach( $headers as $id => $header ) { + foreach ( $headers as $id => $header ) { $attribs = array(); if ( is_string( $id ) ) { @@ -806,7 +799,7 @@ class Xml { $s .= Xml::closeElement( 'thead' ); } - foreach( $rows as $id => $row ) { + foreach ( $rows as $id => $row ) { $attribs = array(); if ( is_string( $id ) ) { @@ -830,8 +823,7 @@ class Xml { public static function buildTableRow( $attribs, $cells ) { $s = Xml::openElement( 'tr', $attribs ); - foreach( $cells as $id => $cell ) { - + foreach ( $cells as $id => $cell ) { $attribs = array(); if ( is_string( $id ) ) { @@ -899,7 +891,7 @@ class XmlSelect { */ public function addOption( $name, $value = false ) { // Stab stab stab - $value = ($value !== false) ? $value : $name; + $value = $value !== false ? $value : $name; $this->options[] = array( $name => $value ); } @@ -927,7 +919,7 @@ class XmlSelect { static function formatOptions( $options, $default = false ) { $data = ''; - foreach( $options as $label => $value ) { + foreach ( $options as $label => $value ) { if ( is_array( $value ) ) { $contents = self::formatOptions( $value, $default ); $data .= Html::rawElement( 'optgroup', array( 'label' => $label ), $contents ) . "\n"; @@ -963,6 +955,11 @@ class XmlSelect { * Xml::encodeJsVar( new XmlJsCode( 'a + b' ) ); * * Returns "a + b". + * + * @note As of 1.21, XmlJsCode objects cannot be nested inside objects or arrays. The sole + * exception is the $args argument to Xml::encodeJsCall() because Xml::encodeJsVar() is + * called for each individual element in that array. + * * @since 1.17 */ class XmlJsCode { |