MediaWiki REL1_28
HTMLFormField.php
Go to the documentation of this file.
1<?php
2
7abstract class HTMLFormField {
8 public $mParams;
9
12 protected $mName;
13 protected $mDir;
14 protected $mLabel; # String label, as HTML. Set on construction.
15 protected $mID;
16 protected $mClass = '';
17 protected $mVFormClass = '';
18 protected $mHelpClass = false;
19 protected $mDefault;
20 protected $mOptions = false;
22 protected $mHideIf = null;
23
28 protected $mShowEmptyLabels = true;
29
33 public $mParent;
34
45 abstract function getInputHTML( $value );
46
54 function getInputOOUI( $value ) {
55 return false;
56 }
57
63 public function canDisplayErrors() {
64 return $this->hasVisibleOutput();
65 }
66
77 function msg() {
78 $args = func_get_args();
79
80 if ( $this->mParent ) {
81 $callback = [ $this->mParent, 'msg' ];
82 } else {
83 $callback = 'wfMessage';
84 }
85
86 return call_user_func_array( $callback, $args );
87 }
88
95 public function hasVisibleOutput() {
96 return true;
97 }
98
112 protected function getNearestFieldByName( $alldata, $name ) {
113 $tmp = $this->mName;
114 $thisKeys = [];
115 while ( preg_match( '/^(.+)\[([^\]]+)\]$/', $tmp, $m ) ) {
116 array_unshift( $thisKeys, $m[2] );
117 $tmp = $m[1];
118 }
119 if ( substr( $tmp, 0, 2 ) == 'wp' &&
120 !array_key_exists( $tmp, $alldata ) &&
121 array_key_exists( substr( $tmp, 2 ), $alldata )
122 ) {
123 // Adjust for name mangling.
124 $tmp = substr( $tmp, 2 );
125 }
126 array_unshift( $thisKeys, $tmp );
127
128 $tmp = $name;
129 $nameKeys = [];
130 while ( preg_match( '/^(.+)\[([^\]]+)\]$/', $tmp, $m ) ) {
131 array_unshift( $nameKeys, $m[2] );
132 $tmp = $m[1];
133 }
134 array_unshift( $nameKeys, $tmp );
135
136 $testValue = '';
137 for ( $i = count( $thisKeys ) - 1; $i >= 0; $i-- ) {
138 $keys = array_merge( array_slice( $thisKeys, 0, $i ), $nameKeys );
139 $data = $alldata;
140 while ( $keys ) {
141 $key = array_shift( $keys );
142 if ( !is_array( $data ) || !array_key_exists( $key, $data ) ) {
143 continue 2;
144 }
145 $data = $data[$key];
146 }
147 $testValue = (string)$data;
148 break;
149 }
150
151 return $testValue;
152 }
153
162 protected function isHiddenRecurse( array $alldata, array $params ) {
163 $origParams = $params;
164 $op = array_shift( $params );
165
166 try {
167 switch ( $op ) {
168 case 'AND':
169 foreach ( $params as $i => $p ) {
170 if ( !is_array( $p ) ) {
171 throw new MWException(
172 "Expected array, found " . gettype( $p ) . " at index $i"
173 );
174 }
175 if ( !$this->isHiddenRecurse( $alldata, $p ) ) {
176 return false;
177 }
178 }
179 return true;
180
181 case 'OR':
182 foreach ( $params as $i => $p ) {
183 if ( !is_array( $p ) ) {
184 throw new MWException(
185 "Expected array, found " . gettype( $p ) . " at index $i"
186 );
187 }
188 if ( $this->isHiddenRecurse( $alldata, $p ) ) {
189 return true;
190 }
191 }
192 return false;
193
194 case 'NAND':
195 foreach ( $params as $i => $p ) {
196 if ( !is_array( $p ) ) {
197 throw new MWException(
198 "Expected array, found " . gettype( $p ) . " at index $i"
199 );
200 }
201 if ( !$this->isHiddenRecurse( $alldata, $p ) ) {
202 return true;
203 }
204 }
205 return false;
206
207 case 'NOR':
208 foreach ( $params as $i => $p ) {
209 if ( !is_array( $p ) ) {
210 throw new MWException(
211 "Expected array, found " . gettype( $p ) . " at index $i"
212 );
213 }
214 if ( $this->isHiddenRecurse( $alldata, $p ) ) {
215 return false;
216 }
217 }
218 return true;
219
220 case 'NOT':
221 if ( count( $params ) !== 1 ) {
222 throw new MWException( "NOT takes exactly one parameter" );
223 }
224 $p = $params[0];
225 if ( !is_array( $p ) ) {
226 throw new MWException(
227 "Expected array, found " . gettype( $p ) . " at index 0"
228 );
229 }
230 return !$this->isHiddenRecurse( $alldata, $p );
231
232 case '===':
233 case '!==':
234 if ( count( $params ) !== 2 ) {
235 throw new MWException( "$op takes exactly two parameters" );
236 }
237 list( $field, $value ) = $params;
238 if ( !is_string( $field ) || !is_string( $value ) ) {
239 throw new MWException( "Parameters for $op must be strings" );
240 }
241 $testValue = $this->getNearestFieldByName( $alldata, $field );
242 switch ( $op ) {
243 case '===':
244 return ( $value === $testValue );
245 case '!==':
246 return ( $value !== $testValue );
247 }
248
249 default:
250 throw new MWException( "Unknown operation" );
251 }
252 } catch ( Exception $ex ) {
253 throw new MWException(
254 "Invalid hide-if specification for $this->mName: " .
255 $ex->getMessage() . " in " . var_export( $origParams, true ),
256 0, $ex
257 );
258 }
259 }
260
269 function isHidden( $alldata ) {
270 if ( !$this->mHideIf ) {
271 return false;
272 }
273
274 return $this->isHiddenRecurse( $alldata, $this->mHideIf );
275 }
276
287 function cancelSubmit( $value, $alldata ) {
288 return false;
289 }
290
302 function validate( $value, $alldata ) {
303 if ( $this->isHidden( $alldata ) ) {
304 return true;
305 }
306
307 if ( isset( $this->mParams['required'] )
308 && $this->mParams['required'] !== false
309 && $value === ''
310 ) {
311 return $this->msg( 'htmlform-required' )->parse();
312 }
313
314 if ( isset( $this->mValidationCallback ) ) {
315 return call_user_func( $this->mValidationCallback, $value, $alldata, $this->mParent );
316 }
317
318 return true;
319 }
320
321 function filter( $value, $alldata ) {
322 if ( isset( $this->mFilterCallback ) ) {
323 $value = call_user_func( $this->mFilterCallback, $value, $alldata, $this->mParent );
324 }
325
326 return $value;
327 }
328
335 protected function needsLabel() {
336 return true;
337 }
338
348 public function setShowEmptyLabel( $show ) {
349 $this->mShowEmptyLabels = $show;
350 }
351
362 protected function isSubmitAttempt( WebRequest $request ) {
363 return $request->getCheck( 'wpEditToken' ) || $request->getCheck( 'wpFormIdentifier' );
364 }
365
374 if ( $request->getCheck( $this->mName ) ) {
375 return $request->getText( $this->mName );
376 } else {
377 return $this->getDefault();
378 }
379 }
380
389 function __construct( $params ) {
390 $this->mParams = $params;
391
392 if ( isset( $params['parent'] ) && $params['parent'] instanceof HTMLForm ) {
393 $this->mParent = $params['parent'];
394 }
395
396 # Generate the label from a message, if possible
397 if ( isset( $params['label-message'] ) ) {
398 $this->mLabel = $this->getMessage( $params['label-message'] )->parse();
399 } elseif ( isset( $params['label'] ) ) {
400 if ( $params['label'] === '&#160;' ) {
401 // Apparently some things set &nbsp directly and in an odd format
402 $this->mLabel = '&#160;';
403 } else {
404 $this->mLabel = htmlspecialchars( $params['label'] );
405 }
406 } elseif ( isset( $params['label-raw'] ) ) {
407 $this->mLabel = $params['label-raw'];
408 }
409
410 $this->mName = "wp{$params['fieldname']}";
411 if ( isset( $params['name'] ) ) {
412 $this->mName = $params['name'];
413 }
414
415 if ( isset( $params['dir'] ) ) {
416 $this->mDir = $params['dir'];
417 }
418
419 $validName = Sanitizer::escapeId( $this->mName );
420 $validName = str_replace( [ '.5B', '.5D' ], [ '[', ']' ], $validName );
421 if ( $this->mName != $validName && !isset( $params['nodata'] ) ) {
422 throw new MWException( "Invalid name '{$this->mName}' passed to " . __METHOD__ );
423 }
424
425 $this->mID = "mw-input-{$this->mName}";
426
427 if ( isset( $params['default'] ) ) {
428 $this->mDefault = $params['default'];
429 }
430
431 if ( isset( $params['id'] ) ) {
432 $id = $params['id'];
433 $validId = Sanitizer::escapeId( $id );
434
435 if ( $id != $validId ) {
436 throw new MWException( "Invalid id '$id' passed to " . __METHOD__ );
437 }
438
439 $this->mID = $id;
440 }
441
442 if ( isset( $params['cssclass'] ) ) {
443 $this->mClass = $params['cssclass'];
444 }
445
446 if ( isset( $params['csshelpclass'] ) ) {
447 $this->mHelpClass = $params['csshelpclass'];
448 }
449
450 if ( isset( $params['validation-callback'] ) ) {
451 $this->mValidationCallback = $params['validation-callback'];
452 }
453
454 if ( isset( $params['filter-callback'] ) ) {
455 $this->mFilterCallback = $params['filter-callback'];
456 }
457
458 if ( isset( $params['hidelabel'] ) ) {
459 $this->mShowEmptyLabels = false;
460 }
461
462 if ( isset( $params['hide-if'] ) ) {
463 $this->mHideIf = $params['hide-if'];
464 }
465 }
466
475 function getTableRow( $value ) {
476 list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
477 $inputHtml = $this->getInputHTML( $value );
478 $fieldType = get_class( $this );
479 $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() );
480 $cellAttributes = [];
481 $rowAttributes = [];
482 $rowClasses = '';
483
484 if ( !empty( $this->mParams['vertical-label'] ) ) {
485 $cellAttributes['colspan'] = 2;
486 $verticalLabel = true;
487 } else {
488 $verticalLabel = false;
489 }
490
491 $label = $this->getLabelHtml( $cellAttributes );
492
493 $field = Html::rawElement(
494 'td',
495 [ 'class' => 'mw-input' ] + $cellAttributes,
496 $inputHtml . "\n$errors"
497 );
498
499 if ( $this->mHideIf ) {
500 $rowAttributes['data-hide-if'] = FormatJson::encode( $this->mHideIf );
501 $rowClasses .= ' mw-htmlform-hide-if';
502 }
503
504 if ( $verticalLabel ) {
505 $html = Html::rawElement( 'tr',
506 $rowAttributes + [ 'class' => "mw-htmlform-vertical-label $rowClasses" ], $label );
507 $html .= Html::rawElement( 'tr',
508 $rowAttributes + [
509 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $rowClasses"
510 ],
511 $field );
512 } else {
513 $html =
514 Html::rawElement( 'tr',
515 $rowAttributes + [
516 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $rowClasses"
517 ],
518 $label . $field );
519 }
520
521 return $html . $helptext;
522 }
523
533 public function getDiv( $value ) {
534 list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
535 $inputHtml = $this->getInputHTML( $value );
536 $fieldType = get_class( $this );
537 $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() );
538 $cellAttributes = [];
539 $label = $this->getLabelHtml( $cellAttributes );
540
541 $outerDivClass = [
542 'mw-input',
543 'mw-htmlform-nolabel' => ( $label === '' )
544 ];
545
546 $horizontalLabel = isset( $this->mParams['horizontal-label'] )
547 ? $this->mParams['horizontal-label'] : false;
548
549 if ( $horizontalLabel ) {
550 $field = '&#160;' . $inputHtml . "\n$errors";
551 } else {
552 $field = Html::rawElement(
553 'div',
554 [ 'class' => $outerDivClass ] + $cellAttributes,
555 $inputHtml . "\n$errors"
556 );
557 }
558 $divCssClasses = [ "mw-htmlform-field-$fieldType",
559 $this->mClass, $this->mVFormClass, $errorClass ];
560
561 $wrapperAttributes = [
562 'class' => $divCssClasses,
563 ];
564 if ( $this->mHideIf ) {
565 $wrapperAttributes['data-hide-if'] = FormatJson::encode( $this->mHideIf );
566 $wrapperAttributes['class'][] = ' mw-htmlform-hide-if';
567 }
568 $html = Html::rawElement( 'div', $wrapperAttributes, $label . $field );
569 $html .= $helptext;
570
571 return $html;
572 }
573
582 public function getOOUI( $value ) {
583 $inputField = $this->getInputOOUI( $value );
584
585 if ( !$inputField ) {
586 // This field doesn't have an OOUI implementation yet at all. Fall back to getDiv() to
587 // generate the whole field, label and errors and all, then wrap it in a Widget.
588 // It might look weird, but it'll work OK.
589 return $this->getFieldLayoutOOUI(
590 new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $this->getDiv( $value ) ) ] ),
591 [ 'infusable' => false, 'align' => 'top' ]
592 );
593 }
594
595 $infusable = true;
596 if ( is_string( $inputField ) ) {
597 // We have an OOUI implementation, but it's not proper, and we got a load of HTML.
598 // Cheat a little and wrap it in a widget. It won't be infusable, though, since client-side
599 // JavaScript doesn't know how to rebuilt the contents.
600 $inputField = new OOUI\Widget( [ 'content' => new OOUI\HtmlSnippet( $inputField ) ] );
601 $infusable = false;
602 }
603
604 $fieldType = get_class( $this );
605 $help = $this->getHelpText();
606 $errors = $this->getErrorsRaw( $value );
607 foreach ( $errors as &$error ) {
608 $error = new OOUI\HtmlSnippet( $error );
609 }
610
611 $notices = $this->getNotices();
612 foreach ( $notices as &$notice ) {
613 $notice = new OOUI\HtmlSnippet( $notice );
614 }
615
616 $config = [
617 'classes' => [ "mw-htmlform-field-$fieldType", $this->mClass ],
618 'align' => $this->getLabelAlignOOUI(),
619 'help' => ( $help !== null && $help !== '' ) ? new OOUI\HtmlSnippet( $help ) : null,
620 'errors' => $errors,
621 'notices' => $notices,
622 'infusable' => $infusable,
623 ];
624
625 $preloadModules = false;
626
627 if ( $infusable && $this->shouldInfuseOOUI() ) {
628 $preloadModules = true;
629 $config['classes'][] = 'mw-htmlform-field-autoinfuse';
630 }
631
632 // the element could specify, that the label doesn't need to be added
633 $label = $this->getLabel();
634 if ( $label ) {
635 $config['label'] = new OOUI\HtmlSnippet( $label );
636 }
637
638 if ( $this->mHideIf ) {
639 $preloadModules = true;
640 $config['hideIf'] = $this->mHideIf;
641 }
642
643 $config['modules'] = $this->getOOUIModules();
644
645 if ( $preloadModules ) {
646 $this->mParent->getOutput()->addModules( 'mediawiki.htmlform.ooui' );
647 $this->mParent->getOutput()->addModules( $this->getOOUIModules() );
648 }
649
650 return $this->getFieldLayoutOOUI( $inputField, $config );
651 }
652
657 protected function getLabelAlignOOUI() {
658 return 'top';
659 }
660
665 protected function getFieldLayoutOOUI( $inputField, $config ) {
666 if ( isset( $this->mClassWithButton ) ) {
667 $buttonWidget = $this->mClassWithButton->getInputOOUI( '' );
668 return new HTMLFormActionFieldLayout( $inputField, $buttonWidget, $config );
669 }
670 return new HTMLFormFieldLayout( $inputField, $config );
671 }
672
680 protected function shouldInfuseOOUI() {
681 // Always infuse fields with help text, since the interface for it is nicer with JS
682 return $this->getHelpText() !== null;
683 }
684
691 protected function getOOUIModules() {
692 return [];
693 }
694
704 public function getRaw( $value ) {
705 list( $errors, ) = $this->getErrorsAndErrorClass( $value );
706 $inputHtml = $this->getInputHTML( $value );
707 $helptext = $this->getHelpTextHtmlRaw( $this->getHelpText() );
708 $cellAttributes = [];
709 $label = $this->getLabelHtml( $cellAttributes );
710
711 $html = "\n$errors";
712 $html .= $label;
713 $html .= $inputHtml;
714 $html .= $helptext;
715
716 return $html;
717 }
718
727 public function getVForm( $value ) {
728 // Ewwww
729 $this->mVFormClass = ' mw-ui-vform-field';
730 return $this->getDiv( $value );
731 }
732
739 public function getInline( $value ) {
740 list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
741 $inputHtml = $this->getInputHTML( $value );
742 $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() );
743 $cellAttributes = [];
744 $label = $this->getLabelHtml( $cellAttributes );
745
746 $html = "\n" . $errors .
747 $label . '&#160;' .
748 $inputHtml .
749 $helptext;
750
751 return $html;
752 }
753
761 public function getHelpTextHtmlTable( $helptext ) {
762 if ( is_null( $helptext ) ) {
763 return '';
764 }
765
766 $rowAttributes = [];
767 if ( $this->mHideIf ) {
768 $rowAttributes['data-hide-if'] = FormatJson::encode( $this->mHideIf );
769 $rowAttributes['class'] = 'mw-htmlform-hide-if';
770 }
771
772 $tdClasses = [ 'htmlform-tip' ];
773 if ( $this->mHelpClass !== false ) {
774 $tdClasses[] = $this->mHelpClass;
775 }
776 $row = Html::rawElement( 'td', [ 'colspan' => 2, 'class' => $tdClasses ], $helptext );
777 $row = Html::rawElement( 'tr', $rowAttributes, $row );
778
779 return $row;
780 }
781
790 public function getHelpTextHtmlDiv( $helptext ) {
791 if ( is_null( $helptext ) ) {
792 return '';
793 }
794
795 $wrapperAttributes = [
796 'class' => 'htmlform-tip',
797 ];
798 if ( $this->mHelpClass !== false ) {
799 $wrapperAttributes['class'] .= " {$this->mHelpClass}";
800 }
801 if ( $this->mHideIf ) {
802 $wrapperAttributes['data-hide-if'] = FormatJson::encode( $this->mHideIf );
803 $wrapperAttributes['class'] .= ' mw-htmlform-hide-if';
804 }
805 $div = Html::rawElement( 'div', $wrapperAttributes, $helptext );
806
807 return $div;
808 }
809
817 public function getHelpTextHtmlRaw( $helptext ) {
818 return $this->getHelpTextHtmlDiv( $helptext );
819 }
820
826 public function getHelpText() {
827 $helptext = null;
828
829 if ( isset( $this->mParams['help-message'] ) ) {
830 $this->mParams['help-messages'] = [ $this->mParams['help-message'] ];
831 }
832
833 if ( isset( $this->mParams['help-messages'] ) ) {
834 foreach ( $this->mParams['help-messages'] as $msg ) {
835 $msg = $this->getMessage( $msg );
836
837 if ( $msg->exists() ) {
838 if ( is_null( $helptext ) ) {
839 $helptext = '';
840 } else {
841 $helptext .= $this->msg( 'word-separator' )->escaped(); // some space
842 }
843 $helptext .= $msg->parse(); // Append message
844 }
845 }
846 } elseif ( isset( $this->mParams['help'] ) ) {
847 $helptext = $this->mParams['help'];
848 }
849
850 return $helptext;
851 }
852
860 public function getErrorsAndErrorClass( $value ) {
861 $errors = $this->validate( $value, $this->mParent->mFieldData );
862
863 if ( is_bool( $errors ) || !$this->mParent->wasSubmitted() ) {
864 $errors = '';
865 $errorClass = '';
866 } else {
867 $errors = self::formatErrors( $errors );
868 $errorClass = 'mw-htmlform-invalid-input';
869 }
870
871 return [ $errors, $errorClass ];
872 }
873
881 public function getErrorsRaw( $value ) {
882 $errors = $this->validate( $value, $this->mParent->mFieldData );
883
884 if ( is_bool( $errors ) || !$this->mParent->wasSubmitted() ) {
885 $errors = [];
886 }
887
888 if ( !is_array( $errors ) ) {
889 $errors = [ $errors ];
890 }
891 foreach ( $errors as &$error ) {
892 if ( $error instanceof Message ) {
893 $error = $error->parse();
894 }
895 }
896
897 return $errors;
898 }
899
906 function getNotices() {
907 $notices = [];
908
909 if ( isset( $this->mParams['notice-message'] ) ) {
910 $notices[] = $this->getMessage( $this->mParams['notice-message'] )->parse();
911 }
912
913 if ( isset( $this->mParams['notice-messages'] ) ) {
914 foreach ( $this->mParams['notice-messages'] as $msg ) {
915 $notices[] = $this->getMessage( $msg )->parse();
916 }
917 } elseif ( isset( $this->mParams['notice'] ) ) {
918 $notices[] = $this->mParams['notice'];
919 }
920
921 return $notices;
922 }
923
927 function getLabel() {
928 return is_null( $this->mLabel ) ? '' : $this->mLabel;
929 }
930
931 function getLabelHtml( $cellAttributes = [] ) {
932 # Don't output a for= attribute for labels with no associated input.
933 # Kind of hacky here, possibly we don't want these to be <label>s at all.
934 $for = [];
935
936 if ( $this->needsLabel() ) {
937 $for['for'] = $this->mID;
938 }
939
940 $labelValue = trim( $this->getLabel() );
941 $hasLabel = false;
942 if ( $labelValue !== '&#160;' && $labelValue !== '' ) {
943 $hasLabel = true;
944 }
945
946 $displayFormat = $this->mParent->getDisplayFormat();
947 $html = '';
948 $horizontalLabel = isset( $this->mParams['horizontal-label'] )
949 ? $this->mParams['horizontal-label'] : false;
950
951 if ( $displayFormat === 'table' ) {
952 $html =
953 Html::rawElement( 'td',
954 [ 'class' => 'mw-label' ] + $cellAttributes,
955 Html::rawElement( 'label', $for, $labelValue ) );
956 } elseif ( $hasLabel || $this->mShowEmptyLabels ) {
957 if ( $displayFormat === 'div' && !$horizontalLabel ) {
958 $html =
959 Html::rawElement( 'div',
960 [ 'class' => 'mw-label' ] + $cellAttributes,
961 Html::rawElement( 'label', $for, $labelValue ) );
962 } else {
963 $html = Html::rawElement( 'label', $for, $labelValue );
964 }
965 }
966
967 return $html;
968 }
969
970 function getDefault() {
971 if ( isset( $this->mDefault ) ) {
972 return $this->mDefault;
973 } else {
974 return null;
975 }
976 }
977
983 public function getTooltipAndAccessKey() {
984 if ( empty( $this->mParams['tooltip'] ) ) {
985 return [];
986 }
987
988 return Linker::tooltipAndAccesskeyAttribs( $this->mParams['tooltip'] );
989 }
990
997 public function getAttributes( array $list ) {
998 static $boolAttribs = [ 'disabled', 'required', 'autofocus', 'multiple', 'readonly' ];
999
1000 $ret = [];
1001 foreach ( $list as $key ) {
1002 if ( in_array( $key, $boolAttribs ) ) {
1003 if ( !empty( $this->mParams[$key] ) ) {
1004 $ret[$key] = '';
1005 }
1006 } elseif ( isset( $this->mParams[$key] ) ) {
1007 $ret[$key] = $this->mParams[$key];
1008 }
1009 }
1010
1011 return $ret;
1012 }
1013
1021 private function lookupOptionsKeys( $options ) {
1022 $ret = [];
1023 foreach ( $options as $key => $value ) {
1024 $key = $this->msg( $key )->plain();
1025 $ret[$key] = is_array( $value )
1026 ? $this->lookupOptionsKeys( $value )
1027 : strval( $value );
1028 }
1029 return $ret;
1030 }
1031
1039 static function forceToStringRecursive( $array ) {
1040 if ( is_array( $array ) ) {
1041 return array_map( [ __CLASS__, 'forceToStringRecursive' ], $array );
1042 } else {
1043 return strval( $array );
1044 }
1045 }
1046
1053 public function getOptions() {
1054 if ( $this->mOptions === false ) {
1055 if ( array_key_exists( 'options-messages', $this->mParams ) ) {
1056 $this->mOptions = $this->lookupOptionsKeys( $this->mParams['options-messages'] );
1057 } elseif ( array_key_exists( 'options', $this->mParams ) ) {
1058 $this->mOptionsLabelsNotFromMessage = true;
1059 $this->mOptions = self::forceToStringRecursive( $this->mParams['options'] );
1060 } elseif ( array_key_exists( 'options-message', $this->mParams ) ) {
1062 $message = $this->getMessage( $this->mParams['options-message'] )->inContentLanguage()->plain();
1063
1064 $optgroup = false;
1065 $this->mOptions = [];
1066 foreach ( explode( "\n", $message ) as $option ) {
1067 $value = trim( $option );
1068 if ( $value == '' ) {
1069 continue;
1070 } elseif ( substr( $value, 0, 1 ) == '*' && substr( $value, 1, 1 ) != '*' ) {
1071 # A new group is starting...
1072 $value = trim( substr( $value, 1 ) );
1073 $optgroup = $value;
1074 } elseif ( substr( $value, 0, 2 ) == '**' ) {
1075 # groupmember
1076 $opt = trim( substr( $value, 2 ) );
1077 if ( $optgroup === false ) {
1078 $this->mOptions[$opt] = $opt;
1079 } else {
1080 $this->mOptions[$optgroup][$opt] = $opt;
1081 }
1082 } else {
1083 # groupless reason list
1084 $optgroup = false;
1085 $this->mOptions[$option] = $option;
1086 }
1087 }
1088 } else {
1089 $this->mOptions = null;
1090 }
1091 }
1092
1093 return $this->mOptions;
1094 }
1095
1100 public function getOptionsOOUI() {
1101 $oldoptions = $this->getOptions();
1102
1103 if ( $oldoptions === null ) {
1104 return null;
1105 }
1106
1107 $options = [];
1108
1109 foreach ( $oldoptions as $text => $data ) {
1110 $options[] = [
1111 'data' => (string)$data,
1112 'label' => (string)$text,
1113 ];
1114 }
1115
1116 return $options;
1117 }
1118
1126 public static function flattenOptions( $options ) {
1127 $flatOpts = [];
1128
1129 foreach ( $options as $value ) {
1130 if ( is_array( $value ) ) {
1131 $flatOpts = array_merge( $flatOpts, self::flattenOptions( $value ) );
1132 } else {
1133 $flatOpts[] = $value;
1134 }
1135 }
1136
1137 return $flatOpts;
1138 }
1139
1147 protected static function formatErrors( $errors ) {
1148 if ( is_array( $errors ) && count( $errors ) === 1 ) {
1149 $errors = array_shift( $errors );
1150 }
1151
1152 if ( is_array( $errors ) ) {
1153 $lines = [];
1154 foreach ( $errors as $error ) {
1155 if ( $error instanceof Message ) {
1156 $lines[] = Html::rawElement( 'li', [], $error->parse() );
1157 } else {
1158 $lines[] = Html::rawElement( 'li', [], $error );
1159 }
1160 }
1161
1162 return Html::rawElement( 'ul', [ 'class' => 'error' ], implode( "\n", $lines ) );
1163 } else {
1164 if ( $errors instanceof Message ) {
1165 $errors = $errors->parse();
1166 }
1167
1168 return Html::rawElement( 'span', [ 'class' => 'error' ], $errors );
1169 }
1170 }
1171
1178 protected function getMessage( $value ) {
1179 $message = Message::newFromSpecifier( $value );
1180
1181 if ( $this->mParent ) {
1182 $message->setContext( $this->mParent );
1183 }
1184
1185 return $message;
1186 }
1187
1194 public function skipLoadData( $request ) {
1195 return !empty( $this->mParams['nodata'] );
1196 }
1197}
if( $line===false) $args
Definition cdb.php:64
The parent class to generate form fields.
getHelpTextHtmlDiv( $helptext)
Generate help text HTML in div format.
HTMLForm null $mParent
getLabelAlignOOUI()
Get label alignment when generating field for OOUI.
getInputOOUI( $value)
Same as getInputHTML, but returns an OOUI object.
getNotices()
Determine notices to display for the field.
getHelpTextHtmlRaw( $helptext)
Generate help text HTML formatted for raw output.
getFieldLayoutOOUI( $inputField, $config)
Get a FieldLayout (or subclass thereof) to wrap this field in when using OOUI output.
isHidden( $alldata)
Test whether this field is supposed to be hidden, based on the values of the other form fields.
filter( $value, $alldata)
isHiddenRecurse(array $alldata, array $params)
Helper function for isHidden to handle recursive data structures.
bool $mShowEmptyLabels
If true will generate an empty div element with no label.
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,...
getOOUIModules()
Get the list of extra ResourceLoader modules which must be loaded client-side before it's possible to...
skipLoadData( $request)
Skip this field when collecting data.
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.
lookupOptionsKeys( $options)
Given an array of msg-key => value mappings, returns an array with keys being the message texts.
canDisplayErrors()
True if this field type is able to display errors; false if validation errors need to be displayed in...
getHelpTextHtmlTable( $helptext)
Generate help text HTML in table format.
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.
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.
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...
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.
getNearestFieldByName( $alldata, $name)
Fetch a field value from $alldata for the closest field matching a given name.
msg()
Get a translated interface message.
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.
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.
Definition HTMLForm.php:128
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[])
Returns the attributes for the tooltip and access key.
Definition Linker.php:2184
MediaWiki exception.
The Message class provides methods which fulfil two basic services:
Definition Message.php:159
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition deferred.txt:11
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
the array() calling protocol came about after MediaWiki 1.4rc1.
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition hooks.txt:183
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition hooks.txt:1096
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2685
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition hooks.txt:1949
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
Definition hooks.txt:1957
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going on
Definition hooks.txt:88
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:304
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
$help
Definition mcc.php:32
$lines
Definition router.php:67
$params