Merge "resourceloader: Simplify StringSet fallback"
[lhc/web/wiklou.git] / includes / user / User.php
index b5fa97f..d6d4db2 100644 (file)
@@ -147,6 +147,9 @@ class User implements IDBAccessObject, UserIdentity {
                'editmyuserjs',
                'editmywatchlist',
                'editsemiprotected',
+               'editsitecss',
+               'editsitejson',
+               'editsitejs',
                'editusercss',
                'edituserjson',
                'edituserjs',
@@ -636,6 +639,27 @@ class User implements IDBAccessObject, UserIdentity {
                return $u;
        }
 
+       /**
+        * Returns a User object corresponding to the given UserIdentity.
+        *
+        * @since 1.32
+        *
+        * @param UserIdentity $identity
+        *
+        * @return User
+        */
+       public static function newFromIdentity( UserIdentity $identity ) {
+               if ( $identity instanceof User ) {
+                       return $identity;
+               }
+
+               return self::newFromAnyId(
+                       $identity->getId() === 0 ? null : $identity->getId(),
+                       $identity->getName() === '' ? null : $identity->getName(),
+                       $identity->getActorId() === 0 ? null : $identity->getActorId()
+               );
+       }
+
        /**
         * Static factory method for creation from an ID, name, and/or actor ID
         *
@@ -738,7 +762,8 @@ class User implements IDBAccessObject, UserIdentity {
         * will be loaded once more from the database when accessing them.
         *
         * @param stdClass $row A row from the user table
-        * @param array $data Further data to load into the object (see User::loadFromRow for valid keys)
+        * @param array|null $data Further data to load into the object
+        *  (see User::loadFromRow for valid keys)
         * @return User
         */
        public static function newFromRow( $row, $data = null ) {
@@ -961,13 +986,13 @@ class User implements IDBAccessObject, UserIdentity {
         * @return bool
         */
        public static function isValidUserName( $name ) {
-               global $wgContLang, $wgMaxNameChars;
+               global $wgMaxNameChars;
 
                if ( $name == ''
                        || self::isIP( $name )
                        || strpos( $name, '/' ) !== false
                        || strlen( $name ) > $wgMaxNameChars
-                       || $name != $wgContLang->ucfirst( $name )
+                       || $name != MediaWikiServices::getInstance()->getContentLanguage()->ucfirst( $name )
                ) {
                        return false;
                }
@@ -985,10 +1010,10 @@ class User implements IDBAccessObject, UserIdentity {
                // Should these be merged into the title char list?
                $unicodeBlacklist = '/[' .
                        '\x{0080}-\x{009f}' . # iso-8859-1 control chars
-                       '\x{00a0}' .          # non-breaking space
+                       '\x{00a0}' . # non-breaking space
                        '\x{2000}-\x{200f}' . # various whitespace
                        '\x{2028}-\x{202f}' . # breaks and control chars
-                       '\x{3000}' .          # ideographic space
+                       '\x{3000}' . # ideographic space
                        '\x{e000}-\x{f8ff}' . # private use
                        ']/u';
                if ( preg_match( $unicodeBlacklist, $name ) ) {
@@ -1041,7 +1066,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @param string|array $groups A single group name or an array of group names
         * @param int $limit Max number of users to return. The actual limit will never exceed 5000
         *   records; larger values are ignored.
-        * @param int $after ID the user to start after
+        * @param int|null $after ID the user to start after
         * @return UserArrayFromResult
         */
        public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) {
@@ -1203,8 +1228,7 @@ class User implements IDBAccessObject, UserIdentity {
         */
        public static function getCanonicalName( $name, $validate = 'valid' ) {
                // Force usernames to capital
-               global $wgContLang;
-               $name = $wgContLang->ucfirst( $name );
+               $name = MediaWikiServices::getInstance()->getContentLanguage()->ucfirst( $name );
 
                # Reject names containing '#'; these will be cleaned up
                # with title normalisation, but then it's too late to
@@ -1444,7 +1468,7 @@ class User implements IDBAccessObject, UserIdentity {
         * Initialize this object from a row from the user table.
         *
         * @param stdClass $row Row from the user table to load.
-        * @param array $data Further user data to load into the object
+        * @param array|null $data Further user data to load into the object
         *
         *  user_groups   Array of arrays or stdClass result rows out of the user_groups
         *                table. Previously you were supposed to pass an array of strings
@@ -1736,24 +1760,29 @@ class User implements IDBAccessObject, UserIdentity {
         * @return array Array of String options
         */
        public static function getDefaultOptions() {
-               global $wgNamespacesToBeSearchedDefault, $wgDefaultUserOptions, $wgContLang, $wgDefaultSkin;
+               global $wgNamespacesToBeSearchedDefault, $wgDefaultUserOptions, $wgDefaultSkin;
 
                static $defOpt = null;
                static $defOptLang = null;
 
-               if ( $defOpt !== null && $defOptLang === $wgContLang->getCode() ) {
-                       // $wgContLang does not change (and should not change) mid-request,
-                       // but the unit tests change it anyway, and expect this method to
-                       // return values relevant to the current $wgContLang.
+               $contLang = MediaWikiServices::getInstance()->getContentLanguage();
+               if ( $defOpt !== null && $defOptLang === $contLang->getCode() ) {
+                       // The content language does not change (and should not change) mid-request, but the
+                       // unit tests change it anyway, and expect this method to return values relevant to the
+                       // current content language.
                        return $defOpt;
                }
 
                $defOpt = $wgDefaultUserOptions;
                // Default language setting
-               $defOptLang = $wgContLang->getCode();
+               $defOptLang = $contLang->getCode();
                $defOpt['language'] = $defOptLang;
                foreach ( LanguageConverter::$languagesWithVariants as $langCode ) {
-                       $defOpt[$langCode == $wgContLang->getCode() ? 'variant' : "variant-$langCode"] = $langCode;
+                       if ( $langCode === $contLang->getCode() ) {
+                               $defOpt['variant'] = $langCode;
+                       } else {
+                               $defOpt["variant-$langCode"] = $langCode;
+                       }
                }
 
                // NOTE: don't use SearchEngineConfig::getSearchableNamespaces here,
@@ -2671,7 +2700,7 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Update the 'You have new messages!' status.
         * @param bool $val Whether the user has new messages
-        * @param Revision $curRev New, as yet unseen revision of the user talk
+        * @param Revision|null $curRev New, as yet unseen revision of the user talk
         *   page. Ignored if null or !$val.
         */
        public function setNewtalk( $val, $curRev = null ) {
@@ -3112,7 +3141,7 @@ class User implements IDBAccessObject, UserIdentity {
         * Get the user's current setting for a given option.
         *
         * @param string $oname The option to check
-        * @param string|array $defaultOverride A default value returned if the option does not exist
+        * @param string|array|null $defaultOverride A default value returned if the option does not exist
         * @param bool $ignoreHidden Whether to ignore the effects of $wgHiddenPrefs
         * @return string|array|int|null User's current value for the option
         * @see getBoolOption()
@@ -3307,7 +3336,7 @@ class User implements IDBAccessObject, UserIdentity {
         *
         * @see User::listOptionKinds
         * @param IContextSource $context
-        * @param array $options Assoc. array with options keys to check as keys.
+        * @param array|null $options Assoc. array with options keys to check as keys.
         *   Defaults to $this->mOptions.
         * @return array The key => kind mapping data
         */
@@ -3335,7 +3364,7 @@ class User implements IDBAccessObject, UserIdentity {
                        if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
                                        ( isset( $info['class'] ) && $info['class'] == HTMLMultiSelectField::class ) ) {
                                $opts = HTMLFormField::flattenOptions( $info['options'] );
-                               $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
+                               $prefix = $info['prefix'] ?? $name;
 
                                foreach ( $opts as $value ) {
                                        $multiselectOptions["$prefix$value"] = true;
@@ -3350,7 +3379,7 @@ class User implements IDBAccessObject, UserIdentity {
                                        ( isset( $info['class'] ) && $info['class'] == HTMLCheckMatrix::class ) ) {
                                $columns = HTMLFormField::flattenOptions( $info['columns'] );
                                $rows = HTMLFormField::flattenOptions( $info['rows'] );
-                               $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
+                               $prefix = $info['prefix'] ?? $name;
 
                                foreach ( $columns as $column ) {
                                        foreach ( $rows as $row ) {
@@ -3669,7 +3698,7 @@ class User implements IDBAccessObject, UserIdentity {
         * never expire.)
         *
         * @param string $group Name of the group to add
-        * @param string $expiry Optional expiry timestamp in any format acceptable to
+        * @param string|null $expiry Optional expiry timestamp in any format acceptable to
         *   wfTimestamp(), or null if the group assignment should not expire
         * @return bool
         */
@@ -4047,7 +4076,7 @@ class User implements IDBAccessObject, UserIdentity {
         *
         * @param WebRequest|null $request WebRequest object to use; $wgRequest will be used if null
         *        is passed.
-        * @param bool $secure Whether to force secure/insecure cookies or use default
+        * @param bool|null $secure Whether to force secure/insecure cookies or use default
         * @param bool $rememberMe Whether to add a Token cookie for elongated sessions
         */
        public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
@@ -4596,7 +4625,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @param string $val Input value to compare
         * @param string|array $salt Optional function-specific data for hashing
         * @param WebRequest|null $request Object to use or null to use $wgRequest
-        * @param int $maxage Fail tokens older than this, in seconds
+        * @param int|null $maxage Fail tokens older than this, in seconds
         * @return bool Whether the token matches
         */
        public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) {
@@ -4610,7 +4639,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @param string $val Input value to compare
         * @param string|array $salt Optional function-specific data for hashing
         * @param WebRequest|null $request Object to use or null to use $wgRequest
-        * @param int $maxage Fail tokens older than this, in seconds
+        * @param int|null $maxage Fail tokens older than this, in seconds
         * @return bool Whether the token matches
         */
        public function matchEditTokenNoSuffix( $val, $salt = '', $request = null, $maxage = null ) {
@@ -4661,7 +4690,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @param string $body Message body
         * @param User|null $from Optional sending user; if unspecified, default
         *   $wgPasswordSender will be used.
-        * @param string $replyto Reply-To address
+        * @param string|null $replyto Reply-To address
         * @return Status
         */
        public function sendMail( $subject, $body, $from = null, $replyto = null ) {
@@ -5048,10 +5077,10 @@ class User implements IDBAccessObject, UserIdentity {
         */
        public static function getAllGroups() {
                global $wgGroupPermissions, $wgRevokePermissions;
-               return array_diff(
+               return array_values( array_diff(
                        array_merge( array_keys( $wgGroupPermissions ), array_keys( $wgRevokePermissions ) ),
                        self::getImplicitGroups()
-               );
+               ) );
        }
 
        /**
@@ -5425,11 +5454,9 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Load the user options either from cache, the database or an array
         *
-        * @param array $data Rows for the current user out of the user_properties table
+        * @param array|null $data Rows for the current user out of the user_properties table
         */
        protected function loadOptions( $data = null ) {
-               global $wgContLang;
-
                $this->load();
 
                if ( $this->mOptionsLoaded ) {
@@ -5443,7 +5470,7 @@ class User implements IDBAccessObject, UserIdentity {
                        // There's no need to do it for logged-in users: they can set preferences,
                        // and handling of page content is done by $pageLang->getPreferredVariant() and such,
                        // so don't override user's choice (especially when the user chooses site default).
-                       $variant = $wgContLang->getDefaultVariant();
+                       $variant = MediaWikiServices::getInstance()->getContentLanguage()->getDefaultVariant();
                        $this->mOptions['variant'] = $variant;
                        $this->mOptions['language'] = $variant;
                        $this->mOptionsLoaded = true;
@@ -5485,18 +5512,17 @@ class User implements IDBAccessObject, UserIdentity {
                                }
                        }
 
-                       // Convert the email blacklist from a new line delimited string
-                       // to an array of ids.
-                       if ( isset( $data['email-blacklist'] ) && $data['email-blacklist'] ) {
-                               $data['email-blacklist'] = array_map( 'intval', explode( "\n", $data['email-blacklist'] ) );
-                       }
-
                        foreach ( $data as $property => $value ) {
                                $this->mOptionOverrides[$property] = $value;
                                $this->mOptions[$property] = $value;
                        }
                }
 
+               // Replace deprecated language codes
+               $this->mOptions['language'] = LanguageCode::replaceDeprecatedCodes(
+                       $this->mOptions['language']
+               );
+
                $this->mOptionsLoaded = true;
 
                Hooks::run( 'UserLoadOptions', [ $this, &$this->mOptions ] );
@@ -5513,26 +5539,6 @@ class User implements IDBAccessObject, UserIdentity {
                // Not using getOptions(), to keep hidden preferences in database
                $saveOptions = $this->mOptions;
 
-               // Convert usernames to ids.
-               if ( isset( $this->mOptions['email-blacklist'] ) ) {
-                       if ( $this->mOptions['email-blacklist'] ) {
-                               $value = $this->mOptions['email-blacklist'];
-                               // Email Blacklist may be an array of ids or a string of new line
-                               // delimnated user names.
-                               if ( is_array( $value ) ) {
-                                       $ids = array_filter( $value, 'is_numeric' );
-                               } else {
-                                       $lookup = CentralIdLookup::factory();
-                                       $ids = $lookup->centralIdsFromNames( explode( "\n", $value ), $this );
-                               }
-                               $this->mOptions['email-blacklist'] = $ids;
-                               $saveOptions['email-blacklist'] = implode( "\n", $this->mOptions['email-blacklist'] );
-                       } else {
-                               // If the blacklist is empty, set it to null rather than an empty string.
-                               $this->mOptions['email-blacklist'] = null;
-                       }
-               }
-
                // Allow hooks to abort, for instance to save to a global profile.
                // Reset options to default state before saving.
                if ( !Hooks::run( 'UserSaveOptions', [ $this, &$saveOptions ] ) ) {