From cb6b72cef7f349c97ff031ba2873bacd05e7e91a Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Sat, 25 Jun 2011 02:52:30 +0000 Subject: [PATCH] Added one-time promote support via Autopromote::autopromoteOnceHook function. This is still a bit rough on the edges. This uses a hook since extension may want to control when it's called for performance reasons. Patch by lampak. (for bug 24948) --- includes/Autopromote.php | 70 +++++++++++++++++++ includes/DefaultSettings.php | 6 ++ includes/User.php | 67 ++++++++++++++++-- includes/installer/MysqlUpdater.php | 1 + includes/installer/SqliteUpdater.php | 1 + .../archives/patch-user_former_groups.sql | 10 +++ .../archives/patch-user_former_groups.sql | 5 ++ maintenance/postgres/tables.sql | 6 ++ maintenance/tables.sql | 10 +++ 9 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 maintenance/archives/patch-user_former_groups.sql create mode 100644 maintenance/postgres/archives/patch-user_former_groups.sql diff --git a/includes/Autopromote.php b/includes/Autopromote.php index e630db95b2..d9c1f5fba1 100644 --- a/includes/Autopromote.php +++ b/includes/Autopromote.php @@ -5,6 +5,41 @@ */ class Autopromote { + /** + * A function which may be assigned to a hook in order to check + * autopromotion of the current user (\ref $wgUser) to the specified + * group. + * + * Contrary to autopromotion by \ref $wgAutopromote, the group will be + * possible to remove manually via Special:UserRights. In such case it + * will not be re-added autmoatically. The user will also not lose the + * group if they no longer meet the criteria. + * + * Example configuration: + * \code $wgHooks['ArticleSaveComplete'][] = array ( + * 'Autopromote::autopromoteOnceHook', + * array( 'somegroup' => array(APCOND_EDITCOUNT, 200) ) + * ); \endcode + * + * The second array should be of the same format as \ref $wgAutopromote. + * + * This funciton simply runs User::autopromoteOnce() on $wgUser. You may + * run this method from your custom function if you wish. + * + * @param $criteria array Groups and conditions which must be met in order to + * aquire these groups. Array of the same format as \ref $wgAutopromote. + * + * @return Always true. + * + * @see User::autopromoteOnce() + * @see $wgAutopromote + */ + public static function autopromoteOnceHook($criteria) { + global $wgUser; + $wgUser->autopromoteOnce($criteria); + return true; + } + /** * Get the groups for the given user based on $wgAutopromote. * @@ -26,6 +61,41 @@ class Autopromote { return $promote; } + + /** + * Get the groups for the given user based on the given criteria. + * + * Does not return groups the user already belongs to or has once belonged. + * + * @param $user The user to get the groups for + * @param $criteria array Groups and conditions the user must meet in order + * to be promoted to these groups. Array of the same format as + * \ref $wgAutopromote. + * + * @return array Groups the user should be promoted to. + */ + public static function getAutopromoteOnceGroups( User $user, $criteria ) { + $promote = array(); + + //get the current groups + $currentGroups = $user->getGroups(); + + foreach( $criteria as $group => $cond ) { + //do not check if the user's already a member + if ( in_array($group, $currentGroups)) + continue; + + //do not autopromote if the user has belonged to the group + $formerGroups = $user->getFormerGroups(); + if ( in_array($group, $formerGroups) ) + continue; + + //finally - check the conditions + if ( self::recCheckCondition($cond, $user) ) + $promote[] = $group; + } + return $promote; + } /** * Recursively check a condition. Conditions are in the form diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index a4c7cb2281..ad7013b85a 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -3513,6 +3513,12 @@ $wgAutoConfirmCount = 0; * * If $wgEmailAuthentication is off, APCOND_EMAILCONFIRMED will be true for any * user who has provided an e-mail address. + * + * If the groups should be removable, consider using + * Autopromote::autopromoteOnceHook() instead. + * + * @see Autopromote::autopromoteOnceHook() + * @see User::autopromoteOnce() */ $wgAutopromote = array( 'autoconfirmed' => array( '&', diff --git a/includes/User.php b/includes/User.php index 23938d614a..c2742a235c 100644 --- a/includes/User.php +++ b/includes/User.php @@ -71,7 +71,7 @@ class User { 'mEmailTokenExpires', 'mRegistration', 'mEditCount', - // user_group table + // user_groups table 'mGroups', // user_properties table 'mOptionOverrides', @@ -182,7 +182,7 @@ class User { * Lazy-initialized variables, invalidated with clearInstanceCache */ var $mNewtalk, $mDatePreference, $mBlockedby, $mHash, $mRights, - $mBlockreason, $mEffectiveGroups, $mBlockedGlobally, + $mBlockreason, $mEffectiveGroups, $mFormerGroups, $mBlockedGlobally, $mLocked, $mHideName, $mOptions; /** @@ -1102,6 +1102,33 @@ class User { } } + /** + * Add the user to the group if he/she meets given criteria. + * + * Contrary to autopromotion by \ref $wgAutopromote, the group will be + * possible to remove manually via Special:UserRights. In such case it + * will not be re-added autmoatically. The user will also not lose the + * group if they no longer meet the criteria. + * + * @param $criteria array Groups and conditions the user must meet in order + * to be promoted to these groups. Array of the same format as + * \ref $wgAutopromote. + * + * @return array Array of groups the user has been promoted to. + * + * @see $wgAutopromote + * @see Autopromote::autopromoteOnceHook() + */ + public function autopromoteOnce( $criteria ) { + if ($this->getId()) { + $toPromote = Autopromote::getAutopromoteOnceGroups($this, $criteria); + foreach($toPromote as $group) + $this->addGroup($group); + return $toPromote; + } + return array(); + } + /** * Clear various cached data stored in this object. * @param $reloadFrom String Reload user and user_groups table data from a @@ -1116,7 +1143,7 @@ class User { $this->mSkin = null; $this->mRights = null; $this->mEffectiveGroups = null; - $this->mOptions = null; + $this->mOptions = null; if ( $reloadFrom ) { $this->mLoadedItems = array(); @@ -2240,7 +2267,31 @@ class User { } return $this->mEffectiveGroups; } - + + /** + * Returns the groups the user has belonged to. + * + * The user may still belong to the returned groups. Compare with getGroups(). + * + * The function will not return groups the user had belonged to before MW 1.17 + * + * @return array Names of the groups the user has belonged to. + */ + function getFormerGroups() { + if(is_null($this->mFormerGroups)) { + $dbr = wfGetDB( DB_MASTER ); + $res = $dbr->select( 'user_former_groups', + array( 'ufg_group' ), + array( 'ufg_user' => $this->mId ), + __METHOD__ ); + $this->mFormerGroups = array(); + while( $row = $dbr->fetchObject( $res ) ) { + $this->mFormerGroups[] = $row->ufg_group; + } + } + return $this->mFormerGroups; + } + /** * Get the user's edit count. * @return Int @@ -2297,6 +2348,14 @@ class User { 'ug_user' => $this->getID(), 'ug_group' => $group, ), __METHOD__ ); + //remember that the user has had this group + $dbw->insert( 'user_former_groups', + array( + 'ufg_user' => $this->getID(), + 'ufg_group' => $group, + ), + __METHOD__, + array( 'IGNORE' ) ); } $this->loadGroups(); $this->mGroups = array_diff( $this->mGroups, array( $group ) ); diff --git a/includes/installer/MysqlUpdater.php b/includes/installer/MysqlUpdater.php index b4e9ac5060..eb8a73d152 100644 --- a/includes/installer/MysqlUpdater.php +++ b/includes/installer/MysqlUpdater.php @@ -162,6 +162,7 @@ class MysqlUpdater extends DatabaseUpdater { // 1.17 array( 'addTable', 'iwlinks', 'patch-iwlinks.sql' ), + array( 'addTable', 'user_former_groups', 'patch-user_former_groups.sql'), array( 'addIndex', 'iwlinks', 'iwl_prefix_title_from', 'patch-rename-iwl_prefix.sql' ), array( 'addField', 'updatelog', 'ul_value', 'patch-ul_value.sql' ), array( 'addField', 'interwiki', 'iw_api', 'patch-iw_api_and_wikiid.sql' ), diff --git a/includes/installer/SqliteUpdater.php b/includes/installer/SqliteUpdater.php index cd7f717e77..b3a8a212cf 100644 --- a/includes/installer/SqliteUpdater.php +++ b/includes/installer/SqliteUpdater.php @@ -43,6 +43,7 @@ class SqliteUpdater extends DatabaseUpdater { // 1.17 array( 'addTable', 'iwlinks', 'patch-iwlinks.sql' ), + array( 'addTable', 'user_former_groups', 'patch-user_former_groups.sql'), array( 'addIndex', 'iwlinks', 'iwl_prefix_title_from', 'patch-rename-iwl_prefix.sql' ), array( 'addField', 'updatelog', 'ul_value', 'patch-ul_value.sql' ), array( 'addField', 'interwiki', 'iw_api', 'patch-iw_api_and_wikiid.sql' ), diff --git a/maintenance/archives/patch-user_former_groups.sql b/maintenance/archives/patch-user_former_groups.sql new file mode 100644 index 0000000000..fdaaf8f2de --- /dev/null +++ b/maintenance/archives/patch-user_former_groups.sql @@ -0,0 +1,10 @@ +-- Stores the groups the user has once belonged to. +-- The user may still belong these groups. Check user_groups. +CREATE TABLE /*_*/user_former_groups ( + -- Key to user_id + ufg_user int unsigned NOT NULL default 0, + ufg_group varbinary(16) NOT NULL default '', + + PRIMARY KEY (ufg_user,ufg_group), + KEY (ufg_group) +) /*$wgDBTableOptions*/; diff --git a/maintenance/postgres/archives/patch-user_former_groups.sql b/maintenance/postgres/archives/patch-user_former_groups.sql new file mode 100644 index 0000000000..9317a202e3 --- /dev/null +++ b/maintenance/postgres/archives/patch-user_former_groups.sql @@ -0,0 +1,5 @@ +CREATE TABLE user_former_groups ( + ufg_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + ufg_group TEXT NOT NULL +); +CREATE UNIQUE INDEX user_former_groups_unique ON user_former_groups (ufg_user, ufg_group); diff --git a/maintenance/postgres/tables.sql b/maintenance/postgres/tables.sql index 3482039abc..c942855ac8 100644 --- a/maintenance/postgres/tables.sql +++ b/maintenance/postgres/tables.sql @@ -54,6 +54,12 @@ CREATE TABLE user_groups ( ); CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group); +CREATE TABLE user_former_groups ( + ufg_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + ufg_group TEXT NOT NULL +); +CREATE UNIQUE INDEX user_former_groups_unique ON user_former_groups (ufg_user, ufg_group); + CREATE TABLE user_newtalk ( user_id INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, user_ip TEXT NULL, diff --git a/maintenance/tables.sql b/maintenance/tables.sql index cee01d5412..4bfa703a84 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -164,6 +164,16 @@ CREATE TABLE /*_*/user_groups ( CREATE UNIQUE INDEX /*i*/ug_user_group ON /*_*/user_groups (ug_user,ug_group); CREATE INDEX /*i*/ug_group ON /*_*/user_groups (ug_group); +-- Stores the groups the user has once belonged to. +-- The user may still belong these groups. Check user_groups. +CREATE TABLE /*_*/user_former_groups ( + -- Key to user_id + ufg_user int unsigned NOT NULL default 0, + ufg_group varbinary(16) NOT NULL default '' +) /*$wgDBTableOptions*/; + +CREATE UNIQUE INDEX /*i*/ufg_user_group ON /*_*/user_former_groups (ufg_user,ufg_group); +CREATE INDEX /*i*/ufg_group ON /*_*/user_former_groups (ufg_group); -- -- Stores notifications of user talk page changes, for the display -- 2.20.1