r41588 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r41587‎ | r41588 | r41589 >
Date:05:17, 3 October 2008
Author:werdna
Status:old
Tags:
Comment:
Apply changes to group rights branch
Modified paths:
  • /branches/group_rights/includes/AutoLoader.php (modified) (history)
  • /branches/group_rights/includes/DefaultSettings.php (modified) (history)
  • /branches/group_rights/includes/GlobalFunctions.php (modified) (history)
  • /branches/group_rights/includes/LogPage.php (modified) (history)
  • /branches/group_rights/includes/ProtectionForm.php (modified) (history)
  • /branches/group_rights/includes/RightsManager.php (added) (history)
  • /branches/group_rights/includes/SpecialPage.php (modified) (history)
  • /branches/group_rights/includes/User.php (modified) (history)
  • /branches/group_rights/includes/api/ApiQuerySiteinfo.php (modified) (history)
  • /branches/group_rights/includes/specials/SpecialGroupRights.php (added) (history)
  • /branches/group_rights/includes/specials/SpecialListgrouprights.php (modified) (history)
  • /branches/group_rights/includes/specials/SpecialUserrights.php (modified) (history)
  • /branches/group_rights/languages/messages/MessagesEn.php (modified) (history)
  • /branches/group_rights/maintenance/archives/patch-group_rights.sql (added) (history)
  • /branches/group_rights/maintenance/tables.sql (modified) (history)
  • /branches/group_rights/maintenance/updaters.inc (modified) (history)

Diff [purge]

Index: branches/group_rights/maintenance/archives/patch-group_rights.sql
@@ -0,0 +1,19 @@
 2+--- Add table for group rights assignable on-wiki
 3+
 4+CREATE TABLE /*$wgDBprefix*/group_rights (
 5+ gr_group varbinary(16) NOT NULL default '',
 6+ gr_right varbinary(16) NOT NULL default '',
 7+ gr_enabled tinyint(1) NOT NULL default 1,
 8+ PRIMARY KEY (gr_group,gr_right),
 9+ KEY gr_right (gr_right)
 10+) /*$wgDBoptions*/;
 11+--- Add table for group rights assignable on-wiki
 12+
 13+CREATE TABLE /*$wgDBprefix*/changeable_groups (
 14+ cg_changer varbinary(16) NOT NULL,
 15+ cg_group varbinary(16) NOT NULL,
 16+ cg_action varbinary(16) default NULL,
 17+ UNIQUE KEY cg_changer (cg_changer,cg_group,cg_action),
 18+ KEY cg_group (cg_group)
 19+) /*$wgDBoptions*/;
 20+--- Add table for group rights assignable on-wiki
\ No newline at end of file
Property changes on: branches/group_rights/maintenance/archives/patch-group_rights.sql
___________________________________________________________________
Added: svn:eol-style
121 + native
Index: branches/group_rights/maintenance/updaters.inc
@@ -145,6 +145,8 @@
146146 array( 'update_password_format' ),
147147
148148 // 1.14
 149+ array( 'add_table', 'group_rights', 'patch-group_rights.sql' ),
 150+ array( 'add_table', 'changeable_groups', 'patch-group_rights.sql' ),
149151 array( 'add_field', 'site_stats', 'ss_active_users', 'patch-ss_active_users.sql' ),
150152 array( 'do_active_users_init' ),
151153 array( 'add_field', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' )
Index: branches/group_rights/maintenance/tables.sql
@@ -1243,4 +1243,23 @@
12441244 PRIMARY KEY (ul_key)
12451245 ) /*$wgDBTableOptions*/;
12461246
 1247+--- Add table for group rights assignable on-wiki
 1248+
 1249+CREATE TABLE /*$wgDBprefix*/group_rights (
 1250+ gr_group varbinary(16) NOT NULL default '',
 1251+ gr_right varbinary(16) NOT NULL default '',
 1252+ gr_enabled tinyint(1) NOT NULL default 1,
 1253+ PRIMARY KEY (gr_group,gr_right),
 1254+ KEY gr_right (gr_right)
 1255+) /*$wgDBoptions*/;
 1256+
 1257+CREATE TABLE /*$wgDBprefix*/changeable_groups (
 1258+ cg_changer varbinary(16) NOT NULL,
 1259+ cg_group varbinary(16) NOT NULL,
 1260+ cg_action varbinary(16) default NULL,
 1261+ UNIQUE KEY cg_changer (cg_changer,cg_group,cg_action),
 1262+ KEY cg_group (cg_group)
 1263+) /*$wgDBoptions*/;
 1264+
 1265+
12471266 -- vim: sw=2 sts=2 et
Index: branches/group_rights/includes/ProtectionForm.php
@@ -254,12 +254,12 @@
255255
256256 # They shouldn't be able to do this anyway, but just to make sure, ensure that cascading restrictions aren't being applied
257257 # to a semi-protected page.
258 - global $wgGroupPermissions;
 258+ $groupPerms = User::getAllGroupPermissions();
259259
260260 $edit_restriction = $this->mRestrictions['edit'];
261261 $this->mCascade = $wgRequest->getBool( 'mwProtect-cascade' );
262262 if ($this->mCascade && ($edit_restriction != 'protect') &&
263 - !(isset($wgGroupPermissions[$edit_restriction]['protect']) && $wgGroupPermissions[$edit_restriction]['protect'] ) )
 263+ !(isset($groupPerms[$edit_restriction]['protect']) && $groupPerms[$edit_restriction]['protect'] ) )
264264 $this->mCascade = false;
265265
266266 if ($this->mTitle->exists()) {
@@ -513,11 +513,12 @@
514514 }
515515
516516 function buildCleanupScript() {
517 - global $wgRestrictionLevels, $wgGroupPermissions;
 517+ global $wgRestrictionLevels;
518518 $script = 'var wgCascadeableLevels=';
519519 $CascadeableLevels = array();
 520+ $groupPerms = User::getAllGroupPermissions();
520521 foreach( $wgRestrictionLevels as $key ) {
521 - if ( (isset($wgGroupPermissions[$key]['protect']) && $wgGroupPermissions[$key]['protect']) || $key == 'protect' ) {
 522+ if ( (isset($groupPerms[$key]['protect']) && $groupPerms[$key]['protect']) || $key == 'protect' ) {
522523 $CascadeableLevels[] = "'" . Xml::escapeJsString( $key ) . "'";
523524 }
524525 }
Index: branches/group_rights/includes/User.php
@@ -1936,7 +1936,7 @@
19371937 function getRights() {
19381938 if ( is_null( $this->mRights ) ) {
19391939 $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
1940 - wfRunHooks( 'UserGetRights', array( $this, &$this->mRights ) );
 1940+ #wfRunHooks( 'UserGetRights', array( $this, &$this->mRights ) );
19411941 // Force reindexation of rights when a hook has unset one of them
19421942 $this->mRights = array_values( $this->mRights );
19431943 }
@@ -2987,32 +2987,30 @@
29882988 * @return \type{\arrayof{\string}} List of permission key names for given groups combined
29892989 */
29902990 static function getGroupPermissions( $groups ) {
2991 - global $wgGroupPermissions;
2992 - $rights = array();
2993 - foreach( $groups as $group ) {
2994 - if( isset( $wgGroupPermissions[$group] ) ) {
2995 - $rights = array_merge( $rights,
2996 - array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
2997 - }
2998 - }
2999 - return $rights;
 2991+ $rm = new RightsManagerMulti;
 2992+
 2993+ return $rm->getGroupPermissions($groups);
30002994 }
30012995
30022996 /**
 2997+ * Get a list of all group permissions.
 2998+ */
 2999+ static function getAllGroupPermissions() {
 3000+ $rm = new RightsManagerMulti;
 3001+
 3002+ return $rm->getAllGroupPermissions();
 3003+ }
 3004+
 3005+ /**
30033006 * Get all the groups who have a given permission
30043007 *
30053008 * @param $role \type{\string} Role to check
30063009 * @return \type{\arrayof{\string}} List of internal group names with the given permission
30073010 */
30083011 static function getGroupsWithPermission( $role ) {
3009 - global $wgGroupPermissions;
3010 - $allowedGroups = array();
3011 - foreach ( $wgGroupPermissions as $group => $rights ) {
3012 - if ( isset( $rights[$role] ) && $rights[$role] ) {
3013 - $allowedGroups[] = $group;
3014 - }
3015 - }
3016 - return $allowedGroups;
 3012+ $rm = new RightsManagerMulti;
 3013+
 3014+ return $rm->getGroupsWithPermission( $role );
30173015 }
30183016
30193017 /**
@@ -3054,9 +3052,12 @@
30553053 * @return \type{\arrayof{\string}} Array of internal group names
30563054 */
30573055 static function getAllGroups() {
3058 - global $wgGroupPermissions;
 3056+ $rm = new RightsManagerMulti;
 3057+
 3058+ $groups = $rm->getAllGroups();
 3059+
30593060 return array_diff(
3060 - array_keys( $wgGroupPermissions ),
 3061+ $groups,
30613062 self::getImplicitGroups()
30623063 );
30633064 }
Index: branches/group_rights/includes/GlobalFunctions.php
@@ -1377,6 +1377,9 @@
13781378 if (strpos( $diff_lines[1], '+++' ) === 0) {
13791379 unset($diff_lines[1]);
13801380 }
 1381+ if (strpos( $diff_lines[count($diff_lines)-1], "\\" ) == 0) {
 1382+ unset( $diff_lines[count($diff_lines-1] );
 1383+ }
13811384
13821385 $diff = implode( "\n", $diff_lines );
13831386
Index: branches/group_rights/includes/api/ApiQuerySiteinfo.php
@@ -245,9 +245,9 @@
246246 }
247247
248248 protected function appendUserGroups( $property ) {
249 - global $wgGroupPermissions;
 249+ $groupPerms = User::getAllGroupPermissions();
250250 $data = array();
251 - foreach( $wgGroupPermissions as $group => $permissions ) {
 251+ foreach( $groupPerms as $group => $permissions ) {
252252 $arr = array( 'name' => $group, 'rights' => array_keys( $permissions, true ) );
253253 $this->getResult()->setIndexedTagName( $arr['rights'], 'permission' );
254254 $data[] = $arr;
Index: branches/group_rights/includes/AutoLoader.php
@@ -153,6 +153,11 @@
154154 'Replacer' => 'includes/StringUtils.php',
155155 'ReverseChronologicalPager' => 'includes/Pager.php',
156156 'Revision' => 'includes/Revision.php',
 157+ 'RightsManager' => 'includes/RightsManager.php',
 158+ 'RightsManagerConfigDB' => 'includes/RightsManager.php',
 159+ 'RightsManagerForeignConfigDB' => 'includes/RightsManager.php',
 160+ 'RightsManagerMulti' => 'includes/RightsManager.php',
 161+ 'RightsManagerReadOnly' => 'includes/RightsManager.php',
157162 'RSSFeed' => 'includes/Feed.php',
158163 'Sanitizer' => 'includes/Sanitizer.php',
159164 'SearchEngineDummy' => 'includes/SearchEngine.php',
@@ -474,6 +479,7 @@
475480 'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
476481 'SpecialAllpages' => 'includes/specials/SpecialAllpages.php',
477482 'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
 483+ 'SpecialGroupRights' => 'includes/specials/SpecialGroupRights.php',
478484 'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
479485 'SpecialMostlinkedtemplates' => 'includes/specials/SpecialMostlinkedtemplates.php',
480486 'SpecialPrefixindex' => 'includes/specials/SpecialPrefixindex.php',
Index: branches/group_rights/includes/DefaultSettings.php
@@ -2736,6 +2736,8 @@
27372737 'protect/unprotect' => 'unprotectedarticle',
27382738 'protect/move_prot' => 'movedarticleprotection',
27392739 'rights/rights' => 'rightslogentry',
 2740+ 'rights/rights2' => 'rightslogentry2',
 2741+ 'rights/grprights' => 'grouprightslog',
27402742 'delete/delete' => 'deletedarticle',
27412743 'delete/restore' => 'undeletedarticle',
27422744 'delete/revision' => 'revdelete-logentry',
@@ -2813,6 +2815,7 @@
28142816
28152817 'Listusers' => 'users',
28162818 'Listgrouprights' => 'users',
 2819+ 'GroupRights' => 'users',
28172820 'Ipblocklist' => 'users',
28182821 'Contributions' => 'users',
28192822 'Emailuser' => 'users',
@@ -3449,3 +3452,16 @@
34503453 * Requires memcached.
34513454 */
34523455 $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
 3456+
 3457+/**
 3458+ * Whether or not to allow rights changes made through Special:GroupRights to revoke
 3459+ * rights issued in the site configuration
 3460+ */
 3461+$wgAllowDBRightSubtraction = true;
 3462+
 3463+/**
 3464+ * The rights managers which are allowed to give users groups and permissions.
 3465+ * An array of class names. Generally not modified by users, but by extensions
 3466+ * and the like.
 3467+ */
 3468+$wgRightsManagers = array( 'RightsManagerConfigDB' );
\ No newline at end of file
Index: branches/group_rights/includes/specials/SpecialUserrights.php
@@ -26,13 +26,54 @@
2727 }
2828
2929 public function userCanExecute( $user ) {
30 - $available = $this->changeableGroups();
31 - return !empty( $available['add'] )
32 - or !empty( $available['remove'] )
33 - or ($this->isself and
34 - (!empty( $available['add-self'] )
35 - or !empty( $available['remove-self'] )));
 30+ return (bool)count( $this->getAvailableBackends( $user ) );
3631 }
 32+
 33+ function getAvailableBackends( $user ) {
 34+ global $wgRightsManagers;
 35+
 36+ $availableBackends = array();
 37+
 38+ foreach( $wgRightsManagers as $rmClass ) {
 39+ $rm = new $rmClass;
 40+
 41+ $changeableGroups = $rm->getChangeableGroupsForUser( $user );
 42+
 43+ $counts = array_map( 'count', $changeableGroups );
 44+ $enabled = array_filter( $counts );
 45+
 46+ if ( count( $enabled ) ) {
 47+ $availableBackends[] = $rmClass;
 48+ }
 49+ }
 50+
 51+ return $availableBackends;
 52+ }
 53+
 54+ function showBackendSelector() {
 55+ global $wgUser, $wgOut;
 56+
 57+ $wgOut->setSubTitle( wfMsg( 'userrights-backendselect-subtitle' ) );
 58+ $wgOut->addWikiMsg( 'userrights-backendselect-text' );
 59+
 60+ // Produce list.
 61+ $sk = $wgUser->getSkin();
 62+ $availableBackends = $this->getAvailableBackends( $wgUser );
 63+ $list = '';
 64+ foreach( $availableBackends as $backend ) {
 65+ $text = wfMsg( "rights-backend-$backend" );
 66+ $link = $sk->link( $this->getTitle(), $text, array(), array( 'backend' => $backend ) );
 67+ $list .= Xml::tags( 'li', null, $link );
 68+ }
 69+
 70+ $list = Xml::tags( 'ul', null, $list );
 71+
 72+ $wgOut->addHTML( $list );
 73+ }
 74+
 75+ function getBackend() {
 76+ return new $this->mBackend;
 77+ }
3778
3879 /**
3980 * Manage forms to be shown according to posted data.
@@ -41,15 +82,39 @@
4283 * @param $par Mixed: string if any subpage provided, else null
4384 */
4485 function execute( $par ) {
45 - // If the visitor doesn't have permissions to assign or remove
46 - // any groups, it's a bit silly to give them the user search prompt.
47 - global $wgUser, $wgRequest;
 86+ global $wgUser, $wgRequest, $wgOut;
 87+
 88+ $wgOut->setPageTitle( wfMsg('userrights') );
4889
4990 if( $par ) {
5091 $this->mTarget = $par;
5192 } else {
5293 $this->mTarget = $wgRequest->getVal( 'user' );
5394 }
 95+
 96+ if( !$this->userCanExecute( $wgUser ) ) {
 97+ // fixme... there may be intermediate groups we can mention.
 98+ global $wgOut;
 99+ $wgOut->showPermissionsErrorPage( array(
 100+ $wgUser->isAnon()
 101+ ? 'userrights-nologin'
 102+ : 'userrights-notallowed' ) );
 103+ return;
 104+ }
 105+
 106+ $specifiedBackend = $this->mBackend = $wgRequest->getVal( 'backend' );
 107+
 108+ $availableBackends = $this->getAvailableBackends( $wgUser );
 109+
 110+ // Check backend.
 111+ if ( count($availableBackends) == 1 ) {
 112+ $this->mBackend = $availableBackends[0];
 113+ } elseif ( in_array( $specifiedBackend, $availableBackends ) ) {
 114+ $this->mBackend = $specifiedBackend;
 115+ } else {
 116+ $this->showBackendSelector();
 117+ return;
 118+ }
54119
55120 if (!$this->mTarget) {
56121 /*
@@ -65,16 +130,6 @@
66131 if ($this->mTarget == $wgUser->getName())
67132 $this->isself = true;
68133
69 - if( !$this->userCanExecute( $wgUser ) ) {
70 - // fixme... there may be intermediate groups we can mention.
71 - global $wgOut;
72 - $wgOut->showPermissionsErrorPage( array(
73 - $wgUser->isAnon()
74 - ? 'userrights-nologin'
75 - : 'userrights-notallowed' ) );
76 - return;
77 - }
78 -
79134 if ( wfReadOnly() ) {
80135 global $wgOut;
81136 $wgOut->readOnlyPage();
@@ -140,64 +195,12 @@
141196 }
142197
143198 // Validate input set...
144 - $changeable = $this->changeableGroups();
145 - $addable = array_merge( $changeable['add'], $this->isself ? $changeable['add-self'] : array() );
146 - $removable = array_merge( $changeable['remove'], $this->isself ? $changeable['remove-self'] : array() );
147 -
148 - $removegroup = array_unique(
149 - array_intersect( (array)$removegroup, $removable ) );
150 - $addgroup = array_unique(
151 - array_intersect( (array)$addgroup, $addable ) );
152 -
153 - $oldGroups = $user->getGroups();
154 - $newGroups = $oldGroups;
155 - // remove then add groups
156 - if( $removegroup ) {
157 - $newGroups = array_diff($newGroups, $removegroup);
158 - foreach( $removegroup as $group ) {
159 - $user->removeGroup( $group );
160 - }
161 - }
162 - if( $addgroup ) {
163 - $newGroups = array_merge($newGroups, $addgroup);
164 - foreach( $addgroup as $group ) {
165 - $user->addGroup( $group );
166 - }
167 - }
168 - $newGroups = array_unique( $newGroups );
169 -
170 - // Ensure that caches are cleared
171 - $user->invalidateCache();
172 -
173 - wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) );
174 - wfDebug( 'newGroups: ' . print_r( $newGroups, true ) );
175 - if( $user instanceof User ) {
176 - // hmmm
177 - wfRunHooks( 'UserRights', array( &$user, $addgroup, $removegroup ) );
178 - }
179 -
180 - if( $newGroups != $oldGroups ) {
181 - $this->addLogEntry( $user, $oldGroups, $newGroups );
182 - }
 199+ $rm = $this->getBackend();
 200+ $reason = $wgRequest->getText( 'user-reason' );
 201+
 202+ $rm->changeUserGroups( $user, $addgroup, $removegroup, $reason, $wgUser );
183203 }
184 -
185 - /**
186 - * Add a rights log entry for an action.
187 - */
188 - function addLogEntry( $user, $oldGroups, $newGroups ) {
189 - global $wgRequest;
190 - $log = new LogPage( 'rights' );
191204
192 - $log->addEntry( 'rights',
193 - $user->getUserPage(),
194 - $wgRequest->getText( 'user-reason' ),
195 - array(
196 - $this->makeGroupNameListForLog( $oldGroups ),
197 - $this->makeGroupNameListForLog( $newGroups )
198 - )
199 - );
200 - }
201 -
202205 /**
203206 * Edit user groups membership
204207 * @param $username String: name of the user.
@@ -210,7 +213,7 @@
211214 return;
212215 }
213216
214 - $groups = $user->getGroups();
 217+ $groups = $this->getBackend()->getUserGroups( $user );
215218
216219 $this->showEditUserGroupsForm( $user, $groups );
217220
@@ -229,52 +232,9 @@
230233 function fetchUser( $username ) {
231234 global $wgOut, $wgUser;
232235
233 - $parts = explode( '@', $username );
234 - if( count( $parts ) < 2 ) {
235 - $name = trim( $username );
236 - $database = '';
237 - } else {
238 - list( $name, $database ) = array_map( 'trim', $parts );
 236+ $user = $this->getBackend()->fetchUser( $username );
239237
240 - if( !$wgUser->isAllowed( 'userrights-interwiki' ) ) {
241 - $wgOut->addWikiMsg( 'userrights-no-interwiki' );
242 - return null;
243 - }
244 - if( !UserRightsProxy::validDatabase( $database ) ) {
245 - $wgOut->addWikiMsg( 'userrights-nodatabase', $database );
246 - return null;
247 - }
248 - }
249 -
250 - if( $name == '' ) {
251 - $wgOut->addWikiMsg( 'nouserspecified' );
252 - return false;
253 - }
254 -
255 - if( $name{0} == '#' ) {
256 - // Numeric ID can be specified...
257 - // We'll do a lookup for the name internally.
258 - $id = intval( substr( $name, 1 ) );
259 -
260 - if( $database == '' ) {
261 - $name = User::whoIs( $id );
262 - } else {
263 - $name = UserRightsProxy::whoIs( $database, $id );
264 - }
265 -
266 - if( !$name ) {
267 - $wgOut->addWikiMsg( 'noname' );
268 - return null;
269 - }
270 - }
271 -
272 - if( $database == '' ) {
273 - $user = User::newFromName( $name );
274 - } else {
275 - $user = UserRightsProxy::newFromName( $database, $name );
276 - }
277 -
278 - if( !$user || $user->isAnon() ) {
 238+ if( $user == null ) {
279239 $wgOut->addWikiMsg( 'nosuchusershort', $username );
280240 return null;
281241 }
@@ -290,14 +250,6 @@
291251 }
292252 }
293253
294 - function makeGroupNameListForLog( $ids ) {
295 - if( empty( $ids ) ) {
296 - return '';
297 - } else {
298 - return $this->makeGroupNameList( $ids );
299 - }
300 - }
301 -
302254 /**
303255 * Output a form to allow searching for a user
304256 */
@@ -306,6 +258,7 @@
307259 $wgOut->addHTML(
308260 Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'name' => 'uluser', 'id' => 'mw-userrights-form1' ) ) .
309261 Xml::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
 262+ Xml::hidden( 'backend', $this->mBackend ) .
310263 Xml::openElement( 'fieldset' ) .
311264 Xml::element( 'legend', array(), wfMsg( 'userrights-lookup-user' ) ) .
312265 Xml::inputLabel( wfMsg( 'userrights-user-editname' ), 'user', 'username', 30, $this->mTarget ) . ' ' .
@@ -324,7 +277,12 @@
325278 * @return Array: Tuple of addable, then removable groups
326279 */
327280 protected function splitGroups( $groups ) {
328 - list($addable, $removable, $addself, $removeself) = array_values( $this->changeableGroups() );
 281+ $changeableGroups = $this->getBackend()->getChangeableGroups( $groups );
 282+
 283+ $addable = $changeableGroups['add'];
 284+ $removable = $changeableGroups['remove'];
 285+ $addself = $changeableGroups['add-self'];
 286+ $removeself = $changeableGroups['remove-self'];
329287
330288 $removable = array_intersect(
331289 array_merge( $this->isself ? $removeself : array(), $removable ),
@@ -348,7 +306,7 @@
349307 list( $addable, $removable ) = $this->splitGroups( $groups );
350308
351309 $list = array();
352 - foreach( $user->getGroups() as $group )
 310+ foreach( $groups as $group )
353311 $list[] = self::buildGroupLink( $group );
354312
355313 $grouplist = '';
@@ -359,6 +317,7 @@
360318 $wgOut->addHTML(
361319 Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalURL(), 'name' => 'editGroup', 'id' => 'mw-userrights-form2' ) ) .
362320 Xml::hidden( 'user', $this->mTarget ) .
 321+ Xml::hidden( 'backend', $this->mBackend ) .
363322 Xml::hidden( 'wpEditToken', $wgUser->editToken( $this->mTarget ) ) .
364323 Xml::openElement( 'fieldset' ) .
365324 Xml::element( 'legend', array(), wfMsg( 'userrights-editusergroup' ) ) .
@@ -404,8 +363,8 @@
405364 * Returns an array of all groups that may be edited
406365 * @return array Array of groups that may be edited.
407366 */
408 - protected static function getAllGroups() {
409 - return User::getAllGroups();
 367+ protected function getAllGroups() {
 368+ return array_diff( $this->getBackend()->getAllGroups(), User::getImplicitGroups() );
410369 }
411370
412371 /**
@@ -509,40 +468,13 @@
510469 function changeableGroups() {
511470 global $wgUser;
512471
513 - if( $wgUser->isAllowed( 'userrights' ) ) {
514 - // This group gives the right to modify everything (reverse-
515 - // compatibility with old "userrights lets you change
516 - // everything")
517 - // Using array_merge to make the groups reindexed
518 - $all = array_merge( User::getAllGroups() );
519 - return array(
520 - 'add' => $all,
521 - 'remove' => $all,
522 - 'add-self' => array(),
523 - 'remove-self' => array()
524 - );
525 - }
526 -
527 - // Okay, it's not so simple, we will have to go through the arrays
528 - $groups = array(
529 - 'add' => array(),
530 - 'remove' => array(),
531 - 'add-self' => array(),
532 - 'remove-self' => array() );
533 - $addergroups = $wgUser->getEffectiveGroups();
534 -
535 - foreach ($addergroups as $addergroup) {
536 - $groups = array_merge_recursive(
537 - $groups, $this->changeableByGroup($addergroup)
538 - );
539 - $groups['add'] = array_unique( $groups['add'] );
540 - $groups['remove'] = array_unique( $groups['remove'] );
541 - $groups['add-self'] = array_unique( $groups['add-self'] );
542 - $groups['remove-self'] = array_unique( $groups['remove-self'] );
543 - }
 472+ $groups = $this->getBackend()->getChangeableGroupsForUser( $wgUser );
544473
 474+ // Load data for hooks
 475+ $addergroups = $this->getBackend()->getUserGroups( $wgUser );
 476+
545477 // Run a hook because we can
546 - wfRunHooks( 'UserrightsChangeableGroups', array( $this, $wgUser, $addergroups, &$groups ) );
 478+ wfRunHooks( 'UserrightsChangeableGroups', array( $this, $wgUser, $addergroups, &$groups, $this->mBackend ) );
547479
548480 return $groups;
549481 }
@@ -553,61 +485,8 @@
554486 * @param $group String: the group to check for whether it can add/remove
555487 * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) , 'add-self' => array( addablegroups to self), 'remove-self' => array( removable groups from self) )
556488 */
557 - private function changeableByGroup( $group ) {
558 - global $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
559 -
560 - $groups = array( 'add' => array(), 'remove' => array(), 'add-self' => array(), 'remove-self' => array() );
561 - if( empty($wgAddGroups[$group]) ) {
562 - // Don't add anything to $groups
563 - } elseif( $wgAddGroups[$group] === true ) {
564 - // You get everything
565 - $groups['add'] = User::getAllGroups();
566 - } elseif( is_array($wgAddGroups[$group]) ) {
567 - $groups['add'] = $wgAddGroups[$group];
568 - }
569 -
570 - // Same thing for remove
571 - if( empty($wgRemoveGroups[$group]) ) {
572 - } elseif($wgRemoveGroups[$group] === true ) {
573 - $groups['remove'] = User::getAllGroups();
574 - } elseif( is_array($wgRemoveGroups[$group]) ) {
575 - $groups['remove'] = $wgRemoveGroups[$group];
576 - }
577 -
578 - // Re-map numeric keys of AddToSelf/RemoveFromSelf to the 'user' key for backwards compatibility
579 - if( empty($wgGroupsAddToSelf['user']) || $wgGroupsAddToSelf['user'] !== true ) {
580 - foreach($wgGroupsAddToSelf as $key => $value) {
581 - if( is_int($key) ) {
582 - $wgGroupsAddToSelf['user'][] = $value;
583 - }
584 - }
585 - }
586 -
587 - if( empty($wgGroupsRemoveFromSelf['user']) || $wgGroupsRemoveFromSelf['user'] !== true ) {
588 - foreach($wgGroupsRemoveFromSelf as $key => $value) {
589 - if( is_int($key) ) {
590 - $wgGroupsRemoveFromSelf['user'][] = $value;
591 - }
592 - }
593 - }
594 -
595 - // Now figure out what groups the user can add to him/herself
596 - if( empty($wgGroupsAddToSelf[$group]) ) {
597 - } elseif( $wgGroupsAddToSelf[$group] === true ) {
598 - // No idea WHY this would be used, but it's there
599 - $groups['add-self'] = User::getAllGroups();
600 - } elseif( is_array($wgGroupsAddToSelf[$group]) ) {
601 - $groups['add-self'] = $wgGroupsAddToSelf[$group];
602 - }
603 -
604 - if( empty($wgGroupsRemoveFromSelf[$group]) ) {
605 - } elseif( $wgGroupsRemoveFromSelf[$group] === true ) {
606 - $groups['remove-self'] = User::getAllGroups();
607 - } elseif( is_array($wgGroupsRemoveFromSelf[$group]) ) {
608 - $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
609 - }
610 -
611 - return $groups;
 489+ private function changeableByGroup( $group ) {
 490+ return $this->getBackend()->getChangeableGroups( array( $group ) );
612491 }
613492
614493 /**
@@ -617,7 +496,6 @@
618497 * @param $output OutputPage to use
619498 */
620499 protected function showLogFragment( $user, $output ) {
621 - $output->addHtml( Xml::element( 'h2', null, LogPage::logName( 'rights' ) . "\n" ) );
622 - LogEventsList::showLogExtract( $output, 'rights', $user->getUserPage()->getPrefixedText() );
 500+ $this->getBackend()->showUserLogFragment( $user, $output );
623501 }
624502 }
Index: branches/group_rights/includes/specials/SpecialListgrouprights.php
@@ -25,8 +25,11 @@
2626 */
2727 public function execute( $par ) {
2828 global $wgOut, $wgImplicitGroups, $wgMessageCache;
29 - global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups;
 29+ global $wgAddGroups, $wgRemoveGroups;
3030 $wgMessageCache->loadAllMessages();
 31+
 32+ $rm = new RightsManagerMulti;
 33+ $groupPerms = $rm->getAllGroupPermissions();
3134
3235 $this->setHeaders();
3336 $this->outputHeader();
@@ -39,8 +42,8 @@
4043 '</tr>'
4144 );
4245
43 - foreach( $wgGroupPermissions as $group => $permissions ) {
44 - $groupname = ( $group == '*' ) ? 'all' : htmlspecialchars( $group ); // Replace * with a more descriptive groupname
 46+ foreach( $groupPerms as $group => $permissions ) {
 47+ $groupname = ( $group == '*' ) ? 'all' : htmlspecialchars( $group ); // TODO: Replace * with a more descriptive groupname
4548
4649 $msg = wfMsg( 'group-' . $groupname );
4750 if ( wfEmptyMsg( 'group-' . $groupname, $msg ) || $msg == '' ) {
@@ -70,16 +73,21 @@
7174 $grouplink = '';
7275 }
7376
74 - $addgroups = isset( $wgAddGroups[$group] ) ? $wgAddGroups[$group] : array();
75 - $removegroups = isset( $wgRemoveGroups[$group] ) ? $wgRemoveGroups[$group] : array();
 77+ $changeableGroups = array();
7678
 79+ global $wgRightsManagers;
 80+ foreach( $wgRightsManagers as $rmClass ) {
 81+ $rm = new $rmClass;
 82+ $changeableGroups = array_merge_recursive( $rm->getChangeableGroups( array( $group ) ), $changeableGroups );
 83+ }
 84+
7785 $wgOut->addHTML(
7886 '<tr>
7987 <td>' .
8088 $grouppage . $grouplink .
8189 '</td>
8290 <td>' .
83 - self::formatPermissions( $permissions, $addgroups, $removegroups ) .
 91+ self::formatPermissions( $permissions, $changeableGroups ) .
8492 '</td>
8593 </tr>'
8694 );
@@ -93,9 +101,10 @@
94102 * Create a user-readable list of permissions from the given array.
95103 *
96104 * @param $permissions Array of permission => bool (from $wgGroupPermissions items)
 105+ * @param $changeableGroups Array of action => list of groups (from RightsManager::getChangeableGroups)
97106 * @return string List of all granted permissions, separated by comma separator
98107 */
99 - private static function formatPermissions( $permissions, $add, $remove ) {
 108+ private static function formatPermissions( $permissions, $changeableGroups ) {
100109 global $wgLang;
101110 $r = array();
102111 foreach( $permissions as $permission => $granted ) {
@@ -108,16 +117,21 @@
109118 }
110119 }
111120 sort( $r );
112 - if( $add === true ){
113 - $r[] = wfMsgExt( 'listgrouprights-addgroup-all', array( 'escape' ) );
114 - } else if( is_array( $add ) && count( $add ) ) {
115 - $r[] = wfMsgExt( 'listgrouprights-addgroup', array( 'parseinline' ), $wgLang->listToText( array_map( array( 'User', 'makeGroupLinkWiki' ), $add ) ), count( $add ) );
 121+
 122+ // Get addable/removable groups.
 123+ $groupActions = array( 'add-self' => 'addself', 'remove-self' => 'removeself', 'add' => 'add', 'remove' => 'remove' );
 124+
 125+ foreach( $groupActions as $key => $value ) {
 126+ if ( !isset($changeableGroups[$key]) || !is_array($changeableGroups[$key]) || !count($changeableGroups[$key]) ) {
 127+ // Do nothing.
 128+ } elseif ( !count( array_diff( $changeableGroups[$key], array_diff( User::getAllGroups(), User::getImplicitGroups() ) ) ) ) {
 129+ // i.e. User can change *all* groups.
 130+ $r[] = wfMsgExt( "listgrouprights-{$value}group-all", array( 'escape' ) );
 131+ } elseif ( count( $changeableGroups[$key] ) ) {
 132+ $r[] = wfMsgExt( "listgrouprights-{$value}group", array( 'parseinline' ), $wgLang->listToText( array_map( array( 'User', 'makeGroupLinkWiki' ), $changeableGroups[$key] ) ), count( $changeableGroups[$key] ) );
 133+ }
116134 }
117 - if( $remove === true ){
118 - $r[] = wfMsgExt( 'listgrouprights-removegroup-all', array( 'escape' ) );
119 - } else if( is_array( $remove ) && count( $remove ) ) {
120 - $r[] = wfMsgExt( 'listgrouprights-removegroup', array( 'parseinline' ), $wgLang->listToText( array_map( array( 'User', 'makeGroupLinkWiki' ), $remove ) ), count( $remove ) );
121 - }
 135+
122136 if( empty( $r ) ) {
123137 return '';
124138 } else {
Index: branches/group_rights/includes/specials/SpecialGroupRights.php
@@ -0,0 +1,340 @@
 2+<?php
 3+
 4+#This file is part of MediaWiki.
 5+
 6+#MediaWiki is free software: you can redistribute it and/or modify
 7+#it under the terms of version 2 of the GNU General Public License
 8+#as published by the Free Software Foundation.
 9+
 10+#MediaWiki is distributed in the hope that it will be useful,
 11+#but WITHOUT ANY WARRANTY; without even the implied warranty of
 12+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 13+#GNU General Public License for more details.
 14+
 15+/**
 16+ * Special page to allow managing groups
 17+ */
 18+
 19+if ( !defined( 'MEDIAWIKI' ) ) {
 20+ die();
 21+}
 22+
 23+
 24+class SpecialGroupRights extends SpecialPage
 25+{
 26+ function __construct() {
 27+ parent::__construct('GroupRights', 'grouprights');
 28+ }
 29+
 30+ function getAvailableBackends( $user ) {
 31+ global $wgRightsManagers;
 32+
 33+ $availableBackends = array();
 34+
 35+ foreach( $wgRightsManagers as $rmClass ) {
 36+ $rm = new $rmClass;
 37+
 38+ if ( $rm->canEditRights( $user ) ) {
 39+ $availableBackends[] = $rmClass;
 40+ }
 41+ }
 42+
 43+ return $availableBackends;
 44+ }
 45+
 46+ function userCanExecute( $user ) {
 47+ return (bool) count($this->getAvailableBackends( $user ));
 48+ }
 49+
 50+ function getBackend() {
 51+ static $backend = null;
 52+
 53+ if ( !is_null($backend) )
 54+ return $backend;
 55+
 56+ return $backend = new $this->mBackend;
 57+ }
 58+
 59+ function execute( $subpage ) {
 60+ global $wgRequest,$wgOut,$wgUser;
 61+
 62+ if (!$this->userCanExecute($wgUser)) {
 63+ $this->displayRestrictionError();
 64+ return;
 65+ }
 66+
 67+ $wgOut->setPageTitle( wfMsg( 'grouprights' ) );
 68+ $wgOut->setRobotPolicy( "noindex,nofollow" );
 69+ $wgOut->setArticleRelated( false );
 70+ $wgOut->enableClientCache( false );
 71+
 72+ $availableBackends = $this->getAvailableBackends( $wgUser );
 73+ $specifiedBackend = $wgRequest->getVal( 'backend' );
 74+
 75+ if ( count($availableBackends) == 1 ) {
 76+ $this->mBackend = $availableBackends[0];
 77+ } elseif ( in_array( $specifiedBackend, $availableBackends ) ) {
 78+ $this->mBackend = $specifiedBackend;
 79+ } else {
 80+ $this->showBackendSelector();
 81+ return;
 82+ }
 83+
 84+ if ($subpage == '' ) {
 85+ $subpage = $wgRequest->getVal( 'wpGroup' );
 86+ }
 87+
 88+ if ($subpage != '' && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) )) {
 89+ $this->doSubmit($subpage);
 90+ } else if ($subpage != '') {
 91+ $this->buildGroupView($subpage);
 92+ } else {
 93+ $this->buildMainView();
 94+ }
 95+ }
 96+
 97+ function showBackendSelector() {
 98+ global $wgUser, $wgOut;
 99+
 100+ $wgOut->setSubTitle( wfMsg( 'grouprights-backendselect-subtitle' ) );
 101+ $wgOut->addWikiMsg( 'grouprights-backendselect-text' );
 102+
 103+ // Produce list.
 104+ $sk = $wgUser->getSkin();
 105+ $availableBackends = $this->getAvailableBackends( $wgUser );
 106+ $list = '';
 107+ foreach( $availableBackends as $backend ) {
 108+ $text = wfMsg( "rights-backend-$backend" );
 109+ $link = $sk->link( $this->getTitle(), $text, array(), array( 'backend' => $backend ) );
 110+ $list .= Xml::tags( 'li', null, $link );
 111+ }
 112+
 113+ $list = Xml::tags( 'ul', null, $list );
 114+
 115+ $wgOut->addHTML( $list );
 116+ }
 117+
 118+ function getAllGroups() {
 119+ return $this->getBackend()->getAllGroups();
 120+ }
 121+
 122+ function buildMainView() {
 123+ global $wgOut,$wgUser,$wgScript;
 124+ $sk = $wgUser->getSkin();
 125+
 126+ $groups = $this->getAllGroups();
 127+
 128+ // Existing groups
 129+ $html = Xml::openElement( 'fieldset' );
 130+ $html .= Xml::element( 'legend', null, wfMsg( 'grouprights-existinggroup-legend' ) );
 131+
 132+ $wgOut->addHTML( $html );
 133+
 134+ if (count($groups)) {
 135+ $wgOut->addWikiMsg( 'grouprights-grouplist' );
 136+ $wgOut->addHTML( '<ul>' );
 137+
 138+ foreach ($groups as $group) {
 139+ $editLink = $sk->link( $this->getTitle( $group ), wfMsg( 'grouprights-editlink'), array(), array( 'backend' => $this->mBackend ) );
 140+ $text = htmlspecialchars($group) ." ($editLink)";
 141+
 142+ $wgOut->addHTML( "<li> $text </li>" );
 143+ }
 144+ } else {
 145+ $wgOut->addWikiMsg( 'grouprights-nogroups' );
 146+ }
 147+
 148+ $wgOut->addHTML( Xml::closeElement( 'ul' ) . Xml::closeElement( 'fieldset' ) );
 149+
 150+ // "Create a group" prompt
 151+ $html = Xml::openElement( 'fieldset' ) . Xml::element( 'legend', null, wfMsg( 'grouprights-newgroup-legend' ) );
 152+ $html .= wfMsgExt( 'grouprights-newgroup-intro', array( 'parse' ) );
 153+ $html .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $wgScript, 'name' => 'grouprights-newgroup' ) );
 154+ $html .= Xml::hidden( 'title', $this->getTitle()->getPrefixedText() );
 155+ $html .= Xml::hidden( 'backend', $this->mBackend );
 156+
 157+ $fields = array( 'grouprights-newgroupname' => wfInput( 'wpGroup', 45 ) );
 158+
 159+ $html .= wfBuildForm( $fields, 'grouprights-creategroup-submit' );
 160+ $html .= Xml::closeElement( 'form' );
 161+ $html .= Xml::closeElement( 'fieldset' );
 162+
 163+ $wgOut->addHTML( $html );
 164+ }
 165+
 166+ function buildGroupView( $group ) {
 167+ global $wgOut, $wgUser, $wgScript;
 168+
 169+ $backend = $this->getBackend();
 170+
 171+ $wgOut->setSubtitle( wfMsg( 'grouprights-subtitle', $group ) );
 172+
 173+ $html = Xml::openElement( 'fieldset' ) . Xml::element( 'legend', null, wfMsg( 'grouprights-fieldset', $group ) );
 174+ $html .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle( $group)->getLocalUrl(), 'name' => 'grouprights-newgroup' ) );
 175+ $html .= Xml::hidden( 'wpGroup', $group );
 176+ $html .= Xml::hidden( 'wpEditToken', $wgUser->editToken() );
 177+ $html .= Xml::hidden( 'backend', $this->mBackend );
 178+
 179+ $fields = array();
 180+
 181+ $fields['grouprights-editgroup-name'] = $group;
 182+ $fields['grouprights-editgroup-display'] = wfMsgExt( 'grouprights-editgroup-display-edit', array( 'parseinline' ), $group, User::getGroupName( $group ) );
 183+ $fields['grouprights-editgroup-member'] = wfMsgExt( 'grouprights-editgroup-member-edit', array( 'parseinline' ), $group, User::getGroupMember( $group ) );
 184+ $fields['grouprights-editgroup-members'] = wfMsgExt( 'grouprights-editgroup-members-link', array( 'parseinline' ), $group, User::getGroupMember( $group ) );
 185+
 186+ // Allow backends to add extras here
 187+ $backend->doExtraGroupForm( $fields, $group );
 188+
 189+ $fields['grouprights-editgroup-perms'] = $this->buildCheckboxes($group);
 190+
 191+ $changeable = $backend->getChangeableGroups( array($group) );
 192+
 193+ foreach( $changeable as $type => $groups ) {
 194+ $changeable[$type] = implode( ', ', $groups );
 195+ }
 196+
 197+ $fields['grouprights-editgroup'] = '';
 198+
 199+ $editTypes = array( 'add', 'remove', 'add-self', 'remove-self' );
 200+ foreach( $editTypes as $type ) {
 201+ $fields["grouprights-editgroup-$type"] = wfInput( "wpGroupChange-$type", 45, $changeable[$type] );
 202+ }
 203+
 204+ $fields['grouprights-editgroup-reason'] = wfInput( 'wpReason', 45 );
 205+
 206+ $html .= wfBuildForm( $fields, 'grouprights-editgroup-submit' );
 207+
 208+ $html .= Xml::closeElement( 'form' );
 209+ $html .= Xml::closeElement( 'fieldset' );
 210+
 211+ $wgOut->addHTML( $html );
 212+
 213+ $backend->showGroupLogFragment( $group, $wgOut );
 214+ }
 215+
 216+ function rightEditable( $group, $right ) {
 217+ return $this->getBackend()->rightEditable( $group, $right );
 218+ }
 219+
 220+ function buildCheckboxes( $group ) {
 221+
 222+ $rights = User::getAllRights();
 223+ $assignedRights = $this->getAssignedRights( $group );
 224+
 225+ sort($rights);
 226+
 227+ $checkboxes = array();
 228+
 229+ foreach( $rights as $right ) {
 230+ # Build a checkbox.
 231+ $checked = in_array( $right, $assignedRights );
 232+ $attribs = array();
 233+ if ( !$this->rightEditable( $group, $right ) ) {
 234+ $attribs['disabled'] = 'disabled';
 235+ }
 236+
 237+ $checkbox = Xml::checkLabel( User::getRightDescription( $right ),
 238+ "wpRightAssigned-$right", "wpRightAssigned-$right", $checked, $attribs );
 239+
 240+ $checkboxes[] = "<li>$checkbox</li>";
 241+ }
 242+
 243+ $count = count($checkboxes);
 244+
 245+ $firstCol = round($count/2);
 246+
 247+ $checkboxes1 = array_slice($checkboxes, 0, $firstCol);
 248+ $checkboxes2 = array_slice($checkboxes, $firstCol );
 249+
 250+ $html = '<table><tbody><tr><td><ul>';
 251+
 252+ foreach( $checkboxes1 as $cb ) {
 253+ $html .= $cb;
 254+ }
 255+
 256+ $html .= '</ul></td><td><ul>';
 257+
 258+ foreach( $checkboxes2 as $cb ) {
 259+ $html .= $cb;
 260+ }
 261+
 262+ $html .= '</ul></td></tr></tbody></table>';
 263+
 264+ return $html;
 265+ }
 266+
 267+ function getAssignedRights( $group ) {
 268+ return $this->getBackend()->getGroupPermissions( array($group) );
 269+ }
 270+
 271+ function beginTransaction() {
 272+ $dbw = wfGetDB( DB_MASTER );
 273+ $dbw->begin();
 274+ }
 275+
 276+ function endTransaction() {
 277+ $dbw = wfGetDB( DB_MASTER );
 278+ $dbw->commit();
 279+ }
 280+
 281+ function doSubmit( $group ) {
 282+ global $wgRequest,$wgOut,$wgScript,$wgUser;
 283+
 284+ $newRights = array();
 285+ $addRights = array();
 286+ $removeRights = array();
 287+ $oldRights = $this->getAssignedRights( $group );
 288+ $allRights = User::getAllRights();
 289+
 290+ $reason = $wgRequest->getVal( 'wpReason', '' );
 291+
 292+ foreach ($allRights as $right) {
 293+ $alreadyAssigned = in_array( $right, $oldRights );
 294+
 295+ if ($wgRequest->getCheck( "wpRightAssigned-$right" )) {
 296+ $newRights[] = $right;
 297+ }
 298+
 299+ if (!$alreadyAssigned && $wgRequest->getCheck( "wpRightAssigned-$right" )) {
 300+ $addRights[] = $right;
 301+ } else if ($alreadyAssigned && !$wgRequest->getCheck( "wpRightAssigned-$right" ) ) {
 302+ $removeRights[] = $right;
 303+ } # Otherwise, do nothing.
 304+ }
 305+
 306+ $backend = $this->getBackend();
 307+
 308+ // Assign the rights.
 309+ if (count($addRights)>0)
 310+ $backend->assignRights( $group, $addRights );
 311+ if (count($removeRights)>0)
 312+ $backend->revokeRights( $group, $removeRights );
 313+
 314+ // Log it
 315+ if (!(count($addRights)==0 && count($removeRights)==0))
 316+ $backend->addGroupLogEntry( $group, $addRights, $removeRights, $reason, $wgUser );
 317+
 318+ // Changeable groups
 319+ $changeableGroups = $backend->getChangeableGroups( array( $group ) );
 320+ $newChangeableGroups = array();
 321+
 322+ $editTypes = array( 'add' => 'addable', 'remove' => 'removable', 'add-self' => 'addself', 'remove-self' => 'removeself' );
 323+ foreach( $editTypes as $type => $var ) {
 324+ $newChangeableGroups[$var] = array_map( 'trim', explode( ',', $wgRequest->getVal( "wpGroupChange-$type" ) ) );
 325+ }
 326+ extract($newChangeableGroups);
 327+ $backend->setChangeableGroups( $group, $addable, $removable, $addself, $removeself );
 328+
 329+ // Clear the cache
 330+ $backend->invalidateGroupCache( $group );
 331+
 332+ // Do extra stuff.
 333+ $backend->doExtraGroupSubmit( $group, $reason, $wgRequest);
 334+
 335+ // Display success
 336+ $sk = $wgUser->getSkin();
 337+ $wgOut->setSubTitle( wfMsg( 'grouprights-editgroup-success' ) );
 338+ $wgOut->addWikiMsg( 'grouprights-editgroup-success-text', $group );
 339+ $wgOut->addHTML( $sk->link( $this->getTitle( ), wfMsg( 'grouprights-return' ), array(), array( 'backend' => $this->mBackend ) ) );
 340+ }
 341+}
Property changes on: branches/group_rights/includes/specials/SpecialGroupRights.php
___________________________________________________________________
Added: svn:eol-style
1342 + native
Index: branches/group_rights/includes/RightsManager.php
@@ -0,0 +1,723 @@
 2+<?php
 3+
 4+if ( !defined( 'MEDIAWIKI' ) )
 5+ die();
 6+
 7+abstract class RightsManager {
 8+
 9+ /**
 10+ * Assign a set of rights to a group.
 11+ * @param $group \type{\string} The group to assign rights to.
 12+ * @param $rights \type{\arrayof{\string}} The rights to assign.
 13+ */
 14+ function assignRights( $group, $rights ) {
 15+ $this->setRightsStatus( $group, array_fill_keys( $rights, true ) );
 16+ }
 17+
 18+ /**
 19+ * Invalidate the cache of rights for a group.
 20+ * @param $group \type{\string} The group to invalidate the cache for.
 21+ */
 22+ function invalidateGroupCache( $group ) {}
 23+
 24+ /**
 25+ * Invalidate the cache of rights for a user.
 26+ * @param $user \type{\object{User}} The user to invalidate the cache for.
 27+ */
 28+ function invalidateUserCache( $user ) {}
 29+
 30+ /**
 31+ * Revoke a set of rights from a group.
 32+ * @param $group \type{\string} The group to revoke rights from.
 33+ * @param $rights \type{\arrayof{\string}} The rights to revoke.
 34+ */
 35+ function revokeRights( $group, $rights ) {
 36+ $this->setRightsStatus( $group, array_fill_keys( $rights, false ) );
 37+ }
 38+
 39+ /**
 40+ * Set the status of rights.
 41+ * @param $group \type{\string} The group to set rights status for
 42+ * @param $rights \type{\array} An array with key=right, value=whether or not the right is assigned.
 43+ */
 44+ abstract function setRightsStatus( $group, $rights );
 45+
 46+ /**
 47+ * Modify the changeable groups for a group.
 48+ * @param $group \type{\string} The group to assign edit changeable groups for.
 49+ * @param $addable \type{\arrayof{\string}} The groups to be addable.
 50+ * @param $removable \type{\arrayof{\string}} The groups to be removable.
 51+ * @param $addtoself \type{\arrayof{\string}} The groups to be addable by the user in question only.
 52+ * @param $removefromself \type{\arrayof{\string}} The groups to be removable by the user in question only.
 53+ */
 54+ abstract function setChangeableGroups( $group, $addable, $removable, $addtoself, $removefromself );
 55+
 56+ /**
 57+ * Get a list of all group permissions.
 58+ * @return \type{\array} Array of group => (permission => allowed, ...), ...
 59+ */
 60+ abstract function getAllGroupPermissions();
 61+
 62+ /**
 63+ * Determine whether a given user is allowed to change group rights.
 64+ * @param $user \type{\object{User}} The user to check.
 65+ * @return \type{\bool} Whether or not the user is allowed to change group rights.
 66+ */
 67+ abstract function canEditRights( $user );
 68+
 69+ /**
 70+ * Add a user to a group or groups.
 71+ * @param $user \type{\object{User}}
 72+ * @param $groups \type{\arrayof{\string}}
 73+ */
 74+ abstract function addUserGroups( $user, $groups );
 75+
 76+ /**
 77+ * Remove a user from a group or groups.
 78+ * @param $user \type{\object{User}}
 79+ * @param $groups \type{\arrayof{\string}}
 80+ */
 81+ abstract function removeUserGroups( $user, $groups );
 82+
 83+ /**
 84+ * Make a list of group names, for the log.
 85+ */
 86+ function makeGroupNameListForLog( $ids ) {
 87+ if( empty( $ids ) ) {
 88+ return wfMsgForContent( 'rightsnone' );
 89+ } else {
 90+ return implode( ', ', $ids );
 91+ }
 92+ }
 93+
 94+ /**
 95+ * Make a list of rights, for the log.
 96+ */
 97+ function makeRightsList( $ids ) {
 98+ return (bool)count($ids) ? implode( ', ', $ids ) : wfMsgForContent( 'rightsnone' );
 99+ }
 100+
 101+ function changeUserGroups( $user, $addgroups, $removegroups, $reason, $doer ) {
 102+ // Validate input set...
 103+ $changeable = $this->getChangeableGroupsForUser( $user );
 104+ $isself = $this->userEquals( $user, $doer );
 105+
 106+ // Allow adding to self
 107+ $addable = array_merge( $changeable['add'], $isself ? $changeable['add-self'] : array() );
 108+ $removable = array_merge( $changeable['remove'], $isself ? $changeable['remove-self'] : array() );
 109+
 110+ // Filter for what we can actually change
 111+ $removegroups = array_unique(
 112+ array_intersect( (array)$removegroups, $removable ) );
 113+ $addgroups = array_unique(
 114+ array_intersect( (array)$addgroups, $addable ) );
 115+
 116+ if (!count(array_merge( $removegroups, $addgroups ) ) )
 117+ return;
 118+
 119+ // Only add groups user doesn't have, remove groups user does have.
 120+ $oldGroups = $this->getUserGroups( $user );
 121+ $removegroups = array_intersect( $removegroups, $oldGroups );
 122+ $addgroups = array_diff( $addgroups, array_intersect( $addgroups, $oldGroups ) );
 123+
 124+ // The actual business end.
 125+ $this->addUserGroups( $user, $addgroups );
 126+ $this->removeUserGroups( $user, $removegroups );
 127+
 128+ // Ensure that caches are cleared
 129+ $this->invalidateUserCache( $user );
 130+
 131+ // Log it
 132+ $this->addUserGroupsLogEntry( $user, $addgroups, $removegroups, $reason, $doer );
 133+ }
 134+
 135+ /**
 136+ * Get a list of changeable groups.
 137+ * @return \type{\array} Array of group => array( group1, group2, ... )
 138+ */
 139+ abstract function getAllChangeableGroups();
 140+
 141+ /**
 142+ * Checks if two user objects are equal
 143+ */
 144+ function userEquals( $a, $b ) {
 145+ return $a->getId() == $b->getId();
 146+ }
 147+
 148+ abstract function fetchUser( $username );
 149+
 150+ /**
 151+ * Get a list of groups changeable by a particular group.
 152+ * @param $groups \type{string} Group or array of groups to check.
 153+ * @return \type{\arrayof{\string}} Array of action => groups changeable by this group/groups
 154+ */
 155+ function getChangeableGroups( $groups ) {
 156+ $changeable = $this->getAllChangeableGroups();
 157+ $result = array('add' => array(), 'remove' => array(), 'addself' => array(), 'removeself' => array());
 158+
 159+ foreach( $groups as $group ) {
 160+ if ( isset( $changeable[$group] ) && is_array( $changeable[$group] ) )
 161+ $result = array_merge_recursive( $result, $changeable[$group] );
 162+ }
 163+
 164+ return $result;
 165+ }
 166+
 167+ /**
 168+ * Get a list of groups changeable by a particular user.
 169+ * @param $user \type{\object{User}} User to check.
 170+ * @return \type{\array} Array of action => groups
 171+ */
 172+ function getChangeableGroupsForUser( $user ) {
 173+ $groups = $this->getUserGroups( $user );
 174+ return $this->getChangeableGroups( $groups );
 175+ }
 176+
 177+ /**
 178+ * Get a list of all defined groups.
 179+ * @return \type{\arrayof{\string}}
 180+ */
 181+ function getAllGroups() {
 182+ $groupPerms = $this->getAllGroupPermissions();
 183+ $changeableGroups = $this->getAllChangeableGroups();
 184+
 185+ return array_unique( array_merge( array_keys( $groupPerms ), array_keys( $changeableGroups ) ) );
 186+ }
 187+
 188+ /**
 189+ * Get a list of groups assigned to a user account.
 190+ * @param $user \type{\object{User}}
 191+ * @return \type{\arrayof{\string}}
 192+ */
 193+ abstract function getUserGroups( $user );
 194+
 195+ /**
 196+ * Get a list of permissions assigned to a user account.
 197+ * @param $user \type{\object{User}}
 198+ * @return \type{\arrayof{\string}}
 199+ */
 200+ function getPermissionsForUser( $user ) {
 201+ $groups = $this->getUserGroups( $user );
 202+ return $this->getGroupPermissions( $groups );
 203+ }
 204+
 205+ /**
 206+ * Get a list of all defined groups with a given permission.
 207+ * @param $permission \type{\string} The permission to check for.
 208+ * @return \type{\arrayof{\string}} List of group names with that permission.
 209+ */
 210+ function getGroupsWithPermission( $permission ) {
 211+ $groupPerms = $this->getAllGroupPermissions();
 212+
 213+ $allowedGroups = array();
 214+ foreach ( $groupPerms as $group => $rights ) {
 215+ if ( isset( $rights[$permission] ) && $rights[$permission] ) {
 216+ $allowedGroups[] = $group;
 217+ }
 218+ }
 219+ return $allowedGroups;
 220+ }
 221+
 222+ /**
 223+ * Get a list of all permissions assigned to the groups given.
 224+ * @param $groups \type{\arrayof{\string}} Array of groups to check.
 225+ * @return \type{\arrayof{\string}} List of permissions assigned to those groups.
 226+ */
 227+ function getGroupPermissions( $groups ) {
 228+ $groupPerms = $this->getAllGroupPermissions();
 229+ $rights = array();
 230+ foreach( $groups as $group ) {
 231+ if( isset( $groupPerms[$group] ) ) {
 232+ $rights = array_merge( $rights,
 233+ array_keys( array_filter( $groupPerms[$group] ) ) );
 234+ }
 235+ }
 236+ return $rights;
 237+ }
 238+
 239+ /**
 240+ * Show the logs for recent changes for a given group.
 241+ * @param $group \type{\string} The group to show changes for.
 242+ * @param $output \type{\object{OutputPage}} The OutputPage to print output to.
 243+ */
 244+ function showGroupLogFragment( $group, $output ) {
 245+ $title = SpecialPage::getTitleFor( 'ListUsers', $group );
 246+ $output->addHTML( Xml::element( 'h2', null, LogPage::logName( 'rights' ) . "\n" ) );
 247+ LogEventsList::showLogExtract( $output, 'rights', $title->getPrefixedText() );
 248+ }
 249+
 250+ function showUserLogFragment( $user, $output ) {
 251+ $output->addHtml( Xml::element( 'h2', null, LogPage::logName( 'rights' ) . "\n" ) );
 252+ LogEventsList::showLogExtract( $output, 'rights', $user->getUserPage()->getPrefixedText() );
 253+ }
 254+
 255+ /**
 256+ * Add a log entry for group rights changes.
 257+ * @param $group \type{\string} The group in question.
 258+ * @param $addRights \type{\arrayof{\string}} The rights added.
 259+ * @param $removeRights \type{\arrayof{\string}} The rights removed.
 260+ * @param $reason \type{\string} The comment attached to the change.
 261+ * @param $user \type{\object{User}} The user doing the change (Optional)
 262+ */
 263+ abstract function addGroupLogEntry( $group, $addRights, $removeRights, $reason, $user );
 264+
 265+ /**
 266+ * Add a log entry for user groups changes.
 267+ * @param $user \type{\object{User}} The user
 268+ * @param $addgroups \type{\arrayof{\string}} The groups added.
 269+ * @param $removegroups \type{\arrayof{\string}} The groups removed.
 270+ */
 271+ abstract function addUserGroupsLogEntry( $user, $addgroups, $removegroups, $reason, $doer );
 272+
 273+ /**
 274+ * Checks whether a given right on a given group may be modified.
 275+ * @param $group \type{\string} The group in question.
 276+ * @param $right \type{\string} The right in question.
 277+ * @return \type{\bool} Whether or not the given group-right may be modified.
 278+ */
 279+ function rightEditable( $group, $right ) { return true; }
 280+
 281+ /**
 282+ * Add any extra desired fields onto the group edit page.
 283+ * @param &$fields \type{\array} Pre-filled array of fields ready to be passed to Xml::buildForm.
 284+ * @param $group \type{\string} The group in question.
 285+ */
 286+ function doExtraGroupForm( &$fields, $group ) {}
 287+
 288+ /**
 289+ * Does any necessary extra processing for the group editing form.
 290+ * @param $group \type{\string} The group in question.
 291+ * @param $reason \type{\string} The reason given for the change.
 292+ * @param $request \type{\string} The HTTP request for the submission.
 293+ */
 294+ function doExtraGroupSubmit( $group, $reason, $request ) {}
 295+}
 296+
 297+// Derive from this class to produce a rights manager which is read-only.
 298+abstract class RightsManagerReadOnly extends RightsManager {
 299+ // These functions are disabled.
 300+ function setChangeableGroups( $group, $add, $remove, $addself, $removeself ) { return false; }
 301+ function setRightsStatus( $group, $rights ) { return false; }
 302+ function addUserGroups( $user, $groups ) { return false; }
 303+ function removeUserGroups( $user, $groups ) { return false; }
 304+ function canEditRights( $user ) {return false;}
 305+}
 306+
 307+// Pseudo-concrete implementation of a rights manager
 308+// -Pools rights and groups from multiple sources.
 309+class RightsManagerMulti extends RightsManagerReadOnly {
 310+
 311+ // Functions left unimplemented because it makes no sense.
 312+ function fetchUser( $username ) { return null; }
 313+ function addGroupLogEntry( $group, $addRights, $removeRights, $reason, $user ) { return false; }
 314+ function addUserGroupsLogEntry( $user, $addgroups, $removegroups, $reason, $doer ) { return false; }
 315+ function getAllChangeableGroups() { return false; }
 316+
 317+ function getAllGroupPermissions() {
 318+ global $wgRightsManagers;
 319+
 320+ $groupPerms = array();
 321+ foreach( $wgRightsManagers as $rmClass ) {
 322+ $rm = new $rmClass;
 323+
 324+ $rights = $rm->getAllGroupPermissions();
 325+
 326+ foreach ($rights as $group => $grouprights) {
 327+ if ( !isset( $groupPerms[$group] ) )
 328+ $groupPerms[$group] = array();
 329+
 330+ $grouprights = array_keys( array_filter( $grouprights ) );
 331+ foreach( $grouprights as $right ) {
 332+ $groupPerms[$group][$right] = true;
 333+ }
 334+ }
 335+ }
 336+
 337+ return $groupPerms;
 338+ }
 339+
 340+ function getGroupPermissions( $groups ) {
 341+ global $wgRightsManagers;
 342+
 343+ if (!is_array( $wgRightsManagers ) ) {
 344+ die( var_dump( $wgRightsManagers ) );
 345+ }
 346+
 347+ $perms = array();
 348+ foreach( $wgRightsManagers as $rmClass ) {
 349+ $rm = new $rmClass;
 350+
 351+ $perms = array_merge( $perms, $rm->getGroupPermissions( $groups ) );
 352+ }
 353+
 354+ return array_unique( $perms );
 355+ }
 356+
 357+ function getUserGroups( $user ) {
 358+ global $wgRightsManagers;
 359+
 360+ $groups = array();
 361+ foreach( $wgRightsManagers as $rmClass ) {
 362+ $rm = new $rmClass;
 363+
 364+ $groups = array_merge( $groups, $rm->getUserGroups( $user ) );
 365+ }
 366+
 367+ return array_unique( $groups );
 368+ }
 369+
 370+ function getPermissionsForUser( $user ) {
 371+ global $wgRightsManagers;
 372+
 373+ $perms = array();
 374+ foreach( $wgRightsManagers as $rmClass ) {
 375+ $rm = new $rmClass;
 376+
 377+ $perms = array_merge( $perms, $rm->getPermissionsForUser( $user ) );
 378+ }
 379+
 380+ return array_unique( $perms );
 381+ }
 382+
 383+ function getAllGroups() {
 384+ global $wgRightsManagers;
 385+
 386+ $groups = array();
 387+ foreach( $wgRightsManagers as $rmClass ) {
 388+ $rm = new $rmClass;
 389+
 390+ $groups = array_merge( $groups, $rm->getAllGroups( ) );
 391+ }
 392+
 393+ return array_unique( $groups );
 394+ }
 395+}
 396+
 397+// Concrete implementation of a rights manager - uses the default DB and configuration setup.
 398+class RightsManagerConfigDB extends RightsManager {
 399+ function getAllGroupPermissions() {
 400+ global $wgGroupPermissions,$wgAllowDBRightSubtraction;
 401+ $groupPerms = $wgGroupPermissions;
 402+ $dbGroupPerms = $this->loadGroupPermissions();
 403+
 404+ foreach( $dbGroupPerms as $group => $rights ) {
 405+ foreach( $rights as $right => $value ) {
 406+ if ($value || $wgAllowDBRightSubtraction)
 407+ $groupPerms[$group][$right] = $value;
 408+ }
 409+ }
 410+
 411+ return $groupPerms;
 412+ }
 413+
 414+ function canEditRights( $user ) {
 415+ return $user->isAllowed( 'grouprights' );
 416+ }
 417+
 418+ /**
 419+ * Load all group permissions from the database.
 420+ */
 421+ function loadGroupPermissions( ) {
 422+ static $groupPerms = null;
 423+
 424+ // In-process caching...
 425+ if ( is_array($groupPerms) ) {
 426+ return $groupPerms;
 427+ }
 428+
 429+ // Memcached caching
 430+ global $wgMemc;
 431+ $groupPerms = $wgMemc->get( wfMemcKey( 'grouprights' ) );
 432+ if ( is_array( $groupPerms ) ) {
 433+ return $groupPerms;
 434+ }
 435+
 436+ // Fetch from DB
 437+
 438+ $groupPerms = array();
 439+
 440+ $dbr = wfGetDB( DB_SLAVE );
 441+ $res = $dbr->select( 'group_rights', '*', array(), __METHOD__ );
 442+
 443+ while ($row = $dbr->fetchObject( $res ) ) {
 444+ $groupPerms[$row->gr_group][$row->gr_right] = (bool)$row->gr_enabled;
 445+ }
 446+
 447+ $wgMemc->set( wfMemcKey( 'grouprights' ), $groupPerms );
 448+
 449+ return $groupPerms;
 450+ }
 451+
 452+ function getUserGroups( $user ) {
 453+ if ( !isset( $user->mGroups ) || is_null( $user->mGroups ) ) {
 454+ $dbr = wfGetDB( DB_MASTER );
 455+ $res = $dbr->select( 'user_groups',
 456+ array( 'ug_group' ),
 457+ array( 'ug_user' => $user->getId() ),
 458+ __METHOD__ );
 459+ $this->mGroups = array();
 460+ while( $row = $dbr->fetchObject( $res ) ) {
 461+ $user->mGroups[] = $row->ug_group;
 462+ }
 463+
 464+ $user->mGroups = array_unique( array_filter( $user->mGroups ) );
 465+ }
 466+
 467+ return $user->mGroups;
 468+ }
 469+
 470+ function getAllChangeableGroups() {
 471+ static $changeableGroups = null;
 472+
 473+ // In-process caching
 474+ if ( is_array( $changeableGroups ) ) {
 475+ return $changeableGroups;
 476+ }
 477+
 478+ global $wgMemc;
 479+ $changeableGroups = $wgMemc->get( wfMemcKey( 'changeablegroups' ) );
 480+ if ( is_array( $changeableGroups ) ) {
 481+ return $changeableGroups;
 482+ }
 483+
 484+ // Fetch from DB
 485+ $changeableGroups = array();
 486+ $groupTemplate = array( 'add' => array(), 'remove' => array(), 'add-self' => array(), 'remove-self' => array() );
 487+
 488+ $dbr = wfGetDB( DB_SLAVE );
 489+ $res = $dbr->select( 'changeable_groups', '*', array(), __METHOD__ );
 490+
 491+ while ( $row = $dbr->fetchObject( $res ) ) {
 492+ $changer = $row->cg_changer;
 493+ $group = $row->cg_group;
 494+ $action = $row->cg_action;
 495+
 496+ if ( !isset( $changeableGroups[$changer] ) || !is_array( $changeableGroups[$changer] ) ) {
 497+ $changeableGroups[$changer] = $groupTemplate;
 498+ }
 499+
 500+ if ($changer && $group && $action)
 501+ $changeableGroups[$changer][$action][] = $group;
 502+ }
 503+
 504+ // People who can change all groups.
 505+ foreach( $this->getGroupsWithPermission( 'userrights' ) as $group ) {
 506+ $changeableGroups[$group] = array_fill_keys( array( 'add', 'remove' ), array_diff( $this->getAllGroups(), User::getImplicitGroups() ) );
 507+ $changeableGroups[$group] = array_merge_recursive( $groupTemplate, $changeableGroups[$group] );
 508+ }
 509+
 510+ $wgMemc->set( wfMemcKey('changeablegroups'), $changeableGroups );
 511+
 512+ return $changeableGroups;
 513+ }
 514+
 515+ function setChangeableGroups( $group, $addable, $removable, $addself, $removeself ) {
 516+ $orig = $this->getChangeableGroups( array( $group ));
 517+
 518+ $rows = array();
 519+ $delete = array();
 520+
 521+ if ( count( array_diff( $addable, $orig['add'] ) ) ) {
 522+ $delete[] = 'add';
 523+
 524+ foreach( $addable as $cgroup ) {
 525+ $rows[] = array( 'cg_changer' => $group, 'cg_group' => $cgroup, 'cg_action' => 'add' );
 526+ }
 527+ }
 528+
 529+ if ( count( array_diff( $removable, $orig['remove'] ) ) ) {
 530+ $delete[] = 'remove';
 531+
 532+ foreach( $removable as $cgroup ) {
 533+ $rows[] = array( 'cg_changer' => $group, 'cg_group' => $cgroup, 'cg_action' => 'remove' );
 534+ }
 535+ }
 536+
 537+ if ( count( array_diff( $addself, $orig['add-self'] ) ) ) {
 538+ $delete[] = 'add-self';
 539+
 540+ foreach( $addself as $cgroup ) {
 541+ $rows[] = array( 'cg_changer' => $group, 'cg_group' => $cgroup, 'cg_action' => 'add-self' );
 542+ }
 543+ }
 544+
 545+ if ( count( array_diff( $removeself, $orig['remove-self'] ) ) ) {
 546+ $delete[] = 'remove-self';
 547+
 548+ foreach( $removeself as $cgroup ) {
 549+ $rows[] = array( 'cg_changer' => $group, 'cg_group' => $cgroup, 'cg_action' => 'remove-self' );
 550+ }
 551+ }
 552+
 553+ $dbw = wfGetDB( DB_MASTER );
 554+
 555+ $dbw->begin();
 556+ $dbw->delete( 'changeable_groups', array( 'cg_changer' => $group, 'cg_action' => $delete ), __METHOD__ );
 557+ $dbw->insert( 'changeable_groups', $rows, __METHOD__ );
 558+ $dbw->commit();
 559+ $this->invalidateGroupCache( $group );
 560+ }
 561+
 562+ function setRightsStatus( $group, $rights ) {
 563+ $dbw = wfGetDB( DB_MASTER );
 564+
 565+ $toDelete = array();
 566+ $toSet = array();
 567+ foreach( $rights as $right => $status ) {
 568+ if ($this->rightInConfig( $group, $right ) == $status) {
 569+ $toDelete[] = $right;
 570+ } else {
 571+ $toSet[] = array( 'gr_group' => $group, 'gr_right' => $right, 'gr_enabled' => $status );
 572+ }
 573+ }
 574+
 575+ if ( count($toDelete) )
 576+ $dbw->delete( 'group_rights', array( 'gr_group' => $group, 'gr_right' => $toDelete), __METHOD__ );
 577+
 578+ global $wgAllowDBRightSubtraction;
 579+ if ( $wgAllowDBRightSubtraction && count($toSet) ) {
 580+ $dbw->replace( 'group_rights', 'gr_group,gr_right', $toSet, __METHOD__ );
 581+ }
 582+
 583+ $this->invalidateGroupCache( $group );
 584+ }
 585+
 586+ protected function rightInConfig( $group, $right ) {
 587+ global $wgGroupPermissions;
 588+
 589+ return isset($wgGroupPermissions[$group]) && isset($wgGroupPermissions[$group][$right]) && $wgGroupPermissions[$group][$right];
 590+ }
 591+
 592+ function changeUserGroups( $user, $addgroups, $removegroups, $reason, $doer ) {
 593+ parent::changeUserGroups( $user, $addgroups, $removegroups, $reason, $doer );
 594+ wfRunHooks( 'UserRights', array( &$user, $addgroups, $removegroups, $reason, $doer ) );
 595+ }
 596+
 597+ function addUserGroups( $user, $groups ) {
 598+ $groups = array_filter( $groups );
 599+
 600+ if (!count($groups))
 601+ return;
 602+
 603+ $dbw = wfGetDB( DB_MASTER );
 604+
 605+ $rows = array();
 606+
 607+ foreach( $groups as $group ) {
 608+ $rows[] = array( 'ug_user' => $user->getId(), 'ug_group' => $group );
 609+ }
 610+
 611+ $dbw->insert( 'user_groups',
 612+ $rows,
 613+ __METHOD__,
 614+ array( 'IGNORE' ) );
 615+ }
 616+
 617+ function removeUserGroups( $user, $groups ) {
 618+ $groups = array_filter( $groups );
 619+
 620+ if (!count($groups))
 621+ return;
 622+
 623+ $dbw = wfGetDB( DB_MASTER );
 624+ $dbw->delete( 'user_groups',
 625+ array(
 626+ 'ug_user' => $user->getID(),
 627+ 'ug_group' => $groups,
 628+ ),
 629+ __METHOD__ );
 630+
 631+ $user->invalidateCache();
 632+ }
 633+
 634+ function invalidateGroupCache( $group ) {
 635+ global $wgMemc;
 636+
 637+ $wgMemc->delete( wfMemcKey( 'changeablegroups' ) );
 638+ $wgMemc->delete( wfMemcKey( 'grouprights' ) );
 639+ }
 640+
 641+ function invalidateUserCache( $user ) {
 642+ $user->invalidateCache();
 643+ }
 644+
 645+ function rightEditable( $group, $right ) {
 646+ global $wgAllowDBRightSubtraction;
 647+
 648+ return $wgAllowDBRightSubtraction || !$this->rightInConfig( $group, $right );
 649+ }
 650+
 651+ function addGroupLogEntry( $group, $addRights, $removeRights, $reason, $user ) {
 652+ global $wgRequest,$wgUser;
 653+
 654+ if ($user == null)
 655+ $user = $wgUser;
 656+
 657+ $log = new LogPage( 'rights' );
 658+
 659+ $log->addEntry( 'grprights',
 660+ SpecialPage::getTitleFor( 'ListUsers', $group ),
 661+ $reason,
 662+ array(
 663+ $this->makeRightsList( $addRights ),
 664+ $this->makeRightsList( $removeRights )
 665+ )
 666+ , $user);
 667+ }
 668+
 669+ function addUserGroupsLogEntry( $user, $addgroups, $removegroups, $reason, $doer ) {
 670+ global $wgRequest;
 671+ $log = new LogPage( 'rights' );
 672+
 673+ $log->addEntry( 'rights2',
 674+ $user->getUserPage(),
 675+ $reason,
 676+ array(
 677+ $this->makeGroupNameListForLog( $addgroups ),
 678+ $this->makeGroupNameListForLog( $removegroups )
 679+ )
 680+ );
 681+ }
 682+
 683+ function makeGroupNameListForLog( $ids ) {
 684+ if( empty( $ids ) ) {
 685+ return '';
 686+ } else {
 687+ return implode( ', ', $ids );
 688+ }
 689+ }
 690+
 691+ function fetchUser( $username ) {
 692+ if( $username{0} == '#' ) {
 693+ // Numeric ID can be specified...
 694+ // We'll do a lookup for the name internally.
 695+ $id = intval( substr( $username, 1 ) );
 696+
 697+ $username = User::whoIs( $id );
 698+
 699+ if( !$username ) {
 700+ return null;
 701+ }
 702+ }
 703+
 704+ $user = User::newFromName( $username );
 705+
 706+ if (!$user || $user->isAnon()) {
 707+ return null;
 708+ }
 709+
 710+ return $user;
 711+ }
 712+
 713+ function getChangeableGroupsForUser( $user ) {
 714+ $cg = parent::getChangeableGroupsForUser( $user );
 715+
 716+ $allgroups = array_diff( $this->getAllGroups(), User::getImplicitGroups() );
 717+
 718+ if ($user->isAllowed( 'userrights' )) {
 719+ $cg = array_merge_recursive( $cg, array_fill_keys( array( 'add', 'remove', 'add-self', 'remove-self' ), $allgroups ) );
 720+ }
 721+
 722+ return $cg;
 723+ }
 724+}
Property changes on: branches/group_rights/includes/RightsManager.php
___________________________________________________________________
Added: svn:eol-style
1725 + native
Index: branches/group_rights/includes/SpecialPage.php
@@ -148,6 +148,7 @@
149149 'Lockdb' => array( 'SpecialPage', 'Lockdb', 'siteadmin' ),
150150 'Unlockdb' => array( 'SpecialPage', 'Unlockdb', 'siteadmin' ),
151151 'Userrights' => 'UserrightsPage',
 152+ 'GroupRights' => 'SpecialGroupRights',
152153 'MIMEsearch' => array( 'SpecialPage', 'MIMEsearch' ),
153154 'FileDuplicateSearch' => array( 'SpecialPage', 'FileDuplicateSearch' ),
154155 'Unwatchedpages' => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ),
Index: branches/group_rights/includes/LogPage.php
@@ -171,7 +171,7 @@
172172 $rv = wfMsg( $wgLogActions[$key] );
173173 } else {
174174 $titleLink = self::getTitleLink( $type, $skin, $title, $params );
175 - if( $key == 'rights/rights' ) {
 175+ if( $key == 'rights/rights' || $key = 'rights/rights2' ) {
176176 if( $skin ) {
177177 $rightsnone = wfMsg( 'rightsnone' );
178178 foreach ( $params as &$param ) {
Index: branches/group_rights/languages/messages/MessagesEn.php
@@ -416,7 +416,7 @@
417417 'Import' => array( 'Import' ),
418418 'Lockdb' => array( 'LockDB' ),
419419 'Unlockdb' => array( 'UnlockDB' ),
420 - 'Userrights' => array( 'UserRights' ),
 420+ 'Userrights' => array( 'UserGroups', 'UserRights' ),
421421 'MIMEsearch' => array( 'MIMESearch' ),
422422 'FileDuplicateSearch' => array( 'FileDuplicateSearch' ),
423423 'Unwatchedpages' => array( 'UnwatchedPages' ),
@@ -1538,12 +1538,12 @@
15391539 'files' => 'Files',
15401540
15411541 # User rights
1542 -'userrights' => 'User rights management', # Not used as normal message but as header for the special page itself
 1542+'userrights' => 'Edit user groups', # Not used as normal message but as header for the special page itself
15431543 'userrights-summary' => '', # do not translate or duplicate this message to other languages
15441544 'userrights-lookup-user' => 'Manage user groups',
15451545 'userrights-user-editname' => 'Enter a username:',
15461546 'editusergroup' => 'Edit user groups',
1547 -'editinguser' => "Changing user rights of user '''[[User:$1|$1]]''' ([[User talk:$1|{{int:talkpagelinktext}}]] | [[Special:Contributions/$1|{{int:contribslink}}]])",
 1547+'editinguser' => "Changing group membership for user '''[[User:$1|$1]]''' ([[User talk:$1|{{int:talkpagelinktext}}]] | [[Special:Contributions/$1|{{int:contribslink}}]])",
15481548 'userrights-editusergroup' => 'Edit user groups',
15491549 'saveusergroups' => 'Save user groups',
15501550 'userrights-groupsmember' => 'Member of:',
@@ -1552,13 +1552,15 @@
15531553 * An unchecked box means the user is not in that group.
15541554 * A * indicates that you cannot remove the group once you have added it, or vice versa.',
15551555 'userrights-reason' => 'Reason for change:',
1556 -'userrights-no-interwiki' => 'You do not have permission to edit user rights on other wikis.',
 1556+'userrights-no-interwiki' => 'You do not have permission to edit user group membership on other wikis.',
15571557 'userrights-nodatabase' => 'Database $1 does not exist or is not local.',
1558 -'userrights-nologin' => 'You must [[Special:UserLogin|log in]] with an administrator account to assign user rights.',
1559 -'userrights-notallowed' => 'Your account does not have permission to assign user rights.',
 1558+'userrights-nologin' => 'You must [[Special:UserLogin|log in]] with an administrator account to assign user groups.',
 1559+'userrights-notallowed' => 'Your account does not have permission to assign user groups.',
15601560 'userrights-changeable-col' => 'Groups you can change',
15611561 'userrights-unchangeable-col' => 'Groups you cannot change',
15621562 'userrights-irreversible-marker' => '$1*', # only translate this message to other languages if you have to change it
 1563+'userrights-backendselect-subtitle' => 'Select a rights manager',
 1564+'userrights-backendselect-text' => "You are permitted to edit user groups for more than one rights management system. Please select the rights management system for which you wish to edit user groups from the list below:",
15631565
15641566 # Groups
15651567 'group' => 'Group:',
@@ -1633,14 +1635,16 @@
16341636 'right-unwatchedpages' => 'View a list of unwatched pages',
16351637 'right-trackback' => 'Submit a trackback',
16361638 'right-mergehistory' => 'Merge the history of pages',
1637 -'right-userrights' => 'Edit all user rights',
 1639+'right-userrights' => 'Modify membership of all user groups',
16381640 'right-userrights-interwiki' => 'Edit user rights of users on other wikis',
16391641 'right-siteadmin' => 'Lock and unlock the database',
 1642+'right-grouprights' => 'Edit permissions assigned to local groups',
16401643
16411644 # User rights log
16421645 'rightslog' => 'User rights log',
16431646 'rightslogtext' => 'This is a log of changes to user rights.',
16441647 'rightslogentry' => 'changed group membership for $1 from $2 to $3',
 1648+'rightslogentry2' => 'changed group membership for $1: added $2, removed $3',
16451649 'rightsnone' => '(none)',
16461650
16471651 # Recent changes
@@ -2152,6 +2156,10 @@
21532157 'listgrouprights-removegroup' => 'Can remove {{PLURAL:$2|group|groups}}: $1',
21542158 'listgrouprights-addgroup-all' => 'Can add all groups',
21552159 'listgrouprights-removegroup-all' => 'Can remove all groups',
 2160+'listgrouprights-addselfgroup-all'=> 'Can add all groups to self',
 2161+'listgrouprights-addselfgroup' => 'Can add {{PLURAL:$2|group|groups}} to self: $1',
 2162+'listgrouprights-removeselfgroup-all' => 'Can remove all groups from self',
 2163+'listgrouprights-removeselfgroup' => 'Can add {{PLURAL:$2|group|groups}} from self: $1',
21562164
21572165 # E-mail user
21582166 'mailnologin' => 'No send address',
@@ -3662,6 +3670,46 @@
36633671 'blankpage' => 'Blank page',
36643672 'intentionallyblankpage' => 'This page is intentionally left blank',
36653673
 3674+# Special:GroupRights
 3675+'grouprights' => "Edit group rights",
 3676+'grouprights-grouplist' => 'The following groups have been configured.
 3677+You may view and edit the rights assigned to any group.
 3678+A group may be deleted by removing all rights from it.',
 3679+'grouprights-editlink' => 'view/edit',
 3680+'grouprights-existinggroup-legend' => 'Existing groups',
 3681+'grouprights-newgroup-legend' => 'Create a new group',
 3682+'grouprights-newgroup-intro' => 'You can use this form to assign rights to a new group.
 3683+Note that a group does not exist unless it has rights assigned to it.',
 3684+'grouprights-newgroupname' => 'New group name:',
 3685+'grouprights-creategroup-submit' => 'Assign rights',
 3686+'grouprights-subtitle' => 'Editing $1',
 3687+'grouprights-fieldset' => 'Rights for $1',
 3688+'grouprights-editgroup-name' => 'Name of group:',
 3689+'grouprights-editgroup-display' => 'Localised name of group:',
 3690+'grouprights-editgroup-display-edit' => '$2 ([[MediaWiki:Group-$1|edit]])',
 3691+'grouprights-editgroup-member' => 'Localised name of group members:',
 3692+'grouprights-editgroup-member-edit' => '$2 ([[MediaWiki:Group-$1-member|edit]])',
 3693+'grouprights-editgroup-members' => 'Member list:',
 3694+'grouprights-editgroup-members-link' => '[[Special:ListUsers/$1|List of users with $2 rights]]',
 3695+'grouprights-editgroup-restrictions' => 'Set of wikis where this group is active:',
 3696+'grouprights-editgroup-noset' => '(none)',
 3697+'grouprights-editgroup-submit' => 'Save changes to group rights',
 3698+'grouprights-editgroup-perms' => 'Assigned rights:',
 3699+'grouprights-editgroup-reason' => 'Reason for change:',
 3700+'grouprights-editgroup-success' => 'Group rights changed',
 3701+'grouprights-editgroup-success-text' => 'You have successfully changed the group rights for the $1 group.',
 3702+'grouprightslog' => 'changed group rights for $1. Added: $2; Removed: $3',
 3703+'grouprights-return' => 'Return to group rights',
 3704+'grouprights-backendselect-subtitle' => 'Select a rights manager',
 3705+'grouprights-backendselect-text' => "You are permitted to edit group rights for more than one rights management system. Please select the rights management system for which you wish to edit group rights from the list below:",
 3706+'grouprights-editgroup' => "'''Changeable groups'''",
 3707+'grouprights-editgroup-add' => 'Can add groups:',
 3708+'grouprights-editgroup-remove' => 'Can remove groups:',
 3709+'grouprights-editgroup-add-self' => 'Can add groups to self:',
 3710+'grouprights-editgroup-remove-self' => 'Can remove groups from self:',
 3711+
 3712+'rights-backend-RightsManagerConfigDB' => "Users and groups for this MediaWiki site.",
 3713+
36663714 # External image whitelist
36673715 'external_image_whitelist' => ' #Leave this line exactly as it is<pre>
36683716 #Put regular expression fragments (just the part that goes between the //) below

Status & tagging log