diff --git a/includes/Preferences.php b/includes/preferences/DefaultPreferencesFactory.php index 33a975d104..230307a17c 100644 --- a/includes/Preferences.php +++ b/includes/preferences/DefaultPreferencesFactory.php @@ -17,55 +17,93 @@ * * @file */ + +namespace MediaWiki\Preferences; + +use CentralIdLookup; +use Config; +use DateTime; +use DateTimeZone; +use Exception; +use Hooks; +use Html; +use HtmlArmor; +use HTMLForm; +use HTMLFormField; +use IContextSource; +use Language; +use LanguageCode; +use LanguageConverter; use MediaWiki\Auth\AuthManager; use MediaWiki\Auth\PasswordAuthenticationRequest; -use MediaWiki\MediaWikiServices; +use MediaWiki\Linker\LinkRenderer; +use MessageLocalizer; +use MWException; +use MWNamespace; +use MWTimestamp; +use Parser; +use ParserOptions; +use PreferencesForm; +use Skin; +use SpecialPage; +use Status; +use Title; +use User; +use UserGroupMembership; +use Xml; /** - * We're now using the HTMLForm object with some customisation to generate the - * Preferences form. This object handles generic submission, CSRF protection, - * layout and other logic in a reusable manner. We subclass it as a PreferencesForm - * to make some minor customisations. - * - * In order to generate the form, the HTMLForm object needs an array structure - * detailing the form fields available, and that's what this class is for. Each - * element of the array is a basic property-list, including the type of field, - * the label it is to be given in the form, callbacks for validation and - * 'filtering', and other pertinent information. Note that the 'default' field - * is named for generic forms, and does not represent the preference's default - * (which is stored in $wgDefaultUserOptions), but the default for the form - * field, which should be whatever the user has set for that preference. There - * is no need to override it unless you have some special storage logic (for - * instance, those not presently stored as options, but which are best set from - * the user preferences view). - * - * Field types are implemented as subclasses of the generic HTMLFormField - * object, and typically implement at least getInputHTML, which generates the - * HTML for the input field to be placed in the table. + * This is the default implementation of PreferencesFactory. * - * Once fields have been retrieved and validated, submission logic is handed - * over to the tryUISubmit static method of this class. + * Note that the public visibility of most of the methods in this class is only temporary while + * the old Preferences class is being removed, and will change to 'protected' after that happens. */ -class Preferences { - /** @var array */ - protected static $saveFilters = [ - 'timecorrection' => [ 'Preferences', 'filterTimezoneInput' ], - 'rclimit' => [ 'Preferences', 'filterIntval' ], - 'wllimit' => [ 'Preferences', 'filterIntval' ], - 'searchlimit' => [ 'Preferences', 'filterIntval' ], - ]; - - // Stuff that shouldn't be saved as a preference. - private static $saveBlacklist = [ - 'realname', - 'emailaddress', - ]; +class DefaultPreferencesFactory implements PreferencesFactory { + + /** @var Config */ + protected $config; + + /** @var Language The wiki's content language, equivalent to $wgContLang. */ + protected $contLang; + + /** @var AuthManager */ + protected $authManager; + + /** @var LinkRenderer */ + protected $linkRenderer; + + public function __construct( + Config $config, + Language $contLang, + AuthManager $authManager, + LinkRenderer $linkRenderer + ) { + $this->config = $config; + $this->contLang = $contLang; + $this->authManager = $authManager; + $this->linkRenderer = $linkRenderer; + } /** - * @return array + * @return callable[] */ - static function getSaveBlacklist() { - return self::$saveBlacklist; + protected function getSaveFilters() { + return [ + 'timecorrection' => [ $this, 'filterTimezoneInput' ], + 'rclimit' => [ $this, 'filterIntval' ], + 'wllimit' => [ $this, 'filterIntval' ], + 'searchlimit' => [ $this, 'filterIntval' ], + ]; + } + + /** + * @inheritDoc + */ + public function getSaveBlacklist() { + return [ + 'realname', + 'emailaddress', + ]; } /** @@ -74,24 +112,23 @@ class Preferences { * @param IContextSource $context * @return array|null */ - static function getPreferences( $user, IContextSource $context ) { - $defaultPreferences = []; - - self::profilePreferences( $user, $context, $defaultPreferences ); - self::skinPreferences( $user, $context, $defaultPreferences ); - self::datetimePreferences( $user, $context, $defaultPreferences ); - self::filesPreferences( $user, $context, $defaultPreferences ); - self::renderingPreferences( $user, $context, $defaultPreferences ); - self::editingPreferences( $user, $context, $defaultPreferences ); - self::rcPreferences( $user, $context, $defaultPreferences ); - self::watchlistPreferences( $user, $context, $defaultPreferences ); - self::searchPreferences( $user, $context, $defaultPreferences ); - self::miscPreferences( $user, $context, $defaultPreferences ); - - Hooks::run( 'GetPreferences', [ $user, &$defaultPreferences ] ); - - self::loadPreferenceValues( $user, $context, $defaultPreferences ); - return $defaultPreferences; + public function getFormDescriptor( User $user, IContextSource $context ) { + $preferences = []; + + $this->profilePreferences( $user, $context, $preferences ); + $this->skinPreferences( $user, $context, $preferences ); + $this->datetimePreferences( $user, $context, $preferences ); + $this->filesPreferences( $context, $preferences ); + $this->renderingPreferences( $context, $preferences ); + $this->editingPreferences( $user, $context, $preferences ); + $this->rcPreferences( $user, $context, $preferences ); + $this->watchlistPreferences( $user, $context, $preferences ); + $this->searchPreferences( $preferences ); + + Hooks::run( 'GetPreferences', [ $user, &$preferences ] ); + + $this->loadPreferenceValues( $user, $context, $preferences ); + return $preferences; } /** @@ -102,9 +139,11 @@ class Preferences { * @param array &$defaultPreferences Array to load values for * @return array|null */ - static function loadPreferenceValues( $user, $context, &$defaultPreferences ) { + public function loadPreferenceValues( + User $user, IContextSource $context, &$defaultPreferences + ) { # # Remove preferences that wikis don't want to use - foreach ( $context->getConfig()->get( 'HiddenPrefs' ) as $pref ) { + foreach ( $this->config->get( 'HiddenPrefs' ) as $pref ) { if ( isset( $defaultPreferences[$pref] ) ) { unset( $defaultPreferences[$pref] ); } @@ -118,8 +157,8 @@ class Preferences { $defaultOptions = User::getDefaultOptions(); # # Prod in defaults from the user foreach ( $defaultPreferences as $name => &$info ) { - $prefFromUser = self::getOptionFromUser( $name, $info, $user ); - if ( $disable && !in_array( $name, self::$saveBlacklist ) ) { + $prefFromUser = $this->getOptionFromUser( $name, $info, $user ); + if ( $disable && !in_array( $name, $this->getSaveBlacklist() ) ) { $info['disabled'] = 'disabled'; } $field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm ); // For validation @@ -152,7 +191,7 @@ class Preferences { * @param User $user * @return array|string */ - static function getOptionFromUser( $name, $info, $user ) { + public function getOptionFromUser( $name, $info, User $user ) { $val = $user->getOption( $name ); // Handling for multiselect preferences @@ -190,16 +229,17 @@ class Preferences { } /** + * @todo Remove global $wgParser. * @param User $user * @param IContextSource $context * @param array &$defaultPreferences * @return void */ - static function profilePreferences( $user, IContextSource $context, &$defaultPreferences ) { - global $wgContLang, $wgParser; + public function profilePreferences( + User $user, IContextSource $context, &$defaultPreferences + ) { + global $wgParser; - $authManager = AuthManager::singleton(); - $config = $context->getConfig(); // retrieving user name for GENDER and misc. $userName = $user->getName(); @@ -266,10 +306,9 @@ class Preferences { 'section' => 'personal/info', ]; - $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); - - $editCount = $linkRenderer->makeLink( SpecialPage::getTitleFor( "Contributions", $userName ), - $lang->formatNum( $user->getEditCount() ) ); + $contribTitle = SpecialPage::getTitleFor( "Contributions", $userName ); + $formattedEditCount = $lang->formatNum( $user->getEditCount() ); + $editCount = $this->linkRenderer->makeLink( $contribTitle, $formattedEditCount ); $defaultPreferences['editcount'] = [ 'type' => 'info', @@ -301,7 +340,7 @@ class Preferences { // Actually changeable stuff $defaultPreferences['realname'] = [ // (not really "private", but still shouldn't be edited without permission) - 'type' => $canEditPrivateInfo && $authManager->allowsPropertyChange( 'realname' ) + 'type' => $canEditPrivateInfo && $this->authManager->allowsPropertyChange( 'realname' ) ? 'text' : 'info', 'default' => $user->getRealName(), 'section' => 'personal/info', @@ -309,13 +348,12 @@ class Preferences { 'help-message' => 'prefs-help-realname', ]; - if ( $canEditPrivateInfo && $authManager->allowsAuthenticationDataChange( + if ( $canEditPrivateInfo && $this->authManager->allowsAuthenticationDataChange( new PasswordAuthenticationRequest(), false )->isGood() ) { - $link = $linkRenderer->makeLink( SpecialPage::getTitleFor( 'ChangePassword' ), + $link = $this->linkRenderer->makeLink( SpecialPage::getTitleFor( 'ChangePassword' ), $context->msg( 'prefs-resetpass' )->text(), [], [ 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ] ); - $defaultPreferences['password'] = [ 'type' => 'info', 'raw' => true, @@ -325,7 +363,9 @@ class Preferences { ]; } // Only show prefershttps if secure login is turned on - if ( $config->get( 'SecureLogin' ) && wfCanIPUseHTTPS( $context->getRequest()->getIP() ) ) { + if ( $this->config->get( 'SecureLogin' ) + && wfCanIPUseHTTPS( $context->getRequest()->getIP() ) + ) { $defaultPreferences['prefershttps'] = [ 'type' => 'toggle', 'label-message' => 'tog-prefershttps', @@ -336,7 +376,7 @@ class Preferences { // Language $languages = Language::fetchLanguageNames( null, 'mw' ); - $languageCode = $config->get( 'LanguageCode' ); + $languageCode = $this->config->get( 'LanguageCode' ); if ( !array_key_exists( $languageCode, $languages ) ) { $languages[$languageCode] = $languageCode; } @@ -369,10 +409,10 @@ class Preferences { ]; // see if there are multiple language variants to choose from - if ( !$config->get( 'DisableLangConversion' ) ) { + if ( !$this->config->get( 'DisableLangConversion' ) ) { foreach ( LanguageConverter::$languagesWithVariants as $langCode ) { - if ( $langCode == $wgContLang->getCode() ) { - $variants = $wgContLang->getVariants(); + if ( $langCode == $this->contLang->getCode() ) { + $variants = $this->contLang->getVariants(); if ( count( $variants ) <= 1 ) { continue; @@ -407,7 +447,7 @@ class Preferences { // Stuff from Language::getExtraUserToggles() // FIXME is this dead code? $extraUserToggles doesn't seem to be defined for any language - $toggles = $wgContLang->getExtraUserToggles(); + $toggles = $this->contLang->getExtraUserToggles(); foreach ( $toggles as $toggle ) { $defaultPreferences[$toggle] = [ @@ -433,12 +473,12 @@ class Preferences { 'section' => 'personal/signature', ]; $defaultPreferences['nickname'] = [ - 'type' => $authManager->allowsPropertyChange( 'nickname' ) ? 'text' : 'info', - 'maxlength' => $config->get( 'MaxSigChars' ), + 'type' => $this->authManager->allowsPropertyChange( 'nickname' ) ? 'text' : 'info', + 'maxlength' => $this->config->get( 'MaxSigChars' ), 'label-message' => 'yournick', - 'validation-callback' => [ 'Preferences', 'validateSignature' ], + 'validation-callback' => [ $this, 'validateSignature' ], 'section' => 'personal/signature', - 'filter-callback' => [ 'Preferences', 'cleanSignature' ], + 'filter-callback' => [ $this, 'cleanSignature' ], ]; $defaultPreferences['fancysig'] = [ 'type' => 'toggle', @@ -450,20 +490,20 @@ class Preferences { # # Email stuff - if ( $config->get( 'EnableEmail' ) ) { + if ( $this->config->get( 'EnableEmail' ) ) { if ( $canViewPrivateInfo ) { - $helpMessages[] = $config->get( 'EmailConfirmToEdit' ) + $helpMessages[] = $this->config->get( 'EmailConfirmToEdit' ) ? 'prefs-help-email-required' : 'prefs-help-email'; - if ( $config->get( 'EnableUserEmail' ) ) { + if ( $this->config->get( 'EnableUserEmail' ) ) { // additional messages when users can send email to each other $helpMessages[] = 'prefs-help-email-others'; } $emailAddress = $user->getEmail() ? htmlspecialchars( $user->getEmail() ) : ''; - if ( $canEditPrivateInfo && $authManager->allowsPropertyChange( 'emailaddress' ) ) { - $link = $linkRenderer->makeLink( + if ( $canEditPrivateInfo && $this->authManager->allowsPropertyChange( 'emailaddress' ) ) { + $link = $this->linkRenderer->makeLink( SpecialPage::getTitleFor( 'ChangeEmail' ), $context->msg( $user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail' )->text(), [], @@ -488,7 +528,7 @@ class Preferences { $disableEmailPrefs = false; - if ( $config->get( 'EmailAuthentication' ) ) { + if ( $this->config->get( 'EmailAuthentication' ) ) { $emailauthenticationclass = 'mw-email-not-authenticated'; if ( $user->getEmail() ) { if ( $user->getEmailAuthenticationTimestamp() ) { @@ -507,7 +547,7 @@ class Preferences { } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg( 'emailnotauthenticated' )->parse() . '
' . - $linkRenderer->makeKnownLink( + $this->linkRenderer->makeKnownLink( SpecialPage::getTitleFor( 'Confirmemail' ), $context->msg( 'emailconfirmlink' )->text() ) . '
'; @@ -532,7 +572,7 @@ class Preferences { } } - if ( $config->get( 'EnableUserEmail' ) && $user->isAllowed( 'sendemail' ) ) { + if ( $this->config->get( 'EnableUserEmail' ) && $user->isAllowed( 'sendemail' ) ) { $defaultPreferences['disablemail'] = [ 'id' => 'wpAllowEmail', 'type' => 'toggle', @@ -557,7 +597,7 @@ class Preferences { 'disabled' => $disableEmailPrefs, ]; - if ( $config->get( 'EnableUserEmailBlacklist' ) ) { + if ( $this->config->get( 'EnableUserEmailBlacklist' ) ) { $lookup = CentralIdLookup::factory(); $ids = $user->getOption( 'email-blacklist', [] ); $names = $ids ? $lookup->namesFromCentralIds( $ids, $user ) : []; @@ -572,7 +612,7 @@ class Preferences { } } - if ( $config->get( 'EnotifWatchlist' ) ) { + if ( $this->config->get( 'EnotifWatchlist' ) ) { $defaultPreferences['enotifwatchlistpages'] = [ 'type' => 'toggle', 'section' => 'personal/email', @@ -580,7 +620,7 @@ class Preferences { 'disabled' => $disableEmailPrefs, ]; } - if ( $config->get( 'EnotifUserTalk' ) ) { + if ( $this->config->get( 'EnotifUserTalk' ) ) { $defaultPreferences['enotifusertalkpages'] = [ 'type' => 'toggle', 'section' => 'personal/email', @@ -588,8 +628,8 @@ class Preferences { 'disabled' => $disableEmailPrefs, ]; } - if ( $config->get( 'EnotifUserTalk' ) || $config->get( 'EnotifWatchlist' ) ) { - if ( $config->get( 'EnotifMinorEdits' ) ) { + if ( $this->config->get( 'EnotifUserTalk' ) || $this->config->get( 'EnotifWatchlist' ) ) { + if ( $this->config->get( 'EnotifMinorEdits' ) ) { $defaultPreferences['enotifminoredits'] = [ 'type' => 'toggle', 'section' => 'personal/email', @@ -598,7 +638,7 @@ class Preferences { ]; } - if ( $config->get( 'EnotifRevealEditorAddress' ) ) { + if ( $this->config->get( 'EnotifRevealEditorAddress' ) ) { $defaultPreferences['enotifrevealaddr'] = [ 'type' => 'toggle', 'section' => 'personal/email', @@ -616,11 +656,11 @@ class Preferences { * @param array &$defaultPreferences * @return void */ - static function skinPreferences( $user, IContextSource $context, &$defaultPreferences ) { + public function skinPreferences( User $user, IContextSource $context, &$defaultPreferences ) { # # Skin ##################################### // Skin selector, if there is at least one valid skin - $skinOptions = self::generateSkinOptions( $user, $context ); + $skinOptions = $this->generateSkinOptions( $user, $context ); if ( $skinOptions ) { $defaultPreferences['skin'] = [ 'type' => 'radio', @@ -629,9 +669,8 @@ class Preferences { ]; } - $config = $context->getConfig(); - $allowUserCss = $config->get( 'AllowUserCss' ); - $allowUserJs = $config->get( 'AllowUserJs' ); + $allowUserCss = $this->config->get( 'AllowUserCss' ); + $allowUserJs = $this->config->get( 'AllowUserJs' ); # Create links to user CSS/JS pages for all skins # This code is basically copied from generateSkinOptions(). It'd # be nice to somehow merge this back in there to avoid redundancy. @@ -639,15 +678,16 @@ class Preferences { $linkTools = []; $userName = $user->getName(); - $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); if ( $allowUserCss ) { $cssPage = Title::makeTitleSafe( NS_USER, $userName . '/common.css' ); - $linkTools[] = $linkRenderer->makeLink( $cssPage, $context->msg( 'prefs-custom-css' )->text() ); + $cssLinkText = $context->msg( 'prefs-custom-css' )->text(); + $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText ); } if ( $allowUserJs ) { $jsPage = Title::makeTitleSafe( NS_USER, $userName . '/common.js' ); - $linkTools[] = $linkRenderer->makeLink( $jsPage, $context->msg( 'prefs-custom-js' )->text() ); + $jsLinkText = $context->msg( 'prefs-custom-js' )->text(); + $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText ); } $defaultPreferences['commoncssjs'] = [ @@ -661,21 +701,20 @@ class Preferences { } /** - * @param User $user * @param IContextSource $context * @param array &$defaultPreferences */ - static function filesPreferences( $user, IContextSource $context, &$defaultPreferences ) { + public function filesPreferences( IContextSource $context, &$defaultPreferences ) { # # Files ##################################### $defaultPreferences['imagesize'] = [ 'type' => 'select', - 'options' => self::getImageSizes( $context ), + 'options' => $this->getImageSizes( $context ), 'label-message' => 'imagemaxsize', 'section' => 'rendering/files', ]; $defaultPreferences['thumbsize'] = [ 'type' => 'select', - 'options' => self::getThumbSizes( $context ), + 'options' => $this->getThumbSizes( $context ), 'label-message' => 'thumbsize', 'section' => 'rendering/files', ]; @@ -687,9 +726,9 @@ class Preferences { * @param array &$defaultPreferences * @return void */ - static function datetimePreferences( $user, IContextSource $context, &$defaultPreferences ) { + public function datetimePreferences( $user, IContextSource $context, &$defaultPreferences ) { # # Date and time ##################################### - $dateOptions = self::getDateOptions( $context ); + $dateOptions = $this->getDateOptions( $context ); if ( $dateOptions ) { $defaultPreferences['date'] = [ 'type' => 'radio', @@ -727,7 +766,7 @@ class Preferences { $tzOffset = $user->getOption( 'timecorrection' ); $tz = explode( '|', $tzOffset, 3 ); - $tzOptions = self::getTimezoneOptions( $context ); + $tzOptions = $this->getTimezoneOptions( $context ); $tzSetting = $tzOffset; if ( count( $tz ) > 1 && $tz[0] == 'ZoneInfo' && @@ -759,11 +798,10 @@ class Preferences { } /** - * @param User $user - * @param IContextSource $context + * @param MessageLocalizer $l10n * @param array &$defaultPreferences */ - static function renderingPreferences( $user, IContextSource $context, &$defaultPreferences ) { + public function renderingPreferences( MessageLocalizer $l10n, &$defaultPreferences ) { # # Diffs #################################### $defaultPreferences['diffonly'] = [ 'type' => 'toggle', @@ -777,13 +815,13 @@ class Preferences { ]; # # Page Rendering ############################## - if ( $context->getConfig()->get( 'AllowUserCssPrefs' ) ) { + if ( $this->config->get( 'AllowUserCssPrefs' ) ) { $defaultPreferences['underline'] = [ 'type' => 'select', 'options' => [ - $context->msg( 'underline-never' )->text() => 0, - $context->msg( 'underline-always' )->text() => 1, - $context->msg( 'underline-default' )->text() => 2, + $l10n->msg( 'underline-never' )->text() => 0, + $l10n->msg( 'underline-always' )->text() => 1, + $l10n->msg( 'underline-default' )->text() => 2, ], 'label-message' => 'tog-underline', 'section' => 'rendering/advancedrendering', @@ -791,9 +829,9 @@ class Preferences { } $stubThresholdValues = [ 50, 100, 500, 1000, 2000, 5000, 10000 ]; - $stubThresholdOptions = [ $context->msg( 'stub-threshold-disabled' )->text() => 0 ]; + $stubThresholdOptions = [ $l10n->msg( 'stub-threshold-disabled' )->text() => 0 ]; foreach ( $stubThresholdValues as $value ) { - $stubThresholdOptions[$context->msg( 'size-bytes', $value )->text()] = $value; + $stubThresholdOptions[$l10n->msg( 'size-bytes', $value )->text()] = $value; } $defaultPreferences['stubthreshold'] = [ @@ -801,9 +839,9 @@ class Preferences { 'section' => 'rendering/advancedrendering', 'options' => $stubThresholdOptions, // This is not a raw HTML message; label-raw is needed for the manual - 'label-raw' => $context->msg( 'stub-threshold' )->rawParams( + 'label-raw' => $l10n->msg( 'stub-threshold' )->rawParams( '' . - $context->msg( 'stub-threshold-sample-link' )->parse() . + $l10n->msg( 'stub-threshold-sample-link' )->parse() . '' )->parse(), ]; @@ -822,10 +860,10 @@ class Preferences { /** * @param User $user - * @param IContextSource $context + * @param MessageLocalizer $l10n * @param array &$defaultPreferences */ - static function editingPreferences( $user, IContextSource $context, &$defaultPreferences ) { + public function editingPreferences( User $user, MessageLocalizer $l10n, &$defaultPreferences ) { # # Editing ##################################### $defaultPreferences['editsectiononrightclick'] = [ 'type' => 'toggle', @@ -838,15 +876,15 @@ class Preferences { 'label-message' => 'tog-editondblclick', ]; - if ( $context->getConfig()->get( 'AllowUserCssPrefs' ) ) { + if ( $this->config->get( 'AllowUserCssPrefs' ) ) { $defaultPreferences['editfont'] = [ 'type' => 'select', 'section' => 'editing/editor', 'label-message' => 'editfont-style', 'options' => [ - $context->msg( 'editfont-monospace' )->text() => 'monospace', - $context->msg( 'editfont-sansserif' )->text() => 'sans-serif', - $context->msg( 'editfont-serif' )->text() => 'serif', + $l10n->msg( 'editfont-monospace' )->text() => 'monospace', + $l10n->msg( 'editfont-sansserif' )->text() => 'sans-serif', + $l10n->msg( 'editfont-serif' )->text() => 'serif', ] ]; } @@ -894,12 +932,11 @@ class Preferences { /** * @param User $user - * @param IContextSource $context + * @param MessageLocalizer $l10n * @param array &$defaultPreferences */ - static function rcPreferences( $user, IContextSource $context, &$defaultPreferences ) { - $config = $context->getConfig(); - $rcMaxAge = $config->get( 'RCMaxAge' ); + public function rcPreferences( User $user, MessageLocalizer $l10n, &$defaultPreferences ) { + $rcMaxAge = $this->config->get( 'RCMaxAge' ); # # RecentChanges ##################################### $defaultPreferences['rcdays'] = [ 'type' => 'float', @@ -907,7 +944,7 @@ class Preferences { 'section' => 'rc/displayrc', 'min' => 1, 'max' => ceil( $rcMaxAge / ( 3600 * 24 ) ), - 'help' => $context->msg( 'recentchangesdays-max' )->numParams( + 'help' => $l10n->msg( 'recentchangesdays-max' )->numParams( ceil( $rcMaxAge / ( 3600 * 24 ) ) )->escaped() ]; $defaultPreferences['rclimit'] = [ @@ -944,7 +981,8 @@ class Preferences { $defaultPreferences['rcfilters-wl-saved-queries-versionbackup'] = [ 'type' => 'api', ]; - if ( $config->get( 'RCWatchCategoryMembership' ) ) { + + if ( $this->config->get( 'RCWatchCategoryMembership' ) ) { $defaultPreferences['hidecategorization'] = [ 'type' => 'toggle', 'label-message' => 'tog-hidecategorization', @@ -968,7 +1006,7 @@ class Preferences { ]; } - if ( $config->get( 'RCShowWatchingUsers' ) ) { + if ( $this->config->get( 'RCShowWatchingUsers' ) ) { $defaultPreferences['shownumberswatching'] = [ 'type' => 'toggle', 'section' => 'rc/advancedrc', @@ -976,7 +1014,7 @@ class Preferences { ]; } - if ( $config->get( 'StructuredChangeFiltersShowPreference' ) ) { + if ( $this->config->get( 'StructuredChangeFiltersShowPreference' ) ) { $defaultPreferences['rcenhancedfilters-disable'] = [ 'type' => 'toggle', 'section' => 'rc/opt-out', @@ -991,9 +1029,10 @@ class Preferences { * @param IContextSource $context * @param array &$defaultPreferences */ - static function watchlistPreferences( $user, IContextSource $context, &$defaultPreferences ) { - $config = $context->getConfig(); - $watchlistdaysMax = ceil( $config->get( 'RCMaxAge' ) / ( 3600 * 24 ) ); + public function watchlistPreferences( + User $user, IContextSource $context, &$defaultPreferences + ) { + $watchlistdaysMax = ceil( $this->config->get( 'RCMaxAge' ) / ( 3600 * 24 ) ); # # Watchlist ##################################### if ( $user->isAllowed( 'editmywatchlist' ) ) { @@ -1003,10 +1042,9 @@ class Preferences { 'raw' => [ 'EditWatchlist', 'raw' ], 'clear' => [ 'EditWatchlist', 'clear' ], ]; - $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); foreach ( $editWatchlistModes as $editWatchlistMode => $mode ) { // Messages: prefs-editwatchlist-edit, prefs-editwatchlist-raw, prefs-editwatchlist-clear - $editWatchlistLinks[] = $linkRenderer->makeKnownLink( + $editWatchlistLinks[] = $this->linkRenderer->makeKnownLink( SpecialPage::getTitleFor( $mode[0], $mode[1] ), new HtmlArmor( $context->msg( "prefs-editwatchlist-{$editWatchlistMode}" )->parse() ) ); @@ -1079,7 +1117,7 @@ class Preferences { 'label-message' => 'tog-watchlistunwatchlinks', ]; - if ( $config->get( 'RCWatchCategoryMembership' ) ) { + if ( $this->config->get( 'RCWatchCategoryMembership' ) ) { $defaultPreferences['watchlisthidecategorization'] = [ 'type' => 'toggle', 'section' => 'watchlist/advancedwatchlist', @@ -1127,7 +1165,7 @@ class Preferences { } } - if ( $config->get( 'EnableAPI' ) ) { + if ( $this->config->get( 'EnableAPI' ) ) { $defaultPreferences['watchlisttoken'] = [ 'type' => 'api', ]; @@ -1142,11 +1180,9 @@ class Preferences { } /** - * @param User $user - * @param IContextSource $context * @param array &$defaultPreferences */ - static function searchPreferences( $user, IContextSource $context, &$defaultPreferences ) { + public function searchPreferences( &$defaultPreferences ) { foreach ( MWNamespace::getValidNamespaces() as $n ) { $defaultPreferences['searchNs' . $n] = [ 'type' => 'api', @@ -1154,28 +1190,17 @@ class Preferences { } } - /** - * Dummy, kept for backwards-compatibility. - * @param User $user - * @param IContextSource $context - * @param array &$defaultPreferences - */ - static function miscPreferences( $user, IContextSource $context, &$defaultPreferences ) { - } - /** * @param User $user The User object * @param IContextSource $context * @return array Text/links to display as key; $skinkey as value */ - static function generateSkinOptions( $user, IContextSource $context ) { + public function generateSkinOptions( User $user, IContextSource $context ) { $ret = []; $mptitle = Title::newMainPage(); $previewtext = $context->msg( 'skin-preview' )->escaped(); - $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); - # Only show skins that aren't disabled in $wgSkipSkins $validSkinNames = Skin::getAllowedSkins(); @@ -1186,10 +1211,9 @@ class Preferences { } } - $config = $context->getConfig(); - $defaultSkin = $config->get( 'DefaultSkin' ); - $allowUserCss = $config->get( 'AllowUserCss' ); - $allowUserJs = $config->get( 'AllowUserJs' ); + $defaultSkin = $this->config->get( 'DefaultSkin' ); + $allowUserCss = $this->config->get( 'AllowUserCss' ); + $allowUserJs = $this->config->get( 'AllowUserJs' ); # Sort by the internal name, so that the ordering is the same for each display language, # especially if some skin names are translated to use a different alphabet and some are not. @@ -1221,12 +1245,14 @@ class Preferences { # Create links to user CSS/JS pages if ( $allowUserCss ) { $cssPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.css' ); - $linkTools[] = $linkRenderer->makeLink( $cssPage, $context->msg( 'prefs-custom-css' )->text() ); + $cssLinkText = $context->msg( 'prefs-custom-css' )->text(); + $linkTools[] = $this->linkRenderer->makeLink( $cssPage, $cssLinkText ); } if ( $allowUserJs ) { $jsPage = Title::makeTitleSafe( NS_USER, $user->getName() . '/' . $skinkey . '.js' ); - $linkTools[] = $linkRenderer->makeLink( $jsPage, $context->msg( 'prefs-custom-js' )->text() ); + $jsLinkText = $context->msg( 'prefs-custom-js' )->text(); + $linkTools[] = $this->linkRenderer->makeLink( $jsPage, $jsLinkText ); } $display = $sn . ' ' . $context->msg( 'parentheses' ) @@ -1248,7 +1274,7 @@ class Preferences { * @param IContextSource $context * @return array */ - static function getDateOptions( IContextSource $context ) { + public function getDateOptions( IContextSource $context ) { $lang = $context->getLanguage(); $dateopts = $lang->getDatePreferences(); @@ -1279,14 +1305,14 @@ class Preferences { } /** - * @param IContextSource $context + * @param MessageLocalizer $l10n * @return array */ - static function getImageSizes( IContextSource $context ) { + public function getImageSizes( MessageLocalizer $l10n ) { $ret = []; - $pixels = $context->msg( 'unit-pixel' )->text(); + $pixels = $l10n->msg( 'unit-pixel' )->text(); - foreach ( $context->getConfig()->get( 'ImageLimits' ) as $index => $limits ) { + foreach ( $this->config->get( 'ImageLimits' ) as $index => $limits ) { // Note: A left-to-right marker (\u200e) is inserted, see T144386 $display = "{$limits[0]}" . json_decode( '"\u200e"' ) . "×{$limits[1]}" . $pixels; $ret[$display] = $index; @@ -1296,14 +1322,14 @@ class Preferences { } /** - * @param IContextSource $context + * @param MessageLocalizer $l10n * @return array */ - static function getThumbSizes( IContextSource $context ) { + public function getThumbSizes( MessageLocalizer $l10n ) { $ret = []; - $pixels = $context->msg( 'unit-pixel' )->text(); + $pixels = $l10n->msg( 'unit-pixel' )->text(); - foreach ( $context->getConfig()->get( 'ThumbLimits' ) as $index => $size ) { + foreach ( $this->config->get( 'ThumbLimits' ) as $index => $size ) { $display = $size . $pixels; $ret[$display] = $index; } @@ -1312,12 +1338,13 @@ class Preferences { } /** + * @todo Remove $wgParser. * @param string $signature * @param array $alldata * @param HTMLForm $form * @return bool|string */ - static function validateSignature( $signature, $alldata, $form ) { + public function validateSignature( $signature, $alldata, $form ) { global $wgParser; $maxSigChars = $form->getConfig()->get( 'MaxSigChars' ); if ( mb_strlen( $signature ) > $maxSigChars ) { @@ -1343,7 +1370,7 @@ class Preferences { * @param HTMLForm $form * @return string */ - static function cleanSignature( $signature, $alldata, $form ) { + public function cleanSignature( $signature, $alldata, $form ) { if ( isset( $alldata['fancysig'] ) && $alldata['fancysig'] ) { global $wgParser; $signature = $wgParser->cleanSig( $signature ); @@ -1362,13 +1389,13 @@ class Preferences { * @param array $remove Array of items to remove * @return PreferencesForm|HTMLForm */ - static function getFormObject( - $user, + public function getForm( + User $user, IContextSource $context, $formClass = 'PreferencesForm', array $remove = [] ) { - $formDescriptor = self::getPreferences( $user, $context ); + $formDescriptor = $this->getFormDescriptor( $user, $context ); if ( count( $remove ) ) { $removeKeys = array_flip( $remove ); $formDescriptor = array_diff_key( $formDescriptor, $removeKeys ); @@ -1393,7 +1420,7 @@ class Preferences { # Used message keys: 'accesskey-preferences-save', 'tooltip-preferences-save' $htmlForm->setSubmitTooltip( 'preferences-save' ); $htmlForm->setSubmitID( 'prefcontrol' ); - $htmlForm->setSubmitCallback( [ 'Preferences', 'tryFormSubmit' ] ); + $htmlForm->setSubmitCallback( [ $this, 'submitForm' ] ); return $htmlForm; } @@ -1402,11 +1429,11 @@ class Preferences { * @param IContextSource $context * @return array */ - static function getTimezoneOptions( IContextSource $context ) { + public function getTimezoneOptions( IContextSource $context ) { $opt = []; $localTZoffset = $context->getConfig()->get( 'LocalTZoffset' ); - $timeZoneList = self::getTimeZoneList( $context->getLanguage() ); + $timeZoneList = $this->getTimeZoneList( $context->getLanguage() ); $timestamp = MWTimestamp::getLocalInstance(); // Check that the LocalTZoffset is the same as the local time zone offset @@ -1447,7 +1474,7 @@ class Preferences { * @param array $alldata * @return int */ - static function filterIntval( $value, $alldata ) { + public static function filterIntval( $value, $alldata ) { return intval( $value ); } @@ -1456,7 +1483,7 @@ class Preferences { * @param array $alldata * @return string */ - static function filterTimezoneInput( $tz, $alldata ) { + public static function filterTimezoneInput( $tz, $alldata ) { $data = explode( '|', $tz, 3 ); switch ( $data[0] ) { case 'ZoneInfo': @@ -1508,7 +1535,7 @@ class Preferences { * @param PreferencesForm $form * @return bool|Status|string */ - static function tryFormSubmit( $formData, $form ) { + public function saveFormData( $formData, $form ) { $user = $form->getModifiedUser(); $hiddenPrefs = $form->getConfig()->get( 'HiddenPrefs' ); $result = true; @@ -1519,9 +1546,9 @@ class Preferences { // Filter input foreach ( array_keys( $formData ) as $name ) { - if ( isset( self::$saveFilters[$name] ) ) { - $formData[$name] = - call_user_func( self::$saveFilters[$name], $formData[$name], $formData ); + $filters = $this->getSaveFilters(); + if ( isset( $filters[$name] ) ) { + $formData[$name] = call_user_func( $filters[$name], $formData[$name], $formData ); } } @@ -1539,7 +1566,7 @@ class Preferences { if ( $user->isAllowed( 'editmyoptions' ) ) { $oldUserOptions = $user->getOptions(); - foreach ( self::$saveBlacklist as $b ) { + foreach ( $this->getSaveBlacklist() as $b ) { unset( $formData[$b] ); } @@ -1573,7 +1600,7 @@ class Preferences { ); } - MediaWiki\Auth\AuthManager::callLegacyAuthPlugin( 'updateExternalDB', [ $user ] ); + AuthManager::callLegacyAuthPlugin( 'updateExternalDB', [ $user ] ); $user->saveSettings(); return $result; @@ -1584,8 +1611,8 @@ class Preferences { * @param PreferencesForm $form * @return Status */ - public static function tryUISubmit( $formData, $form ) { - $res = self::tryFormSubmit( $formData, $form ); + public function submitForm( $formData, $form ) { + $res = $this->saveFormData( $formData, $form ); if ( $res ) { $urlOptions = []; @@ -1616,7 +1643,7 @@ class Preferences { * preferences and the region * @since 1.26 */ - public static function getTimeZoneList( Language $language ) { + public function getTimeZoneList( Language $language ) { $identifiers = DateTimeZone::listIdentifiers(); if ( $identifiers === false ) { return [];