(Bug 17970) {{PROTECTIONLEVEL}} should be able to return the status for pages besides...
[lhc/web/wiklou.git] / includes / User.php
index f55281e..21f8dd0 100644 (file)
@@ -253,7 +253,7 @@ class User {
        /**
         * @return String
         */
-       function __toString(){
+       function __toString() {
                return $this->getName();
        }
 
@@ -293,6 +293,7 @@ class User {
                                wfRunHooks( 'UserLoadAfterLoadFromSession', array( $this ) );
                                break;
                        default:
+                               wfProfileOut( __METHOD__ );
                                throw new MWException( "Unrecognised value for User->mFrom: \"{$this->mFrom}\"" );
                }
                wfProfileOut( __METHOD__ );
@@ -332,6 +333,9 @@ class User {
                                $this->$name = $data[$name];
                        }
                }
+
+               $this->mLoadedItems = true;
+
                return true;
        }
 
@@ -370,7 +374,7 @@ class User {
         *    User::getCanonicalName(), except that true is accepted as an alias
         *    for 'valid', for BC.
         *
-        * @return User object, or false if the username is invalid
+        * @return User|bool User object, or false if the username is invalid
         *    (e.g. if it contains illegal characters or is an IP address). If the
         *    username is not present in the database, the result will be a user object
         *    with a name, zero user ID and default settings.
@@ -1201,6 +1205,7 @@ class User {
                $this->mRights = null;
                $this->mEffectiveGroups = null;
                $this->mImplicitGroups = null;
+               $this->mGroups = null;
                $this->mOptions = null;
                $this->mOptionsLoaded = false;
                $this->mEditCount = null;
@@ -2305,12 +2310,113 @@ class User {
        }
 
        /**
-        * Reset all options to the site defaults
+        * Return an associative array mapping preferences keys to the kind of a preference they're
+        * used for. Different kinds are handled differently when setting or reading preferences.
+        *
+        * Currently, the kind is one of:
+        * - 'registered' - preferences which are registered in core MediaWiki or
+        *                  by extensions using the UserGetDefaultOptions hook.
+        * - 'registered-multiselect' - as above, using the 'multiselect' type.
+        * - 'userjs' - preferences with names starting with 'userjs-', intended to
+        *              be used by user scripts.
+        * - 'unused' - preferences about which MediaWiki doesn't know anything.
+        *              These are usually legacy options, removed in newer versions.
+        *
+        * @param $context IContextSource
+        * @param $options array assoc. array with options keys to check as keys. Defaults to $this->mOptions.
+        * @return array the key => kind mapping data
         */
-       public function resetOptions() {
+       public function getOptionKinds( IContextSource $context, $options = null ) {
+               $this->loadOptions();
+               if ( $options === null ) {
+                       $options = $this->mOptions;
+               }
+
+               $prefs = Preferences::getPreferences( $this, $context );
+               $mapping = array();
+
+               // Multiselect options are stored in the database with one key per
+               // option, each having a boolean value. Extract those keys.
+               $multiselectOptions = array();
+               foreach ( $prefs as $name => $info ) {
+                       if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
+                                       ( isset( $info['class'] ) && $info['class'] == 'HTMLMultiSelectField' ) ) {
+                               $opts = HTMLFormField::flattenOptions( $info['options'] );
+                               $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
+
+                               foreach ( $opts as $value ) {
+                                       $multiselectOptions["$prefix$value"] = true;
+                               }
+
+                               unset( $prefs[$name] );
+                       }
+               }
+
+               // $value is ignored
+               foreach ( $options as $key => $value ) {
+                       if ( isset( $prefs[$key] ) ) {
+                               $mapping[$key] = 'registered';
+                       } elseif( isset( $multiselectOptions[$key] ) ) {
+                               $mapping[$key] = 'registered-multiselect';
+                       } elseif ( substr( $key, 0, 7 ) === 'userjs-' ) {
+                               $mapping[$key] = 'userjs';
+                       } else {
+                               $mapping[$key] = 'unused';
+                       }
+               }
+
+               return $mapping;
+       }
+
+       /**
+        * Reset certain (or all) options to the site defaults
+        *
+        * The optional parameter determines which kinds of preferences will be reset.
+        * Supported values are everything that can be reported by getOptionKinds()
+        * and 'all', which forces a reset of *all* preferences and overrides everything else.
+        *
+        * @param $resetKinds array|string which kinds of preferences to reset. Defaults to
+        *                                 array( 'registered', 'registered-multiselect', 'unused' )
+        *                                 for backwards-compatibility.
+        * @param $context IContextSource|null context source used when $resetKinds
+        *                                     does not contain 'all', passed to getOptionKinds().
+        *                                     Defaults to RequestContext::getMain() when null.
+        */
+       public function resetOptions(
+               $resetKinds = array( 'registered', 'registered-multiselect', 'unused' ),
+               IContextSource $context = null
+       ) {
                $this->load();
+               $defaultOptions = self::getDefaultOptions();
 
-               $this->mOptions = self::getDefaultOptions();
+               if ( !is_array( $resetKinds ) ) {
+                       $resetKinds = array( $resetKinds );
+               }
+
+               if ( in_array( 'all', $resetKinds ) ) {
+                       $newOptions = $defaultOptions;
+               } else {
+                       if ( $context === null ) {
+                               $context = RequestContext::getMain();
+                       }
+
+                       $optionKinds = $this->getOptionKinds( $context );
+                       $newOptions = array();
+
+                       // Use default values for the options that should be deleted, and
+                       // copy old values for the ones that shouldn't.
+                       foreach ( $this->mOptions as $key => $value ) {
+                               if ( in_array( $optionKinds[$key], $resetKinds ) ) {
+                                       if ( array_key_exists( $key, $defaultOptions ) ) {
+                                               $newOptions[$key] = $defaultOptions[$key];
+                                       }
+                               } else {
+                                       $newOptions[$key] = $value;
+                               }
+                       }
+               }
+
+               $this->mOptions = $newOptions;
                $this->mOptionsLoaded = true;
        }
 
@@ -2558,7 +2664,7 @@ class User {
         *
         * @return bool
         */
-       public function isAllowedAny( /*...*/ ){
+       public function isAllowedAny( /*...*/ ) {
                $permissions = func_get_args();
                foreach( $permissions as $permission ){
                        if( $this->isAllowed( $permission ) ){
@@ -2573,7 +2679,7 @@ class User {
         * @internal param $varargs string
         * @return bool True if the user is allowed to perform *all* of the given actions
         */
-       public function isAllowedAll( /*...*/ ){
+       public function isAllowedAll( /*...*/ ) {
                $permissions = func_get_args();
                foreach( $permissions as $permission ){
                        if( !$this->isAllowed( $permission ) ){
@@ -3034,6 +3140,7 @@ class User {
         * so it is still advisable to make the call conditional on isLoggedIn(),
         * and to commit the transaction after calling.
         *
+        * @throws MWException
         * @return Status
         */
        public function addToDatabase() {
@@ -3182,7 +3289,7 @@ class User {
                # bug 13611: if the IP address the user is trying to create an account from is
                # blocked with createaccount disabled, prevent new account creation there even
                # when the user is logged in
-               if( $this->mBlockedFromCreateAccount === false ){
+               if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
                        $this->mBlockedFromCreateAccount = Block::newFromTarget( null, $this->getRequest()->getIP() );
                }
                return $this->mBlockedFromCreateAccount instanceof Block && $this->mBlockedFromCreateAccount->prevents( 'createaccount' )