Merge "Adding explantation for why to use User::incEditCount()"
[lhc/web/wiklou.git] / includes / user / User.php
index 0acdb55..bb1e751 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  */
 
+use IPSet\IPSet;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Session\SessionManager;
 use MediaWiki\Session\Token;
@@ -27,6 +28,8 @@ use MediaWiki\Auth\AuthManager;
 use MediaWiki\Auth\AuthenticationResponse;
 use MediaWiki\Auth\AuthenticationRequest;
 use Wikimedia\ScopedCallback;
+use Wikimedia\Rdbms\Database;
+use Wikimedia\Rdbms\DBExpectedError;
 
 /**
  * String Some punctuation to prevent editing from broken text-mangling proxies.
@@ -146,7 +149,6 @@ class User implements IDBAccessObject {
                'editmyuserjs',
                'editmywatchlist',
                'editsemiprotected',
-               'editusercssjs', # deprecated
                'editusercss',
                'edituserjs',
                'hideuser',
@@ -508,6 +510,17 @@ class User implements IDBAccessObject {
 
                                $ttl = $cache->adaptiveTTL( wfTimestamp( TS_UNIX, $this->mTouched ), $ttl );
 
+                               // if a user group membership is about to expire, the cache needs to
+                               // expire at that time (T163691)
+                               foreach ( $this->mGroupMemberships as $ugm ) {
+                                       if ( $ugm->getExpiry() ) {
+                                               $secondsUntilExpiry = wfTimestamp( TS_UNIX, $ugm->getExpiry() ) - time();
+                                               if ( $secondsUntilExpiry > 0 && $secondsUntilExpiry < $ttl ) {
+                                                       $ttl = $secondsUntilExpiry;
+                                               }
+                                       }
+                               }
+
                                return $data;
 
                        },
@@ -1745,11 +1758,12 @@ class User implements IDBAccessObject {
                                        $this->blockTrigger = 'cookie-block';
                                        return $tmpBlock;
                                } else {
-                                       // If the block is not valid, clear the block cookie (but don't delete it,
-                                       // because it needs to be cleared from LocalStorage as well and an empty string
-                                       // value is checked for in the mediawiki.user.blockcookie module).
-                                       $tmpBlock->setCookie( $this->getRequest()->response(), true );
+                                       // If the block is not valid, remove the cookie.
+                                       Block::clearCookie( $this->getRequest()->response() );
                                }
+                       } else {
+                               // If the block doesn't exist, remove the cookie.
+                               Block::clearCookie( $this->getRequest()->response() );
                        }
                }
                return false;
@@ -1841,18 +1855,33 @@ class User implements IDBAccessObject {
                        $wgProxyList = array_map( 'trim', file( $wgProxyList ) );
                }
 
-               if ( is_array( $wgProxyList ) ) {
-                       if (
-                               // Look for IP as value
-                               array_search( $ip, $wgProxyList ) !== false ||
-                               // Look for IP as key (for backwards-compatility)
-                               array_key_exists( $ip, $wgProxyList )
-                       ) {
-                               return true;
+               $resultProxyList = [];
+               $deprecatedIPEntries = [];
+
+               // backward compatibility: move all ip addresses in keys to values
+               foreach ( $wgProxyList as $key => $value ) {
+                       $keyIsIP = IP::isIPAddress( $key );
+                       $valueIsIP = IP::isIPAddress( $value );
+                       if ( $keyIsIP && !$valueIsIP ) {
+                               $deprecatedIPEntries[] = $key;
+                               $resultProxyList[] = $key;
+                       } elseif ( $keyIsIP && $valueIsIP ) {
+                               $deprecatedIPEntries[] = $key;
+                               $resultProxyList[] = $key;
+                               $resultProxyList[] = $value;
+                       } else {
+                               $resultProxyList[] = $value;
                        }
                }
 
-               return false;
+               if ( $deprecatedIPEntries ) {
+                       wfDeprecated(
+                               'IP addresses in the keys of $wgProxyList (found the following IP addresses in keys: ' .
+                               implode( ', ', $deprecatedIPEntries ) . ', please move them to values)', '1.30' );
+               }
+
+               $proxyListIPSet = new IPSet( $resultProxyList );
+               return $proxyListIPSet->match( $ip );
        }
 
        /**
@@ -3215,7 +3244,7 @@ class User implements IDBAccessObject {
 
        /**
         * Get the permissions this user has.
-        * @return array Array of String permission names
+        * @return string[] permission names
         */
        public function getRights() {
                if ( is_null( $this->mRights ) ) {
@@ -5064,6 +5093,9 @@ class User implements IDBAccessObject {
 
        /**
         * Deferred version of incEditCountImmediate()
+        *
+        * This function, rather than incEditCountImmediate(), should be used for
+        * most cases as it avoids potential deadlocks caused by concurrent editing.
         */
        public function incEditCount() {
                wfGetDB( DB_MASTER )->onTransactionPreCommitOrIdle(
@@ -5265,6 +5297,13 @@ class User implements IDBAccessObject {
                                $this->mOptionOverrides = [];
                                $data = [];
                                foreach ( $res as $row ) {
+                                       // Convert '0' to 0. PHP's boolean conversion considers them both
+                                       // false, but e.g. JavaScript considers the former as true.
+                                       // @todo: T54542 Somehow determine the desired type (string/int/bool)
+                                       //  and convert all values here.
+                                       if ( $row->up_value === '0' ) {
+                                               $row->up_value = 0;
+                                       }
                                        $data[$row->up_property] = $row->up_value;
                                }
                        }