MediaWiki  REL1_23
HTMLFormField.php
Go to the documentation of this file.
00001 <?php
00002 
00007 abstract class HTMLFormField {
00008     public $mParams;
00009 
00010     protected $mValidationCallback;
00011     protected $mFilterCallback;
00012     protected $mName;
00013     protected $mLabel; # String label.  Set on construction
00014     protected $mID;
00015     protected $mClass = '';
00016     protected $mDefault;
00017     protected $mOptions = false;
00018     protected $mOptionsLabelsNotFromMessage = false;
00019 
00024     protected $mShowEmptyLabels = true;
00025 
00029     public $mParent;
00030 
00041     abstract function getInputHTML( $value );
00042 
00053     function msg() {
00054         $args = func_get_args();
00055 
00056         if ( $this->mParent ) {
00057             $callback = array( $this->mParent, 'msg' );
00058         } else {
00059             $callback = 'wfMessage';
00060         }
00061 
00062         return call_user_func_array( $callback, $args );
00063     }
00064 
00075     function validate( $value, $alldata ) {
00076         if ( isset( $this->mParams['required'] )
00077             && $this->mParams['required'] !== false
00078             && $value === ''
00079         ) {
00080             return $this->msg( 'htmlform-required' )->parse();
00081         }
00082 
00083         if ( isset( $this->mValidationCallback ) ) {
00084             return call_user_func( $this->mValidationCallback, $value, $alldata, $this->mParent );
00085         }
00086 
00087         return true;
00088     }
00089 
00090     function filter( $value, $alldata ) {
00091         if ( isset( $this->mFilterCallback ) ) {
00092             $value = call_user_func( $this->mFilterCallback, $value, $alldata, $this->mParent );
00093         }
00094 
00095         return $value;
00096     }
00097 
00104     protected function needsLabel() {
00105         return true;
00106     }
00107 
00117     public function setShowEmptyLabel( $show ) {
00118         $this->mShowEmptyLabels = $show;
00119     }
00120 
00128     function loadDataFromRequest( $request ) {
00129         if ( $request->getCheck( $this->mName ) ) {
00130             return $request->getText( $this->mName );
00131         } else {
00132             return $this->getDefault();
00133         }
00134     }
00135 
00144     function __construct( $params ) {
00145         $this->mParams = $params;
00146 
00147         # Generate the label from a message, if possible
00148         if ( isset( $params['label-message'] ) ) {
00149             $msgInfo = $params['label-message'];
00150 
00151             if ( is_array( $msgInfo ) ) {
00152                 $msg = array_shift( $msgInfo );
00153             } else {
00154                 $msg = $msgInfo;
00155                 $msgInfo = array();
00156             }
00157 
00158             $this->mLabel = wfMessage( $msg, $msgInfo )->parse();
00159         } elseif ( isset( $params['label'] ) ) {
00160             if ( $params['label'] === '&#160;' ) {
00161                 // Apparently some things set &nbsp directly and in an odd format
00162                 $this->mLabel = '&#160;';
00163             } else {
00164                 $this->mLabel = htmlspecialchars( $params['label'] );
00165             }
00166         } elseif ( isset( $params['label-raw'] ) ) {
00167             $this->mLabel = $params['label-raw'];
00168         }
00169 
00170         $this->mName = "wp{$params['fieldname']}";
00171         if ( isset( $params['name'] ) ) {
00172             $this->mName = $params['name'];
00173         }
00174 
00175         $validName = Sanitizer::escapeId( $this->mName );
00176         if ( $this->mName != $validName && !isset( $params['nodata'] ) ) {
00177             throw new MWException( "Invalid name '{$this->mName}' passed to " . __METHOD__ );
00178         }
00179 
00180         $this->mID = "mw-input-{$this->mName}";
00181 
00182         if ( isset( $params['default'] ) ) {
00183             $this->mDefault = $params['default'];
00184         }
00185 
00186         if ( isset( $params['id'] ) ) {
00187             $id = $params['id'];
00188             $validId = Sanitizer::escapeId( $id );
00189 
00190             if ( $id != $validId ) {
00191                 throw new MWException( "Invalid id '$id' passed to " . __METHOD__ );
00192             }
00193 
00194             $this->mID = $id;
00195         }
00196 
00197         if ( isset( $params['cssclass'] ) ) {
00198             $this->mClass = $params['cssclass'];
00199         }
00200 
00201         if ( isset( $params['validation-callback'] ) ) {
00202             $this->mValidationCallback = $params['validation-callback'];
00203         }
00204 
00205         if ( isset( $params['filter-callback'] ) ) {
00206             $this->mFilterCallback = $params['filter-callback'];
00207         }
00208 
00209         if ( isset( $params['flatlist'] ) ) {
00210             $this->mClass .= ' mw-htmlform-flatlist';
00211         }
00212 
00213         if ( isset( $params['hidelabel'] ) ) {
00214             $this->mShowEmptyLabels = false;
00215         }
00216     }
00217 
00226     function getTableRow( $value ) {
00227         list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
00228         $inputHtml = $this->getInputHTML( $value );
00229         $fieldType = get_class( $this );
00230         $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() );
00231         $cellAttributes = array();
00232 
00233         if ( !empty( $this->mParams['vertical-label'] ) ) {
00234             $cellAttributes['colspan'] = 2;
00235             $verticalLabel = true;
00236         } else {
00237             $verticalLabel = false;
00238         }
00239 
00240         $label = $this->getLabelHtml( $cellAttributes );
00241 
00242         $field = Html::rawElement(
00243             'td',
00244             array( 'class' => 'mw-input' ) + $cellAttributes,
00245             $inputHtml . "\n$errors"
00246         );
00247 
00248         if ( $verticalLabel ) {
00249             $html = Html::rawElement( 'tr', array( 'class' => 'mw-htmlform-vertical-label' ), $label );
00250             $html .= Html::rawElement( 'tr',
00251                 array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
00252                 $field );
00253         } else {
00254             $html =
00255                 Html::rawElement( 'tr',
00256                     array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
00257                     $label . $field );
00258         }
00259 
00260         return $html . $helptext;
00261     }
00262 
00272     public function getDiv( $value ) {
00273         list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
00274         $inputHtml = $this->getInputHTML( $value );
00275         $fieldType = get_class( $this );
00276         $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() );
00277         $cellAttributes = array();
00278         $label = $this->getLabelHtml( $cellAttributes );
00279 
00280         $outerDivClass = array(
00281             'mw-input',
00282             'mw-htmlform-nolabel' => ( $label === '' )
00283         );
00284 
00285         $field = Html::rawElement(
00286             'div',
00287             array( 'class' => $outerDivClass ) + $cellAttributes,
00288             $inputHtml . "\n$errors"
00289         );
00290         $divCssClasses = array( "mw-htmlform-field-$fieldType", $this->mClass, $errorClass );
00291         if ( $this->mParent->isVForm() ) {
00292             $divCssClasses[] = 'mw-ui-vform-div';
00293         }
00294         $html = Html::rawElement( 'div', array( 'class' => $divCssClasses ), $label . $field );
00295         $html .= $helptext;
00296 
00297         return $html;
00298     }
00299 
00309     public function getRaw( $value ) {
00310         list( $errors, ) = $this->getErrorsAndErrorClass( $value );
00311         $inputHtml = $this->getInputHTML( $value );
00312         $helptext = $this->getHelpTextHtmlRaw( $this->getHelpText() );
00313         $cellAttributes = array();
00314         $label = $this->getLabelHtml( $cellAttributes );
00315 
00316         $html = "\n$errors";
00317         $html .= $label;
00318         $html .= $inputHtml;
00319         $html .= $helptext;
00320 
00321         return $html;
00322     }
00323 
00331     public function getHelpTextHtmlTable( $helptext ) {
00332         if ( is_null( $helptext ) ) {
00333             return '';
00334         }
00335 
00336         $row = Html::rawElement( 'td', array( 'colspan' => 2, 'class' => 'htmlform-tip' ), $helptext );
00337         $row = Html::rawElement( 'tr', array(), $row );
00338 
00339         return $row;
00340     }
00341 
00350     public function getHelpTextHtmlDiv( $helptext ) {
00351         if ( is_null( $helptext ) ) {
00352             return '';
00353         }
00354 
00355         $div = Html::rawElement( 'div', array( 'class' => 'htmlform-tip' ), $helptext );
00356 
00357         return $div;
00358     }
00359 
00367     public function getHelpTextHtmlRaw( $helptext ) {
00368         return $this->getHelpTextHtmlDiv( $helptext );
00369     }
00370 
00376     public function getHelpText() {
00377         $helptext = null;
00378 
00379         if ( isset( $this->mParams['help-message'] ) ) {
00380             $this->mParams['help-messages'] = array( $this->mParams['help-message'] );
00381         }
00382 
00383         if ( isset( $this->mParams['help-messages'] ) ) {
00384             foreach ( $this->mParams['help-messages'] as $name ) {
00385                 $helpMessage = (array)$name;
00386                 $msg = $this->msg( array_shift( $helpMessage ), $helpMessage );
00387 
00388                 if ( $msg->exists() ) {
00389                     if ( is_null( $helptext ) ) {
00390                         $helptext = '';
00391                     } else {
00392                         $helptext .= $this->msg( 'word-separator' )->escaped(); // some space
00393                     }
00394                     $helptext .= $msg->parse(); // Append message
00395                 }
00396             }
00397         } elseif ( isset( $this->mParams['help'] ) ) {
00398             $helptext = $this->mParams['help'];
00399         }
00400 
00401         return $helptext;
00402     }
00403 
00411     public function getErrorsAndErrorClass( $value ) {
00412         $errors = $this->validate( $value, $this->mParent->mFieldData );
00413 
00414         if ( $errors === true ||
00415             ( !$this->mParent->getRequest()->wasPosted() && $this->mParent->getMethod() === 'post' )
00416         ) {
00417             $errors = '';
00418             $errorClass = '';
00419         } else {
00420             $errors = self::formatErrors( $errors );
00421             $errorClass = 'mw-htmlform-invalid-input';
00422         }
00423 
00424         return array( $errors, $errorClass );
00425     }
00426 
00427     function getLabel() {
00428         return is_null( $this->mLabel ) ? '' : $this->mLabel;
00429     }
00430 
00431     function getLabelHtml( $cellAttributes = array() ) {
00432         # Don't output a for= attribute for labels with no associated input.
00433         # Kind of hacky here, possibly we don't want these to be <label>s at all.
00434         $for = array();
00435 
00436         if ( $this->needsLabel() ) {
00437             $for['for'] = $this->mID;
00438         }
00439 
00440         $labelValue = trim( $this->getLabel() );
00441         $hasLabel = false;
00442         if ( $labelValue !== '&#160;' && $labelValue !== '' ) {
00443             $hasLabel = true;
00444         }
00445 
00446         $displayFormat = $this->mParent->getDisplayFormat();
00447         $html = '';
00448 
00449         if ( $displayFormat === 'table' ) {
00450             $html =
00451                 Html::rawElement( 'td',
00452                     array( 'class' => 'mw-label' ) + $cellAttributes,
00453                     Html::rawElement( 'label', $for, $labelValue ) );
00454         } elseif ( $hasLabel || $this->mShowEmptyLabels ) {
00455             if ( $displayFormat === 'div' ) {
00456                 $html =
00457                     Html::rawElement( 'div',
00458                         array( 'class' => 'mw-label' ) + $cellAttributes,
00459                         Html::rawElement( 'label', $for, $labelValue ) );
00460             } else {
00461                 $html = Html::rawElement( 'label', $for, $labelValue );
00462             }
00463         }
00464 
00465         return $html;
00466     }
00467 
00468     function getDefault() {
00469         if ( isset( $this->mDefault ) ) {
00470             return $this->mDefault;
00471         } else {
00472             return null;
00473         }
00474     }
00475 
00481     public function getTooltipAndAccessKey() {
00482         if ( empty( $this->mParams['tooltip'] ) ) {
00483             return array();
00484         }
00485 
00486         return Linker::tooltipAndAccesskeyAttribs( $this->mParams['tooltip'] );
00487     }
00488 
00495     public function getAttributes( array $list ) {
00496         static $boolAttribs = array( 'disabled', 'required', 'autofocus', 'multiple', 'readonly' );
00497 
00498         $ret = array();
00499 
00500         foreach ( $list as $key ) {
00501             if ( in_array( $key, $boolAttribs ) ) {
00502                 if ( !empty( $this->mParams[$key] ) ) {
00503                     $ret[$key] = '';
00504                 }
00505             } elseif ( isset( $this->mParams[$key] ) ) {
00506                 $ret[$key] = $this->mParams[$key];
00507             }
00508         }
00509 
00510         return $ret;
00511     }
00512 
00520     private function lookupOptionsKeys( $options ) {
00521         $ret = array();
00522         foreach ( $options as $key => $value ) {
00523             $key = $this->msg( $key )->plain();
00524             $ret[$key] = is_array( $value )
00525                 ? $this->lookupOptionsKeys( $value )
00526                 : strval( $value );
00527         }
00528         return $ret;
00529     }
00530 
00538     static function forceToStringRecursive( $array ) {
00539         if ( is_array( $array ) ) {
00540             return array_map( array( __CLASS__, 'forceToStringRecursive' ), $array );
00541         } else {
00542             return strval( $array );
00543         }
00544     }
00545 
00552     public function getOptions() {
00553         if ( $this->mOptions === false ) {
00554             if ( array_key_exists( 'options-messages', $this->mParams ) ) {
00555                 $this->mOptions = $this->lookupOptionsKeys( $this->mParams['options-messages'] );
00556             } elseif ( array_key_exists( 'options', $this->mParams ) ) {
00557                 $this->mOptionsLabelsNotFromMessage = true;
00558                 $this->mOptions = self::forceToStringRecursive( $this->mParams['options'] );
00559             } elseif ( array_key_exists( 'options-message', $this->mParams ) ) {
00561                 $message = $this->msg( $this->mParams['options-message'] )->inContentLanguage()->plain();
00562 
00563                 $optgroup = false;
00564                 $this->mOptions = array();
00565                 foreach ( explode( "\n", $message ) as $option ) {
00566                     $value = trim( $option );
00567                     if ( $value == '' ) {
00568                         continue;
00569                     } elseif ( substr( $value, 0, 1 ) == '*' && substr( $value, 1, 1 ) != '*' ) {
00570                         # A new group is starting...
00571                         $value = trim( substr( $value, 1 ) );
00572                         $optgroup = $value;
00573                     } elseif ( substr( $value, 0, 2 ) == '**' ) {
00574                         # groupmember
00575                         $opt = trim( substr( $value, 2 ) );
00576                         if ( $optgroup === false ) {
00577                             $this->mOptions[$opt] = $opt;
00578                         } else {
00579                             $this->mOptions[$optgroup][$opt] = $opt;
00580                         }
00581                     } else {
00582                         # groupless reason list
00583                         $optgroup = false;
00584                         $this->mOptions[$option] = $option;
00585                     }
00586                 }
00587             } else {
00588                 $this->mOptions = null;
00589             }
00590         }
00591 
00592         return $this->mOptions;
00593     }
00594 
00603     public static function flattenOptions( $options ) {
00604         $flatOpts = array();
00605 
00606         foreach ( $options as $value ) {
00607             if ( is_array( $value ) ) {
00608                 $flatOpts = array_merge( $flatOpts, self::flattenOptions( $value ) );
00609             } else {
00610                 $flatOpts[] = $value;
00611             }
00612         }
00613 
00614         return $flatOpts;
00615     }
00616 
00624     protected static function formatErrors( $errors ) {
00625         if ( is_array( $errors ) && count( $errors ) === 1 ) {
00626             $errors = array_shift( $errors );
00627         }
00628 
00629         if ( is_array( $errors ) ) {
00630             $lines = array();
00631             foreach ( $errors as $error ) {
00632                 if ( $error instanceof Message ) {
00633                     $lines[] = Html::rawElement( 'li', array(), $error->parse() );
00634                 } else {
00635                     $lines[] = Html::rawElement( 'li', array(), $error );
00636                 }
00637             }
00638 
00639             return Html::rawElement( 'ul', array( 'class' => 'error' ), implode( "\n", $lines ) );
00640         } else {
00641             if ( $errors instanceof Message ) {
00642                 $errors = $errors->parse();
00643             }
00644 
00645             return Html::rawElement( 'span', array( 'class' => 'error' ), $errors );
00646         }
00647     }
00648 }