44 private static $counter = 0;
61 $this->uniqueId = $this->
getClassName() . ++self::$counter .
'x';
62 parent::__construct( $params );
64 if ( empty( $this->mParams[
'fields'] ) || !is_array( $this->mParams[
'fields'] ) ) {
65 throw new InvalidArgumentException(
'HTMLFormFieldCloner called without any fields' );
69 if ( isset( $this->mParams[
'fields'][
'delete'] ) ) {
70 $class =
'mw-htmlform-cloner-delete-button';
71 $info = $this->mParams[
'fields'][
'delete'] + [
72 'formnovalidate' =>
true,
75 unset( $info[
'name'], $info[
'class'] );
77 if ( !isset( $info[
'type'] ) || $info[
'type'] !==
'submit' ) {
78 throw new InvalidArgumentException(
79 'HTMLFormFieldCloner delete field, if specified, must be of type "submit"'
83 if ( !in_array( $class, explode(
' ', $info[
'cssclass'] ) ) ) {
84 $info[
'cssclass'] .=
" $class";
87 $this->mParams[
'fields'][
'delete'] = $info;
96 if ( !isset( $this->mFields[$key] ) ) {
99 return $this->mFields[$key];
111 foreach ( $this->mParams[
'fields'] as $fieldname => $info ) {
112 $name =
"{$this->mName}[$key][$fieldname]";
113 if ( isset( $info[
'name'] ) ) {
114 $info[
'name'] =
"{$this->mName}[$key][{$info['name']}]";
116 $info[
'name'] = $name;
118 if ( isset( $info[
'id'] ) ) {
119 $info[
'id'] = Sanitizer::escapeIdForAttribute(
"{$this->mID}--$key--{$info['id']}" );
121 $info[
'id'] = Sanitizer::escapeIdForAttribute(
"{$this->mID}--$key--$fieldname" );
125 if ( $this->mCondState ) {
126 foreach ( [
'hide',
'disable' ] as $type ) {
127 if ( !isset( $this->mCondState[$type] ) ) {
130 $param = $type .
'-if';
131 if ( isset( $info[$param] ) ) {
133 $info[$param] = [
'OR', $info[$param], $this->mCondState[$type] ];
136 $info[$param] = $this->mCondState[$type];
141 $info[
'cloner'] = &$cloner;
142 $info[
'cloner-key'] = $key;
144 $fields[$fieldname] = $field;
159 foreach ( $values as $fieldname => $value ) {
160 $name =
"{$this->mName}[$key][$fieldname]";
161 $data[$name] = $value;
172 while ( preg_match(
'/^(.+)\[([^\]]+)\]$/', $name, $m ) ) {
173 array_unshift( $fieldKeys, $m[2] );
176 array_unshift( $fieldKeys, $name );
192 if ( count( $findPath ) > 1 ) {
195 if ( !isset( $this->mParams[
'fields'][$find] ) ) {
196 if ( isset( $this->mParams[
'cloner'] ) ) {
197 return $this->mParams[
'cloner']->findNearestField( $this, $find );
202 return $fields[$find];
210 $path = [ $this->mParams[
'fieldname'], $field->mParams[
'cloner-key'] ];
211 if ( isset( $this->mParams[
'cloner'] ) ) {
226 $alldata = $alldata[$key];
228 return $alldata[$field->mParams[
'fieldname']];
238 if ( !$request->getCheck(
'wpEditToken' ) && $request->getArray( $this->mName ) ===
null ) {
242 $values = $request->getArray( $this->mName ) ?? [];
245 foreach ( $values as $key => $value ) {
246 if ( $key ===
'create' || isset( $value[
'delete'] ) ) {
258 foreach ( $fields as $fieldname => $field ) {
259 if ( $field->skipLoadData( $subrequest ) ) {
262 if ( !empty( $field->mParams[
'disabled'] ) ) {
263 $row[$fieldname] = $field->getDefault();
265 $row[$fieldname] = $field->loadDataFromRequest( $subrequest );
271 if ( isset( $values[
'create'] ) ) {
275 foreach ( $fields as $fieldname => $field ) {
276 if ( !empty( $field->mParams[
'nodata'] ) ) {
279 $row[$fieldname] = $field->getDefault();
288 $ret = parent::getDefault();
291 if ( $ret ===
null ) {
294 foreach ( $fields as $fieldname => $field ) {
295 if ( !empty( $field->mParams[
'nodata'] ) ) {
298 $row[$fieldname] = $field->getDefault();
311 if ( isset( $values[
'nonjs'] ) ) {
315 foreach ( $values as $key => $value ) {
317 foreach ( $fields as $fieldname => $field ) {
318 if ( !array_key_exists( $fieldname, $value ) ) {
321 if ( $field->cancelSubmit( $value[$fieldname], $alldata ) ) {
327 return parent::cancelSubmit( $values, $alldata );
335 if ( isset( $this->mParams[
'required'] )
336 && $this->mParams[
'required'] !==
false
339 return $this->
msg(
'htmlform-cloner-required' );
342 if ( isset( $values[
'nonjs'] ) ) {
349 foreach ( $values as $key => $value ) {
351 foreach ( $fields as $fieldname => $field ) {
352 if ( !array_key_exists( $fieldname, $value ) || $field->isHidden( $alldata ) ) {
355 $ok = $field->validate( $value[$fieldname], $alldata );
356 if ( $ok !==
true ) {
362 return parent::validate( $values, $alldata );
373 $displayFormat = $this->mParams[
'format'] ?? $this->mParent->getDisplayFormat();
376 $getFieldHtmlMethod = $displayFormat ==
'table' ?
'getTableRow' : (
'get' . $displayFormat );
383 foreach ( $fields as $fieldname => $field ) {
384 $v = array_key_exists( $fieldname, $values )
385 ? $values[$fieldname]
386 : $field->getDefault();
390 [ $name, $value, $params ] = $field->getHiddenFieldData( $v );
391 $hidden .= Html::hidden( $name, $value, $params ) .
"\n";
393 $html .= $field->$getFieldHtmlMethod( $v );
395 $labelValue = trim( $field->getLabel() );
396 if ( $labelValue !==
"\u{00A0}" && $labelValue !==
' ' && $labelValue !==
'' ) {
402 if ( !isset( $fields[
'delete'] ) ) {
405 if ( $displayFormat ===
'table' ) {
406 $html .= $field->$getFieldHtmlMethod( $field->getDefault() );
408 $html .= $field->getInputHTML( $field->getDefault() );
412 if ( $displayFormat !==
'raw' ) {
413 $classes = [
'mw-htmlform-cloner-row' ];
416 $classes[] =
'mw-htmlform-nolabel';
419 $attribs = [
'class' => $classes ];
421 if ( $displayFormat ===
'table' ) {
422 $html = Html::rawElement(
'table',
424 Html::rawElement(
'tbody', [],
"\n$html\n" ) ) .
"\n";
426 $html = Html::rawElement(
'div', $attribs,
"\n$html\n" );
432 if ( !empty( $this->mParams[
'row-legend'] ) ) {
433 $legend = $this->
msg( $this->mParams[
'row-legend'] )->text();
445 $name =
"{$this->mName}[$key][delete]";
446 $label = $this->mParams[
'delete-button-message'] ??
'htmlform-cloner-delete';
449 'formnovalidate' =>
true,
451 'id' => Sanitizer::escapeIdForAttribute(
"{$this->mID}--$key--delete" ),
452 'cssclass' =>
'mw-htmlform-cloner-delete-button',
453 'default' => $this->
getMessage( $label )->text(),
454 'disabled' => $this->mParams[
'disabled'] ??
false,
460 $name =
"{$this->mName}[create]";
461 $label = $this->mParams[
'create-button-message'] ??
'htmlform-cloner-create';
464 'formnovalidate' =>
true,
466 'id' => Sanitizer::escapeIdForAttribute(
"{$this->mID}--create" ),
467 'cssclass' =>
'mw-htmlform-cloner-create-button',
468 'default' => $this->getMessage( $label )->text(),
469 'disabled' => $this->mParams[
'disabled'] ??
false,
476 foreach ( (array)$values as $key => $value ) {
477 if ( $key ===
'nonjs' ) {
480 $html .= Html::rawElement(
'li', [
'class' =>
'mw-htmlform-cloner-li' ],
481 $this->getInputHTMLForKey( $key, $value )
485 $template = $this->getInputHTMLForKey( $this->uniqueId, [] );
486 $html = Html::rawElement(
'ul', [
487 'id' =>
"mw-htmlform-cloner-list-{$this->mID}",
488 'class' =>
'mw-htmlform-cloner-ul',
489 'data-template' => $template,
490 'data-unique-id' => $this->uniqueId,
493 $field = $this->getCreateButtonHtml();
494 $html .= $field->getInputHTML( $field->getDefault() );
510 $fields = $this->getFieldsForKey( $key );
511 foreach ( $fields as $fieldname => $field ) {
512 $v = array_key_exists( $fieldname, $values )
513 ? $values[$fieldname]
514 : $field->getDefault();
518 [ $name, $value, $params ] = $field->getHiddenFieldData( $v );
519 $hidden .= Html::hidden( $name, $value, $params ) .
"\n";
521 $html .= $field->getOOUI( $v );
525 if ( !isset( $fields[
'delete'] ) ) {
526 $field = $this->getDeleteButtonHtml( $key );
527 $fieldHtml = $field->getInputOOUI( $field->getDefault() );
528 $fieldHtml->setInfusable(
true );
533 $html = Html::rawElement(
'div', [
'class' =>
'mw-htmlform-cloner-row' ],
"\n$html\n" );
537 if ( !empty( $this->mParams[
'row-legend'] ) ) {
538 $legend = $this->msg( $this->mParams[
'row-legend'] )->text();
548 foreach ( (array)$values as $key => $value ) {
549 if ( $key ===
'nonjs' ) {
552 $html .= Html::rawElement(
'li', [
'class' =>
'mw-htmlform-cloner-li' ],
553 $this->getInputOOUIForKey( $key, $value )
557 $template = $this->getInputOOUIForKey( $this->uniqueId, [] );
558 $html = Html::rawElement(
'ul', [
559 'id' =>
"mw-htmlform-cloner-list-{$this->mID}",
560 'class' =>
'mw-htmlform-cloner-ul',
561 'data-template' => $template,
562 'data-unique-id' => $this->uniqueId,
565 $field = $this->getCreateButtonHtml();
566 $fieldHtml = $field->getInputOOUI( $field->getDefault() );
567 $fieldHtml->setInfusable(
true );
static fieldset( $legend=false, $content=false, $attribs=[])
Shortcut for creating fieldsets.