r48746 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r48745‎ | r48746 | r48747 >
Date:16:04, 24 March 2009
Author:catrope
Status:resolved (Comments)
Tags:
Comment:
* API: (bug 15935) Add action=userrights to the API
* Add ustoken=userrights to list=users
* Move the non-UI part of UserrightsPage::saveUserGroups() to the static and more generic doSaveUserGroups()
* Add a $reason parameter to UserrightsPage::addLogEntry() and make it and its helpers static
* Move UserrightsPage::changeableGroups() and changeableByGroup() to the User class and make the latter static
* In doSaveUserGroups(), drop groups that the user doesn't have from $remove (and those that they do have from $add), and return array($add, $remove)
* Fix up a comment in ApiQueryRecentChanges
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/User.php (modified) (history)
  • /trunk/phase3/includes/api/ApiMain.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryRecentChanges.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryUsers.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialUserrights.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/User.php
@@ -3231,8 +3231,116 @@
32323232 return $text;
32333233 }
32343234 }
 3235+
 3236+ /**
 3237+ * Returns an array of the groups that a particular group can add/remove.
 3238+ *
 3239+ * @param $group String: the group to check for whether it can add/remove
 3240+ * @return Array array( 'add' => array( addablegroups ),
 3241+ * 'remove' => array( removablegroups ),
 3242+ * 'add-self' => array( addablegroups to self),
 3243+ * 'remove-self' => array( removable groups from self) )
 3244+ */
 3245+ static function changeableByGroup( $group ) {
 3246+ global $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
32353247
 3248+ $groups = array( 'add' => array(), 'remove' => array(), 'add-self' => array(), 'remove-self' => array() );
 3249+ if( empty($wgAddGroups[$group]) ) {
 3250+ // Don't add anything to $groups
 3251+ } elseif( $wgAddGroups[$group] === true ) {
 3252+ // You get everything
 3253+ $groups['add'] = self::getAllGroups();
 3254+ } elseif( is_array($wgAddGroups[$group]) ) {
 3255+ $groups['add'] = $wgAddGroups[$group];
 3256+ }
 3257+
 3258+ // Same thing for remove
 3259+ if( empty($wgRemoveGroups[$group]) ) {
 3260+ } elseif($wgRemoveGroups[$group] === true ) {
 3261+ $groups['remove'] = self::getAllGroups();
 3262+ } elseif( is_array($wgRemoveGroups[$group]) ) {
 3263+ $groups['remove'] = $wgRemoveGroups[$group];
 3264+ }
 3265+
 3266+ // Re-map numeric keys of AddToSelf/RemoveFromSelf to the 'user' key for backwards compatibility
 3267+ if( empty($wgGroupsAddToSelf['user']) || $wgGroupsAddToSelf['user'] !== true ) {
 3268+ foreach($wgGroupsAddToSelf as $key => $value) {
 3269+ if( is_int($key) ) {
 3270+ $wgGroupsAddToSelf['user'][] = $value;
 3271+ }
 3272+ }
 3273+ }
 3274+
 3275+ if( empty($wgGroupsRemoveFromSelf['user']) || $wgGroupsRemoveFromSelf['user'] !== true ) {
 3276+ foreach($wgGroupsRemoveFromSelf as $key => $value) {
 3277+ if( is_int($key) ) {
 3278+ $wgGroupsRemoveFromSelf['user'][] = $value;
 3279+ }
 3280+ }
 3281+ }
 3282+
 3283+ // Now figure out what groups the user can add to him/herself
 3284+ if( empty($wgGroupsAddToSelf[$group]) ) {
 3285+ } elseif( $wgGroupsAddToSelf[$group] === true ) {
 3286+ // No idea WHY this would be used, but it's there
 3287+ $groups['add-self'] = User::getAllGroups();
 3288+ } elseif( is_array($wgGroupsAddToSelf[$group]) ) {
 3289+ $groups['add-self'] = $wgGroupsAddToSelf[$group];
 3290+ }
 3291+
 3292+ if( empty($wgGroupsRemoveFromSelf[$group]) ) {
 3293+ } elseif( $wgGroupsRemoveFromSelf[$group] === true ) {
 3294+ $groups['remove-self'] = User::getAllGroups();
 3295+ } elseif( is_array($wgGroupsRemoveFromSelf[$group]) ) {
 3296+ $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
 3297+ }
 3298+
 3299+ return $groups;
 3300+ }
 3301+
32363302 /**
 3303+ * Returns an array of groups that this user can add and remove
 3304+ * @return Array array( 'add' => array( addablegroups ),
 3305+ * 'remove' => array( removablegroups ),
 3306+ * 'add-self' => array( addablegroups to self),
 3307+ * 'remove-self' => array( removable groups from self) )
 3308+ */
 3309+ function changeableGroups() {
 3310+ if( $this->isAllowed( 'userrights' ) ) {
 3311+ // This group gives the right to modify everything (reverse-
 3312+ // compatibility with old "userrights lets you change
 3313+ // everything")
 3314+ // Using array_merge to make the groups reindexed
 3315+ $all = array_merge( User::getAllGroups() );
 3316+ return array(
 3317+ 'add' => $all,
 3318+ 'remove' => $all,
 3319+ 'add-self' => array(),
 3320+ 'remove-self' => array()
 3321+ );
 3322+ }
 3323+
 3324+ // Okay, it's not so simple, we will have to go through the arrays
 3325+ $groups = array(
 3326+ 'add' => array(),
 3327+ 'remove' => array(),
 3328+ 'add-self' => array(),
 3329+ 'remove-self' => array() );
 3330+ $addergroups = $this->getEffectiveGroups();
 3331+
 3332+ foreach ($addergroups as $addergroup) {
 3333+ $groups = array_merge_recursive(
 3334+ $groups, $this->changeableByGroup($addergroup)
 3335+ );
 3336+ $groups['add'] = array_unique( $groups['add'] );
 3337+ $groups['remove'] = array_unique( $groups['remove'] );
 3338+ $groups['add-self'] = array_unique( $groups['add-self'] );
 3339+ $groups['remove-self'] = array_unique( $groups['remove-self'] );
 3340+ }
 3341+ return $groups;
 3342+ }
 3343+
 3344+ /**
32373345 * Increment the user's edit-count field.
32383346 * Will have no effect for anonymous users.
32393347 */
Index: trunk/phase3/includes/api/ApiQueryRecentChanges.php
@@ -43,12 +43,13 @@
4444 private $fld_comment = false, $fld_user = false, $fld_flags = false,
4545 $fld_timestamp = false, $fld_title = false, $fld_ids = false,
4646 $fld_sizes = false;
47 -
 47+ /**
 48+ * Get an array mapping token names to their handler functions.
 49+ * The prototype for a token function is func($pageid, $title, $rc)
 50+ * it should return a token or false (permission denied)
 51+ * @return array(tokenname => function)
 52+ */
4853 protected function getTokenFunctions() {
49 - // tokenname => function
50 - // function prototype is func($pageid, $title, $rev)
51 - // should return token or false
52 -
5354 // Don't call the hooks twice
5455 if(isset($this->tokenFunctions))
5556 return $this->tokenFunctions;
Index: trunk/phase3/includes/api/ApiMain.php
@@ -80,6 +80,7 @@
8181 'watch' => 'ApiWatch',
8282 'patrol' => 'ApiPatrol',
8383 'import' => 'ApiImport',
 84+ 'userrights' => 'ApiUserrights',
8485 );
8586
8687 /**
Index: trunk/phase3/includes/api/ApiQueryUsers.php
@@ -39,7 +39,37 @@
4040 public function __construct($query, $moduleName) {
4141 parent :: __construct($query, $moduleName, 'us');
4242 }
 43+
 44+ /**
 45+ * Get an array mapping token names to their handler functions.
 46+ * The prototype for a token function is func($user)
 47+ * it should return a token or false (permission denied)
 48+ * @return array(tokenname => function)
 49+ */
 50+ protected function getTokenFunctions() {
 51+ // Don't call the hooks twice
 52+ if(isset($this->tokenFunctions))
 53+ return $this->tokenFunctions;
4354
 55+ // If we're in JSON callback mode, no tokens can be obtained
 56+ if(!is_null($this->getMain()->getRequest()->getVal('callback')))
 57+ return array();
 58+
 59+ $this->tokenFunctions = array(
 60+ 'userrights' => array( 'ApiQueryUsers', 'getUserrightsToken' ),
 61+ );
 62+ wfRunHooks('APIQueryUsersTokens', array(&$this->tokenFunctions));
 63+ return $this->tokenFunctions;
 64+ }
 65+
 66+ public static function getUserrightsToken($user)
 67+ {
 68+ global $wgUser;
 69+ // Since the permissions check for userrights is non-trivial,
 70+ // don't bother with it here
 71+ return $wgUser->editToken($user->getName());
 72+ }
 73+
4474 public function execute() {
4575 $params = $this->extractRequestParams();
4676 $result = $this->getResult();
@@ -115,6 +145,18 @@
116146 }
117147 if(isset($this->prop['emailable']) && $user->canReceiveEmail())
118148 $data[$name]['emailable'] = '';
 149+ if(!is_null($params['token']))
 150+ {
 151+ $tokenFunctions = $this->getTokenFunctions();
 152+ foreach($params['token'] as $t)
 153+ {
 154+ $val = call_user_func($tokenFunctions[$t], $user);
 155+ if($val === false)
 156+ $this->setWarning("Action '$t' is not allowed for the current user");
 157+ else
 158+ $data[$name][$t . 'token'] = $val;
 159+ }
 160+ }
119161 }
120162 }
121163 // Second pass: add result data to $retval
@@ -153,7 +195,11 @@
154196 ),
155197 'users' => array(
156198 ApiBase :: PARAM_ISMULTI => true
157 - )
 199+ ),
 200+ 'token' => array(
 201+ ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()),
 202+ ApiBase :: PARAM_ISMULTI => true
 203+ ),
158204 );
159205 }
160206
@@ -167,7 +213,8 @@
168214 ' registration - adds the user\'s registration timestamp',
169215 ' emailable - tags if the user can and wants to receive e-mail through [[Special:Emailuser]]',
170216 ),
171 - 'users' => 'A list of users to obtain the same information for'
 217+ 'users' => 'A list of users to obtain the same information for',
 218+ 'token' => 'Which tokens to obtain for each user',
172219 );
173220 }
174221
Index: trunk/phase3/includes/AutoLoader.php
@@ -290,6 +290,7 @@
291291 'ApiRollback' => 'includes/api/ApiRollback.php',
292292 'ApiUnblock' => 'includes/api/ApiUnblock.php',
293293 'ApiUndelete' => 'includes/api/ApiUndelete.php',
 294+ 'ApiUserrights' => 'includes/api/ApiUserrights.php',
294295 'ApiWatch' => 'includes/api/ApiWatch.php',
295296 'Services_JSON' => 'includes/api/ApiFormatJson_json.php',
296297 'Services_JSON_Error' => 'includes/api/ApiFormatJson_json.php',
Index: trunk/phase3/includes/specials/SpecialUserrights.php
@@ -149,29 +149,46 @@
150150 $removegroup[] = $group;
151151 }
152152 }
 153+ self::doSaveUserGroups( $user, $addgroup, $removegroup, $reason );
 154+ }
 155+
 156+ /**
 157+ * Save user groups changes in the database.
 158+ *
 159+ * @param $user User object
 160+ * @param $add Array of groups to add
 161+ * @param $remove Array of groups to remove
 162+ * @param $reason String: reason for group change
 163+ * @return Array: Tuple of added, then removed groups
 164+ */
 165+ static function doSaveUserGroups( User $user, $add, $remove, $reason = '' ) {
 166+ global $wgUser;
153167
154168 // Validate input set...
155 - $changeable = $this->changeableGroups();
156 - $addable = array_merge( $changeable['add'], $this->isself ? $changeable['add-self'] : array() );
157 - $removable = array_merge( $changeable['remove'], $this->isself ? $changeable['remove-self'] : array() );
 169+ $isself = ($user->getName() == $wgUser->getName());
 170+ $groups = $user->getGroups();
 171+ $changeable = $wgUser->changeableGroups();
 172+ $addable = array_merge( $changeable['add'], $isself ? $changeable['add-self'] : array() );
 173+ $removable = array_merge( $changeable['remove'], $isself ? $changeable['remove-self'] : array() );
158174
159 - $removegroup = array_unique(
160 - array_intersect( (array)$removegroup, $removable ) );
161 - $addgroup = array_unique(
162 - array_intersect( (array)$addgroup, $addable ) );
 175+ $remove = array_unique(
 176+ array_intersect( (array)$remove, $removable, $groups ) );
 177+ $add = array_unique( array_diff(
 178+ array_intersect( (array)$add, $addable ),
 179+ $groups ) );
163180
164181 $oldGroups = $user->getGroups();
165182 $newGroups = $oldGroups;
166183 // remove then add groups
167 - if( $removegroup ) {
168 - $newGroups = array_diff($newGroups, $removegroup);
169 - foreach( $removegroup as $group ) {
 184+ if( $remove ) {
 185+ $newGroups = array_diff($newGroups, $remove);
 186+ foreach( $remove as $group ) {
170187 $user->removeGroup( $group );
171188 }
172189 }
173 - if( $addgroup ) {
174 - $newGroups = array_merge($newGroups, $addgroup);
175 - foreach( $addgroup as $group ) {
 190+ if( $add ) {
 191+ $newGroups = array_merge($newGroups, $add);
 192+ foreach( $add as $group ) {
176193 $user->addGroup( $group );
177194 }
178195 }
@@ -182,29 +199,27 @@
183200
184201 wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) );
185202 wfDebug( 'newGroups: ' . print_r( $newGroups, true ) );
186 - if( $user instanceof User ) {
187 - // hmmm
188 - wfRunHooks( 'UserRights', array( &$user, $addgroup, $removegroup ) );
189 - }
 203+ wfRunHooks( 'UserRights', array( &$user, $add, $remove ) );
190204
191205 if( $newGroups != $oldGroups ) {
192 - $this->addLogEntry( $user, $oldGroups, $newGroups );
 206+ self::addLogEntry( $user, $oldGroups, $newGroups, $reason );
193207 }
 208+ return array( $add, $remove );
194209 }
 210+
195211
196212 /**
197213 * Add a rights log entry for an action.
198214 */
199 - function addLogEntry( $user, $oldGroups, $newGroups ) {
200 - global $wgRequest;
 215+ static function addLogEntry( $user, $oldGroups, $newGroups, $reason ) {
201216 $log = new LogPage( 'rights' );
202217
203218 $log->addEntry( 'rights',
204219 $user->getUserPage(),
205 - $wgRequest->getText( 'user-reason' ),
 220+ $reason,
206221 array(
207 - $this->makeGroupNameListForLog( $oldGroups ),
208 - $this->makeGroupNameListForLog( $newGroups )
 222+ self::makeGroupNameListForLog( $oldGroups ),
 223+ self::makeGroupNameListForLog( $newGroups )
209224 )
210225 );
211226 }
@@ -293,7 +308,7 @@
294309 return $user;
295310 }
296311
297 - function makeGroupNameList( $ids ) {
 312+ static function makeGroupNameList( $ids ) {
298313 if( empty( $ids ) ) {
299314 return wfMsgForContent( 'rightsnone' );
300315 } else {
@@ -301,11 +316,11 @@
302317 }
303318 }
304319
305 - function makeGroupNameListForLog( $ids ) {
 320+ static function makeGroupNameListForLog( $ids ) {
306321 if( empty( $ids ) ) {
307322 return '';
308323 } else {
309 - return $this->makeGroupNameList( $ids );
 324+ return self::makeGroupNameList( $ids );
310325 }
311326 }
312327
@@ -511,115 +526,20 @@
512527 }
513528
514529 /**
515 - * Returns an array of the groups that the user can add/remove.
 530+ * Returns $wgUser->changeableGroups()
516531 *
517532 * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) , 'add-self' => array( addablegroups to self), 'remove-self' => array( removable groups from self) )
518533 */
519534 function changeableGroups() {
520535 global $wgUser;
521 -
522 - if( $wgUser->isAllowed( 'userrights' ) ) {
523 - // This group gives the right to modify everything (reverse-
524 - // compatibility with old "userrights lets you change
525 - // everything")
526 - // Using array_merge to make the groups reindexed
527 - $all = array_merge( User::getAllGroups() );
528 - return array(
529 - 'add' => $all,
530 - 'remove' => $all,
531 - 'add-self' => array(),
532 - 'remove-self' => array()
533 - );
534 - }
535 -
536 - // Okay, it's not so simple, we will have to go through the arrays
537 - $groups = array(
538 - 'add' => array(),
539 - 'remove' => array(),
540 - 'add-self' => array(),
541 - 'remove-self' => array() );
542 - $addergroups = $wgUser->getEffectiveGroups();
543 -
544 - foreach ($addergroups as $addergroup) {
545 - $groups = array_merge_recursive(
546 - $groups, $this->changeableByGroup($addergroup)
547 - );
548 - $groups['add'] = array_unique( $groups['add'] );
549 - $groups['remove'] = array_unique( $groups['remove'] );
550 - $groups['add-self'] = array_unique( $groups['add-self'] );
551 - $groups['remove-self'] = array_unique( $groups['remove-self'] );
552 - }
553 -
 536+ $groups = $wgUser->changeableGroups();
554537 // Run a hook because we can
555 - wfRunHooks( 'UserrightsChangeableGroups', array( $this, $wgUser, $addergroups, &$groups ) );
556 -
 538+ wfRunHooks( 'UserrightsChangeableGroups', array( $this,
 539+ $wgUser, $wgUser->getEffectiveGroups(), &$groups ) );
557540 return $groups;
558541 }
559542
560543 /**
561 - * Returns an array of the groups that a particular group can add/remove.
562 - *
563 - * @param $group String: the group to check for whether it can add/remove
564 - * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) , 'add-self' => array( addablegroups to self), 'remove-self' => array( removable groups from self) )
565 - */
566 - private function changeableByGroup( $group ) {
567 - global $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
568 -
569 - $groups = array( 'add' => array(), 'remove' => array(), 'add-self' => array(), 'remove-self' => array() );
570 - if( empty($wgAddGroups[$group]) ) {
571 - // Don't add anything to $groups
572 - } elseif( $wgAddGroups[$group] === true ) {
573 - // You get everything
574 - $groups['add'] = User::getAllGroups();
575 - } elseif( is_array($wgAddGroups[$group]) ) {
576 - $groups['add'] = $wgAddGroups[$group];
577 - }
578 -
579 - // Same thing for remove
580 - if( empty($wgRemoveGroups[$group]) ) {
581 - } elseif($wgRemoveGroups[$group] === true ) {
582 - $groups['remove'] = User::getAllGroups();
583 - } elseif( is_array($wgRemoveGroups[$group]) ) {
584 - $groups['remove'] = $wgRemoveGroups[$group];
585 - }
586 -
587 - // Re-map numeric keys of AddToSelf/RemoveFromSelf to the 'user' key for backwards compatibility
588 - if( empty($wgGroupsAddToSelf['user']) || $wgGroupsAddToSelf['user'] !== true ) {
589 - foreach($wgGroupsAddToSelf as $key => $value) {
590 - if( is_int($key) ) {
591 - $wgGroupsAddToSelf['user'][] = $value;
592 - }
593 - }
594 - }
595 -
596 - if( empty($wgGroupsRemoveFromSelf['user']) || $wgGroupsRemoveFromSelf['user'] !== true ) {
597 - foreach($wgGroupsRemoveFromSelf as $key => $value) {
598 - if( is_int($key) ) {
599 - $wgGroupsRemoveFromSelf['user'][] = $value;
600 - }
601 - }
602 - }
603 -
604 - // Now figure out what groups the user can add to him/herself
605 - if( empty($wgGroupsAddToSelf[$group]) ) {
606 - } elseif( $wgGroupsAddToSelf[$group] === true ) {
607 - // No idea WHY this would be used, but it's there
608 - $groups['add-self'] = User::getAllGroups();
609 - } elseif( is_array($wgGroupsAddToSelf[$group]) ) {
610 - $groups['add-self'] = $wgGroupsAddToSelf[$group];
611 - }
612 -
613 - if( empty($wgGroupsRemoveFromSelf[$group]) ) {
614 - } elseif( $wgGroupsRemoveFromSelf[$group] === true ) {
615 - $groups['remove-self'] = User::getAllGroups();
616 - } elseif( is_array($wgGroupsRemoveFromSelf[$group]) ) {
617 - $groups['remove-self'] = $wgGroupsRemoveFromSelf[$group];
618 - }
619 -
620 - return $groups;
621 - }
622 -
623 - /**
624544 * Show a rights log fragment for the specified user
625545 *
626546 * @param $user User to show log for
Index: trunk/phase3/RELEASE-NOTES
@@ -330,6 +330,7 @@
331331 when someone tries to use them
332332 * BREAKING CHANGE: action=purge requires write rights and, for anonymous users,
333333 a POST request
 334+* (bug 15935) Added action=userrights to add/remove users to/from groups
334335
335336 === Languages updated in 1.15 ===
336337

Follow-up revisions

RevisionCommit summaryAuthorDate
r48747Followup to r48746: forgot to add new filecatrope16:06, 24 March 2009
r48778Implement changes from r48746 in subclassers of Userrightspage, avoid warningswerdna01:30, 25 March 2009
r48909Revert r48746 (API userrights). Breaks Special:GlobalGroupMembership by chang...werdna05:59, 27 March 2009
r48970Redo r48746 (API userrights, reverted in r48909 and r48910) in a way that doe...catrope19:08, 28 March 2009

Comments

#Comment by Catrope (talk | contribs)   16:07, 24 March 2009

ApiUserrights.php added in r47847

#Comment by Catrope (talk | contribs)   16:07, 24 March 2009

Oops, r48747

#Comment by Werdna (talk | contribs)   05:37, 27 March 2009

This breaks global rights management at Special:GlobalGroupMembership.

#Comment by Catrope (talk | contribs)   19:16, 28 March 2009

Fixed in r48970, r48971

Status & tagging log