MediaWiki  1.34.4
DefaultPreferencesFactory.php
Go to the documentation of this file.
1 <?php
22 
23 use DateTime;
24 use DateTimeZone;
25 use Exception;
26 use Hooks;
27 use Html;
28 use HTMLForm;
29 use HTMLFormField;
30 use IContextSource;
31 use Language;
32 use LanguageCode;
41 use MWException;
42 use MWTimestamp;
43 use NamespaceInfo;
44 use OutputPage;
45 use Parser;
46 use ParserOptions;
48 use Psr\Log\LoggerAwareTrait;
49 use Psr\Log\NullLogger;
50 use Skin;
51 use SpecialPage;
52 use Status;
53 use Title;
54 use UnexpectedValueException;
55 use User;
57 use Xml;
58 
63  use LoggerAwareTrait;
64 
66  protected $options;
67 
69  protected $contLang;
70 
72  protected $authManager;
73 
75  protected $linkRenderer;
76 
78  protected $nsInfo;
79 
81  protected $permissionManager;
82 
87  public const CONSTRUCTOR_OPTIONS = [
88  'AllowRequiringEmailForResets',
89  'AllowUserCss',
90  'AllowUserCssPrefs',
91  'AllowUserJs',
92  'DefaultSkin',
93  'DisableLangConversion',
94  'EmailAuthentication',
95  'EmailConfirmToEdit',
96  'EnableEmail',
97  'EnableUserEmail',
98  'EnableUserEmailBlacklist',
99  'EnotifMinorEdits',
100  'EnotifRevealEditorAddress',
101  'EnotifUserTalk',
102  'EnotifWatchlist',
103  'ForceHTTPS',
104  'HiddenPrefs',
105  'ImageLimits',
106  'LanguageCode',
107  'LocalTZoffset',
108  'MaxSigChars',
109  'RCMaxAge',
110  'RCShowWatchingUsers',
111  'RCWatchCategoryMembership',
112  'SecureLogin',
113  'ThumbLimits',
114  ];
115 
126  public function __construct(
133  ) {
134  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
135 
136  $this->options = $options;
137  $this->contLang = $contLang;
138  $this->authManager = $authManager;
139  $this->linkRenderer = $linkRenderer;
140  $this->nsInfo = $nsInfo;
141  $this->permissionManager = $permissionManager;
142  $this->logger = new NullLogger();
143  }
144 
148  public function getSaveBlacklist() {
149  return [
150  'realname',
151  'emailaddress',
152  ];
153  }
154 
161  public function getFormDescriptor( User $user, IContextSource $context ) {
162  $preferences = [];
163 
165  strtolower( $context->getSkin()->getSkinName() ),
166  $context->getLanguage()->getDir()
167  );
168 
169  $canIPUseHTTPS = wfCanIPUseHTTPS( $context->getRequest()->getIP() );
170  $this->profilePreferences( $user, $context, $preferences, $canIPUseHTTPS );
171  $this->skinPreferences( $user, $context, $preferences );
172  $this->datetimePreferences( $user, $context, $preferences );
173  $this->filesPreferences( $context, $preferences );
174  $this->renderingPreferences( $user, $context, $preferences );
175  $this->editingPreferences( $user, $context, $preferences );
176  $this->rcPreferences( $user, $context, $preferences );
177  $this->watchlistPreferences( $user, $context, $preferences );
178  $this->searchPreferences( $preferences );
179 
180  Hooks::run( 'GetPreferences', [ $user, &$preferences ] );
181 
182  $this->loadPreferenceValues( $user, $context, $preferences );
183  $this->logger->debug( "Created form descriptor for user '{$user->getName()}'" );
184  return $preferences;
185  }
186 
195  private function loadPreferenceValues(
196  User $user, IContextSource $context, &$defaultPreferences
197  ) {
198  # # Remove preferences that wikis don't want to use
199  foreach ( $this->options->get( 'HiddenPrefs' ) as $pref ) {
200  if ( isset( $defaultPreferences[$pref] ) ) {
201  unset( $defaultPreferences[$pref] );
202  }
203  }
204 
205  # # Make sure that form fields have their parent set. See T43337.
206  $dummyForm = new HTMLForm( [], $context );
207 
208  $disable = !$this->permissionManager->userHasRight( $user, 'editmyoptions' );
209 
210  $defaultOptions = User::getDefaultOptions();
211  $userOptions = $user->getOptions();
212  $this->applyFilters( $userOptions, $defaultPreferences, 'filterForForm' );
213  # # Prod in defaults from the user
214  foreach ( $defaultPreferences as $name => &$info ) {
215  $prefFromUser = $this->getOptionFromUser( $name, $info, $userOptions );
216  if ( $disable && !in_array( $name, $this->getSaveBlacklist() ) ) {
217  $info['disabled'] = 'disabled';
218  }
219  $field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm ); // For validation
220  $globalDefault = $defaultOptions[$name] ?? null;
221 
222  // If it validates, set it as the default
223  if ( isset( $info['default'] ) ) {
224  // Already set, no problem
225  continue;
226  } elseif ( !is_null( $prefFromUser ) && // Make sure we're not just pulling nothing
227  $field->validate( $prefFromUser, $user->getOptions() ) === true ) {
228  $info['default'] = $prefFromUser;
229  } elseif ( $field->validate( $globalDefault, $user->getOptions() ) === true ) {
230  $info['default'] = $globalDefault;
231  } else {
232  $globalDefault = json_encode( $globalDefault );
233  throw new MWException(
234  "Default '$globalDefault' is invalid for preference $name of user $user"
235  );
236  }
237  }
238 
239  return $defaultPreferences;
240  }
241 
250  protected function getOptionFromUser( $name, $info, array $userOptions ) {
251  $val = $userOptions[$name] ?? null;
252 
253  // Handling for multiselect preferences
254  if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
255  ( isset( $info['class'] ) && $info['class'] == \HTMLMultiSelectField::class ) ) {
256  $options = HTMLFormField::flattenOptions( $info['options'] );
257  $prefix = $info['prefix'] ?? $name;
258  $val = [];
259 
260  foreach ( $options as $value ) {
261  if ( $userOptions["$prefix$value"] ?? false ) {
262  $val[] = $value;
263  }
264  }
265  }
266 
267  // Handling for checkmatrix preferences
268  if ( ( isset( $info['type'] ) && $info['type'] == 'checkmatrix' ) ||
269  ( isset( $info['class'] ) && $info['class'] == \HTMLCheckMatrix::class ) ) {
270  $columns = HTMLFormField::flattenOptions( $info['columns'] );
271  $rows = HTMLFormField::flattenOptions( $info['rows'] );
272  $prefix = $info['prefix'] ?? $name;
273  $val = [];
274 
275  foreach ( $columns as $column ) {
276  foreach ( $rows as $row ) {
277  if ( $userOptions["$prefix$column-$row"] ?? false ) {
278  $val[] = "$column-$row";
279  }
280  }
281  }
282  }
283 
284  return $val;
285  }
286 
296  protected function profilePreferences(
297  User $user, IContextSource $context, &$defaultPreferences, $canIPUseHTTPS
298  ) {
299  // retrieving user name for GENDER and misc.
300  $userName = $user->getName();
301 
302  # # User info #####################################
303  // Information panel
304  $defaultPreferences['username'] = [
305  'type' => 'info',
306  'label-message' => [ 'username', $userName ],
307  'default' => $userName,
308  'section' => 'personal/info',
309  ];
310 
312 
313  # Get groups to which the user belongs
314  $userEffectiveGroups = $user->getEffectiveGroups();
315  $userGroupMemberships = $user->getGroupMemberships();
316  $userGroups = $userMembers = $userTempGroups = $userTempMembers = [];
317  foreach ( $userEffectiveGroups as $ueg ) {
318  if ( $ueg == '*' ) {
319  // Skip the default * group, seems useless here
320  continue;
321  }
322 
323  $groupStringOrObject = $userGroupMemberships[$ueg] ?? $ueg;
324 
325  $userG = UserGroupMembership::getLink( $groupStringOrObject, $context, 'html' );
326  $userM = UserGroupMembership::getLink( $groupStringOrObject, $context, 'html',
327  $userName );
328 
329  // Store expiring groups separately, so we can place them before non-expiring
330  // groups in the list. This is to avoid the ambiguity of something like
331  // "administrator, bureaucrat (until X date)" -- users might wonder whether the
332  // expiry date applies to both groups, or just the last one
333  if ( $groupStringOrObject instanceof UserGroupMembership &&
334  $groupStringOrObject->getExpiry()
335  ) {
336  $userTempGroups[] = $userG;
337  $userTempMembers[] = $userM;
338  } else {
339  $userGroups[] = $userG;
340  $userMembers[] = $userM;
341  }
342  }
343  sort( $userGroups );
344  sort( $userMembers );
345  sort( $userTempGroups );
346  sort( $userTempMembers );
347  $userGroups = array_merge( $userTempGroups, $userGroups );
348  $userMembers = array_merge( $userTempMembers, $userMembers );
349 
350  $defaultPreferences['usergroups'] = [
351  'type' => 'info',
352  'label' => $context->msg( 'prefs-memberingroups' )->numParams(
353  count( $userGroups ) )->params( $userName )->parse(),
354  'default' => $context->msg( 'prefs-memberingroups-type' )
355  ->rawParams( $lang->commaList( $userGroups ), $lang->commaList( $userMembers ) )
356  ->escaped(),
357  'raw' => true,
358  'section' => 'personal/info',
359  ];
360 
361  $contribTitle = SpecialPage::getTitleFor( "Contributions", $userName );
362  $formattedEditCount = $lang->formatNum( $user->getEditCount() );
363  $editCount = $this->linkRenderer->makeLink( $contribTitle, $formattedEditCount );
364 
365  $defaultPreferences['editcount'] = [
366  'type' => 'info',
367  'raw' => true,
368  'label-message' => 'prefs-edits',
369  'default' => $editCount,
370  'section' => 'personal/info',
371  ];
372 
373  if ( $user->getRegistration() ) {
374  $displayUser = $context->getUser();
375  $userRegistration = $user->getRegistration();
376  $defaultPreferences['registrationdate'] = [
377  'type' => 'info',
378  'label-message' => 'prefs-registration',
379  'default' => $context->msg(
380  'prefs-registration-date-time',
381  $lang->userTimeAndDate( $userRegistration, $displayUser ),
382  $lang->userDate( $userRegistration, $displayUser ),
383  $lang->userTime( $userRegistration, $displayUser )
384  )->text(),
385  'section' => 'personal/info',
386  ];
387  }
388 
389  $canViewPrivateInfo = $this->permissionManager->userHasRight( $user, 'viewmyprivateinfo' );
390  $canEditPrivateInfo = $this->permissionManager->userHasRight( $user, 'editmyprivateinfo' );
391 
392  // Actually changeable stuff
393  $defaultPreferences['realname'] = [
394  // (not really "private", but still shouldn't be edited without permission)
395  'type' => $canEditPrivateInfo && $this->authManager->allowsPropertyChange( 'realname' )
396  ? 'text' : 'info',
397  'default' => $user->getRealName(),
398  'section' => 'personal/info',
399  'label-message' => 'yourrealname',
400  'help-message' => 'prefs-help-realname',
401  ];
402 
403  if ( $canEditPrivateInfo && $this->authManager->allowsAuthenticationDataChange(
404  new PasswordAuthenticationRequest(), false )->isGood()
405  ) {
406  $defaultPreferences['password'] = [
407  'type' => 'info',
408  'raw' => true,
409  'default' => (string)new \OOUI\ButtonWidget( [
410  'href' => SpecialPage::getTitleFor( 'ChangePassword' )->getLinkURL( [
411  'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
412  ] ),
413  'label' => $context->msg( 'prefs-resetpass' )->text(),
414  ] ),
415  'label-message' => 'yourpassword',
416  'section' => 'personal/info',
417  ];
418  }
419  // Only show prefershttps if secure login is turned on
420  if ( !$this->options->get( 'ForceHTTPS' )
421  && $this->options->get( 'SecureLogin' )
422  && $canIPUseHTTPS
423  ) {
424  $defaultPreferences['prefershttps'] = [
425  'type' => 'toggle',
426  'label-message' => 'tog-prefershttps',
427  'help-message' => 'prefs-help-prefershttps',
428  'section' => 'personal/info'
429  ];
430  }
431 
432  $languages = Language::fetchLanguageNames( null, 'mwfile' );
433  $languageCode = $this->options->get( 'LanguageCode' );
434  if ( !array_key_exists( $languageCode, $languages ) ) {
435  $languages[$languageCode] = $languageCode;
436  // Sort the array again
437  ksort( $languages );
438  }
439 
440  $options = [];
441  foreach ( $languages as $code => $name ) {
442  $display = LanguageCode::bcp47( $code ) . ' - ' . $name;
443  $options[$display] = $code;
444  }
445  $defaultPreferences['language'] = [
446  'type' => 'select',
447  'section' => 'personal/i18n',
448  'options' => $options,
449  'label-message' => 'yourlanguage',
450  ];
451 
452  $defaultPreferences['gender'] = [
453  'type' => 'radio',
454  'section' => 'personal/i18n',
455  'options' => [
456  $context->msg( 'parentheses' )
457  ->params( $context->msg( 'gender-unknown' )->plain() )
458  ->escaped() => 'unknown',
459  $context->msg( 'gender-female' )->escaped() => 'female',
460  $context->msg( 'gender-male' )->escaped() => 'male',
461  ],
462  'label-message' => 'yourgender',
463  'help-message' => 'prefs-help-gender',
464  ];
465 
466  // see if there are multiple language variants to choose from
467  if ( !$this->options->get( 'DisableLangConversion' ) ) {
468  foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
469  if ( $langCode == $this->contLang->getCode() ) {
470  if ( !$this->contLang->hasVariants() ) {
471  continue;
472  }
473 
474  $variants = $this->contLang->getVariants();
475  $variantArray = [];
476  foreach ( $variants as $v ) {
477  $v = str_replace( '_', '-', strtolower( $v ) );
478  $variantArray[$v] = $lang->getVariantname( $v, false );
479  }
480 
481  $options = [];
482  foreach ( $variantArray as $code => $name ) {
483  $display = LanguageCode::bcp47( $code ) . ' - ' . $name;
484  $options[$display] = $code;
485  }
486 
487  $defaultPreferences['variant'] = [
488  'label-message' => 'yourvariant',
489  'type' => 'select',
490  'options' => $options,
491  'section' => 'personal/i18n',
492  'help-message' => 'prefs-help-variant',
493  ];
494  } else {
495  $defaultPreferences["variant-$langCode"] = [
496  'type' => 'api',
497  ];
498  }
499  }
500  }
501 
502  // show a preview of the old signature first
503  $oldsigWikiText = MediaWikiServices::getInstance()->getParser()->preSaveTransform(
504  '~~~',
505  $context->getTitle(),
506  $user,
508  );
509  $oldsigHTML = Parser::stripOuterParagraph(
510  $context->getOutput()->parseAsContent( $oldsigWikiText )
511  );
512  $defaultPreferences['oldsig'] = [
513  'type' => 'info',
514  'raw' => true,
515  'label-message' => 'tog-oldsig',
516  'default' => $oldsigHTML,
517  'section' => 'personal/signature',
518  ];
519  $defaultPreferences['nickname'] = [
520  'type' => $this->authManager->allowsPropertyChange( 'nickname' ) ? 'text' : 'info',
521  'maxlength' => $this->options->get( 'MaxSigChars' ),
522  'label-message' => 'yournick',
523  'validation-callback' => function ( $signature, $alldata, HTMLForm $form ) {
524  return $this->validateSignature( $signature, $alldata, $form );
525  },
526  'section' => 'personal/signature',
527  'filter-callback' => function ( $signature, array $alldata, HTMLForm $form ) {
528  return $this->cleanSignature( $signature, $alldata, $form );
529  },
530  ];
531  $defaultPreferences['fancysig'] = [
532  'type' => 'toggle',
533  'label-message' => 'tog-fancysig',
534  // show general help about signature at the bottom of the section
535  'help-message' => 'prefs-help-signature',
536  'section' => 'personal/signature'
537  ];
538 
539  # # Email stuff
540 
541  if ( $this->options->get( 'EnableEmail' ) ) {
542  if ( $canViewPrivateInfo ) {
543  $helpMessages = [];
544  $helpMessages[] = $this->options->get( 'EmailConfirmToEdit' )
545  ? 'prefs-help-email-required'
546  : 'prefs-help-email';
547 
548  if ( $this->options->get( 'EnableUserEmail' ) ) {
549  // additional messages when users can send email to each other
550  $helpMessages[] = 'prefs-help-email-others';
551  }
552 
553  $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : '';
554  if ( $canEditPrivateInfo && $this->authManager->allowsPropertyChange( 'emailaddress' ) ) {
555  $button = new \OOUI\ButtonWidget( [
556  'href' => SpecialPage::getTitleFor( 'ChangeEmail' )->getLinkURL( [
557  'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
558  ] ),
559  'label' =>
560  $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->text(),
561  ] );
562 
563  $emailAddress .= $emailAddress == '' ? $button : ( '<br />' . $button );
564  }
565 
566  $defaultPreferences['emailaddress'] = [
567  'type' => 'info',
568  'raw' => true,
569  'default' => $emailAddress,
570  'label-message' => 'youremail',
571  'section' => 'personal/email',
572  'help-messages' => $helpMessages,
573  # 'cssclass' chosen below
574  ];
575  }
576 
577  $disableEmailPrefs = false;
578 
579  if ( $this->options->get( 'EmailAuthentication' ) ) {
580  $emailauthenticationclass = 'mw-email-not-authenticated';
581  if ( $user->getEmail() ) {
582  if ( $user->getEmailAuthenticationTimestamp() ) {
583  // date and time are separate parameters to facilitate localisation.
584  // $time is kept for backward compat reasons.
585  // 'emailauthenticated' is also used in SpecialConfirmemail.php
586  $displayUser = $context->getUser();
587  $emailTimestamp = $user->getEmailAuthenticationTimestamp();
588  $time = $lang->userTimeAndDate( $emailTimestamp, $displayUser );
589  $d = $lang->userDate( $emailTimestamp, $displayUser );
590  $t = $lang->userTime( $emailTimestamp, $displayUser );
591  $emailauthenticated = $context->msg( 'emailauthenticated',
592  $time, $d, $t )->parse() . '<br />';
593  $disableEmailPrefs = false;
594  $emailauthenticationclass = 'mw-email-authenticated';
595  } else {
596  $disableEmailPrefs = true;
597  $emailauthenticated = $context->msg( 'emailnotauthenticated' )->parse() . '<br />' .
598  new \OOUI\ButtonWidget( [
599  'href' => SpecialPage::getTitleFor( 'Confirmemail' )->getLinkURL(),
600  'label' => $context->msg( 'emailconfirmlink' )->text(),
601  ] );
602  $emailauthenticationclass = "mw-email-not-authenticated";
603  }
604  } else {
605  $disableEmailPrefs = true;
606  $emailauthenticated = $context->msg( 'noemailprefs' )->escaped();
607  $emailauthenticationclass = 'mw-email-none';
608  }
609 
610  if ( $canViewPrivateInfo ) {
611  $defaultPreferences['emailauthentication'] = [
612  'type' => 'info',
613  'raw' => true,
614  'section' => 'personal/email',
615  'label-message' => 'prefs-emailconfirm-label',
616  'default' => $emailauthenticated,
617  # Apply the same CSS class used on the input to the message:
618  'cssclass' => $emailauthenticationclass,
619  ];
620  }
621  }
622 
623  if ( $this->options->get( 'AllowRequiringEmailForResets' ) ) {
624  $defaultPreferences['requireemail'] = [
625  'type' => 'toggle',
626  'label-message' => 'tog-requireemail',
627  'help-message' => 'prefs-help-requireemail',
628  'section' => 'personal/email',
629  'disabled' => $disableEmailPrefs,
630  ];
631  }
632 
633  if ( $this->options->get( 'EnableUserEmail' ) &&
634  $this->permissionManager->userHasRight( $user, 'sendemail' )
635  ) {
636  $defaultPreferences['disablemail'] = [
637  'id' => 'wpAllowEmail',
638  'type' => 'toggle',
639  'invert' => true,
640  'section' => 'personal/email',
641  'label-message' => 'allowemail',
642  'disabled' => $disableEmailPrefs,
643  ];
644 
645  $defaultPreferences['email-allow-new-users'] = [
646  'id' => 'wpAllowEmailFromNewUsers',
647  'type' => 'toggle',
648  'section' => 'personal/email',
649  'label-message' => 'email-allow-new-users-label',
650  'disabled' => $disableEmailPrefs,
651  ];
652 
653  $defaultPreferences['ccmeonemails'] = [
654  'type' => 'toggle',
655  'section' => 'personal/email',
656  'label-message' => 'tog-ccmeonemails',
657  'disabled' => $disableEmailPrefs,
658  ];
659 
660  if ( $this->options->get( 'EnableUserEmailBlacklist' ) ) {
661  $defaultPreferences['email-blacklist'] = [
662  'type' => 'usersmultiselect',
663  'label-message' => 'email-blacklist-label',
664  'section' => 'personal/email',
665  'disabled' => $disableEmailPrefs,
666  'filter' => MultiUsernameFilter::class,
667  ];
668  }
669  }
670 
671  if ( $this->options->get( 'EnotifWatchlist' ) ) {
672  $defaultPreferences['enotifwatchlistpages'] = [
673  'type' => 'toggle',
674  'section' => 'personal/email',
675  'label-message' => 'tog-enotifwatchlistpages',
676  'disabled' => $disableEmailPrefs,
677  ];
678  }
679  if ( $this->options->get( 'EnotifUserTalk' ) ) {
680  $defaultPreferences['enotifusertalkpages'] = [
681  'type' => 'toggle',
682  'section' => 'personal/email',
683  'label-message' => 'tog-enotifusertalkpages',
684  'disabled' => $disableEmailPrefs,
685  ];
686  }
687  if ( $this->options->get( 'EnotifUserTalk' ) ||
688  $this->options->get( 'EnotifWatchlist' ) ) {
689  if ( $this->options->get( 'EnotifMinorEdits' ) ) {
690  $defaultPreferences['enotifminoredits'] = [
691  'type' => 'toggle',
692  'section' => 'personal/email',
693  'label-message' => 'tog-enotifminoredits',
694  'disabled' => $disableEmailPrefs,
695  ];
696  }
697 
698  if ( $this->options->get( 'EnotifRevealEditorAddress' ) ) {
699  $defaultPreferences['enotifrevealaddr'] = [
700  'type' => 'toggle',
701  'section' => 'personal/email',
702  'label-message' => 'tog-enotifrevealaddr',
703  'disabled' => $disableEmailPrefs,
704  ];
705  }
706  }
707  }
708  }
709 
716  protected function skinPreferences( User $user, IContextSource $context, &$defaultPreferences ) {
717  # # Skin #####################################
718 
719  // Skin selector, if there is at least one valid skin
720  $skinOptions = $this->generateSkinOptions( $user, $context );
721  if ( $skinOptions ) {
722  $defaultPreferences['skin'] = [
723  'type' => 'radio',
724  'options' => $skinOptions,
725  'section' => 'rendering/skin',
726  ];
727  }
728 
729  $allowUserCss = $this->options->get( 'AllowUserCss' );
730  $allowUserJs = $this->options->get( 'AllowUserJs' );
731  # Create links to user CSS/JS pages for all skins
732  # This code is basically copied from generateSkinOptions(). It'd
733  # be nice to somehow merge this back in there to avoid redundancy.
734  if ( $allowUserCss || $allowUserJs ) {
735  $linkTools = [];
736  $userName = $user->getName();
737 
738  if ( $allowUserCss ) {
739  $cssPage = Title::makeTitleSafe( NS_USER, $userName . '/common.css' );
740  $cssLinkText = $context->msg( 'prefs-custom-css' )->text();
741  $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
742  }
743 
744  if ( $allowUserJs ) {
745  $jsPage = Title::makeTitleSafe( NS_USER, $userName . '/common.js' );
746  $jsLinkText = $context->msg( 'prefs-custom-js' )->text();
747  $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
748  }
749 
750  $defaultPreferences['commoncssjs'] = [
751  'type' => 'info',
752  'raw' => true,
753  'default' => $context->getLanguage()->pipeList( $linkTools ),
754  'label-message' => 'prefs-common-config',
755  'section' => 'rendering/skin',
756  ];
757  }
758  }
759 
764  protected function filesPreferences( IContextSource $context, &$defaultPreferences ) {
765  # # Files #####################################
766  $defaultPreferences['imagesize'] = [
767  'type' => 'select',
768  'options' => $this->getImageSizes( $context ),
769  'label-message' => 'imagemaxsize',
770  'section' => 'rendering/files',
771  ];
772  $defaultPreferences['thumbsize'] = [
773  'type' => 'select',
774  'options' => $this->getThumbSizes( $context ),
775  'label-message' => 'thumbsize',
776  'section' => 'rendering/files',
777  ];
778  }
779 
786  protected function datetimePreferences( $user, IContextSource $context, &$defaultPreferences ) {
787  # # Date and time #####################################
788  $dateOptions = $this->getDateOptions( $context );
789  if ( $dateOptions ) {
790  $defaultPreferences['date'] = [
791  'type' => 'radio',
792  'options' => $dateOptions,
793  'section' => 'rendering/dateformat',
794  ];
795  }
796 
797  // Info
798  $now = wfTimestampNow();
800  $nowlocal = Xml::element( 'span', [ 'id' => 'wpLocalTime' ],
801  $lang->userTime( $now, $user ) );
802  $nowserver = $lang->userTime( $now, $user,
803  [ 'format' => false, 'timecorrection' => false ] ) .
804  Html::hidden( 'wpServerTime', (int)substr( $now, 8, 2 ) * 60 + (int)substr( $now, 10, 2 ) );
805 
806  $defaultPreferences['nowserver'] = [
807  'type' => 'info',
808  'raw' => 1,
809  'label-message' => 'servertime',
810  'default' => $nowserver,
811  'section' => 'rendering/timeoffset',
812  ];
813 
814  $defaultPreferences['nowlocal'] = [
815  'type' => 'info',
816  'raw' => 1,
817  'label-message' => 'localtime',
818  'default' => $nowlocal,
819  'section' => 'rendering/timeoffset',
820  ];
821 
822  // Grab existing pref.
823  $tzOffset = $user->getOption( 'timecorrection' );
824  $tz = explode( '|', $tzOffset, 3 );
825 
826  $tzOptions = $this->getTimezoneOptions( $context );
827 
828  $tzSetting = $tzOffset;
829  if ( count( $tz ) > 1 && $tz[0] == 'ZoneInfo' &&
830  !in_array( $tzOffset, HTMLFormField::flattenOptions( $tzOptions ) )
831  ) {
832  // Timezone offset can vary with DST
833  try {
834  $userTZ = new DateTimeZone( $tz[2] );
835  $minDiff = floor( $userTZ->getOffset( new DateTime( 'now' ) ) / 60 );
836  $tzSetting = "ZoneInfo|$minDiff|{$tz[2]}";
837  } catch ( Exception $e ) {
838  // User has an invalid time zone set. Fall back to just using the offset
839  $tz[0] = 'Offset';
840  }
841  }
842  if ( count( $tz ) > 1 && $tz[0] == 'Offset' ) {
843  $minDiff = $tz[1];
844  $tzSetting = sprintf( '%+03d:%02d', floor( $minDiff / 60 ), abs( $minDiff ) % 60 );
845  }
846 
847  $defaultPreferences['timecorrection'] = [
848  'class' => \HTMLSelectOrOtherField::class,
849  'label-message' => 'timezonelegend',
850  'options' => $tzOptions,
851  'default' => $tzSetting,
852  'size' => 20,
853  'section' => 'rendering/timeoffset',
854  'id' => 'wpTimeCorrection',
855  'filter' => TimezoneFilter::class,
856  'placeholder-message' => 'timezone-useoffset-placeholder',
857  ];
858  }
859 
865  protected function renderingPreferences(
866  User $user,
867  MessageLocalizer $l10n,
868  &$defaultPreferences
869  ) {
870  # # Diffs ####################################
871  $defaultPreferences['diffonly'] = [
872  'type' => 'toggle',
873  'section' => 'rendering/diffs',
874  'label-message' => 'tog-diffonly',
875  ];
876  $defaultPreferences['norollbackdiff'] = [
877  'type' => 'toggle',
878  'section' => 'rendering/diffs',
879  'label-message' => 'tog-norollbackdiff',
880  ];
881 
882  # # Page Rendering ##############################
883  if ( $this->options->get( 'AllowUserCssPrefs' ) ) {
884  $defaultPreferences['underline'] = [
885  'type' => 'select',
886  'options' => [
887  $l10n->msg( 'underline-never' )->text() => 0,
888  $l10n->msg( 'underline-always' )->text() => 1,
889  $l10n->msg( 'underline-default' )->text() => 2,
890  ],
891  'label-message' => 'tog-underline',
892  'section' => 'rendering/advancedrendering',
893  ];
894  }
895 
896  $stubThresholdValues = [ 50, 100, 500, 1000, 2000, 5000, 10000 ];
897  $stubThresholdOptions = [ $l10n->msg( 'stub-threshold-disabled' )->text() => 0 ];
898  foreach ( $stubThresholdValues as $value ) {
899  $stubThresholdOptions[$l10n->msg( 'size-bytes', $value )->text()] = $value;
900  }
901 
902  $defaultPreferences['stubthreshold'] = [
903  'type' => 'select',
904  'section' => 'rendering/advancedrendering',
905  'options' => $stubThresholdOptions,
906  // This is not a raw HTML message; label-raw is needed for the manual <a></a>
907  'label-raw' => $l10n->msg( 'stub-threshold' )->rawParams(
908  '<a class="stub">' .
909  $l10n->msg( 'stub-threshold-sample-link' )->parse() .
910  '</a>' )->parse(),
911  ];
912 
913  $defaultPreferences['showhiddencats'] = [
914  'type' => 'toggle',
915  'section' => 'rendering/advancedrendering',
916  'label-message' => 'tog-showhiddencats'
917  ];
918 
919  $defaultPreferences['numberheadings'] = [
920  'type' => 'toggle',
921  'section' => 'rendering/advancedrendering',
922  'label-message' => 'tog-numberheadings',
923  ];
924 
925  if ( $this->permissionManager->userHasRight( $user, 'rollback' ) ) {
926  $defaultPreferences['showrollbackconfirmation'] = [
927  'type' => 'toggle',
928  'section' => 'rendering/advancedrendering',
929  'label-message' => 'tog-showrollbackconfirmation',
930  ];
931  }
932  }
933 
939  protected function editingPreferences( User $user, MessageLocalizer $l10n, &$defaultPreferences ) {
940  # # Editing #####################################
941  $defaultPreferences['editsectiononrightclick'] = [
942  'type' => 'toggle',
943  'section' => 'editing/advancedediting',
944  'label-message' => 'tog-editsectiononrightclick',
945  ];
946  $defaultPreferences['editondblclick'] = [
947  'type' => 'toggle',
948  'section' => 'editing/advancedediting',
949  'label-message' => 'tog-editondblclick',
950  ];
951 
952  if ( $this->options->get( 'AllowUserCssPrefs' ) ) {
953  $defaultPreferences['editfont'] = [
954  'type' => 'select',
955  'section' => 'editing/editor',
956  'label-message' => 'editfont-style',
957  'options' => [
958  $l10n->msg( 'editfont-monospace' )->text() => 'monospace',
959  $l10n->msg( 'editfont-sansserif' )->text() => 'sans-serif',
960  $l10n->msg( 'editfont-serif' )->text() => 'serif',
961  ]
962  ];
963  }
964 
965  if ( $this->permissionManager->userHasRight( $user, 'minoredit' ) ) {
966  $defaultPreferences['minordefault'] = [
967  'type' => 'toggle',
968  'section' => 'editing/editor',
969  'label-message' => 'tog-minordefault',
970  ];
971  }
972 
973  $defaultPreferences['forceeditsummary'] = [
974  'type' => 'toggle',
975  'section' => 'editing/editor',
976  'label-message' => 'tog-forceeditsummary',
977  ];
978  $defaultPreferences['useeditwarning'] = [
979  'type' => 'toggle',
980  'section' => 'editing/editor',
981  'label-message' => 'tog-useeditwarning',
982  ];
983 
984  $defaultPreferences['previewonfirst'] = [
985  'type' => 'toggle',
986  'section' => 'editing/preview',
987  'label-message' => 'tog-previewonfirst',
988  ];
989  $defaultPreferences['previewontop'] = [
990  'type' => 'toggle',
991  'section' => 'editing/preview',
992  'label-message' => 'tog-previewontop',
993  ];
994  $defaultPreferences['uselivepreview'] = [
995  'type' => 'toggle',
996  'section' => 'editing/preview',
997  'label-message' => 'tog-uselivepreview',
998  ];
999  }
1000 
1006  protected function rcPreferences( User $user, MessageLocalizer $l10n, &$defaultPreferences ) {
1007  $rcMaxAge = $this->options->get( 'RCMaxAge' );
1008  # # RecentChanges #####################################
1009  $defaultPreferences['rcdays'] = [
1010  'type' => 'float',
1011  'label-message' => 'recentchangesdays',
1012  'section' => 'rc/displayrc',
1013  'min' => 1 / 24,
1014  'max' => ceil( $rcMaxAge / ( 3600 * 24 ) ),
1015  'help' => $l10n->msg( 'recentchangesdays-max' )->numParams(
1016  ceil( $rcMaxAge / ( 3600 * 24 ) ) )->escaped()
1017  ];
1018  $defaultPreferences['rclimit'] = [
1019  'type' => 'int',
1020  'min' => 1,
1021  'max' => 1000,
1022  'label-message' => 'recentchangescount',
1023  'help-message' => 'prefs-help-recentchangescount',
1024  'section' => 'rc/displayrc',
1025  'filter' => IntvalFilter::class,
1026  ];
1027  $defaultPreferences['usenewrc'] = [
1028  'type' => 'toggle',
1029  'label-message' => 'tog-usenewrc',
1030  'section' => 'rc/advancedrc',
1031  ];
1032  $defaultPreferences['hideminor'] = [
1033  'type' => 'toggle',
1034  'label-message' => 'tog-hideminor',
1035  'section' => 'rc/changesrc',
1036  ];
1037  $defaultPreferences['rcfilters-rc-collapsed'] = [
1038  'type' => 'api',
1039  ];
1040  $defaultPreferences['rcfilters-wl-collapsed'] = [
1041  'type' => 'api',
1042  ];
1043  $defaultPreferences['rcfilters-saved-queries'] = [
1044  'type' => 'api',
1045  ];
1046  $defaultPreferences['rcfilters-wl-saved-queries'] = [
1047  'type' => 'api',
1048  ];
1049  // Override RCFilters preferences for RecentChanges 'limit'
1050  $defaultPreferences['rcfilters-limit'] = [
1051  'type' => 'api',
1052  ];
1053  $defaultPreferences['rcfilters-saved-queries-versionbackup'] = [
1054  'type' => 'api',
1055  ];
1056  $defaultPreferences['rcfilters-wl-saved-queries-versionbackup'] = [
1057  'type' => 'api',
1058  ];
1059 
1060  if ( $this->options->get( 'RCWatchCategoryMembership' ) ) {
1061  $defaultPreferences['hidecategorization'] = [
1062  'type' => 'toggle',
1063  'label-message' => 'tog-hidecategorization',
1064  'section' => 'rc/changesrc',
1065  ];
1066  }
1067 
1068  if ( $user->useRCPatrol() ) {
1069  $defaultPreferences['hidepatrolled'] = [
1070  'type' => 'toggle',
1071  'section' => 'rc/changesrc',
1072  'label-message' => 'tog-hidepatrolled',
1073  ];
1074  }
1075 
1076  if ( $user->useNPPatrol() ) {
1077  $defaultPreferences['newpageshidepatrolled'] = [
1078  'type' => 'toggle',
1079  'section' => 'rc/changesrc',
1080  'label-message' => 'tog-newpageshidepatrolled',
1081  ];
1082  }
1083 
1084  if ( $this->options->get( 'RCShowWatchingUsers' ) ) {
1085  $defaultPreferences['shownumberswatching'] = [
1086  'type' => 'toggle',
1087  'section' => 'rc/advancedrc',
1088  'label-message' => 'tog-shownumberswatching',
1089  ];
1090  }
1091 
1092  $defaultPreferences['rcenhancedfilters-disable'] = [
1093  'type' => 'toggle',
1094  'section' => 'rc/advancedrc',
1095  'label-message' => 'rcfilters-preference-label',
1096  'help-message' => 'rcfilters-preference-help',
1097  ];
1098  }
1099 
1105  protected function watchlistPreferences(
1106  User $user, IContextSource $context, &$defaultPreferences
1107  ) {
1108  $watchlistdaysMax = ceil( $this->options->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
1109 
1110  # # Watchlist #####################################
1111  if ( $this->permissionManager->userHasRight( $user, 'editmywatchlist' ) ) {
1112  $editWatchlistLinks = '';
1113  $editWatchlistModes = [
1114  'edit' => [ 'subpage' => false, 'flags' => [] ],
1115  'raw' => [ 'subpage' => 'raw', 'flags' => [] ],
1116  'clear' => [ 'subpage' => 'clear', 'flags' => [ 'destructive' ] ],
1117  ];
1118  foreach ( $editWatchlistModes as $mode => $options ) {
1119  // Messages: prefs-editwatchlist-edit, prefs-editwatchlist-raw, prefs-editwatchlist-clear
1120  $editWatchlistLinks .=
1121  new \OOUI\ButtonWidget( [
1122  'href' => SpecialPage::getTitleFor( 'EditWatchlist', $options['subpage'] )->getLinkURL(),
1123  'flags' => $options[ 'flags' ],
1124  'label' => new \OOUI\HtmlSnippet(
1125  $context->msg( "prefs-editwatchlist-{$mode}" )->parse()
1126  ),
1127  ] );
1128  }
1129 
1130  $defaultPreferences['editwatchlist'] = [
1131  'type' => 'info',
1132  'raw' => true,
1133  'default' => $editWatchlistLinks,
1134  'label-message' => 'prefs-editwatchlist-label',
1135  'section' => 'watchlist/editwatchlist',
1136  ];
1137  }
1138 
1139  $defaultPreferences['watchlistdays'] = [
1140  'type' => 'float',
1141  'min' => 1 / 24,
1142  'max' => $watchlistdaysMax,
1143  'section' => 'watchlist/displaywatchlist',
1144  'help' => $context->msg( 'prefs-watchlist-days-max' )->numParams(
1145  $watchlistdaysMax )->escaped(),
1146  'label-message' => 'prefs-watchlist-days',
1147  ];
1148  $defaultPreferences['wllimit'] = [
1149  'type' => 'int',
1150  'min' => 1,
1151  'max' => 1000,
1152  'label-message' => 'prefs-watchlist-edits',
1153  'help' => $context->msg( 'prefs-watchlist-edits-max' )->escaped(),
1154  'section' => 'watchlist/displaywatchlist',
1155  'filter' => IntvalFilter::class,
1156  ];
1157  $defaultPreferences['extendwatchlist'] = [
1158  'type' => 'toggle',
1159  'section' => 'watchlist/advancedwatchlist',
1160  'label-message' => 'tog-extendwatchlist',
1161  ];
1162  $defaultPreferences['watchlisthideminor'] = [
1163  'type' => 'toggle',
1164  'section' => 'watchlist/changeswatchlist',
1165  'label-message' => 'tog-watchlisthideminor',
1166  ];
1167  $defaultPreferences['watchlisthidebots'] = [
1168  'type' => 'toggle',
1169  'section' => 'watchlist/changeswatchlist',
1170  'label-message' => 'tog-watchlisthidebots',
1171  ];
1172  $defaultPreferences['watchlisthideown'] = [
1173  'type' => 'toggle',
1174  'section' => 'watchlist/changeswatchlist',
1175  'label-message' => 'tog-watchlisthideown',
1176  ];
1177  $defaultPreferences['watchlisthideanons'] = [
1178  'type' => 'toggle',
1179  'section' => 'watchlist/changeswatchlist',
1180  'label-message' => 'tog-watchlisthideanons',
1181  ];
1182  $defaultPreferences['watchlisthideliu'] = [
1183  'type' => 'toggle',
1184  'section' => 'watchlist/changeswatchlist',
1185  'label-message' => 'tog-watchlisthideliu',
1186  ];
1187 
1189  $defaultPreferences['watchlistreloadautomatically'] = [
1190  'type' => 'toggle',
1191  'section' => 'watchlist/advancedwatchlist',
1192  'label-message' => 'tog-watchlistreloadautomatically',
1193  ];
1194  }
1195 
1196  $defaultPreferences['watchlistunwatchlinks'] = [
1197  'type' => 'toggle',
1198  'section' => 'watchlist/advancedwatchlist',
1199  'label-message' => 'tog-watchlistunwatchlinks',
1200  ];
1201 
1202  if ( $this->options->get( 'RCWatchCategoryMembership' ) ) {
1203  $defaultPreferences['watchlisthidecategorization'] = [
1204  'type' => 'toggle',
1205  'section' => 'watchlist/changeswatchlist',
1206  'label-message' => 'tog-watchlisthidecategorization',
1207  ];
1208  }
1209 
1210  if ( $user->useRCPatrol() ) {
1211  $defaultPreferences['watchlisthidepatrolled'] = [
1212  'type' => 'toggle',
1213  'section' => 'watchlist/changeswatchlist',
1214  'label-message' => 'tog-watchlisthidepatrolled',
1215  ];
1216  }
1217 
1218  $watchTypes = [
1219  'edit' => 'watchdefault',
1220  'move' => 'watchmoves',
1221  'delete' => 'watchdeletion'
1222  ];
1223 
1224  // Kinda hacky
1225  if ( $this->permissionManager->userHasAnyRight( $user, 'createpage', 'createtalk' ) ) {
1226  $watchTypes['read'] = 'watchcreations';
1227  }
1228 
1229  if ( $this->permissionManager->userHasRight( $user, 'rollback' ) ) {
1230  $watchTypes['rollback'] = 'watchrollback';
1231  }
1232 
1233  if ( $this->permissionManager->userHasRight( $user, 'upload' ) ) {
1234  $watchTypes['upload'] = 'watchuploads';
1235  }
1236 
1237  foreach ( $watchTypes as $action => $pref ) {
1238  if ( $this->permissionManager->userHasRight( $user, $action ) ) {
1239  // Messages:
1240  // tog-watchdefault, tog-watchmoves, tog-watchdeletion, tog-watchcreations, tog-watchuploads
1241  // tog-watchrollback
1242  $defaultPreferences[$pref] = [
1243  'type' => 'toggle',
1244  'section' => 'watchlist/pageswatchlist',
1245  'label-message' => "tog-$pref",
1246  ];
1247  }
1248  }
1249 
1250  $defaultPreferences['watchlisttoken'] = [
1251  'type' => 'api',
1252  ];
1253 
1254  $tokenButton = new \OOUI\ButtonWidget( [
1255  'href' => SpecialPage::getTitleFor( 'ResetTokens' )->getLinkURL( [
1256  'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText()
1257  ] ),
1258  'label' => $context->msg( 'prefs-watchlist-managetokens' )->text(),
1259  ] );
1260  $defaultPreferences['watchlisttoken-info'] = [
1261  'type' => 'info',
1262  'section' => 'watchlist/tokenwatchlist',
1263  'label-message' => 'prefs-watchlist-token',
1264  'help-message' => 'prefs-help-tokenmanagement',
1265  'raw' => true,
1266  'default' => (string)$tokenButton,
1267  ];
1268 
1269  $defaultPreferences['wlenhancedfilters-disable'] = [
1270  'type' => 'toggle',
1271  'section' => 'watchlist/advancedwatchlist',
1272  'label-message' => 'rcfilters-watchlist-preference-label',
1273  'help-message' => 'rcfilters-watchlist-preference-help',
1274  ];
1275  }
1276 
1280  protected function searchPreferences( &$defaultPreferences ) {
1281  foreach ( $this->nsInfo->getValidNamespaces() as $n ) {
1282  $defaultPreferences['searchNs' . $n] = [
1283  'type' => 'api',
1284  ];
1285  }
1286  }
1287 
1293  protected function generateSkinOptions( User $user, IContextSource $context ) {
1294  $ret = [];
1295 
1296  $mptitle = Title::newMainPage();
1297  $previewtext = $context->msg( 'skin-preview' )->escaped();
1298 
1299  # Only show skins that aren't disabled in $wgSkipSkins
1300  $validSkinNames = Skin::getAllowedSkins();
1301  $allInstalledSkins = Skin::getSkinNames();
1302 
1303  // Display the installed skin the user has specifically requested via useskin=….
1304  $useSkin = $context->getRequest()->getRawVal( 'useskin' );
1305  if ( isset( $allInstalledSkins[$useSkin] )
1306  && $context->msg( "skinname-$useSkin" )->exists()
1307  ) {
1308  $validSkinNames[$useSkin] = $useSkin;
1309  }
1310 
1311  // Display the skin if the user has set it as a preference already before it was hidden.
1312  $currentUserSkin = $user->getOption( 'skin' );
1313  if ( isset( $allInstalledSkins[$currentUserSkin] )
1314  && $context->msg( "skinname-$currentUserSkin" )->exists()
1315  ) {
1316  $validSkinNames[$currentUserSkin] = $currentUserSkin;
1317  }
1318 
1319  foreach ( $validSkinNames as $skinkey => &$skinname ) {
1320  $msg = $context->msg( "skinname-{$skinkey}" );
1321  if ( $msg->exists() ) {
1322  $skinname = htmlspecialchars( $msg->text() );
1323  }
1324  }
1325 
1326  $defaultSkin = $this->options->get( 'DefaultSkin' );
1327  $allowUserCss = $this->options->get( 'AllowUserCss' );
1328  $allowUserJs = $this->options->get( 'AllowUserJs' );
1329 
1330  # Sort by the internal name, so that the ordering is the same for each display language,
1331  # especially if some skin names are translated to use a different alphabet and some are not.
1332  uksort( $validSkinNames, function ( $a, $b ) use ( $defaultSkin ) {
1333  # Display the default first in the list by comparing it as lesser than any other.
1334  if ( strcasecmp( $a, $defaultSkin ) === 0 ) {
1335  return -1;
1336  }
1337  if ( strcasecmp( $b, $defaultSkin ) === 0 ) {
1338  return 1;
1339  }
1340  return strcasecmp( $a, $b );
1341  } );
1342 
1343  $foundDefault = false;
1344  foreach ( $validSkinNames as $skinkey => $sn ) {
1345  $linkTools = [];
1346 
1347  # Mark the default skin
1348  if ( strcasecmp( $skinkey, $defaultSkin ) === 0 ) {
1349  $linkTools[] = $context->msg( 'default' )->escaped();
1350  $foundDefault = true;
1351  }
1352 
1353  # Create preview link
1354  $mplink = htmlspecialchars( $mptitle->getLocalURL( [ 'useskin' => $skinkey ] ) );
1355  $linkTools[] = "<a target='_blank' href=\"$mplink\">$previewtext</a>";
1356 
1357  # Create links to user CSS/JS pages
1358  if ( $allowUserCss ) {
1359  $cssPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.css' );
1360  $cssLinkText = $context->msg( 'prefs-custom-css' )->text();
1361  $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
1362  }
1363 
1364  if ( $allowUserJs ) {
1365  $jsPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.js' );
1366  $jsLinkText = $context->msg( 'prefs-custom-js' )->text();
1367  $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
1368  }
1369 
1370  $display = $sn . ' ' . $context->msg( 'parentheses' )
1371  ->rawParams( $context->getLanguage()->pipeList( $linkTools ) )
1372  ->escaped();
1373  $ret[$display] = $skinkey;
1374  }
1375 
1376  if ( !$foundDefault ) {
1377  // If the default skin is not available, things are going to break horribly because the
1378  // default value for skin selector will not be a valid value. Let's just not show it then.
1379  return [];
1380  }
1381 
1382  return $ret;
1383  }
1384 
1389  protected function getDateOptions( IContextSource $context ) {
1390  $lang = $context->getLanguage();
1391  $dateopts = $lang->getDatePreferences();
1392 
1393  $ret = [];
1394 
1395  if ( $dateopts ) {
1396  if ( !in_array( 'default', $dateopts ) ) {
1397  $dateopts[] = 'default'; // Make sure default is always valid T21237
1398  }
1399 
1400  // FIXME KLUGE: site default might not be valid for user language
1401  global $wgDefaultUserOptions;
1402  if ( !in_array( $wgDefaultUserOptions['date'], $dateopts ) ) {
1403  $wgDefaultUserOptions['date'] = 'default';
1404  }
1405 
1406  $epoch = wfTimestampNow();
1407  foreach ( $dateopts as $key ) {
1408  if ( $key == 'default' ) {
1409  $formatted = $context->msg( 'datedefault' )->escaped();
1410  } else {
1411  $formatted = htmlspecialchars( $lang->timeanddate( $epoch, false, $key ) );
1412  }
1413  $ret[$formatted] = $key;
1414  }
1415  }
1416  return $ret;
1417  }
1418 
1423  protected function getImageSizes( MessageLocalizer $l10n ) {
1424  $ret = [];
1425  $pixels = $l10n->msg( 'unit-pixel' )->text();
1426 
1427  foreach ( $this->options->get( 'ImageLimits' ) as $index => $limits ) {
1428  // Note: A left-to-right marker (U+200E) is inserted, see T144386
1429  $display = "{$limits[0]}\u{200E}×{$limits[1]}$pixels";
1430  $ret[$display] = $index;
1431  }
1432 
1433  return $ret;
1434  }
1435 
1440  protected function getThumbSizes( MessageLocalizer $l10n ) {
1441  $ret = [];
1442  $pixels = $l10n->msg( 'unit-pixel' )->text();
1443 
1444  foreach ( $this->options->get( 'ThumbLimits' ) as $index => $size ) {
1445  $display = $size . $pixels;
1446  $ret[$display] = $index;
1447  }
1448 
1449  return $ret;
1450  }
1451 
1458  protected function validateSignature( $signature, $alldata, HTMLForm $form ) {
1459  $maxSigChars = $this->options->get( 'MaxSigChars' );
1460  if ( mb_strlen( $signature ) > $maxSigChars ) {
1461  return Xml::element( 'span', [ 'class' => 'error' ],
1462  $form->msg( 'badsiglength' )->numParams( $maxSigChars )->text() );
1463  } elseif ( isset( $alldata['fancysig'] ) &&
1464  $alldata['fancysig'] &&
1465  MediaWikiServices::getInstance()->getParser()->validateSig( $signature ) === false
1466  ) {
1467  return Xml::element(
1468  'span',
1469  [ 'class' => 'error' ],
1470  $form->msg( 'badsig' )->text()
1471  );
1472  } else {
1473  return true;
1474  }
1475  }
1476 
1483  protected function cleanSignature( $signature, $alldata, HTMLForm $form ) {
1484  $parser = MediaWikiServices::getInstance()->getParser();
1485  if ( isset( $alldata['fancysig'] ) && $alldata['fancysig'] ) {
1486  $signature = $parser->cleanSig( $signature );
1487  } else {
1488  // When no fancy sig used, make sure ~{3,5} get removed.
1489  $signature = Parser::cleanSigInSig( $signature );
1490  }
1491 
1492  return $signature;
1493  }
1494 
1502  public function getForm(
1503  User $user,
1505  $formClass = PreferencesFormOOUI::class,
1506  array $remove = []
1507  ) {
1508  // We use ButtonWidgets in some of the getPreferences() functions
1509  $context->getOutput()->enableOOUI();
1510 
1511  $formDescriptor = $this->getFormDescriptor( $user, $context );
1512  if ( count( $remove ) ) {
1513  $removeKeys = array_flip( $remove );
1514  $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
1515  }
1516 
1517  // Remove type=api preferences. They are not intended for rendering in the form.
1518  foreach ( $formDescriptor as $name => $info ) {
1519  if ( isset( $info['type'] ) && $info['type'] === 'api' ) {
1520  unset( $formDescriptor[$name] );
1521  }
1522  }
1523 
1527  $htmlForm = new $formClass( $formDescriptor, $context, 'prefs' );
1528 
1529  // This allows users to opt-in to hidden skins. While this should be discouraged and is not
1530  // discoverable, this allows users to still use hidden skins while preventing new users from
1531  // adopting unsupported skins. If no useskin=… parameter was provided, it will not show up
1532  // in the resulting URL.
1533  $htmlForm->setAction( $context->getTitle()->getLocalURL( [
1534  'useskin' => $context->getRequest()->getRawVal( 'useskin' )
1535  ] ) );
1536 
1537  $htmlForm->setModifiedUser( $user );
1538  $htmlForm->setOptionsEditable( $this->permissionManager
1539  ->userHasRight( $user, 'editmyoptions' ) );
1540  $htmlForm->setPrivateInfoEditable( $this->permissionManager
1541  ->userHasRight( $user, 'editmyprivateinfo' ) );
1542  $htmlForm->setId( 'mw-prefs-form' );
1543  $htmlForm->setAutocomplete( 'off' );
1544  $htmlForm->setSubmitText( $context->msg( 'saveprefs' )->text() );
1545  # Used message keys: 'accesskey-preferences-save', 'tooltip-preferences-save'
1546  $htmlForm->setSubmitTooltip( 'preferences-save' );
1547  $htmlForm->setSubmitID( 'prefcontrol' );
1548  $htmlForm->setSubmitCallback(
1549  function ( array $formData, PreferencesFormOOUI $form ) use ( $formDescriptor ) {
1550  return $this->submitForm( $formData, $form, $formDescriptor );
1551  }
1552  );
1553 
1554  return $htmlForm;
1555  }
1556 
1562  $opt = [];
1563 
1564  $localTZoffset = $this->options->get( 'LocalTZoffset' );
1565  $timeZoneList = $this->getTimeZoneList( $context->getLanguage() );
1566 
1567  $timestamp = MWTimestamp::getLocalInstance();
1568  // Check that the LocalTZoffset is the same as the local time zone offset
1569  if ( $localTZoffset == $timestamp->format( 'Z' ) / 60 ) {
1570  $timezoneName = $timestamp->getTimezone()->getName();
1571  // Localize timezone
1572  if ( isset( $timeZoneList[$timezoneName] ) ) {
1573  $timezoneName = $timeZoneList[$timezoneName]['name'];
1574  }
1575  $server_tz_msg = $context->msg(
1576  'timezoneuseserverdefault',
1577  $timezoneName
1578  )->text();
1579  } else {
1580  $tzstring = sprintf(
1581  '%+03d:%02d',
1582  floor( $localTZoffset / 60 ),
1583  abs( $localTZoffset ) % 60
1584  );
1585  $server_tz_msg = $context->msg( 'timezoneuseserverdefault', $tzstring )->text();
1586  }
1587  $opt[$server_tz_msg] = "System|$localTZoffset";
1588  $opt[$context->msg( 'timezoneuseoffset' )->text()] = 'other';
1589  $opt[$context->msg( 'guesstimezone' )->text()] = 'guess';
1590 
1591  foreach ( $timeZoneList as $timeZoneInfo ) {
1592  $region = $timeZoneInfo['region'];
1593  if ( !isset( $opt[$region] ) ) {
1594  $opt[$region] = [];
1595  }
1596  $opt[$region][$timeZoneInfo['name']] = $timeZoneInfo['timecorrection'];
1597  }
1598  return $opt;
1599  }
1600 
1609  protected function saveFormData( $formData, PreferencesFormOOUI $form, array $formDescriptor ) {
1610  $user = $form->getModifiedUser();
1611  $hiddenPrefs = $this->options->get( 'HiddenPrefs' );
1612  $result = true;
1613 
1614  if ( !$this->permissionManager
1615  ->userHasAnyRight( $user, 'editmyprivateinfo', 'editmyoptions' )
1616  ) {
1617  return Status::newFatal( 'mypreferencesprotected' );
1618  }
1619 
1620  // Filter input
1621  $this->applyFilters( $formData, $formDescriptor, 'filterFromForm' );
1622 
1623  // Fortunately, the realname field is MUCH simpler
1624  // (not really "private", but still shouldn't be edited without permission)
1625 
1626  if ( !in_array( 'realname', $hiddenPrefs )
1627  && $this->permissionManager->userHasRight( $user, 'editmyprivateinfo' )
1628  && array_key_exists( 'realname', $formData )
1629  ) {
1630  $realName = $formData['realname'];
1631  $user->setRealName( $realName );
1632  }
1633 
1634  if ( $this->permissionManager->userHasRight( $user, 'editmyoptions' ) ) {
1635  $oldUserOptions = $user->getOptions();
1636 
1637  foreach ( $this->getSaveBlacklist() as $b ) {
1638  unset( $formData[$b] );
1639  }
1640 
1641  # If users have saved a value for a preference which has subsequently been disabled
1642  # via $wgHiddenPrefs, we don't want to destroy that setting in case the preference
1643  # is subsequently re-enabled
1644  foreach ( $hiddenPrefs as $pref ) {
1645  # If the user has not set a non-default value here, the default will be returned
1646  # and subsequently discarded
1647  $formData[$pref] = $user->getOption( $pref, null, true );
1648  }
1649 
1650  // If the user changed the rclimit preference, also change the rcfilters-rclimit preference
1651  if (
1652  isset( $formData['rclimit'] ) &&
1653  intval( $formData[ 'rclimit' ] ) !== $user->getIntOption( 'rclimit' )
1654  ) {
1655  $formData['rcfilters-limit'] = $formData['rclimit'];
1656  }
1657 
1658  // Keep old preferences from interfering due to back-compat code, etc.
1659  $user->resetOptions( 'unused', $form->getContext() );
1660 
1661  foreach ( $formData as $key => $value ) {
1662  $user->setOption( $key, $value );
1663  }
1664 
1665  Hooks::run(
1666  'PreferencesFormPreSave',
1667  [ $formData, $form, $user, &$result, $oldUserOptions ]
1668  );
1669  }
1670 
1671  $user->saveSettings();
1672 
1673  return $result;
1674  }
1675 
1684  protected function applyFilters( array &$preferences, array $formDescriptor, $verb ) {
1685  foreach ( $formDescriptor as $preference => $desc ) {
1686  if ( !isset( $desc['filter'] ) || !isset( $preferences[$preference] ) ) {
1687  continue;
1688  }
1689  $filterDesc = $desc['filter'];
1690  if ( $filterDesc instanceof Filter ) {
1691  $filter = $filterDesc;
1692  } elseif ( class_exists( $filterDesc ) ) {
1693  $filter = new $filterDesc();
1694  } elseif ( is_callable( $filterDesc ) ) {
1695  $filter = $filterDesc();
1696  } else {
1697  throw new UnexpectedValueException(
1698  "Unrecognized filter type for preference '$preference'"
1699  );
1700  }
1701  $preferences[$preference] = $filter->$verb( $preferences[$preference] );
1702  }
1703  }
1704 
1713  protected function submitForm(
1714  array $formData,
1715  PreferencesFormOOUI $form,
1716  array $formDescriptor
1717  ) {
1718  $res = $this->saveFormData( $formData, $form, $formDescriptor );
1719 
1720  if ( $res === true ) {
1721  $context = $form->getContext();
1722  $urlOptions = [];
1723 
1724  if ( $res === 'eauth' ) {
1725  $urlOptions['eauth'] = 1;
1726  }
1727 
1728  $urlOptions += $form->getExtraSuccessRedirectParameters();
1729 
1730  $url = $form->getTitle()->getFullURL( $urlOptions );
1731 
1732  // Set session data for the success message
1733  $context->getRequest()->getSession()->set( 'specialPreferencesSaveSuccess', 1 );
1734 
1735  $context->getOutput()->redirect( $url );
1736  }
1737 
1738  return ( $res === true ? Status::newGood() : $res );
1739  }
1740 
1749  protected function getTimeZoneList( Language $language ) {
1750  $identifiers = DateTimeZone::listIdentifiers();
1751  // @phan-suppress-next-line PhanTypeComparisonFromArray See phan issue #3162
1752  if ( $identifiers === false ) {
1753  return [];
1754  }
1755  sort( $identifiers );
1756 
1757  $tzRegions = [
1758  'Africa' => wfMessage( 'timezoneregion-africa' )->inLanguage( $language )->text(),
1759  'America' => wfMessage( 'timezoneregion-america' )->inLanguage( $language )->text(),
1760  'Antarctica' => wfMessage( 'timezoneregion-antarctica' )->inLanguage( $language )->text(),
1761  'Arctic' => wfMessage( 'timezoneregion-arctic' )->inLanguage( $language )->text(),
1762  'Asia' => wfMessage( 'timezoneregion-asia' )->inLanguage( $language )->text(),
1763  'Atlantic' => wfMessage( 'timezoneregion-atlantic' )->inLanguage( $language )->text(),
1764  'Australia' => wfMessage( 'timezoneregion-australia' )->inLanguage( $language )->text(),
1765  'Europe' => wfMessage( 'timezoneregion-europe' )->inLanguage( $language )->text(),
1766  'Indian' => wfMessage( 'timezoneregion-indian' )->inLanguage( $language )->text(),
1767  'Pacific' => wfMessage( 'timezoneregion-pacific' )->inLanguage( $language )->text(),
1768  ];
1769  asort( $tzRegions );
1770 
1771  $timeZoneList = [];
1772 
1773  $now = new DateTime();
1774 
1775  foreach ( $identifiers as $identifier ) {
1776  $parts = explode( '/', $identifier, 2 );
1777 
1778  // DateTimeZone::listIdentifiers() returns a number of
1779  // backwards-compatibility entries. This filters them out of the
1780  // list presented to the user.
1781  if ( count( $parts ) !== 2 || !array_key_exists( $parts[0], $tzRegions ) ) {
1782  continue;
1783  }
1784 
1785  // Localize region
1786  $parts[0] = $tzRegions[$parts[0]];
1787 
1788  $dateTimeZone = new DateTimeZone( $identifier );
1789  $minDiff = floor( $dateTimeZone->getOffset( $now ) / 60 );
1790 
1791  $display = str_replace( '_', ' ', $parts[0] . '/' . $parts[1] );
1792  $value = "ZoneInfo|$minDiff|$identifier";
1793 
1794  $timeZoneList[$identifier] = [
1795  'name' => $display,
1796  'timecorrection' => $value,
1797  'region' => $parts[0],
1798  ];
1799  }
1800 
1801  return $timeZoneList;
1802  }
1803 }
$filter
$filter
Definition: profileinfo.php:344
PreferencesFormOOUI
Form to edit user preferences.
Definition: PreferencesFormOOUI.php:26
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:42
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:32
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:40
MediaWiki\Preferences\DefaultPreferencesFactory\watchlistPreferences
watchlistPreferences(User $user, IContextSource $context, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:1105
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:2927
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
MediaWiki\Preferences\DefaultPreferencesFactory\$permissionManager
PermissionManager $permissionManager
Definition: DefaultPreferencesFactory.php:81
IContextSource\getSkin
getSkin()
PreferencesFormOOUI\getExtraSuccessRedirectParameters
getExtraSuccessRedirectParameters()
Get extra parameters for the query string when redirecting after successful save.
Definition: PreferencesFormOOUI.php:92
SpecialWatchlist\checkStructuredFilterUiEnabled
static checkStructuredFilterUiEnabled( $user)
Definition: SpecialWatchlist.php:116
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:117
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
MediaWiki\Preferences\DefaultPreferencesFactory\getForm
getForm(User $user, IContextSource $context, $formClass=PreferencesFormOOUI::class, array $remove=[])
Definition: DefaultPreferencesFactory.php:1502
User\getEditCount
getEditCount()
Get the user's edit count.
Definition: User.php:3518
MediaWiki\Preferences\DefaultPreferencesFactory\applyFilters
applyFilters(array &$preferences, array $formDescriptor, $verb)
Applies filters to preferences either before or after form usage.
Definition: DefaultPreferencesFactory.php:1684
$languages
switch( $options['output']) $languages
Definition: transstat.php:76
User\getOptions
getOptions( $flags=0)
Get all user's options.
Definition: User.php:3050
UserGroupMembership\getExpiry
getExpiry()
Definition: UserGroupMembership.php:75
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
MediaWiki\Preferences\DefaultPreferencesFactory\editingPreferences
editingPreferences(User $user, MessageLocalizer $l10n, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:939
MediaWiki\Preferences\DefaultPreferencesFactory\getTimezoneOptions
getTimezoneOptions(IContextSource $context)
Definition: DefaultPreferencesFactory.php:1561
$wgDefaultUserOptions
$wgDefaultUserOptions
Settings added to this array will override the default globals for the user preferences used by anony...
Definition: DefaultSettings.php:4856
MediaWiki\Preferences\DefaultPreferencesFactory\profilePreferences
profilePreferences(User $user, IContextSource $context, &$defaultPreferences, $canIPUseHTTPS)
Definition: DefaultPreferencesFactory.php:296
MediaWiki\Preferences\DefaultPreferencesFactory\getOptionFromUser
getOptionFromUser( $name, $info, array $userOptions)
Pull option from a user account.
Definition: DefaultPreferencesFactory.php:250
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1263
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:83
MediaWiki\Preferences\DefaultPreferencesFactory\saveFormData
saveFormData( $formData, PreferencesFormOOUI $form, array $formDescriptor)
Handle the form submission if everything validated properly.
Definition: DefaultPreferencesFactory.php:1609
User\useNPPatrol
useNPPatrol()
Check whether to enable new pages patrol features for this user.
Definition: User.php:3712
Title\newMainPage
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:649
User\getEmailAuthenticationTimestamp
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
Definition: User.php:2915
$res
$res
Definition: testCompression.php:52
User\useRCPatrol
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
Definition: User.php:3703
PreferencesFormOOUI\getModifiedUser
getModifiedUser()
Definition: PreferencesFormOOUI.php:49
MessageLocalizer
Interface for localizing messages in MediaWiki.
Definition: MessageLocalizer.php:28
Skin\getSkinNames
static getSkinNames()
Fetch the set of available skins.
Definition: Skin.php:57
User\getDefaultOptions
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Definition: User.php:1692
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:40
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:138
User\getEmail
getEmail()
Get the user's e-mail address.
Definition: User.php:2905
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:72
MediaWiki\Preferences\DefaultPreferencesFactory\getThumbSizes
getThumbSizes(MessageLocalizer $l10n)
Definition: DefaultPreferencesFactory.php:1440
MWException
MediaWiki exception.
Definition: MWException.php:26
MediaWiki\Preferences
Definition: DefaultPreferencesFactory.php:21
MediaWiki\Preferences\DefaultPreferencesFactory\getImageSizes
getImageSizes(MessageLocalizer $l10n)
Definition: DefaultPreferencesFactory.php:1423
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:25
MessageLocalizer\msg
msg( $key,... $params)
This is the method for getting translated interface messages.
MediaWiki\Preferences\DefaultPreferencesFactory\getTimeZoneList
getTimeZoneList(Language $language)
Get a list of all time zones.
Definition: DefaultPreferencesFactory.php:1749
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:376
MediaWiki\Preferences\DefaultPreferencesFactory\$linkRenderer
LinkRenderer $linkRenderer
Definition: DefaultPreferencesFactory.php:75
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
User\getEffectiveGroups
getEffectiveGroups( $recache=false)
Get the list of implicit group memberships this user has.
Definition: User.php:3442
$t
$t
Definition: make-normalization-table.php:143
MediaWiki\Preferences\DefaultPreferencesFactory\rcPreferences
rcPreferences(User $user, MessageLocalizer $l10n, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:1006
MediaWiki\Preferences\DefaultPreferencesFactory\cleanSignature
cleanSignature( $signature, $alldata, HTMLForm $form)
Definition: DefaultPreferencesFactory.php:1483
MediaWiki\Preferences\DefaultPreferencesFactory\renderingPreferences
renderingPreferences(User $user, MessageLocalizer $l10n, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:865
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1878
MediaWiki\Preferences\DefaultPreferencesFactory\__construct
__construct(ServiceOptions $options, Language $contLang, AuthManager $authManager, LinkRenderer $linkRenderer, NamespaceInfo $nsInfo, PermissionManager $permissionManager)
Do not call this directly.
Definition: DefaultPreferencesFactory.php:126
MediaWiki\Preferences\DefaultPreferencesFactory\$contLang
Language $contLang
The wiki's content language.
Definition: DefaultPreferencesFactory.php:69
MediaWiki\Preferences\DefaultPreferencesFactory\filesPreferences
filesPreferences(IContextSource $context, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:764
HTMLForm\loadInputFromParameters
static loadInputFromParameters( $fieldname, $descriptor, HTMLForm $parent=null)
Initialise a new Object for the field.
Definition: HTMLForm.php:508
OutputPage
This is one of the Core classes and should be read at least once by any new developers.
Definition: OutputPage.php:46
MediaWiki\Preferences\DefaultPreferencesFactory\searchPreferences
searchPreferences(&$defaultPreferences)
Definition: DefaultPreferencesFactory.php:1280
MediaWiki\Preferences\DefaultPreferencesFactory\$nsInfo
NamespaceInfo $nsInfo
Definition: DefaultPreferencesFactory.php:78
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:168
MediaWiki\Preferences\DefaultPreferencesFactory\submitForm
submitForm(array $formData, PreferencesFormOOUI $form, array $formDescriptor)
Save the form data and reload the page.
Definition: DefaultPreferencesFactory.php:1713
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:802
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:613
MediaWiki\Permissions\PermissionManager
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Definition: PermissionManager.php:47
ParserOptions\newFromContext
static newFromContext(IContextSource $context)
Get a ParserOptions object from a IContextSource object.
Definition: ParserOptions.php:1052
User\getOption
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
Definition: User.php:3022
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:37
MediaWiki\Preferences\PreferencesFactory
A PreferencesFactory is a MediaWiki service that provides the definitions of preferences for a given ...
Definition: PreferencesFactory.php:52
User\getRealName
getRealName()
Get the user's real name.
Definition: User.php:2995
OutputPage\setupOOUI
static setupOOUI( $skinName='default', $dir='ltr')
Helper function to setup the PHP implementation of OOUI to use in this request.
Definition: OutputPage.php:4025
MediaWiki\Preferences\DefaultPreferencesFactory\getDateOptions
getDateOptions(IContextSource $context)
Definition: DefaultPreferencesFactory.php:1389
IContextSource\getUser
getUser()
Parser
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition: Parser.php:74
IContextSource\getTitle
getTitle()
MediaWiki\Preferences\DefaultPreferencesFactory\$options
ServiceOptions $options
Definition: DefaultPreferencesFactory.php:66
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:85
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
User\getRegistration
getRegistration()
Get the timestamp of account creation.
Definition: User.php:4781
MediaWiki\$action
string $action
Cache what action this request is.
Definition: MediaWiki.php:48
Title
Represents a title within MediaWiki.
Definition: Title.php:42
LanguageConverter\$languagesWithVariants
static array $languagesWithVariants
languages supporting variants
Definition: LanguageConverter.php:41
Parser\stripOuterParagraph
static stripOuterParagraph( $html)
Strip outer.
Definition: Parser.php:6797
MediaWiki\Preferences\DefaultPreferencesFactory\getSaveBlacklist
getSaveBlacklist()
Get the names of preferences that should never be saved (such as 'realname' and 'emailaddress')....
Definition: DefaultPreferencesFactory.php:148
MediaWiki\Preferences\DefaultPreferencesFactory\loadPreferenceValues
loadPreferenceValues(User $user, IContextSource $context, &$defaultPreferences)
Loads existing values for a given array of preferences.
Definition: DefaultPreferencesFactory.php:195
MediaWiki\Preferences\DefaultPreferencesFactory\datetimePreferences
datetimePreferences( $user, IContextSource $context, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:786
LanguageCode\bcp47
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: LanguageCode.php:178
NS_USER
const NS_USER
Definition: Defines.php:71
HTMLForm\getTitle
getTitle()
Get the title.
Definition: HTMLForm.php:1616
MediaWiki\Preferences\DefaultPreferencesFactory
This is the default implementation of PreferencesFactory.
Definition: DefaultPreferencesFactory.php:62
IContextSource\getRequest
getRequest()
LanguageCode
Methods for dealing with language codes.
Definition: LanguageCode.php:28
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:1093
NamespaceInfo
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Definition: NamespaceInfo.php:33
Skin\getAllowedSkins
static getAllowedSkins()
Fetch the list of user-selectable skins in regards to $wgSkipSkins.
Definition: Skin.php:83
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:818
MediaWiki\Preferences\DefaultPreferencesFactory\validateSignature
validateSignature( $signature, $alldata, HTMLForm $form)
Definition: DefaultPreferencesFactory.php:1458
Parser\cleanSigInSig
static cleanSigInSig( $text)
Strip 3, 4 or 5 tildes out of signatures.
Definition: Parser.php:5096
MediaWiki\Preferences\DefaultPreferencesFactory\generateSkinOptions
generateSkinOptions(User $user, IContextSource $context)
Definition: DefaultPreferencesFactory.php:1293
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
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 filters that work as a middleware between storage and interface.
Definition: Filter.php:27
IContextSource\getOutput
getOutput()
LanguageConverter
Base class for language conversion.
Definition: LanguageConverter.php:35
MWTimestamp\getLocalInstance
static getLocalInstance( $ts=false)
Get a timestamp instance in the server local timezone ($wgLocaltimezone)
Definition: MWTimestamp.php:204
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:2364
Xml
Module of static functions for generating XML.
Definition: Xml.php:28
User\getGroupMemberships
getGroupMemberships()
Get the list of explicit group memberships this user has, stored as UserGroupMembership objects.
Definition: User.php:3429
Language
Internationalisation code.
Definition: Language.php:37
MediaWiki\Preferences\DefaultPreferencesFactory\skinPreferences
skinPreferences(User $user, IContextSource $context, &$defaultPreferences)
Definition: DefaultPreferencesFactory.php:716
MediaWiki\Preferences\DefaultPreferencesFactory\getFormDescriptor
getFormDescriptor(User $user, IContextSource $context)
Definition: DefaultPreferencesFactory.php:161
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()
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:62
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 in a reusabl...
Definition: HTMLForm.php:131