MediaWiki REL1_40
HTMLFormField.php
Go to the documentation of this file.
1<?php
2
5
12abstract class HTMLFormField {
14 public $mParams;
15
18 protected $mName;
19 protected $mDir;
20 protected $mLabel; # String label, as HTML. Set on construction.
21 protected $mID;
22 protected $mClass = '';
23 protected $mVFormClass = '';
24 protected $mHelpClass = false;
25 protected $mDefault;
29 protected $mOptions = false;
34 protected $mCondState = [];
35 protected $mCondStateClass = [];
36
41 protected $mShowEmptyLabels = true;
42
46 public $mParent;
47
58 abstract public function getInputHTML( $value );
59
68 public function getInputOOUI( $value ) {
69 return false;
70 }
71
78 public function canDisplayErrors() {
79 return $this->hasVisibleOutput();
80 }
81
94 public function msg( $key, ...$params ) {
95 if ( $this->mParent ) {
96 return $this->mParent->msg( $key, ...$params );
97 }
98 return wfMessage( $key, ...$params );
99 }
100
108 public function hasVisibleOutput() {
109 return true;
110 }
111
118 public function getName() {
119 return $this->mName;
120 }
121
133 protected function getNearestField( $name, $backCompat = false ) {
134 // When the field is belong to a HTMLFormFieldCloner
135 if ( isset( $this->mParams['cloner'] ) ) {
136 $field = $this->mParams['cloner']->findNearestField( $this, $name );
137 if ( $field ) {
138 return $field;
139 }
140 }
141
142 if ( $backCompat && str_starts_with( $name, 'wp' ) &&
143 !$this->mParent->hasField( $name )
144 ) {
145 // Don't break the existed use cases.
146 return $this->mParent->getField( substr( $name, 2 ) );
147 }
148 return $this->mParent->getField( $name );
149 }
150
162 protected function getNearestFieldValue( $alldata, $name, $asDisplay = false, $backCompat = false ) {
163 $field = $this->getNearestField( $name, $backCompat );
164 // When the field belongs to a HTMLFormFieldCloner
165 if ( isset( $field->mParams['cloner'] ) ) {
166 $value = $field->mParams['cloner']->extractFieldData( $field, $alldata );
167 } else {
168 // Note $alldata is an empty array when first rendering a form with a formIdentifier.
169 // In that case, $alldata[$field->mParams['fieldname']] is unset and we use the
170 // field's default value
171 $value = $alldata[$field->mParams['fieldname']] ?? $field->getDefault();
172 }
173
174 // Check invert state for HTMLCheckField
175 if ( $asDisplay && $field instanceof HTMLCheckField && ( $field->mParams['invert'] ?? false ) ) {
176 $value = !$value;
177 }
178
179 return $value;
180 }
181
192 protected function getNearestFieldByName( $alldata, $name, $asDisplay = false ) {
193 return (string)$this->getNearestFieldValue( $alldata, $name, $asDisplay );
194 }
195
203 protected function validateCondState( $params ) {
204 $origParams = $params;
205 $op = array_shift( $params );
206
207 try {
208 switch ( $op ) {
209 case 'NOT':
210 if ( count( $params ) !== 1 ) {
211 throw new MWException( "NOT takes exactly one parameter" );
212 }
213 // Fall-through intentionally
214
215 case 'AND':
216 case 'OR':
217 case 'NAND':
218 case 'NOR':
219 foreach ( $params as $i => $p ) {
220 if ( !is_array( $p ) ) {
221 $type = gettype( $p );
222 throw new MWException( "Expected array, found $type at index $i" );
223 }
224 $this->validateCondState( $p );
225 }
226 break;
227
228 case '===':
229 case '!==':
230 if ( count( $params ) !== 2 ) {
231 throw new MWException( "$op takes exactly two parameters" );
232 }
233 [ $name, $value ] = $params;
234 if ( !is_string( $name ) || !is_string( $value ) ) {
235 throw new MWException( "Parameters for $op must be strings" );
236 }
237 break;
238
239 default:
240 throw new MWException( "Unknown operation" );
241 }
242 } catch ( MWException $ex ) {
243 throw new MWException(
244 "Invalid hide-if or disable-if specification for $this->mName: " .
245 $ex->getMessage() . " in " . var_export( $origParams, true ),
246 0, $ex
247 );
248 }
249 }
250
259 protected function checkStateRecurse( array $alldata, array $params ) {
260 $op = array_shift( $params );
261 $valueChk = [ 'AND' => false, 'OR' => true, 'NAND' => false, 'NOR' => true ];
262 $valueRet = [ 'AND' => true, 'OR' => false, 'NAND' => false, 'NOR' => true ];
263
264 switch ( $op ) {
265 case 'AND':
266 case 'OR':
267 case 'NAND':
268 case 'NOR':
269 foreach ( $params as $p ) {
270 if ( $valueChk[$op] === $this->checkStateRecurse( $alldata, $p ) ) {
271 return !$valueRet[$op];
272 }
273 }
274 return $valueRet[$op];
275
276 case 'NOT':
277 return !$this->checkStateRecurse( $alldata, $params[0] );
278
279 case '===':
280 case '!==':
281 [ $field, $value ] = $params;
282 $testValue = (string)$this->getNearestFieldValue( $alldata, $field, true, true );
283 switch ( $op ) {
284 case '===':
285 return ( $value === $testValue );
286 case '!==':
287 return ( $value !== $testValue );
288 }
289 }
290 }
291
300 protected function parseCondState( $params ) {
301 $op = array_shift( $params );
302
303 switch ( $op ) {
304 case 'AND':
305 case 'OR':
306 case 'NAND':
307 case 'NOR':
308 $ret = [ $op ];
309 foreach ( $params as $p ) {
310 $ret[] = $this->parseCondState( $p );
311 }
312 return $ret;
313
314 case 'NOT':
315 return [ 'NOT', $this->parseCondState( $params[0] ) ];
316
317 case '===':
318 case '!==':
319 [ $name, $value ] = $params;
320 $field = $this->getNearestField( $name, true );
321 return [ $op, $field->getName(), $value ];
322 }
323 }
324
330 protected function parseCondStateForClient() {
331 $parsed = [];
332 foreach ( $this->mCondState as $type => $params ) {
333 $parsed[$type] = $this->parseCondState( $params );
334 }
335 return $parsed;
336 }
337
346 public function isHidden( $alldata ) {
347 if ( !( $this->mCondState && isset( $this->mCondState['hide'] ) ) ) {
348 return false;
349 }
350
351 return $this->checkStateRecurse( $alldata, $this->mCondState['hide'] );
352 }
353
362 public function isDisabled( $alldata ) {
363 if ( $this->mParams['disabled'] ?? false ) {
364 return true;
365 }
366 $hidden = $this->isHidden( $alldata );
367 if ( !$this->mCondState || !isset( $this->mCondState['disable'] ) ) {
368 return $hidden;
369 }
370
371 return $hidden || $this->checkStateRecurse( $alldata, $this->mCondState['disable'] );
372 }
373
385 public function cancelSubmit( $value, $alldata ) {
386 return false;
387 }
388
401 public function validate( $value, $alldata ) {
402 if ( $this->isHidden( $alldata ) ) {
403 return true;
404 }
405
406 if ( isset( $this->mParams['required'] )
407 && $this->mParams['required'] !== false
408 && ( $value === '' || $value === false || $value === null )
409 ) {
410 return $this->msg( 'htmlform-required' );
411 }
412
413 if ( isset( $this->mValidationCallback ) ) {
414 return ( $this->mValidationCallback )( $value, $alldata, $this->mParent );
415 }
416
417 return true;
418 }
419
428 public function filter( $value, $alldata ) {
429 if ( isset( $this->mFilterCallback ) ) {
430 $value = ( $this->mFilterCallback )( $value, $alldata, $this->mParent );
431 }
432
433 return $value;
434 }
435
443 protected function needsLabel() {
444 return true;
445 }
446
456 public function setShowEmptyLabel( $show ) {
457 $this->mShowEmptyLabels = $show;
458 }
459
471 protected function isSubmitAttempt( WebRequest $request ) {
472 // HTMLForm would add a hidden field of edit token for forms that require to be posted.
473 return $request->wasPosted() && $request->getCheck( 'wpEditToken' )
474 // The identifier matching or not has been checked in HTMLForm::prepareForm()
475 || $request->getCheck( 'wpFormIdentifier' );
476 }
477
486 public function loadDataFromRequest( $request ) {
487 if ( $request->getCheck( $this->mName ) ) {
488 return $request->getText( $this->mName );
489 } else {
490 return $this->getDefault();
491 }
492 }
493
503 public function __construct( $params ) {
504 $this->mParams = $params;
505
506 if ( isset( $params['parent'] ) && $params['parent'] instanceof HTMLForm ) {
507 $this->mParent = $params['parent'];
508 } else {
509 // Normally parent is added automatically by HTMLForm::factory.
510 // Several field types already assume unconditionally this is always set,
511 // so deprecate manually creating an HTMLFormField without a parent form set.
513 __METHOD__ . ": Constructing an HTMLFormField without a 'parent' parameter",
514 "1.40"
515 );
516 }
517
518 # Generate the label from a message, if possible
519 if ( isset( $params['label-message'] ) ) {
520 $this->mLabel = $this->getMessage( $params['label-message'] )->parse();
521 } elseif ( isset( $params['label'] ) ) {
522 if ( $params['label'] === '&#160;' || $params['label'] === "\u{00A0}" ) {
523 // Apparently some things set &nbsp directly and in an odd format
524 $this->mLabel = "\u{00A0}";
525 } else {
526 $this->mLabel = htmlspecialchars( $params['label'] );
527 }
528 } elseif ( isset( $params['label-raw'] ) ) {
529 $this->mLabel = $params['label-raw'];
530 }
531
532 $this->mName = "wp{$params['fieldname']}";
533 if ( isset( $params['name'] ) ) {
534 $this->mName = $params['name'];
535 }
536
537 if ( isset( $params['dir'] ) ) {
538 $this->mDir = $params['dir'];
539 }
540
541 $this->mID = "mw-input-{$this->mName}";
542
543 if ( isset( $params['default'] ) ) {
544 $this->mDefault = $params['default'];
545 }
546
547 if ( isset( $params['id'] ) ) {
548 $this->mID = $params['id'];
549 }
550
551 if ( isset( $params['cssclass'] ) ) {
552 $this->mClass = $params['cssclass'];
553 }
554
555 if ( isset( $params['csshelpclass'] ) ) {
556 $this->mHelpClass = $params['csshelpclass'];
557 }
558
559 if ( isset( $params['validation-callback'] ) ) {
560 $this->mValidationCallback = $params['validation-callback'];
561 }
562
563 if ( isset( $params['filter-callback'] ) ) {
564 $this->mFilterCallback = $params['filter-callback'];
565 }
566
567 if ( isset( $params['hidelabel'] ) ) {
568 $this->mShowEmptyLabels = false;
569 }
570
571 if ( isset( $params['hide-if'] ) && $params['hide-if'] ) {
572 $this->validateCondState( $params['hide-if'] );
573 $this->mCondState['hide'] = $params['hide-if'];
574 $this->mCondStateClass[] = 'mw-htmlform-hide-if';
575 }
576 if ( !( isset( $params['disabled'] ) && $params['disabled'] ) &&
577 isset( $params['disable-if'] ) && $params['disable-if']
578 ) {
579 $this->validateCondState( $params['disable-if'] );
580 $this->mCondState['disable'] = $params['disable-if'];
581 $this->mCondStateClass[] = 'mw-htmlform-disable-if';
582 }
583 }
584
594 public function getTableRow( $value ) {
595 [ $errors, $errorClass ] = $this->getErrorsAndErrorClass( $value );
596 $inputHtml = $this->getInputHTML( $value );
597 $fieldType = $this->getClassName();
598 $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() );
599 $cellAttributes = [];
600 $rowAttributes = [];
601 $rowClasses = '';
602
603 if ( !empty( $this->mParams['vertical-label'] ) ) {
604 $cellAttributes['colspan'] = 2;
605 $verticalLabel = true;
606 } else {
607 $verticalLabel = false;
608 }
609
610 $label = $this->getLabelHtml( $cellAttributes );
611
612 $field = Html::rawElement(
613 'td',
614 [ 'class' => 'mw-input' ] + $cellAttributes,
615 $inputHtml . "\n$errors"
616 );
617
618 if ( $this->mCondState ) {
619 $rowAttributes['data-cond-state'] = FormatJson::encode( $this->parseCondStateForClient() );
620 $rowClasses .= implode( ' ', $this->mCondStateClass );
621 }
622
623 if ( $verticalLabel ) {
624 $html = Html::rawElement( 'tr',
625 $rowAttributes + [ 'class' => "mw-htmlform-vertical-label $rowClasses" ], $label );
626 $html .= Html::rawElement( 'tr',
627 $rowAttributes + [
628 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $rowClasses"
629 ],
630 $field );
631 } else {
632 $html = Html::rawElement( 'tr',
633 $rowAttributes + [
634 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $rowClasses"
635 ],
636 $label . $field );
637 }
638
639 return $html . $helptext;
640 }
641
652 public function getDiv( $value ) {
653 [ $errors, $errorClass ] = $this->getErrorsAndErrorClass( $value );
654 $inputHtml = $this->getInputHTML( $value );
655 $fieldType = $this->getClassName();
656 $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() );
657 $cellAttributes = [];
658 $label = $this->getLabelHtml( $cellAttributes );
659
660 $outerDivClass = [
661 'mw-input',
662 'mw-htmlform-nolabel' => ( $label === '' )
663 ];
664
665 $horizontalLabel = $this->mParams['horizontal-label'] ?? false;
666
667 if ( $horizontalLabel ) {
668 $field = "\u{00A0}" . $inputHtml . "\n$errors";
669 } else {
670 $field = Html::rawElement(
671 'div',
672 // @phan-suppress-next-line PhanUselessBinaryAddRight
673 [ 'class' => $outerDivClass ] + $cellAttributes,
674 $inputHtml . "\n$errors"
675 );
676 }
677 $divCssClasses = [ "mw-htmlform-field-$fieldType",
678 $this->mClass, $this->mVFormClass, $errorClass ];
679
680 $wrapperAttributes = [
681 'class' => $divCssClasses,
682 ];
683 if ( $this->mCondState ) {
684 $wrapperAttributes['data-cond-state'] = FormatJson::encode( $this->parseCondStateForClient() );
685 $wrapperAttributes['class'] = array_merge( $wrapperAttributes['class'], $this->mCondStateClass );
686 }
687 $html = Html::rawElement( 'div', $wrapperAttributes, $label . $field );
688 $html .= $helptext;
689
690 return $html;
691 }
692
702 public function getOOUI( $value ) {
703 $inputField = $this->getInputOOUI( $value );
704
705 if ( !$inputField ) {
706 // This field doesn't have an OOUI implementation yet at all. Fall back to getDiv() to
707 // generate the whole field, label and errors and all, then wrap it in a Widget.
708 // It might look weird, but it'll work OK.
709 return $this->getFieldLayoutOOUI(
710 new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $this->getDiv( $value ) ) ] ),
711 [ 'align' => 'top' ]
712 );
713 }
714
715 $infusable = true;
716 if ( is_string( $inputField ) ) {
717 // We have an OOUI implementation, but it's not proper, and we got a load of HTML.
718 // Cheat a little and wrap it in a widget. It won't be infusable, though, since client-side
719 // JavaScript doesn't know how to rebuilt the contents.
720 $inputField = new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $inputField ) ] );
721 $infusable = false;
722 }
723
724 $fieldType = $this->getClassName();
725 $help = $this->getHelpText();
726 $errors = $this->getErrorsRaw( $value );
727 foreach ( $errors as &$error ) {
728 $error = new OOUI\HtmlSnippet( $error );
729 }
730
731 $config = [
732 'classes' => [ "mw-htmlform-field-$fieldType" ],
733 'align' => $this->getLabelAlignOOUI(),
734 'help' => ( $help !== null && $help !== '' ) ? new OOUI\HtmlSnippet( $help ) : null,
735 'errors' => $errors,
736 'infusable' => $infusable,
737 'helpInline' => $this->isHelpInline(),
738 ];
739 if ( $this->mClass !== '' ) {
740 $config['classes'][] = $this->mClass;
741 }
742
743 $preloadModules = false;
744
745 if ( $infusable && $this->shouldInfuseOOUI() ) {
746 $preloadModules = true;
747 $config['classes'][] = 'mw-htmlform-autoinfuse';
748 }
749 if ( $this->mCondState ) {
750 $config['classes'] = array_merge( $config['classes'], $this->mCondStateClass );
751 }
752
753 // the element could specify, that the label doesn't need to be added
754 $label = $this->getLabel();
755 if ( $label && $label !== "\u{00A0}" && $label !== '&#160;' ) {
756 $config['label'] = new OOUI\HtmlSnippet( $label );
757 }
758
759 if ( $this->mCondState ) {
760 $preloadModules = true;
761 $config['condState'] = $this->parseCondStateForClient();
762 }
763
764 $config['modules'] = $this->getOOUIModules();
765
766 if ( $preloadModules ) {
767 $this->mParent->getOutput()->addModules( 'mediawiki.htmlform.ooui' );
768 $this->mParent->getOutput()->addModules( $this->getOOUIModules() );
769 }
770
771 return $this->getFieldLayoutOOUI( $inputField, $config );
772 }
773
781 protected function getClassName() {
782 $name = explode( '\\', static::class );
783 return end( $name );
784 }
785
791 protected function getLabelAlignOOUI() {
792 return 'top';
793 }
794
801 protected function getFieldLayoutOOUI( $inputField, $config ) {
802 return new HTMLFormFieldLayout( $inputField, $config );
803 }
804
813 protected function shouldInfuseOOUI() {
814 // Always infuse fields with popup help text, since the interface for it is nicer with JS
815 return $this->getHelpText() !== null && !$this->isHelpInline();
816 }
817
825 protected function getOOUIModules() {
826 return [];
827 }
828
839 public function getRaw( $value ) {
840 [ $errors, ] = $this->getErrorsAndErrorClass( $value );
841 $inputHtml = $this->getInputHTML( $value );
842 $helptext = $this->getHelpTextHtmlRaw( $this->getHelpText() );
843 $cellAttributes = [];
844 $label = $this->getLabelHtml( $cellAttributes );
845
846 $html = "\n$errors";
847 $html .= $label;
848 $html .= $inputHtml;
849 $html .= $helptext;
850
851 return $html;
852 }
853
863 public function getVForm( $value ) {
864 // Ewwww
865 $this->mVFormClass = ' mw-ui-vform-field';
866 return $this->getDiv( $value );
867 }
868
876 public function getInline( $value ) {
877 [ $errors, ] = $this->getErrorsAndErrorClass( $value );
878 $inputHtml = $this->getInputHTML( $value );
879 $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() );
880 $cellAttributes = [];
881 $label = $this->getLabelHtml( $cellAttributes );
882
883 $html = "\n" . $errors .
884 $label . "\u{00A0}" .
885 $inputHtml .
886 $helptext;
887
888 return $html;
889 }
890
898 public function getHelpTextHtmlTable( $helptext ) {
899 if ( $helptext === null ) {
900 return '';
901 }
902
903 $rowAttributes = [];
904 if ( $this->mCondState ) {
905 $rowAttributes['data-cond-state'] = FormatJson::encode( $this->parseCondStateForClient() );
906 $rowAttributes['class'] = $this->mCondStateClass;
907 }
908
909 $tdClasses = [ 'htmlform-tip' ];
910 if ( $this->mHelpClass !== false ) {
911 $tdClasses[] = $this->mHelpClass;
912 }
913 $row = Html::rawElement( 'td', [ 'colspan' => 2, 'class' => $tdClasses ], $helptext );
914 $row = Html::rawElement( 'tr', $rowAttributes, $row );
915
916 return $row;
917 }
918
927 public function getHelpTextHtmlDiv( $helptext ) {
928 if ( $helptext === null ) {
929 return '';
930 }
931
932 $wrapperAttributes = [
933 'class' => [ 'htmlform-tip' ],
934 ];
935 if ( $this->mHelpClass !== false ) {
936 $wrapperAttributes['class'][] = $this->mHelpClass;
937 }
938 if ( $this->mCondState ) {
939 $wrapperAttributes['data-cond-state'] = FormatJson::encode( $this->parseCondStateForClient() );
940 $wrapperAttributes['class'] = array_merge( $wrapperAttributes['class'], $this->mCondStateClass );
941 }
942 $div = Html::rawElement( 'div', $wrapperAttributes, $helptext );
943
944 return $div;
945 }
946
954 public function getHelpTextHtmlRaw( $helptext ) {
955 return $this->getHelpTextHtmlDiv( $helptext );
956 }
957
964 public function getHelpText() {
965 $helptext = null;
966
967 if ( isset( $this->mParams['help-message'] ) ) {
968 $this->mParams['help-messages'] = [ $this->mParams['help-message'] ];
969 }
970
971 if ( isset( $this->mParams['help-messages'] ) ) {
972 foreach ( $this->mParams['help-messages'] as $msg ) {
973 $msg = $this->getMessage( $msg );
974
975 if ( $msg->exists() ) {
976 if ( $helptext === null ) {
977 $helptext = '';
978 } else {
979 $helptext .= $this->msg( 'word-separator' )->escaped(); // some space
980 }
981 $helptext .= $msg->parse(); // Append message
982 }
983 }
984 } elseif ( isset( $this->mParams['help'] ) ) {
985 $helptext = $this->mParams['help'];
986 }
987
988 return $helptext;
989 }
990
999 public function isHelpInline() {
1000 return $this->mParams['help-inline'] ?? true;
1001 }
1002
1015 public function getErrorsAndErrorClass( $value ) {
1016 $errors = $this->validate( $value, $this->mParent->mFieldData );
1017
1018 if ( is_bool( $errors ) || !$this->mParent->wasSubmitted() ) {
1019 $errors = '';
1020 $errorClass = '';
1021 } else {
1022 $errors = self::formatErrors( $errors );
1023 $errorClass = 'mw-htmlform-invalid-input';
1024 }
1025
1026 return [ $errors, $errorClass ];
1027 }
1028
1036 public function getErrorsRaw( $value ) {
1037 $errors = $this->validate( $value, $this->mParent->mFieldData );
1038
1039 if ( is_bool( $errors ) || !$this->mParent->wasSubmitted() ) {
1040 $errors = [];
1041 }
1042
1043 if ( !is_array( $errors ) ) {
1044 $errors = [ $errors ];
1045 }
1046 foreach ( $errors as &$error ) {
1047 if ( $error instanceof Message ) {
1048 $error = $error->parse();
1049 }
1050 }
1051
1052 return $errors;
1053 }
1054
1059 public function getLabel() {
1060 return $this->mLabel ?? '';
1061 }
1062
1069 public function getLabelHtml( $cellAttributes = [] ) {
1070 # Don't output a for= attribute for labels with no associated input.
1071 # Kind of hacky here, possibly we don't want these to be <label>s at all.
1072 $for = [];
1073
1074 if ( $this->needsLabel() ) {
1075 $for['for'] = $this->mID;
1076 }
1077
1078 $labelValue = trim( $this->getLabel() );
1079 $hasLabel = false;
1080 if ( $labelValue !== "\u{00A0}" && $labelValue !== '&#160;' && $labelValue !== '' ) {
1081 $hasLabel = true;
1082 }
1083
1084 $displayFormat = $this->mParent->getDisplayFormat();
1085 $html = '';
1086 $horizontalLabel = $this->mParams['horizontal-label'] ?? false;
1087
1088 if ( $displayFormat === 'table' ) {
1089 $html =
1090 Html::rawElement( 'td',
1091 [ 'class' => 'mw-label' ] + $cellAttributes,
1092 Html::rawElement( 'label', $for, $labelValue ) );
1093 } elseif ( $hasLabel || $this->mShowEmptyLabels ) {
1094 if ( $displayFormat === 'div' && !$horizontalLabel ) {
1095 $html =
1096 Html::rawElement( 'div',
1097 [ 'class' => 'mw-label' ] + $cellAttributes,
1098 Html::rawElement( 'label', $for, $labelValue ) );
1099 } else {
1100 $html = Html::rawElement( 'label', $for, $labelValue );
1101 }
1102 }
1103
1104 return $html;
1105 }
1106
1111 public function getDefault() {
1112 return $this->mDefault ?? null;
1113 }
1114
1120 public function getTooltipAndAccessKey() {
1121 if ( empty( $this->mParams['tooltip'] ) ) {
1122 return [];
1123 }
1124
1125 return Linker::tooltipAndAccesskeyAttribs( $this->mParams['tooltip'] );
1126 }
1127
1133 public function getTooltipAndAccessKeyOOUI() {
1134 if ( empty( $this->mParams['tooltip'] ) ) {
1135 return [];
1136 }
1137
1138 return [
1139 'title' => Linker::titleAttrib( $this->mParams['tooltip'] ),
1140 'accessKey' => Linker::accesskey( $this->mParams['tooltip'] ),
1141 ];
1142 }
1143
1151 public function getAttributes( array $list ) {
1152 static $boolAttribs = [ 'disabled', 'required', 'autofocus', 'multiple', 'readonly' ];
1153
1154 $ret = [];
1155 foreach ( $list as $key ) {
1156 if ( in_array( $key, $boolAttribs ) ) {
1157 if ( !empty( $this->mParams[$key] ) ) {
1158 $ret[$key] = '';
1159 }
1160 } elseif ( isset( $this->mParams[$key] ) ) {
1161 $ret[$key] = $this->mParams[$key];
1162 }
1163 }
1164
1165 return $ret;
1166 }
1167
1177 private function lookupOptionsKeys( $options, $needsParse ) {
1178 $ret = [];
1179 foreach ( $options as $key => $value ) {
1180 $msg = $this->msg( $key );
1181 $key = $needsParse ? $msg->parse() : $msg->plain();
1182 $ret[$key] = is_array( $value )
1183 ? $this->lookupOptionsKeys( $value, $needsParse )
1184 : strval( $value );
1185 }
1186 return $ret;
1187 }
1188
1196 public static function forceToStringRecursive( $array ) {
1197 if ( is_array( $array ) ) {
1198 return array_map( [ __CLASS__, 'forceToStringRecursive' ], $array );
1199 } else {
1200 return strval( $array );
1201 }
1202 }
1203
1210 public function getOptions() {
1211 if ( $this->mOptions === false ) {
1212 if ( array_key_exists( 'options-messages', $this->mParams ) ) {
1213 $needsParse = $this->mParams['options-messages-parse'] ?? false;
1214 if ( $needsParse ) {
1215 $this->mOptionsLabelsNotFromMessage = true;
1216 }
1217 $this->mOptions = $this->lookupOptionsKeys( $this->mParams['options-messages'], $needsParse );
1218 } elseif ( array_key_exists( 'options', $this->mParams ) ) {
1219 $this->mOptionsLabelsNotFromMessage = true;
1220 $this->mOptions = self::forceToStringRecursive( $this->mParams['options'] );
1221 } elseif ( array_key_exists( 'options-message', $this->mParams ) ) {
1222 $message = $this->getMessage( $this->mParams['options-message'] )->inContentLanguage()->plain();
1223 $this->mOptions = Xml::listDropDownOptions( $message );
1224 } else {
1225 $this->mOptions = null;
1226 }
1227 }
1228
1229 return $this->mOptions;
1230 }
1231
1237 public function getOptionsOOUI() {
1238 $oldoptions = $this->getOptions();
1239
1240 if ( $oldoptions === null ) {
1241 return null;
1242 }
1243
1244 return Xml::listDropDownOptionsOoui( $oldoptions );
1245 }
1246
1254 public static function flattenOptions( $options ) {
1255 $flatOpts = [];
1256
1257 foreach ( $options as $value ) {
1258 if ( is_array( $value ) ) {
1259 $flatOpts = array_merge( $flatOpts, self::flattenOptions( $value ) );
1260 } else {
1261 $flatOpts[] = $value;
1262 }
1263 }
1264
1265 return $flatOpts;
1266 }
1267
1281 protected static function formatErrors( $errors ) {
1282 if ( is_array( $errors ) && count( $errors ) === 1 ) {
1283 $errors = array_shift( $errors );
1284 }
1285
1286 if ( is_array( $errors ) ) {
1287 $lines = [];
1288 foreach ( $errors as $error ) {
1289 if ( $error instanceof Message ) {
1290 $lines[] = Html::rawElement( 'li', [], $error->parse() );
1291 } else {
1292 $lines[] = Html::rawElement( 'li', [], $error );
1293 }
1294 }
1295
1296 $errors = Html::rawElement( 'ul', [], implode( "\n", $lines ) );
1297 } else {
1298 if ( $errors instanceof Message ) {
1299 $errors = $errors->parse();
1300 }
1301 }
1302
1303 return Html::errorBox( $errors );
1304 }
1305
1312 protected function getMessage( $value ) {
1313 $message = Message::newFromSpecifier( $value );
1314
1315 if ( $this->mParent ) {
1316 $message->setContext( $this->mParent );
1317 }
1318
1319 return $message;
1320 }
1321
1329 public function skipLoadData( $request ) {
1330 return !empty( $this->mParams['nodata'] );
1331 }
1332
1341 if ( $this->mCondState ) {
1342 // This is probably more restrictive than it needs to be, but better safe than sorry
1343 return true;
1344 }
1345 return false;
1346 }
1347}
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
A checkbox field.
The parent class to generate form fields.
getName()
Get the field name that will be used for submission.
getHelpTextHtmlDiv( $helptext)
Generate help text HTML in div format.
needsJSForHtml5FormValidation()
Whether this field requires the user agent to have JavaScript enabled for the client-side HTML5 form ...
parseCondStateForClient()
Parse the cond-state array for client-side.
HTMLForm null $mParent
getLabelAlignOOUI()
Get label alignment when generating field for OOUI.
getInputOOUI( $value)
Same as getInputHTML, but returns an OOUI object.
getHelpTextHtmlRaw( $helptext)
Generate help text HTML formatted for raw output.
isHelpInline()
Determine if the help text should be displayed inline.
isDisabled( $alldata)
Test whether this field is supposed to be disabled, based on the values of the other form fields.
getFieldLayoutOOUI( $inputField, $config)
Get a FieldLayout (or subclass thereof) to wrap this field in when using OOUI output.
validateCondState( $params)
Validate the cond-state params, the existence check of fields should be done later.
isHidden( $alldata)
Test whether this field is supposed to be hidden, based on the values of the other form fields.
filter( $value, $alldata)
bool $mShowEmptyLabels
If true will generate an empty div element with no label.
getNearestField( $name, $backCompat=false)
Get the closest field matching a given name.
shouldInfuseOOUI()
Whether the field should be automatically infused.
__construct( $params)
Initialise the object.
getMessage( $value)
Turns a *-message parameter (which could be a MessageSpecifier, or a message name,...
getClassName()
Gets the non namespaced class name.
getOOUIModules()
Get the list of extra ResourceLoader modules which must be loaded client-side before it's possible to...
getNearestFieldByName( $alldata, $name, $asDisplay=false)
Fetch a field value from $alldata for the closest field matching a given name.
skipLoadData( $request)
Skip this field when collecting data.
array $mCondState
Array to hold params for 'hide-if' or 'disable-if' statements.
getVForm( $value)
Get the complete field for the input, including help text, labels, and whatever.
getInputHTML( $value)
This function must be implemented to return the HTML to generate the input object itself.
getErrorsAndErrorClass( $value)
Determine form errors to display and their classes.
validate( $value, $alldata)
Override this function to add specific validation checks on the field input.
getOOUI( $value)
Get the OOUI version of the div.
canDisplayErrors()
True if this field type is able to display errors; false if validation errors need to be displayed in...
checkStateRecurse(array $alldata, array $params)
Helper function for isHidden and isDisabled to handle recursive data structures.
getHelpTextHtmlTable( $helptext)
Generate help text HTML in table format.
array array[] $mParams
static flattenOptions( $options)
flatten an array of options to a single array, for instance, a set of "<options>" inside "<optgroups>...
getRaw( $value)
Get the complete raw fields for the input, including help text, labels, and whatever.
getTooltipAndAccessKeyOOUI()
Returns the attributes required for the tooltip and accesskey, for OOUI widgets' config.
getErrorsRaw( $value)
Determine form errors to display, returning them in an array.
getHelpText()
Determine the help text to display.
getOptions()
Fetch the array of options from the field's parameters.
parseCondState( $params)
Parse the cond-state array to use the field name for submission, since the key in the form descriptor...
getTableRow( $value)
Get the complete table row for the input, including help text, labels, and whatever.
getInline( $value)
Get the complete field as an inline element.
isSubmitAttempt(WebRequest $request)
Can we assume that the request is an attempt to submit a HTMLForm, as opposed to an attempt to just v...
msg( $key,... $params)
Get a translated interface message.
array null false $mOptions
getLabelHtml( $cellAttributes=[])
loadDataFromRequest( $request)
Get the value that this input has been set to from a posted form, or the input's default value if it ...
getAttributes(array $list)
Returns the given attributes from the parameters.
getNearestFieldValue( $alldata, $name, $asDisplay=false, $backCompat=false)
Fetch a field value from $alldata for the closest field matching a given name.
setShowEmptyLabel( $show)
Tell the field whether to generate a separate label element if its label is blank.
needsLabel()
Should this field have a label, or is there no input element with the appropriate id for the label to...
getDiv( $value)
Get the complete div for the input, including help text, labels, and whatever.
cancelSubmit( $value, $alldata)
Override this function if the control can somehow trigger a form submission that shouldn't actually s...
getOptionsOOUI()
Get options and make them into arrays suitable for OOUI.
hasVisibleOutput()
If this field has a user-visible output or not.
getTooltipAndAccessKey()
Returns the attributes required for the tooltip and accesskey, for Html::element() etc.
static formatErrors( $errors)
Formats one or more errors as accepted by field validation-callback.
static forceToStringRecursive( $array)
Recursively forces values in an array to strings, because issues arise with integer 0 as a value.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:153
MediaWiki exception.
This class is a collection of static functions that serve two purposes:
Definition Html.php:55
Some internal bits split of from Skin.php.
Definition Linker.php:67
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:144
static newFromSpecifier( $value)
Transform a MessageSpecifier or a primitive value used interchangeably with specifiers (a message key...
Definition Message.php:426
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
getCheck( $name)
Return true if the named value is set in the input, whatever that value is (even "0").
wasPosted()
Returns true if the present request was reached by a POST operation, false otherwise (GET,...
return true
Definition router.php:92
if(!file_exists( $CREDITS)) $lines