5use InvalidArgumentException;
50 private static $counter = 0;
60 protected $mFields = [];
70 if ( empty( $this->mParams[
'fields'] ) || !is_array( $this->mParams[
'fields'] ) ) {
71 throw new InvalidArgumentException(
'HTMLFormFieldCloner called without any fields' );
75 if ( isset( $this->mParams[
'fields'][
'delete'] ) ) {
76 $class =
'mw-htmlform-cloner-delete-button';
77 $info = $this->mParams[
'fields'][
'delete'] + [
78 'formnovalidate' =>
true,
81 unset( $info[
'name'], $info[
'class'] );
83 if ( !isset( $info[
'type'] ) || $info[
'type'] !==
'submit' ) {
84 throw new InvalidArgumentException(
85 'HTMLFormFieldCloner delete field, if specified, must be of type "submit"'
89 if ( !in_array( $class, explode(
' ', $info[
'cssclass'] ) ) ) {
90 $info[
'cssclass'] .=
" $class";
93 $this->mParams[
'fields'][
'delete'] = $info;
102 if ( !isset( $this->mFields[$key] ) ) {
105 return $this->mFields[$key];
117 foreach ( $this->mParams[
'fields'] as $fieldname => $info ) {
118 $name =
"{$this->mName}[$key][$fieldname]";
119 if ( isset( $info[
'name'] ) ) {
120 $info[
'name'] =
"{$this->mName}[$key][{$info['name']}]";
122 $info[
'name'] = $name;
124 if ( isset( $info[
'id'] ) ) {
125 $info[
'id'] = Sanitizer::escapeIdForAttribute(
"{$this->mID}--$key--{$info['id']}" );
127 $info[
'id'] = Sanitizer::escapeIdForAttribute(
"{$this->mID}--$key--$fieldname" );
131 if ( $this->mCondState ) {
132 foreach ( [
'hide',
'disable' ] as $type ) {
133 if ( !isset( $this->mCondState[$type] ) ) {
136 $param = $type .
'-if';
137 if ( isset( $info[$param] ) ) {
139 $info[$param] = [
'OR', $info[$param], $this->mCondState[$type] ];
142 $info[$param] = $this->mCondState[$type];
147 $info[
'cloner'] = &$cloner;
148 $info[
'cloner-key'] = $key;
150 $fields[$fieldname] = $field;
165 foreach ( $values as $fieldname => $value ) {
166 $name =
"{$this->mName}[$key][$fieldname]";
167 $data[$name] = $value;
178 while ( preg_match(
'/^(.+)\[([^\]]+)\]$/', $name, $m ) ) {
179 array_unshift( $fieldKeys, $m[2] );
182 array_unshift( $fieldKeys, $name );
198 if ( count( $findPath ) > 1 ) {
201 if ( !isset( $this->mParams[
'fields'][$find] ) ) {
202 $cloner = $this->mParams[
'cloner'] ??
null;
203 if ( $cloner instanceof
self ) {
204 return $cloner->findNearestField( $this, $find );
209 return $fields[$find];
217 $path = [ $this->mParams[
'fieldname'], $field->mParams[
'cloner-key'] ];
218 $cloner = $this->mParams[
'cloner'] ??
null;
219 if ( $cloner instanceof
self ) {
220 $path = array_merge( $cloner->getFieldPath( $this ),
$path );
234 $alldata = $alldata[$key];
236 return $alldata[$field->mParams[
'fieldname']];
246 if ( !$request->getCheck(
'wpEditToken' ) && $request->getArray( $this->mName ) ===
null ) {
250 $values = $request->getArray( $this->mName ) ?? [];
253 foreach ( $values as $key => $value ) {
254 if ( $key ===
'create' || isset( $value[
'delete'] ) ) {
266 foreach ( $fields as $fieldname => $field ) {
267 if ( $field->skipLoadData( $subrequest ) ) {
270 if ( !empty( $field->mParams[
'disabled'] ) ) {
271 $row[$fieldname] = $field->getDefault();
273 $row[$fieldname] = $field->loadDataFromRequest( $subrequest );
279 if ( isset( $values[
'create'] ) ) {
283 foreach ( $fields as $fieldname => $field ) {
284 if ( !empty( $field->mParams[
'nodata'] ) ) {
287 $row[$fieldname] = $field->getDefault();
296 $ret = parent::getDefault();
299 if ( $ret ===
null ) {
302 foreach ( $fields as $fieldname => $field ) {
303 if ( !empty( $field->mParams[
'nodata'] ) ) {
306 $row[$fieldname] = $field->getDefault();
319 if ( isset( $values[
'nonjs'] ) ) {
323 foreach ( $values as $key => $value ) {
325 foreach ( $fields as $fieldname => $field ) {
326 if ( !array_key_exists( $fieldname, $value ) ) {
329 if ( $field->cancelSubmit( $value[$fieldname], $alldata ) ) {
335 return parent::cancelSubmit( $values, $alldata );
343 if ( isset( $this->mParams[
'required'] )
344 && $this->mParams[
'required'] !==
false
347 return $this->
msg(
'htmlform-cloner-required' );
350 if ( isset( $values[
'nonjs'] ) ) {
357 foreach ( $values as $key => $value ) {
359 foreach ( $fields as $fieldname => $field ) {
360 if ( !array_key_exists( $fieldname, $value ) || $field->isHidden( $alldata ) ) {
363 $ok = $field->validate( $value[$fieldname], $alldata );
364 if ( $ok !==
true ) {
370 return parent::validate( $values, $alldata );
381 $displayFormat = $this->mParams[
'format'] ?? $this->mParent->getDisplayFormat();
384 $getFieldHtmlMethod = $displayFormat ==
'table' ?
'getTableRow' : (
'get' . $displayFormat );
391 foreach ( $fields as $fieldname => $field ) {
392 $v = array_key_exists( $fieldname, $values )
393 ? $values[$fieldname]
394 : $field->getDefault();
398 [ $name, $value,
$params ] = $field->getHiddenFieldData( $v );
399 $hidden .= Html::hidden( $name, $value,
$params ) .
"\n";
401 $html .= $field->$getFieldHtmlMethod( $v );
403 $labelValue = trim( $field->getLabel() );
404 if ( $labelValue !==
"\u{00A0}" && $labelValue !==
' ' && $labelValue !==
'' ) {
410 if ( !isset( $fields[
'delete'] ) ) {
413 if ( $displayFormat ===
'table' ) {
414 $html .= $field->$getFieldHtmlMethod( $field->getDefault() );
416 $html .= $field->getInputHTML( $field->getDefault() );
420 if ( $displayFormat !==
'raw' ) {
421 $classes = [
'mw-htmlform-cloner-row' ];
424 $classes[] =
'mw-htmlform-nolabel';
427 $attribs = [
'class' => $classes ];
429 if ( $displayFormat ===
'table' ) {
430 $html = Html::rawElement(
'table',
432 Html::rawElement(
'tbody', [],
"\n$html\n" ) ) .
"\n";
434 $html = Html::rawElement(
'div', $attribs,
"\n$html\n" );
440 if ( !empty( $this->mParams[
'row-legend'] ) ) {
441 $legend = $this->
msg( $this->mParams[
'row-legend'] )->text();
442 $html = Xml::fieldset( $legend, $html );
453 $name =
"{$this->mName}[$key][delete]";
454 $label = $this->mParams[
'delete-button-message'] ??
'htmlform-cloner-delete';
457 'formnovalidate' =>
true,
459 'id' => Sanitizer::escapeIdForAttribute(
"{$this->mID}--$key--delete" ),
460 'cssclass' =>
'mw-htmlform-cloner-delete-button',
461 'default' => $this->
getMessage( $label )->text(),
462 'disabled' => $this->mParams[
'disabled'] ??
false,
468 $name =
"{$this->mName}[create]";
469 $label = $this->mParams[
'create-button-message'] ??
'htmlform-cloner-create';
472 'formnovalidate' =>
true,
474 'id' => Sanitizer::escapeIdForAttribute(
"{$this->mID}--create" ),
475 'cssclass' =>
'mw-htmlform-cloner-create-button',
476 'default' => $this->getMessage( $label )->text(),
477 'disabled' => $this->mParams[
'disabled'] ??
false,
484 foreach ( (array)$values as $key => $value ) {
485 if ( $key ===
'nonjs' ) {
488 $html .= Html::rawElement(
'li', [
'class' =>
'mw-htmlform-cloner-li' ],
489 $this->getInputHTMLForKey( $key, $value )
493 $template = $this->getInputHTMLForKey( $this->uniqueId, [] );
494 $html = Html::rawElement(
'ul', [
495 'id' =>
"mw-htmlform-cloner-list-{$this->mID}",
496 'class' =>
'mw-htmlform-cloner-ul',
497 'data-template' => $template,
498 'data-unique-id' => $this->uniqueId,
501 $field = $this->getCreateButtonHtml();
502 $html .= $field->getInputHTML( $field->getDefault() );
518 $fields = $this->getFieldsForKey( $key );
519 foreach ( $fields as $fieldname => $field ) {
520 $v = array_key_exists( $fieldname, $values )
521 ? $values[$fieldname]
522 : $field->getDefault();
526 [ $name, $value,
$params ] = $field->getHiddenFieldData( $v );
527 $hidden .= Html::hidden( $name, $value,
$params ) .
"\n";
529 $html .= $field->getOOUI( $v );
533 if ( !isset( $fields[
'delete'] ) ) {
534 $field = $this->getDeleteButtonHtml( $key );
535 $fieldHtml = $field->getInputOOUI( $field->getDefault() );
536 $fieldHtml->setInfusable(
true );
541 $html = Html::rawElement(
'div', [
'class' =>
'mw-htmlform-cloner-row' ],
"\n$html\n" );
545 if ( !empty( $this->mParams[
'row-legend'] ) ) {
546 $legend = $this->msg( $this->mParams[
'row-legend'] )->text();
547 $html = Xml::fieldset( $legend, $html );
556 foreach ( (array)$values as $key => $value ) {
557 if ( $key ===
'nonjs' ) {
560 $html .= Html::rawElement(
'li', [
'class' =>
'mw-htmlform-cloner-li' ],
561 $this->getInputOOUIForKey( $key, $value )
565 $template = $this->getInputOOUIForKey( $this->uniqueId, [] );
566 $html = Html::rawElement(
'ul', [
567 'id' =>
"mw-htmlform-cloner-list-{$this->mID}",
568 'class' =>
'mw-htmlform-cloner-ul',
569 'data-template' => $template,
570 'data-unique-id' => $this->uniqueId,
573 $field = $this->getCreateButtonHtml();
574 $fieldHtml = $field->getInputOOUI( $field->getDefault() );
575 $fieldHtml->setInfusable(
true );
584class_alias( HTMLFormFieldCloner::class,
'HTMLFormFieldCloner' );
array $params
The job parameters.
Module of static functions for generating XML.