48use Psr\Log\LoggerAwareTrait;
49use Psr\Log\NullLogger;
92 $this->logger =
new NullLogger();
100 $intvalFilter =
function (
$value, $alldata ) {
104 'timecorrection' => [ $this,
'filterTimezoneInput' ],
105 'rclimit' => $intvalFilter,
106 'wllimit' => $intvalFilter,
107 'searchlimit' => $intvalFilter,
141 Hooks::run(
'GetPreferences', [ $user, &$preferences ] );
144 $this->logger->debug(
"Created form descriptor for user '{$user->getName()}'" );
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] );
166 # # Make sure that form fields have their parent set. See T43337.
169 $disable = !$user->isAllowed(
'editmyoptions' );
172 # # Prod in defaults from the user
173 foreach ( $defaultPreferences as $name => &$info ) {
176 $info[
'disabled'] =
'disabled';
178 $field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm );
179 $globalDefault = isset( $defaultOptions[$name] )
180 ? $defaultOptions[
$name]
184 if ( isset( $info[
'default'] ) ) {
187 } elseif ( !is_null( $prefFromUser ) &&
188 $field->validate( $prefFromUser, $user->getOptions() ) ===
true ) {
189 $info[
'default'] = $prefFromUser;
190 } elseif ( $field->validate( $globalDefault, $user->getOptions() ) ===
true ) {
191 $info[
'default'] = $globalDefault;
193 throw new MWException(
"Global default '$globalDefault' is invalid for field $name" );
197 return $defaultPreferences;
209 $val = $user->getOption( $name );
212 if ( ( isset( $info[
'type'] ) && $info[
'type'] ==
'multiselect' ) ||
213 ( isset( $info[
'class'] ) && $info[
'class'] == \HTMLMultiSelectField::class ) ) {
215 $prefix = isset( $info[
'prefix'] ) ? $info[
'prefix'] :
$name;
219 if ( $user->getOption(
"$prefix$value" ) ) {
226 if ( ( isset( $info[
'type'] ) && $info[
'type'] ==
'checkmatrix' ) ||
227 ( isset( $info[
'class'] ) && $info[
'class'] == \HTMLCheckMatrix::class ) ) {
230 $prefix = isset( $info[
'prefix'] ) ? $info[
'prefix'] :
$name;
233 foreach ( $columns as $column ) {
234 foreach (
$rows as $row ) {
235 if ( $user->getOption(
"$prefix$column-$row" ) ) {
236 $val[] =
"$column-$row";
258 $userName = $user->getName();
260 # # User info #####################################
262 $defaultPreferences[
'username'] = [
264 'label-message' => [
'username', $userName ],
265 'default' => $userName,
266 'section' =>
'personal/info',
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 ) {
281 if ( isset( $userGroupMemberships[$ueg] ) ) {
282 $groupStringOrObject = $userGroupMemberships[$ueg];
284 $groupStringOrObject = $ueg;
298 $userTempGroups[] = $userG;
299 $userTempMembers[] = $userM;
301 $userGroups[] = $userG;
302 $userMembers[] = $userM;
306 sort( $userMembers );
307 sort( $userTempGroups );
308 sort( $userTempMembers );
309 $userGroups = array_merge( $userTempGroups, $userGroups );
310 $userMembers = array_merge( $userTempMembers, $userMembers );
312 $defaultPreferences[
'usergroups'] = [
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 ) )
320 'section' =>
'personal/info',
324 $formattedEditCount =
$lang->formatNum( $user->getEditCount() );
325 $editCount = $this->linkRenderer->makeLink( $contribTitle, $formattedEditCount );
327 $defaultPreferences[
'editcount'] = [
330 'label-message' =>
'prefs-edits',
331 'default' => $editCount,
332 'section' =>
'personal/info',
335 if ( $user->getRegistration() ) {
337 $userRegistration = $user->getRegistration();
338 $defaultPreferences[
'registrationdate'] = [
340 'label-message' =>
'prefs-registration',
342 'prefs-registration-date-time',
343 $lang->userTimeAndDate( $userRegistration, $displayUser ),
344 $lang->userDate( $userRegistration, $displayUser ),
345 $lang->userTime( $userRegistration, $displayUser )
347 'section' =>
'personal/info',
351 $canViewPrivateInfo = $user->isAllowed(
'viewmyprivateinfo' );
352 $canEditPrivateInfo = $user->isAllowed(
'editmyprivateinfo' );
355 $defaultPreferences[
'realname'] = [
357 'type' => $canEditPrivateInfo && $this->authManager->allowsPropertyChange(
'realname' )
359 'default' => $user->getRealName(),
360 'section' =>
'personal/info',
361 'label-message' =>
'yourrealname',
362 'help-message' =>
'prefs-help-realname',
365 if ( $canEditPrivateInfo && $this->authManager->allowsAuthenticationDataChange(
369 $context->msg(
'prefs-resetpass' )->text(), [],
371 $defaultPreferences[
'password'] = [
375 'label-message' =>
'yourpassword',
376 'section' =>
'personal/info',
380 if ( !$this->config->get(
'ForceHTTPS' )
381 && $this->config->get(
'SecureLogin' )
384 $defaultPreferences[
'prefershttps'] = [
386 'label-message' =>
'tog-prefershttps',
387 'help-message' =>
'prefs-help-prefershttps',
388 'section' =>
'personal/info'
393 $languages = Language::fetchLanguageNames(
null,
'mw' );
394 $languageCode = $this->config->get(
'LanguageCode' );
395 if ( !array_key_exists( $languageCode,
$languages ) ) {
405 $defaultPreferences[
'language'] = [
407 'section' =>
'personal/i18n',
409 'label-message' =>
'yourlanguage',
412 $defaultPreferences[
'gender'] = [
414 'section' =>
'personal/i18n',
417 ->params(
$context->msg(
'gender-unknown' )->plain() )
418 ->escaped() =>
'unknown',
419 $context->msg(
'gender-female' )->escaped() =>
'female',
420 $context->msg(
'gender-male' )->escaped() =>
'male',
422 'label-message' =>
'yourgender',
423 'help-message' =>
'prefs-help-gender',
427 if ( !$this->config->get(
'DisableLangConversion' ) ) {
429 if ( $langCode == $this->contLang->getCode() ) {
430 $variants = $this->contLang->getVariants();
432 if ( count( $variants ) <= 1 ) {
437 foreach ( $variants as $v ) {
438 $v = str_replace(
'_',
'-', strtolower( $v ) );
439 $variantArray[$v] =
$lang->getVariantname( $v,
false );
443 foreach ( $variantArray as
$code => $name ) {
448 $defaultPreferences[
'variant'] = [
449 'label-message' =>
'yourvariant',
452 'section' =>
'personal/i18n',
453 'help-message' =>
'prefs-help-variant',
456 $defaultPreferences[
"variant-$langCode"] = [
465 $toggles = $this->contLang->getExtraUserToggles();
467 foreach ( $toggles as $toggle ) {
468 $defaultPreferences[$toggle] = [
470 'section' =>
'personal/i18n',
471 'label-message' =>
"tog-$toggle",
480 ParserOptions::newFromContext(
$context )
482 $oldsigHTML =
$context->getOutput()->parseInline( $oldsigWikiText,
true,
true );
483 $defaultPreferences[
'oldsig'] = [
486 'label-message' =>
'tog-oldsig',
487 'default' => $oldsigHTML,
488 'section' =>
'personal/signature',
490 $defaultPreferences[
'nickname'] = [
491 'type' => $this->authManager->allowsPropertyChange(
'nickname' ) ?
'text' :
'info',
492 'maxlength' => $this->config->get(
'MaxSigChars' ),
493 'label-message' =>
'yournick',
494 'validation-callback' =>
function ( $signature, $alldata,
HTMLForm $form ) {
497 'section' =>
'personal/signature',
498 'filter-callback' =>
function ( $signature, array $alldata,
HTMLForm $form ) {
502 $defaultPreferences[
'fancysig'] = [
504 'label-message' =>
'tog-fancysig',
506 'help-message' =>
'prefs-help-signature',
507 'section' =>
'personal/signature'
512 if ( $this->config->get(
'EnableEmail' ) ) {
513 if ( $canViewPrivateInfo ) {
514 $helpMessages[] = $this->config->get(
'EmailConfirmToEdit' )
515 ?
'prefs-help-email-required'
516 :
'prefs-help-email';
518 if ( $this->config->get(
'EnableUserEmail' ) ) {
520 $helpMessages[] =
'prefs-help-email-others';
523 $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) :
'';
524 if ( $canEditPrivateInfo && $this->authManager->allowsPropertyChange(
'emailaddress' ) ) {
525 $link = $this->linkRenderer->makeLink(
527 $context->msg( $user->getEmail() ?
'prefs-changeemail' :
'prefs-setemail' )->
text(),
531 $emailAddress .= $emailAddress ==
'' ?
$link : (
532 $context->msg(
'word-separator' )->escaped()
533 .
$context->msg(
'parentheses' )->rawParams(
$link )->escaped()
537 $defaultPreferences[
'emailaddress'] = [
540 'default' => $emailAddress,
541 'label-message' =>
'youremail',
542 'section' =>
'personal/email',
543 'help-messages' => $helpMessages,
544 # 'cssclass' chosen below
548 $disableEmailPrefs =
false;
550 if ( $this->config->get(
'EmailAuthentication' ) ) {
551 $emailauthenticationclass =
'mw-email-not-authenticated';
552 if ( $user->getEmail() ) {
553 if ( $user->getEmailAuthenticationTimestamp() ) {
558 $emailTimestamp = $user->getEmailAuthenticationTimestamp();
559 $time =
$lang->userTimeAndDate( $emailTimestamp, $displayUser );
560 $d =
$lang->userDate( $emailTimestamp, $displayUser );
561 $t =
$lang->userTime( $emailTimestamp, $displayUser );
562 $emailauthenticated =
$context->msg(
'emailauthenticated',
563 $time, $d,
$t )->parse() .
'<br />';
564 $disableEmailPrefs =
false;
565 $emailauthenticationclass =
'mw-email-authenticated';
567 $disableEmailPrefs =
true;
568 $emailauthenticated =
$context->msg(
'emailnotauthenticated' )->parse() .
'<br />' .
569 $this->linkRenderer->makeKnownLink(
571 $context->msg(
'emailconfirmlink' )->text()
573 $emailauthenticationclass =
"mw-email-not-authenticated";
576 $disableEmailPrefs =
true;
577 $emailauthenticated =
$context->msg(
'noemailprefs' )->escaped();
578 $emailauthenticationclass =
'mw-email-none';
581 if ( $canViewPrivateInfo ) {
582 $defaultPreferences[
'emailauthentication'] = [
585 'section' =>
'personal/email',
586 'label-message' =>
'prefs-emailconfirm-label',
587 'default' => $emailauthenticated,
588 # Apply the same CSS class used on the input to the message:
589 'cssclass' => $emailauthenticationclass,
594 if ( $this->config->get(
'EnableUserEmail' ) && $user->isAllowed(
'sendemail' ) ) {
595 $defaultPreferences[
'disablemail'] = [
596 'id' =>
'wpAllowEmail',
599 'section' =>
'personal/email',
600 'label-message' =>
'allowemail',
601 'disabled' => $disableEmailPrefs,
604 $defaultPreferences[
'email-allow-new-users'] = [
605 'id' =>
'wpAllowEmailFromNewUsers',
607 'section' =>
'personal/email',
608 'label-message' =>
'email-allow-new-users-label',
609 'disabled' => $disableEmailPrefs,
612 $defaultPreferences[
'ccmeonemails'] = [
614 'section' =>
'personal/email',
615 'label-message' =>
'tog-ccmeonemails',
616 'disabled' => $disableEmailPrefs,
619 if ( $this->config->get(
'EnableUserEmailBlacklist' ) ) {
621 $ids = $user->getOption(
'email-blacklist', [] );
622 $names = $ids ? $lookup->namesFromCentralIds( $ids, $user ) : [];
624 $defaultPreferences[
'email-blacklist'] = [
625 'type' =>
'usersmultiselect',
626 'label-message' =>
'email-blacklist-label',
627 'section' =>
'personal/email',
628 'default' => implode(
"\n", $names ),
629 'disabled' => $disableEmailPrefs,
634 if ( $this->config->get(
'EnotifWatchlist' ) ) {
635 $defaultPreferences[
'enotifwatchlistpages'] = [
637 'section' =>
'personal/email',
638 'label-message' =>
'tog-enotifwatchlistpages',
639 'disabled' => $disableEmailPrefs,
642 if ( $this->config->get(
'EnotifUserTalk' ) ) {
643 $defaultPreferences[
'enotifusertalkpages'] = [
645 'section' =>
'personal/email',
646 'label-message' =>
'tog-enotifusertalkpages',
647 'disabled' => $disableEmailPrefs,
650 if ( $this->config->get(
'EnotifUserTalk' ) || $this->config->get(
'EnotifWatchlist' ) ) {
651 if ( $this->config->get(
'EnotifMinorEdits' ) ) {
652 $defaultPreferences[
'enotifminoredits'] = [
654 'section' =>
'personal/email',
655 'label-message' =>
'tog-enotifminoredits',
656 'disabled' => $disableEmailPrefs,
660 if ( $this->config->get(
'EnotifRevealEditorAddress' ) ) {
661 $defaultPreferences[
'enotifrevealaddr'] = [
663 'section' =>
'personal/email',
664 'label-message' =>
'tog-enotifrevealaddr',
665 'disabled' => $disableEmailPrefs,
679 # # Skin #####################################
683 if ( $skinOptions ) {
684 $defaultPreferences[
'skin'] = [
686 'options' => $skinOptions,
687 'section' =>
'rendering/skin',
691 $allowUserCss = $this->config->get(
'AllowUserCss' );
692 $allowUserJs = $this->config->get(
'AllowUserJs' );
693 # Create links to user CSS/JS pages for all skins
694 # This code is basically copied from generateSkinOptions(). It'd
695 # be nice to somehow merge this back in there to avoid redundancy.
696 if ( $allowUserCss || $allowUserJs ) {
698 $userName = $user->getName();
700 if ( $allowUserCss ) {
701 $cssPage = Title::makeTitleSafe( NS_USER, $userName .
'/common.css' );
702 $cssLinkText =
$context->msg(
'prefs-custom-css' )->text();
703 $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
706 if ( $allowUserJs ) {
707 $jsPage = Title::makeTitleSafe( NS_USER, $userName .
'/common.js' );
708 $jsLinkText =
$context->msg(
'prefs-custom-js' )->text();
709 $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
712 $defaultPreferences[
'commoncssjs'] = [
715 'default' =>
$context->getLanguage()->pipeList( $linkTools ),
716 'label-message' =>
'prefs-common-config',
717 'section' =>
'rendering/skin',
727 # # Files #####################################
728 $defaultPreferences[
'imagesize'] = [
731 'label-message' =>
'imagemaxsize',
732 'section' =>
'rendering/files',
734 $defaultPreferences[
'thumbsize'] = [
737 'label-message' =>
'thumbsize',
738 'section' =>
'rendering/files',
749 # # Date and time #####################################
751 if ( $dateOptions ) {
752 $defaultPreferences[
'date'] = [
754 'options' => $dateOptions,
755 'section' =>
'rendering/dateformat',
762 $nowlocal =
Xml::element(
'span', [
'id' =>
'wpLocalTime' ],
763 $lang->userTime( $now, $user ) );
764 $nowserver =
$lang->userTime( $now, $user,
765 [
'format' =>
false,
'timecorrection' =>
false ] ) .
766 Html::hidden(
'wpServerTime', (
int)substr( $now, 8, 2 ) * 60 + (
int)substr( $now, 10, 2 ) );
768 $defaultPreferences[
'nowserver'] = [
771 'label-message' =>
'servertime',
772 'default' => $nowserver,
773 'section' =>
'rendering/timeoffset',
776 $defaultPreferences[
'nowlocal'] = [
779 'label-message' =>
'localtime',
780 'default' => $nowlocal,
781 'section' =>
'rendering/timeoffset',
785 $tzOffset = $user->getOption(
'timecorrection' );
786 $tz = explode(
'|', $tzOffset, 3 );
790 $tzSetting = $tzOffset;
791 if ( count( $tz ) > 1 && $tz[0] ==
'ZoneInfo' &&
796 $userTZ =
new DateTimeZone( $tz[2] );
797 $minDiff = floor( $userTZ->getOffset(
new DateTime(
'now' ) ) / 60 );
798 $tzSetting =
"ZoneInfo|$minDiff|{$tz[2]}";
799 }
catch ( Exception
$e ) {
804 if ( count( $tz ) > 1 && $tz[0] ==
'Offset' ) {
806 $tzSetting = sprintf(
'%+03d:%02d', floor( $minDiff / 60 ), abs( $minDiff ) % 60 );
809 $defaultPreferences[
'timecorrection'] = [
810 'class' => \HTMLSelectOrOtherField::class,
811 'label-message' =>
'timezonelegend',
812 'options' => $tzOptions,
813 'default' => $tzSetting,
815 'section' =>
'rendering/timeoffset',
824 # # Diffs ####################################
825 $defaultPreferences[
'diffonly'] = [
827 'section' =>
'rendering/diffs',
828 'label-message' =>
'tog-diffonly',
830 $defaultPreferences[
'norollbackdiff'] = [
832 'section' =>
'rendering/diffs',
833 'label-message' =>
'tog-norollbackdiff',
836 # # Page Rendering ##############################
837 if ( $this->config->get(
'AllowUserCssPrefs' ) ) {
838 $defaultPreferences[
'underline'] = [
841 $l10n->msg(
'underline-never' )->text() => 0,
842 $l10n->msg(
'underline-always' )->text() => 1,
843 $l10n->msg(
'underline-default' )->text() => 2,
845 'label-message' =>
'tog-underline',
846 'section' =>
'rendering/advancedrendering',
850 $stubThresholdValues = [ 50, 100, 500, 1000, 2000, 5000, 10000 ];
851 $stubThresholdOptions = [ $l10n->msg(
'stub-threshold-disabled' )->text() => 0 ];
852 foreach ( $stubThresholdValues as
$value ) {
853 $stubThresholdOptions[$l10n->msg(
'size-bytes',
$value )->text()] =
$value;
856 $defaultPreferences[
'stubthreshold'] = [
858 'section' =>
'rendering/advancedrendering',
859 'options' => $stubThresholdOptions,
861 'label-raw' => $l10n->msg(
'stub-threshold' )->rawParams(
862 '<a href="#" class="stub">' .
863 $l10n->msg(
'stub-threshold-sample-link' )->parse() .
867 $defaultPreferences[
'showhiddencats'] = [
869 'section' =>
'rendering/advancedrendering',
870 'label-message' =>
'tog-showhiddencats'
873 $defaultPreferences[
'numberheadings'] = [
875 'section' =>
'rendering/advancedrendering',
876 'label-message' =>
'tog-numberheadings',
886 # # Editing #####################################
887 $defaultPreferences[
'editsectiononrightclick'] = [
889 'section' =>
'editing/advancedediting',
890 'label-message' =>
'tog-editsectiononrightclick',
892 $defaultPreferences[
'editondblclick'] = [
894 'section' =>
'editing/advancedediting',
895 'label-message' =>
'tog-editondblclick',
898 if ( $this->config->get(
'AllowUserCssPrefs' ) ) {
899 $defaultPreferences[
'editfont'] = [
901 'section' =>
'editing/editor',
902 'label-message' =>
'editfont-style',
904 $l10n->msg(
'editfont-monospace' )->text() =>
'monospace',
905 $l10n->msg(
'editfont-sansserif' )->text() =>
'sans-serif',
906 $l10n->msg(
'editfont-serif' )->text() =>
'serif',
911 if ( $user->isAllowed(
'minoredit' ) ) {
912 $defaultPreferences[
'minordefault'] = [
914 'section' =>
'editing/editor',
915 'label-message' =>
'tog-minordefault',
919 $defaultPreferences[
'forceeditsummary'] = [
921 'section' =>
'editing/editor',
922 'label-message' =>
'tog-forceeditsummary',
924 $defaultPreferences[
'useeditwarning'] = [
926 'section' =>
'editing/editor',
927 'label-message' =>
'tog-useeditwarning',
929 $defaultPreferences[
'showtoolbar'] = [
931 'section' =>
'editing/editor',
932 'label-message' =>
'tog-showtoolbar',
935 $defaultPreferences[
'previewonfirst'] = [
937 'section' =>
'editing/preview',
938 'label-message' =>
'tog-previewonfirst',
940 $defaultPreferences[
'previewontop'] = [
942 'section' =>
'editing/preview',
943 'label-message' =>
'tog-previewontop',
945 $defaultPreferences[
'uselivepreview'] = [
947 'section' =>
'editing/preview',
948 'label-message' =>
'tog-uselivepreview',
958 $rcMaxAge = $this->config->get(
'RCMaxAge' );
959 # # RecentChanges #####################################
960 $defaultPreferences[
'rcdays'] = [
962 'label-message' =>
'recentchangesdays',
963 'section' =>
'rc/displayrc',
965 'max' => ceil( $rcMaxAge / ( 3600 * 24 ) ),
966 'help' => $l10n->msg(
'recentchangesdays-max' )->numParams(
967 ceil( $rcMaxAge / ( 3600 * 24 ) ) )->escaped()
969 $defaultPreferences[
'rclimit'] = [
973 'label-message' =>
'recentchangescount',
974 'help-message' =>
'prefs-help-recentchangescount',
975 'section' =>
'rc/displayrc',
977 $defaultPreferences[
'usenewrc'] = [
979 'label-message' =>
'tog-usenewrc',
980 'section' =>
'rc/advancedrc',
982 $defaultPreferences[
'hideminor'] = [
984 'label-message' =>
'tog-hideminor',
985 'section' =>
'rc/advancedrc',
987 $defaultPreferences[
'rcfilters-saved-queries'] = [
990 $defaultPreferences[
'rcfilters-wl-saved-queries'] = [
994 $defaultPreferences[
'rcfilters-limit'] = [
997 $defaultPreferences[
'rcfilters-saved-queries-versionbackup'] = [
1000 $defaultPreferences[
'rcfilters-wl-saved-queries-versionbackup'] = [
1004 if ( $this->config->get(
'RCWatchCategoryMembership' ) ) {
1005 $defaultPreferences[
'hidecategorization'] = [
1007 'label-message' =>
'tog-hidecategorization',
1008 'section' =>
'rc/advancedrc',
1012 if ( $user->useRCPatrol() ) {
1013 $defaultPreferences[
'hidepatrolled'] = [
1015 'section' =>
'rc/advancedrc',
1016 'label-message' =>
'tog-hidepatrolled',
1020 if ( $user->useNPPatrol() ) {
1021 $defaultPreferences[
'newpageshidepatrolled'] = [
1023 'section' =>
'rc/advancedrc',
1024 'label-message' =>
'tog-newpageshidepatrolled',
1028 if ( $this->config->get(
'RCShowWatchingUsers' ) ) {
1029 $defaultPreferences[
'shownumberswatching'] = [
1031 'section' =>
'rc/advancedrc',
1032 'label-message' =>
'tog-shownumberswatching',
1036 if ( $this->config->get(
'StructuredChangeFiltersShowPreference' ) ) {
1037 $defaultPreferences[
'rcenhancedfilters-disable'] = [
1039 'section' =>
'rc/opt-out',
1040 'label-message' =>
'rcfilters-preference-label',
1041 'help-message' =>
'rcfilters-preference-help',
1054 $watchlistdaysMax = ceil( $this->config->get(
'RCMaxAge' ) / ( 3600 * 24 ) );
1056 # # Watchlist #####################################
1057 if ( $user->isAllowed(
'editmywatchlist' ) ) {
1058 $editWatchlistLinks = [];
1059 $editWatchlistModes = [
1060 'edit' => [
'EditWatchlist',
false ],
1061 'raw' => [
'EditWatchlist',
'raw' ],
1062 'clear' => [
'EditWatchlist',
'clear' ],
1064 foreach ( $editWatchlistModes as $editWatchlistMode => $mode ) {
1066 $editWatchlistLinks[] = $this->linkRenderer->makeKnownLink(
1068 new HtmlArmor(
$context->msg(
"prefs-editwatchlist-{$editWatchlistMode}" )->parse() )
1072 $defaultPreferences[
'editwatchlist'] = [
1075 'default' =>
$context->getLanguage()->pipeList( $editWatchlistLinks ),
1076 'label-message' =>
'prefs-editwatchlist-label',
1077 'section' =>
'watchlist/editwatchlist',
1081 $defaultPreferences[
'watchlistdays'] = [
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',
1090 $defaultPreferences[
'wllimit'] = [
1094 'label-message' =>
'prefs-watchlist-edits',
1095 'help' =>
$context->msg(
'prefs-watchlist-edits-max' )->escaped(),
1096 'section' =>
'watchlist/displaywatchlist',
1098 $defaultPreferences[
'extendwatchlist'] = [
1100 'section' =>
'watchlist/advancedwatchlist',
1101 'label-message' =>
'tog-extendwatchlist',
1103 $defaultPreferences[
'watchlisthideminor'] = [
1105 'section' =>
'watchlist/advancedwatchlist',
1106 'label-message' =>
'tog-watchlisthideminor',
1108 $defaultPreferences[
'watchlisthidebots'] = [
1110 'section' =>
'watchlist/advancedwatchlist',
1111 'label-message' =>
'tog-watchlisthidebots',
1113 $defaultPreferences[
'watchlisthideown'] = [
1115 'section' =>
'watchlist/advancedwatchlist',
1116 'label-message' =>
'tog-watchlisthideown',
1118 $defaultPreferences[
'watchlisthideanons'] = [
1120 'section' =>
'watchlist/advancedwatchlist',
1121 'label-message' =>
'tog-watchlisthideanons',
1123 $defaultPreferences[
'watchlisthideliu'] = [
1125 'section' =>
'watchlist/advancedwatchlist',
1126 'label-message' =>
'tog-watchlisthideliu',
1133 $defaultPreferences[
'watchlistreloadautomatically'] = [
1135 'section' =>
'watchlist/advancedwatchlist',
1136 'label-message' =>
'tog-watchlistreloadautomatically',
1140 $defaultPreferences[
'watchlistunwatchlinks'] = [
1142 'section' =>
'watchlist/advancedwatchlist',
1143 'label-message' =>
'tog-watchlistunwatchlinks',
1146 if ( $this->config->get(
'RCWatchCategoryMembership' ) ) {
1147 $defaultPreferences[
'watchlisthidecategorization'] = [
1149 'section' =>
'watchlist/advancedwatchlist',
1150 'label-message' =>
'tog-watchlisthidecategorization',
1154 if ( $user->useRCPatrol() ) {
1155 $defaultPreferences[
'watchlisthidepatrolled'] = [
1157 'section' =>
'watchlist/advancedwatchlist',
1158 'label-message' =>
'tog-watchlisthidepatrolled',
1163 'edit' =>
'watchdefault',
1164 'move' =>
'watchmoves',
1165 'delete' =>
'watchdeletion'
1169 if ( $user->isAllowed(
'createpage' ) || $user->isAllowed(
'createtalk' ) ) {
1170 $watchTypes[
'read'] =
'watchcreations';
1173 if ( $user->isAllowed(
'rollback' ) ) {
1174 $watchTypes[
'rollback'] =
'watchrollback';
1177 if ( $user->isAllowed(
'upload' ) ) {
1178 $watchTypes[
'upload'] =
'watchuploads';
1181 foreach ( $watchTypes as $action => $pref ) {
1182 if ( $user->isAllowed( $action ) ) {
1186 $defaultPreferences[$pref] = [
1188 'section' =>
'watchlist/advancedwatchlist',
1189 'label-message' =>
"tog-$pref",
1194 if ( $this->config->get(
'EnableAPI' ) ) {
1195 $defaultPreferences[
'watchlisttoken'] = [
1198 $defaultPreferences[
'watchlisttoken-info'] = [
1200 'section' =>
'watchlist/tokenwatchlist',
1201 'label-message' =>
'prefs-watchlist-token',
1202 'default' => $user->getTokenFromOption(
'watchlisttoken' ),
1203 'help-message' =>
'prefs-help-watchlist-token2',
1212 foreach ( MWNamespace::getValidNamespaces() as $n ) {
1213 $defaultPreferences[
'searchNs' . $n] = [
1227 $mptitle = Title::newMainPage();
1228 $previewtext =
$context->msg(
'skin-preview' )->escaped();
1230 # Only show skins that aren't disabled in $wgSkipSkins
1233 foreach ( $validSkinNames as $skinkey => &$skinname ) {
1234 $msg =
$context->msg(
"skinname-{$skinkey}" );
1235 if ( $msg->exists() ) {
1236 $skinname = htmlspecialchars( $msg->text() );
1240 $defaultSkin = $this->config->get(
'DefaultSkin' );
1241 $allowUserCss = $this->config->get(
'AllowUserCss' );
1242 $allowUserJs = $this->config->get(
'AllowUserJs' );
1244 # Sort by the internal name, so that the ordering is the same for each display language,
1245 # especially if some skin names are translated to use a different alphabet and some are not.
1246 uksort( $validSkinNames,
function ( $a, $b ) use ( $defaultSkin ) {
1247 # Display the default first in the list by comparing it as lesser than any other.
1248 if ( strcasecmp( $a, $defaultSkin ) === 0 ) {
1251 if ( strcasecmp( $b, $defaultSkin ) === 0 ) {
1254 return strcasecmp( $a, $b );
1257 $foundDefault =
false;
1258 foreach ( $validSkinNames as $skinkey => $sn ) {
1261 # Mark the default skin
1262 if ( strcasecmp( $skinkey, $defaultSkin ) === 0 ) {
1263 $linkTools[] =
$context->msg(
'default' )->escaped();
1264 $foundDefault =
true;
1267 # Create preview link
1268 $mplink = htmlspecialchars( $mptitle->getLocalURL( [
'useskin' => $skinkey ] ) );
1269 $linkTools[] =
"<a target='_blank' href=\"$mplink\">$previewtext</a>";
1271 # Create links to user CSS/JS pages
1272 if ( $allowUserCss ) {
1273 $cssPage = Title::makeTitleSafe( NS_USER, $user->getName() .
'/' . $skinkey .
'.css' );
1274 $cssLinkText =
$context->msg(
'prefs-custom-css' )->text();
1275 $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
1278 if ( $allowUserJs ) {
1279 $jsPage = Title::makeTitleSafe( NS_USER, $user->getName() .
'/' . $skinkey .
'.js' );
1280 $jsLinkText =
$context->msg(
'prefs-custom-js' )->text();
1281 $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
1284 $display = $sn .
' ' .
$context->msg(
'parentheses' )
1285 ->rawParams(
$context->getLanguage()->pipeList( $linkTools ) )
1287 $ret[$display] = $skinkey;
1290 if ( !$foundDefault ) {
1305 $dateopts =
$lang->getDatePreferences();
1310 if ( !in_array(
'default', $dateopts ) ) {
1311 $dateopts[] =
'default';
1321 foreach ( $dateopts as $key ) {
1322 if ( $key ==
'default' ) {
1323 $formatted =
$context->msg(
'datedefault' )->escaped();
1325 $formatted = htmlspecialchars(
$lang->timeanddate( $epoch,
false, $key ) );
1327 $ret[$formatted] = $key;
1339 $pixels = $l10n->msg(
'unit-pixel' )->text();
1341 foreach ( $this->config->get(
'ImageLimits' ) as $index => $limits ) {
1343 $display =
"{$limits[0]}" . json_decode(
'"\u200e"' ) .
"×{$limits[1]}" . $pixels;
1344 $ret[$display] = $index;
1356 $pixels = $l10n->msg(
'unit-pixel' )->text();
1358 foreach ( $this->config->get(
'ThumbLimits' ) as $index => $size ) {
1359 $display = $size . $pixels;
1360 $ret[$display] = $index;
1373 $maxSigChars = $this->config->get(
'MaxSigChars' );
1374 if ( mb_strlen( $signature ) > $maxSigChars ) {
1376 $form->msg(
'badsiglength' )->numParams( $maxSigChars )->text() );
1377 } elseif ( isset( $alldata[
'fancysig'] ) &&
1378 $alldata[
'fancysig'] &&
1383 [
'class' =>
'error' ],
1384 $form->msg(
'badsig' )->text()
1399 if ( isset( $alldata[
'fancysig'] ) && $alldata[
'fancysig'] ) {
1400 $signature =
$parser->cleanSig( $signature );
1419 $formClass = PreferencesForm::class,
1423 if ( count( $remove ) ) {
1424 $removeKeys = array_flip( $remove );
1425 $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
1429 foreach ( $formDescriptor as $name => $info ) {
1430 if ( isset( $info[
'type'] ) && $info[
'type'] ===
'api' ) {
1431 unset( $formDescriptor[$name] );
1438 $htmlForm =
new $formClass( $formDescriptor,
$context,
'prefs' );
1440 $htmlForm->setModifiedUser( $user );
1441 $htmlForm->setId(
'mw-prefs-form' );
1442 $htmlForm->setAutocomplete(
'off' );
1443 $htmlForm->setSubmitText(
$context->msg(
'saveprefs' )->text() );
1444 # Used message keys: 'accesskey-preferences-save', 'tooltip-preferences-save'
1445 $htmlForm->setSubmitTooltip(
'preferences-save' );
1446 $htmlForm->setSubmitID(
'prefcontrol' );
1447 $htmlForm->setSubmitCallback(
function ( array $formData,
PreferencesForm $form ) {
1448 return $this->
submitForm( $formData, $form );
1461 $localTZoffset = $this->config->get(
'LocalTZoffset' );
1466 if ( $localTZoffset == $timestamp->format(
'Z' ) / 60 ) {
1467 $timezoneName = $timestamp->getTimezone()->getName();
1469 if ( isset( $timeZoneList[$timezoneName] ) ) {
1470 $timezoneName = $timeZoneList[$timezoneName][
'name'];
1473 'timezoneuseserverdefault',
1477 $tzstring = sprintf(
1479 floor( $localTZoffset / 60 ),
1480 abs( $localTZoffset ) % 60
1482 $server_tz_msg =
$context->msg(
'timezoneuseserverdefault', $tzstring )->text();
1484 $opt[$server_tz_msg] =
"System|$localTZoffset";
1485 $opt[
$context->msg(
'timezoneuseoffset' )->text()] =
'other';
1486 $opt[
$context->msg(
'guesstimezone' )->text()] =
'guess';
1488 foreach ( $timeZoneList as $timeZoneInfo ) {
1489 $region = $timeZoneInfo[
'region'];
1490 if ( !isset(
$opt[$region] ) ) {
1493 $opt[$region][$timeZoneInfo[
'name']] = $timeZoneInfo[
'timecorrection'];
1504 $data = explode(
'|', $tz, 3 );
1505 switch ( $data[0] ) {
1509 if ( count( $data ) === 3 ) {
1512 new DateTimeZone( $data[2] );
1515 }
catch ( Exception
$e ) {
1522 return 'Offset|' . intval( $tz[1] );
1528 $data = explode(
':', $tz, 2 );
1529 if ( count( $data ) == 2 ) {
1530 $data[0] = intval( $data[0] );
1531 $data[1] = intval( $data[1] );
1532 $minDiff = abs( $data[0] ) * 60 + $data[1];
1533 if ( $data[0] < 0 ) {
1534 $minDiff = - $minDiff;
1537 $minDiff = intval( $data[0] ) * 60;
1540 # Max is +14:00 and min is -12:00, see:
1542 $minDiff = min( $minDiff, 840 ); # 14:00
1543 $minDiff = max( $minDiff, -720 ); # -12:00
1544 return 'Offset|' . $minDiff;
1556 $user = $form->getModifiedUser();
1557 $hiddenPrefs = $this->config->get(
'HiddenPrefs' );
1560 if ( !$user->isAllowedAny(
'editmyprivateinfo',
'editmyoptions' ) ) {
1561 return Status::newFatal(
'mypreferencesprotected' );
1565 foreach ( array_keys( $formData ) as $name ) {
1567 if ( isset( $filters[$name] ) ) {
1568 $formData[
$name] = call_user_func( $filters[$name], $formData[$name], $formData );
1575 if ( !in_array(
'realname', $hiddenPrefs )
1576 && $user->isAllowed(
'editmyprivateinfo' )
1577 && array_key_exists(
'realname', $formData )
1579 $realName = $formData[
'realname'];
1580 $user->setRealName( $realName );
1583 if ( $user->isAllowed(
'editmyoptions' ) ) {
1584 $oldUserOptions = $user->getOptions();
1587 unset( $formData[$b] );
1590 # If users have saved a value for a preference which has subsequently been disabled
1591 # via $wgHiddenPrefs, we don't want to destroy that setting in case the preference
1592 # is subsequently re-enabled
1593 foreach ( $hiddenPrefs as $pref ) {
1594 # If the user has not set a non-default value here, the default will be returned
1595 # and subsequently discarded
1596 $formData[$pref] = $user->getOption( $pref,
null,
true );
1601 isset( $formData[
'rclimit'] ) &&
1602 intval( $formData[
'rclimit' ] ) !== $user->getIntOption(
'rclimit' )
1604 $formData[
'rcfilters-limit'] = $formData[
'rclimit'];
1608 $user->resetOptions(
'unused', $form->getContext() );
1610 foreach ( $formData as $key =>
$value ) {
1611 $user->setOption( $key,
$value );
1615 'PreferencesFormPreSave',
1616 [ $formData, $form, $user, &$result, $oldUserOptions ]
1620 AuthManager::callLegacyAuthPlugin(
'updateExternalDB', [ $user ] );
1621 $user->saveSettings();
1652 if (
$res ===
'eauth' ) {
1653 $urlOptions[
'eauth'] = 1;
1656 $urlOptions += $form->getExtraSuccessRedirectParameters();
1658 $url = $form->getTitle()->getFullURL( $urlOptions );
1662 $context->getRequest()->getSession()->set(
'specialPreferencesSaveSuccess', 1 );
1664 $context->getOutput()->redirect( $url );
1667 return Status::newGood();
1680 return $this->
submitForm( $formData, $form );
1692 $identifiers = DateTimeZone::listIdentifiers();
1693 if ( $identifiers ===
false ) {
1696 sort( $identifiers );
1699 'Africa' =>
wfMessage(
'timezoneregion-africa' )->inLanguage( $language )->text(),
1700 'America' =>
wfMessage(
'timezoneregion-america' )->inLanguage( $language )->text(),
1701 'Antarctica' =>
wfMessage(
'timezoneregion-antarctica' )->inLanguage( $language )->text(),
1702 'Arctic' =>
wfMessage(
'timezoneregion-arctic' )->inLanguage( $language )->text(),
1703 'Asia' =>
wfMessage(
'timezoneregion-asia' )->inLanguage( $language )->text(),
1704 'Atlantic' =>
wfMessage(
'timezoneregion-atlantic' )->inLanguage( $language )->text(),
1705 'Australia' =>
wfMessage(
'timezoneregion-australia' )->inLanguage( $language )->text(),
1706 'Europe' =>
wfMessage(
'timezoneregion-europe' )->inLanguage( $language )->text(),
1707 'Indian' =>
wfMessage(
'timezoneregion-indian' )->inLanguage( $language )->text(),
1708 'Pacific' =>
wfMessage(
'timezoneregion-pacific' )->inLanguage( $language )->text(),
1710 asort( $tzRegions );
1714 $now =
new DateTime();
1716 foreach ( $identifiers as $identifier ) {
1717 $parts = explode(
'/', $identifier, 2 );
1722 if ( count( $parts ) !== 2 || !array_key_exists( $parts[0], $tzRegions ) ) {
1727 $parts[0] = $tzRegions[$parts[0]];
1729 $dateTimeZone =
new DateTimeZone( $identifier );
1730 $minDiff = floor( $dateTimeZone->getOffset( $now ) / 60 );
1732 $display = str_replace(
'_',
' ', $parts[0] .
'/' . $parts[1] );
1733 $value =
"ZoneInfo|$minDiff|$identifier";
1735 $timeZoneList[$identifier] = [
1737 'timecorrection' =>
$value,
1738 'region' => $parts[0],
1742 return $timeZoneList;
$wgDefaultUserOptions
Settings added to this array will override the default globals for the user preferences used by anony...
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfCanIPUseHTTPS( $ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS.
The CentralIdLookup service allows for connecting local users with cluster-wide IDs.
static factory( $providerId=null)
Fetch a CentralIdLookup.
Marks HTML that shouldn't be escaped.
This class is a collection of static functions that serve two purposes:
Methods for dealing with language codes.
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
Base class for language conversion.
static array $languagesWithVariants
languages supporting variants
Internationalisation code.
This is a utility class with only static functions for dealing with namespaces that encodes all the "...
Library for creating and parsing MW-style timestamps.
static getLocalInstance( $ts=false)
Get a timestamp instance in the server local timezone ($wgLocaltimezone)
Set options of the Parser.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
static cleanSigInSig( $text)
Strip 3, 4 or 5 tildes out of signatures.
The main skin class which provides methods and properties for all other skins.
static getAllowedSkins()
Fetch the list of user-selectable skins in regards to $wgSkipSkins.
Parent class for all special pages.
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,...
static checkStructuredFilterUiEnabled(Config $config, User $user)
Static method to check whether StructuredFilter UI is enabled for the given user.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Represents a title within MediaWiki.
Represents a "user group membership" – a specific instance of a user belonging to a group.
static getLink( $ugm, IContextSource $context, $format, $userName=null)
Gets a link for a user group, possibly including the expiry date if relevant.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Module of static functions for generating XML.
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
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
namespace being checked & $result
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
do that in ParserLimitReportFormat instead $parser
see documentation in includes Linker php for Linker::makeImageLink & $time
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
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
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 you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
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 "<div ...>$1</div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
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
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Allows to change the fields on the form that will be generated $name
processing should stop and the error should be shown to the user * false
returning false will NOT prevent logging $e
Interface for configuration instances.
Interface for objects which can provide a MediaWiki context on request.
Interface for localizing messages in MediaWiki.
if(!isset( $args[0])) $lang
switch( $options['output']) $languages