MediaWiki  REL1_19
HTMLForm.php
Go to the documentation of this file.
00001 <?php
00056 class HTMLForm extends ContextSource {
00057 
00058         // A mapping of 'type' inputs onto standard HTMLFormField subclasses
00059         static $typeMappings = array(
00060                 'api' => 'HTMLApiField',
00061                 'text' => 'HTMLTextField',
00062                 'textarea' => 'HTMLTextAreaField',
00063                 'select' => 'HTMLSelectField',
00064                 'radio' => 'HTMLRadioField',
00065                 'multiselect' => 'HTMLMultiSelectField',
00066                 'check' => 'HTMLCheckField',
00067                 'toggle' => 'HTMLCheckField',
00068                 'int' => 'HTMLIntField',
00069                 'float' => 'HTMLFloatField',
00070                 'info' => 'HTMLInfoField',
00071                 'selectorother' => 'HTMLSelectOrOtherField',
00072                 'selectandother' => 'HTMLSelectAndOtherField',
00073                 'submit' => 'HTMLSubmitField',
00074                 'hidden' => 'HTMLHiddenField',
00075                 'edittools' => 'HTMLEditTools',
00076 
00077                 // HTMLTextField will output the correct type="" attribute automagically.
00078                 // There are about four zillion other HTML5 input types, like url, but
00079                 // we don't use those at the moment, so no point in adding all of them.
00080                 'email' => 'HTMLTextField',
00081                 'password' => 'HTMLTextField',
00082         );
00083 
00084         protected $mMessagePrefix;
00085 
00087         protected $mFlatFields;
00088 
00089         protected $mFieldTree;
00090         protected $mShowReset = false;
00091         public $mFieldData;
00092 
00093         protected $mSubmitCallback;
00094         protected $mValidationErrorMessage;
00095 
00096         protected $mPre = '';
00097         protected $mHeader = '';
00098         protected $mFooter = '';
00099         protected $mSectionHeaders = array();
00100         protected $mSectionFooters = array();
00101         protected $mPost = '';
00102         protected $mId;
00103 
00104         protected $mSubmitID;
00105         protected $mSubmitName;
00106         protected $mSubmitText;
00107         protected $mSubmitTooltip;
00108 
00109         protected $mTitle;
00110         protected $mMethod = 'post';
00111 
00117         protected $mAction = false;
00118 
00119         protected $mUseMultipart = false;
00120         protected $mHiddenFields = array();
00121         protected $mButtons = array();
00122 
00123         protected $mWrapperLegend = false;
00124         
00132         protected $mSubSectionBeforeFields = true;
00133 
00141         public function __construct( $descriptor, /*IContextSource*/ $context = null, $messagePrefix = '' ) {
00142                 if( $context instanceof IContextSource ){
00143                         $this->setContext( $context );
00144                         $this->mTitle = false; // We don't need them to set a title
00145                         $this->mMessagePrefix = $messagePrefix;
00146                 } else {
00147                         // B/C since 1.18
00148                         if( is_string( $context ) && $messagePrefix === '' ){
00149                                 // it's actually $messagePrefix
00150                                 $this->mMessagePrefix = $context;
00151                         }
00152                 }
00153 
00154                 // Expand out into a tree.
00155                 $loadedDescriptor = array();
00156                 $this->mFlatFields = array();
00157 
00158                 foreach ( $descriptor as $fieldname => $info ) {
00159                         $section = isset( $info['section'] )
00160                                 ? $info['section']
00161                                 : '';
00162 
00163                         if ( isset( $info['type'] ) && $info['type'] == 'file' ) {
00164                                 $this->mUseMultipart = true;
00165                         }
00166 
00167                         $field = self::loadInputFromParameters( $fieldname, $info );
00168                         $field->mParent = $this;
00169 
00170                         $setSection =& $loadedDescriptor;
00171                         if ( $section ) {
00172                                 $sectionParts = explode( '/', $section );
00173 
00174                                 while ( count( $sectionParts ) ) {
00175                                         $newName = array_shift( $sectionParts );
00176 
00177                                         if ( !isset( $setSection[$newName] ) ) {
00178                                                 $setSection[$newName] = array();
00179                                         }
00180 
00181                                         $setSection =& $setSection[$newName];
00182                                 }
00183                         }
00184 
00185                         $setSection[$fieldname] = $field;
00186                         $this->mFlatFields[$fieldname] = $field;
00187                 }
00188 
00189                 $this->mFieldTree = $loadedDescriptor;
00190         }
00191 
00197         static function addJS() { wfDeprecated( __METHOD__, '1.18' ); }
00198 
00205         static function loadInputFromParameters( $fieldname, $descriptor ) {
00206                 if ( isset( $descriptor['class'] ) ) {
00207                         $class = $descriptor['class'];
00208                 } elseif ( isset( $descriptor['type'] ) ) {
00209                         $class = self::$typeMappings[$descriptor['type']];
00210                         $descriptor['class'] = $class;
00211                 } else {
00212                         $class = null;
00213                 }
00214 
00215                 if ( !$class ) {
00216                         throw new MWException( "Descriptor with no class: " . print_r( $descriptor, true ) );
00217                 }
00218 
00219                 $descriptor['fieldname'] = $fieldname;
00220 
00221                 $obj = new $class( $descriptor );
00222 
00223                 return $obj;
00224         }
00225 
00229         function prepareForm() {
00230                 # Check if we have the info we need
00231                 if ( !$this->mTitle instanceof Title && $this->mTitle !== false ) {
00232                         throw new MWException( "You must call setTitle() on an HTMLForm" );
00233                 }
00234 
00235                 # Load data from the request.
00236                 $this->loadData();
00237         }
00238 
00243         function tryAuthorizedSubmit() {
00244                 $result = false;
00245 
00246                 $submit = false;
00247                 if ( $this->getMethod() != 'post' ) {
00248                         $submit = true; // no session check needed
00249                 } elseif ( $this->getRequest()->wasPosted() ) {
00250                         $editToken = $this->getRequest()->getVal( 'wpEditToken' );
00251                         if ( $this->getUser()->isLoggedIn() || $editToken != null ) {
00252                                 // Session tokens for logged-out users have no security value.
00253                                 // However, if the user gave one, check it in order to give a nice 
00254                                 // "session expired" error instead of "permission denied" or such.
00255                                 $submit = $this->getUser()->matchEditToken( $editToken );
00256                         } else {
00257                                 $submit = true;
00258                         }
00259                 }
00260 
00261                 if ( $submit ) {
00262                         $result = $this->trySubmit();
00263                 }
00264 
00265                 return $result;
00266         }
00267 
00274         function show() {
00275                 $this->prepareForm();
00276 
00277                 $result = $this->tryAuthorizedSubmit();
00278                 if ( $result === true || ( $result instanceof Status && $result->isGood() ) ){
00279                         return $result;
00280                 }
00281 
00282                 $this->displayForm( $result );
00283                 return false;
00284         }
00285 
00293         function trySubmit() {
00294                 # Check for validation
00295                 foreach ( $this->mFlatFields as $fieldname => $field ) {
00296                         if ( !empty( $field->mParams['nodata'] ) ) {
00297                                 continue;
00298                         }
00299                         if ( $field->validate(
00300                                         $this->mFieldData[$fieldname],
00301                                         $this->mFieldData )
00302                                 !== true
00303                         ) {
00304                                 return isset( $this->mValidationErrorMessage )
00305                                         ? $this->mValidationErrorMessage
00306                                         : array( 'htmlform-invalid-input' );
00307                         }
00308                 }
00309 
00310                 $callback = $this->mSubmitCallback;
00311 
00312                 $data = $this->filterDataForSubmit( $this->mFieldData );
00313 
00314                 $res = call_user_func( $callback, $data, $this );
00315 
00316                 return $res;
00317         }
00318 
00327         function setSubmitCallback( $cb ) {
00328                 $this->mSubmitCallback = $cb;
00329         }
00330 
00336         function setValidationErrorMessage( $msg ) {
00337                 $this->mValidationErrorMessage = $msg;
00338         }
00339 
00344         function setIntro( $msg ) {
00345                 $this->setPreText( $msg );
00346         }
00347 
00353         function setPreText( $msg ) { $this->mPre = $msg; }
00354 
00359         function addPreText( $msg ) { $this->mPre .= $msg; }
00360 
00366         function addHeaderText( $msg, $section = null ) {
00367                 if ( is_null( $section ) ) {
00368                         $this->mHeader .= $msg;
00369                 } else {
00370                         if ( !isset( $this->mSectionHeaders[$section] ) ) {
00371                                 $this->mSectionHeaders[$section] = '';
00372                         }
00373                         $this->mSectionHeaders[$section] .= $msg;
00374                 }
00375         }
00376 
00383         function setHeaderText( $msg, $section = null ) {
00384                 if ( is_null( $section ) ) {
00385                         $this->mHeader = $msg;
00386                 } else {
00387                         $this->mSectionHeaders[$section] = $msg;
00388                 }
00389         }
00390 
00396         function addFooterText( $msg, $section = null ) {
00397                 if ( is_null( $section ) ) {
00398                         $this->mFooter .= $msg;
00399                 } else {
00400                         if ( !isset( $this->mSectionFooters[$section] ) ) {
00401                                 $this->mSectionFooters[$section] = '';
00402                         }
00403                         $this->mSectionFooters[$section] .= $msg;
00404                 }
00405         }
00406 
00413         function setFooterText( $msg, $section = null ) {
00414                 if ( is_null( $section ) ) {
00415                         $this->mFooter = $msg;
00416                 } else {
00417                         $this->mSectionFooters[$section] = $msg;
00418                 }
00419         }
00420 
00425         function addPostText( $msg ) { $this->mPost .= $msg; }
00426 
00431         function setPostText( $msg ) { $this->mPost = $msg; }
00432 
00439         public function addHiddenField( $name, $value, $attribs = array() ) {
00440                 $attribs += array( 'name' => $name );
00441                 $this->mHiddenFields[] = array( $value, $attribs );
00442         }
00443 
00444         public function addButton( $name, $value, $id = null, $attribs = null ) {
00445                 $this->mButtons[] = compact( 'name', 'value', 'id', 'attribs' );
00446         }
00447 
00453         function displayForm( $submitResult ) {
00454                 $this->getOutput()->addHTML( $this->getHTML( $submitResult ) );
00455         }
00456 
00462         function getHTML( $submitResult ) {
00463                 # For good measure (it is the default)
00464                 $this->getOutput()->preventClickjacking();
00465                 $this->getOutput()->addModules( 'mediawiki.htmlform' );
00466 
00467                 $html = ''
00468                         . $this->getErrors( $submitResult )
00469                         . $this->mHeader
00470                         . $this->getBody()
00471                         . $this->getHiddenFields()
00472                         . $this->getButtons()
00473                         . $this->mFooter
00474                 ;
00475 
00476                 $html = $this->wrapForm( $html );
00477 
00478                 return '' . $this->mPre . $html . $this->mPost;
00479         }
00480 
00486         function wrapForm( $html ) {
00487 
00488                 # Include a <fieldset> wrapper for style, if requested.
00489                 if ( $this->mWrapperLegend !== false ) {
00490                         $html = Xml::fieldset( $this->mWrapperLegend, $html );
00491                 }
00492                 # Use multipart/form-data
00493                 $encType = $this->mUseMultipart
00494                         ? 'multipart/form-data'
00495                         : 'application/x-www-form-urlencoded';
00496                 # Attributes
00497                 $attribs = array(
00498                         'action'  => $this->mAction === false ? $this->getTitle()->getFullURL() : $this->mAction,
00499                         'method'  => $this->mMethod,
00500                         'class'   => 'visualClear',
00501                         'enctype' => $encType,
00502                 );
00503                 if ( !empty( $this->mId ) ) {
00504                         $attribs['id'] = $this->mId;
00505                 }
00506 
00507                 return Html::rawElement( 'form', $attribs, $html );
00508         }
00509 
00514         function getHiddenFields() {
00515                 global $wgUsePathInfo;
00516 
00517                 $html = '';
00518                 if( $this->getMethod() == 'post' ){
00519                         $html .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken(), array( 'id' => 'wpEditToken' ) ) . "\n";
00520                         $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n";
00521                 }
00522 
00523                 if ( !$wgUsePathInfo && $this->getMethod() == 'get' ) {
00524                         $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n";
00525                 }
00526 
00527                 foreach ( $this->mHiddenFields as $data ) {
00528                         list( $value, $attribs ) = $data;
00529                         $html .= Html::hidden( $attribs['name'], $value, $attribs ) . "\n";
00530                 }
00531 
00532                 return $html;
00533         }
00534 
00539         function getButtons() {
00540                 $html = '';
00541                 $attribs = array();
00542 
00543                 if ( isset( $this->mSubmitID ) ) {
00544                         $attribs['id'] = $this->mSubmitID;
00545                 }
00546 
00547                 if ( isset( $this->mSubmitName ) ) {
00548                         $attribs['name'] = $this->mSubmitName;
00549                 }
00550 
00551                 if ( isset( $this->mSubmitTooltip ) ) {
00552                         $attribs += Linker::tooltipAndAccesskeyAttribs( $this->mSubmitTooltip );
00553                 }
00554 
00555                 $attribs['class'] = 'mw-htmlform-submit';
00556 
00557                 $html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
00558 
00559                 if ( $this->mShowReset ) {
00560                         $html .= Html::element(
00561                                 'input',
00562                                 array(
00563                                         'type' => 'reset',
00564                                         'value' => wfMsg( 'htmlform-reset' )
00565                                 )
00566                         ) . "\n";
00567                 }
00568 
00569                 foreach ( $this->mButtons as $button ) {
00570                         $attrs = array(
00571                                 'type'  => 'submit',
00572                                 'name'  => $button['name'],
00573                                 'value' => $button['value']
00574                         );
00575 
00576                         if ( $button['attribs'] ) {
00577                                 $attrs += $button['attribs'];
00578                         }
00579 
00580                         if ( isset( $button['id'] ) ) {
00581                                 $attrs['id'] = $button['id'];
00582                         }
00583 
00584                         $html .= Html::element( 'input', $attrs );
00585                 }
00586 
00587                 return $html;
00588         }
00589 
00594         function getBody() {
00595                 return $this->displaySection( $this->mFieldTree );
00596         }
00597 
00603         function getErrors( $errors ) {
00604                 if ( $errors instanceof Status ) {
00605                         if ( $errors->isOK() ) {
00606                                 $errorstr = '';
00607                         } else {
00608                                 $errorstr = $this->getOutput()->parse( $errors->getWikiText() );
00609                         }
00610                 } elseif ( is_array( $errors ) ) {
00611                         $errorstr = $this->formatErrors( $errors );
00612                 } else {
00613                         $errorstr = $errors;
00614                 }
00615 
00616                 return $errorstr
00617                         ? Html::rawElement( 'div', array( 'class' => 'error' ), $errorstr )
00618                         : '';
00619         }
00620 
00626         public static function formatErrors( $errors ) {
00627                 $errorstr = '';
00628 
00629                 foreach ( $errors as $error ) {
00630                         if ( is_array( $error ) ) {
00631                                 $msg = array_shift( $error );
00632                         } else {
00633                                 $msg = $error;
00634                                 $error = array();
00635                         }
00636 
00637                         $errorstr .= Html::rawElement(
00638                                 'li',
00639                                 array(),
00640                                 wfMsgExt( $msg, array( 'parseinline' ), $error )
00641                         );
00642                 }
00643 
00644                 $errorstr = Html::rawElement( 'ul', array(), $errorstr );
00645 
00646                 return $errorstr;
00647         }
00648 
00653         function setSubmitText( $t ) {
00654                 $this->mSubmitText = $t;
00655         }
00656 
00662         public function setSubmitTextMsg( $msg ) {
00663                 return $this->setSubmitText( $this->msg( $msg )->escaped() );
00664         }
00665 
00670         function getSubmitText() {
00671                 return $this->mSubmitText
00672                         ? $this->mSubmitText
00673                         : wfMsg( 'htmlform-submit' );
00674         }
00675 
00676         public function setSubmitName( $name ) {
00677                 $this->mSubmitName = $name;
00678         }
00679 
00680         public function setSubmitTooltip( $name ) {
00681                 $this->mSubmitTooltip = $name;
00682         }
00683 
00689         function setSubmitID( $t ) {
00690                 $this->mSubmitID = $t;
00691         }
00692 
00693         public function setId( $id ) {
00694                 $this->mId = $id;
00695         }
00702         public function setWrapperLegend( $legend ) { $this->mWrapperLegend = $legend; }
00703 
00710         public function setWrapperLegendMsg( $msg ) {
00711                 return $this->setWrapperLegend( $this->msg( $msg )->escaped() );
00712         }
00713 
00720         function setMessagePrefix( $p ) {
00721                 $this->mMessagePrefix = $p;
00722         }
00723 
00728         function setTitle( $t ) {
00729                 $this->mTitle = $t;
00730         }
00731 
00736         function getTitle() {
00737                 return $this->mTitle === false
00738                         ? $this->getContext()->getTitle()
00739                         : $this->mTitle;
00740         }
00741 
00746         public function setMethod( $method='post' ){
00747                 $this->mMethod = $method;
00748         }
00749 
00750         public function getMethod(){
00751                 return $this->mMethod;
00752         }
00753 
00761         function displaySection( $fields, $sectionName = '', $fieldsetIDPrefix = '' ) {
00762                 $tableHtml = '';
00763                 $subsectionHtml = '';
00764                 $hasLeftColumn = false;
00765 
00766                 foreach ( $fields as $key => $value ) {
00767                         if ( is_object( $value ) ) {
00768                                 $v = empty( $value->mParams['nodata'] )
00769                                         ? $this->mFieldData[$key]
00770                                         : $value->getDefault();
00771                                 $tableHtml .= $value->getTableRow( $v );
00772 
00773                                 if ( $value->getLabel() != '&#160;' ) {
00774                                         $hasLeftColumn = true;
00775                                 }
00776                         } elseif ( is_array( $value ) ) {
00777                                 $section = $this->displaySection( $value, $key );
00778                                 $legend = $this->getLegend( $key );
00779                                 if ( isset( $this->mSectionHeaders[$key] ) ) {
00780                                         $section = $this->mSectionHeaders[$key] . $section;
00781                                 }
00782                                 if ( isset( $this->mSectionFooters[$key] ) ) {
00783                                         $section .= $this->mSectionFooters[$key];
00784                                 }
00785                                 $attributes = array();
00786                                 if ( $fieldsetIDPrefix ) {
00787                                         $attributes['id'] = Sanitizer::escapeId( "$fieldsetIDPrefix$key" );
00788                                 }
00789                                 $subsectionHtml .= Xml::fieldset( $legend, $section, $attributes ) . "\n";
00790                         }
00791                 }
00792 
00793                 $classes = array();
00794 
00795                 if ( !$hasLeftColumn ) { // Avoid strange spacing when no labels exist
00796                         $classes[] = 'mw-htmlform-nolabel';
00797                 }
00798 
00799                 $attribs = array(
00800                         'class' => implode( ' ', $classes ),
00801                 );
00802 
00803                 if ( $sectionName ) {
00804                         $attribs['id'] = Sanitizer::escapeId( "mw-htmlform-$sectionName" );
00805                 }
00806 
00807                 $tableHtml = Html::rawElement( 'table', $attribs,
00808                         Html::rawElement( 'tbody', array(), "\n$tableHtml\n" ) ) . "\n";
00809 
00810                 if ( $this->mSubSectionBeforeFields ) {
00811                         return $subsectionHtml . "\n" . $tableHtml;
00812                 } else {
00813                         return $tableHtml . "\n" . $subsectionHtml;
00814                 }
00815         }
00816 
00820         function loadData() {
00821                 $fieldData = array();
00822 
00823                 foreach ( $this->mFlatFields as $fieldname => $field ) {
00824                         if ( !empty( $field->mParams['nodata'] ) ) {
00825                                 continue;
00826                         } elseif ( !empty( $field->mParams['disabled'] ) ) {
00827                                 $fieldData[$fieldname] = $field->getDefault();
00828                         } else {
00829                                 $fieldData[$fieldname] = $field->loadDataFromRequest( $this->getRequest() );
00830                         }
00831                 }
00832 
00833                 # Filter data.
00834                 foreach ( $fieldData as $name => &$value ) {
00835                         $field = $this->mFlatFields[$name];
00836                         $value = $field->filter( $value, $this->mFlatFields );
00837                 }
00838 
00839                 $this->mFieldData = $fieldData;
00840         }
00841 
00847         function suppressReset( $suppressReset = true ) {
00848                 $this->mShowReset = !$suppressReset;
00849         }
00850 
00858         function filterDataForSubmit( $data ) {
00859                 return $data;
00860         }
00861 
00868         public function getLegend( $key ) {
00869                 return wfMsg( "{$this->mMessagePrefix}-$key" );
00870         }
00871 
00880         public function setAction( $action ) {
00881                 $this->mAction = $action;
00882         }
00883 
00884 }
00885 
00890 abstract class HTMLFormField {
00891 
00892         protected $mValidationCallback;
00893         protected $mFilterCallback;
00894         protected $mName;
00895         public $mParams;
00896         protected $mLabel;      # String label.  Set on construction
00897         protected $mID;
00898         protected $mClass = '';
00899         protected $mDefault;
00900 
00904         public $mParent;
00905 
00914         abstract function getInputHTML( $value );
00915 
00924         function validate( $value, $alldata ) {
00925                 if ( isset( $this->mParams['required'] ) && $value === '' ) {
00926                         return wfMsgExt( 'htmlform-required', 'parseinline' );
00927                 }
00928 
00929                 if ( isset( $this->mValidationCallback ) ) {
00930                         return call_user_func( $this->mValidationCallback, $value, $alldata, $this->mParent );
00931                 }
00932 
00933                 return true;
00934         }
00935 
00936         function filter( $value, $alldata ) {
00937                 if ( isset( $this->mFilterCallback ) ) {
00938                         $value = call_user_func( $this->mFilterCallback, $value, $alldata, $this->mParent );
00939                 }
00940 
00941                 return $value;
00942         }
00943 
00950         protected function needsLabel() {
00951                 return true;
00952         }
00953 
00960         function loadDataFromRequest( $request ) {
00961                 if ( $request->getCheck( $this->mName ) ) {
00962                         return $request->getText( $this->mName );
00963                 } else {
00964                         return $this->getDefault();
00965                 }
00966         }
00967 
00972         function __construct( $params ) {
00973                 $this->mParams = $params;
00974 
00975                 # Generate the label from a message, if possible
00976                 if ( isset( $params['label-message'] ) ) {
00977                         $msgInfo = $params['label-message'];
00978 
00979                         if ( is_array( $msgInfo ) ) {
00980                                 $msg = array_shift( $msgInfo );
00981                         } else {
00982                                 $msg = $msgInfo;
00983                                 $msgInfo = array();
00984                         }
00985 
00986                         $this->mLabel = wfMsgExt( $msg, 'parseinline', $msgInfo );
00987                 } elseif ( isset( $params['label'] ) ) {
00988                         $this->mLabel = $params['label'];
00989                 }
00990 
00991                 $this->mName = "wp{$params['fieldname']}";
00992                 if ( isset( $params['name'] ) ) {
00993                         $this->mName = $params['name'];
00994                 }
00995 
00996                 $validName = Sanitizer::escapeId( $this->mName );
00997                 if ( $this->mName != $validName && !isset( $params['nodata'] ) ) {
00998                         throw new MWException( "Invalid name '{$this->mName}' passed to " . __METHOD__ );
00999                 }
01000 
01001                 $this->mID = "mw-input-{$this->mName}";
01002 
01003                 if ( isset( $params['default'] ) ) {
01004                         $this->mDefault = $params['default'];
01005                 }
01006 
01007                 if ( isset( $params['id'] ) ) {
01008                         $id = $params['id'];
01009                         $validId = Sanitizer::escapeId( $id );
01010 
01011                         if ( $id != $validId ) {
01012                                 throw new MWException( "Invalid id '$id' passed to " . __METHOD__ );
01013                         }
01014 
01015                         $this->mID = $id;
01016                 }
01017 
01018                 if ( isset( $params['cssclass'] ) ) {
01019                         $this->mClass = $params['cssclass'];
01020                 }
01021 
01022                 if ( isset( $params['validation-callback'] ) ) {
01023                         $this->mValidationCallback = $params['validation-callback'];
01024                 }
01025 
01026                 if ( isset( $params['filter-callback'] ) ) {
01027                         $this->mFilterCallback = $params['filter-callback'];
01028                 }
01029 
01030                 if ( isset( $params['flatlist'] ) ){
01031                         $this->mClass .= ' mw-htmlform-flatlist';
01032                 }
01033         }
01034 
01041         function getTableRow( $value ) {
01042                 # Check for invalid data.
01043 
01044                 $errors = $this->validate( $value, $this->mParent->mFieldData );
01045 
01046                 $cellAttributes = array();
01047                 $verticalLabel = false;
01048 
01049                 if ( !empty($this->mParams['vertical-label']) ) {
01050                         $cellAttributes['colspan'] = 2;
01051                         $verticalLabel = true;
01052                 }
01053 
01054                 if ( $errors === true || ( !$this->mParent->getRequest()->wasPosted() && ( $this->mParent->getMethod() == 'post' ) ) ) {
01055                         $errors = '';
01056                         $errorClass = '';
01057                 } else {
01058                         $errors = self::formatErrors( $errors );
01059                         $errorClass = 'mw-htmlform-invalid-input';
01060                 }
01061 
01062                 $label = $this->getLabelHtml( $cellAttributes );
01063                 $field = Html::rawElement(
01064                         'td',
01065                         array( 'class' => 'mw-input' ) + $cellAttributes,
01066                         $this->getInputHTML( $value ) . "\n$errors"
01067                 );
01068 
01069                 $fieldType = get_class( $this );
01070 
01071                 if ( $verticalLabel ) {
01072                         $html = Html::rawElement( 'tr',
01073                                 array( 'class' => 'mw-htmlform-vertical-label' ), $label );
01074                         $html .= Html::rawElement( 'tr',
01075                                 array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
01076                                 $field );
01077                 } else {
01078                         $html = Html::rawElement( 'tr',
01079                                 array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
01080                                 $label . $field );
01081                 }
01082 
01083                 $helptext = null;
01084 
01085                 if ( isset( $this->mParams['help-message'] ) ) {
01086                         $msg = wfMessage( $this->mParams['help-message'] );
01087                         if ( $msg->exists() ) {
01088                                 $helptext = $msg->parse();
01089                         }
01090                 } elseif ( isset( $this->mParams['help-messages'] ) ) {
01091                         # help-message can be passed a message key (string) or an array containing
01092                         # a message key and additional parameters. This makes it impossible to pass
01093                         # an array of message key
01094                         foreach( $this->mParams['help-messages'] as $name ) {
01095                                 $msg = wfMessage( $name );
01096                                 if( $msg->exists() ) {
01097                                         $helptext .= $msg->parse(); // append message
01098                                 }
01099                         }
01100                 } elseif ( isset( $this->mParams['help'] ) ) {
01101                         $helptext = $this->mParams['help'];
01102                 }
01103 
01104                 if ( !is_null( $helptext ) ) {
01105                         $row = Html::rawElement( 'td', array( 'colspan' => 2, 'class' => 'htmlform-tip' ),
01106                                 $helptext );
01107                         $row = Html::rawElement( 'tr', array(), $row );
01108                         $html .= "$row\n";
01109                 }
01110 
01111                 return $html;
01112         }
01113 
01114         function getLabel() {
01115                 return $this->mLabel;
01116         }
01117         function getLabelHtml( $cellAttributes = array() ) {
01118                 # Don't output a for= attribute for labels with no associated input.
01119                 # Kind of hacky here, possibly we don't want these to be <label>s at all.
01120                 $for = array();
01121 
01122                 if ( $this->needsLabel() ) {
01123                         $for['for'] = $this->mID;
01124                 }
01125 
01126                 return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes,
01127                         Html::rawElement( 'label', $for, $this->getLabel() )
01128                 );
01129         }
01130 
01131         function getDefault() {
01132                 if ( isset( $this->mDefault ) ) {
01133                         return $this->mDefault;
01134                 } else {
01135                         return null;
01136                 }
01137         }
01138 
01144         public function getTooltipAndAccessKey() {
01145                 if ( empty( $this->mParams['tooltip'] ) ) {
01146                         return array();
01147                 }
01148                 return Linker::tooltipAndAccesskeyAttribs( $this->mParams['tooltip'] );
01149         }
01150 
01158         public static function flattenOptions( $options ) {
01159                 $flatOpts = array();
01160 
01161                 foreach ( $options as $value ) {
01162                         if ( is_array( $value ) ) {
01163                                 $flatOpts = array_merge( $flatOpts, self::flattenOptions( $value ) );
01164                         } else {
01165                                 $flatOpts[] = $value;
01166                         }
01167                 }
01168 
01169                 return $flatOpts;
01170         }
01171 
01178         protected static function formatErrors( $errors ) {
01179                 if ( is_array( $errors ) && count( $errors ) === 1 ) {
01180                         $errors = array_shift( $errors );
01181                 }
01182 
01183                 if ( is_array( $errors ) ) {
01184                         $lines = array();
01185                         foreach ( $errors as $error ) {
01186                                 if ( $error instanceof Message ) {
01187                                         $lines[] = Html::rawElement( 'li', array(), $error->parse() );
01188                                 } else {
01189                                         $lines[] = Html::rawElement( 'li', array(), $error );
01190                                 }
01191                         }
01192                         return Html::rawElement( 'ul', array( 'class' => 'error' ), implode( "\n", $lines ) );
01193                 } else {
01194                         if ( $errors instanceof Message ) {
01195                                 $errors = $errors->parse();
01196                         }
01197                         return Html::rawElement( 'span', array( 'class' => 'error' ), $errors );
01198                 }
01199         }
01200 }
01201 
01202 class HTMLTextField extends HTMLFormField {
01203         function getSize() {
01204                 return isset( $this->mParams['size'] )
01205                         ? $this->mParams['size']
01206                         : 45;
01207         }
01208 
01209         function getInputHTML( $value ) {
01210                 $attribs = array(
01211                         'id' => $this->mID,
01212                         'name' => $this->mName,
01213                         'size' => $this->getSize(),
01214                         'value' => $value,
01215                 ) + $this->getTooltipAndAccessKey();
01216 
01217                 if ( $this->mClass !== '' ) {
01218                         $attribs['class'] = $this->mClass;
01219                 }
01220                 
01221                 if ( isset( $this->mParams['maxlength'] ) ) {
01222                         $attribs['maxlength'] = $this->mParams['maxlength'];
01223                 }
01224 
01225                 if ( !empty( $this->mParams['disabled'] ) ) {
01226                         $attribs['disabled'] = 'disabled';
01227                 }
01228 
01229                 # TODO: Enforce pattern, step, required, readonly on the server side as
01230                 # well
01231                 foreach ( array( 'min', 'max', 'pattern', 'title', 'step',
01232                 'placeholder' ) as $param ) {
01233                         if ( isset( $this->mParams[$param] ) ) {
01234                                 $attribs[$param] = $this->mParams[$param];
01235                         }
01236                 }
01237 
01238                 foreach ( array( 'required', 'autofocus', 'multiple', 'readonly' ) as $param ) {
01239                         if ( isset( $this->mParams[$param] ) ) {
01240                                 $attribs[$param] = '';
01241                         }
01242                 }
01243 
01244                 # Implement tiny differences between some field variants
01245                 # here, rather than creating a new class for each one which
01246                 # is essentially just a clone of this one.
01247                 if ( isset( $this->mParams['type'] ) ) {
01248                         switch ( $this->mParams['type'] ) {
01249                                 case 'email':
01250                                         $attribs['type'] = 'email';
01251                                         break;
01252                                 case 'int':
01253                                         $attribs['type'] = 'number';
01254                                         break;
01255                                 case 'float':
01256                                         $attribs['type'] = 'number';
01257                                         $attribs['step'] = 'any';
01258                                         break;
01259                                 # Pass through
01260                                 case 'password':
01261                                 case 'file':
01262                                         $attribs['type'] = $this->mParams['type'];
01263                                         break;
01264                         }
01265                 }
01266 
01267                 return Html::element( 'input', $attribs );
01268         }
01269 }
01270 class HTMLTextAreaField extends HTMLFormField {
01271         function getCols() {
01272                 return isset( $this->mParams['cols'] )
01273                         ? $this->mParams['cols']
01274                         : 80;
01275         }
01276 
01277         function getRows() {
01278                 return isset( $this->mParams['rows'] )
01279                         ? $this->mParams['rows']
01280                         : 25;
01281         }
01282 
01283         function getInputHTML( $value ) {
01284                 $attribs = array(
01285                         'id' => $this->mID,
01286                         'name' => $this->mName,
01287                         'cols' => $this->getCols(),
01288                         'rows' => $this->getRows(),
01289                 ) + $this->getTooltipAndAccessKey();
01290 
01291                 if ( $this->mClass !== '' ) {
01292                         $attribs['class'] = $this->mClass;
01293                 }
01294                 
01295                 if ( !empty( $this->mParams['disabled'] ) ) {
01296                         $attribs['disabled'] = 'disabled';
01297                 }
01298 
01299                 if ( !empty( $this->mParams['readonly'] ) ) {
01300                         $attribs['readonly'] = 'readonly';
01301                 }
01302 
01303                 foreach ( array( 'required', 'autofocus' ) as $param ) {
01304                         if ( isset( $this->mParams[$param] ) ) {
01305                                 $attribs[$param] = '';
01306                         }
01307                 }
01308 
01309                 return Html::element( 'textarea', $attribs, $value );
01310         }
01311 }
01312 
01316 class HTMLFloatField extends HTMLTextField {
01317         function getSize() {
01318                 return isset( $this->mParams['size'] )
01319                         ? $this->mParams['size']
01320                         : 20;
01321         }
01322 
01323         function validate( $value, $alldata ) {
01324                 $p = parent::validate( $value, $alldata );
01325 
01326                 if ( $p !== true ) {
01327                         return $p;
01328                 }
01329 
01330                 $value = trim( $value );
01331 
01332                 # http://dev.w3.org/html5/spec/common-microsyntaxes.html#real-numbers
01333                 # with the addition that a leading '+' sign is ok.
01334                 if ( !preg_match( '/^((\+|\-)?\d+(\.\d+)?(E(\+|\-)?\d+)?)?$/i', $value ) ) {
01335                         return wfMsgExt( 'htmlform-float-invalid', 'parse' );
01336                 }
01337 
01338                 # The "int" part of these message names is rather confusing.
01339                 # They make equal sense for all numbers.
01340                 if ( isset( $this->mParams['min'] ) ) {
01341                         $min = $this->mParams['min'];
01342 
01343                         if ( $min > $value ) {
01344                                 return wfMsgExt( 'htmlform-int-toolow', 'parse', array( $min ) );
01345                         }
01346                 }
01347 
01348                 if ( isset( $this->mParams['max'] ) ) {
01349                         $max = $this->mParams['max'];
01350 
01351                         if ( $max < $value ) {
01352                                 return wfMsgExt( 'htmlform-int-toohigh', 'parse', array( $max ) );
01353                         }
01354                 }
01355 
01356                 return true;
01357         }
01358 }
01359 
01363 class HTMLIntField extends HTMLFloatField {
01364         function validate( $value, $alldata ) {
01365                 $p = parent::validate( $value, $alldata );
01366 
01367                 if ( $p !== true ) {
01368                         return $p;
01369                 }
01370 
01371                 # http://dev.w3.org/html5/spec/common-microsyntaxes.html#signed-integers
01372                 # with the addition that a leading '+' sign is ok. Note that leading zeros
01373                 # are fine, and will be left in the input, which is useful for things like
01374                 # phone numbers when you know that they are integers (the HTML5 type=tel
01375                 # input does not require its value to be numeric).  If you want a tidier
01376                 # value to, eg, save in the DB, clean it up with intval().
01377                 if ( !preg_match( '/^((\+|\-)?\d+)?$/', trim( $value ) )
01378                 ) {
01379                         return wfMsgExt( 'htmlform-int-invalid', 'parse' );
01380                 }
01381 
01382                 return true;
01383         }
01384 }
01385 
01389 class HTMLCheckField extends HTMLFormField {
01390         function getInputHTML( $value ) {
01391                 if ( !empty( $this->mParams['invert'] ) ) {
01392                         $value = !$value;
01393                 }
01394 
01395                 $attr = $this->getTooltipAndAccessKey();
01396                 $attr['id'] = $this->mID;
01397 
01398                 if ( !empty( $this->mParams['disabled'] ) ) {
01399                         $attr['disabled'] = 'disabled';
01400                 }
01401                 
01402                 if ( $this->mClass !== '' ) {
01403                         $attr['class'] = $this->mClass;
01404                 }
01405 
01406                 return Xml::check( $this->mName, $value, $attr ) . '&#160;' .
01407                         Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
01408         }
01409 
01415         function getLabel() {
01416                 return '&#160;';
01417         }
01418 
01423         function loadDataFromRequest( $request ) {
01424                 $invert = false;
01425                 if ( isset( $this->mParams['invert'] ) && $this->mParams['invert'] ) {
01426                         $invert = true;
01427                 }
01428 
01429                 // GetCheck won't work like we want for checks.
01430                 // Fetch the value in either one of the two following case:
01431                 // - we have a valid token (form got posted or GET forged by the user)
01432                 // - checkbox name has a value (false or true), ie is not null
01433                 if ( $request->getCheck( 'wpEditToken' ) || $request->getVal( $this->mName )!== null ) {
01434                         // XOR has the following truth table, which is what we want
01435                         // INVERT VALUE | OUTPUT
01436                         // true   true  | false
01437                         // false  true  | true
01438                         // false  false | false
01439                         // true   false | true
01440                         return $request->getBool( $this->mName ) xor $invert;
01441                 } else {
01442                         return $this->getDefault();
01443                 }
01444         }
01445 }
01446 
01450 class HTMLSelectField extends HTMLFormField {
01451         function validate( $value, $alldata ) {
01452                 $p = parent::validate( $value, $alldata );
01453 
01454                 if ( $p !== true ) {
01455                         return $p;
01456                 }
01457 
01458                 $validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] );
01459 
01460                 if ( in_array( $value, $validOptions ) )
01461                         return true;
01462                 else
01463                         return wfMsgExt( 'htmlform-select-badoption', 'parseinline' );
01464         }
01465 
01466         function getInputHTML( $value ) {
01467                 $select = new XmlSelect( $this->mName, $this->mID, strval( $value ) );
01468 
01469                 # If one of the options' 'name' is int(0), it is automatically selected.
01470                 # because PHP sucks and thinks int(0) == 'some string'.
01471                 # Working around this by forcing all of them to strings.
01472                 foreach( $this->mParams['options'] as &$opt ){
01473                         if( is_int( $opt ) ){
01474                                 $opt = strval( $opt );
01475                         }
01476                 }
01477                 unset( $opt ); # PHP keeps $opt around as a reference, which is a bit scary
01478 
01479                 if ( !empty( $this->mParams['disabled'] ) ) {
01480                         $select->setAttribute( 'disabled', 'disabled' );
01481                 }
01482                 
01483                 if ( $this->mClass !== '' ) {
01484                         $select->setAttribute( 'class', $this->mClass );
01485                 }
01486 
01487                 $select->addOptions( $this->mParams['options'] );
01488 
01489                 return $select->getHTML();
01490         }
01491 }
01492 
01496 class HTMLSelectOrOtherField extends HTMLTextField {
01497         static $jsAdded = false;
01498 
01499         function __construct( $params ) {
01500                 if ( !in_array( 'other', $params['options'], true ) ) {
01501                         $msg = isset( $params['other'] ) ? $params['other'] : wfMsg( 'htmlform-selectorother-other' );
01502                         $params['options'][$msg] = 'other';
01503                 }
01504 
01505                 parent::__construct( $params );
01506         }
01507 
01508         static function forceToStringRecursive( $array ) {
01509                 if ( is_array( $array ) ) {
01510                         return array_map( array( __CLASS__, 'forceToStringRecursive' ), $array );
01511                 } else {
01512                         return strval( $array );
01513                 }
01514         }
01515 
01516         function getInputHTML( $value ) {
01517                 $valInSelect = false;
01518 
01519                 if ( $value !== false ) {
01520                         $valInSelect = in_array(
01521                                 $value,
01522                                 HTMLFormField::flattenOptions( $this->mParams['options'] )
01523                         );
01524                 }
01525 
01526                 $selected = $valInSelect ? $value : 'other';
01527 
01528                 $opts = self::forceToStringRecursive( $this->mParams['options'] );
01529 
01530                 $select = new XmlSelect( $this->mName, $this->mID, $selected );
01531                 $select->addOptions( $opts );
01532 
01533                 $select->setAttribute( 'class', 'mw-htmlform-select-or-other' );
01534 
01535                 $tbAttribs = array( 'id' => $this->mID . '-other', 'size' => $this->getSize() );
01536 
01537                 if ( !empty( $this->mParams['disabled'] ) ) {
01538                         $select->setAttribute( 'disabled', 'disabled' );
01539                         $tbAttribs['disabled'] = 'disabled';
01540                 }
01541 
01542                 $select = $select->getHTML();
01543 
01544                 if ( isset( $this->mParams['maxlength'] ) ) {
01545                         $tbAttribs['maxlength'] = $this->mParams['maxlength'];
01546                 }
01547                 
01548                 if ( $this->mClass !== '' ) {
01549                         $tbAttribs['class'] = $this->mClass;
01550                 }
01551 
01552                 $textbox = Html::input(
01553                         $this->mName . '-other',
01554                         $valInSelect ? '' : $value,
01555                         'text',
01556                         $tbAttribs
01557                 );
01558 
01559                 return "$select<br />\n$textbox";
01560         }
01561 
01566         function loadDataFromRequest( $request ) {
01567                 if ( $request->getCheck( $this->mName ) ) {
01568                         $val = $request->getText( $this->mName );
01569 
01570                         if ( $val == 'other' ) {
01571                                 $val = $request->getText( $this->mName . '-other' );
01572                         }
01573 
01574                         return $val;
01575                 } else {
01576                         return $this->getDefault();
01577                 }
01578         }
01579 }
01580 
01584 class HTMLMultiSelectField extends HTMLFormField {
01585 
01586         function validate( $value, $alldata ) {
01587                 $p = parent::validate( $value, $alldata );
01588 
01589                 if ( $p !== true ) {
01590                         return $p;
01591                 }
01592 
01593                 if ( !is_array( $value ) ) {
01594                         return false;
01595                 }
01596 
01597                 # If all options are valid, array_intersect of the valid options
01598                 # and the provided options will return the provided options.
01599                 $validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] );
01600 
01601                 $validValues = array_intersect( $value, $validOptions );
01602                 if ( count( $validValues ) == count( $value ) ) {
01603                         return true;
01604                 } else {
01605                         return wfMsgExt( 'htmlform-select-badoption', 'parseinline' );
01606                 }
01607         }
01608 
01609         function getInputHTML( $value ) {
01610                 $html = $this->formatOptions( $this->mParams['options'], $value );
01611 
01612                 return $html;
01613         }
01614 
01615         function formatOptions( $options, $value ) {
01616                 $html = '';
01617 
01618                 $attribs = array();
01619 
01620                 if ( !empty( $this->mParams['disabled'] ) ) {
01621                         $attribs['disabled'] = 'disabled';
01622                 }
01623 
01624                 foreach ( $options as $label => $info ) {
01625                         if ( is_array( $info ) ) {
01626                                 $html .= Html::rawElement( 'h1', array(), $label ) . "\n";
01627                                 $html .= $this->formatOptions( $info, $value );
01628                         } else {
01629                                 $thisAttribs = array( 'id' => "{$this->mID}-$info", 'value' => $info );
01630 
01631                                 $checkbox = Xml::check(
01632                                         $this->mName . '[]',
01633                                         in_array( $info, $value, true ),
01634                                         $attribs + $thisAttribs );
01635                                 $checkbox .= '&#160;' . Html::rawElement( 'label', array( 'for' => "{$this->mID}-$info" ), $label );
01636 
01637                                 $html .= ' ' . Html::rawElement( 'div', array( 'class' => 'mw-htmlform-flatlist-item' ), $checkbox );
01638                         }
01639                 }
01640 
01641                 return $html;
01642         }
01643 
01648         function loadDataFromRequest( $request ) {
01649                 if ( $this->mParent->getMethod() == 'post' ) {
01650                         if( $request->wasPosted() ){
01651                                 # Checkboxes are just not added to the request arrays if they're not checked,
01652                                 # so it's perfectly possible for there not to be an entry at all
01653                                 return $request->getArray( $this->mName, array() );
01654                         } else {
01655                                 # That's ok, the user has not yet submitted the form, so show the defaults
01656                                 return $this->getDefault();
01657                         }
01658                 } else {
01659                         # This is the impossible case: if we look at $_GET and see no data for our
01660                         # field, is it because the user has not yet submitted the form, or that they
01661                         # have submitted it with all the options unchecked? We will have to assume the
01662                         # latter, which basically means that you can't specify 'positive' defaults
01663                         # for GET forms.
01664                         # @todo FIXME...
01665                         return $request->getArray( $this->mName, array() );
01666                 }
01667         }
01668 
01669         function getDefault() {
01670                 if ( isset( $this->mDefault ) ) {
01671                         return $this->mDefault;
01672                 } else {
01673                         return array();
01674                 }
01675         }
01676 
01677         protected function needsLabel() {
01678                 return false;
01679         }
01680 }
01681 
01692 class HTMLSelectAndOtherField extends HTMLSelectField {
01693 
01694         function __construct( $params ) {
01695                 if ( array_key_exists( 'other', $params ) ) {
01696                 } elseif( array_key_exists( 'other-message', $params ) ){
01697                         $params['other'] = wfMessage( $params['other-message'] )->plain();
01698                 } else {
01699                         $params['other'] = null;
01700                 }
01701 
01702                 if ( array_key_exists( 'options', $params ) ) {
01703                         # Options array already specified
01704                 } elseif( array_key_exists( 'options-message', $params ) ){
01705                         # Generate options array from a system message
01706                         $params['options'] = self::parseMessage(
01707                                 wfMessage( $params['options-message'] )->inContentLanguage()->plain(),
01708                                 $params['other']
01709                         );
01710                 } else {
01711                         # Sulk
01712                         throw new MWException( 'HTMLSelectAndOtherField called without any options' );
01713                 }
01714                 $this->mFlatOptions = self::flattenOptions( $params['options'] );
01715 
01716                 parent::__construct( $params );
01717         }
01718 
01726         public static function parseMessage( $string, $otherName=null ) {
01727                 if( $otherName === null ){
01728                         $otherName = wfMessage( 'htmlform-selectorother-other' )->plain();
01729                 }
01730 
01731                 $optgroup = false;
01732                 $options = array( $otherName => 'other' );
01733 
01734                 foreach ( explode( "\n", $string ) as $option ) {
01735                         $value = trim( $option );
01736                         if ( $value == '' ) {
01737                                 continue;
01738                         } elseif ( substr( $value, 0, 1) == '*' && substr( $value, 1, 1) != '*' ) {
01739                                 # A new group is starting...
01740                                 $value = trim( substr( $value, 1 ) );
01741                                 $optgroup = $value;
01742                         } elseif ( substr( $value, 0, 2) == '**' ) {
01743                                 # groupmember
01744                                 $opt = trim( substr( $value, 2 ) );
01745                                 if( $optgroup === false ){
01746                                         $options[$opt] = $opt;
01747                                 } else {
01748                                         $options[$optgroup][$opt] = $opt;
01749                                 }
01750                         } else {
01751                                 # groupless reason list
01752                                 $optgroup = false;
01753                                 $options[$option] = $option;
01754                         }
01755                 }
01756 
01757                 return $options;
01758         }
01759 
01760         function getInputHTML( $value ) {
01761                 $select = parent::getInputHTML( $value[1] );
01762 
01763                 $textAttribs = array(
01764                         'id' => $this->mID . '-other',
01765                         'size' => $this->getSize(),
01766                 );
01767                 
01768                 if ( $this->mClass !== '' ) {
01769                         $textAttribs['class'] = $this->mClass;
01770                 }
01771 
01772                 foreach ( array( 'required', 'autofocus', 'multiple', 'disabled' ) as $param ) {
01773                         if ( isset( $this->mParams[$param] ) ) {
01774                                 $textAttribs[$param] = '';
01775                         }
01776                 }
01777 
01778                 $textbox = Html::input(
01779                         $this->mName . '-other',
01780                         $value[2],
01781                         'text',
01782                         $textAttribs
01783                 );
01784 
01785                 return "$select<br />\n$textbox";
01786         }
01787 
01792         function loadDataFromRequest( $request ) {
01793                 if ( $request->getCheck( $this->mName ) ) {
01794 
01795                         $list = $request->getText( $this->mName );
01796                         $text = $request->getText( $this->mName . '-other' );
01797 
01798                         if ( $list == 'other' ) {
01799                                 $final = $text;
01800                         } elseif( !in_array( $list, $this->mFlatOptions ) ){
01801                                 # User has spoofed the select form to give an option which wasn't
01802                                 # in the original offer.  Sulk...
01803                                 $final = $text;
01804                         } elseif( $text == '' ) {
01805                                 $final = $list;
01806                         } else {
01807                                 $final = $list . wfMsgForContent( 'colon-separator' ) . $text;
01808                         }
01809 
01810                 } else {
01811                         $final = $this->getDefault();
01812 
01813                         $list = 'other';
01814                         $text = $final;
01815                         foreach ( $this->mFlatOptions as $option ) {
01816                                 $match = $option . wfMsgForContent( 'colon-separator' );
01817                                 if( strpos( $text, $match ) === 0 ) {
01818                                         $list = $option;
01819                                         $text = substr( $text, strlen( $match ) );
01820                                         break;
01821                                 }
01822                         }
01823                 }
01824                 return array( $final, $list, $text );
01825         }
01826 
01827         function getSize() {
01828                 return isset( $this->mParams['size'] )
01829                         ? $this->mParams['size']
01830                         : 45;
01831         }
01832 
01833         function validate( $value, $alldata ) {
01834                 # HTMLSelectField forces $value to be one of the options in the select
01835                 # field, which is not useful here.  But we do want the validation further up
01836                 # the chain
01837                 $p = parent::validate( $value[1], $alldata );
01838 
01839                 if ( $p !== true ) {
01840                         return $p;
01841                 }
01842 
01843                 if( isset( $this->mParams['required'] ) && $value[1] === '' ){
01844                         return wfMsgExt( 'htmlform-required', 'parseinline' );
01845                 }
01846 
01847                 return true;
01848         }
01849 }
01850 
01854 class HTMLRadioField extends HTMLFormField {
01855 
01856 
01857         function validate( $value, $alldata ) {
01858                 $p = parent::validate( $value, $alldata );
01859 
01860                 if ( $p !== true ) {
01861                         return $p;
01862                 }
01863 
01864                 if ( !is_string( $value ) && !is_int( $value ) ) {
01865                         return false;
01866                 }
01867 
01868                 $validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] );
01869 
01870                 if ( in_array( $value, $validOptions ) ) {
01871                         return true;
01872                 } else {
01873                         return wfMsgExt( 'htmlform-select-badoption', 'parseinline' );
01874                 }
01875         }
01876 
01883         function getInputHTML( $value ) {
01884                 $html = $this->formatOptions( $this->mParams['options'], $value );
01885 
01886                 return $html;
01887         }
01888 
01889         function formatOptions( $options, $value ) {
01890                 $html = '';
01891 
01892                 $attribs = array();
01893                 if ( !empty( $this->mParams['disabled'] ) ) {
01894                         $attribs['disabled'] = 'disabled';
01895                 }
01896 
01897                 # TODO: should this produce an unordered list perhaps?
01898                 foreach ( $options as $label => $info ) {
01899                         if ( is_array( $info ) ) {
01900                                 $html .= Html::rawElement( 'h1', array(), $label ) . "\n";
01901                                 $html .= $this->formatOptions( $info, $value );
01902                         } else {
01903                                 $id = Sanitizer::escapeId( $this->mID . "-$info" );
01904                                 $radio = Xml::radio(
01905                                         $this->mName,
01906                                         $info,
01907                                         $info == $value,
01908                                         $attribs + array( 'id' => $id )
01909                                 );
01910                                 $radio .= '&#160;' .
01911                                                 Html::rawElement( 'label', array( 'for' => $id ), $label );
01912 
01913                                 $html .= ' ' . Html::rawElement( 'div', array( 'class' => 'mw-htmlform-flatlist-item' ), $radio );
01914                         }
01915                 }
01916 
01917                 return $html;
01918         }
01919 
01920         protected function needsLabel() {
01921                 return false;
01922         }
01923 }
01924 
01928 class HTMLInfoField extends HTMLFormField {
01929         function __construct( $info ) {
01930                 $info['nodata'] = true;
01931 
01932                 parent::__construct( $info );
01933         }
01934 
01935         function getInputHTML( $value ) {
01936                 return !empty( $this->mParams['raw'] ) ? $value : htmlspecialchars( $value );
01937         }
01938 
01939         function getTableRow( $value ) {
01940                 if ( !empty( $this->mParams['rawrow'] ) ) {
01941                         return $value;
01942                 }
01943 
01944                 return parent::getTableRow( $value );
01945         }
01946 
01947         protected function needsLabel() {
01948                 return false;
01949         }
01950 }
01951 
01952 class HTMLHiddenField extends HTMLFormField {
01953         public function __construct( $params ) {
01954                 parent::__construct( $params );
01955 
01956                 # Per HTML5 spec, hidden fields cannot be 'required'
01957                 # http://dev.w3.org/html5/spec/states-of-the-type-attribute.html#hidden-state
01958                 unset( $this->mParams['required'] );
01959         }
01960 
01961         public function getTableRow( $value ) {
01962                 $params = array();
01963                 if ( $this->mID ) {
01964                         $params['id'] = $this->mID;
01965                 }
01966 
01967                 $this->mParent->addHiddenField(
01968                         $this->mName,
01969                         $this->mDefault,
01970                         $params
01971                 );
01972 
01973                 return '';
01974         }
01975 
01976         public function getInputHTML( $value ) { return ''; }
01977 }
01978 
01983 class HTMLSubmitField extends HTMLFormField {
01984 
01985         function __construct( $info ) {
01986                 $info['nodata'] = true;
01987                 parent::__construct( $info );
01988         }
01989 
01990         function getInputHTML( $value ) {
01991                 return Xml::submitButton(
01992                         $value,
01993                         array(
01994                                 'class' => 'mw-htmlform-submit ' . $this->mClass,
01995                                 'name' => $this->mName,
01996                                 'id' => $this->mID,
01997                         )
01998                 );
01999         }
02000 
02001         protected function needsLabel() {
02002                 return false;
02003         }
02004 
02011         public function validate( $value, $alldata ){
02012                 return true;
02013         }
02014 }
02015 
02016 class HTMLEditTools extends HTMLFormField {
02017         public function getInputHTML( $value ) {
02018                 return '';
02019         }
02020 
02021         public function getTableRow( $value ) {
02022                 if ( empty( $this->mParams['message'] ) ) {
02023                         $msg = wfMessage( 'edittools' );
02024                 } else {
02025                         $msg = wfMessage( $this->mParams['message'] );
02026                         if ( $msg->isDisabled() ) {
02027                                 $msg = wfMessage( 'edittools' );
02028                         }
02029                 }
02030                 $msg->inContentLanguage();
02031 
02032 
02033                 return '<tr><td></td><td class="mw-input">'
02034                         . '<div class="mw-editTools">'
02035                         . $msg->parseAsBlock()
02036                         . "</div></td></tr>\n";
02037         }
02038 }
02039 
02040 class HTMLApiField extends HTMLFormField {
02041         public function getTableRow( $value ) {
02042                 return '';
02043         }
02044 
02045         public function getDiv( $value ) {
02046                 return $this->getTableRow( $value );
02047         }
02048 
02049         public function getRaw( $value ) {
02050                 return $this->getTableRow( $value );
02051         }
02052 
02053         public function getInputHTML( $value ) {
02054                 return '';
02055         }
02056 }