MediaWiki  1.29.2
HTMLFormField.php
Go to the documentation of this file.
1 <?php
2 
7 abstract class HTMLFormField {
8  public $mParams;
9 
11  protected $mFilterCallback;
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;
21  protected $mOptionsLabelsNotFromMessage = false;
22  protected $mHideIf = null;
23 
28  protected $mShowEmptyLabels = true;
29 
33  public $mParent;
34 
45  abstract public function getInputHTML( $value );
46 
54  public function getInputOOUI( $value ) {
55  return false;
56  }
57 
63  public function canDisplayErrors() {
64  return $this->hasVisibleOutput();
65  }
66 
77  public 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  public function isHidden( $alldata ) {
270  if ( !$this->mHideIf ) {
271  return false;
272  }
273 
274  return $this->isHiddenRecurse( $alldata, $this->mHideIf );
275  }
276 
287  public function cancelSubmit( $value, $alldata ) {
288  return false;
289  }
290 
302  public 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' );
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  public 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 
373  public function loadDataFromRequest( $request ) {
374  if ( $request->getCheck( $this->mName ) ) {
375  return $request->getText( $this->mName );
376  } else {
377  return $this->getDefault();
378  }
379  }
380 
389  public 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  public function getTableRow( $value ) {
476  list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
477  $inputHtml = $this->getInputHTML( $value );
478  $fieldType = static::class;
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 = static::class;
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 = static::class;
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  public 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  public function getLabel() {
928  return is_null( $this->mLabel ) ? '' : $this->mLabel;
929  }
930 
931  public 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  public 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  public 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  // Note: If you change the logic in this method, change
1149  // htmlform.Checker.js to match.
1150 
1151  if ( is_array( $errors ) && count( $errors ) === 1 ) {
1152  $errors = array_shift( $errors );
1153  }
1154 
1155  if ( is_array( $errors ) ) {
1156  $lines = [];
1157  foreach ( $errors as $error ) {
1158  if ( $error instanceof Message ) {
1159  $lines[] = Html::rawElement( 'li', [], $error->parse() );
1160  } else {
1161  $lines[] = Html::rawElement( 'li', [], $error );
1162  }
1163  }
1164 
1165  return Html::rawElement( 'ul', [ 'class' => 'error' ], implode( "\n", $lines ) );
1166  } else {
1167  if ( $errors instanceof Message ) {
1168  $errors = $errors->parse();
1169  }
1170 
1171  return Html::rawElement( 'span', [ 'class' => 'error' ], $errors );
1172  }
1173  }
1174 
1181  protected function getMessage( $value ) {
1182  $message = Message::newFromSpecifier( $value );
1183 
1184  if ( $this->mParent ) {
1185  $message->setContext( $this->mParent );
1186  }
1187 
1188  return $message;
1189  }
1190 
1197  public function skipLoadData( $request ) {
1198  return !empty( $this->mParams['nodata'] );
1199  }
1200 
1208  public function needsJSForHtml5FormValidation() {
1209  if ( $this->mHideIf ) {
1210  // This is probably more restrictive than it needs to be, but better safe than sorry
1211  return true;
1212  }
1213  return false;
1214  }
1215 }
HTMLFormField\getOptions
getOptions()
Fetch the array of options from the field's parameters.
Definition: HTMLFormField.php:1053
HTMLFormField\__construct
__construct( $params)
Initialise the object.
Definition: HTMLFormField.php:389
$request
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2612
HTMLFormField\$mHelpClass
$mHelpClass
Definition: HTMLFormField.php:18
HTMLFormField\$mLabel
$mLabel
Definition: HTMLFormField.php:14
HTMLFormField\isHiddenRecurse
isHiddenRecurse(array $alldata, array $params)
Helper function for isHidden to handle recursive data structures.
Definition: HTMLFormField.php:162
HTMLFormField\setShowEmptyLabel
setShowEmptyLabel( $show)
Tell the field whether to generate a separate label element if its label is blank.
Definition: HTMLFormField.php:348
$opt
$opt
Definition: postprocess-phan.php:115
captcha-old.count
count
Definition: captcha-old.py:225
HTMLFormField\getErrorsAndErrorClass
getErrorsAndErrorClass( $value)
Determine form errors to display and their classes.
Definition: HTMLFormField.php:860
HTMLFormField\validate
validate( $value, $alldata)
Override this function to add specific validation checks on the field input.
Definition: HTMLFormField.php:302
HTMLFormActionFieldLayout
Definition: HTMLFormElement.php:52
$params
$params
Definition: styleTest.css.php:40
HTMLFormField\formatErrors
static formatErrors( $errors)
Formats one or more errors as accepted by field validation-callback.
Definition: HTMLFormField.php:1147
HTMLFormField\hasVisibleOutput
hasVisibleOutput()
If this field has a user-visible output or not.
Definition: HTMLFormField.php:95
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:304
HTMLFormField\getMessage
getMessage( $value)
Turns a *-message parameter (which could be a MessageSpecifier, or a message name,...
Definition: HTMLFormField.php:1181
HTMLFormField\getNearestFieldByName
getNearestFieldByName( $alldata, $name)
Fetch a field value from $alldata for the closest field matching a given name.
Definition: HTMLFormField.php:112
HTMLFormField\getNotices
getNotices()
Determine notices to display for the field.
Definition: HTMLFormField.php:906
HTMLFormField\$mClass
$mClass
Definition: HTMLFormField.php:16
HTMLFormField\shouldInfuseOOUI
shouldInfuseOOUI()
Whether the field should be automatically infused.
Definition: HTMLFormField.php:680
php
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:35
HTMLFormField\needsLabel
needsLabel()
Should this field have a label, or is there no input element with the appropriate id for the label to...
Definition: HTMLFormField.php:335
HTMLFormField\$mDir
$mDir
Definition: HTMLFormField.php:13
HTMLFormField\skipLoadData
skipLoadData( $request)
Skip this field when collecting data.
Definition: HTMLFormField.php:1197
HTMLFormField\getTableRow
getTableRow( $value)
Get the complete table row for the input, including help text, labels, and whatever.
Definition: HTMLFormField.php:475
HTMLFormField\cancelSubmit
cancelSubmit( $value, $alldata)
Override this function if the control can somehow trigger a form submission that shouldn't actually s...
Definition: HTMLFormField.php:287
HTMLFormField\getLabelHtml
getLabelHtml( $cellAttributes=[])
Definition: HTMLFormField.php:931
HTMLFormField\canDisplayErrors
canDisplayErrors()
True if this field type is able to display errors; false if validation errors need to be displayed in...
Definition: HTMLFormField.php:63
HTMLFormField\getHelpTextHtmlTable
getHelpTextHtmlTable( $helptext)
Generate help text HTML in table format.
Definition: HTMLFormField.php:761
HTMLFormField\$mParent
HTMLForm null $mParent
Definition: HTMLFormField.php:33
FormatJson\encode
static encode( $value, $pretty=false, $escaping=0)
Returns the JSON representation of a value.
Definition: FormatJson.php:127
HTMLFormField\getHelpText
getHelpText()
Determine the help text to display.
Definition: HTMLFormField.php:826
$html
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:1956
MWException
MediaWiki exception.
Definition: MWException.php:26
HTMLFormField\$mName
$mName
Definition: HTMLFormField.php:12
HTMLFormField\getLabelAlignOOUI
getLabelAlignOOUI()
Get label alignment when generating field for OOUI.
Definition: HTMLFormField.php:657
HTMLFormField
The parent class to generate form fields.
Definition: HTMLFormField.php:7
HTMLFormField\lookupOptionsKeys
lookupOptionsKeys( $options)
Given an array of msg-key => value mappings, returns an array with keys being the message texts.
Definition: HTMLFormField.php:1021
$lines
$lines
Definition: router.php:67
HTMLFormField\$mDefault
$mDefault
Definition: HTMLFormField.php:19
HTMLFormField\needsJSForHtml5FormValidation
needsJSForHtml5FormValidation()
Whether this field requires the user agent to have JavaScript enabled for the client-side HTML5 form ...
Definition: HTMLFormField.php:1208
string
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:177
list
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
HTMLFormField\filter
filter( $value, $alldata)
Definition: HTMLFormField.php:321
HTMLFormField\$mID
$mID
Definition: HTMLFormField.php:15
HTMLFormField\getOOUI
getOOUI( $value)
Get the OOUI version of the div.
Definition: HTMLFormField.php:582
HTMLFormField\isHidden
isHidden( $alldata)
Test whether this field is supposed to be hidden, based on the values of the other form fields.
Definition: HTMLFormField.php:269
$value
$value
Definition: styleTest.css.php:45
on
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:86
Linker\tooltipAndAccesskeyAttribs
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[])
Returns the attributes for the tooltip and access key.
Definition: Linker.php:2098
HTMLFormField\$mValidationCallback
$mValidationCallback
Definition: HTMLFormField.php:10
HTMLFormField\getDefault
getDefault()
Definition: HTMLFormField.php:970
HTMLFormFieldLayout
Definition: HTMLFormElement.php:37
HTMLFormField\$mFilterCallback
$mFilterCallback
Definition: HTMLFormField.php:11
HTMLFormField\getFieldLayoutOOUI
getFieldLayoutOOUI( $inputField, $config)
Get a FieldLayout (or subclass thereof) to wrap this field in when using OOUI output.
Definition: HTMLFormField.php:665
HTMLFormField\msg
msg()
Get a translated interface message.
Definition: HTMLFormField.php:77
HTMLFormField\getLabel
getLabel()
Definition: HTMLFormField.php:927
$ret
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:1956
HTMLFormField\$mOptions
$mOptions
Definition: HTMLFormField.php:20
HTMLFormField\$mParams
$mParams
Definition: HTMLFormField.php:8
HTMLFormField\getHelpTextHtmlRaw
getHelpTextHtmlRaw( $helptext)
Generate help text HTML formatted for raw output.
Definition: HTMLFormField.php:817
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:38
HTMLFormField\getOOUIModules
getOOUIModules()
Get the list of extra ResourceLoader modules which must be loaded client-side before it's possible to...
Definition: HTMLFormField.php:691
HTMLFormField\getInputOOUI
getInputOOUI( $value)
Same as getInputHTML, but returns an OOUI object.
Definition: HTMLFormField.php:54
HTMLFormField\$mShowEmptyLabels
bool $mShowEmptyLabels
If true will generate an empty div element with no label.
Definition: HTMLFormField.php:28
$args
if( $line===false) $args
Definition: cdb.php:63
HTMLFormField\getDiv
getDiv( $value)
Get the complete div for the input, including help text, labels, and whatever.
Definition: HTMLFormField.php:533
HTMLFormField\getVForm
getVForm( $value)
Get the complete field for the input, including help text, labels, and whatever.
Definition: HTMLFormField.php:727
HTMLFormField\getInputHTML
getInputHTML( $value)
This function must be implemented to return the HTML to generate the input object itself.
as
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
Definition: distributors.txt:9
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
HTMLFormField\$mOptionsLabelsNotFromMessage
$mOptionsLabelsNotFromMessage
Definition: HTMLFormField.php:21
HTMLFormField\getRaw
getRaw( $value)
Get the complete raw fields for the input, including help text, labels, and whatever.
Definition: HTMLFormField.php:704
$keys
$keys
Definition: testCompression.php:65
HTMLFormField\getErrorsRaw
getErrorsRaw( $value)
Determine form errors to display, returning them in an array.
Definition: HTMLFormField.php:881
HTMLFormField\forceToStringRecursive
static forceToStringRecursive( $array)
Recursively forces values in an array to strings, because issues arise with integer 0 as a value.
Definition: HTMLFormField.php:1039
HTMLFormField\isSubmitAttempt
isSubmitAttempt(WebRequest $request)
Can we assume that the request is an attempt to submit a HTMLForm, as opposed to an attempt to just v...
Definition: HTMLFormField.php:362
$help
$help
Definition: mcc.php:32
HTMLFormField\$mHideIf
$mHideIf
Definition: HTMLFormField.php:22
HTMLFormField\getTooltipAndAccessKey
getTooltipAndAccessKey()
Returns the attributes required for the tooltip and accesskey.
Definition: HTMLFormField.php:983
HTMLFormField\flattenOptions
static flattenOptions( $options)
flatten an array of options to a single array, for instance, a set of "<options>" inside "<optgroups>...
Definition: HTMLFormField.php:1126
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
HTMLFormField\$mVFormClass
$mVFormClass
Definition: HTMLFormField.php:17
HTMLFormField\getHelpTextHtmlDiv
getHelpTextHtmlDiv( $helptext)
Generate help text HTML in div format.
Definition: HTMLFormField.php:790
HTMLFormField\getInline
getInline( $value)
Get the complete field as an inline element.
Definition: HTMLFormField.php:739
$options
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup 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:1049
HTMLFormField\getOptionsOOUI
getOptionsOOUI()
Get options and make them into arrays suitable for OOUI.
Definition: HTMLFormField.php:1100
array
the array() calling protocol came about after MediaWiki 1.4rc1.
HTMLFormField\getAttributes
getAttributes(array $list)
Returns the given attributes from the parameters.
Definition: HTMLFormField.php:997
HTMLForm
Object handling generic submission, CSRF protection, layout and other logic for UI forms.
Definition: HTMLForm.php:128
HTMLFormField\loadDataFromRequest
loadDataFromRequest( $request)
Get the value that this input has been set to from a posted form, or the input's default value if it ...
Definition: HTMLFormField.php:373