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