MediaWiki  1.32.0
DefaultPreferencesFactory.php
Go to the documentation of this file.
1 <?php
22 
23 use Config;
24 use DateTime;
25 use DateTimeZone;
26 use Exception;
27 use Hooks;
28 use Html;
34 use LanguageConverter;
43 use OutputPage;
44 use Parser;
47 use Psr\Log\LoggerAwareTrait;
48 use Psr\Log\NullLogger;
49 use Skin;
51 use Status;
52 use Title;
53 use UnexpectedValueException;
54 use User;
56 use Xml;
57 
62  use LoggerAwareTrait;
63 
65  protected $config;
66 
68  protected $contLang;
69 
71  protected $authManager;
72 
74  protected $linkRenderer;
75 
82  public function __construct(
87  ) {
88  $this->config = $config;
89  $this->contLang = $contLang;
90  $this->authManager = $authManager;
91  $this->linkRenderer = $linkRenderer;
92  $this->logger = new NullLogger();
93  }
94 
98  public function getSaveBlacklist() {
99  return [
100  'realname',
101  'emailaddress',
102  ];
103  }
104 
112  $preferences = [];
113 
114  OutputPage::setupOOUI(
115  strtolower( $context->getSkin()->getSkinName() ),
116  $context->getLanguage()->getDir()
117  );
118 
119  $canIPUseHTTPS = wfCanIPUseHTTPS( $context->getRequest()->getIP() );
120  $this->profilePreferences( $user, $context, $preferences, $canIPUseHTTPS );
121  $this->skinPreferences( $user, $context, $preferences );
122  $this->datetimePreferences( $user, $context, $preferences );
123  $this->filesPreferences( $context, $preferences );
124  $this->renderingPreferences( $context, $preferences );
125  $this->editingPreferences( $user, $context, $preferences );
126  $this->rcPreferences( $user, $context, $preferences );
127  $this->watchlistPreferences( $user, $context, $preferences );
128  $this->searchPreferences( $preferences );
129 
130  Hooks::run( 'GetPreferences', [ $user, &$preferences ] );
131 
132  $this->loadPreferenceValues( $user, $context, $preferences );
133  $this->logger->debug( "Created form descriptor for user '{$user->getName()}'" );
134  return $preferences;
135  }
136 
145  private function loadPreferenceValues(
146  User $user, IContextSource $context, &$defaultPreferences
147  ) {
148  # # Remove preferences that wikis don't want to use
149  foreach ( $this->config->get( 'HiddenPrefs' ) as $pref ) {
150  if ( isset( $defaultPreferences[$pref] ) ) {
151  unset( $defaultPreferences[$pref] );
152  }
153  }
154 
155  # # Make sure that form fields have their parent set. See T43337.
156  $dummyForm = new HTMLForm( [], $context );
157 
158  $disable = !$user->isAllowed( 'editmyoptions' );
159 
160  $defaultOptions = User::getDefaultOptions();
161  $userOptions = $user->getOptions();
162  $this->applyFilters( $userOptions, $defaultPreferences, 'filterForForm' );
163  # # Prod in defaults from the user
164  foreach ( $defaultPreferences as $name => &$info ) {
165  $prefFromUser = $this->getOptionFromUser( $name, $info, $userOptions );
166  if ( $disable && !in_array( $name, $this->getSaveBlacklist() ) ) {
167  $info['disabled'] = 'disabled';
168  }
169  $field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm ); // For validation
170  $globalDefault = $defaultOptions[$name] ?? null;
171 
172  // If it validates, set it as the default
173  if ( isset( $info['default'] ) ) {
174  // Already set, no problem
175  continue;
176  } elseif ( !is_null( $prefFromUser ) && // Make sure we're not just pulling nothing
177  $field->validate( $prefFromUser, $user->getOptions() ) === true ) {
178  $info['default'] = $prefFromUser;
179  } elseif ( $field->validate( $globalDefault, $user->getOptions() ) === true ) {
180  $info['default'] = $globalDefault;
181  } else {
182  throw new MWException( "Global default '$globalDefault' is invalid for field $name" );
183  }
184  }
185 
186  return $defaultPreferences;
187  }
188 
197  protected function getOptionFromUser( $name, $info, array $userOptions ) {
198  $val = $userOptions[$name] ?? null;
199 
200  // Handling for multiselect preferences
201  if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
202  ( isset( $info['class'] ) && $info['class'] == \HTMLMultiSelectField::class ) ) {
203  $options = HTMLFormField::flattenOptions( $info['options'] );
204  $prefix = $info['prefix'] ?? $name;
205  $val = [];
206 
207  foreach ( $options as $value ) {
208  if ( $userOptions["$prefix$value"] ?? false ) {
209  $val[] = $value;
210  }
211  }
212  }
213 
214  // Handling for checkmatrix preferences
215  if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
216  ( isset( $info['class'] ) && $info['class'] == \HTMLCheckMatrix::class ) ) {
217  $columns = HTMLFormField::flattenOptions( $info['columns'] );
218  $rows = HTMLFormField::flattenOptions( $info['rows'] );
219  $prefix = $info['prefix'] ?? $name;
220  $val = [];
221 
222  foreach ( $columns as $column ) {
223  foreach ( $rows as $row ) {
224  if ( $userOptions["$prefix$column-$row"] ?? false ) {
225  $val[] = "$column-$row";
226  }
227  }
228  }
229  }
230 
231  return $val;
232  }
233 
243  protected function profilePreferences(
244  User $user, IContextSource $context, &$defaultPreferences, $canIPUseHTTPS
245  ) {
246  // retrieving user name for GENDER and misc.
247  $userName = $user->getName();
248 
249  # # User info #####################################
250  // Information panel
251  $defaultPreferences['username'] = [
252  'type' => 'info',
253  'label-message' => [ 'username', $userName ],
254  'default' => $userName,
255  'section' => 'personal/info',
256  ];
257 
259 
260  # Get groups to which the user belongs
261  $userEffectiveGroups = $user->getEffectiveGroups();
262  $userGroupMemberships = $user->getGroupMemberships();
263  $userGroups = $userMembers = $userTempGroups = $userTempMembers = [];
264  foreach ( $userEffectiveGroups as $ueg ) {
265  if ( $ueg == '*' ) {
266  // Skip the default * group, seems useless here
267  continue;
268  }
269 
270  if ( isset( $userGroupMemberships[$ueg] ) ) {
271  $groupStringOrObject = $userGroupMemberships[$ueg];
272  } else {
273  $groupStringOrObject = $ueg;
274  }
275 
276  $userG = UserGroupMembership::getLink( $groupStringOrObject, $context, 'html' );
277  $userM = UserGroupMembership::getLink( $groupStringOrObject, $context, 'html',
278  $userName );
279 
280  // Store expiring groups separately, so we can place them before non-expiring
281  // groups in the list. This is to avoid the ambiguity of something like
282  // "administrator, bureaucrat (until X date)" -- users might wonder whether the
283  // expiry date applies to both groups, or just the last one
284  if ( $groupStringOrObject instanceof UserGroupMembership &&
285  $groupStringOrObject->getExpiry()
286  ) {
287  $userTempGroups[] = $userG;
288  $userTempMembers[] = $userM;
289  } else {
290  $userGroups[] = $userG;
291  $userMembers[] = $userM;
292  }
293  }
294  sort( $userGroups );
295  sort( $userMembers );
296  sort( $userTempGroups );
297  sort( $userTempMembers );
298  $userGroups = array_merge( $userTempGroups, $userGroups );
299  $userMembers = array_merge( $userTempMembers, $userMembers );
300 
301  $defaultPreferences['usergroups'] = [
302  'type' => 'info',
303  'label' => $context->msg( 'prefs-memberingroups' )->numParams(
304  count( $userGroups ) )->params( $userName )->parse(),
305  'default' => $context->msg( 'prefs-memberingroups-type' )
306  ->rawParams( $lang->commaList( $userGroups ), $lang->commaList( $userMembers ) )
307  ->escaped(),
308  'raw' => true,
309  'section' => 'personal/info',
310  ];
311 
312  $contribTitle = SpecialPage::getTitleFor( "Contributions", $userName );
313  $formattedEditCount = $lang->formatNum( $user->getEditCount() );
314  $editCount = $this->linkRenderer->makeLink( $contribTitle, $formattedEditCount );
315 
316  $defaultPreferences['editcount'] = [
317  'type' => 'info',
318  'raw' => true,
319  'label-message' => 'prefs-edits',
320  'default' => $editCount,
321  'section' => 'personal/info',
322  ];
323 
324  if ( $user->getRegistration() ) {
325  $displayUser = $context->getUser();
326  $userRegistration = $user->getRegistration();
327  $defaultPreferences['registrationdate'] = [
328  'type' => 'info',
329  'label-message' => 'prefs-registration',
330  'default' => $context->msg(
331  'prefs-registration-date-time',
332  $lang->userTimeAndDate( $userRegistration, $displayUser ),
333  $lang->userDate( $userRegistration, $displayUser ),
334  $lang->userTime( $userRegistration, $displayUser )
335  )->text(),
336  'section' => 'personal/info',
337  ];
338  }
339 
340  $canViewPrivateInfo = $user->isAllowed( 'viewmyprivateinfo' );
341  $canEditPrivateInfo = $user->isAllowed( 'editmyprivateinfo' );
342 
343  // Actually changeable stuff
344  $defaultPreferences['realname'] = [
345  // (not really "private", but still shouldn't be edited without permission)
346  'type' => $canEditPrivateInfo && $this->authManager->allowsPropertyChange( 'realname' )
347  ? 'text' : 'info',
348  'default' => $user->getRealName(),
349  'section' => 'personal/info',
350  'label-message' => 'yourrealname',
351  'help-message' => 'prefs-help-realname',
352  ];
353 
354  if ( $canEditPrivateInfo && $this->authManager->allowsAuthenticationDataChange(
355  new PasswordAuthenticationRequest(), false )->isGood()
356  ) {
357  $defaultPreferences['password'] = [
358  'type' => 'info',
359  'raw' => true,
360  'default' => (string)new \OOUI\ButtonWidget( [
361  'href' => SpecialPage::getTitleFor( 'ChangePassword' )->getLinkURL( [
362  'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
363  ] ),
364  'label' => $context->msg( 'prefs-resetpass' )->text(),
365  ] ),
366  'label-message' => 'yourpassword',
367  'section' => 'personal/info',
368  ];
369  }
370  // Only show prefershttps if secure login is turned on
371  if ( $this->config->get( 'SecureLogin' ) && $canIPUseHTTPS ) {
372  $defaultPreferences['prefershttps'] = [
373  'type' => 'toggle',
374  'label-message' => 'tog-prefershttps',
375  'help-message' => 'prefs-help-prefershttps',
376  'section' => 'personal/info'
377  ];
378  }
379 
380  // Language
381  $languages = Language::fetchLanguageNames( null, 'mwfile' );
382  $languageCode = $this->config->get( 'LanguageCode' );
383  if ( !array_key_exists( $languageCode, $languages ) ) {
384  $languages[$languageCode] = $languageCode;
385  // Sort the array again
386  ksort( $languages );
387  }
388 
389  $options = [];
390  foreach ( $languages as $code => $name ) {
391  $display = LanguageCode::bcp47( $code ) . ' - ' . $name;
392  $options[$display] = $code;
393  }
394  $defaultPreferences['language'] = [
395  'type' => 'select',
396  'section' => 'personal/i18n',
397  'options' => $options,
398  'label-message' => 'yourlanguage',
399  ];
400 
401  $defaultPreferences['gender'] = [
402  'type' => 'radio',
403  'section' => 'personal/i18n',
404  'options' => [
405  $context->msg( 'parentheses' )
406  ->params( $context->msg( 'gender-unknown' )->plain() )
407  ->escaped() => 'unknown',
408  $context->msg( 'gender-female' )->escaped() => 'female',
409  $context->msg( 'gender-male' )->escaped() => 'male',
410  ],
411  'label-message' => 'yourgender',
412  'help-message' => 'prefs-help-gender',
413  ];
414 
415  // see if there are multiple language variants to choose from
416  if ( !$this->config->get( 'DisableLangConversion' ) ) {
417  foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
418  if ( $langCode == $this->contLang->getCode() ) {
419  if ( !$this->contLang->hasVariants() ) {
420  continue;
421  }
422 
423  $variants = $this->contLang->getVariants();
424  $variantArray = [];
425  foreach ( $variants as $v ) {
426  $v = str_replace( '_', '-', strtolower( $v ) );
427  $variantArray[$v] = $lang->getVariantname( $v, false );
428  }
429 
430  $options = [];
431  foreach ( $variantArray as $code => $name ) {
432  $display = LanguageCode::bcp47( $code ) . ' - ' . $name;
433  $options[$display] = $code;
434  }
435 
436  $defaultPreferences['variant'] = [
437  'label-message' => 'yourvariant',
438  'type' => 'select',
439  'options' => $options,
440  'section' => 'personal/i18n',
441  'help-message' => 'prefs-help-variant',
442  ];
443  } else {
444  $defaultPreferences["variant-$langCode"] = [
445  'type' => 'api',
446  ];
447  }
448  }
449  }
450 
451  // Stuff from Language::getExtraUserToggles()
452  // FIXME is this dead code? $extraUserToggles doesn't seem to be defined for any language
453  $toggles = $this->contLang->getExtraUserToggles();
454 
455  foreach ( $toggles as $toggle ) {
456  $defaultPreferences[$toggle] = [
457  'type' => 'toggle',
458  'section' => 'personal/i18n',
459  'label-message' => "tog-$toggle",
460  ];
461  }
462 
463  // show a preview of the old signature first
464  $oldsigWikiText = MediaWikiServices::getInstance()->getParser()->preSaveTransform(
465  '~~~',
466  $context->getTitle(),
467  $user,
469  );
470  $oldsigHTML = $context->getOutput()->parseInline( $oldsigWikiText, true, true );
471  $defaultPreferences['oldsig'] = [
472  'type' => 'info',
473  'raw' => true,
474  'label-message' => 'tog-oldsig',
475  'default' => $oldsigHTML,
476  'section' => 'personal/signature',
477  ];
478  $defaultPreferences['nickname'] = [
479  'type' => $this->authManager->allowsPropertyChange( 'nickname' ) ? 'text' : 'info',
480  'maxlength' => $this->config->get( 'MaxSigChars' ),
481  'label-message' => 'yournick',
482  'validation-callback' => function ( $signature, $alldata, HTMLForm $form ) {
483  return $this->validateSignature( $signature, $alldata, $form );
484  },
485  'section' => 'personal/signature',
486  'filter-callback' => function ( $signature, array $alldata, HTMLForm $form ) {
487  return $this->cleanSignature( $signature, $alldata, $form );
488  },
489  ];
490  $defaultPreferences['fancysig'] = [
491  'type' => 'toggle',
492  'label-message' => 'tog-fancysig',
493  // show general help about signature at the bottom of the section
494  'help-message' => 'prefs-help-signature',
495  'section' => 'personal/signature'
496  ];
497 
498  # # Email stuff
499 
500  if ( $this->config->get( 'EnableEmail' ) ) {
501  if ( $canViewPrivateInfo ) {
502  $helpMessages[] = $this->config->get( 'EmailConfirmToEdit' )
503  ? 'prefs-help-email-required'
504  : 'prefs-help-email';
505 
506  if ( $this->config->get( 'EnableUserEmail' ) ) {
507  // additional messages when users can send email to each other
508  $helpMessages[] = 'prefs-help-email-others';
509  }
510 
511  $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : '';
512  if ( $canEditPrivateInfo && $this->authManager->allowsPropertyChange( 'emailaddress' ) ) {
513  $button = new \OOUI\ButtonWidget( [
514  'href' => SpecialPage::getTitleFor( 'ChangeEmail' )->getLinkURL( [
515  'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
516  ] ),
517  'label' =>
518  $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->text(),
519  ] );
520 
521  $emailAddress .= $emailAddress == '' ? $button : ( '<br />' . $button );
522  }
523 
524  $defaultPreferences['emailaddress'] = [
525  'type' => 'info',
526  'raw' => true,
527  'default' => $emailAddress,
528  'label-message' => 'youremail',
529  'section' => 'personal/email',
530  'help-messages' => $helpMessages,
531  # 'cssclass' chosen below
532  ];
533  }
534 
535  $disableEmailPrefs = false;
536 
537  if ( $this->config->get( 'EmailAuthentication' ) ) {
538  $emailauthenticationclass = 'mw-email-not-authenticated';
539  if ( $user->getEmail() ) {
540  if ( $user->getEmailAuthenticationTimestamp() ) {
541  // date and time are separate parameters to facilitate localisation.
542  // $time is kept for backward compat reasons.
543  // 'emailauthenticated' is also used in SpecialConfirmemail.php
544  $displayUser = $context->getUser();
545  $emailTimestamp = $user->getEmailAuthenticationTimestamp();
546  $time = $lang->userTimeAndDate( $emailTimestamp, $displayUser );
547  $d = $lang->userDate( $emailTimestamp, $displayUser );
548  $t = $lang->userTime( $emailTimestamp, $displayUser );
549  $emailauthenticated = $context->msg( 'emailauthenticated',
550  $time, $d, $t )->parse() . '<br />';
551  $disableEmailPrefs = false;
552  $emailauthenticationclass = 'mw-email-authenticated';
553  } else {
554  $disableEmailPrefs = true;
555  $emailauthenticated = $context->msg( 'emailnotauthenticated' )->parse() . '<br />' .
556  new \OOUI\ButtonWidget( [
557  'href' => SpecialPage::getTitleFor( 'Confirmemail' )->getLinkURL(),
558  'label' => $context->msg( 'emailconfirmlink' )->text(),
559  ] );
560  $emailauthenticationclass = "mw-email-not-authenticated";
561  }
562  } else {
563  $disableEmailPrefs = true;
564  $emailauthenticated = $context->msg( 'noemailprefs' )->escaped();
565  $emailauthenticationclass = 'mw-email-none';
566  }
567 
568  if ( $canViewPrivateInfo ) {
569  $defaultPreferences['emailauthentication'] = [
570  'type' => 'info',
571  'raw' => true,
572  'section' => 'personal/email',
573  'label-message' => 'prefs-emailconfirm-label',
574  'default' => $emailauthenticated,
575  # Apply the same CSS class used on the input to the message:
576  'cssclass' => $emailauthenticationclass,
577  ];
578  }
579  }
580 
581  if ( $this->config->get( 'EnableUserEmail' ) && $user->isAllowed( 'sendemail' ) ) {
582  $defaultPreferences['disablemail'] = [
583  'id' => 'wpAllowEmail',
584  'type' => 'toggle',
585  'invert' => true,
586  'section' => 'personal/email',
587  'label-message' => 'allowemail',
588  'disabled' => $disableEmailPrefs,
589  ];
590 
591  $defaultPreferences['email-allow-new-users'] = [
592  'id' => 'wpAllowEmailFromNewUsers',
593  'type' => 'toggle',
594  'section' => 'personal/email',
595  'label-message' => 'email-allow-new-users-label',
596  'disabled' => $disableEmailPrefs,
597  ];
598 
599  $defaultPreferences['ccmeonemails'] = [
600  'type' => 'toggle',
601  'section' => 'personal/email',
602  'label-message' => 'tog-ccmeonemails',
603  'disabled' => $disableEmailPrefs,
604  ];
605 
606  if ( $this->config->get( 'EnableUserEmailBlacklist' ) ) {
607  $defaultPreferences['email-blacklist'] = [
608  'type' => 'usersmultiselect',
609  'label-message' => 'email-blacklist-label',
610  'section' => 'personal/email',
611  'disabled' => $disableEmailPrefs,
612  'filter' => MultiUsernameFilter::class,
613  ];
614  }
615  }
616 
617  if ( $this->config->get( 'EnotifWatchlist' ) ) {
618  $defaultPreferences['enotifwatchlistpages'] = [
619  'type' => 'toggle',
620  'section' => 'personal/email',
621  'label-message' => 'tog-enotifwatchlistpages',
622  'disabled' => $disableEmailPrefs,
623  ];
624  }
625  if ( $this->config->get( 'EnotifUserTalk' ) ) {
626  $defaultPreferences['enotifusertalkpages'] = [
627  'type' => 'toggle',
628  'section' => 'personal/email',
629  'label-message' => 'tog-enotifusertalkpages',
630  'disabled' => $disableEmailPrefs,
631  ];
632  }
633  if ( $this->config->get( 'EnotifUserTalk' ) || $this->config->get( 'EnotifWatchlist' ) ) {
634  if ( $this->config->get( 'EnotifMinorEdits' ) ) {
635  $defaultPreferences['enotifminoredits'] = [
636  'type' => 'toggle',
637  'section' => 'personal/email',
638  'label-message' => 'tog-enotifminoredits',
639  'disabled' => $disableEmailPrefs,
640  ];
641  }
642 
643  if ( $this->config->get( 'EnotifRevealEditorAddress' ) ) {
644  $defaultPreferences['enotifrevealaddr'] = [
645  'type' => 'toggle',
646  'section' => 'personal/email',
647  'label-message' => 'tog-enotifrevealaddr',
648  'disabled' => $disableEmailPrefs,
649  ];
650  }
651  }
652  }
653  }
654 
661  protected function skinPreferences( User $user, IContextSource $context, &$defaultPreferences ) {
662  # # Skin #####################################
663 
664  // Skin selector, if there is at least one valid skin
665  $skinOptions = $this->generateSkinOptions( $user, $context );
666  if ( $skinOptions ) {
667  $defaultPreferences['skin'] = [
668  'type' => 'radio',
669  'options' => $skinOptions,
670  'section' => 'rendering/skin',
671  ];
672  }
673 
674  $allowUserCss = $this->config->get( 'AllowUserCss' );
675  $allowUserJs = $this->config->get( 'AllowUserJs' );
676  # Create links to user CSS/JS pages for all skins
677  # This code is basically copied from generateSkinOptions(). It'd
678  # be nice to somehow merge this back in there to avoid redundancy.
679  if ( $allowUserCss || $allowUserJs ) {
680  $linkTools = [];
681  $userName = $user->getName();
682 
683  if ( $allowUserCss ) {
684  $cssPage = Title::makeTitleSafe( NS_USER, $userName . '/common.css' );
685  $cssLinkText = $context->msg( 'prefs-custom-css' )->text();
686  $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
687  }
688 
689  if ( $allowUserJs ) {
690  $jsPage = Title::makeTitleSafe( NS_USER, $userName . '/common.js' );
691  $jsLinkText = $context->msg( 'prefs-custom-js' )->text();
692  $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
693  }
694 
695  $defaultPreferences['commoncssjs'] = [
696  'type' => 'info',
697  'raw' => true,
698  'default' => $context->getLanguage()->pipeList( $linkTools ),
699  'label-message' => 'prefs-common-config',
700  'section' => 'rendering/skin',
701  ];
702  }
703  }
704 
709  protected function filesPreferences( IContextSource $context, &$defaultPreferences ) {
710  # # Files #####################################
711  $defaultPreferences['imagesize'] = [
712  'type' => 'select',
713  'options' => $this->getImageSizes( $context ),
714  'label-message' => 'imagemaxsize',
715  'section' => 'rendering/files',
716  ];
717  $defaultPreferences['thumbsize'] = [
718  'type' => 'select',
719  'options' => $this->getThumbSizes( $context ),
720  'label-message' => 'thumbsize',
721  'section' => 'rendering/files',
722  ];
723  }
724 
731  protected function datetimePreferences( $user, IContextSource $context, &$defaultPreferences ) {
732  # # Date and time #####################################
733  $dateOptions = $this->getDateOptions( $context );
734  if ( $dateOptions ) {
735  $defaultPreferences['date'] = [
736  'type' => 'radio',
737  'options' => $dateOptions,
738  'section' => 'rendering/dateformat',
739  ];
740  }
741 
742  // Info
743  $now = wfTimestampNow();
745  $nowlocal = Xml::element( 'span', [ 'id' => 'wpLocalTime' ],
746  $lang->userTime( $now, $user ) );
747  $nowserver = $lang->userTime( $now, $user,
748  [ 'format' => false, 'timecorrection' => false ] ) .
749  Html::hidden( 'wpServerTime', (int)substr( $now, 8, 2 ) * 60 + (int)substr( $now, 10, 2 ) );
750 
751  $defaultPreferences['nowserver'] = [
752  'type' => 'info',
753  'raw' => 1,
754  'label-message' => 'servertime',
755  'default' => $nowserver,
756  'section' => 'rendering/timeoffset',
757  ];
758 
759  $defaultPreferences['nowlocal'] = [
760  'type' => 'info',
761  'raw' => 1,
762  'label-message' => 'localtime',
763  'default' => $nowlocal,
764  'section' => 'rendering/timeoffset',
765  ];
766 
767  // Grab existing pref.
768  $tzOffset = $user->getOption( 'timecorrection' );
769  $tz = explode( '|', $tzOffset, 3 );
770 
771  $tzOptions = $this->getTimezoneOptions( $context );
772 
773  $tzSetting = $tzOffset;
774  if ( count( $tz ) > 1 && $tz[0] == 'ZoneInfo' &&
775  !in_array( $tzOffset, HTMLFormField::flattenOptions( $tzOptions ) )
776  ) {
777  // Timezone offset can vary with DST
778  try {
779  $userTZ = new DateTimeZone( $tz[2] );
780  $minDiff = floor( $userTZ->getOffset( new DateTime( 'now' ) ) / 60 );
781  $tzSetting = "ZoneInfo|$minDiff|{$tz[2]}";
782  } catch ( Exception $e ) {
783  // User has an invalid time zone set. Fall back to just using the offset
784  $tz[0] = 'Offset';
785  }
786  }
787  if ( count( $tz ) > 1 && $tz[0] == 'Offset' ) {
788  $minDiff = $tz[1];
789  $tzSetting = sprintf( '%+03d:%02d', floor( $minDiff / 60 ), abs( $minDiff ) % 60 );
790  }
791 
792  $defaultPreferences['timecorrection'] = [
794  'label-message' => 'timezonelegend',
795  'options' => $tzOptions,
796  'default' => $tzSetting,
797  'size' => 20,
798  'section' => 'rendering/timeoffset',
799  'id' => 'wpTimeCorrection',
800  'filter' => TimezoneFilter::class,
801  ];
802  }
803 
808  protected function renderingPreferences( MessageLocalizer $l10n, &$defaultPreferences ) {
809  # # Diffs ####################################
810  $defaultPreferences['diffonly'] = [
811  'type' => 'toggle',
812  'section' => 'rendering/diffs',
813  'label-message' => 'tog-diffonly',
814  ];
815  $defaultPreferences['norollbackdiff'] = [
816  'type' => 'toggle',
817  'section' => 'rendering/diffs',
818  'label-message' => 'tog-norollbackdiff',
819  ];
820 
821  # # Page Rendering ##############################
822  if ( $this->config->get( 'AllowUserCssPrefs' ) ) {
823  $defaultPreferences['underline'] = [
824  'type' => 'select',
825  'options' => [
826  $l10n->msg( 'underline-never' )->text() => 0,
827  $l10n->msg( 'underline-always' )->text() => 1,
828  $l10n->msg( 'underline-default' )->text() => 2,
829  ],
830  'label-message' => 'tog-underline',
831  'section' => 'rendering/advancedrendering',
832  ];
833  }
834 
835  $stubThresholdValues = [ 50, 100, 500, 1000, 2000, 5000, 10000 ];
836  $stubThresholdOptions = [ $l10n->msg( 'stub-threshold-disabled' )->text() => 0 ];
837  foreach ( $stubThresholdValues as $value ) {
838  $stubThresholdOptions[$l10n->msg( 'size-bytes', $value )->text()] = $value;
839  }
840 
841  $defaultPreferences['stubthreshold'] = [
842  'type' => 'select',
843  'section' => 'rendering/advancedrendering',
844  'options' => $stubThresholdOptions,
845  // This is not a raw HTML message; label-raw is needed for the manual <a></a>
846  'label-raw' => $l10n->msg( 'stub-threshold' )->rawParams(
847  '<a href="#" class="stub">' .
848  $l10n->msg( 'stub-threshold-sample-link' )->parse() .
849  '</a>' )->parse(),
850  ];
851 
852  $defaultPreferences['showhiddencats'] = [
853  'type' => 'toggle',
854  'section' => 'rendering/advancedrendering',
855  'label-message' => 'tog-showhiddencats'
856  ];
857 
858  $defaultPreferences['numberheadings'] = [
859  'type' => 'toggle',
860  'section' => 'rendering/advancedrendering',
861  'label-message' => 'tog-numberheadings',
862  ];
863  }
864 
870  protected function editingPreferences( User $user, MessageLocalizer $l10n, &$defaultPreferences ) {
871  # # Editing #####################################
872  $defaultPreferences['editsectiononrightclick'] = [
873  'type' => 'toggle',
874  'section' => 'editing/advancedediting',
875  'label-message' => 'tog-editsectiononrightclick',
876  ];
877  $defaultPreferences['editondblclick'] = [
878  'type' => 'toggle',
879  'section' => 'editing/advancedediting',
880  'label-message' => 'tog-editondblclick',
881  ];
882 
883  if ( $this->config->get( 'AllowUserCssPrefs' ) ) {
884  $defaultPreferences['editfont'] = [
885  'type' => 'select',
886  'section' => 'editing/editor',
887  'label-message' => 'editfont-style',
888  'options' => [
889  $l10n->msg( 'editfont-monospace' )->text() => 'monospace',
890  $l10n->msg( 'editfont-sansserif' )->text() => 'sans-serif',
891  $l10n->msg( 'editfont-serif' )->text() => 'serif',
892  ]
893  ];
894  }
895 
896  if ( $user->isAllowed( 'minoredit' ) ) {
897  $defaultPreferences['minordefault'] = [
898  'type' => 'toggle',
899  'section' => 'editing/editor',
900  'label-message' => 'tog-minordefault',
901  ];
902  }
903 
904  $defaultPreferences['forceeditsummary'] = [
905  'type' => 'toggle',
906  'section' => 'editing/editor',
907  'label-message' => 'tog-forceeditsummary',
908  ];
909  $defaultPreferences['useeditwarning'] = [
910  'type' => 'toggle',
911  'section' => 'editing/editor',
912  'label-message' => 'tog-useeditwarning',
913  ];
914 
915  $defaultPreferences['previewonfirst'] = [
916  'type' => 'toggle',
917  'section' => 'editing/preview',
918  'label-message' => 'tog-previewonfirst',
919  ];
920  $defaultPreferences['previewontop'] = [
921  'type' => 'toggle',
922  'section' => 'editing/preview',
923  'label-message' => 'tog-previewontop',
924  ];
925  $defaultPreferences['uselivepreview'] = [
926  'type' => 'toggle',
927  'section' => 'editing/preview',
928  'label-message' => 'tog-uselivepreview',
929  ];
930  }
931 
937  protected function rcPreferences( User $user, MessageLocalizer $l10n, &$defaultPreferences ) {
938  $rcMaxAge = $this->config->get( 'RCMaxAge' );
939  # # RecentChanges #####################################
940  $defaultPreferences['rcdays'] = [
941  'type' => 'float',
942  'label-message' => 'recentchangesdays',
943  'section' => 'rc/displayrc',
944  'min' => 1 / 24,
945  'max' => ceil( $rcMaxAge / ( 3600 * 24 ) ),
946  'help' => $l10n->msg( 'recentchangesdays-max' )->numParams(
947  ceil( $rcMaxAge / ( 3600 * 24 ) ) )->escaped()
948  ];
949  $defaultPreferences['rclimit'] = [
950  'type' => 'int',
951  'min' => 1,
952  'max' => 1000,
953  'label-message' => 'recentchangescount',
954  'help-message' => 'prefs-help-recentchangescount',
955  'section' => 'rc/displayrc',
956  'filter' => IntvalFilter::class,
957  ];
958  $defaultPreferences['usenewrc'] = [
959  'type' => 'toggle',
960  'label-message' => 'tog-usenewrc',
961  'section' => 'rc/advancedrc',
962  ];
963  $defaultPreferences['hideminor'] = [
964  'type' => 'toggle',
965  'label-message' => 'tog-hideminor',
966  'section' => 'rc/advancedrc',
967  ];
968  $defaultPreferences['rcfilters-rc-collapsed'] = [
969  'type' => 'api',
970  ];
971  $defaultPreferences['rcfilters-wl-collapsed'] = [
972  'type' => 'api',
973  ];
974  $defaultPreferences['rcfilters-saved-queries'] = [
975  'type' => 'api',
976  ];
977  $defaultPreferences['rcfilters-wl-saved-queries'] = [
978  'type' => 'api',
979  ];
980  // Override RCFilters preferences for RecentChanges 'limit'
981  $defaultPreferences['rcfilters-limit'] = [
982  'type' => 'api',
983  ];
984  $defaultPreferences['rcfilters-saved-queries-versionbackup'] = [
985  'type' => 'api',
986  ];
987  $defaultPreferences['rcfilters-wl-saved-queries-versionbackup'] = [
988  'type' => 'api',
989  ];
990 
991  if ( $this->config->get( 'RCWatchCategoryMembership' ) ) {
992  $defaultPreferences['hidecategorization'] = [
993  'type' => 'toggle',
994  'label-message' => 'tog-hidecategorization',
995  'section' => 'rc/advancedrc',
996  ];
997  }
998 
999  if ( $user->useRCPatrol() ) {
1000  $defaultPreferences['hidepatrolled'] = [
1001  'type' => 'toggle',
1002  'section' => 'rc/advancedrc',
1003  'label-message' => 'tog-hidepatrolled',
1004  ];
1005  }
1006 
1007  if ( $user->useNPPatrol() ) {
1008  $defaultPreferences['newpageshidepatrolled'] = [
1009  'type' => 'toggle',
1010  'section' => 'rc/advancedrc',
1011  'label-message' => 'tog-newpageshidepatrolled',
1012  ];
1013  }
1014 
1015  if ( $this->config->get( 'RCShowWatchingUsers' ) ) {
1016  $defaultPreferences['shownumberswatching'] = [
1017  'type' => 'toggle',
1018  'section' => 'rc/advancedrc',
1019  'label-message' => 'tog-shownumberswatching',
1020  ];
1021  }
1022 
1023  $defaultPreferences['rcenhancedfilters-disable'] = [
1024  'type' => 'toggle',
1025  'section' => 'rc/optoutrc',
1026  'label-message' => 'rcfilters-preference-label',
1027  'help-message' => 'rcfilters-preference-help',
1028  ];
1029  }
1030 
1036  protected function watchlistPreferences(
1037  User $user, IContextSource $context, &$defaultPreferences
1038  ) {
1039  $watchlistdaysMax = ceil( $this->config->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
1040 
1041  # # Watchlist #####################################
1042  if ( $user->isAllowed( 'editmywatchlist' ) ) {
1043  $editWatchlistLinks = '';
1044  $editWatchlistLinksOld = [];
1045  $editWatchlistModes = [
1046  'edit' => [ 'subpage' => false, 'flags' => [] ],
1047  'raw' => [ 'subpage' => 'raw', 'flags' => [] ],
1048  'clear' => [ 'subpage' => 'clear', 'flags' => [ 'destructive' ] ],
1049  ];
1050  foreach ( $editWatchlistModes as $mode => $options ) {
1051  // Messages: prefs-editwatchlist-edit, prefs-editwatchlist-raw, prefs-editwatchlist-clear
1052  $editWatchlistLinks .=
1053  new \OOUI\ButtonWidget( [
1054  'href' => SpecialPage::getTitleFor( 'EditWatchlist', $options['subpage'] )->getLinkURL(),
1055  'flags' => $options[ 'flags' ],
1056  'label' => new \OOUI\HtmlSnippet(
1057  $context->msg( "prefs-editwatchlist-{$mode}" )->parse()
1058  ),
1059  ] );
1060  }
1061 
1062  $defaultPreferences['editwatchlist'] = [
1063  'type' => 'info',
1064  'raw' => true,
1065  'default' => $editWatchlistLinks,
1066  'label-message' => 'prefs-editwatchlist-label',
1067  'section' => 'watchlist/editwatchlist',
1068  ];
1069  }
1070 
1071  $defaultPreferences['watchlistdays'] = [
1072  'type' => 'float',
1073  'min' => 1 / 24,
1074  'max' => $watchlistdaysMax,
1075  'section' => 'watchlist/displaywatchlist',
1076  'help' => $context->msg( 'prefs-watchlist-days-max' )->numParams(
1077  $watchlistdaysMax )->escaped(),
1078  'label-message' => 'prefs-watchlist-days',
1079  ];
1080  $defaultPreferences['wllimit'] = [
1081  'type' => 'int',
1082  'min' => 1,
1083  'max' => 1000,
1084  'label-message' => 'prefs-watchlist-edits',
1085  'help' => $context->msg( 'prefs-watchlist-edits-max' )->escaped(),
1086  'section' => 'watchlist/displaywatchlist',
1087  'filter' => IntvalFilter::class,
1088  ];
1089  $defaultPreferences['extendwatchlist'] = [
1090  'type' => 'toggle',
1091  'section' => 'watchlist/advancedwatchlist',
1092  'label-message' => 'tog-extendwatchlist',
1093  ];
1094  $defaultPreferences['watchlisthideminor'] = [
1095  'type' => 'toggle',
1096  'section' => 'watchlist/advancedwatchlist',
1097  'label-message' => 'tog-watchlisthideminor',
1098  ];
1099  $defaultPreferences['watchlisthidebots'] = [
1100  'type' => 'toggle',
1101  'section' => 'watchlist/advancedwatchlist',
1102  'label-message' => 'tog-watchlisthidebots',
1103  ];
1104  $defaultPreferences['watchlisthideown'] = [
1105  'type' => 'toggle',
1106  'section' => 'watchlist/advancedwatchlist',
1107  'label-message' => 'tog-watchlisthideown',
1108  ];
1109  $defaultPreferences['watchlisthideanons'] = [
1110  'type' => 'toggle',
1111  'section' => 'watchlist/advancedwatchlist',
1112  'label-message' => 'tog-watchlisthideanons',
1113  ];
1114  $defaultPreferences['watchlisthideliu'] = [
1115  'type' => 'toggle',
1116  'section' => 'watchlist/advancedwatchlist',
1117  'label-message' => 'tog-watchlisthideliu',
1118  ];
1119 
1121  $this->config,
1122  $user
1123  ) ) {
1124  $defaultPreferences['watchlistreloadautomatically'] = [
1125  'type' => 'toggle',
1126  'section' => 'watchlist/advancedwatchlist',
1127  'label-message' => 'tog-watchlistreloadautomatically',
1128  ];
1129  }
1130 
1131  $defaultPreferences['watchlistunwatchlinks'] = [
1132  'type' => 'toggle',
1133  'section' => 'watchlist/advancedwatchlist',
1134  'label-message' => 'tog-watchlistunwatchlinks',
1135  ];
1136 
1137  if ( $this->config->get( 'RCWatchCategoryMembership' ) ) {
1138  $defaultPreferences['watchlisthidecategorization'] = [
1139  'type' => 'toggle',
1140  'section' => 'watchlist/advancedwatchlist',
1141  'label-message' => 'tog-watchlisthidecategorization',
1142  ];
1143  }
1144 
1145  if ( $user->useRCPatrol() ) {
1146  $defaultPreferences['watchlisthidepatrolled'] = [
1147  'type' => 'toggle',
1148  'section' => 'watchlist/advancedwatchlist',
1149  'label-message' => 'tog-watchlisthidepatrolled',
1150  ];
1151  }
1152 
1153  $watchTypes = [
1154  'edit' => 'watchdefault',
1155  'move' => 'watchmoves',
1156  'delete' => 'watchdeletion'
1157  ];
1158 
1159  // Kinda hacky
1160  if ( $user->isAllowed( 'createpage' ) || $user->isAllowed( 'createtalk' ) ) {
1161  $watchTypes['read'] = 'watchcreations';
1162  }
1163 
1164  if ( $user->isAllowed( 'rollback' ) ) {
1165  $watchTypes['rollback'] = 'watchrollback';
1166  }
1167 
1168  if ( $user->isAllowed( 'upload' ) ) {
1169  $watchTypes['upload'] = 'watchuploads';
1170  }
1171 
1172  foreach ( $watchTypes as $action => $pref ) {
1173  if ( $user->isAllowed( $action ) ) {
1174  // Messages:
1175  // tog-watchdefault, tog-watchmoves, tog-watchdeletion, tog-watchcreations, tog-watchuploads
1176  // tog-watchrollback
1177  $defaultPreferences[$pref] = [
1178  'type' => 'toggle',
1179  'section' => 'watchlist/advancedwatchlist',
1180  'label-message' => "tog-$pref",
1181  ];
1182  }
1183  }
1184 
1185  $defaultPreferences['watchlisttoken'] = [
1186  'type' => 'api',
1187  ];
1188 
1189  $tokenButton = new \OOUI\ButtonWidget( [
1190  'href' => SpecialPage::getTitleFor( 'ResetTokens' )->getLinkURL( [
1191  'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
1192  ] ),
1193  'label' => $context->msg( 'prefs-watchlist-managetokens' )->text(),
1194  ] );
1195  $defaultPreferences['watchlisttoken-info'] = [
1196  'type' => 'info',
1197  'section' => 'watchlist/tokenwatchlist',
1198  'label-message' => 'prefs-watchlist-token',
1199  'help-message' => 'prefs-help-tokenmanagement',
1200  'raw' => true,
1201  'default' => (string)$tokenButton,
1202  ];
1203 
1204  $defaultPreferences['wlenhancedfilters-disable'] = [
1205  'type' => 'toggle',
1206  'section' => 'watchlist/optoutwatchlist',
1207  'label-message' => 'rcfilters-watchlist-preference-label',
1208  'help-message' => 'rcfilters-watchlist-preference-help',
1209  ];
1210  }
1211 
1215  protected function searchPreferences( &$defaultPreferences ) {
1216  foreach ( MWNamespace::getValidNamespaces() as $n ) {
1217  $defaultPreferences['searchNs' . $n] = [
1218  'type' => 'api',
1219  ];
1220  }
1221  }
1222 
1229  $ret = [];
1230 
1231  $mptitle = Title::newMainPage();
1232  $previewtext = $context->msg( 'skin-preview' )->escaped();
1233 
1234  # Only show skins that aren't disabled in $wgSkipSkins
1235  $validSkinNames = Skin::getAllowedSkins();
1236 
1237  foreach ( $validSkinNames as $skinkey => &$skinname ) {
1238  $msg = $context->msg( "skinname-{$skinkey}" );
1239  if ( $msg->exists() ) {
1240  $skinname = htmlspecialchars( $msg->text() );
1241  }
1242  }
1243 
1244  $defaultSkin = $this->config->get( 'DefaultSkin' );
1245  $allowUserCss = $this->config->get( 'AllowUserCss' );
1246  $allowUserJs = $this->config->get( 'AllowUserJs' );
1247 
1248  # Sort by the internal name, so that the ordering is the same for each display language,
1249  # especially if some skin names are translated to use a different alphabet and some are not.
1250  uksort( $validSkinNames, function ( $a, $b ) use ( $defaultSkin ) {
1251  # Display the default first in the list by comparing it as lesser than any other.
1252  if ( strcasecmp( $a, $defaultSkin ) === 0 ) {
1253  return -1;
1254  }
1255  if ( strcasecmp( $b, $defaultSkin ) === 0 ) {
1256  return 1;
1257  }
1258  return strcasecmp( $a, $b );
1259  } );
1260 
1261  $foundDefault = false;
1262  foreach ( $validSkinNames as $skinkey => $sn ) {
1263  $linkTools = [];
1264 
1265  # Mark the default skin
1266  if ( strcasecmp( $skinkey, $defaultSkin ) === 0 ) {
1267  $linkTools[] = $context->msg( 'default' )->escaped();
1268  $foundDefault = true;
1269  }
1270 
1271  # Create preview link
1272  $mplink = htmlspecialchars( $mptitle->getLocalURL( [ 'useskin' => $skinkey ] ) );
1273  $linkTools[] = "<a target='_blank' href=\"$mplink\">$previewtext</a>";
1274 
1275  # Create links to user CSS/JS pages
1276  if ( $allowUserCss ) {
1277  $cssPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.css' );
1278  $cssLinkText = $context->msg( 'prefs-custom-css' )->text();
1279  $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
1280  }
1281 
1282  if ( $allowUserJs ) {
1283  $jsPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.js' );
1284  $jsLinkText = $context->msg( 'prefs-custom-js' )->text();
1285  $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
1286  }
1287 
1288  $display = $sn . ' ' . $context->msg( 'parentheses' )
1289  ->rawParams( $context->getLanguage()->pipeList( $linkTools ) )
1290  ->escaped();
1291  $ret[$display] = $skinkey;
1292  }
1293 
1294  if ( !$foundDefault ) {
1295  // If the default skin is not available, things are going to break horribly because the
1296  // default value for skin selector will not be a valid value. Let's just not show it then.
1297  return [];
1298  }
1299 
1300  return $ret;
1301  }
1302 
1307  protected function getDateOptions( IContextSource $context ) {
1308  $lang = $context->getLanguage();
1309  $dateopts = $lang->getDatePreferences();
1310 
1311  $ret = [];
1312 
1313  if ( $dateopts ) {
1314  if ( !in_array( 'default', $dateopts ) ) {
1315  $dateopts[] = 'default'; // Make sure default is always valid T21237
1316  }
1317 
1318  // FIXME KLUGE: site default might not be valid for user language
1319  global $wgDefaultUserOptions;
1320  if ( !in_array( $wgDefaultUserOptions['date'], $dateopts ) ) {
1321  $wgDefaultUserOptions['date'] = 'default';
1322  }
1323 
1324  $epoch = wfTimestampNow();
1325  foreach ( $dateopts as $key ) {
1326  if ( $key == 'default' ) {
1327  $formatted = $context->msg( 'datedefault' )->escaped();
1328  } else {
1329  $formatted = htmlspecialchars( $lang->timeanddate( $epoch, false, $key ) );
1330  }
1331  $ret[$formatted] = $key;
1332  }
1333  }
1334  return $ret;
1335  }
1336 
1341  protected function getImageSizes( MessageLocalizer $l10n ) {
1342  $ret = [];
1343  $pixels = $l10n->msg( 'unit-pixel' )->text();
1344 
1345  foreach ( $this->config->get( 'ImageLimits' ) as $index => $limits ) {
1346  // Note: A left-to-right marker (U+200E) is inserted, see T144386
1347  $display = "{$limits[0]}\u{200E}×{$limits[1]}$pixels";
1348  $ret[$display] = $index;
1349  }
1350 
1351  return $ret;
1352  }
1353 
1358  protected function getThumbSizes( MessageLocalizer $l10n ) {
1359  $ret = [];
1360  $pixels = $l10n->msg( 'unit-pixel' )->text();
1361 
1362  foreach ( $this->config->get( 'ThumbLimits' ) as $index => $size ) {
1363  $display = $size . $pixels;
1364  $ret[$display] = $index;
1365  }
1366 
1367  return $ret;
1368  }
1369 
1376  protected function validateSignature( $signature, $alldata, HTMLForm $form ) {
1377  $maxSigChars = $this->config->get( 'MaxSigChars' );
1378  if ( mb_strlen( $signature ) > $maxSigChars ) {
1379  return Xml::element( 'span', [ 'class' => 'error' ],
1380  $form->msg( 'badsiglength' )->numParams( $maxSigChars )->text() );
1381  } elseif ( isset( $alldata['fancysig'] ) &&
1382  $alldata['fancysig'] &&
1383  MediaWikiServices::getInstance()->getParser()->validateSig( $signature ) === false
1384  ) {
1385  return Xml::element(
1386  'span',
1387  [ 'class' => 'error' ],
1388  $form->msg( 'badsig' )->text()
1389  );
1390  } else {
1391  return true;
1392  }
1393  }
1394 
1401  protected function cleanSignature( $signature, $alldata, HTMLForm $form ) {
1402  $parser = MediaWikiServices::getInstance()->getParser();
1403  if ( isset( $alldata['fancysig'] ) && $alldata['fancysig'] ) {
1404  $signature = $parser->cleanSig( $signature );
1405  } else {
1406  // When no fancy sig used, make sure ~{3,5} get removed.
1407  $signature = Parser::cleanSigInSig( $signature );
1408  }
1409 
1410  return $signature;
1411  }
1412 
1420  public function getForm(
1421  User $user,
1423  $formClass = PreferencesFormLegacy::class,
1424  array $remove = []
1425  ) {
1426  // We use ButtonWidgets in some of the getPreferences() functions
1427  $context->getOutput()->enableOOUI();
1428 
1429  $formDescriptor = $this->getFormDescriptor( $user, $context );
1430  if ( count( $remove ) ) {
1431  $removeKeys = array_flip( $remove );
1432  $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
1433  }
1434 
1435  // Remove type=api preferences. They are not intended for rendering in the form.
1436  foreach ( $formDescriptor as $name => $info ) {
1437  if ( isset( $info['type'] ) && $info['type'] === 'api' ) {
1438  unset( $formDescriptor[$name] );
1439  }
1440  }
1441 
1445  $htmlForm = new $formClass( $formDescriptor, $context, 'prefs' );
1446 
1447  $htmlForm->setModifiedUser( $user );
1448  $htmlForm->setId( 'mw-prefs-form' );
1449  $htmlForm->setAutocomplete( 'off' );
1450  $htmlForm->setSubmitText( $context->msg( 'saveprefs' )->text() );
1451  # Used message keys: 'accesskey-preferences-save', 'tooltip-preferences-save'
1452  $htmlForm->setSubmitTooltip( 'preferences-save' );
1453  $htmlForm->setSubmitID( 'prefcontrol' );
1454  $htmlForm->setSubmitCallback(
1455  function ( array $formData, HTMLForm $form ) use ( $formDescriptor ) {
1456  return $this->submitForm( $formData, $form, $formDescriptor );
1457  }
1458  );
1459 
1460  return $htmlForm;
1461  }
1462 
1468  $opt = [];
1469 
1470  $localTZoffset = $this->config->get( 'LocalTZoffset' );
1471  $timeZoneList = $this->getTimeZoneList( $context->getLanguage() );
1472 
1473  $timestamp = MWTimestamp::getLocalInstance();
1474  // Check that the LocalTZoffset is the same as the local time zone offset
1475  if ( $localTZoffset == $timestamp->format( 'Z' ) / 60 ) {
1476  $timezoneName = $timestamp->getTimezone()->getName();
1477  // Localize timezone
1478  if ( isset( $timeZoneList[$timezoneName] ) ) {
1479  $timezoneName = $timeZoneList[$timezoneName]['name'];
1480  }
1481  $server_tz_msg = $context->msg(
1482  'timezoneuseserverdefault',
1483  $timezoneName
1484  )->text();
1485  } else {
1486  $tzstring = sprintf(
1487  '%+03d:%02d',
1488  floor( $localTZoffset / 60 ),
1489  abs( $localTZoffset ) % 60
1490  );
1491  $server_tz_msg = $context->msg( 'timezoneuseserverdefault', $tzstring )->text();
1492  }
1493  $opt[$server_tz_msg] = "System|$localTZoffset";
1494  $opt[$context->msg( 'timezoneuseoffset' )->text()] = 'other';
1495  $opt[$context->msg( 'guesstimezone' )->text()] = 'guess';
1496 
1497  foreach ( $timeZoneList as $timeZoneInfo ) {
1498  $region = $timeZoneInfo['region'];
1499  if ( !isset( $opt[$region] ) ) {
1500  $opt[$region] = [];
1501  }
1502  $opt[$region][$timeZoneInfo['name']] = $timeZoneInfo['timecorrection'];
1503  }
1504  return $opt;
1505  }
1506 
1515  protected function saveFormData( $formData, HTMLForm $form, array $formDescriptor ) {
1517  $user = $form->getModifiedUser();
1518  $hiddenPrefs = $this->config->get( 'HiddenPrefs' );
1519  $result = true;
1520 
1521  if ( !$user->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) {
1522  return Status::newFatal( 'mypreferencesprotected' );
1523  }
1524 
1525  // Filter input
1526  $this->applyFilters( $formData, $formDescriptor, 'filterFromForm' );
1527 
1528  // Fortunately, the realname field is MUCH simpler
1529  // (not really "private", but still shouldn't be edited without permission)
1530 
1531  if ( !in_array( 'realname', $hiddenPrefs )
1532  && $user->isAllowed( 'editmyprivateinfo' )
1533  && array_key_exists( 'realname', $formData )
1534  ) {
1535  $realName = $formData['realname'];
1536  $user->setRealName( $realName );
1537  }
1538 
1539  if ( $user->isAllowed( 'editmyoptions' ) ) {
1540  $oldUserOptions = $user->getOptions();
1541 
1542  foreach ( $this->getSaveBlacklist() as $b ) {
1543  unset( $formData[$b] );
1544  }
1545 
1546  # If users have saved a value for a preference which has subsequently been disabled
1547  # via $wgHiddenPrefs, we don't want to destroy that setting in case the preference
1548  # is subsequently re-enabled
1549  foreach ( $hiddenPrefs as $pref ) {
1550  # If the user has not set a non-default value here, the default will be returned
1551  # and subsequently discarded
1552  $formData[$pref] = $user->getOption( $pref, null, true );
1553  }
1554 
1555  // If the user changed the rclimit preference, also change the rcfilters-rclimit preference
1556  if (
1557  isset( $formData['rclimit'] ) &&
1558  intval( $formData[ 'rclimit' ] ) !== $user->getIntOption( 'rclimit' )
1559  ) {
1560  $formData['rcfilters-limit'] = $formData['rclimit'];
1561  }
1562 
1563  // Keep old preferences from interfering due to back-compat code, etc.
1564  $user->resetOptions( 'unused', $form->getContext() );
1565 
1566  foreach ( $formData as $key => $value ) {
1567  $user->setOption( $key, $value );
1568  }
1569 
1570  Hooks::run(
1571  'PreferencesFormPreSave',
1572  [ $formData, $form, $user, &$result, $oldUserOptions ]
1573  );
1574  }
1575 
1576  AuthManager::callLegacyAuthPlugin( 'updateExternalDB', [ $user ] );
1577  $user->saveSettings();
1578 
1579  return $result;
1580  }
1581 
1590  protected function applyFilters( array &$preferences, array $formDescriptor, $verb ) {
1591  foreach ( $formDescriptor as $preference => $desc ) {
1592  if ( !isset( $desc['filter'] ) || !isset( $preferences[$preference] ) ) {
1593  continue;
1594  }
1595  $filterDesc = $desc['filter'];
1596  if ( $filterDesc instanceof Filter ) {
1597  $filter = $filterDesc;
1598  } elseif ( class_exists( $filterDesc ) ) {
1599  $filter = new $filterDesc();
1600  } elseif ( is_callable( $filterDesc ) ) {
1601  $filter = $filterDesc();
1602  } else {
1603  throw new UnexpectedValueException(
1604  "Unrecognized filter type for preference '$preference'"
1605  );
1606  }
1607  $preferences[$preference] = $filter->$verb( $preferences[$preference] );
1608  }
1609  }
1610 
1619  protected function submitForm( array $formData, HTMLForm $form, array $formDescriptor ) {
1620  $res = $this->saveFormData( $formData, $form, $formDescriptor );
1621 
1622  if ( $res === true ) {
1623  $context = $form->getContext();
1624  $urlOptions = [];
1625 
1626  if ( $res === 'eauth' ) {
1627  $urlOptions['eauth'] = 1;
1628  }
1629 
1630  $urlOptions += $form->getExtraSuccessRedirectParameters();
1631 
1632  $url = $form->getTitle()->getFullURL( $urlOptions );
1633 
1634  // Set session data for the success message
1635  $context->getRequest()->getSession()->set( 'specialPreferencesSaveSuccess', 1 );
1636 
1637  $context->getOutput()->redirect( $url );
1638  }
1639 
1640  return ( $res === true ? Status::newGood() : $res );
1641  }
1642 
1651  protected function getTimeZoneList( Language $language ) {
1652  $identifiers = DateTimeZone::listIdentifiers();
1653  if ( $identifiers === false ) {
1654  return [];
1655  }
1656  sort( $identifiers );
1657 
1658  $tzRegions = [
1659  'Africa' => wfMessage( 'timezoneregion-africa' )->inLanguage( $language )->text(),
1660  'America' => wfMessage( 'timezoneregion-america' )->inLanguage( $language )->text(),
1661  'Antarctica' => wfMessage( 'timezoneregion-antarctica' )->inLanguage( $language )->text(),
1662  'Arctic' => wfMessage( 'timezoneregion-arctic' )->inLanguage( $language )->text(),
1663  'Asia' => wfMessage( 'timezoneregion-asia' )->inLanguage( $language )->text(),
1664  'Atlantic' => wfMessage( 'timezoneregion-atlantic' )->inLanguage( $language )->text(),
1665  'Australia' => wfMessage( 'timezoneregion-australia' )->inLanguage( $language )->text(),
1666  'Europe' => wfMessage( 'timezoneregion-europe' )->inLanguage( $language )->text(),
1667  'Indian' => wfMessage( 'timezoneregion-indian' )->inLanguage( $language )->text(),
1668  'Pacific' => wfMessage( 'timezoneregion-pacific' )->inLanguage( $language )->text(),
1669  ];
1670  asort( $tzRegions );
1671 
1672  $timeZoneList = [];
1673 
1674  $now = new DateTime();
1675 
1676  foreach ( $identifiers as $identifier ) {
1677  $parts = explode( '/', $identifier, 2 );
1678 
1679  // DateTimeZone::listIdentifiers() returns a number of
1680  // backwards-compatibility entries. This filters them out of the
1681  // list presented to the user.
1682  if ( count( $parts ) !== 2 || !array_key_exists( $parts[0], $tzRegions ) ) {
1683  continue;
1684  }
1685 
1686  // Localize region
1687  $parts[0] = $tzRegions[$parts[0]];
1688 
1689  $dateTimeZone = new DateTimeZone( $identifier );
1690  $minDiff = floor( $dateTimeZone->getOffset( $now ) / 60 );
1691 
1692  $display = str_replace( '_', ' ', $parts[0] . '/' . $parts[1] );
1693  $value = "ZoneInfo|$minDiff|$identifier";
1694 
1695  $timeZoneList[$identifier] = [
1696  'name' => $display,
1697  'timecorrection' => $value,
1698  'region' => $parts[0],
1699  ];
1700  }
1701 
1702  return $timeZoneList;
1703  }
1704 }
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:42
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:32
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:244
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:40
MediaWiki\Preferences\DefaultPreferencesFactory\__construct
__construct(Config $config, Language $contLang, AuthManager $authManager, LinkRenderer $linkRenderer)
Definition: DefaultPreferencesFactory.php:82
MediaWiki\$action
String $action
Cache what action this request is.
Definition: MediaWiki.php:48
MediaWiki\Preferences\DefaultPreferencesFactory\watchlistPreferences
watchlistPreferences(User $user, IContextSource $context, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:1036
wfCanIPUseHTTPS
wfCanIPUseHTTPS( $ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS.
Definition: GlobalFunctions.php:3163
MWNamespace\getValidNamespaces
static getValidNamespaces()
Returns an array of the namespaces (by integer id) that exist on the wiki.
Definition: MWNamespace.php:286
IContextSource\getSkin
getSkin()
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
$opt
$opt
Definition: postprocess-phan.php:115
captcha-old.count
count
Definition: captcha-old.py:249
MediaWiki\Preferences\DefaultPreferencesFactory\applyFilters
applyFilters(array &$preferences, array $formDescriptor, $verb)
Applies filters to preferences either before or after form usage.
Definition: DefaultPreferencesFactory.php:1590
$languages
switch( $options['output']) $languages
Definition: transstat.php:76
ContextSource\msg
msg( $key)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:168
Title\newMainPage
static newMainPage()
Create a new Title for the Main Page.
Definition: Title.php:597
UserGroupMembership\getExpiry
getExpiry()
Definition: UserGroupMembership.php:75
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED since 1.16! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:2034
MediaWiki\Preferences\DefaultPreferencesFactory\saveFormData
saveFormData( $formData, HTMLForm $form, array $formDescriptor)
Handle the form submission if everything validated properly.
Definition: DefaultPreferencesFactory.php:1515
MediaWiki\Preferences\DefaultPreferencesFactory\editingPreferences
editingPreferences(User $user, MessageLocalizer $l10n, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:870
MediaWiki\Preferences\DefaultPreferencesFactory\getTimezoneOptions
getTimezoneOptions(IContextSource $context)
Definition: DefaultPreferencesFactory.php:1467
MediaWiki\Preferences\DefaultPreferencesFactory\submitForm
submitForm(array $formData, HTMLForm $form, array $formDescriptor)
Save the form data and reload the page.
Definition: DefaultPreferencesFactory.php:1619
$wgDefaultUserOptions
$wgDefaultUserOptions
Settings added to this array will override the default globals for the user preferences used by anony...
Definition: DefaultSettings.php:4859
MediaWiki\Preferences\DefaultPreferencesFactory\profilePreferences
profilePreferences(User $user, IContextSource $context, &$defaultPreferences, $canIPUseHTTPS)
Definition: DefaultPreferencesFactory.php:243
StatusValue\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: StatusValue.php:68
MediaWiki\Preferences\DefaultPreferencesFactory\getOptionFromUser
getOptionFromUser( $name, $info, array $userOptions)
Pull option from a user account.
Definition: DefaultPreferencesFactory.php:197
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Definition: SpecialPage.php:82
$formDescriptor
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead & $formDescriptor
Definition: hooks.txt:2115
MediaWiki\Preferences\DefaultPreferencesFactory\renderingPreferences
renderingPreferences(MessageLocalizer $l10n, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:808
$res
$res
Definition: database.txt:21
User
User
Definition: All_system_messages.txt:425
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
MessageLocalizer
Interface for localizing messages in MediaWiki.
Definition: MessageLocalizer.php:28
User\getDefaultOptions
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Definition: User.php:1773
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:40
MediaWiki\Auth\PasswordAuthenticationRequest
This is a value object for authentication requests with a username and password.
Definition: PasswordAuthenticationRequest.php:29
MediaWiki\Preferences\DefaultPreferencesFactory\$authManager
AuthManager $authManager
Definition: DefaultPreferencesFactory.php:71
Config
Interface for configuration instances.
Definition: Config.php:28
MediaWiki\Preferences\DefaultPreferencesFactory\getThumbSizes
getThumbSizes(MessageLocalizer $l10n)
Definition: DefaultPreferencesFactory.php:1358
MWException
MediaWiki exception.
Definition: MWException.php:26
MediaWiki\Preferences
Definition: DefaultPreferencesFactory.php:21
MediaWiki\Preferences\DefaultPreferencesFactory\getImageSizes
getImageSizes(MessageLocalizer $l10n)
Definition: DefaultPreferencesFactory.php:1341
MediaWiki\Preferences\DefaultPreferencesFactory\getTimeZoneList
getTimeZoneList(Language $language)
Get a list of all time zones.
Definition: DefaultPreferencesFactory.php:1651
UserGroupMembership\getLink
static getLink( $ugm, IContextSource $context, $format, $userName=null)
Gets a link for a user group, possibly including the expiry date if relevant.
Definition: UserGroupMembership.php:373
MediaWiki\Preferences\DefaultPreferencesFactory\$linkRenderer
LinkRenderer $linkRenderer
Definition: DefaultPreferencesFactory.php:74
HTMLFormField
The parent class to generate form fields.
Definition: HTMLFormField.php:7
Xml\element
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:41
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
$code
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition: hooks.txt:813
MediaWiki\Preferences\DefaultPreferencesFactory\rcPreferences
rcPreferences(User $user, MessageLocalizer $l10n, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:937
MWNamespace
This is a utility class with only static functions for dealing with namespaces that encodes all the "...
Definition: MWNamespace.php:32
$parser
see documentation in includes Linker php for Linker::makeImageLink or false for current used if you return false $parser
Definition: hooks.txt:1841
MessageLocalizer\msg
msg( $key)
This is the method for getting translated interface messages.
MediaWiki\Preferences\DefaultPreferencesFactory\cleanSignature
cleanSignature( $signature, $alldata, HTMLForm $form)
Definition: DefaultPreferencesFactory.php:1401
$time
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition: hooks.txt:1841
MediaWiki\Preferences\DefaultPreferencesFactory\getForm
getForm(User $user, IContextSource $context, $formClass=PreferencesFormLegacy::class, array $remove=[])
Definition: DefaultPreferencesFactory.php:1420
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1983
MediaWiki\Preferences\DefaultPreferencesFactory\$contLang
Language $contLang
The wiki's content language.
Definition: DefaultPreferencesFactory.php:68
MediaWiki\Preferences\DefaultPreferencesFactory\filesPreferences
filesPreferences(IContextSource $context, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:709
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
HTMLForm\loadInputFromParameters
static loadInputFromParameters( $fieldname, $descriptor, HTMLForm $parent=null)
Initialise a new Object for the field.
Definition: HTMLForm.php:482
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:175
MediaWiki\Auth\AuthManager\callLegacyAuthPlugin
static callLegacyAuthPlugin( $method, array $params, $return=null)
Call a legacy AuthPlugin method, if necessary.
Definition: AuthManager.php:239
MediaWiki\Preferences\DefaultPreferencesFactory\searchPreferences
searchPreferences(&$defaultPreferences)
Definition: DefaultPreferencesFactory.php:1215
PreferencesFormLegacy
Form to edit user preferences.
Definition: PreferencesFormLegacy.php:26
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:795
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:120
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:573
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2213
$value
$value
Definition: styleTest.css.php:49
ParserOptions\newFromContext
static newFromContext(IContextSource $context)
Get a ParserOptions object from a IContextSource object.
Definition: ParserOptions.php:1040
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:36
MediaWiki\Preferences\PreferencesFactory
A PreferencesFactory is a MediaWiki service that provides the definitions of preferences for a given ...
Definition: PreferencesFactory.php:51
MediaWiki\Preferences\DefaultPreferencesFactory\getDateOptions
getDateOptions(IContextSource $context)
Definition: DefaultPreferencesFactory.php:1307
$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:2036
IContextSource\getUser
getUser()
IContextSource\getTitle
getTitle()
MediaWiki\Preferences\DefaultPreferencesFactory\$config
Config $config
Definition: DefaultPreferencesFactory.php:65
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:83
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
text
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second as well as the first line of the second redirect text
Definition: All_system_messages.txt:1267
Title
Represents a title within MediaWiki.
Definition: Title.php:39
MediaWiki\Preferences\DefaultPreferencesFactory\getSaveBlacklist
getSaveBlacklist()
@inheritDoc
Definition: DefaultPreferencesFactory.php:98
$rows
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction $rows
Definition: hooks.txt:2675
$options
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 & $options
Definition: hooks.txt:2036
MediaWiki\Preferences\DefaultPreferencesFactory\loadPreferenceValues
loadPreferenceValues(User $user, IContextSource $context, &$defaultPreferences)
Loads existing values for a given array of preferences.
Definition: DefaultPreferencesFactory.php:145
MediaWiki\Preferences\DefaultPreferencesFactory\datetimePreferences
datetimePreferences( $user, IContextSource $context, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:731
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
LanguageCode\bcp47
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: LanguageCode.php:182
NS_USER
const NS_USER
Definition: Defines.php:66
HTMLForm\getTitle
getTitle()
Get the title.
Definition: HTMLForm.php:1591
MediaWiki\Preferences\DefaultPreferencesFactory
This is the default implementation of PreferencesFactory.
Definition: DefaultPreferencesFactory.php:61
IContextSource\getRequest
getRequest()
LanguageCode
Methods for dealing with language codes.
Definition: LanguageCode.php:29
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:1139
SpecialWatchlist\checkStructuredFilterUiEnabled
static checkStructuredFilterUiEnabled(Config $config, User $user)
Static method to check whether StructuredFilter UI is enabled for the given user.
Definition: SpecialWatchlist.php:113
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
$t
$t
Definition: testCompression.php:69
MediaWikiServices
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 MediaWikiServices
Definition: injection.txt:23
Skin\getAllowedSkins
static getAllowedSkins()
Fetch the list of user-selectable skins in regards to $wgSkipSkins.
Definition: Skin.php:80
Skin
The main skin class which provides methods and properties for all other skins.
Definition: Skin.php:38
MediaWiki\$context
IContextSource $context
Definition: MediaWiki.php:38
Language\fetchLanguageNames
static fetchLanguageNames( $inLanguage=self::AS_AUTONYMS, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:843
MediaWiki\Preferences\DefaultPreferencesFactory\validateSignature
validateSignature( $signature, $alldata, HTMLForm $form)
Definition: DefaultPreferencesFactory.php:1376
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
MediaWiki\Preferences\DefaultPreferencesFactory\generateSkinOptions
generateSkinOptions(User $user, IContextSource $context)
Definition: DefaultPreferencesFactory.php:1228
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:47
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
MediaWiki\Preferences\Filter
Base interface for user preference flters that work as a middleware between storage and interface.
Definition: Filter.php:27
IContextSource\getOutput
getOutput()
MWTimestamp\getLocalInstance
static getLocalInstance( $ts=false)
Get a timestamp instance in the server local timezone ($wgLocaltimezone)
Definition: MWTimestamp.php:204
Xml
Module of static functions for generating XML.
Definition: Xml.php:28
Language
Internationalisation code.
Definition: Language.php:35
MediaWiki\Preferences\DefaultPreferencesFactory\skinPreferences
skinPreferences(User $user, IContextSource $context, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:661
MediaWiki\Preferences\DefaultPreferencesFactory\getFormDescriptor
getFormDescriptor(User $user, IContextSource $context)
Definition: DefaultPreferencesFactory.php:111
Hooks
Hooks class.
Definition: Hooks.php:34
UserGroupMembership
Represents a "user group membership" – a specific instance of a user belonging to a group.
Definition: UserGroupMembership.php:37
IContextSource\getLanguage
getLanguage()
Html
This class is a collection of static functions that serve two purposes:
Definition: Html.php:49
HTMLForm
Object handling generic submission, CSRF protection, layout and other logic for UI forms.
Definition: HTMLForm.php:136