57 $this->uniqueId = $this->
getClassName() . ++self::$counter .
'x';
58 parent::__construct( $params );
60 if ( empty( $this->mParams[
'fields'] ) || !is_array( $this->mParams[
'fields'] ) ) {
61 throw new MWException(
'HTMLFormFieldCloner called without any fields' );
65 if ( isset( $this->mParams[
'fields'][
'delete'] ) ) {
66 $class =
'mw-htmlform-cloner-delete-button';
67 $info = $this->mParams[
'fields'][
'delete'] + [
68 'formnovalidate' =>
true,
71 unset( $info[
'name'], $info[
'class'] );
73 if ( !isset( $info[
'type'] ) || $info[
'type'] !==
'submit' ) {
75 'HTMLFormFieldCloner delete field, if specified, must be of type "submit"'
79 if ( !in_array( $class, explode(
' ', $info[
'cssclass'] ) ) ) {
80 $info[
'cssclass'] .=
" $class";
83 $this->mParams[
'fields'][
'delete'] = $info;
92 if ( !isset( $this->mFields[$key] ) ) {
95 return $this->mFields[$key];
107 foreach ( $this->mParams[
'fields'] as $fieldname => $info ) {
108 $name =
"{$this->mName}[$key][$fieldname]";
109 if ( isset( $info[
'name'] ) ) {
110 $info[
'name'] =
"{$this->mName}[$key][{$info['name']}]";
112 $info[
'name'] = $name;
114 if ( isset( $info[
'id'] ) ) {
121 if ( $this->mCondState ) {
122 foreach ( [
'hide',
'disable' ] as
$type ) {
123 if ( !isset( $this->mCondState[
$type] ) ) {
126 $param =
$type .
'-if';
127 if ( isset( $info[$param] ) ) {
129 $info[$param] = [
'OR', $info[$param], $this->mCondState[
$type] ];
132 $info[$param] = $this->mCondState[
$type];
137 $info[
'cloner'] = &$cloner;
138 $info[
'cloner-key'] = $key;
140 $fields[$fieldname] = $field;
155 foreach ( $values as $fieldname => $value ) {
156 $name =
"{$this->mName}[$key][$fieldname]";
157 $data[$name] = $value;
168 while ( preg_match(
'/^(.+)\[([^\]]+)\]$/', $name, $m ) ) {
169 array_unshift( $fieldKeys, $m[2] );
172 array_unshift( $fieldKeys, $name );
188 if ( count( $findPath ) > 1 ) {
191 if ( !isset( $this->mParams[
'fields'][$find] ) ) {
192 if ( isset( $this->mParams[
'cloner'] ) ) {
193 return $this->mParams[
'cloner']->findNearestField( $this, $find );
198 return $fields[$find];
206 $path = [ $this->mParams[
'fieldname'], $field->mParams[
'cloner-key'] ];
207 if ( isset( $this->mParams[
'cloner'] ) ) {
222 $alldata = $alldata[$key];
224 return $alldata[$field->mParams[
'fieldname']];
234 if ( !$request->getCheck(
'wpEditToken' ) && $request->getArray( $this->mName ) ===
null ) {
238 $values = $request->getArray( $this->mName );
239 if ( $values ===
null ) {
244 foreach ( $values as $key => $value ) {
245 if ( $key ===
'create' || isset( $value[
'delete'] ) ) {
257 foreach ( $fields as $fieldname => $field ) {
258 if ( $field->skipLoadData( $subrequest ) ) {
261 if ( !empty( $field->mParams[
'disabled'] ) ) {
262 $row[$fieldname] = $field->getDefault();
264 $row[$fieldname] = $field->loadDataFromRequest( $subrequest );
270 if ( isset( $values[
'create'] ) ) {
274 foreach ( $fields as $fieldname => $field ) {
275 if ( !empty( $field->mParams[
'nodata'] ) ) {
278 $row[$fieldname] = $field->getDefault();
287 $ret = parent::getDefault();
290 if ( $ret ===
null ) {
293 foreach ( $fields as $fieldname => $field ) {
294 if ( !empty( $field->mParams[
'nodata'] ) ) {
297 $row[$fieldname] = $field->getDefault();
310 if ( isset( $values[
'nonjs'] ) ) {
314 foreach ( $values as $key => $value ) {
316 foreach ( $fields as $fieldname => $field ) {
317 if ( !array_key_exists( $fieldname, $value ) ) {
320 if ( $field->cancelSubmit( $value[$fieldname], $alldata ) ) {
326 return parent::cancelSubmit( $values, $alldata );
334 if ( isset( $this->mParams[
'required'] )
335 && $this->mParams[
'required'] !==
false
338 return $this->
msg(
'htmlform-cloner-required' );
341 if ( isset( $values[
'nonjs'] ) ) {
348 foreach ( $values as $key => $value ) {
350 foreach ( $fields as $fieldname => $field ) {
351 if ( !array_key_exists( $fieldname, $value ) ) {
354 if ( $field->isHidden( $alldata ) ) {
357 $ok = $field->validate( $value[$fieldname], $alldata );
358 if ( $ok !==
true ) {
364 return parent::validate( $values, $alldata );
375 $displayFormat = $this->mParams[
'format'] ?? $this->mParent->getDisplayFormat();
378 $getFieldHtmlMethod = $displayFormat ==
'table' ?
'getTableRow' : (
'get' . $displayFormat );
385 foreach ( $fields as $fieldname => $field ) {
386 $v = array_key_exists( $fieldname, $values )
387 ? $values[$fieldname]
388 : $field->getDefault();
392 list( $name, $value, $params ) = $field->getHiddenFieldData( $v );
393 $hidden .=
Html::hidden( $name, $value, $params ) .
"\n";
395 $html .= $field->$getFieldHtmlMethod( $v );
397 $labelValue = trim( $field->getLabel() );
398 if ( $labelValue !==
"\u{00A0}" && $labelValue !==
' ' && $labelValue !==
'' ) {
404 if ( !isset( $fields[
'delete'] ) ) {
407 if ( $displayFormat ===
'table' ) {
408 $html .= $field->$getFieldHtmlMethod( $field->getDefault() );
410 $html .= $field->getInputHTML( $field->getDefault() );
414 if ( $displayFormat !==
'raw' ) {
415 $classes = [
'mw-htmlform-cloner-row' ];
418 $classes[] =
'mw-htmlform-nolabel';
421 $attribs = [
'class' => $classes ];
423 if ( $displayFormat ===
'table' ) {
434 if ( !empty( $this->mParams[
'row-legend'] ) ) {
435 $legend = $this->
msg( $this->mParams[
'row-legend'] )->text();
447 $name =
"{$this->mName}[$key][delete]";
448 $label = $this->mParams[
'delete-button-message'] ??
'htmlform-cloner-delete';
451 'formnovalidate' =>
true,
454 'cssclass' =>
'mw-htmlform-cloner-delete-button',
455 'default' => $this->
getMessage( $label )->text(),
456 'disabled' => $this->mParams[
'disabled'] ??
false,
462 $name =
"{$this->mName}[create]";
463 $label = $this->mParams[
'create-button-message'] ??
'htmlform-cloner-create';
466 'formnovalidate' =>
true,
469 'cssclass' =>
'mw-htmlform-cloner-create-button',
470 'default' => $this->getMessage( $label )->text(),
471 'disabled' => $this->mParams[
'disabled'] ??
false,
478 foreach ( (array)$values as $key => $value ) {
479 if ( $key ===
'nonjs' ) {
483 $this->getInputHTMLForKey( $key, $value )
487 $template = $this->getInputHTMLForKey( $this->uniqueId, [] );
490 'id' =>
"mw-htmlform-cloner-list-{$this->mID}",
491 'class' =>
'mw-htmlform-cloner-ul',
492 'data-template' => $template,
493 'data-unique-id' => $this->uniqueId,
496 $field = $this->getCreateButtonHtml();
497 $html .= $field->getInputHTML( $field->getDefault() );
513 $fields = $this->getFieldsForKey( $key );
514 foreach ( $fields as $fieldname => $field ) {
515 $v = array_key_exists( $fieldname, $values )
516 ? $values[$fieldname]
517 : $field->getDefault();
521 list( $name, $value, $params ) = $field->getHiddenFieldData( $v );
522 $hidden .=
Html::hidden( $name, $value, $params ) .
"\n";
524 $html .= $field->getOOUI( $v );
528 if ( !isset( $fields[
'delete'] ) ) {
529 $field = $this->getDeleteButtonHtml( $key );
530 $fieldHtml = $field->getInputOOUI( $field->getDefault() );
531 $fieldHtml->setInfusable(
true );
536 $html =
Html::rawElement(
'div', [
'class' =>
'mw-htmlform-cloner-row' ],
"\n$html\n" );
540 if ( !empty( $this->mParams[
'row-legend'] ) ) {
541 $legend = $this->msg( $this->mParams[
'row-legend'] )->text();
551 foreach ( (array)$values as $key => $value ) {
552 if ( $key ===
'nonjs' ) {
556 $this->getInputOOUIForKey( $key, $value )
560 $template = $this->getInputOOUIForKey( $this->uniqueId, [] );
563 'id' =>
"mw-htmlform-cloner-list-{$this->mID}",
564 'class' =>
'mw-htmlform-cloner-ul',
565 'data-template' => $template,
566 'data-unique-id' => $this->uniqueId,
569 $field = $this->getCreateButtonHtml();
570 $fieldHtml = $field->getInputOOUI( $field->getDefault() );
571 $fieldHtml->setInfusable(
true );
Similar to FauxRequest, but only fakes URL parameters and method (POST or GET) and use the base reque...
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
static escapeIdForAttribute( $id, $mode=self::ID_PRIMARY)
Given a section name or other user-generated or otherwise unsafe string, escapes it to be a valid HTM...
static fieldset( $legend=false, $content=false, $attribs=[])
Shortcut for creating fieldsets.