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