52use Psr\Log\LoggerAwareTrait;
53use Psr\Log\NullLogger;
57use UnexpectedValueException;
102 'AllowRequiringEmailForResets',
107 'EmailAuthentication',
108 'EmailConfirmToEdit',
111 'EnableUserEmailBlacklist',
113 'EnotifRevealEditorAddress',
123 'RCShowWatchingUsers',
124 'RCWatchCategoryMembership',
125 'SearchMatchRedirectPreference',
128 'SignatureValidation',
164 $this->logger =
new NullLogger();
167 wfDeprecated( __METHOD__ .
' without $languageConverter parameter',
'1.35' );
169 ->getLanguageConverterFactory()
170 ->getLanguageConverter();
175 wfDeprecated( __METHOD__ .
' without $languageNameUtils parameter',
'1.35' );
180 if ( !$hookContainer ) {
181 wfDeprecated( __METHOD__ .
' without $hookContainer parameter',
'1.35' );
184 $this->hookRunner =
new HookRunner( $hookContainer );
211 OutputPage::setupOOUI(
212 strtolower( $context->
getSkin()->getSkinName() ),
227 $this->hookRunner->onGetPreferences( $user, $preferences );
230 $this->logger->debug(
"Created form descriptor for user '{$user->getName()}'" );
244 foreach ( $this->options->get(
'HiddenPrefs' ) as $pref ) {
245 unset( $defaultPreferences[$pref] );
249 $dummyForm =
new HTMLForm( [], $context );
251 $disable = !$this->permissionManager->userHasRight( $user,
'editmyoptions' );
253 $defaultOptions = $this->userOptionsLookup->getDefaultOptions();
255 $this->
applyFilters( $userOptions, $defaultPreferences,
'filterForForm' );
257 foreach ( $defaultPreferences as $name => &$info ) {
260 $info[
'disabled'] =
'disabled';
262 $field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm );
263 $globalDefault = $defaultOptions[$name] ??
null;
266 if ( isset( $info[
'default'] ) ) {
269 } elseif ( $prefFromUser !==
null &&
270 $field->validate( $prefFromUser, $user->
getOptions() ) ===
true ) {
271 $info[
'default'] = $prefFromUser;
272 } elseif ( $field->validate( $globalDefault, $user->
getOptions() ) ===
true ) {
273 $info[
'default'] = $globalDefault;
275 $globalDefault = json_encode( $globalDefault );
277 "Default '$globalDefault' is invalid for preference $name of user $user"
282 return $defaultPreferences;
294 $val = $userOptions[$name] ??
null;
297 if ( ( isset( $info[
'type'] ) && $info[
'type'] ==
'multiselect' ) ||
298 ( isset( $info[
'class'] ) && $info[
'class'] == \HTMLMultiSelectField::class ) ) {
299 $options = HTMLFormField::flattenOptions( $info[
'options'] );
300 $prefix = $info[
'prefix'] ?? $name;
304 if ( $userOptions[
"$prefix$value"] ??
false ) {
311 if ( ( isset( $info[
'type'] ) && $info[
'type'] ==
'checkmatrix' ) ||
312 ( isset( $info[
'class'] ) && $info[
'class'] == \HTMLCheckMatrix::class ) ) {
313 $columns = HTMLFormField::flattenOptions( $info[
'columns'] );
314 $rows = HTMLFormField::flattenOptions( $info[
'rows'] );
315 $prefix = $info[
'prefix'] ?? $name;
318 foreach ( $columns as $column ) {
319 foreach ( $rows as $row ) {
320 if ( $userOptions[
"$prefix$column-$row"] ??
false ) {
321 $val[] =
"$column-$row";
348 $defaultPreferences[
'username'] = [
350 'label-message' => [
'username', $userName ],
351 'default' => $userName,
352 'section' =>
'personal/info',
360 $userGroups = $userMembers = $userTempGroups = $userTempMembers = [];
361 foreach ( $userEffectiveGroups as $ueg ) {
367 $groupStringOrObject = $userGroupMemberships[$ueg] ?? $ueg;
369 $userG = UserGroupMembership::getLink( $groupStringOrObject, $context,
'html' );
370 $userM = UserGroupMembership::getLink( $groupStringOrObject, $context,
'html',
380 $userTempGroups[] = $userG;
381 $userTempMembers[] = $userM;
383 $userGroups[] = $userG;
384 $userMembers[] = $userM;
388 sort( $userMembers );
389 sort( $userTempGroups );
390 sort( $userTempMembers );
391 $userGroups = array_merge( $userTempGroups, $userGroups );
392 $userMembers = array_merge( $userTempMembers, $userMembers );
394 $defaultPreferences[
'usergroups'] = [
396 'label' => $context->
msg(
'prefs-memberingroups' )->numParams(
397 count( $userGroups ) )->params( $userName )->text(),
399 'default' => $context->
msg(
'prefs-memberingroups-type' )
400 ->rawParams(
$lang->commaList( $userGroups ),
$lang->commaList( $userMembers ) )
403 'section' =>
'personal/info',
408 $editCount = $this->linkRenderer->makeLink( $contribTitle, $formattedEditCount );
410 $defaultPreferences[
'editcount'] = [
413 'label-message' =>
'prefs-edits',
414 'default' => $editCount,
415 'section' =>
'personal/info',
419 $displayUser = $context->
getUser();
421 $defaultPreferences[
'registrationdate'] = [
423 'label-message' =>
'prefs-registration',
424 'default' => $context->
msg(
425 'prefs-registration-date-time',
426 $lang->userTimeAndDate( $userRegistration, $displayUser ),
427 $lang->userDate( $userRegistration, $displayUser ),
428 $lang->userTime( $userRegistration, $displayUser )
430 'section' =>
'personal/info',
434 $canViewPrivateInfo = $this->permissionManager->userHasRight( $user,
'viewmyprivateinfo' );
435 $canEditPrivateInfo = $this->permissionManager->userHasRight( $user,
'editmyprivateinfo' );
438 $defaultPreferences[
'realname'] = [
440 'type' => $canEditPrivateInfo && $this->authManager->allowsPropertyChange(
'realname' )
443 'section' =>
'personal/info',
444 'label-message' =>
'yourrealname',
445 'help-message' =>
'prefs-help-realname',
448 if ( $canEditPrivateInfo && $this->authManager->allowsAuthenticationDataChange(
451 $defaultPreferences[
'password'] = [
454 'default' => (string)
new \OOUI\ButtonWidget( [
458 'label' => $context->
msg(
'prefs-resetpass' )->text(),
460 'label-message' =>
'yourpassword',
462 'help' => $this->options->get(
'AllowRequiringEmailForResets' ) && $user->
getEmail()
463 ? $context->
msg(
'prefs-help-yourpassword',
464 '[[#mw-prefsection-personal-email|{{int:prefs-email}}]]' )->parse()
466 'section' =>
'personal/info',
470 if ( !$this->options->get(
'ForceHTTPS' )
471 && $this->options->get(
'SecureLogin' )
474 $defaultPreferences[
'prefershttps'] = [
476 'label-message' =>
'tog-prefershttps',
477 'help-message' =>
'prefs-help-prefershttps',
478 'section' =>
'personal/info'
482 $defaultPreferences[
'downloaduserdata'] = [
485 'label-message' =>
'prefs-user-downloaddata-label',
486 'default' => HTML::Element(
489 'href' => $this->options->get(
'ScriptPath' ) .
490 '/api.php?action=query&meta=userinfo&uiprop=*',
492 $context->
msg(
'prefs-user-downloaddata-info' )->text()
494 'help-message' => [
'prefs-user-downloaddata-help-message', urlencode( $user->
getTitleKey() ) ],
495 'section' =>
'personal/info',
498 $languages = $this->languageNameUtils->getLanguageNames(
null,
'mwfile' );
499 $languageCode = $this->options->get(
'LanguageCode' );
500 if ( !array_key_exists( $languageCode, $languages ) ) {
501 $languages[$languageCode] = $languageCode;
507 foreach ( $languages as $code => $name ) {
508 $display = LanguageCode::bcp47( $code ) .
' - ' . $name;
511 $defaultPreferences[
'language'] = [
513 'section' =>
'personal/i18n',
515 'label-message' =>
'yourlanguage',
518 $neutralGenderMessage = $context->
msg(
'gender-notknown' )->escaped() . (
519 !$context->
msg(
'gender-unknown' )->isDisabled()
520 ?
"<br>" . $context->
msg(
'parentheses' )
521 ->params( $context->
msg(
'gender-unknown' )->plain() )
526 $defaultPreferences[
'gender'] = [
528 'section' =>
'personal/i18n',
530 $neutralGenderMessage =>
'unknown',
531 $context->
msg(
'gender-female' )->escaped() =>
'female',
532 $context->
msg(
'gender-male' )->escaped() =>
'male',
534 'label-message' =>
'yourgender',
535 'help-message' =>
'prefs-help-gender',
539 $languageConverterFactory = $services->getLanguageConverterFactory();
540 if ( !$languageConverterFactory->isConversionDisabled() ) {
542 foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
543 if ( $langCode == $this->contLang->getCode() ) {
544 if ( !$this->languageConverter->hasVariants() ) {
548 $variants = $this->languageConverter->getVariants();
550 foreach ( $variants as $v ) {
551 $v = str_replace(
'_',
'-', strtolower( $v ) );
552 $variantArray[$v] =
$lang->getVariantname( $v,
false );
556 foreach ( $variantArray as $code => $name ) {
557 $display = LanguageCode::bcp47( $code ) .
' - ' . $name;
561 $defaultPreferences[
'variant'] = [
562 'label-message' =>
'yourvariant',
565 'section' =>
'personal/i18n',
566 'help-message' =>
'prefs-help-variant',
569 $defaultPreferences[
"variant-$langCode"] = [
577 $oldsigWikiText = $services->getParser()->preSaveTransform(
581 ParserOptions::newFromContext( $context )
584 $context->
getOutput()->parseAsContent( $oldsigWikiText )
586 $signatureFieldConfig = [];
588 if ( $services->getUserOptionsLookup()->getBoolOption( $user,
'fancysig' ) ) {
592 ParserOptions::newFromContext( $context )
594 $signatureErrors = $validator->validateSignature( $user->
getOption(
'nickname' ) );
595 if ( $signatureErrors ) {
596 $sigValidation = $this->options->get(
'SignatureValidation' );
597 $oldsigHTML .=
'<p><strong>' .
602 $context->
msg(
"prefs-signature-invalid-$sigValidation" )->parse() .
607 foreach ( $signatureErrors as &$sigError ) {
608 $sigError = new \OOUI\HtmlSnippet( $sigError );
611 $signatureFieldConfig = [
612 'warnings' => $sigValidation !==
'disallow' ? $signatureErrors :
null,
613 'errors' => $sigValidation ===
'disallow' ? $signatureErrors :
null,
618 $defaultPreferences[
'oldsig'] = [
623 'default' => new \OOUI\FieldLayout(
624 new \OOUI\LabelWidget( [
625 'label' =>
new \OOUI\HtmlSnippet( $oldsigHTML ),
629 'label' =>
new \OOUI\HtmlSnippet( $context->
msg(
'tog-oldsig' )->parse() )
630 ] + $signatureFieldConfig
632 'section' =>
'personal/signature',
634 $defaultPreferences[
'nickname'] = [
635 'type' => $this->authManager->allowsPropertyChange(
'nickname' ) ?
'text' :
'info',
636 'maxlength' => $this->options->get(
'MaxSigChars' ),
637 'label-message' =>
'yournick',
638 'validation-callback' =>
function ( $signature, $alldata,
HTMLForm $form ) {
641 'section' =>
'personal/signature',
642 'filter-callback' =>
function ( $signature, array $alldata,
HTMLForm $form ) {
646 $defaultPreferences[
'fancysig'] = [
648 'label-message' =>
'tog-fancysig',
650 'help-message' =>
'prefs-help-signature',
651 'section' =>
'personal/signature'
655 if ( $this->options->get(
'EnableEmail' ) ) {
656 if ( $canViewPrivateInfo ) {
658 $helpMessages[] = $this->options->get(
'EmailConfirmToEdit' )
659 ?
'prefs-help-email-required'
660 :
'prefs-help-email';
662 if ( $this->options->get(
'EnableUserEmail' ) ) {
664 $helpMessages[] =
'prefs-help-email-others';
667 $emailAddress = $user->
getEmail() ? htmlspecialchars( $user->
getEmail() ) :
'';
668 if ( $canEditPrivateInfo && $this->authManager->allowsPropertyChange(
'emailaddress' ) ) {
669 $button = new \OOUI\ButtonWidget( [
674 $context->
msg( $user->
getEmail() ?
'prefs-changeemail' :
'prefs-setemail' )->text(),
677 $emailAddress .= $emailAddress ==
'' ? $button : (
'<br />' . $button );
680 $defaultPreferences[
'emailaddress'] = [
683 'default' => $emailAddress,
684 'label-message' =>
'youremail',
685 'section' =>
'personal/email',
686 'help-messages' => $helpMessages,
691 $disableEmailPrefs =
false;
693 if ( $this->options->get(
'AllowRequiringEmailForResets' ) ) {
694 $defaultPreferences[
'requireemail'] = [
696 'label-message' =>
'tog-requireemail',
697 'help-message' =>
'prefs-help-requireemail',
698 'section' =>
'personal/email',
699 'disabled' => $user->
getEmail() ? false :
true,
703 if ( $this->options->get(
'EmailAuthentication' ) ) {
704 $emailauthenticationclass =
'mw-email-not-authenticated';
710 $displayUser = $context->
getUser();
712 $time =
$lang->userTimeAndDate( $emailTimestamp, $displayUser );
713 $d =
$lang->userDate( $emailTimestamp, $displayUser );
714 $t =
$lang->userTime( $emailTimestamp, $displayUser );
715 $emailauthenticated = $context->
msg(
'emailauthenticated',
716 $time, $d,
$t )->parse() .
'<br />';
717 $disableEmailPrefs =
false;
718 $emailauthenticationclass =
'mw-email-authenticated';
720 $disableEmailPrefs =
true;
721 $emailauthenticated = $context->
msg(
'emailnotauthenticated' )->parse() .
'<br />' .
722 new \OOUI\ButtonWidget( [
724 'label' => $context->
msg(
'emailconfirmlink' )->text(),
726 $emailauthenticationclass =
"mw-email-not-authenticated";
729 $disableEmailPrefs =
true;
730 $emailauthenticated = $context->
msg(
'noemailprefs' )->escaped();
731 $emailauthenticationclass =
'mw-email-none';
734 if ( $canViewPrivateInfo ) {
735 $defaultPreferences[
'emailauthentication'] = [
738 'section' =>
'personal/email',
739 'label-message' =>
'prefs-emailconfirm-label',
740 'default' => $emailauthenticated,
742 'cssclass' => $emailauthenticationclass,
747 if ( $this->options->get(
'EnableUserEmail' ) &&
748 $this->permissionManager->userHasRight( $user,
'sendemail' )
750 $defaultPreferences[
'disablemail'] = [
751 'id' =>
'wpAllowEmail',
754 'section' =>
'personal/email',
755 'label-message' =>
'allowemail',
756 'disabled' => $disableEmailPrefs,
759 $defaultPreferences[
'email-allow-new-users'] = [
760 'id' =>
'wpAllowEmailFromNewUsers',
762 'section' =>
'personal/email',
763 'label-message' =>
'email-allow-new-users-label',
764 'disabled' => $disableEmailPrefs,
767 $defaultPreferences[
'ccmeonemails'] = [
769 'section' =>
'personal/email',
770 'label-message' =>
'tog-ccmeonemails',
771 'disabled' => $disableEmailPrefs,
774 if ( $this->options->get(
'EnableUserEmailBlacklist' ) ) {
775 $defaultPreferences[
'email-blacklist'] = [
776 'type' =>
'usersmultiselect',
777 'label-message' =>
'email-blacklist-label',
778 'section' =>
'personal/email',
779 'disabled' => $disableEmailPrefs,
780 'filter' => MultiUsernameFilter::class,
785 if ( $this->options->get(
'EnotifWatchlist' ) ) {
786 $defaultPreferences[
'enotifwatchlistpages'] = [
788 'section' =>
'personal/email',
789 'label-message' =>
'tog-enotifwatchlistpages',
790 'disabled' => $disableEmailPrefs,
793 if ( $this->options->get(
'EnotifUserTalk' ) ) {
794 $defaultPreferences[
'enotifusertalkpages'] = [
796 'section' =>
'personal/email',
797 'label-message' =>
'tog-enotifusertalkpages',
798 'disabled' => $disableEmailPrefs,
801 if ( $this->options->get(
'EnotifUserTalk' ) ||
802 $this->options->get(
'EnotifWatchlist' ) ) {
803 if ( $this->options->get(
'EnotifMinorEdits' ) ) {
804 $defaultPreferences[
'enotifminoredits'] = [
806 'section' =>
'personal/email',
807 'label-message' =>
'tog-enotifminoredits',
808 'disabled' => $disableEmailPrefs,
812 if ( $this->options->get(
'EnotifRevealEditorAddress' ) ) {
813 $defaultPreferences[
'enotifrevealaddr'] = [
815 'section' =>
'personal/email',
816 'label-message' =>
'tog-enotifrevealaddr',
817 'disabled' => $disableEmailPrefs,
833 if ( $skinOptions ) {
834 $defaultPreferences[
'skin'] = [
837 'options' => $skinOptions,
838 'section' =>
'rendering/skin',
842 $allowUserCss = $this->options->get(
'AllowUserCss' );
843 $allowUserJs = $this->options->get(
'AllowUserJs' );
847 if ( $allowUserCss || $allowUserJs ) {
851 if ( $allowUserCss ) {
852 $cssPage = Title::makeTitleSafe(
NS_USER, $userName .
'/common.css' );
853 $cssLinkText = $context->
msg(
'prefs-custom-css' )->text();
854 $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
857 if ( $allowUserJs ) {
858 $jsPage = Title::makeTitleSafe(
NS_USER, $userName .
'/common.js' );
859 $jsLinkText = $context->
msg(
'prefs-custom-js' )->text();
860 $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
863 $defaultPreferences[
'commoncssjs'] = [
866 'default' => $context->
getLanguage()->pipeList( $linkTools ),
867 'label-message' =>
'prefs-common-config',
868 'section' =>
'rendering/skin',
878 $defaultPreferences[
'imagesize'] = [
881 'label-message' =>
'imagemaxsize',
882 'section' =>
'rendering/files',
884 $defaultPreferences[
'thumbsize'] = [
887 'label-message' =>
'thumbsize',
888 'section' =>
'rendering/files',
902 if ( $dateOptions ) {
903 $defaultPreferences[
'date'] = [
905 'options' => $dateOptions,
906 'section' =>
'rendering/dateformat',
913 $nowlocal =
Xml::element(
'span', [
'id' =>
'wpLocalTime' ],
914 $lang->userTime( $now, $user ) );
915 $nowserver =
$lang->userTime( $now, $user,
916 [
'format' =>
false,
'timecorrection' =>
false ] ) .
917 Html::hidden(
'wpServerTime', (
int)substr( $now, 8, 2 ) * 60 + (
int)substr( $now, 10, 2 ) );
919 $defaultPreferences[
'nowserver'] = [
922 'label-message' =>
'servertime',
923 'default' => $nowserver,
924 'section' =>
'rendering/timeoffset',
927 $defaultPreferences[
'nowlocal'] = [
930 'label-message' =>
'localtime',
931 'default' => $nowlocal,
932 'section' =>
'rendering/timeoffset',
936 $tzOffset = $user->
getOption(
'timecorrection' );
937 $tz = explode(
'|', $tzOffset, 3 );
941 $tzSetting = $tzOffset;
942 if ( count( $tz ) > 1 && $tz[0] ==
'ZoneInfo' &&
943 !in_array( $tzOffset, HTMLFormField::flattenOptions( $tzOptions ) )
947 $userTZ =
new DateTimeZone( $tz[2] );
948 $minDiff = floor( $userTZ->getOffset(
new DateTime(
'now' ) ) / 60 );
949 $tzSetting =
"ZoneInfo|$minDiff|{$tz[2]}";
950 }
catch ( Exception $e ) {
955 if ( count( $tz ) > 1 && $tz[0] ==
'Offset' ) {
957 $tzSetting = sprintf(
'%+03d:%02d', floor( $minDiff / 60 ), abs( $minDiff ) % 60 );
960 $defaultPreferences[
'timecorrection'] = [
961 'class' => \HTMLSelectOrOtherField::class,
962 'label-message' =>
'timezonelegend',
963 'options' => $tzOptions,
964 'default' => $tzSetting,
966 'section' =>
'rendering/timeoffset',
967 'id' =>
'wpTimeCorrection',
968 'filter' => TimezoneFilter::class,
969 'placeholder-message' =>
'timezone-useoffset-placeholder',
984 $defaultPreferences[
'diffonly'] = [
986 'section' =>
'rendering/diffs',
987 'label-message' =>
'tog-diffonly',
989 $defaultPreferences[
'norollbackdiff'] = [
991 'section' =>
'rendering/diffs',
992 'label-message' =>
'tog-norollbackdiff',
996 if ( $this->options->get(
'AllowUserCssPrefs' ) ) {
997 $defaultPreferences[
'underline'] = [
1000 $l10n->
msg(
'underline-never' )->text() => 0,
1001 $l10n->
msg(
'underline-always' )->text() => 1,
1002 $l10n->
msg(
'underline-default' )->text() => 2,
1004 'label-message' =>
'tog-underline',
1005 'section' =>
'rendering/advancedrendering',
1009 $stubThresholdValues = [ 50, 100, 500, 1000, 2000, 5000, 10000 ];
1010 $stubThresholdOptions = [ $l10n->
msg(
'stub-threshold-disabled' )->text() => 0 ];
1011 foreach ( $stubThresholdValues as $value ) {
1012 $stubThresholdOptions[$l10n->
msg(
'size-bytes', $value )->text()] = $value;
1015 $defaultPreferences[
'stubthreshold'] = [
1017 'section' =>
'rendering/advancedrendering',
1018 'options' => $stubThresholdOptions,
1020 'label-raw' => $l10n->
msg(
'stub-threshold' )->rawParams(
1021 '<a class="stub">' .
1022 $l10n->
msg(
'stub-threshold-sample-link' )->parse() .
1026 $defaultPreferences[
'showhiddencats'] = [
1028 'section' =>
'rendering/advancedrendering',
1029 'label-message' =>
'tog-showhiddencats'
1032 $defaultPreferences[
'numberheadings'] = [
1034 'section' =>
'rendering/advancedrendering',
1035 'label-message' =>
'tog-numberheadings',
1038 if ( $this->permissionManager->userHasRight( $user,
'rollback' ) ) {
1039 $defaultPreferences[
'showrollbackconfirmation'] = [
1041 'section' =>
'rendering/advancedrendering',
1042 'label-message' =>
'tog-showrollbackconfirmation',
1053 $defaultPreferences[
'editsectiononrightclick'] = [
1055 'section' =>
'editing/advancedediting',
1056 'label-message' =>
'tog-editsectiononrightclick',
1058 $defaultPreferences[
'editondblclick'] = [
1060 'section' =>
'editing/advancedediting',
1061 'label-message' =>
'tog-editondblclick',
1064 if ( $this->options->get(
'AllowUserCssPrefs' ) ) {
1065 $defaultPreferences[
'editfont'] = [
1067 'section' =>
'editing/editor',
1068 'label-message' =>
'editfont-style',
1070 $l10n->
msg(
'editfont-monospace' )->text() =>
'monospace',
1071 $l10n->
msg(
'editfont-sansserif' )->text() =>
'sans-serif',
1072 $l10n->
msg(
'editfont-serif' )->text() =>
'serif',
1077 if ( $this->permissionManager->userHasRight( $user,
'minoredit' ) ) {
1078 $defaultPreferences[
'minordefault'] = [
1080 'section' =>
'editing/editor',
1081 'label-message' =>
'tog-minordefault',
1085 $defaultPreferences[
'forceeditsummary'] = [
1087 'section' =>
'editing/editor',
1088 'label-message' =>
'tog-forceeditsummary',
1090 $defaultPreferences[
'useeditwarning'] = [
1092 'section' =>
'editing/editor',
1093 'label-message' =>
'tog-useeditwarning',
1096 $defaultPreferences[
'previewonfirst'] = [
1098 'section' =>
'editing/preview',
1099 'label-message' =>
'tog-previewonfirst',
1101 $defaultPreferences[
'previewontop'] = [
1103 'section' =>
'editing/preview',
1104 'label-message' =>
'tog-previewontop',
1106 $defaultPreferences[
'uselivepreview'] = [
1108 'section' =>
'editing/preview',
1109 'label-message' =>
'tog-uselivepreview',
1119 $rcMaxAge = $this->options->get(
'RCMaxAge' );
1120 $defaultPreferences[
'rcdays'] = [
1122 'label-message' =>
'recentchangesdays',
1123 'section' =>
'rc/displayrc',
1125 'max' => ceil( $rcMaxAge / ( 3600 * 24 ) ),
1126 'help' => $l10n->
msg(
'recentchangesdays-max' )->numParams(
1127 ceil( $rcMaxAge / ( 3600 * 24 ) ) )->escaped()
1129 $defaultPreferences[
'rclimit'] = [
1133 'label-message' =>
'recentchangescount',
1134 'help-message' =>
'prefs-help-recentchangescount',
1135 'section' =>
'rc/displayrc',
1136 'filter' => IntvalFilter::class,
1138 $defaultPreferences[
'usenewrc'] = [
1140 'label-message' =>
'tog-usenewrc',
1141 'section' =>
'rc/advancedrc',
1143 $defaultPreferences[
'hideminor'] = [
1145 'label-message' =>
'tog-hideminor',
1146 'section' =>
'rc/changesrc',
1148 $defaultPreferences[
'pst-cssjs'] = [
1151 $defaultPreferences[
'rcfilters-rc-collapsed'] = [
1154 $defaultPreferences[
'rcfilters-wl-collapsed'] = [
1157 $defaultPreferences[
'rcfilters-saved-queries'] = [
1160 $defaultPreferences[
'rcfilters-wl-saved-queries'] = [
1164 $defaultPreferences[
'rcfilters-limit'] = [
1167 $defaultPreferences[
'rcfilters-saved-queries-versionbackup'] = [
1170 $defaultPreferences[
'rcfilters-wl-saved-queries-versionbackup'] = [
1174 if ( $this->options->get(
'RCWatchCategoryMembership' ) ) {
1175 $defaultPreferences[
'hidecategorization'] = [
1177 'label-message' =>
'tog-hidecategorization',
1178 'section' =>
'rc/changesrc',
1183 $defaultPreferences[
'hidepatrolled'] = [
1185 'section' =>
'rc/changesrc',
1186 'label-message' =>
'tog-hidepatrolled',
1191 $defaultPreferences[
'newpageshidepatrolled'] = [
1193 'section' =>
'rc/changesrc',
1194 'label-message' =>
'tog-newpageshidepatrolled',
1198 if ( $this->options->get(
'RCShowWatchingUsers' ) ) {
1199 $defaultPreferences[
'shownumberswatching'] = [
1201 'section' =>
'rc/advancedrc',
1202 'label-message' =>
'tog-shownumberswatching',
1206 $defaultPreferences[
'rcenhancedfilters-disable'] = [
1208 'section' =>
'rc/advancedrc',
1209 'label-message' =>
'rcfilters-preference-label',
1210 'help-message' =>
'rcfilters-preference-help',
1222 $watchlistdaysMax = ceil( $this->options->get(
'RCMaxAge' ) / ( 3600 * 24 ) );
1224 if ( $this->permissionManager->userHasRight( $user,
'editmywatchlist' ) ) {
1225 $editWatchlistLinks =
'';
1226 $editWatchlistModes = [
1227 'edit' => [
'subpage' =>
false,
'flags' => [] ],
1228 'raw' => [
'subpage' =>
'raw',
'flags' => [] ],
1229 'clear' => [
'subpage' =>
'clear',
'flags' => [
'destructive' ] ],
1231 foreach ( $editWatchlistModes as $mode =>
$options ) {
1233 $editWatchlistLinks .=
1234 new \OOUI\ButtonWidget( [
1237 'label' =>
new \OOUI\HtmlSnippet(
1238 $context->
msg(
"prefs-editwatchlist-{$mode}" )->parse()
1243 $defaultPreferences[
'editwatchlist'] = [
1246 'default' => $editWatchlistLinks,
1247 'label-message' =>
'prefs-editwatchlist-label',
1248 'section' =>
'watchlist/editwatchlist',
1252 $defaultPreferences[
'watchlistdays'] = [
1255 'max' => $watchlistdaysMax,
1256 'section' =>
'watchlist/displaywatchlist',
1257 'help' => $context->
msg(
'prefs-watchlist-days-max' )->numParams(
1258 $watchlistdaysMax )->escaped(),
1259 'label-message' =>
'prefs-watchlist-days',
1261 $defaultPreferences[
'wllimit'] = [
1265 'label-message' =>
'prefs-watchlist-edits',
1266 'help' => $context->
msg(
'prefs-watchlist-edits-max' )->escaped(),
1267 'section' =>
'watchlist/displaywatchlist',
1268 'filter' => IntvalFilter::class,
1270 $defaultPreferences[
'extendwatchlist'] = [
1272 'section' =>
'watchlist/advancedwatchlist',
1273 'label-message' =>
'tog-extendwatchlist',
1275 $defaultPreferences[
'watchlisthideminor'] = [
1277 'section' =>
'watchlist/changeswatchlist',
1278 'label-message' =>
'tog-watchlisthideminor',
1280 $defaultPreferences[
'watchlisthidebots'] = [
1282 'section' =>
'watchlist/changeswatchlist',
1283 'label-message' =>
'tog-watchlisthidebots',
1285 $defaultPreferences[
'watchlisthideown'] = [
1287 'section' =>
'watchlist/changeswatchlist',
1288 'label-message' =>
'tog-watchlisthideown',
1290 $defaultPreferences[
'watchlisthideanons'] = [
1292 'section' =>
'watchlist/changeswatchlist',
1293 'label-message' =>
'tog-watchlisthideanons',
1295 $defaultPreferences[
'watchlisthideliu'] = [
1297 'section' =>
'watchlist/changeswatchlist',
1298 'label-message' =>
'tog-watchlisthideliu',
1302 $defaultPreferences[
'watchlistreloadautomatically'] = [
1304 'section' =>
'watchlist/advancedwatchlist',
1305 'label-message' =>
'tog-watchlistreloadautomatically',
1309 $defaultPreferences[
'watchlistunwatchlinks'] = [
1311 'section' =>
'watchlist/advancedwatchlist',
1312 'label-message' =>
'tog-watchlistunwatchlinks',
1315 if ( $this->options->get(
'RCWatchCategoryMembership' ) ) {
1316 $defaultPreferences[
'watchlisthidecategorization'] = [
1318 'section' =>
'watchlist/changeswatchlist',
1319 'label-message' =>
'tog-watchlisthidecategorization',
1324 $defaultPreferences[
'watchlisthidepatrolled'] = [
1326 'section' =>
'watchlist/changeswatchlist',
1327 'label-message' =>
'tog-watchlisthidepatrolled',
1332 'edit' =>
'watchdefault',
1333 'move' =>
'watchmoves',
1334 'delete' =>
'watchdeletion'
1338 if ( $this->permissionManager->userHasAnyRight( $user,
'createpage',
'createtalk' ) ) {
1339 $watchTypes[
'read'] =
'watchcreations';
1342 if ( $this->permissionManager->userHasRight( $user,
'rollback' ) ) {
1343 $watchTypes[
'rollback'] =
'watchrollback';
1346 if ( $this->permissionManager->userHasRight( $user,
'upload' ) ) {
1347 $watchTypes[
'upload'] =
'watchuploads';
1350 foreach ( $watchTypes as $action => $pref ) {
1351 if ( $this->permissionManager->userHasRight( $user, $action ) ) {
1355 $defaultPreferences[$pref] = [
1357 'section' =>
'watchlist/pageswatchlist',
1358 'label-message' =>
"tog-$pref",
1363 $defaultPreferences[
'watchlisttoken'] = [
1367 $tokenButton = new \OOUI\ButtonWidget( [
1371 'label' => $context->
msg(
'prefs-watchlist-managetokens' )->text(),
1373 $defaultPreferences[
'watchlisttoken-info'] = [
1375 'section' =>
'watchlist/tokenwatchlist',
1376 'label-message' =>
'prefs-watchlist-token',
1377 'help-message' =>
'prefs-help-tokenmanagement',
1379 'default' => (string)$tokenButton,
1382 $defaultPreferences[
'wlenhancedfilters-disable'] = [
1384 'section' =>
'watchlist/advancedwatchlist',
1385 'label-message' =>
'rcfilters-watchlist-preference-label',
1386 'help-message' =>
'rcfilters-watchlist-preference-help',
1394 foreach ( $this->nsInfo->getValidNamespaces() as $n ) {
1395 $defaultPreferences[
'searchNs' . $n] = [
1400 if ( $this->options->get(
'SearchMatchRedirectPreference' ) ) {
1401 $defaultPreferences[
'search-match-redirect'] = [
1403 'section' =>
'searchoptions/searchmisc',
1404 'label-message' =>
'search-match-redirect-label',
1405 'help-message' =>
'search-match-redirect-help',
1408 $defaultPreferences[
'search-match-redirect'] = [
1422 $mptitle = Title::newMainPage();
1423 $previewtext = $context->
msg(
'skin-preview' )->escaped();
1427 $validSkinNames = $skinFactory->getAllowedSkins();
1428 $allInstalledSkins = $skinFactory->getSkinNames();
1431 $useSkin = $context->
getRequest()->getRawVal(
'useskin' );
1432 if ( isset( $allInstalledSkins[$useSkin] )
1433 && $context->
msg(
"skinname-$useSkin" )->exists()
1435 $validSkinNames[$useSkin] = $useSkin;
1439 $currentUserSkin = $user->
getOption(
'skin' );
1440 if ( isset( $allInstalledSkins[$currentUserSkin] )
1441 && $context->
msg(
"skinname-$currentUserSkin" )->exists()
1443 $validSkinNames[$currentUserSkin] = $currentUserSkin;
1446 foreach ( $validSkinNames as $skinkey => &$skinname ) {
1447 $msg = $context->
msg(
"skinname-{$skinkey}" );
1448 if ( $msg->exists() ) {
1449 $skinname = htmlspecialchars( $msg->text() );
1453 $defaultSkin = $this->options->get(
'DefaultSkin' );
1454 $allowUserCss = $this->options->get(
'AllowUserCss' );
1455 $allowUserJs = $this->options->get(
'AllowUserJs' );
1459 uksort( $validSkinNames,
static function ( $a, $b ) use ( $defaultSkin ) {
1461 if ( strcasecmp( $a, $defaultSkin ) === 0 ) {
1464 if ( strcasecmp( $b, $defaultSkin ) === 0 ) {
1467 return strcasecmp( $a, $b );
1470 $foundDefault =
false;
1471 foreach ( $validSkinNames as $skinkey => $sn ) {
1475 if ( strcasecmp( $skinkey, $defaultSkin ) === 0 ) {
1476 $linkTools[] = $context->
msg(
'default' )->escaped();
1477 $foundDefault =
true;
1481 $mplink = htmlspecialchars( $mptitle->getLocalURL( [
'useskin' => $skinkey ] ) );
1482 $linkTools[] =
"<a target='_blank' href=\"$mplink\">$previewtext</a>";
1486 if ( $allowUserCss ) {
1487 $cssPage = Title::makeTitleSafe(
NS_USER, $user->
getName() .
'/' . $skinkey .
'.css' );
1488 $cssLinkText = $context->
msg(
'prefs-custom-css' )->text();
1489 $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText );
1492 if ( $allowUserJs ) {
1493 $jsPage = Title::makeTitleSafe(
NS_USER, $user->
getName() .
'/' . $skinkey .
'.js' );
1494 $jsLinkText = $context->
msg(
'prefs-custom-js' )->text();
1495 $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText );
1498 $display = $sn .
' ' . $context->
msg(
'parentheses' )
1499 ->rawParams( $context->
getLanguage()->pipeList( $linkTools ) )
1501 $ret[$display] = $skinkey;
1504 if ( !$foundDefault ) {
1519 $dateopts =
$lang->getDatePreferences();
1524 if ( !in_array(
'default', $dateopts ) ) {
1525 $dateopts[] =
'default';
1535 foreach ( $dateopts as $key ) {
1536 if ( $key ==
'default' ) {
1537 $formatted = $context->
msg(
'datedefault' )->escaped();
1539 $formatted = htmlspecialchars(
$lang->timeanddate( $epoch,
false, $key ) );
1541 $ret[$formatted] = $key;
1553 $pixels = $l10n->
msg(
'unit-pixel' )->text();
1555 foreach ( $this->options->get(
'ImageLimits' ) as $index => $limits ) {
1557 $display =
"{$limits[0]}\u{200E}×{$limits[1]}$pixels";
1558 $ret[$display] = $index;
1570 $pixels = $l10n->
msg(
'unit-pixel' )->text();
1572 foreach ( $this->options->get(
'ThumbLimits' ) as $index => $size ) {
1573 $display = $size . $pixels;
1574 $ret[$display] = $index;
1587 $sigValidation = $this->options->get(
'SignatureValidation' );
1588 $maxSigChars = $this->options->get(
'MaxSigChars' );
1589 if ( mb_strlen( $signature ) > $maxSigChars ) {
1590 return $form->
msg(
'badsiglength' )->numParams( $maxSigChars )->escaped();
1594 if ( !( isset( $alldata[
'fancysig'] ) && $alldata[
'fancysig'] ) ) {
1614 $signature === $form->
getUser()->getOption(
'nickname' ) &&
1615 (
bool)$alldata[
'fancysig'] === $form->
getUser()->getBoolOption(
'fancysig' )
1620 if ( $sigValidation ===
'new' || $sigValidation ===
'disallow' ) {
1625 ParserOptions::newFromContext( $form->
getContext() )
1627 $errors = $validator->validateSignature( $signature );
1637 if ( $parser->validateSig( $signature ) ===
false ) {
1638 return $form->
msg(
'badsig' )->escaped();
1652 if ( isset( $alldata[
'fancysig'] ) && $alldata[
'fancysig'] ) {
1653 $signature = $parser->cleanSig( $signature );
1672 $formClass = PreferencesFormOOUI::class,
1680 if ( count( $remove ) ) {
1681 $removeKeys = array_flip( $remove );
1682 $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
1686 foreach ( $formDescriptor as $name => $info ) {
1687 if ( isset( $info[
'type'] ) && $info[
'type'] ===
'api' ) {
1688 unset( $formDescriptor[$name] );
1695 $htmlForm =
new $formClass( $formDescriptor, $context,
'prefs' );
1701 $htmlForm->setAction( $context->
getTitle()->getLocalURL( [
1702 'useskin' => $context->
getRequest()->getRawVal(
'useskin' )
1705 $htmlForm->setModifiedUser( $user );
1706 $htmlForm->setOptionsEditable( $this->permissionManager
1707 ->userHasRight( $user,
'editmyoptions' ) );
1708 $htmlForm->setPrivateInfoEditable( $this->permissionManager
1709 ->userHasRight( $user,
'editmyprivateinfo' ) );
1710 $htmlForm->setId(
'mw-prefs-form' );
1711 $htmlForm->setAutocomplete(
'off' );
1712 $htmlForm->setSubmitText( $context->
msg(
'saveprefs' )->text() );
1714 $htmlForm->setSubmitTooltip(
'preferences-save' );
1715 $htmlForm->setSubmitID(
'prefcontrol' );
1716 $htmlForm->setSubmitCallback(
1718 return $this->
submitForm( $formData, $form, $formDescriptor );
1732 $localTZoffset = $this->options->get(
'LocalTZoffset' );
1735 $timestamp = MWTimestamp::getLocalInstance();
1737 if ( $localTZoffset == $timestamp->format(
'Z' ) / 60 ) {
1738 $timezoneName = $timestamp->getTimezone()->getName();
1740 if ( isset( $timeZoneList[$timezoneName] ) ) {
1741 $timezoneName = $timeZoneList[$timezoneName][
'name'];
1743 $server_tz_msg = $context->
msg(
1744 'timezoneuseserverdefault',
1748 $tzstring = sprintf(
1750 floor( $localTZoffset / 60 ),
1751 abs( $localTZoffset ) % 60
1753 $server_tz_msg = $context->
msg(
'timezoneuseserverdefault', $tzstring )->text();
1755 $opt[$server_tz_msg] =
"System|$localTZoffset";
1756 $opt[$context->
msg(
'timezoneuseoffset' )->text()] =
'other';
1757 $opt[$context->
msg(
'guesstimezone' )->text()] =
'guess';
1759 foreach ( $timeZoneList as $timeZoneInfo ) {
1760 $region = $timeZoneInfo[
'region'];
1761 if ( !isset( $opt[$region] ) ) {
1764 $opt[$region][$timeZoneInfo[
'name']] = $timeZoneInfo[
'timecorrection'];
1779 $hiddenPrefs = $this->options->get(
'HiddenPrefs' );
1782 if ( !$this->permissionManager
1783 ->userHasAnyRight( $user,
'editmyprivateinfo',
'editmyoptions' )
1785 return Status::newFatal(
'mypreferencesprotected' );
1789 $this->
applyFilters( $formData, $formDescriptor,
'filterFromForm' );
1794 if ( !in_array(
'realname', $hiddenPrefs )
1795 && $this->permissionManager->userHasRight( $user,
'editmyprivateinfo' )
1796 && array_key_exists(
'realname', $formData )
1798 $realName = $formData[
'realname'];
1799 $user->setRealName( $realName );
1802 if ( $this->permissionManager->userHasRight( $user,
'editmyoptions' ) ) {
1803 $oldUserOptions = $user->getOptions();
1806 unset( $formData[$b] );
1812 foreach ( $hiddenPrefs as $pref ) {
1815 $formData[$pref] = $user->getOption( $pref,
null,
true );
1820 isset( $formData[
'rclimit'] ) &&
1821 intval( $formData[
'rclimit' ] ) !== $user->getIntOption(
'rclimit' )
1823 $formData[
'rcfilters-limit'] = $formData[
'rclimit'];
1827 $user->resetOptions(
'unused', $form->
getContext() );
1829 foreach ( $formData as $key => $value ) {
1830 $user->setOption( $key, $value );
1833 $this->hookRunner->onPreferencesFormPreSave(
1834 $formData, $form, $user, $result, $oldUserOptions );
1837 $user->saveSettings();
1850 protected function applyFilters( array &$preferences, array $formDescriptor, $verb ) {
1851 foreach ( $formDescriptor as $preference => $desc ) {
1852 if ( !isset( $desc[
'filter'] ) || !isset( $preferences[$preference] ) ) {
1855 $filterDesc = $desc[
'filter'];
1856 if ( $filterDesc instanceof
Filter ) {
1857 $filter = $filterDesc;
1858 } elseif ( class_exists( $filterDesc ) ) {
1859 $filter =
new $filterDesc();
1860 } elseif ( is_callable( $filterDesc ) ) {
1861 $filter = $filterDesc();
1863 throw new UnexpectedValueException(
1864 "Unrecognized filter type for preference '$preference'"
1867 $preferences[$preference] = $filter->$verb( $preferences[$preference] );
1882 array $formDescriptor
1886 if (
$res ===
true ) {
1892 $url = $form->
getTitle()->getFullURL( $urlOptions );
1895 $context->getRequest()->getSession()->set(
'specialPreferencesSaveSuccess', 1 );
1897 $context->getOutput()->redirect( $url );
1900 return (
$res ===
true ? Status::newGood() :
$res );
1912 $identifiers = DateTimeZone::listIdentifiers();
1914 if ( $identifiers ===
false ) {
1917 sort( $identifiers );
1920 'Africa' =>
wfMessage(
'timezoneregion-africa' )->inLanguage( $language )->text(),
1921 'America' =>
wfMessage(
'timezoneregion-america' )->inLanguage( $language )->text(),
1922 'Antarctica' =>
wfMessage(
'timezoneregion-antarctica' )->inLanguage( $language )->text(),
1923 'Arctic' =>
wfMessage(
'timezoneregion-arctic' )->inLanguage( $language )->text(),
1924 'Asia' =>
wfMessage(
'timezoneregion-asia' )->inLanguage( $language )->text(),
1925 'Atlantic' =>
wfMessage(
'timezoneregion-atlantic' )->inLanguage( $language )->text(),
1926 'Australia' =>
wfMessage(
'timezoneregion-australia' )->inLanguage( $language )->text(),
1927 'Europe' =>
wfMessage(
'timezoneregion-europe' )->inLanguage( $language )->text(),
1928 'Indian' =>
wfMessage(
'timezoneregion-indian' )->inLanguage( $language )->text(),
1929 'Pacific' =>
wfMessage(
'timezoneregion-pacific' )->inLanguage( $language )->text(),
1931 asort( $tzRegions );
1935 $now =
new DateTime();
1937 foreach ( $identifiers as $identifier ) {
1938 $parts = explode(
'/', $identifier, 2 );
1943 if ( count( $parts ) !== 2 || !array_key_exists( $parts[0], $tzRegions ) ) {
1948 $parts[0] = $tzRegions[$parts[0]];
1950 $dateTimeZone =
new DateTimeZone( $identifier );
1951 $minDiff = floor( $dateTimeZone->getOffset( $now ) / 60 );
1953 $display = str_replace(
'_',
' ', $parts[0] .
'/' . $parts[1] );
1954 $value =
"ZoneInfo|$minDiff|$identifier";
1956 $timeZoneList[$identifier] = [
1958 'timecorrection' => $value,
1959 'region' => $parts[0],
1963 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.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
getContext()
Get the base IContextSource object.
This class is a collection of static functions that serve two purposes:
Methods for dealing with language codes.
Base class for multi-variant language conversion.
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Library for creating and parsing MW-style timestamps.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
This is one of the Core classes and should be read at least once by any new developers.
Set options of the Parser.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
static stripOuterParagraph( $html)
Strip outer.
static cleanSigInSig( $text)
Strip 3, 4 or 5 tildes out of signatures.
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( $user)
Static method to check whether StructuredFilter UI is enabled for the given user.1....
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.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
getOptions( $flags=0)
Get all user's options.
getName()
Get the user name, or the IP of an anonymous user.
getEmailAuthenticationTimestamp()
Get the timestamp of the user's e-mail authentication.
getRealName()
Get the user's real name.
getRegistration()
Get the timestamp of account creation.
useNPPatrol()
Check whether to enable new pages patrol features for this user.
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
getEffectiveGroups( $recache=false)
Get the list of implicit group memberships this user has.
getGroupMemberships()
Get the list of explicit group memberships this user has, stored as UserGroupMembership objects.
useRCPatrol()
Check whether to enable recent changes patrol features for this user.
getEditCount()
Get the user's edit count.
getTitleKey()
Get the user's name escaped by underscores.
getEmail()
Get the user's e-mail address.
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.
Interface for objects which can provide a MediaWiki context on request.
The shared interface for all language converters.
Interface for localizing messages in MediaWiki.
msg( $key,... $params)
This is the method for getting translated interface messages.
if(!isset( $args[0])) $lang