Index: branches/preferences-work/phase3/docs/hooks.txt |
— | — | @@ -1397,6 +1397,9 @@ |
1398 | 1398 | $ip: User's IP address |
1399 | 1399 | &$blocked: Whether the user is blocked, to be modified by the hook |
1400 | 1400 | |
| 1401 | +'UserLoadAfterLoadFromSession': called to authenticate users on external/environmental means; occurs after session is loaded |
| 1402 | +$user: user object being loaded |
| 1403 | + |
1401 | 1404 | 'UserLoadDefaults': called when loading a default user |
1402 | 1405 | $user: user object |
1403 | 1406 | $name: user name |
— | — | @@ -1409,8 +1412,9 @@ |
1410 | 1413 | $user: user object being loaded |
1411 | 1414 | &$result: set this to a boolean value to abort the normal authentification process |
1412 | 1415 | |
1413 | | -'UserLoadAfterLoadFromSession': called to authenticate users on external/environmental means; occurs after session is loaded |
1414 | | -$user: user object being loaded |
| 1416 | +'UserLoadOptions': when user options/preferences are being loaded from the database. |
| 1417 | +$user: User object |
| 1418 | +&$options: Options, can be modified. |
1415 | 1419 | |
1416 | 1420 | 'UserLoginComplete': after a user has logged in |
1417 | 1421 | $user: the user object that was created on login |
— | — | @@ -1450,6 +1454,10 @@ |
1451 | 1455 | 'UserSaveSettings': called when saving user settings |
1452 | 1456 | $user: User object |
1453 | 1457 | |
| 1458 | +'UserSaveOptions': Called just before saving user preferences/options. |
| 1459 | +$user: User object |
| 1460 | +&$options: Options, modifiable |
| 1461 | + |
1454 | 1462 | 'UserSetCookies': called when setting user cookies |
1455 | 1463 | $user: User object |
1456 | 1464 | &$session: session array, will be added to $_SESSION |
Index: branches/preferences-work/phase3/includes/User.php |
— | — | @@ -3391,6 +3391,8 @@ |
3392 | 3392 | $this->load(); |
3393 | 3393 | if ($this->mOptionsLoaded || !$this->getId() ) |
3394 | 3394 | return; |
| 3395 | + |
| 3396 | + $this->mOptions = self::getDefaultOptions(); |
3395 | 3397 | |
3396 | 3398 | // Load from database |
3397 | 3399 | $dbr = wfGetDB( DB_SLAVE ); |
— | — | @@ -3401,14 +3403,13 @@ |
3402 | 3404 | __METHOD__ |
3403 | 3405 | ); |
3404 | 3406 | |
3405 | | - global $wgDefaultUserOptions; |
3406 | | - $this->mOptions = self::getDefaultOptions(); |
3407 | | - |
3408 | 3407 | while( $row = $dbr->fetchObject( $res ) ) { |
3409 | 3408 | $this->mOptions[$row->up_property] = unserialize( $row->up_value ); |
3410 | 3409 | } |
3411 | 3410 | |
3412 | 3411 | $this->mOptionsLoaded = true; |
| 3412 | + |
| 3413 | + wfRunHooks( 'UserLoadOptions', array( $this, &$this->mOptions ) ); |
3413 | 3414 | } |
3414 | 3415 | |
3415 | 3416 | protected function saveOptions() { |
— | — | @@ -3417,7 +3418,14 @@ |
3418 | 3419 | |
3419 | 3420 | $insert_rows = array(); |
3420 | 3421 | |
3421 | | - foreach( $this->mOptions as $key => $value ) { |
| 3422 | + $saveOptions = $this->mOptions; |
| 3423 | + |
| 3424 | + // Allow hooks to abort, for instance to save to a global profile. |
| 3425 | + // Reset options to default state before saving. |
| 3426 | + if (!wfRunHooks( 'UserSaveOptions', array($this, &$saveOptions) ) ) |
| 3427 | + return; |
| 3428 | + |
| 3429 | + foreach( $saveOptions as $key => $value ) { |
3422 | 3430 | $ser = serialize($value); |
3423 | 3431 | |
3424 | 3432 | if ( is_null(self::getDefaultOption($key)) || |
Index: branches/preferences-work/phase3/includes/Preferences.php |
— | — | @@ -741,6 +741,8 @@ |
742 | 742 | 'label-message' => 'tog-uselivepreview', |
743 | 743 | ); |
744 | 744 | |
| 745 | + wfRunHooks( 'GetPreferences', array( $user, &$defaultPreferences ) ); |
| 746 | + |
745 | 747 | ## Prod in defaults from the user |
746 | 748 | global $wgDefaultUserOptions; |
747 | 749 | foreach( $defaultPreferences as $name => &$info ) { |
— | — | @@ -761,8 +763,6 @@ |
762 | 764 | $info['default'] = $globalDefault; |
763 | 765 | } |
764 | 766 | } |
765 | | - |
766 | | - wfRunHooks( 'GetPreferences', array( $user, &$defaultPreferences ) ); |
767 | 767 | |
768 | 768 | self::$defaultPreferences = $defaultPreferences; |
769 | 769 | |
— | — | @@ -997,9 +997,10 @@ |
998 | 998 | global $wgUser, $wgEmailAuthentication, $wgEnableEmail; |
999 | 999 | |
1000 | 1000 | // Filter input |
1001 | | - foreach( $formData as $name => &$value ) { |
| 1001 | + foreach( array_keys($formData) as $name ) { |
1002 | 1002 | if ( isset(self::$saveFilters[$name]) ) { |
1003 | | - $value = call_user_func( self::$saveFilters[$name], $value, $formData ); |
| 1003 | + $formData[$name] = |
| 1004 | + call_user_func( self::$saveFilters[$name], $formData[$name], $formData ); |
1004 | 1005 | } |
1005 | 1006 | } |
1006 | 1007 | |
— | — | @@ -1046,8 +1047,7 @@ |
1047 | 1048 | |
1048 | 1049 | foreach( $saveBlacklist as $b ) |
1049 | 1050 | unset( $formData[$b] ); |
1050 | | - |
1051 | | - // Reset options to default state before saving. |
| 1051 | + |
1052 | 1052 | // Keeps old preferences from interfering due to back-compat |
1053 | 1053 | // code, etc. |
1054 | 1054 | $wgUser->resetOptions(); |
Index: branches/preferences-work/extensions/CentralAuth/central-auth.sql |
— | — | @@ -164,3 +164,13 @@ |
165 | 165 | PRIMARY KEY (ggr_group), |
166 | 166 | KEY (ggr_set) |
167 | 167 | ) /*$wgDBTableOptions*/; |
| 168 | + |
| 169 | +-- Allow users to share preferences across wikis |
| 170 | +CREATE TABLE global_preferences ( |
| 171 | + gp_user int(11) NOT NULL, |
| 172 | + gp_key varbinary(255) NOT NULL, |
| 173 | + gp_value BLOB, |
| 174 | + |
| 175 | + PRIMARY KEY (up_user,up_property), |
| 176 | + KEY (up_property) |
| 177 | +) /*$wgDBTableOptions*/; |
Index: branches/preferences-work/extensions/CentralAuth/db_patches/patch-global_properties.sql |
— | — | @@ -0,0 +1,9 @@ |
| 2 | +-- Allow users to share preferences across wikis |
| 3 | +CREATE TABLE global_user_properties ( |
| 4 | + gp_user int(11) NOT NULL, |
| 5 | + gp_property varbinary(255) NOT NULL, |
| 6 | + gp_value BLOB, |
| 7 | + |
| 8 | + PRIMARY KEY (gp_user,gp_property), |
| 9 | + KEY (gp_property) |
| 10 | +) /*$wgDBTableOptions*/; |
Index: branches/preferences-work/extensions/CentralAuth/CentralAuthUser.php |
— | — | @@ -18,7 +18,7 @@ |
19 | 19 | */ |
20 | 20 | /*private*/ var $mName; |
21 | 21 | /*private*/ var $mStateDirty = false; |
22 | | - /*private*/ var $mVersion = 2; |
| 22 | + /*private*/ var $mVersion = 3; |
23 | 23 | /*private*/ var $mDelayInvalidation = 0; |
24 | 24 | |
25 | 25 | static $mCacheVars = array( |
— | — | @@ -33,6 +33,7 @@ |
34 | 34 | 'mAuthenticationTimestamp', |
35 | 35 | 'mGroups', |
36 | 36 | 'mRights', |
| 37 | + 'mProperties', |
37 | 38 | |
38 | 39 | # Store the string list instead of the array, to save memory, and |
39 | 40 | # avoid unserialize() overhead |
— | — | @@ -93,6 +94,7 @@ |
94 | 95 | unset( $this->mGroups ); |
95 | 96 | unset( $this->mAttachedArray ); |
96 | 97 | unset( $this->mAttachedList ); |
| 98 | + unset( $this->mProperties ); |
97 | 99 | } |
98 | 100 | |
99 | 101 | /** |
— | — | @@ -190,6 +192,87 @@ |
191 | 193 | $this->mRights = $rights; |
192 | 194 | $this->mGroups = array_keys($groups); |
193 | 195 | } |
| 196 | + |
| 197 | + /** |
| 198 | + * Load user properties from the database. |
| 199 | + */ |
| 200 | + protected function loadProperties() { |
| 201 | + if ( isset($this->mProperties) ) return; |
| 202 | + |
| 203 | + wfDebugLog( 'CentralAuth', "Loading properties for global user {$this->mName}" ); |
| 204 | + |
| 205 | + $dbr = self::getCentralDB(); |
| 206 | + |
| 207 | + $res = $dbr->select( |
| 208 | + 'global_user_properties', array( 'gp_property', 'gp_value' ), |
| 209 | + array( 'gp_user' => $this->getId() ), __METHOD__ ); |
| 210 | + |
| 211 | + $properties = array(); |
| 212 | + |
| 213 | + while( $row = $dbr->fetchObject( $res ) ) { |
| 214 | + $properties[$row->gp_property] = unserialize($row->gp_value); |
| 215 | + } |
| 216 | + |
| 217 | + $this->mProperties = $properties; |
| 218 | + } |
| 219 | + |
| 220 | + function saveProperties() { |
| 221 | + $this->loadProperties(); |
| 222 | + $dbw = self::getCentralDB(); |
| 223 | + |
| 224 | + $insert_rows = array(); |
| 225 | + |
| 226 | + foreach( $this->mProperties as $key => $value ) { |
| 227 | + if ( is_null(User::getDefaultOption($key)) || |
| 228 | + $value != User::getDefaultOption( $key ) ) { |
| 229 | + $ser = serialize($value); |
| 230 | + $insert_rows[] = array( |
| 231 | + 'gp_user' => $this->getId(), |
| 232 | + 'gp_property' => $key, |
| 233 | + 'gp_value' => $ser, |
| 234 | + ); |
| 235 | + } |
| 236 | + } |
| 237 | + |
| 238 | + $dbw->begin(); |
| 239 | + |
| 240 | + $dbw->delete( 'global_user_properties', |
| 241 | + array( 'gp_user' => $this->getId() ), |
| 242 | + __METHOD__ ); |
| 243 | + $dbw->insert( 'global_user_properties', $insert_rows, __METHOD__ ); |
| 244 | + |
| 245 | + $dbw->commit(); |
| 246 | + |
| 247 | + $this->invalidateCache(); |
| 248 | + } |
| 249 | + |
| 250 | + function setProperty( $key, $value ) { |
| 251 | + $this->loadProperties(); |
| 252 | + $this->mProperties[$key] = $value; |
| 253 | + $this->mStateDirty = true; |
| 254 | + } |
| 255 | + |
| 256 | + function setProperties( $properties, $method = 'replace' ) { |
| 257 | + $this->loadProperties(); |
| 258 | + if ($method == 'replace') { |
| 259 | + $this->mProperties = $properties; |
| 260 | + } elseif ($method == 'insert') { |
| 261 | + $this->mProperties = array_merge( $this->mProperties, $properties ); |
| 262 | + } |
| 263 | + |
| 264 | + $this->mStateDirty = true; |
| 265 | + } |
| 266 | + |
| 267 | + function getProperties() { |
| 268 | + $this->loadProperties(); |
| 269 | + return $this->mProperties; |
| 270 | + } |
| 271 | + |
| 272 | + function getProperty( $name ) { |
| 273 | + $properties = $this->getProperties(); |
| 274 | + |
| 275 | + return $properties[$name]; |
| 276 | + } |
194 | 277 | |
195 | 278 | /** |
196 | 279 | * Load user state from a joined globaluser/localuser row |
— | — | @@ -266,6 +349,7 @@ |
267 | 350 | $this->loadState(); |
268 | 351 | $this->loadAttached(); |
269 | 352 | $this->loadGroups(); |
| 353 | + $this->loadProperties(); |
270 | 354 | |
271 | 355 | $obj = array(); |
272 | 356 | foreach( self::$mCacheVars as $var ) { |
Index: branches/preferences-work/extensions/CentralAuth/CentralAuth.php |
— | — | @@ -172,6 +172,8 @@ |
173 | 173 | $wgHooks['UserLoadDefaults'][] = 'CentralAuthHooks::onUserLoadDefaults'; |
174 | 174 | $wgHooks['getUserPermissionsErrorsExpensive'][] = 'CentralAuthHooks::onGetUserPermissionsErrorsExpensive'; |
175 | 175 | $wgHooks['MakeGlobalVariablesScript'][] = 'CentralAuthHooks::onMakeGlobalVariablesScript'; |
| 176 | +$wgHooks['UserSaveOptions'][] = 'CentralAuthHooks::onSavePreferences'; |
| 177 | +$wgHooks['UserLoadOptions'][] = 'CentralAuthHooks::onUserLoadOptions'; |
176 | 178 | |
177 | 179 | // For interaction with the Special:Renameuser extension |
178 | 180 | $wgHooks['RenameUserWarning'][] = 'CentralAuthHooks::onRenameUserWarning'; |
— | — | @@ -219,6 +221,8 @@ |
220 | 222 | $wgLogActions['gblrights/groupprms3'] = 'centralauth-rightslog-entry-groupperms3'; |
221 | 223 | foreach( array( 'newset', 'setrename', 'setnewtype', 'setchange' ) as $type ) |
222 | 224 | $wgLogActionsHandlers["gblrights/{$type}"] = 'efHandleWikiSetLogEntry'; |
| 225 | + |
| 226 | +$wgDefaultUserOptions['globalpreferences'] = true; |
223 | 227 | |
224 | 228 | function efHandleWikiSetLogEntry( $type, $action, $title, $skin, $params, $filterWikilinks = false ) { |
225 | 229 | wfLoadExtensionMessages('SpecialCentralAuth'); |
Index: branches/preferences-work/extensions/CentralAuth/CentralAuthHooks.php |
— | — | @@ -83,6 +83,13 @@ |
84 | 84 | ); |
85 | 85 | |
86 | 86 | $preferences = array_insert_after( $preferences, $prefInsert, 'registrationdate' ); |
| 87 | + |
| 88 | + $preferences['globalpreferences'] = |
| 89 | + array( |
| 90 | + 'section' => 'personal', |
| 91 | + 'label-message' => 'centralauth-prefs-global', |
| 92 | + 'type' => 'toggle', |
| 93 | + ); |
87 | 94 | |
88 | 95 | return true; |
89 | 96 | } |
— | — | @@ -555,4 +562,36 @@ |
556 | 563 | } |
557 | 564 | return true; |
558 | 565 | } |
| 566 | + |
| 567 | + static function onSavePreferences( $user, &$preferences ) { |
| 568 | + $centralUser = CentralAuthUser::getInstance( $user ); |
| 569 | + |
| 570 | + if ($centralUser->exists() && $centralUser->isAttached() && |
| 571 | + !empty($preferences['globalpreferences']) ) { |
| 572 | + // Save preferences into the global account. |
| 573 | + |
| 574 | + $centralUser->setProperties( $preferences, 'replace' ); |
| 575 | + $centralUser->saveProperties(); |
| 576 | + |
| 577 | + $preferences = array( 'globalpreferences' => true ); |
| 578 | + |
| 579 | + return true; |
| 580 | + } |
| 581 | + |
| 582 | + return true; |
| 583 | + } |
| 584 | + |
| 585 | + static function onUserLoadOptions( $user, &$preferences ) { |
| 586 | + $centralUser = CentralAuthUser::getInstance( $user ); |
| 587 | + |
| 588 | + if ($centralUser->exists() && $centralUser->isAttached() && |
| 589 | + !empty($preferences['globalpreferences']) ) { |
| 590 | + // Pull preferences from global account |
| 591 | + |
| 592 | + $preferences = array_merge( $preferences, $centralUser->getProperties() ); |
| 593 | + $preferences['globalpreferences'] = true; |
| 594 | + } |
| 595 | + |
| 596 | + return true; |
| 597 | + } |
559 | 598 | } |
Index: branches/preferences-work/extensions/CentralAuth/CentralAuth.i18n.php |
— | — | @@ -195,6 +195,7 @@ |
196 | 196 | 'centralauth-prefs-count-unattached' => 'Unconfirmed accounts with your name remain on $1 {{PLURAL:$1|project|projects}}.', |
197 | 197 | 'centralauth-prefs-detail-unattached' => 'This project site has not been confirmed as belonging to the global account.', |
198 | 198 | 'centralauth-prefs-manage' => 'Manage your global account', |
| 199 | + 'centralauth-prefs-global' => 'Use these preferences on all projects', |
199 | 200 | |
200 | 201 | // Interaction with Special:Renameuser |
201 | 202 | 'centralauth-renameuser-merged' => "User $1 has been migrated to the unified login system. |