MediaWiki REL1_39
HTMLFormField.php
Go to the documentation of this file.
1<?php
2
9abstract class HTMLFormField {
11 public $mParams;
12
15 protected $mName;
16 protected $mDir;
17 protected $mLabel; # String label, as HTML. Set on construction.
18 protected $mID;
19 protected $mClass = '';
20 protected $mVFormClass = '';
21 protected $mHelpClass = false;
22 protected $mDefault;
26 protected $mOptions = false;
31 protected $mCondState = [];
32 protected $mCondStateClass = [];
33
38 protected $mShowEmptyLabels = true;
39
43 public $mParent;
44
55 abstract public function getInputHTML( $value );
56
65 public function getInputOOUI( $value ) {
66 return false;
67 }
68
75 public function canDisplayErrors() {
76 return $this->hasVisibleOutput();
77 }
78
91 public function msg( $key, ...$params ) {
92 if ( $this->mParent ) {
93 return $this->mParent->msg( $key, ...$params );
94 }
95 return wfMessage( $key, ...$params );
96 }
97
105 public function hasVisibleOutput() {
106 return true;
107 }
108
115 public function getName() {
116 return $this->mName;
117 }
118
130 protected function getNearestField( $name, $backCompat = false ) {
131 // When the field is belong to a HTMLFormFieldCloner
132 if ( isset( $this->mParams['cloner'] ) ) {
133 $field = $this->mParams['cloner']->findNearestField( $this, $name );
134 if ( $field ) {
135 return $field;
136 }
137 }
138
139 if ( $backCompat && substr( $name, 0, 2 ) === 'wp' &&
140 !$this->mParent->hasField( $name )
141 ) {
142 // Don't break the existed use cases.
143 return $this->mParent->getField( substr( $name, 2 ) );
144 }
145 return $this->mParent->getField( $name );
146 }
147
159 protected function getNearestFieldValue( $alldata, $name, $asDisplay = false, $backCompat = false ) {
160 $field = $this->getNearestField( $name, $backCompat );
161 // When the field belongs to a HTMLFormFieldCloner
162 if ( isset( $field->mParams['cloner'] ) ) {
163 $value = $field->mParams['cloner']->extractFieldData( $field, $alldata );
164 } else {
165 // Note $alldata is an empty array when first rendering a form with a formIdentifier.
166 // In that case, $alldata[$field->mParams['fieldname']] is unset and we use the
167 // field's default value
168 $value = $alldata[$field->mParams['fieldname']] ?? $field->getDefault();
169 }
170
171 // Check invert state for HTMLCheckField
172 if ( $asDisplay && $field instanceof HTMLCheckField && ( $field->mParams['invert'] ?? false ) ) {
173 $value = !$value;
174 }
175
176 return $value;
177 }
178
189 protected function getNearestFieldByName( $alldata, $name, $asDisplay = false ) {
190 return (string)$this->getNearestFieldValue( $alldata, $name, $asDisplay );
191 }
192
200 protected function validateCondState( $params ) {
201 $origParams = $params;
202 $op = array_shift( $params );
203
204 try {
205 switch ( $op ) {
206 case 'NOT':
207 if ( count( $params ) !== 1 ) {
208 throw new MWException( "NOT takes exactly one parameter" );
209 }
210 // Fall-through intentionally
211
212 case 'AND':
213 case 'OR':
214 case 'NAND':
215 case 'NOR':
216 foreach ( $params as $i => $p ) {
217 if ( !is_array( $p ) ) {
218 $type = gettype( $p );
219 throw new MWException( "Expected array, found $type at index $i" );
220 }
221 $this->validateCondState( $p );
222 }
223 break;
224
225 case '===':
226 case '!==':
227 if ( count( $params ) !== 2 ) {
228 throw new MWException( "$op takes exactly two parameters" );
229 }
230 list( $name, $value ) = $params;
231 if ( !is_string( $name ) || !is_string( $value ) ) {
232 throw new MWException( "Parameters for $op must be strings" );
233 }
234 break;
235
236 default:
237 throw new MWException( "Unknown operation" );
238 }
239 } catch ( MWException $ex ) {
240 throw new MWException(
241 "Invalid hide-if or disable-if specification for $this->mName: " .
242 $ex->getMessage() . " in " . var_export( $origParams, true ),
243 0, $ex
244 );
245 }
246 }
247
256 protected function checkStateRecurse( array $alldata, array $params ) {
257 $origParams = $params;
258 $op = array_shift( $params );
259 $valueChk = [ 'AND' => false, 'OR' => true, 'NAND' => false, 'NOR' => true ];
260 $valueRet = [ 'AND' => true, 'OR' => false, 'NAND' => false, 'NOR' => true ];
261
262 switch ( $op ) {
263 case 'AND':
264 case 'OR':
265 case 'NAND':
266 case 'NOR':
267 foreach ( $params as $i => $p ) {
268 if ( $valueChk[$op] === $this->checkStateRecurse( $alldata, $p ) ) {
269 return !$valueRet[$op];
270 }
271 }
272 return $valueRet[$op];
273
274 case 'NOT':
275 return !$this->checkStateRecurse( $alldata, $params[0] );
276
277 case '===':
278 case '!==':
279 list( $field, $value ) = $params;
280 $testValue = (string)$this->getNearestFieldValue( $alldata, $field, true, true );
281 switch ( $op ) {
282 case '===':
283 return ( $value === $testValue );
284 case '!==':
285 return ( $value !== $testValue );
286 }
287 }
288 }
289
298 protected function parseCondState( $params ) {
299 $origParams = $params;
300 $op = array_shift( $params );
301
302 switch ( $op ) {
303 case 'AND':
304 case 'OR':
305 case 'NAND':
306 case 'NOR':
307 $ret = [ $op ];
308 foreach ( $params as $i => $p ) {
309 $ret[] = $this->parseCondState( $p );
310 }
311 return $ret;
312
313 case 'NOT':
314 return [ 'NOT', $this->parseCondState( $params[0] ) ];
315
316 case '===':
317 case '!==':
318 list( $name, $value ) = $params;
319 $field = $this->getNearestField( $name, true );
320 return [ $op, $field->getName(), $value ];
321 }
322 }
323
329 protected function parseCondStateForClient() {
330 $parsed = [];
331 foreach ( $this->mCondState as $type => $params ) {
332 $parsed[$type] = $this->parseCondState( $params );
333 }
334 return $parsed;
335 }
336
345 public function isHidden( $alldata ) {
346 if ( !( $this->mCondState && isset( $this->mCondState['hide'] ) ) ) {
347 return false;
348 }
349
350 return $this->checkStateRecurse( $alldata, $this->mCondState['hide'] );
351 }
352
361 public function isDisabled( $alldata ) {
362 if ( $this->mParams['disabled'] ?? false ) {
363 return true;
364 }
365 $hidden = $this->isHidden( $alldata );
366 if ( !$this->mCondState || !isset( $this->mCondState['disable'] ) ) {
367 return $hidden;
368 }
369
370 return $hidden || $this->checkStateRecurse( $alldata, $this->mCondState['disable'] );
371 }
372
384 public function cancelSubmit( $value, $alldata ) {
385 return false;
386 }
387
400 public function validate( $value, $alldata ) {
401 if ( $this->isHidden( $alldata ) ) {
402 return true;
403 }
404
405 if ( isset( $this->mParams['required'] )
406 && $this->mParams['required'] !== false
407 && ( $value === '' || $value === false )
408 ) {
409 return $this->msg( 'htmlform-required' );
410 }
411
412 if ( isset( $this->mValidationCallback ) ) {
413 return ( $this->mValidationCallback )( $value, $alldata, $this->mParent );
414 }
415
416 return true;
417 }
418
427 public function filter( $value, $alldata ) {
428 if ( isset( $this->mFilterCallback ) ) {
429 $value = ( $this->mFilterCallback )( $value, $alldata, $this->mParent );
430 }
431
432 return $value;
433 }
434
442 protected function needsLabel() {
443 return true;
444 }
445
455 public function setShowEmptyLabel( $show ) {
456 $this->mShowEmptyLabels = $show;
457 }
458
470 protected function isSubmitAttempt( WebRequest $request ) {
471 // HTMLForm would add a hidden field of edit token for forms that require to be posted.
472 return $request->wasPosted() && $request->getCheck( 'wpEditToken' )
473 // The identifier matching or not has been checked in HTMLForm::prepareForm()
474 || $request->getCheck( 'wpFormIdentifier' );
475 }
476
485 public function loadDataFromRequest( $request ) {
486 if ( $request->getCheck( $this->mName ) ) {
487 return $request->getText( $this->mName );
488 } else {
489 return $this->getDefault();
490 }
491 }
492
502 public function __construct( $params ) {
503 $this->mParams = $params;
504
505 if ( isset( $params['parent'] ) && $params['parent'] instanceof HTMLForm ) {
506 $this->mParent = $params['parent'];
507 }
508
509 # Generate the label from a message, if possible
510 if ( isset( $params['label-message'] ) ) {
511 $this->mLabel = $this->getMessage( $params['label-message'] )->parse();
512 } elseif ( isset( $params['label'] ) ) {
513 if ( $params['label'] === '&#160;' || $params['label'] === "\u{00A0}" ) {
514 // Apparently some things set &nbsp directly and in an odd format
515 $this->mLabel = "\u{00A0}";
516 } else {
517 $this->mLabel = htmlspecialchars( $params['label'] );
518 }
519 } elseif ( isset( $params['label-raw'] ) ) {
520 $this->mLabel = $params['label-raw'];
521 }
522
523 $this->mName = "wp{$params['fieldname']}";
524 if ( isset( $params['name'] ) ) {
525 $this->mName = $params['name'];
526 }
527
528 if ( isset( $params['dir'] ) ) {
529 $this->mDir = $params['dir'];
530 }
531
532 $this->mID = "mw-input-{$this->mName}";
533
534 if ( isset( $params['default'] ) ) {
535 $this->mDefault = $params['default'];
536 }
537
538 if ( isset( $params['id'] ) ) {
539 $this->mID = $params['id'];
540 }
541
542 if ( isset( $params['cssclass'] ) ) {
543 $this->mClass = $params['cssclass'];
544 }
545
546 if ( isset( $params['csshelpclass'] ) ) {
547 $this->mHelpClass = $params['csshelpclass'];
548 }
549
550 if ( isset( $params['validation-callback'] ) ) {
551 $this->mValidationCallback = $params['validation-callback'];
552 }
553
554 if ( isset( $params['filter-callback'] ) ) {
555 $this->mFilterCallback = $params['filter-callback'];
556 }
557
558 if ( isset( $params['hidelabel'] ) ) {
559 $this->mShowEmptyLabels = false;
560 }
561
562 if ( isset( $params['hide-if'] ) && $params['hide-if'] ) {
563 $this->validateCondState( $params['hide-if'] );
564 $this->mCondState['hide'] = $params['hide-if'];
565 $this->mCondStateClass[] = 'mw-htmlform-hide-if';
566 }
567 if ( !( isset( $params['disabled'] ) && $params['disabled'] ) &&
568 isset( $params['disable-if'] ) && $params['disable-if']
569 ) {
570 $this->validateCondState( $params['disable-if'] );
571 $this->mCondState['disable'] = $params['disable-if'];
572 $this->mCondStateClass[] = 'mw-htmlform-disable-if';
573 }
574 }
575
585 public function getTableRow( $value ) {
586 list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
587 $inputHtml = $this->getInputHTML( $value );
588 $fieldType = $this->getClassName();
589 $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() );
590 $cellAttributes = [];
591 $rowAttributes = [];
592 $rowClasses = '';
593
594 if ( !empty( $this->mParams['vertical-label'] ) ) {
595 $cellAttributes['colspan'] = 2;
596 $verticalLabel = true;
597 } else {
598 $verticalLabel = false;
599 }
600
601 $label = $this->getLabelHtml( $cellAttributes );
602
603 $field = Html::rawElement(
604 'td',
605 [ 'class' => 'mw-input' ] + $cellAttributes,
606 $inputHtml . "\n$errors"
607 );
608
609 if ( $this->mCondState ) {
610 $rowAttributes['data-cond-state'] = FormatJson::encode( $this->parseCondStateForClient() );
611 $rowClasses .= implode( ' ', $this->mCondStateClass );
612 }
613
614 if ( $verticalLabel ) {
615 $html = Html::rawElement( 'tr',
616 $rowAttributes + [ 'class' => "mw-htmlform-vertical-label $rowClasses" ], $label );
617 $html .= Html::rawElement( 'tr',
618 $rowAttributes + [
619 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $rowClasses"
620 ],
621 $field );
622 } else {
623 $html = Html::rawElement( 'tr',
624 $rowAttributes + [
625 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $rowClasses"
626 ],
627 $label . $field );
628 }
629
630 return $html . $helptext;
631 }
632
643 public function getDiv( $value ) {
644 list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
645 $inputHtml = $this->getInputHTML( $value );
646 $fieldType = $this->getClassName();
647 $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() );
648 $cellAttributes = [];
649 $label = $this->getLabelHtml( $cellAttributes );
650
651 $outerDivClass = [
652 'mw-input',
653 'mw-htmlform-nolabel' => ( $label === '' )
654 ];
655
656 $horizontalLabel = $this->mParams['horizontal-label'] ?? false;
657
658 if ( $horizontalLabel ) {
659 $field = "\u{00A0}" . $inputHtml . "\n$errors";
660 } else {
661 $field = Html::rawElement(
662 'div',
663 // @phan-suppress-next-line PhanUselessBinaryAddRight
664 [ 'class' => $outerDivClass ] + $cellAttributes,
665 $inputHtml . "\n$errors"
666 );
667 }
668 $divCssClasses = [ "mw-htmlform-field-$fieldType",
669 $this->mClass, $this->mVFormClass, $errorClass ];
670
671 $wrapperAttributes = [
672 'class' => $divCssClasses,
673 ];
674 if ( $this->mCondState ) {
675 $wrapperAttributes['data-cond-state'] = FormatJson::encode( $this->parseCondStateForClient() );
676 $wrapperAttributes['class'] = array_merge( $wrapperAttributes['class'], $this->mCondStateClass );
677 }
678 $html = Html::rawElement( 'div', $wrapperAttributes, $label . $field );
679 $html .= $helptext;
680
681 return $html;
682 }
683
693 public function getOOUI( $value ) {
694 $inputField = $this->getInputOOUI( $value );
695
696 if ( !$inputField ) {
697 // This field doesn't have an OOUI implementation yet at all. Fall back to getDiv() to
698 // generate the whole field, label and errors and all, then wrap it in a Widget.
699 // It might look weird, but it'll work OK.
700 return $this->getFieldLayoutOOUI(
701 new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $this->getDiv( $value ) ) ] ),
702 [ 'align' => 'top' ]
703 );
704 }
705
706 $infusable = true;
707 if ( is_string( $inputField ) ) {
708 // We have an OOUI implementation, but it's not proper, and we got a load of HTML.
709 // Cheat a little and wrap it in a widget. It won't be infusable, though, since client-side
710 // JavaScript doesn't know how to rebuilt the contents.
711 $inputField = new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $inputField ) ] );
712 $infusable = false;
713 }
714
715 $fieldType = $this->getClassName();
716 $help = $this->getHelpText();
717 $errors = $this->getErrorsRaw( $value );
718 foreach ( $errors as &$error ) {
719 $error = new OOUI\HtmlSnippet( $error );
720 }
721
722 $config = [
723 'classes' => [ "mw-htmlform-field-$fieldType" ],
724 'align' => $this->getLabelAlignOOUI(),
725 'help' => ( $help !== null && $help !== '' ) ? new OOUI\HtmlSnippet( $help ) : null,
726 'errors' => $errors,
727 'infusable' => $infusable,
728 'helpInline' => $this->isHelpInline(),
729 ];
730 if ( $this->mClass !== '' ) {
731 $config['classes'][] = $this->mClass;
732 }
733
734 $preloadModules = false;
735
736 if ( $infusable && $this->shouldInfuseOOUI() ) {
737 $preloadModules = true;
738 $config['classes'][] = 'mw-htmlform-autoinfuse';
739 }
740 if ( $this->mCondState ) {
741 $config['classes'] = array_merge( $config['classes'], $this->mCondStateClass );
742 }
743
744 // the element could specify, that the label doesn't need to be added
745 $label = $this->getLabel();
746 if ( $label && $label !== "\u{00A0}" && $label !== '&#160;' ) {
747 $config['label'] = new OOUI\HtmlSnippet( $label );
748 }
749
750 if ( $this->mCondState ) {
751 $preloadModules = true;
752 $config['condState'] = $this->parseCondStateForClient();
753 }
754
755 $config['modules'] = $this->getOOUIModules();
756
757 if ( $preloadModules ) {
758 $this->mParent->getOutput()->addModules( 'mediawiki.htmlform.ooui' );
759 $this->mParent->getOutput()->addModules( $this->getOOUIModules() );
760 }
761
762 return $this->getFieldLayoutOOUI( $inputField, $config );
763 }
764
772 protected function getClassName() {
773 $name = explode( '\\', static::class );
774 return end( $name );
775 }
776
782 protected function getLabelAlignOOUI() {
783 return 'top';
784 }
785
792 protected function getFieldLayoutOOUI( $inputField, $config ) {
793 return new HTMLFormFieldLayout( $inputField, $config );
794 }
795
804 protected function shouldInfuseOOUI() {
805 // Always infuse fields with popup help text, since the interface for it is nicer with JS
806 return $this->getHelpText() !== null && !$this->isHelpInline();
807 }
808
816 protected function getOOUIModules() {
817 return [];
818 }
819
830 public function getRaw( $value ) {
831 list( $errors, ) = $this->getErrorsAndErrorClass( $value );
832 $inputHtml = $this->getInputHTML( $value );
833 $helptext = $this->getHelpTextHtmlRaw( $this->getHelpText() );
834 $cellAttributes = [];
835 $label = $this->getLabelHtml( $cellAttributes );
836
837 $html = "\n$errors";
838 $html .= $label;
839 $html .= $inputHtml;
840 $html .= $helptext;
841
842 return $html;
843 }
844
854 public function getVForm( $value ) {
855 // Ewwww
856 $this->mVFormClass = ' mw-ui-vform-field';
857 return $this->getDiv( $value );
858 }
859
867 public function getInline( $value ) {
868 list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
869 $inputHtml = $this->getInputHTML( $value );
870 $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() );
871 $cellAttributes = [];
872 $label = $this->getLabelHtml( $cellAttributes );
873
874 $html = "\n" . $errors .
875 $label . "\u{00A0}" .
876 $inputHtml .
877 $helptext;
878
879 return $html;
880 }
881
889 public function getHelpTextHtmlTable( $helptext ) {
890 if ( $helptext === null ) {
891 return '';
892 }
893
894 $rowAttributes = [];
895 if ( $this->mCondState ) {
896 $rowAttributes['data-cond-state'] = FormatJson::encode( $this->parseCondStateForClient() );
897 $rowAttributes['class'] = $this->mCondStateClass;
898 }
899
900 $tdClasses = [ 'htmlform-tip' ];
901 if ( $this->mHelpClass !== false ) {
902 $tdClasses[] = $this->mHelpClass;
903 }
904 $row = Html::rawElement( 'td', [ 'colspan' => 2, 'class' => $tdClasses ], $helptext );
905 $row = Html::rawElement( 'tr', $rowAttributes, $row );
906
907 return $row;
908 }
909
918 public function getHelpTextHtmlDiv( $helptext ) {
919 if ( $helptext === null ) {
920 return '';
921 }
922
923 $wrapperAttributes = [
924 'class' => [ 'htmlform-tip' ],
925 ];
926 if ( $this->mHelpClass !== false ) {
927 $wrapperAttributes['class'][] = $this->mHelpClass;
928 }
929 if ( $this->mCondState ) {
930 $wrapperAttributes['data-cond-state'] = FormatJson::encode( $this->parseCondStateForClient() );
931 $wrapperAttributes['class'] = array_merge( $wrapperAttributes['class'], $this->mCondStateClass );
932 }
933 $div = Html::rawElement( 'div', $wrapperAttributes, $helptext );
934
935 return $div;
936 }
937
945 public function getHelpTextHtmlRaw( $helptext ) {
946 return $this->getHelpTextHtmlDiv( $helptext );
947 }
948
955 public function getHelpText() {
956 $helptext = null;
957
958 if ( isset( $this->mParams['help-message'] ) ) {
959 $this->mParams['help-messages'] = [ $this->mParams['help-message'] ];
960 }
961
962 if ( isset( $this->mParams['help-messages'] ) ) {
963 foreach ( $this->mParams['help-messages'] as $msg ) {
964 $msg = $this->getMessage( $msg );
965
966 if ( $msg->exists() ) {
967 if ( $helptext === null ) {
968 $helptext = '';
969 } else {
970 $helptext .= $this->msg( 'word-separator' )->escaped(); // some space
971 }
972 $helptext .= $msg->parse(); // Append message
973 }
974 }
975 } elseif ( isset( $this->mParams['help'] ) ) {
976 $helptext = $this->mParams['help'];
977 }
978
979 return $helptext;
980 }
981
990 public function isHelpInline() {
991 return $this->mParams['help-inline'] ?? true;
992 }
993
1006 public function getErrorsAndErrorClass( $value ) {
1007 $errors = $this->validate( $value, $this->mParent->mFieldData );
1008
1009 if ( is_bool( $errors ) || !$this->mParent->wasSubmitted() ) {
1010 $errors = '';
1011 $errorClass = '';
1012 } else {
1013 $errors = self::formatErrors( $errors );
1014 $errorClass = 'mw-htmlform-invalid-input';
1015 }
1016
1017 return [ $errors, $errorClass ];
1018 }
1019
1027 public function getErrorsRaw( $value ) {
1028 $errors = $this->validate( $value, $this->mParent->mFieldData );
1029
1030 if ( is_bool( $errors ) || !$this->mParent->wasSubmitted() ) {
1031 $errors = [];
1032 }
1033
1034 if ( !is_array( $errors ) ) {
1035 $errors = [ $errors ];
1036 }
1037 foreach ( $errors as &$error ) {
1038 if ( $error instanceof Message ) {
1039 $error = $error->parse();
1040 }
1041 }
1042
1043 return $errors;
1044 }
1045
1050 public function getLabel() {
1051 return $this->mLabel ?? '';
1052 }
1053
1060 public function getLabelHtml( $cellAttributes = [] ) {
1061 # Don't output a for= attribute for labels with no associated input.
1062 # Kind of hacky here, possibly we don't want these to be <label>s at all.
1063 $for = [];
1064
1065 if ( $this->needsLabel() ) {
1066 $for['for'] = $this->mID;
1067 }
1068
1069 $labelValue = trim( $this->getLabel() );
1070 $hasLabel = false;
1071 if ( $labelValue !== "\u{00A0}" && $labelValue !== '&#160;' && $labelValue !== '' ) {
1072 $hasLabel = true;
1073 }
1074
1075 $displayFormat = $this->mParent->getDisplayFormat();
1076 $html = '';
1077 $horizontalLabel = $this->mParams['horizontal-label'] ?? false;
1078
1079 if ( $displayFormat === 'table' ) {
1080 $html =
1081 Html::rawElement( 'td',
1082 [ 'class' => 'mw-label' ] + $cellAttributes,
1083 Html::rawElement( 'label', $for, $labelValue ) );
1084 } elseif ( $hasLabel || $this->mShowEmptyLabels ) {
1085 if ( $displayFormat === 'div' && !$horizontalLabel ) {
1086 $html =
1087 Html::rawElement( 'div',
1088 [ 'class' => 'mw-label' ] + $cellAttributes,
1089 Html::rawElement( 'label', $for, $labelValue ) );
1090 } else {
1091 $html = Html::rawElement( 'label', $for, $labelValue );
1092 }
1093 }
1094
1095 return $html;
1096 }
1097
1102 public function getDefault() {
1103 return $this->mDefault ?? null;
1104 }
1105
1111 public function getTooltipAndAccessKey() {
1112 if ( empty( $this->mParams['tooltip'] ) ) {
1113 return [];
1114 }
1115
1116 return Linker::tooltipAndAccesskeyAttribs( $this->mParams['tooltip'] );
1117 }
1118
1124 public function getTooltipAndAccessKeyOOUI() {
1125 if ( empty( $this->mParams['tooltip'] ) ) {
1126 return [];
1127 }
1128
1129 return [
1130 'title' => Linker::titleAttrib( $this->mParams['tooltip'] ),
1131 'accessKey' => Linker::accesskey( $this->mParams['tooltip'] ),
1132 ];
1133 }
1134
1142 public function getAttributes( array $list ) {
1143 static $boolAttribs = [ 'disabled', 'required', 'autofocus', 'multiple', 'readonly' ];
1144
1145 $ret = [];
1146 foreach ( $list as $key ) {
1147 if ( in_array( $key, $boolAttribs ) ) {
1148 if ( !empty( $this->mParams[$key] ) ) {
1149 $ret[$key] = '';
1150 }
1151 } elseif ( isset( $this->mParams[$key] ) ) {
1152 $ret[$key] = $this->mParams[$key];
1153 }
1154 }
1155
1156 return $ret;
1157 }
1158
1168 private function lookupOptionsKeys( $options, $needsParse ) {
1169 $ret = [];
1170 foreach ( $options as $key => $value ) {
1171 $msg = $this->msg( $key );
1172 $key = $needsParse ? $msg->parse() : $msg->plain();
1173 $ret[$key] = is_array( $value )
1174 ? $this->lookupOptionsKeys( $value, $needsParse )
1175 : strval( $value );
1176 }
1177 return $ret;
1178 }
1179
1187 public static function forceToStringRecursive( $array ) {
1188 if ( is_array( $array ) ) {
1189 return array_map( [ __CLASS__, 'forceToStringRecursive' ], $array );
1190 } else {
1191 return strval( $array );
1192 }
1193 }
1194
1201 public function getOptions() {
1202 if ( $this->mOptions === false ) {
1203 if ( array_key_exists( 'options-messages', $this->mParams ) ) {
1204 $needsParse = $this->mParams['options-messages-parse'] ?? false;
1205 if ( $needsParse ) {
1206 $this->mOptionsLabelsNotFromMessage = true;
1207 }
1208 $this->mOptions = $this->lookupOptionsKeys( $this->mParams['options-messages'], $needsParse );
1209 } elseif ( array_key_exists( 'options', $this->mParams ) ) {
1210 $this->mOptionsLabelsNotFromMessage = true;
1211 $this->mOptions = self::forceToStringRecursive( $this->mParams['options'] );
1212 } elseif ( array_key_exists( 'options-message', $this->mParams ) ) {
1213 $message = $this->getMessage( $this->mParams['options-message'] )->inContentLanguage()->plain();
1214 $this->mOptions = Xml::listDropDownOptions( $message );
1215 } else {
1216 $this->mOptions = null;
1217 }
1218 }
1219
1220 return $this->mOptions;
1221 }
1222
1228 public function getOptionsOOUI() {
1229 $oldoptions = $this->getOptions();
1230
1231 if ( $oldoptions === null ) {
1232 return null;
1233 }
1234
1235 return Xml::listDropDownOptionsOoui( $oldoptions );
1236 }
1237
1245 public static function flattenOptions( $options ) {
1246 $flatOpts = [];
1247
1248 foreach ( $options as $value ) {
1249 if ( is_array( $value ) ) {
1250 $flatOpts = array_merge( $flatOpts, self::flattenOptions( $value ) );
1251 } else {
1252 $flatOpts[] = $value;
1253 }
1254 }
1255
1256 return $flatOpts;
1257 }
1258
1272 protected static function formatErrors( $errors ) {
1273 if ( is_array( $errors ) && count( $errors ) === 1 ) {
1274 $errors = array_shift( $errors );
1275 }
1276
1277 if ( is_array( $errors ) ) {
1278 $lines = [];
1279 foreach ( $errors as $error ) {
1280 if ( $error instanceof Message ) {
1281 $lines[] = Html::rawElement( 'li', [], $error->parse() );
1282 } else {
1283 $lines[] = Html::rawElement( 'li', [], $error );
1284 }
1285 }
1286
1287 $errors = Html::rawElement( 'ul', [], implode( "\n", $lines ) );
1288 } else {
1289 if ( $errors instanceof Message ) {
1290 $errors = $errors->parse();
1291 }
1292 }
1293
1294 return Html::errorBox( $errors );
1295 }
1296
1303 protected function getMessage( $value ) {
1304 $message = Message::newFromSpecifier( $value );
1305
1306 if ( $this->mParent ) {
1307 $message->setContext( $this->mParent );
1308 }
1309
1310 return $message;
1311 }
1312
1320 public function skipLoadData( $request ) {
1321 return !empty( $this->mParams['nodata'] );
1322 }
1323
1332 if ( $this->mCondState ) {
1333 // This is probably more restrictive than it needs to be, but better safe than sorry
1334 return true;
1335 }
1336 return false;
1337 }
1338}
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...
array bool null $mOptions
msg( $key,... $params)
Get a translated interface message.
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:150
static accesskey( $name, $localizer=null)
Given the id of an interface element, constructs the appropriate accesskey attribute from the system ...
Definition Linker.php:2128
static titleAttrib( $name, $options=null, array $msgParams=[], $localizer=null)
Given the id of an interface element, constructs the appropriate title attribute from the system mess...
Definition Linker.php:2080
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null, $localizer=null, $user=null, $config=null, $relevantTitle=null)
Returns the attributes for the tooltip and access key.
Definition Linker.php:2299
MediaWiki exception.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:140
static newFromSpecifier( $value)
Transform a MessageSpecifier or a primitive value used interchangeably with specifiers (a message key...
Definition Message.php:422
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,...
$help
Definition mcc.php:32
return true
Definition router.php:92
if(!file_exists( $CREDITS)) $lines