[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/ -> Xml.php (source)

   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 = array(), $contents = '' ) {
  92          global $wgContLang;
  93          if ( $attribs ) {
  94              $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs );
  95          }
  96          if ( $contents ) {
  97              wfProfileIn( __METHOD__ . '-norm' );
  98              $contents = $wgContLang->normalize( $contents );
  99              wfProfileOut( __METHOD__ . '-norm' );
 100          }
 101          return self::element( $element, $attribs, $contents );
 102      }
 103  
 104      /**
 105       * This opens an XML element
 106       *
 107       * @param string $element Name of the element
 108       * @param array $attribs Array of attributes, see Xml::expandAttributes()
 109       * @return string
 110       */
 111  	public static function openElement( $element, $attribs = null ) {
 112          return '<' . $element . self::expandAttributes( $attribs ) . '>';
 113      }
 114  
 115      /**
 116       * Shortcut to close an XML element
 117       * @param string $element Element name
 118       * @return string
 119       */
 120  	public static function closeElement( $element ) {
 121          return "</$element>";
 122      }
 123  
 124      /**
 125       * Same as Xml::element(), but does not escape contents. Handy when the
 126       * content you have is already valid xml.
 127       *
 128       * @param string $element Element name
 129       * @param array $attribs Array of attributes
 130       * @param string $contents Content of the element
 131       * @return string
 132       */
 133  	public static function tags( $element, $attribs = null, $contents ) {
 134          return self::openElement( $element, $attribs ) . $contents . "</$element>";
 135      }
 136  
 137      /**
 138       * Create a date selector
 139       *
 140       * @param string $selected The month which should be selected, default ''.
 141       * @param string $allmonths Value of a special item denoting all month.
 142       *   Null to not include (default).
 143       * @param string $id Element identifier
 144       * @return string Html string containing the month selector
 145       */
 146  	public static function monthSelector( $selected = '', $allmonths = null, $id = 'month' ) {
 147          global $wgLang;
 148          $options = array();
 149          if ( is_null( $selected ) ) {
 150              $selected = '';
 151          }
 152          if ( !is_null( $allmonths ) ) {
 153              $options[] = self::option(
 154                  wfMessage( 'monthsall' )->text(),
 155                  $allmonths,
 156                  $selected === $allmonths
 157              );
 158          }
 159          for ( $i = 1; $i < 13; $i++ ) {
 160              $options[] = self::option( $wgLang->getMonthName( $i ), $i, $selected === $i );
 161          }
 162          return self::openElement( 'select', array(
 163              'id' => $id,
 164              'name' => 'month',
 165              'class' => 'mw-month-selector'
 166          ) )
 167              . implode( "\n", $options )
 168              . self::closeElement( 'select' );
 169      }
 170  
 171      /**
 172       * @param int $year
 173       * @param int $month
 174       * @return string Formatted HTML
 175       */
 176  	public static function dateMenu( $year, $month ) {
 177          # Offset overrides year/month selection
 178          if ( $month && $month !== -1 ) {
 179              $encMonth = intval( $month );
 180          } else {
 181              $encMonth = '';
 182          }
 183          if ( $year ) {
 184              $encYear = intval( $year );
 185          } elseif ( $encMonth ) {
 186              $timestamp = MWTimestamp::getInstance();
 187              $thisMonth = intval( $timestamp->format( 'n' ) );
 188              $thisYear = intval( $timestamp->format( 'Y' ) );
 189              if ( intval( $encMonth ) > $thisMonth ) {
 190                  $thisYear--;
 191              }
 192              $encYear = $thisYear;
 193          } else {
 194              $encYear = '';
 195          }
 196          $inputAttribs = array( 'id' => 'year', 'maxlength' => 4, 'size' => 7 );
 197          return self::label( wfMessage( 'year' )->text(), 'year' ) . ' ' .
 198              Html::input( 'year', $encYear, 'number', $inputAttribs ) . ' ' .
 199              self::label( wfMessage( 'month' )->text(), 'month' ) . ' ' .
 200              self::monthSelector( $encMonth, -1 );
 201      }
 202  
 203      /**
 204       * Construct a language selector appropriate for use in a form or preferences
 205       *
 206       * @param string $selected The language code of the selected language
 207       * @param bool $customisedOnly If true only languages which have some content are listed
 208       * @param string $inLanguage The ISO code of the language to display the select list in (optional)
 209       * @param array $overrideAttrs Override the attributes of the select tag (since 1.20)
 210       * @param Message|null $msg Label message key (since 1.20)
 211       * @return array Array containing 2 items: label HTML and select list HTML
 212       */
 213  	public static function languageSelector( $selected, $customisedOnly = true,
 214          $inLanguage = null, $overrideAttrs = array(), Message $msg = null
 215      ) {
 216          global $wgLanguageCode;
 217  
 218          $include = $customisedOnly ? 'mwfile' : 'mw';
 219          $languages = Language::fetchLanguageNames( $inLanguage, $include );
 220  
 221          // Make sure the site language is in the list;
 222          // a custom language code might not have a defined name...
 223          if ( !array_key_exists( $wgLanguageCode, $languages ) ) {
 224              $languages[$wgLanguageCode] = $wgLanguageCode;
 225          }
 226  
 227          ksort( $languages );
 228  
 229          /**
 230           * If a bogus value is set, default to the content language.
 231           * Otherwise, no default is selected and the user ends up
 232           * with Afrikaans since it's first in the list.
 233           */
 234          $selected = isset( $languages[$selected] ) ? $selected : $wgLanguageCode;
 235          $options = "\n";
 236          foreach ( $languages as $code => $name ) {
 237              $options .= Xml::option( "$code - $name", $code, $code == $selected ) . "\n";
 238          }
 239  
 240          $attrs = array( 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' );
 241          $attrs = array_merge( $attrs, $overrideAttrs );
 242  
 243          if ( $msg === null ) {
 244              $msg = wfMessage( 'yourlanguage' );
 245          }
 246          return array(
 247              Xml::label( $msg->text(), $attrs['id'] ),
 248              Xml::tags( 'select', $attrs, $options )
 249          );
 250  
 251      }
 252  
 253      /**
 254       * Shortcut to make a span element
 255       * @param string $text Content of the element, will be escaped
 256       * @param string $class Class name of the span element
 257       * @param array $attribs Other attributes
 258       * @return string
 259       */
 260  	public static function span( $text, $class, $attribs = array() ) {
 261          return self::element( 'span', array( 'class' => $class ) + $attribs, $text );
 262      }
 263  
 264      /**
 265       * Shortcut to make a specific element with a class attribute
 266       * @param string $text Content of the element, will be escaped
 267       * @param string $class Class name of the span element
 268       * @param string $tag Element name
 269       * @param array $attribs Other attributes
 270       * @return string
 271       */
 272  	public static function wrapClass( $text, $class, $tag = 'span', $attribs = array() ) {
 273          return self::tags( $tag, array( 'class' => $class ) + $attribs, $text );
 274      }
 275  
 276      /**
 277       * Convenience function to build an HTML text input field
 278       * @param string $name Value of the name attribute
 279       * @param int $size Value of the size attribute
 280       * @param mixed $value Value of the value attribute
 281       * @param array $attribs Other attributes
 282       * @return string HTML
 283       */
 284  	public static function input( $name, $size = false, $value = false, $attribs = array() ) {
 285          $attributes = array( 'name' => $name );
 286  
 287          if ( $size ) {
 288              $attributes['size'] = $size;
 289          }
 290  
 291          if ( $value !== false ) { // maybe 0
 292              $attributes['value'] = $value;
 293          }
 294  
 295          return self::element( 'input',
 296              Html::getTextInputAttributes( $attributes + $attribs ) );
 297      }
 298  
 299      /**
 300       * Convenience function to build an HTML password input field
 301       * @param string $name Value of the name attribute
 302       * @param int $size Value of the size attribute
 303       * @param mixed $value Value of the value attribute
 304       * @param array $attribs Other attributes
 305       * @return string HTML
 306       */
 307  	public static function password( $name, $size = false, $value = false,
 308          $attribs = array()
 309      ) {
 310          return self::input( $name, $size, $value,
 311              array_merge( $attribs, array( 'type' => 'password' ) ) );
 312      }
 313  
 314      /**
 315       * Internal function for use in checkboxes and radio buttons and such.
 316       *
 317       * @param string $name
 318       * @param bool $present
 319       *
 320       * @return array
 321       */
 322  	public static function attrib( $name, $present = true ) {
 323          return $present ? array( $name => $name ) : array();
 324      }
 325  
 326      /**
 327       * Convenience function to build an HTML checkbox
 328       * @param string $name Value of the name attribute
 329       * @param bool $checked Whether the checkbox is checked or not
 330       * @param array $attribs Array other attributes
 331       * @return string HTML
 332       */
 333  	public static function check( $name, $checked = false, $attribs = array() ) {
 334          return self::element( 'input', array_merge(
 335              array(
 336                  'name' => $name,
 337                  'type' => 'checkbox',
 338                  'value' => 1 ),
 339              self::attrib( 'checked', $checked ),
 340              $attribs ) );
 341      }
 342  
 343      /**
 344       * Convenience function to build an HTML radio button
 345       * @param string $name Value of the name attribute
 346       * @param string $value Value of the value attribute
 347       * @param bool $checked Whether the checkbox is checked or not
 348       * @param array $attribs Other attributes
 349       * @return string HTML
 350       */
 351  	public static function radio( $name, $value, $checked = false, $attribs = array() ) {
 352          return self::element( 'input', array(
 353              'name' => $name,
 354              'type' => 'radio',
 355              'value' => $value ) + self::attrib( 'checked', $checked ) + $attribs );
 356      }
 357  
 358      /**
 359       * Convenience function to build an HTML form label
 360       * @param string $label Text of the label
 361       * @param string $id
 362       * @param array $attribs An attribute array.  This will usually be
 363       *     the same array as is passed to the corresponding input element,
 364       *     so this function will cherry-pick appropriate attributes to
 365       *     apply to the label as well; only class and title are applied.
 366       * @return string HTML
 367       */
 368  	public static function label( $label, $id, $attribs = array() ) {
 369          $a = array( 'for' => $id );
 370  
 371          # FIXME avoid copy pasting below:
 372          if ( isset( $attribs['class'] ) ) {
 373                  $a['class'] = $attribs['class'];
 374          }
 375          if ( isset( $attribs['title'] ) ) {
 376                  $a['title'] = $attribs['title'];
 377          }
 378  
 379          return self::element( 'label', $a, $label );
 380      }
 381  
 382      /**
 383       * Convenience function to build an HTML text input field with a label
 384       * @param string $label Text of the label
 385       * @param string $name Value of the name attribute
 386       * @param string $id Id of the input
 387       * @param int|bool $size Value of the size attribute
 388       * @param string|bool $value Value of the value attribute
 389       * @param array $attribs Other attributes
 390       * @return string HTML
 391       */
 392  	public static function inputLabel( $label, $name, $id, $size = false,
 393          $value = false, $attribs = array()
 394      ) {
 395          list( $label, $input ) = self::inputLabelSep( $label, $name, $id, $size, $value, $attribs );
 396          return $label . '&#160;' . $input;
 397      }
 398  
 399      /**
 400       * Same as Xml::inputLabel() but return input and label in an array
 401       *
 402       * @param string $label
 403       * @param string $name
 404       * @param string $id
 405       * @param int|bool $size
 406       * @param string|bool $value
 407       * @param array $attribs
 408       *
 409       * @return array
 410       */
 411  	public static function inputLabelSep( $label, $name, $id, $size = false,
 412          $value = false, $attribs = array()
 413      ) {
 414          return array(
 415              Xml::label( $label, $id, $attribs ),
 416              self::input( $name, $size, $value, array( 'id' => $id ) + $attribs )
 417          );
 418      }
 419  
 420      /**
 421       * Convenience function to build an HTML checkbox with a label
 422       *
 423       * @param string $label
 424       * @param string $name
 425       * @param string $id
 426       * @param bool $checked
 427       * @param array $attribs
 428       *
 429       * @return string HTML
 430       */
 431  	public static function checkLabel( $label, $name, $id, $checked = false, $attribs = array() ) {
 432          global $wgUseMediaWikiUIEverywhere;
 433          $chkLabel = self::check( $name, $checked, array( 'id' => $id ) + $attribs ) .
 434              '&#160;' .
 435              self::label( $label, $id, $attribs );
 436  
 437          if ( $wgUseMediaWikiUIEverywhere ) {
 438              $chkLabel = self::openElement( 'div', array( 'class' => 'mw-ui-checkbox' ) ) .
 439                  $chkLabel . self::closeElement( 'div' );
 440          }
 441          return $chkLabel;
 442      }
 443  
 444      /**
 445       * Convenience function to build an HTML radio button with a label
 446       *
 447       * @param string $label
 448       * @param string $name
 449       * @param string $value
 450       * @param string $id
 451       * @param bool $checked
 452       * @param array $attribs
 453       *
 454       * @return string HTML
 455       */
 456  	public static function radioLabel( $label, $name, $value, $id,
 457          $checked = false, $attribs = array()
 458      ) {
 459          return self::radio( $name, $value, $checked, array( 'id' => $id ) + $attribs ) .
 460              '&#160;' .
 461              self::label( $label, $id, $attribs );
 462      }
 463  
 464      /**
 465       * Convenience function to build an HTML submit button
 466       * When $wgUseMediaWikiUIEverywhere is true it will default to a constructive button
 467       * @param string $value Label text for the button
 468       * @param array $attribs Optional custom attributes
 469       * @return string HTML
 470       */
 471  	public static function submitButton( $value, $attribs = array() ) {
 472          global $wgUseMediaWikiUIEverywhere;
 473          $baseAttrs = array(
 474              'type' => 'submit',
 475              'value' => $value,
 476          );
 477          // Done conditionally for time being as it is possible
 478          // some submit forms
 479          // might need to be mw-ui-destructive (e.g. delete a page)
 480          if ( $wgUseMediaWikiUIEverywhere ) {
 481              $baseAttrs['class'] = 'mw-ui-button mw-ui-constructive';
 482          }
 483          // Any custom attributes will take precendence of anything in baseAttrs e.g. override the class
 484          $attribs = $attribs + $baseAttrs;
 485          return Html::element( 'input', $attribs );
 486      }
 487  
 488      /**
 489       * Convenience function to build an HTML drop-down list item.
 490       * @param string $text Text for this item. Will be HTML escaped
 491       * @param string $value Form submission value; if empty, use text
 492       * @param bool $selected If true, will be the default selected item
 493       * @param array $attribs Optional additional HTML attributes
 494       * @return string HTML
 495       */
 496  	public static function option( $text, $value = null, $selected = false,
 497              $attribs = array() ) {
 498          if ( !is_null( $value ) ) {
 499              $attribs['value'] = $value;
 500          }
 501          if ( $selected ) {
 502              $attribs['selected'] = 'selected';
 503          }
 504          return Html::element( 'option', $attribs, $text );
 505      }
 506  
 507      /**
 508       * Build a drop-down box from a textual list.
 509       *
 510       * @param string $name Name and id for the drop-down
 511       * @param string $list Correctly formatted text (newline delimited) to be
 512       *   used to generate the options.
 513       * @param string $other Text for the "Other reasons" option
 514       * @param string $selected Option which should be pre-selected
 515       * @param string $class CSS classes for the drop-down
 516       * @param int $tabindex Value of the tabindex attribute
 517       * @return string
 518       */
 519  	public static function listDropDown( $name = '', $list = '', $other = '',
 520          $selected = '', $class = '', $tabindex = null
 521      ) {
 522          $optgroup = false;
 523  
 524          $options = self::option( $other, 'other', $selected === 'other' );
 525  
 526          foreach ( explode( "\n", $list ) as $option ) {
 527              $value = trim( $option );
 528              if ( $value == '' ) {
 529                  continue;
 530              } elseif ( substr( $value, 0, 1 ) == '*' && substr( $value, 1, 1 ) != '*' ) {
 531                  // A new group is starting ...
 532                  $value = trim( substr( $value, 1 ) );
 533                  if ( $optgroup ) {
 534                      $options .= self::closeElement( 'optgroup' );
 535                  }
 536                  $options .= self::openElement( 'optgroup', array( 'label' => $value ) );
 537                  $optgroup = true;
 538              } elseif ( substr( $value, 0, 2 ) == '**' ) {
 539                  // groupmember
 540                  $value = trim( substr( $value, 2 ) );
 541                  $options .= self::option( $value, $value, $selected === $value );
 542              } else {
 543                  // groupless reason list
 544                  if ( $optgroup ) {
 545                      $options .= self::closeElement( 'optgroup' );
 546                  }
 547                  $options .= self::option( $value, $value, $selected === $value );
 548                  $optgroup = false;
 549              }
 550          }
 551  
 552          if ( $optgroup ) {
 553              $options .= self::closeElement( 'optgroup' );
 554          }
 555  
 556          $attribs = array();
 557  
 558          if ( $name ) {
 559              $attribs['id'] = $name;
 560              $attribs['name'] = $name;
 561          }
 562  
 563          if ( $class ) {
 564              $attribs['class'] = $class;
 565          }
 566  
 567          if ( $tabindex ) {
 568              $attribs['tabindex'] = $tabindex;
 569          }
 570  
 571          return Xml::openElement( 'select', $attribs )
 572              . "\n"
 573              . $options
 574              . "\n"
 575              . Xml::closeElement( 'select' );
 576      }
 577  
 578      /**
 579       * Shortcut for creating fieldsets.
 580       *
 581       * @param string|bool $legend Legend of the fieldset. If evaluates to false,
 582       *   legend is not added.
 583       * @param string $content Pre-escaped content for the fieldset. If false,
 584       *   only open fieldset is returned.
 585       * @param array $attribs Any attributes to fieldset-element.
 586       *
 587       * @return string
 588       */
 589  	public static function fieldset( $legend = false, $content = false, $attribs = array() ) {
 590          $s = Xml::openElement( 'fieldset', $attribs ) . "\n";
 591  
 592          if ( $legend ) {
 593              $s .= Xml::element( 'legend', null, $legend ) . "\n";
 594          }
 595  
 596          if ( $content !== false ) {
 597              $s .= $content . "\n";
 598              $s .= Xml::closeElement( 'fieldset' ) . "\n";
 599          }
 600  
 601          return $s;
 602      }
 603  
 604      /**
 605       * Shortcut for creating textareas.
 606       *
 607       * @param string $name The 'name' for the textarea
 608       * @param string $content Content for the textarea
 609       * @param int $cols The number of columns for the textarea
 610       * @param int $rows The number of rows for the textarea
 611       * @param array $attribs Any other attributes for the textarea
 612       *
 613       * @return string
 614       */
 615  	public static function textarea( $name, $content, $cols = 40, $rows = 5, $attribs = array() ) {
 616          return self::element( 'textarea',
 617                      Html::getTextInputAttributes(
 618                          array(
 619                              'name' => $name,
 620                              'id' => $name,
 621                              'cols' => $cols,
 622                              'rows' => $rows
 623                          ) + $attribs
 624                      ),
 625                      $content, false );
 626      }
 627  
 628      /**
 629       * Returns an escaped string suitable for inclusion in a string literal
 630       * for JavaScript source code.
 631       * Illegal control characters are assumed not to be present.
 632       *
 633       * @deprecated since 1.21; use Xml::encodeJsVar() or Xml::encodeJsCall() instead
 634       * @param string $string String to escape
 635       * @return string
 636       */
 637  	public static function escapeJsString( $string ) {
 638          // See ECMA 262 section 7.8.4 for string literal format
 639          $pairs = array(
 640              "\\" => "\\\\",
 641              "\"" => "\\\"",
 642              '\'' => '\\\'',
 643              "\n" => "\\n",
 644              "\r" => "\\r",
 645  
 646              # To avoid closing the element or CDATA section
 647              "<" => "\\x3c",
 648              ">" => "\\x3e",
 649  
 650              # To avoid any complaints about bad entity refs
 651              "&" => "\\x26",
 652  
 653              # Work around https://bugzilla.mozilla.org/show_bug.cgi?id=274152
 654              # Encode certain Unicode formatting chars so affected
 655              # versions of Gecko don't misinterpret our strings;
 656              # this is a common problem with Farsi text.
 657              "\xe2\x80\x8c" => "\\u200c", // ZERO WIDTH NON-JOINER
 658              "\xe2\x80\x8d" => "\\u200d", // ZERO WIDTH JOINER
 659          );
 660  
 661          return strtr( $string, $pairs );
 662      }
 663  
 664      /**
 665       * Encode a variable of arbitrary type to JavaScript.
 666       * If the value is an XmlJsCode object, pass through the object's value verbatim.
 667       *
 668       * @note Only use this function for generating JavaScript code. If generating output
 669       *       for a proper JSON parser, just call FormatJson::encode() directly.
 670       *
 671       * @param mixed $value The value being encoded. Can be any type except a resource.
 672       * @param bool $pretty If true, add non-significant whitespace to improve readability.
 673       * @return string|bool String if successful; false upon failure
 674       */
 675  	public static function encodeJsVar( $value, $pretty = false ) {
 676          if ( $value instanceof XmlJsCode ) {
 677              return $value->value;
 678          }
 679          return FormatJson::encode( $value, $pretty, FormatJson::UTF8_OK );
 680      }
 681  
 682      /**
 683       * Create a call to a JavaScript function. The supplied arguments will be
 684       * encoded using Xml::encodeJsVar().
 685       *
 686       * @since 1.17
 687       * @param string $name The name of the function to call, or a JavaScript expression
 688       *    which evaluates to a function object which is called.
 689       * @param array $args The arguments to pass to the function.
 690       * @param bool $pretty If true, add non-significant whitespace to improve readability.
 691       * @return string|bool String if successful; false upon failure
 692       */
 693  	public static function encodeJsCall( $name, $args, $pretty = false ) {
 694          foreach ( $args as &$arg ) {
 695              $arg = Xml::encodeJsVar( $arg, $pretty );
 696              if ( $arg === false ) {
 697                  return false;
 698              }
 699          }
 700  
 701          return "$name(" . ( $pretty
 702              ? ( ' ' . implode( ', ', $args ) . ' ' )
 703              : implode( ',', $args )
 704          ) . ");";
 705      }
 706  
 707      /**
 708       * Check if a string is well-formed XML.
 709       * Must include the surrounding tag.
 710       *
 711       * @param string $text String to test.
 712       * @return bool
 713       *
 714       * @todo Error position reporting return
 715       */
 716  	public static function isWellFormed( $text ) {
 717          $parser = xml_parser_create( "UTF-8" );
 718  
 719          # case folding violates XML standard, turn it off
 720          xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
 721  
 722          if ( !xml_parse( $parser, $text, true ) ) {
 723              //$err = xml_error_string( xml_get_error_code( $parser ) );
 724              //$position = xml_get_current_byte_index( $parser );
 725              //$fragment = $this->extractFragment( $html, $position );
 726              //$this->mXmlError = "$err at byte $position:\n$fragment";
 727              xml_parser_free( $parser );
 728              return false;
 729          }
 730  
 731          xml_parser_free( $parser );
 732  
 733          return true;
 734      }
 735  
 736      /**
 737       * Check if a string is a well-formed XML fragment.
 738       * Wraps fragment in an \<html\> bit and doctype, so it can be a fragment
 739       * and can use HTML named entities.
 740       *
 741       * @param string $text
 742       * @return bool
 743       */
 744  	public static function isWellFormedXmlFragment( $text ) {
 745          $html =
 746              Sanitizer::hackDocType() .
 747              '<html>' .
 748              $text .
 749              '</html>';
 750  
 751          return Xml::isWellFormed( $html );
 752      }
 753  
 754      /**
 755       * Replace " > and < with their respective HTML entities ( &quot;,
 756       * &gt;, &lt;)
 757       *
 758       * @param string $in Text that might contain HTML tags.
 759       * @return string Escaped string
 760       */
 761  	public static function escapeTagsOnly( $in ) {
 762          return str_replace(
 763              array( '"', '>', '<' ),
 764              array( '&quot;', '&gt;', '&lt;' ),
 765              $in );
 766      }
 767  
 768      /**
 769       * Generate a form (without the opening form element).
 770       * Output optionally includes a submit button.
 771       * @param array $fields Associative array, key is the name of a message that
 772       *   contains a description for the field, value is an HTML string
 773       *   containing the appropriate input.
 774       * @param string $submitLabel The name of a message containing a label for
 775       *   the submit button.
 776       * @param array $submitAttribs The attributes to add to the submit button
 777       * @return string HTML form.
 778       */
 779  	public static function buildForm( $fields, $submitLabel = null, $submitAttribs = array() ) {
 780          $form = '';
 781          $form .= "<table><tbody>";
 782  
 783          foreach ( $fields as $labelmsg => $input ) {
 784              $id = "mw-$labelmsg";
 785              $form .= Xml::openElement( 'tr', array( 'id' => $id ) );
 786  
 787              // TODO use a <label> here for accessibility purposes - will need
 788              // to either not use a table to build the form, or find the ID of
 789              // the input somehow.
 790  
 791              $form .= Xml::tags( 'td', array( 'class' => 'mw-label' ), wfMessage( $labelmsg )->parse() );
 792              $form .= Xml::openElement( 'td', array( 'class' => 'mw-input' ) )
 793                  . $input . Xml::closeElement( 'td' );
 794              $form .= Xml::closeElement( 'tr' );
 795          }
 796  
 797          if ( $submitLabel ) {
 798              $form .= Xml::openElement( 'tr' );
 799              $form .= Xml::tags( 'td', array(), '' );
 800              $form .= Xml::openElement( 'td', array( 'class' => 'mw-submit' ) )
 801                  . Xml::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs )
 802                  . Xml::closeElement( 'td' );
 803              $form .= Xml::closeElement( 'tr' );
 804          }
 805  
 806          $form .= "</tbody></table>";
 807  
 808          return $form;
 809      }
 810  
 811      /**
 812       * Build a table of data
 813       * @param array $rows An array of arrays of strings, each to be a row in a table
 814       * @param array $attribs An array of attributes to apply to the table tag [optional]
 815       * @param array $headers An array of strings to use as table headers [optional]
 816       * @return string
 817       */
 818  	public static function buildTable( $rows, $attribs = array(), $headers = null ) {
 819          $s = Xml::openElement( 'table', $attribs );
 820  
 821          if ( is_array( $headers ) ) {
 822              $s .= Xml::openElement( 'thead', $attribs );
 823  
 824              foreach ( $headers as $id => $header ) {
 825                  $attribs = array();
 826  
 827                  if ( is_string( $id ) ) {
 828                      $attribs['id'] = $id;
 829                  }
 830  
 831                  $s .= Xml::element( 'th', $attribs, $header );
 832              }
 833              $s .= Xml::closeElement( 'thead' );
 834          }
 835  
 836          foreach ( $rows as $id => $row ) {
 837              $attribs = array();
 838  
 839              if ( is_string( $id ) ) {
 840                  $attribs['id'] = $id;
 841              }
 842  
 843              $s .= Xml::buildTableRow( $attribs, $row );
 844          }
 845  
 846          $s .= Xml::closeElement( 'table' );
 847  
 848          return $s;
 849      }
 850  
 851      /**
 852       * Build a row for a table
 853       * @param array $attribs An array of attributes to apply to the tr tag
 854       * @param array $cells An array of strings to put in <td>
 855       * @return string
 856       */
 857  	public static function buildTableRow( $attribs, $cells ) {
 858          $s = Xml::openElement( 'tr', $attribs );
 859  
 860          foreach ( $cells as $id => $cell ) {
 861              $attribs = array();
 862  
 863              if ( is_string( $id ) ) {
 864                  $attribs['id'] = $id;
 865              }
 866  
 867              $s .= Xml::element( 'td', $attribs, $cell );
 868          }
 869  
 870          $s .= Xml::closeElement( 'tr' );
 871  
 872          return $s;
 873      }
 874  }
 875  
 876  class XmlSelect {
 877      protected $options = array();
 878      protected $default = false;
 879      protected $attributes = array();
 880  
 881  	public function __construct( $name = false, $id = false, $default = false ) {
 882          if ( $name ) {
 883              $this->setAttribute( 'name', $name );
 884          }
 885  
 886          if ( $id ) {
 887              $this->setAttribute( 'id', $id );
 888          }
 889  
 890          if ( $default !== false ) {
 891              $this->default = $default;
 892          }
 893      }
 894  
 895      /**
 896       * @param string $default
 897       */
 898  	public function setDefault( $default ) {
 899          $this->default = $default;
 900      }
 901  
 902      /**
 903       * @param string $name
 904       * @param array $value
 905       */
 906  	public function setAttribute( $name, $value ) {
 907          $this->attributes[$name] = $value;
 908      }
 909  
 910      /**
 911       * @param string $name
 912       * @return array|null
 913       */
 914  	public function getAttribute( $name ) {
 915          if ( isset( $this->attributes[$name] ) ) {
 916              return $this->attributes[$name];
 917          } else {
 918              return null;
 919          }
 920      }
 921  
 922      /**
 923       * @param string $name
 924       * @param bool $value
 925       */
 926  	public function addOption( $name, $value = false ) {
 927          // Stab stab stab
 928          $value = $value !== false ? $value : $name;
 929  
 930          $this->options[] = array( $name => $value );
 931      }
 932  
 933      /**
 934       * This accepts an array of form
 935       * label => value
 936       * label => ( label => value, label => value )
 937       *
 938       * @param array $options
 939       */
 940  	public function addOptions( $options ) {
 941          $this->options[] = $options;
 942      }
 943  
 944      /**
 945       * This accepts an array of form
 946       * label => value
 947       * label => ( label => value, label => value )
 948       *
 949       * @param array $options
 950       * @param bool $default
 951       * @return string
 952       */
 953  	static function formatOptions( $options, $default = false ) {
 954          $data = '';
 955  
 956          foreach ( $options as $label => $value ) {
 957              if ( is_array( $value ) ) {
 958                  $contents = self::formatOptions( $value, $default );
 959                  $data .= Html::rawElement( 'optgroup', array( 'label' => $label ), $contents ) . "\n";
 960              } else {
 961                  $data .= Xml::option( $label, $value, $value === $default ) . "\n";
 962              }
 963          }
 964  
 965          return $data;
 966      }
 967  
 968      /**
 969       * @return string
 970       */
 971  	public function getHTML() {
 972          $contents = '';
 973  
 974          foreach ( $this->options as $options ) {
 975              $contents .= self::formatOptions( $options, $this->default );
 976          }
 977  
 978          return Html::rawElement( 'select', $this->attributes, rtrim( $contents ) );
 979      }
 980  }
 981  
 982  /**
 983   * A wrapper class which causes Xml::encodeJsVar() and Xml::encodeJsCall() to
 984   * interpret a given string as being a JavaScript expression, instead of string
 985   * data.
 986   *
 987   * Example:
 988   *
 989   *    Xml::encodeJsVar( new XmlJsCode( 'a + b' ) );
 990   *
 991   * Returns "a + b".
 992   *
 993   * @note As of 1.21, XmlJsCode objects cannot be nested inside objects or arrays. The sole
 994   *       exception is the $args argument to Xml::encodeJsCall() because Xml::encodeJsVar() is
 995   *       called for each individual element in that array.
 996   *
 997   * @since 1.17
 998   */
 999  class XmlJsCode {
1000      public $value;
1001  
1002  	function __construct( $value ) {
1003          $this->value = $value;
1004      }
1005  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1