Index: trunk/phase3/includes/User.php |
— | — | @@ -157,10 +157,13 @@ |
158 | 158 | /** |
159 | 159 | * Bool Whether the cache variables have been loaded. |
160 | 160 | */ |
161 | | - var $mDataLoaded, $mAuthLoaded, $mOptionsLoaded; |
| 161 | + //@{ |
| 162 | + var $mOptionsLoaded; |
| 163 | + private $mLoadedItems = array(); |
| 164 | + //@} |
162 | 165 | |
163 | 166 | /** |
164 | | - * String Initialization data source if mDataLoaded==false. May be one of: |
| 167 | + * String Initialization data source if mLoadedItems!==true. May be one of: |
165 | 168 | * - 'defaults' anonymous user initialised from class defaults |
166 | 169 | * - 'name' initialise from mName |
167 | 170 | * - 'id' initialise from mId |
— | — | @@ -211,13 +214,13 @@ |
212 | 215 | * Load the user table data for this object from the source given by mFrom. |
213 | 216 | */ |
214 | 217 | function load() { |
215 | | - if ( $this->mDataLoaded ) { |
| 218 | + if ( $this->mLoadedItems === true ) { |
216 | 219 | return; |
217 | 220 | } |
218 | 221 | wfProfileIn( __METHOD__ ); |
219 | 222 | |
220 | 223 | # Set it now to avoid infinite recursion in accessors |
221 | | - $this->mDataLoaded = true; |
| 224 | + $this->mLoadedItems = true; |
222 | 225 | |
223 | 226 | switch ( $this->mFrom ) { |
224 | 227 | case 'defaults': |
— | — | @@ -336,6 +339,7 @@ |
337 | 340 | $u = new User; |
338 | 341 | $u->mName = $name; |
339 | 342 | $u->mFrom = 'name'; |
| 343 | + $u->setItemLoaded( 'name' ); |
340 | 344 | return $u; |
341 | 345 | } |
342 | 346 | } |
— | — | @@ -350,6 +354,7 @@ |
351 | 355 | $u = new User; |
352 | 356 | $u->mId = $id; |
353 | 357 | $u->mFrom = 'id'; |
| 358 | + $u->setItemLoaded( 'id' ); |
354 | 359 | return $u; |
355 | 360 | } |
356 | 361 | |
— | — | @@ -390,7 +395,14 @@ |
391 | 396 | |
392 | 397 | /** |
393 | 398 | * Create a new user object from a user row. |
394 | | - * The row should have all fields from the user table in it. |
| 399 | + * The row should have the following fields from the user table in it: |
| 400 | + * - either user_name or user_id to load further data if needed (or both) |
| 401 | + * - user_real_name |
| 402 | + * - all other fields (email, password, etc.) |
| 403 | + * It is useless to provide the remaining fields if either user_id, |
| 404 | + * user_name and user_real_name are not provided because the whole row |
| 405 | + * will be loaded once more from the database when accessing them. |
| 406 | + * |
395 | 407 | * @param $row Array A row from the user table |
396 | 408 | * @return User |
397 | 409 | */ |
— | — | @@ -847,6 +859,34 @@ |
848 | 860 | } |
849 | 861 | |
850 | 862 | /** |
| 863 | + * Return whether an item has been loaded. |
| 864 | + * |
| 865 | + * @param $item String: item to check. Current possibilities: |
| 866 | + * - id |
| 867 | + * - name |
| 868 | + * - realname |
| 869 | + * @param $all String: 'all' to check if the whole object has been loaded |
| 870 | + * or any other string to check if only the item is available (e.g. |
| 871 | + * for optimisation) |
| 872 | + * @return Boolean |
| 873 | + */ |
| 874 | + public function isItemLoaded( $item, $all = 'all' ) { |
| 875 | + return ( $this->mLoadedItems === true && $all === 'all' ) || |
| 876 | + ( isset( $this->mLoadedItems[$item] ) && $this->mLoadedItems[$item] === true ); |
| 877 | + } |
| 878 | + |
| 879 | + /** |
| 880 | + * Set that an item has been loaded |
| 881 | + * |
| 882 | + * @param $item String |
| 883 | + */ |
| 884 | + private function setItemLoaded( $item ) { |
| 885 | + if ( is_array( $this->mLoadedItems ) ) { |
| 886 | + $this->mLoadedItems[$item] = true; |
| 887 | + } |
| 888 | + } |
| 889 | + |
| 890 | + /** |
851 | 891 | * Load user data from the session or login cookie. If there are no valid |
852 | 892 | * credentials, initialises the user as an anonymous user. |
853 | 893 | * @return Bool True if the user is logged in, false otherwise. |
— | — | @@ -936,7 +976,7 @@ |
937 | 977 | |
938 | 978 | /** |
939 | 979 | * Load user and user_group data from the database. |
940 | | - * $this::mId must be set, this is how the user is identified. |
| 980 | + * $this->mId must be set, this is how the user is identified. |
941 | 981 | * |
942 | 982 | * @return Bool True if the user exists, false if the user is anonymous |
943 | 983 | * @private |
— | — | @@ -976,25 +1016,51 @@ |
977 | 1017 | * @param $row Array Row from the user table to load. |
978 | 1018 | */ |
979 | 1019 | function loadFromRow( $row ) { |
980 | | - $this->mDataLoaded = true; |
| 1020 | + $all = true; |
981 | 1021 | |
| 1022 | + if ( isset( $row->user_name ) ) { |
| 1023 | + $this->mName = $row->user_name; |
| 1024 | + $this->mFrom = 'name'; |
| 1025 | + $this->setItemLoaded( 'name' ); |
| 1026 | + } else { |
| 1027 | + $all = false; |
| 1028 | + } |
| 1029 | + |
| 1030 | + if ( isset( $row->user_name ) ) { |
| 1031 | + $this->mRealName = $row->user_real_name; |
| 1032 | + $this->setItemLoaded( 'realname' ); |
| 1033 | + } else { |
| 1034 | + $all = false; |
| 1035 | + } |
| 1036 | + |
982 | 1037 | if ( isset( $row->user_id ) ) { |
983 | 1038 | $this->mId = intval( $row->user_id ); |
| 1039 | + $this->mFrom = 'id'; |
| 1040 | + $this->setItemLoaded( 'id' ); |
| 1041 | + } else { |
| 1042 | + $all = false; |
984 | 1043 | } |
985 | | - $this->mName = $row->user_name; |
986 | | - $this->mRealName = $row->user_real_name; |
987 | | - $this->mPassword = $row->user_password; |
988 | | - $this->mNewpassword = $row->user_newpassword; |
989 | | - $this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time ); |
990 | | - $this->mEmail = $row->user_email; |
991 | | - $this->decodeOptions( $row->user_options ); |
992 | | - $this->mTouched = wfTimestamp(TS_MW,$row->user_touched); |
993 | | - $this->mToken = $row->user_token; |
994 | | - $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated ); |
995 | | - $this->mEmailToken = $row->user_email_token; |
996 | | - $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires ); |
997 | | - $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration ); |
998 | | - $this->mEditCount = $row->user_editcount; |
| 1044 | + |
| 1045 | + if ( isset( $row->user_password ) ) { |
| 1046 | + $this->mPassword = $row->user_password; |
| 1047 | + $this->mNewpassword = $row->user_newpassword; |
| 1048 | + $this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time ); |
| 1049 | + $this->mEmail = $row->user_email; |
| 1050 | + $this->decodeOptions( $row->user_options ); |
| 1051 | + $this->mTouched = wfTimestamp(TS_MW,$row->user_touched); |
| 1052 | + $this->mToken = $row->user_token; |
| 1053 | + $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated ); |
| 1054 | + $this->mEmailToken = $row->user_email_token; |
| 1055 | + $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires ); |
| 1056 | + $this->mRegistration = wfTimestampOrNull( TS_MW, $row->user_registration ); |
| 1057 | + $this->mEditCount = $row->user_editcount; |
| 1058 | + } else { |
| 1059 | + $all = false; |
| 1060 | + } |
| 1061 | + |
| 1062 | + if ( $all ) { |
| 1063 | + $this->mLoadedItems = true; |
| 1064 | + } |
999 | 1065 | } |
1000 | 1066 | |
1001 | 1067 | /** |
— | — | @@ -1032,7 +1098,7 @@ |
1033 | 1099 | $this->mOptions = null; |
1034 | 1100 | |
1035 | 1101 | if ( $reloadFrom ) { |
1036 | | - $this->mDataLoaded = false; |
| 1102 | + $this->mLoadedItems = array(); |
1037 | 1103 | $this->mFrom = $reloadFrom; |
1038 | 1104 | } |
1039 | 1105 | } |
— | — | @@ -1461,7 +1527,7 @@ |
1462 | 1528 | && User::isIP( $this->mName ) ) { |
1463 | 1529 | // Special case, we know the user is anonymous |
1464 | 1530 | return 0; |
1465 | | - } elseif( $this->mId === null ) { |
| 1531 | + } elseif( !$this->isItemLoaded( 'id' ) ) { |
1466 | 1532 | // Don't load if this was initialized from an ID |
1467 | 1533 | $this->load(); |
1468 | 1534 | } |
— | — | @@ -1482,7 +1548,7 @@ |
1483 | 1549 | * @return String User's name or IP address |
1484 | 1550 | */ |
1485 | 1551 | function getName() { |
1486 | | - if ( !$this->mDataLoaded && $this->mFrom == 'name' ) { |
| 1552 | + if ( $this->isItemLoaded( 'name', 'only' ) ) { |
1487 | 1553 | # Special case optimisation |
1488 | 1554 | return $this->mName; |
1489 | 1555 | } else { |
— | — | @@ -1910,7 +1976,10 @@ |
1911 | 1977 | * @return String User's real name |
1912 | 1978 | */ |
1913 | 1979 | function getRealName() { |
1914 | | - $this->load(); |
| 1980 | + if ( !$this->isItemLoaded( 'realname' ) ) { |
| 1981 | + $this->load(); |
| 1982 | + } |
| 1983 | + |
1915 | 1984 | return $this->mRealName; |
1916 | 1985 | } |
1917 | 1986 | |
— | — | @@ -2859,6 +2928,8 @@ |
2860 | 2929 | */ |
2861 | 2930 | function checkTemporaryPassword( $plaintext ) { |
2862 | 2931 | global $wgNewPasswordExpiry; |
| 2932 | + |
| 2933 | + $this->load(); |
2863 | 2934 | if( self::comparePasswords( $this->mNewpassword, $plaintext, $this->getId() ) ) { |
2864 | 2935 | if ( is_null( $this->mNewpassTime ) ) { |
2865 | 2936 | return true; |
— | — | @@ -3174,9 +3245,11 @@ |
3175 | 3246 | * non-existent/anonymous user accounts. |
3176 | 3247 | */ |
3177 | 3248 | public function getRegistration() { |
3178 | | - return $this->getId() > 0 |
3179 | | - ? $this->mRegistration |
3180 | | - : false; |
| 3249 | + if ( $this->isAnon() ) { |
| 3250 | + return false; |
| 3251 | + } |
| 3252 | + $this->load(); |
| 3253 | + return $this->mRegistration; |
3181 | 3254 | } |
3182 | 3255 | |
3183 | 3256 | /** |
Index: trunk/phase3/includes/Article.php |
— | — | @@ -836,11 +836,18 @@ |
837 | 837 | $dbr = wfGetDB( DB_SLAVE ); |
838 | 838 | $userTable = $dbr->tableName( 'user' ); |
839 | 839 | |
| 840 | + if ( $dbr->implicitGroupby() ) { |
| 841 | + $realNameField = 'user_real_name'; |
| 842 | + } else { |
| 843 | + $realNameField = 'FIRST(user_real_name) AS user_real_name'; |
| 844 | + } |
| 845 | + |
840 | 846 | $tables = array( 'revision', 'user' ); |
841 | 847 | |
842 | 848 | $fields = array( |
843 | | - "$userTable.*", |
| 849 | + 'rev_user as user_id', |
844 | 850 | 'rev_user_text AS user_name', |
| 851 | + $realNameField, |
845 | 852 | 'MAX(rev_timestamp) AS timestamp', |
846 | 853 | ); |
847 | 854 | |
Index: trunk/phase3/includes/GlobalFunctions.php |
— | — | @@ -389,9 +389,9 @@ |
390 | 390 | if ( $forward ) { |
391 | 391 | $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})"; |
392 | 392 | } |
393 | | - // Don't unstub $wgUser at this late stage just for statistics purposes |
| 393 | + // Don't load $wgUser at this late stage just for statistics purposes |
394 | 394 | // FIXME: We can detect some anons even if it is not loaded. See User::getId() |
395 | | - if ( $wgUser->mDataLoaded && $wgUser->isAnon() ) { |
| 395 | + if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) { |
396 | 396 | $forward .= ' anon'; |
397 | 397 | } |
398 | 398 | $log = sprintf( "%s\t%04.3f\t%s\n", |
Index: trunk/phase3/includes/db/Database.php |
— | — | @@ -668,7 +668,7 @@ |
669 | 669 | # Add a comment for easy SHOW PROCESSLIST interpretation |
670 | 670 | # if ( $fname ) { |
671 | 671 | global $wgUser; |
672 | | - if ( is_object( $wgUser ) && $wgUser->mDataLoaded ) { |
| 672 | + if ( is_object( $wgUser ) && $wgUser->isItemLoaded( 'name' ) ) { |
673 | 673 | $userName = $wgUser->getName(); |
674 | 674 | if ( mb_strlen( $userName ) > 15 ) { |
675 | 675 | $userName = mb_substr( $userName, 0, 15 ) . '...'; |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -254,6 +254,7 @@ |
255 | 255 | * (bug 27249) "Installed software" table in Special:Version should always be |
256 | 256 | left-to-right. |
257 | 257 | * (bug 28719) Do not call mLinkHolders __destruct explicitly |
| 258 | +* (bug 21196) Article::getContributors() no longer fail on PostgreSQL. |
258 | 259 | * (bug 28752) XCache doesn't work in CLI mode. |
259 | 260 | |
260 | 261 | === API changes in 1.18 === |