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