MediaWiki  REL1_24
HTMLFormFieldCloner.php
Go to the documentation of this file.
00001 <?php
00002 
00038 class HTMLFormFieldCloner extends HTMLFormField {
00039     private static $counter = 0;
00040 
00046     protected $uniqueId;
00047 
00048     public function __construct( $params ) {
00049         $this->uniqueId = get_class( $this ) . ++self::$counter . 'x';
00050         parent::__construct( $params );
00051 
00052         if ( empty( $this->mParams['fields'] ) || !is_array( $this->mParams['fields'] ) ) {
00053             throw new MWException( 'HTMLFormFieldCloner called without any fields' );
00054         }
00055 
00056         // Make sure the delete button, if explicitly specified, is sane
00057         if ( isset( $this->mParams['fields']['delete'] ) ) {
00058             $class = 'mw-htmlform-cloner-delete-button';
00059             $info = $this->mParams['fields']['delete'] + array(
00060                 'cssclass' => $class
00061             );
00062             unset( $info['name'], $info['class'] );
00063 
00064             if ( !isset( $info['type'] ) || $info['type'] !== 'submit' ) {
00065                 throw new MWException(
00066                     'HTMLFormFieldCloner delete field, if specified, must be of type "submit"'
00067                 );
00068             }
00069 
00070             if ( !in_array( $class, explode( ' ', $info['cssclass'] ) ) ) {
00071                 $info['cssclass'] .= " $class";
00072             }
00073 
00074             $this->mParams['fields']['delete'] = $info;
00075         }
00076     }
00077 
00085     protected function createFieldsForKey( $key ) {
00086         $fields = array();
00087         foreach ( $this->mParams['fields'] as $fieldname => $info ) {
00088             $name = "{$this->mName}[$key][$fieldname]";
00089             if ( isset( $info['name'] ) ) {
00090                 $info['name'] = "{$this->mName}[$key][{$info['name']}]";
00091             } else {
00092                 $info['name'] = $name;
00093             }
00094             if ( isset( $info['id'] ) ) {
00095                 $info['id'] = Sanitizer::escapeId( "{$this->mID}--$key--{$info['id']}" );
00096             } else {
00097                 $info['id'] = Sanitizer::escapeId( "{$this->mID}--$key--$fieldname" );
00098             }
00099             $field = HTMLForm::loadInputFromParameters( $name, $info );
00100             $field->mParent = $this->mParent;
00101             $fields[$fieldname] = $field;
00102         }
00103         return $fields;
00104     }
00105 
00114     protected function rekeyValuesArray( $key, $values ) {
00115         $data = array();
00116         foreach ( $values as $fieldname => $value ) {
00117             $name = "{$this->mName}[$key][$fieldname]";
00118             $data[$name] = $value;
00119         }
00120         return $data;
00121     }
00122 
00123     protected function needsLabel() {
00124         return false;
00125     }
00126 
00127     public function loadDataFromRequest( $request ) {
00128         // It's possible that this might be posted with no fields. Detect that
00129         // by looking for an edit token.
00130         if ( !$request->getCheck( 'wpEditToken' ) && $request->getArray( $this->mName ) === null ) {
00131             return $this->getDefault();
00132         }
00133 
00134         $values = $request->getArray( $this->mName );
00135         if ( $values === null ) {
00136             $values = array();
00137         }
00138 
00139         $ret = array();
00140         foreach ( $values as $key => $value ) {
00141             if ( $key === 'create' || isset( $value['delete'] ) ) {
00142                 $ret['nonjs'] = 1;
00143                 continue;
00144             }
00145 
00146             // Add back in $request->getValues() so things that look for e.g.
00147             // wpEditToken don't fail.
00148             $data = $this->rekeyValuesArray( $key, $value ) + $request->getValues();
00149 
00150             $fields = $this->createFieldsForKey( $key );
00151             $subrequest = new DerivativeRequest( $request, $data, $request->wasPosted() );
00152             $row = array();
00153             foreach ( $fields as $fieldname => $field ) {
00154                 if ( !empty( $field->mParams['nodata'] ) ) {
00155                     continue;
00156                 } elseif ( !empty( $field->mParams['disabled'] ) ) {
00157                     $row[$fieldname] = $field->getDefault();
00158                 } else {
00159                     $row[$fieldname] = $field->loadDataFromRequest( $subrequest );
00160                 }
00161             }
00162             $ret[] = $row;
00163         }
00164 
00165         if ( isset( $values['create'] ) ) {
00166             // Non-JS client clicked the "create" button.
00167             $fields = $this->createFieldsForKey( $this->uniqueId );
00168             $row = array();
00169             foreach ( $fields as $fieldname => $field ) {
00170                 if ( !empty( $field->mParams['nodata'] ) ) {
00171                     continue;
00172                 } else {
00173                     $row[$fieldname] = $field->getDefault();
00174                 }
00175             }
00176             $ret[] = $row;
00177         }
00178 
00179         return $ret;
00180     }
00181 
00182     public function getDefault() {
00183         $ret = parent::getDefault();
00184 
00185         // The default default is one entry with all subfields at their
00186         // defaults.
00187         if ( $ret === null ) {
00188             $fields = $this->createFieldsForKey( $this->uniqueId );
00189             $row = array();
00190             foreach ( $fields as $fieldname => $field ) {
00191                 if ( !empty( $field->mParams['nodata'] ) ) {
00192                     continue;
00193                 } else {
00194                     $row[$fieldname] = $field->getDefault();
00195                 }
00196             }
00197             $ret = array( $row );
00198         }
00199 
00200         return $ret;
00201     }
00202 
00203     public function cancelSubmit( $values, $alldata ) {
00204         if ( isset( $values['nonjs'] ) ) {
00205             return true;
00206         }
00207 
00208         foreach ( $values as $key => $value ) {
00209             $fields = $this->createFieldsForKey( $key );
00210             foreach ( $fields as $fieldname => $field ) {
00211                 if ( !empty( $field->mParams['nodata'] ) ) {
00212                     continue;
00213                 }
00214                 if ( $field->cancelSubmit( $value[$fieldname], $alldata ) ) {
00215                     return true;
00216                 }
00217             }
00218         }
00219 
00220         return parent::cancelSubmit( $values, $alldata );
00221     }
00222 
00223     public function validate( $values, $alldata ) {
00224         if ( isset( $this->mParams['required'] )
00225             && $this->mParams['required'] !== false
00226             && !$values
00227         ) {
00228             return $this->msg( 'htmlform-cloner-required' )->parseAsBlock();
00229         }
00230 
00231         if ( isset( $values['nonjs'] ) ) {
00232             // The submission was a non-JS create/delete click, so fail
00233             // validation in case cancelSubmit() somehow didn't already handle
00234             // it.
00235             return false;
00236         }
00237 
00238         foreach ( $values as $key => $value ) {
00239             $fields = $this->createFieldsForKey( $key );
00240             foreach ( $fields as $fieldname => $field ) {
00241                 if ( !empty( $field->mParams['nodata'] ) ) {
00242                     continue;
00243                 }
00244                 $ok = $field->validate( $value[$fieldname], $alldata );
00245                 if ( $ok !== true ) {
00246                     return false;
00247                 }
00248             }
00249         }
00250 
00251         return parent::validate( $values, $alldata );
00252     }
00253 
00261     protected function getInputHTMLForKey( $key, $values ) {
00262         $displayFormat = isset( $this->mParams['format'] )
00263             ? $this->mParams['format']
00264             : $this->mParent->getDisplayFormat();
00265 
00266         switch ( $displayFormat ) {
00267             case 'table':
00268                 $getFieldHtmlMethod = 'getTableRow';
00269                 break;
00270             case 'vform':
00271                 // Close enough to a div.
00272                 $getFieldHtmlMethod = 'getDiv';
00273                 break;
00274             default:
00275                 $getFieldHtmlMethod = 'get' . ucfirst( $displayFormat );
00276         }
00277 
00278         $html = '';
00279         $hasLabel = false;
00280 
00281         $fields = $this->createFieldsForKey( $key );
00282         foreach ( $fields as $fieldname => $field ) {
00283             $v = ( empty( $field->mParams['nodata'] ) && $values !== null )
00284                 ? $values[$fieldname]
00285                 : $field->getDefault();
00286             $html .= $field->$getFieldHtmlMethod( $v );
00287 
00288             $labelValue = trim( $field->getLabel() );
00289             if ( $labelValue != '&#160;' && $labelValue !== '' ) {
00290                 $hasLabel = true;
00291             }
00292         }
00293 
00294         if ( !isset( $fields['delete'] ) ) {
00295             $name = "{$this->mName}[$key][delete]";
00296             $label = isset( $this->mParams['delete-button-message'] )
00297                 ? $this->mParams['delete-button-message']
00298                 : 'htmlform-cloner-delete';
00299             $field = HTMLForm::loadInputFromParameters( $name, array(
00300                 'type' => 'submit',
00301                 'name' => $name,
00302                 'id' => Sanitizer::escapeId( "{$this->mID}--$key--delete" ),
00303                 'cssclass' => 'mw-htmlform-cloner-delete-button',
00304                 'default' => $this->msg( $label )->text(),
00305             ) );
00306             $field->mParent = $this->mParent;
00307             $v = $field->getDefault();
00308 
00309             if ( $displayFormat === 'table' ) {
00310                 $html .= $field->$getFieldHtmlMethod( $v );
00311             } else {
00312                 $html .= $field->getInputHTML( $v );
00313             }
00314         }
00315 
00316         if ( $displayFormat !== 'raw' ) {
00317             $classes = array(
00318                 'mw-htmlform-cloner-row',
00319             );
00320 
00321             if ( !$hasLabel ) { // Avoid strange spacing when no labels exist
00322                 $classes[] = 'mw-htmlform-nolabel';
00323             }
00324 
00325             $attribs = array(
00326                 'class' => implode( ' ', $classes ),
00327             );
00328 
00329             if ( $displayFormat === 'table' ) {
00330                 $html = Html::rawElement( 'table',
00331                     $attribs,
00332                     Html::rawElement( 'tbody', array(), "\n$html\n" ) ) . "\n";
00333             } elseif ( $displayFormat === 'div' || $displayFormat === 'vform' ) {
00334                 $html = Html::rawElement( 'div', $attribs, "\n$html\n" );
00335             }
00336         }
00337 
00338         if ( !empty( $this->mParams['row-legend'] ) ) {
00339             $legend = $this->msg( $this->mParams['row-legend'] )->text();
00340             $html = Xml::fieldset( $legend, $html );
00341         }
00342 
00343         return $html;
00344     }
00345 
00346     public function getInputHTML( $values ) {
00347         $html = '';
00348 
00349         foreach ( (array)$values as $key => $value ) {
00350             if ( $key === 'nonjs' ) {
00351                 continue;
00352             }
00353             $html .= Html::rawElement( 'li', array( 'class' => 'mw-htmlform-cloner-li' ),
00354                 $this->getInputHTMLForKey( $key, $value )
00355             );
00356         }
00357 
00358         $template = $this->getInputHTMLForKey( $this->uniqueId, null );
00359         $html = Html::rawElement( 'ul', array(
00360             'id' => "mw-htmlform-cloner-list-{$this->mID}",
00361             'class' => 'mw-htmlform-cloner-ul',
00362             'data-template' => $template,
00363             'data-unique-id' => $this->uniqueId,
00364         ), $html );
00365 
00366         $name = "{$this->mName}[create]";
00367         $label = isset( $this->mParams['create-button-message'] )
00368             ? $this->mParams['create-button-message']
00369             : 'htmlform-cloner-create';
00370         $field = HTMLForm::loadInputFromParameters( $name, array(
00371             'type' => 'submit',
00372             'name' => $name,
00373             'id' => Sanitizer::escapeId( "{$this->mID}--create" ),
00374             'cssclass' => 'mw-htmlform-cloner-create-button',
00375             'default' => $this->msg( $label )->text(),
00376         ) );
00377         $field->mParent = $this->mParent;
00378         $html .= $field->getInputHTML( $field->getDefault() );
00379 
00380         return $html;
00381     }
00382 }