MediaWiki  REL1_24
HTMLForm.php
Go to the documentation of this file.
00001 <?php
00002 
00101 class HTMLForm extends ContextSource {
00102     // A mapping of 'type' inputs onto standard HTMLFormField subclasses
00103     public static $typeMappings = array(
00104         'api' => 'HTMLApiField',
00105         'text' => 'HTMLTextField',
00106         'textarea' => 'HTMLTextAreaField',
00107         'select' => 'HTMLSelectField',
00108         'radio' => 'HTMLRadioField',
00109         'multiselect' => 'HTMLMultiSelectField',
00110         'limitselect' => 'HTMLSelectLimitField',
00111         'check' => 'HTMLCheckField',
00112         'toggle' => 'HTMLCheckField',
00113         'int' => 'HTMLIntField',
00114         'float' => 'HTMLFloatField',
00115         'info' => 'HTMLInfoField',
00116         'selectorother' => 'HTMLSelectOrOtherField',
00117         'selectandother' => 'HTMLSelectAndOtherField',
00118         'submit' => 'HTMLSubmitField',
00119         'hidden' => 'HTMLHiddenField',
00120         'edittools' => 'HTMLEditTools',
00121         'checkmatrix' => 'HTMLCheckMatrix',
00122         'cloner' => 'HTMLFormFieldCloner',
00123         'autocompleteselect' => 'HTMLAutoCompleteSelectField',
00124         // HTMLTextField will output the correct type="" attribute automagically.
00125         // There are about four zillion other HTML5 input types, like range, but
00126         // we don't use those at the moment, so no point in adding all of them.
00127         'email' => 'HTMLTextField',
00128         'password' => 'HTMLTextField',
00129         'url' => 'HTMLTextField',
00130     );
00131 
00132     public $mFieldData;
00133 
00134     protected $mMessagePrefix;
00135 
00137     protected $mFlatFields;
00138 
00139     protected $mFieldTree;
00140     protected $mShowReset = false;
00141     protected $mShowSubmit = true;
00142     protected $mSubmitModifierClass = 'mw-ui-constructive';
00143 
00144     protected $mSubmitCallback;
00145     protected $mValidationErrorMessage;
00146 
00147     protected $mPre = '';
00148     protected $mHeader = '';
00149     protected $mFooter = '';
00150     protected $mSectionHeaders = array();
00151     protected $mSectionFooters = array();
00152     protected $mPost = '';
00153     protected $mId;
00154     protected $mTableId = '';
00155 
00156     protected $mSubmitID;
00157     protected $mSubmitName;
00158     protected $mSubmitText;
00159     protected $mSubmitTooltip;
00160 
00161     protected $mTitle;
00162     protected $mMethod = 'post';
00163     protected $mWasSubmitted = false;
00164 
00170     protected $mAction = false;
00171 
00172     protected $mUseMultipart = false;
00173     protected $mHiddenFields = array();
00174     protected $mButtons = array();
00175 
00176     protected $mWrapperLegend = false;
00177 
00182     protected $mTokenSalt = '';
00183 
00191     protected $mSubSectionBeforeFields = true;
00192 
00198     protected $displayFormat = 'table';
00199 
00204     protected $availableDisplayFormats = array(
00205         'table',
00206         'div',
00207         'raw',
00208         'vform',
00209     );
00210 
00219     public function __construct( $descriptor, /*IContextSource*/ $context = null,
00220         $messagePrefix = ''
00221     ) {
00222         if ( $context instanceof IContextSource ) {
00223             $this->setContext( $context );
00224             $this->mTitle = false; // We don't need them to set a title
00225             $this->mMessagePrefix = $messagePrefix;
00226         } elseif ( is_null( $context ) && $messagePrefix !== '' ) {
00227             $this->mMessagePrefix = $messagePrefix;
00228         } elseif ( is_string( $context ) && $messagePrefix === '' ) {
00229             // B/C since 1.18
00230             // it's actually $messagePrefix
00231             $this->mMessagePrefix = $context;
00232         }
00233 
00234         // Expand out into a tree.
00235         $loadedDescriptor = array();
00236         $this->mFlatFields = array();
00237 
00238         foreach ( $descriptor as $fieldname => $info ) {
00239             $section = isset( $info['section'] )
00240                 ? $info['section']
00241                 : '';
00242 
00243             if ( isset( $info['type'] ) && $info['type'] == 'file' ) {
00244                 $this->mUseMultipart = true;
00245             }
00246 
00247             $field = self::loadInputFromParameters( $fieldname, $info );
00248             // FIXME During field's construct, the parent form isn't available!
00249             // could add a 'parent' name-value to $info, could add a third parameter.
00250             $field->mParent = $this;
00251 
00252             // vform gets too much space if empty labels generate HTML.
00253             if ( $this->isVForm() ) {
00254                 $field->setShowEmptyLabel( false );
00255             }
00256 
00257             $setSection =& $loadedDescriptor;
00258             if ( $section ) {
00259                 $sectionParts = explode( '/', $section );
00260 
00261                 while ( count( $sectionParts ) ) {
00262                     $newName = array_shift( $sectionParts );
00263 
00264                     if ( !isset( $setSection[$newName] ) ) {
00265                         $setSection[$newName] = array();
00266                     }
00267 
00268                     $setSection =& $setSection[$newName];
00269                 }
00270             }
00271 
00272             $setSection[$fieldname] = $field;
00273             $this->mFlatFields[$fieldname] = $field;
00274         }
00275 
00276         $this->mFieldTree = $loadedDescriptor;
00277     }
00278 
00289     public function setDisplayFormat( $format ) {
00290         if ( !in_array( $format, $this->availableDisplayFormats ) ) {
00291             throw new MWException( 'Display format must be one of ' .
00292                 print_r( $this->availableDisplayFormats, true ) );
00293         }
00294         $this->displayFormat = $format;
00295 
00296         return $this;
00297     }
00298 
00304     public function getDisplayFormat() {
00305         $format = $this->displayFormat;
00306         if ( !$this->getConfig()->get( 'HTMLFormAllowTableFormat' ) && $format === 'table' ) {
00307             $format = 'div';
00308         }
00309         return $format;
00310     }
00311 
00317     public function isVForm() {
00318         return $this->displayFormat === 'vform';
00319     }
00320 
00337     public static function getClassFromDescriptor( $fieldname, &$descriptor ) {
00338         if ( isset( $descriptor['class'] ) ) {
00339             $class = $descriptor['class'];
00340         } elseif ( isset( $descriptor['type'] ) ) {
00341             $class = self::$typeMappings[$descriptor['type']];
00342             $descriptor['class'] = $class;
00343         } else {
00344             $class = null;
00345         }
00346 
00347         if ( !$class ) {
00348             throw new MWException( "Descriptor with no class for $fieldname: "
00349                 . print_r( $descriptor, true ) );
00350         }
00351 
00352         return $class;
00353     }
00354 
00364     public static function loadInputFromParameters( $fieldname, $descriptor ) {
00365         $class = self::getClassFromDescriptor( $fieldname, $descriptor );
00366 
00367         $descriptor['fieldname'] = $fieldname;
00368 
00369         # @todo This will throw a fatal error whenever someone try to use
00370         # 'class' to feed a CSS class instead of 'cssclass'. Would be
00371         # great to avoid the fatal error and show a nice error.
00372         $obj = new $class( $descriptor );
00373 
00374         return $obj;
00375     }
00376 
00386     function prepareForm() {
00387         # Check if we have the info we need
00388         if ( !$this->mTitle instanceof Title && $this->mTitle !== false ) {
00389             throw new MWException( "You must call setTitle() on an HTMLForm" );
00390         }
00391 
00392         # Load data from the request.
00393         $this->loadData();
00394 
00395         return $this;
00396     }
00397 
00402     function tryAuthorizedSubmit() {
00403         $result = false;
00404 
00405         $submit = false;
00406         if ( $this->getMethod() != 'post' ) {
00407             $submit = true; // no session check needed
00408         } elseif ( $this->getRequest()->wasPosted() ) {
00409             $editToken = $this->getRequest()->getVal( 'wpEditToken' );
00410             if ( $this->getUser()->isLoggedIn() || $editToken != null ) {
00411                 // Session tokens for logged-out users have no security value.
00412                 // However, if the user gave one, check it in order to give a nice
00413                 // "session expired" error instead of "permission denied" or such.
00414                 $submit = $this->getUser()->matchEditToken( $editToken, $this->mTokenSalt );
00415             } else {
00416                 $submit = true;
00417             }
00418         }
00419 
00420         if ( $submit ) {
00421             $this->mWasSubmitted = true;
00422             $result = $this->trySubmit();
00423         }
00424 
00425         return $result;
00426     }
00427 
00434     function show() {
00435         $this->prepareForm();
00436 
00437         $result = $this->tryAuthorizedSubmit();
00438         if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) {
00439             return $result;
00440         }
00441 
00442         $this->displayForm( $result );
00443 
00444         return false;
00445     }
00446 
00458     function trySubmit() {
00459         $this->mWasSubmitted = true;
00460 
00461         # Check for cancelled submission
00462         foreach ( $this->mFlatFields as $fieldname => $field ) {
00463             if ( !empty( $field->mParams['nodata'] ) ) {
00464                 continue;
00465             }
00466             if ( $field->cancelSubmit( $this->mFieldData[$fieldname], $this->mFieldData ) ) {
00467                 $this->mWasSubmitted = false;
00468                 return false;
00469             }
00470         }
00471 
00472         # Check for validation
00473         foreach ( $this->mFlatFields as $fieldname => $field ) {
00474             if ( !empty( $field->mParams['nodata'] ) ) {
00475                 continue;
00476             }
00477             if ( $field->isHidden( $this->mFieldData ) ) {
00478                 continue;
00479             }
00480             if ( $field->validate(
00481                     $this->mFieldData[$fieldname],
00482                     $this->mFieldData )
00483                 !== true
00484             ) {
00485                 return isset( $this->mValidationErrorMessage )
00486                     ? $this->mValidationErrorMessage
00487                     : array( 'htmlform-invalid-input' );
00488             }
00489         }
00490 
00491         $callback = $this->mSubmitCallback;
00492         if ( !is_callable( $callback ) ) {
00493             throw new MWException( 'HTMLForm: no submit callback provided. Use ' .
00494                 'setSubmitCallback() to set one.' );
00495         }
00496 
00497         $data = $this->filterDataForSubmit( $this->mFieldData );
00498 
00499         $res = call_user_func( $callback, $data, $this );
00500         if ( $res === false ) {
00501             $this->mWasSubmitted = false;
00502         }
00503 
00504         return $res;
00505     }
00506 
00518     function wasSubmitted() {
00519         return $this->mWasSubmitted;
00520     }
00521 
00532     function setSubmitCallback( $cb ) {
00533         $this->mSubmitCallback = $cb;
00534 
00535         return $this;
00536     }
00537 
00546     function setValidationErrorMessage( $msg ) {
00547         $this->mValidationErrorMessage = $msg;
00548 
00549         return $this;
00550     }
00551 
00559     function setIntro( $msg ) {
00560         $this->setPreText( $msg );
00561 
00562         return $this;
00563     }
00564 
00573     function setPreText( $msg ) {
00574         $this->mPre = $msg;
00575 
00576         return $this;
00577     }
00578 
00586     function addPreText( $msg ) {
00587         $this->mPre .= $msg;
00588 
00589         return $this;
00590     }
00591 
00600     function addHeaderText( $msg, $section = null ) {
00601         if ( is_null( $section ) ) {
00602             $this->mHeader .= $msg;
00603         } else {
00604             if ( !isset( $this->mSectionHeaders[$section] ) ) {
00605                 $this->mSectionHeaders[$section] = '';
00606             }
00607             $this->mSectionHeaders[$section] .= $msg;
00608         }
00609 
00610         return $this;
00611     }
00612 
00622     function setHeaderText( $msg, $section = null ) {
00623         if ( is_null( $section ) ) {
00624             $this->mHeader = $msg;
00625         } else {
00626             $this->mSectionHeaders[$section] = $msg;
00627         }
00628 
00629         return $this;
00630     }
00631 
00640     function addFooterText( $msg, $section = null ) {
00641         if ( is_null( $section ) ) {
00642             $this->mFooter .= $msg;
00643         } else {
00644             if ( !isset( $this->mSectionFooters[$section] ) ) {
00645                 $this->mSectionFooters[$section] = '';
00646             }
00647             $this->mSectionFooters[$section] .= $msg;
00648         }
00649 
00650         return $this;
00651     }
00652 
00662     function setFooterText( $msg, $section = null ) {
00663         if ( is_null( $section ) ) {
00664             $this->mFooter = $msg;
00665         } else {
00666             $this->mSectionFooters[$section] = $msg;
00667         }
00668 
00669         return $this;
00670     }
00671 
00679     function addPostText( $msg ) {
00680         $this->mPost .= $msg;
00681 
00682         return $this;
00683     }
00684 
00692     function setPostText( $msg ) {
00693         $this->mPost = $msg;
00694 
00695         return $this;
00696     }
00697 
00707     public function addHiddenField( $name, $value, $attribs = array() ) {
00708         $attribs += array( 'name' => $name );
00709         $this->mHiddenFields[] = array( $value, $attribs );
00710 
00711         return $this;
00712     }
00713 
00724     public function addHiddenFields( array $fields ) {
00725         foreach ( $fields as $name => $value ) {
00726             $this->mHiddenFields[] = array( $value, array( 'name' => $name ) );
00727         }
00728 
00729         return $this;
00730     }
00731 
00742     public function addButton( $name, $value, $id = null, $attribs = null ) {
00743         $this->mButtons[] = compact( 'name', 'value', 'id', 'attribs' );
00744 
00745         return $this;
00746     }
00747 
00757     public function setTokenSalt( $salt ) {
00758         $this->mTokenSalt = $salt;
00759 
00760         return $this;
00761     }
00762 
00775     function displayForm( $submitResult ) {
00776         $this->getOutput()->addHTML( $this->getHTML( $submitResult ) );
00777     }
00778 
00786     function getHTML( $submitResult ) {
00787         # For good measure (it is the default)
00788         $this->getOutput()->preventClickjacking();
00789         $this->getOutput()->addModules( 'mediawiki.htmlform' );
00790         if ( $this->isVForm() ) {
00791             $this->getOutput()->addModuleStyles( array(
00792                 'mediawiki.ui',
00793                 'mediawiki.ui.button',
00794             ) );
00795             // @todo Should vertical form set setWrapperLegend( false )
00796             // to hide ugly fieldsets?
00797         }
00798 
00799         $html = ''
00800             . $this->getErrors( $submitResult )
00801             . $this->mHeader
00802             . $this->getBody()
00803             . $this->getHiddenFields()
00804             . $this->getButtons()
00805             . $this->mFooter;
00806 
00807         $html = $this->wrapForm( $html );
00808 
00809         return '' . $this->mPre . $html . $this->mPost;
00810     }
00811 
00819     function wrapForm( $html ) {
00820 
00821         # Include a <fieldset> wrapper for style, if requested.
00822         if ( $this->mWrapperLegend !== false ) {
00823             $html = Xml::fieldset( $this->mWrapperLegend, $html );
00824         }
00825         # Use multipart/form-data
00826         $encType = $this->mUseMultipart
00827             ? 'multipart/form-data'
00828             : 'application/x-www-form-urlencoded';
00829         # Attributes
00830         $attribs = array(
00831             'action' => $this->getAction(),
00832             'method' => $this->getMethod(),
00833             'class' => array( 'visualClear' ),
00834             'enctype' => $encType,
00835         );
00836         if ( !empty( $this->mId ) ) {
00837             $attribs['id'] = $this->mId;
00838         }
00839 
00840         if ( $this->isVForm() ) {
00841             array_push( $attribs['class'], 'mw-ui-vform', 'mw-ui-container' );
00842         }
00843 
00844         return Html::rawElement( 'form', $attribs, $html );
00845     }
00846 
00851     function getHiddenFields() {
00852         $html = '';
00853         if ( $this->getMethod() == 'post' ) {
00854             $html .= Html::hidden(
00855                 'wpEditToken',
00856                 $this->getUser()->getEditToken( $this->mTokenSalt ),
00857                 array( 'id' => 'wpEditToken' )
00858             ) . "\n";
00859             $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n";
00860         }
00861 
00862         $articlePath = $this->getConfig()->get( 'ArticlePath' );
00863         if ( strpos( $articlePath, '?' ) !== false && $this->getMethod() == 'get' ) {
00864             $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n";
00865         }
00866 
00867         foreach ( $this->mHiddenFields as $data ) {
00868             list( $value, $attribs ) = $data;
00869             $html .= Html::hidden( $attribs['name'], $value, $attribs ) . "\n";
00870         }
00871 
00872         return $html;
00873     }
00874 
00879     function getButtons() {
00880         $buttons = '';
00881         $useMediaWikiUIEverywhere = $this->getConfig()->get( 'UseMediaWikiUIEverywhere' );
00882 
00883         if ( $this->mShowSubmit ) {
00884             $attribs = array();
00885 
00886             if ( isset( $this->mSubmitID ) ) {
00887                 $attribs['id'] = $this->mSubmitID;
00888             }
00889 
00890             if ( isset( $this->mSubmitName ) ) {
00891                 $attribs['name'] = $this->mSubmitName;
00892             }
00893 
00894             if ( isset( $this->mSubmitTooltip ) ) {
00895                 $attribs += Linker::tooltipAndAccesskeyAttribs( $this->mSubmitTooltip );
00896             }
00897 
00898             $attribs['class'] = array( 'mw-htmlform-submit' );
00899 
00900             if ( $this->isVForm() || $useMediaWikiUIEverywhere ) {
00901                 array_push( $attribs['class'], 'mw-ui-button', $this->mSubmitModifierClass );
00902             }
00903 
00904             if ( $this->isVForm() ) {
00905                 // mw-ui-block is necessary because the buttons aren't necessarily in an
00906                 // immediate child div of the vform.
00907                 // @todo Let client specify if the primary submit button is progressive or destructive
00908                 array_push(
00909                     $attribs['class'],
00910                     'mw-ui-big',
00911                     'mw-ui-block'
00912                 );
00913             }
00914 
00915             $buttons .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
00916         }
00917 
00918         if ( $this->mShowReset ) {
00919             $buttons .= Html::element(
00920                 'input',
00921                 array(
00922                     'type' => 'reset',
00923                     'value' => $this->msg( 'htmlform-reset' )->text()
00924                 )
00925             ) . "\n";
00926         }
00927 
00928         foreach ( $this->mButtons as $button ) {
00929             $attrs = array(
00930                 'type' => 'submit',
00931                 'name' => $button['name'],
00932                 'value' => $button['value']
00933             );
00934 
00935             if ( $button['attribs'] ) {
00936                 $attrs += $button['attribs'];
00937             }
00938 
00939             if ( isset( $button['id'] ) ) {
00940                 $attrs['id'] = $button['id'];
00941             }
00942 
00943             if ( $this->isVForm() || $useMediaWikiUIEverywhere ) {
00944                 if ( isset( $attrs['class'] ) ) {
00945                     $attrs['class'] .= ' mw-ui-button';
00946                 } else {
00947                     $attrs['class'] = 'mw-ui-button';
00948                 }
00949                 if ( $this->isVForm() ) {
00950                     $attrs['class'] .= ' mw-ui-big mw-ui-block';
00951                 }
00952             }
00953 
00954             $buttons .= Html::element( 'input', $attrs ) . "\n";
00955         }
00956 
00957         $html = Html::rawElement( 'span',
00958             array( 'class' => 'mw-htmlform-submit-buttons' ), "\n$buttons" ) . "\n";
00959 
00960         // Buttons are top-level form elements in table and div layouts,
00961         // but vform wants all elements inside divs to get spaced-out block
00962         // styling.
00963         if ( $this->mShowSubmit && $this->isVForm() ) {
00964             $html = Html::rawElement( 'div', null, "\n$html" ) . "\n";
00965         }
00966 
00967         return $html;
00968     }
00969 
00974     function getBody() {
00975         return $this->displaySection( $this->mFieldTree, $this->mTableId );
00976     }
00977 
00985     function getErrors( $errors ) {
00986         if ( $errors instanceof Status ) {
00987             if ( $errors->isOK() ) {
00988                 $errorstr = '';
00989             } else {
00990                 $errorstr = $this->getOutput()->parse( $errors->getWikiText() );
00991             }
00992         } elseif ( is_array( $errors ) ) {
00993             $errorstr = $this->formatErrors( $errors );
00994         } else {
00995             $errorstr = $errors;
00996         }
00997 
00998         return $errorstr
00999             ? Html::rawElement( 'div', array( 'class' => 'error' ), $errorstr )
01000             : '';
01001     }
01002 
01010     public static function formatErrors( $errors ) {
01011         $errorstr = '';
01012 
01013         foreach ( $errors as $error ) {
01014             if ( is_array( $error ) ) {
01015                 $msg = array_shift( $error );
01016             } else {
01017                 $msg = $error;
01018                 $error = array();
01019             }
01020 
01021             $errorstr .= Html::rawElement(
01022                 'li',
01023                 array(),
01024                 wfMessage( $msg, $error )->parse()
01025             );
01026         }
01027 
01028         $errorstr = Html::rawElement( 'ul', array(), $errorstr );
01029 
01030         return $errorstr;
01031     }
01032 
01040     function setSubmitText( $t ) {
01041         $this->mSubmitText = $t;
01042 
01043         return $this;
01044     }
01045 
01050     public function setSubmitDestructive() {
01051         $this->mSubmitModifierClass = 'mw-ui-destructive';
01052     }
01053 
01062     public function setSubmitTextMsg( $msg ) {
01063         if ( !$msg instanceof Message ) {
01064             $msg = $this->msg( $msg );
01065         }
01066         $this->setSubmitText( $msg->text() );
01067 
01068         return $this;
01069     }
01070 
01075     function getSubmitText() {
01076         return $this->mSubmitText
01077             ? $this->mSubmitText
01078             : $this->msg( 'htmlform-submit' )->text();
01079     }
01080 
01086     public function setSubmitName( $name ) {
01087         $this->mSubmitName = $name;
01088 
01089         return $this;
01090     }
01091 
01097     public function setSubmitTooltip( $name ) {
01098         $this->mSubmitTooltip = $name;
01099 
01100         return $this;
01101     }
01102 
01111     function setSubmitID( $t ) {
01112         $this->mSubmitID = $t;
01113 
01114         return $this;
01115     }
01116 
01127     function suppressDefaultSubmit( $suppressSubmit = true ) {
01128         $this->mShowSubmit = !$suppressSubmit;
01129 
01130         return $this;
01131     }
01132 
01142     public function setTableId( $id ) {
01143         $this->mTableId = $id;
01144 
01145         return $this;
01146     }
01147 
01153     public function setId( $id ) {
01154         $this->mId = $id;
01155 
01156         return $this;
01157     }
01158 
01169     public function setWrapperLegend( $legend ) {
01170         $this->mWrapperLegend = $legend;
01171 
01172         return $this;
01173     }
01174 
01184     public function setWrapperLegendMsg( $msg ) {
01185         if ( !$msg instanceof Message ) {
01186             $msg = $this->msg( $msg );
01187         }
01188         $this->setWrapperLegend( $msg->text() );
01189 
01190         return $this;
01191     }
01192 
01202     function setMessagePrefix( $p ) {
01203         $this->mMessagePrefix = $p;
01204 
01205         return $this;
01206     }
01207 
01215     function setTitle( $t ) {
01216         $this->mTitle = $t;
01217 
01218         return $this;
01219     }
01220 
01225     function getTitle() {
01226         return $this->mTitle === false
01227             ? $this->getContext()->getTitle()
01228             : $this->mTitle;
01229     }
01230 
01238     public function setMethod( $method = 'post' ) {
01239         $this->mMethod = $method;
01240 
01241         return $this;
01242     }
01243 
01244     public function getMethod() {
01245         return $this->mMethod;
01246     }
01247 
01261     public function displaySection( $fields,
01262         $sectionName = '',
01263         $fieldsetIDPrefix = '',
01264         &$hasUserVisibleFields = false ) {
01265         $displayFormat = $this->getDisplayFormat();
01266 
01267         $html = '';
01268         $subsectionHtml = '';
01269         $hasLabel = false;
01270 
01271         switch ( $displayFormat ) {
01272             case 'table':
01273                 $getFieldHtmlMethod = 'getTableRow';
01274                 break;
01275             case 'vform':
01276                 // Close enough to a div.
01277                 $getFieldHtmlMethod = 'getDiv';
01278                 break;
01279             case 'div':
01280                 $getFieldHtmlMethod = 'getDiv';
01281                 break;
01282             default:
01283                 $getFieldHtmlMethod = 'get' . ucfirst( $displayFormat );
01284         }
01285 
01286         foreach ( $fields as $key => $value ) {
01287             if ( $value instanceof HTMLFormField ) {
01288                 $v = empty( $value->mParams['nodata'] )
01289                     ? $this->mFieldData[$key]
01290                     : $value->getDefault();
01291                 $html .= $value->$getFieldHtmlMethod( $v );
01292 
01293                 $labelValue = trim( $value->getLabel() );
01294                 if ( $labelValue != '&#160;' && $labelValue !== '' ) {
01295                     $hasLabel = true;
01296                 }
01297 
01298                 if ( get_class( $value ) !== 'HTMLHiddenField' &&
01299                     get_class( $value ) !== 'HTMLApiField'
01300                 ) {
01301                     $hasUserVisibleFields = true;
01302                 }
01303             } elseif ( is_array( $value ) ) {
01304                 $subsectionHasVisibleFields = false;
01305                 $section =
01306                     $this->displaySection( $value,
01307                         "mw-htmlform-$key",
01308                         "$fieldsetIDPrefix$key-",
01309                         $subsectionHasVisibleFields );
01310                 $legend = null;
01311 
01312                 if ( $subsectionHasVisibleFields === true ) {
01313                     // Display the section with various niceties.
01314                     $hasUserVisibleFields = true;
01315 
01316                     $legend = $this->getLegend( $key );
01317 
01318                     if ( isset( $this->mSectionHeaders[$key] ) ) {
01319                         $section = $this->mSectionHeaders[$key] . $section;
01320                     }
01321                     if ( isset( $this->mSectionFooters[$key] ) ) {
01322                         $section .= $this->mSectionFooters[$key];
01323                     }
01324 
01325                     $attributes = array();
01326                     if ( $fieldsetIDPrefix ) {
01327                         $attributes['id'] = Sanitizer::escapeId( "$fieldsetIDPrefix$key" );
01328                     }
01329                     $subsectionHtml .= Xml::fieldset( $legend, $section, $attributes ) . "\n";
01330                 } else {
01331                     // Just return the inputs, nothing fancy.
01332                     $subsectionHtml .= $section;
01333                 }
01334             }
01335         }
01336 
01337         if ( $displayFormat !== 'raw' ) {
01338             $classes = array();
01339 
01340             if ( !$hasLabel ) { // Avoid strange spacing when no labels exist
01341                 $classes[] = 'mw-htmlform-nolabel';
01342             }
01343 
01344             $attribs = array(
01345                 'class' => implode( ' ', $classes ),
01346             );
01347 
01348             if ( $sectionName ) {
01349                 $attribs['id'] = Sanitizer::escapeId( $sectionName );
01350             }
01351 
01352             if ( $displayFormat === 'table' ) {
01353                 $html = Html::rawElement( 'table',
01354                         $attribs,
01355                         Html::rawElement( 'tbody', array(), "\n$html\n" ) ) . "\n";
01356             } elseif ( $displayFormat === 'div' || $displayFormat === 'vform' ) {
01357                 $html = Html::rawElement( 'div', $attribs, "\n$html\n" );
01358             }
01359         }
01360 
01361         if ( $this->mSubSectionBeforeFields ) {
01362             return $subsectionHtml . "\n" . $html;
01363         } else {
01364             return $html . "\n" . $subsectionHtml;
01365         }
01366     }
01367 
01371     function loadData() {
01372         $fieldData = array();
01373 
01374         foreach ( $this->mFlatFields as $fieldname => $field ) {
01375             if ( !empty( $field->mParams['nodata'] ) ) {
01376                 continue;
01377             } elseif ( !empty( $field->mParams['disabled'] ) ) {
01378                 $fieldData[$fieldname] = $field->getDefault();
01379             } else {
01380                 $fieldData[$fieldname] = $field->loadDataFromRequest( $this->getRequest() );
01381             }
01382         }
01383 
01384         # Filter data.
01385         foreach ( $fieldData as $name => &$value ) {
01386             $field = $this->mFlatFields[$name];
01387             $value = $field->filter( $value, $this->mFlatFields );
01388         }
01389 
01390         $this->mFieldData = $fieldData;
01391     }
01392 
01400     function suppressReset( $suppressReset = true ) {
01401         $this->mShowReset = !$suppressReset;
01402 
01403         return $this;
01404     }
01405 
01415     function filterDataForSubmit( $data ) {
01416         return $data;
01417     }
01418 
01427     public function getLegend( $key ) {
01428         return $this->msg( "{$this->mMessagePrefix}-$key" )->text();
01429     }
01430 
01441     public function setAction( $action ) {
01442         $this->mAction = $action;
01443 
01444         return $this;
01445     }
01446 
01454     public function getAction() {
01455         // If an action is alredy provided, return it
01456         if ( $this->mAction !== false ) {
01457             return $this->mAction;
01458         }
01459 
01460         $articlePath = $this->getConfig()->get( 'ArticlePath' );
01461         // Check whether we are in GET mode and the ArticlePath contains a "?"
01462         // meaning that getLocalURL() would return something like "index.php?title=...".
01463         // As browser remove the query string before submitting GET forms,
01464         // it means that the title would be lost. In such case use wfScript() instead
01465         // and put title in an hidden field (see getHiddenFields()).
01466         if ( strpos( $articlePath, '?' ) !== false && $this->getMethod() === 'get' ) {
01467             return wfScript();
01468         }
01469 
01470         return $this->getTitle()->getLocalURL();
01471     }
01472 }