Index: trunk/phase3/includes/SpecialUserrights.php |
— | — | @@ -27,7 +27,7 @@ |
28 | 28 | var $action; |
29 | 29 | |
30 | 30 | /** Constructor*/ |
31 | | - function UserrightsForm ( &$request ) { |
| 31 | + public function __construct( &$request ) { |
32 | 32 | $this->mPosted = $request->wasPosted(); |
33 | 33 | $this->mRequest =& $request; |
34 | 34 | $this->mName = 'userrights'; |
— | — | @@ -94,13 +94,17 @@ |
95 | 95 | if(isset($removegroup)) { |
96 | 96 | $newGroups = array_diff($newGroups, $removegroup); |
97 | 97 | foreach( $removegroup as $group ) { |
98 | | - $u->removeGroup( $group ); |
| 98 | + if ( $this->canRemove( $group ) ) { |
| 99 | + $u->removeGroup( $group ); |
| 100 | + } |
99 | 101 | } |
100 | 102 | } |
101 | 103 | if(isset($addgroup)) { |
102 | 104 | $newGroups = array_merge($newGroups, $addgroup); |
103 | 105 | foreach( $addgroup as $group ) { |
104 | | - $u->addGroup( $group ); |
| 106 | + if ( $this->canAdd( $group ) ) { |
| 107 | + $u->addGroup( $group ); |
| 108 | + } |
105 | 109 | } |
106 | 110 | } |
107 | 111 | $newGroups = array_unique( $newGroups ); |
— | — | @@ -108,7 +112,7 @@ |
109 | 113 | wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) ); |
110 | 114 | wfDebug( 'newGroups: ' . print_r( $newGroups, true ) ); |
111 | 115 | |
112 | | - wfRunHooks( 'UserRights', array( &$u, $addgroup, $removegroup ) ); |
| 116 | + wfRunHooks( 'UserRights', array( &$u, $addgroup, $removegroup ) ); |
113 | 117 | $log = new LogPage( 'rights' ); |
114 | 118 | $log->addEntry( 'rights', Title::makeTitle( NS_USER, $u->getName() ), $reason, array( $this->makeGroupNameList( $oldGroups ), |
115 | 119 | $this->makeGroupNameList( $newGroups ) ) ); |
— | — | @@ -139,7 +143,7 @@ |
140 | 144 | */ |
141 | 145 | function editUserGroupsForm($username) { |
142 | 146 | global $wgOut; |
143 | | - |
| 147 | + |
144 | 148 | $user = User::newFromName($username); |
145 | 149 | if( is_null( $user ) ) { |
146 | 150 | $wgOut->addWikiText( wfMsg( 'nouserspecified' ) ); |
— | — | @@ -149,12 +153,24 @@ |
150 | 154 | return; |
151 | 155 | } |
152 | 156 | |
153 | | - $groups = $user->getGroups(); |
154 | | - $this->showEditUserGroupsForm( $username, $groups ); |
| 157 | + list($addable, $removable) = array_values( $this->changeableGroups() ); |
| 158 | + $removable = array_intersect($removable, $user->getGroups()); // Can't remove groups the user doesn't have |
| 159 | + $addable = array_diff( $addable, $user->getGroups()); // Can't add groups the user does have |
| 160 | + |
| 161 | + $this->showEditUserGroupsForm( $username, $addable, $removable ); |
155 | 162 | } |
156 | | - |
157 | | - function showEditUserGroupsForm( $username, $groups ) { |
| 163 | + |
| 164 | + /** |
| 165 | + * Show the form to edit group memberships. |
| 166 | + * |
| 167 | + * @todo make all CSS-y and semantic |
| 168 | + * @param $username String: Name of user you're editing |
| 169 | + * @param $addable Array: Array of groups that can be added |
| 170 | + * @param $removable Array: Array of groups that can be removed |
| 171 | + */ |
| 172 | + private function showEditUserGroupsForm( $username, $addable, $removable ) { |
158 | 173 | global $wgOut, $wgUser; |
| 174 | + |
159 | 175 | $wgOut->addHTML( |
160 | 176 | Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->action, 'name' => 'editGroup' ) ) . |
161 | 177 | Xml::hidden( 'user-editname', $username ) . |
— | — | @@ -162,14 +178,15 @@ |
163 | 179 | Xml::openElement( 'fieldset' ) . |
164 | 180 | Xml::element( 'legend', array(), wfMsg( 'userrights-editusergroup' ) ) . |
165 | 181 | $wgOut->parse( wfMsg( 'editinguser', $username ) ) . |
| 182 | + $this->explainRights() . |
166 | 183 | "<table border='0'> |
167 | 184 | <tr> |
168 | 185 | <td></td> |
169 | 186 | <td> |
170 | 187 | <table width='400'> |
171 | 188 | <tr> |
172 | | - <td width='50%'>" . HTMLSelectGroups( 'member', $this->mName.'-groupsmember', $groups, true, 6 ) . "</td> |
173 | | - <td width='50%'>" . HTMLSelectGroups( 'available', $this->mName.'-groupsavailable', $groups, true, 6, true) . "</td> |
| 189 | + <td width='50%'>" . $this->removeSelect( $removable ) . "</td> |
| 190 | + <td width='50%'>" . $this->addSelect( $addable ) . "</td> |
174 | 191 | </tr> |
175 | 192 | </table> |
176 | 193 | </tr> |
— | — | @@ -197,5 +214,181 @@ |
198 | 215 | Xml::closeElement( 'form' ) . "\n" |
199 | 216 | ); |
200 | 217 | } |
| 218 | + |
| 219 | + /** |
| 220 | + * Explains what groups the user can add and remove, and why. |
| 221 | + * |
| 222 | + * @return string Explanatory sanitized HTML message |
| 223 | + */ |
| 224 | + private function explainRights() { |
| 225 | + global $wgUser; |
| 226 | + $groups = $wgUser->getEffectiveGroups(); |
| 227 | + foreach( $groups as $group ) { |
| 228 | + if( $this->changeableByGroup( $group ) == array( |
| 229 | + 'add' => array(), |
| 230 | + 'remove' => array() |
| 231 | + ) ) { |
| 232 | + // Can't add or remove anything, ignore this group |
| 233 | + $groups = array_diff( $groups, array( $group ) ); |
| 234 | + } |
| 235 | + } |
| 236 | + $grouplists = array( $groups ); |
| 237 | + list( $grouplists[1], $grouplists[2] ) = array_values( $this->changeableGroups() ); |
| 238 | + |
| 239 | + // Now format them nicely for display (yay mutable variables? I'm sick |
| 240 | + // of thinking up new names) |
| 241 | + foreach( $grouplists as &$list ) { |
| 242 | + if( $list == array() ) { |
| 243 | + $list = wfMsgExt( 'userrights-list-nogroups', 'parseinline' ); |
| 244 | + } else { |
| 245 | + $list = wfMsgExt( |
| 246 | + 'userrights-list-groups', |
| 247 | + 'parseinline', |
| 248 | + count( $list ), |
| 249 | + implode( |
| 250 | + $list, |
| 251 | + wfMsgHtml( 'userrights-list-separator' ) |
| 252 | + ) |
| 253 | + ); |
| 254 | + } |
| 255 | + } |
| 256 | + |
| 257 | + return wfMsgExt( |
| 258 | + 'userrights-list', |
| 259 | + 'parse', |
| 260 | + $grouplists[0], |
| 261 | + $grouplists[1], |
| 262 | + $grouplists[2] |
| 263 | + ); |
| 264 | + |
| 265 | + } |
| 266 | + |
| 267 | + /** |
| 268 | + * Adds the <select> thingie where you can select what groups to remove |
| 269 | + * |
| 270 | + * @param array $groups The groups that can be removed |
| 271 | + * @return string XHTML <select> element |
| 272 | + */ |
| 273 | + private function removeSelect( $groups ) { |
| 274 | + return $this->doSelect( $groups, 'member' ); |
| 275 | + } |
| 276 | + |
| 277 | + /** |
| 278 | + * Adds the <select> thingie where you can select what groups to add |
| 279 | + * |
| 280 | + * @param array $groups The groups that can be added |
| 281 | + * @return string XHTML <select> element |
| 282 | + */ |
| 283 | + private function addSelect( $groups ) { |
| 284 | + return $this->doSelect( $groups, 'available' ); |
| 285 | + } |
| 286 | + |
| 287 | + /** |
| 288 | + * Adds the <select> thingie where you can select what groups to add/remove |
| 289 | + * |
| 290 | + * @param array $groups The groups that can be added/removed |
| 291 | + * @param string $name 'member' or 'available' |
| 292 | + * @return string XHTML <select> element |
| 293 | + */ |
| 294 | + private function doSelect( $groups, $name ) { |
| 295 | + $ret = wfMsgHtml( "{$this->mName}-groups$name" ) . |
| 296 | + Xml::openElement( 'select', array( |
| 297 | + 'name' => "{$name}[]", |
| 298 | + 'multiple' => 'multiple', |
| 299 | + 'size' => '6', |
| 300 | + 'style' => 'width: 100%;' |
| 301 | + ) |
| 302 | + ); |
| 303 | + foreach ($groups as $group) { |
| 304 | + $ret .= Xml::element( 'option', array( 'value' => $group ), User::getGroupName( $group ) ); |
| 305 | + } |
| 306 | + $ret .= Xml::closeElement( 'select' ); |
| 307 | + return $ret; |
| 308 | + } |
| 309 | + |
| 310 | + /** |
| 311 | + * @param string $group The name of the group to check |
| 312 | + * @return bool Can we remove the group? |
| 313 | + */ |
| 314 | + private function canRemove( $group ) { |
| 315 | + // $this->changeableGroups()['remove'] doesn't work, of course. Thanks, |
| 316 | + // PHP. |
| 317 | + $groups = $this->changeableGroups(); |
| 318 | + return in_array( $group, $groups['remove'] ); |
| 319 | + } |
| 320 | + |
| 321 | + /** |
| 322 | + * @param string $group The name of the group to check |
| 323 | + * @return bool Can we add the group? |
| 324 | + */ |
| 325 | + private function canAdd( $group ) { |
| 326 | + $groups = $this->changeableGroups(); |
| 327 | + return in_array( $group, $groups['add'] ); |
| 328 | + } |
| 329 | + |
| 330 | + /** |
| 331 | + * Returns an array of the groups that the user can add/remove. |
| 332 | + * |
| 333 | + * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) ) |
| 334 | + */ |
| 335 | + private function changeableGroups() { |
| 336 | + global $wgUser, $wgGroupPermissions; |
| 337 | + |
| 338 | + $groups = array( 'add' => array(), 'remove' => array() ); |
| 339 | + $addergroups = $wgUser->getEffectiveGroups(); |
| 340 | + |
| 341 | + foreach ($addergroups as $addergroup) { |
| 342 | + $groups = array_merge_recursive( |
| 343 | + $groups, $this->changeableByGroup($addergroup) |
| 344 | + ); |
| 345 | + $groups['add'] = array_unique( $groups['add'] ); |
| 346 | + $groups['remove'] = array_unique( $groups['remove'] ); |
| 347 | + } |
| 348 | + return $groups; |
| 349 | + } |
| 350 | + |
| 351 | + /** |
| 352 | + * Returns an array of the groups that a particular group can add/remove. |
| 353 | + * |
| 354 | + * @param String $group The group to check for whether it can add/remove |
| 355 | + * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) ) |
| 356 | + */ |
| 357 | + private function changeableByGroup( $group ) { |
| 358 | + global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups; |
| 359 | + |
| 360 | + if( empty($wgGroupPermissions[$group]['userrights']) ) { |
| 361 | + // This group doesn't give the right to modify anything |
| 362 | + return array( 'add' => array(), 'remove' => array() ); |
| 363 | + } |
| 364 | + if( empty($wgAddGroups[$group]) and empty($wgRemoveGroups[$group]) ) { |
| 365 | + // This group gives the right to modify everything (reverse- |
| 366 | + // compatibility with old "userrights lets you change |
| 367 | + // everything") |
| 368 | + return array( |
| 369 | + 'add' => User::getAllGroups(), |
| 370 | + 'remove' => User::getAllGroups() |
| 371 | + ); |
| 372 | + } |
| 373 | + |
| 374 | + // Okay, it's not so simple, we have to go through the arrays |
| 375 | + $groups = array( 'add' => array(), 'remove' => array() ); |
| 376 | + if( empty($wgAddGroups[$group]) ) { |
| 377 | + // Don't add anything to $groups |
| 378 | + } elseif( $wgAddGroups[$group] === true ) { |
| 379 | + // You get everything |
| 380 | + $groups['add'] = User::getAllGroups(); |
| 381 | + } elseif( is_array($wgAddGroups[$group]) ) { |
| 382 | + $groups['add'] = $wgAddGroups[$group]; |
| 383 | + } |
| 384 | + |
| 385 | + // Same thing for remove |
| 386 | + if( empty($wgRemoveGroups[$group]) ) { |
| 387 | + } elseif($wgRemoveGroups[$group] === true ) { |
| 388 | + $groups['remove'] = User::getAllGroups(); |
| 389 | + } elseif( is_array($wgRemoveGroups[$group]) ) { |
| 390 | + $groups['remove'] = $wgRemoveGroups[$group]; |
| 391 | + } |
| 392 | + return $groups; |
| 393 | + } |
201 | 394 | } // end class UserrightsForm |
202 | 395 | ?> |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -1132,6 +1132,20 @@ |
1133 | 1133 | $wgAutoConfirmCount = 0; |
1134 | 1134 | //$wgAutoConfirmCount = 50; |
1135 | 1135 | |
| 1136 | +/** |
| 1137 | + * These settings can be used to give finer control over who can assign which |
| 1138 | + * groups at Special:Userrights. Example configuration: |
| 1139 | + * |
| 1140 | + * // Bureaucrat can add any group |
| 1141 | + * $wgAddGroups['bureaucrat'] = true; |
| 1142 | + * // Bureaucrats can only remove bots and sysops |
| 1143 | + * $wgRemoveGroups['bureaucrat'] = array( 'bot', 'sysop' ); |
| 1144 | + * // Sysops can make bots |
| 1145 | + * $wgAddGroups['sysop'] = array( 'bot' ); |
| 1146 | + * // Sysops can disable other sysops in an emergency, and disable bots |
| 1147 | + * $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' ); |
| 1148 | + */ |
| 1149 | +$wgAddGroups = $wgRemoveGroups = array(); // Add customizations after this line |
1136 | 1150 | |
1137 | 1151 | |
1138 | 1152 | # Proxy scanner settings |
Index: trunk/phase3/languages/messages/MessagesEn.php |
— | — | @@ -1277,6 +1277,10 @@ |
1278 | 1278 | 'userrights-groupshelp' => 'Select groups you want the user to be removed from or added to. |
1279 | 1279 | Unselected groups will not be changed. You can deselect a group with CTRL + Left Click', |
1280 | 1280 | 'userrights-reason' => 'Reason for change:', |
| 1281 | +'userrights-list' => 'Because you are a member of $1, you can add $2 and remove $3.', |
| 1282 | +'userrights-list-nogroups' => 'no groups', |
| 1283 | +'userrights-list-groups' => 'the {{PLURAL:$1|group|groups}} $2', |
| 1284 | +'userrights-list-separator' => ', ', |
1281 | 1285 | |
1282 | 1286 | # Groups |
1283 | 1287 | 'group' => 'Group:', |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -22,12 +22,14 @@ |
23 | 23 | |
24 | 24 | * $wgThumbUpright - Adjust width of upright images when parameter 'upright' is |
25 | 25 | used |
| 26 | +* $wgAddGroups, $wgRemoveGroups - Finer control over who can assign which |
| 27 | + usergroups |
| 28 | +* $wgEnotifImpersonal, $wgEnotifUseJobQ - Bulk mail options for large sites |
26 | 29 | |
27 | 30 | == New features since 1.10 == |
28 | 31 | |
29 | 32 | * (bug 8868) Separate "blocked" message for autoblocks |
30 | 33 | * Adding expiry of block to block messages |
31 | | -* Bulk mail options ($wgEnotifImpersonal, $wgEnotifUseJobQ) for large sites |
32 | 34 | * Links to redirect pages in categories are wrapped in |
33 | 35 | <span class="redirect-in-category"></span> |
34 | 36 | * Introduced 'ImageOpenShowImageInlineBefore' hook; see docs/hooks.txt for |
— | — | @@ -105,6 +107,8 @@ |
106 | 108 | * Wrap site CSS and JavaScript in a <pre> tag, like user JS/CSS |
107 | 109 | * (bug 10196) Add classes and dir="ltr" to the <pre>s on CSS and JS pages (new |
108 | 110 | classes: mw-user-css, mw-user-js, mw-site-css, mw-site-js) |
| 111 | +* (bug 6711) Add $wgAddGroups and $wgRemoveGroups to allow finer control over |
| 112 | + usergroup assignment. |
109 | 113 | |
110 | 114 | == Bugfixes since 1.10 == |
111 | 115 | |