Index: trunk/phase3/docs/hooks.txt |
— | — | @@ -16,10 +16,10 @@ |
17 | 17 | hook |
18 | 18 | A clump of code and data that should be run when an event happens. This can |
19 | 19 | be either a function and a chunk of data, or an object and a method. |
20 | | - |
| 20 | + |
21 | 21 | hook function |
22 | 22 | The function part of a hook. |
23 | | - |
| 23 | + |
24 | 24 | ==Rationale== |
25 | 25 | |
26 | 26 | Hooks allow us to decouple optionally-run code from code that is run for |
— | — | @@ -54,17 +54,17 @@ |
55 | 55 | |
56 | 56 | function showAnArticle($article) { |
57 | 57 | global $wgReverseTitle, $wgCapitalizeTitle, $wgNotifyArticle; |
58 | | - |
| 58 | + |
59 | 59 | if ($wgReverseTitle) { |
60 | 60 | wfReverseTitle($article); |
61 | 61 | } |
62 | | - |
| 62 | + |
63 | 63 | if ($wgCapitalizeTitle) { |
64 | 64 | wfCapitalizeTitle($article); |
65 | 65 | } |
66 | 66 | |
67 | 67 | # code to actually show the article goes here |
68 | | - |
| 68 | + |
69 | 69 | if ($wgNotifyArticle) { |
70 | 70 | wfNotifyArticleShow($article)); |
71 | 71 | } |
— | — | @@ -87,7 +87,7 @@ |
88 | 88 | code and moving them off somewhere else. It's much easier for someone working |
89 | 89 | with this code to see what's _really_ going on, and make changes or fix bugs. |
90 | 90 | |
91 | | -In addition, we can take all the code that deals with the little-used |
| 91 | +In addition, we can take all the code that deals with the little-used |
92 | 92 | title-reversing options (say) and put it in one place. Instead of having little |
93 | 93 | title-reversing if-blocks spread all over the codebase in showAnArticle, |
94 | 94 | deleteAnArticle, exportArticle, etc., we can concentrate it all in an extension |
— | — | @@ -116,8 +116,8 @@ |
117 | 117 | that it's easier to read and understand; you don't have to do a grep-find to see |
118 | 118 | where the $wgReverseTitle variable is used, say. |
119 | 119 | |
120 | | -If the code is well enough isolated, it can even be excluded when not used -- |
121 | | -making for some slight savings in memory and load-up performance at runtime. |
| 120 | +If the code is well enough isolated, it can even be excluded when not used -- |
| 121 | +making for some slight savings in memory and load-up performance at runtime. |
122 | 122 | Admins who want to have all the reversed titles can add: |
123 | 123 | |
124 | 124 | require_once('extensions/ReverseTitle.php'); |
— | — | @@ -162,7 +162,7 @@ |
163 | 163 | $object->someMethod($param1, $param2) |
164 | 164 | # object with method and data |
165 | 165 | $object->someMethod($someData, $param1, $param2) |
166 | | - |
| 166 | + |
167 | 167 | Note that when an object is the hook, and there's no specified method, the |
168 | 168 | default method called is 'onEventName'. For different events this would be |
169 | 169 | different: 'onArticleSave', 'onUserLogin', etc. |
— | — | @@ -183,13 +183,13 @@ |
184 | 184 | should be shown to the user |
185 | 185 | * false: the hook has successfully done the work necessary and the calling |
186 | 186 | function should skip |
187 | | - |
| 187 | + |
188 | 188 | The last result would be for cases where the hook function replaces the main |
189 | 189 | functionality. For example, if you wanted to authenticate users to a custom |
190 | 190 | system (LDAP, another PHP program, whatever), you could do: |
191 | 191 | |
192 | 192 | $wgHooks['UserLogin'][] = array('ldapLogin', $ldapServer); |
193 | | - |
| 193 | + |
194 | 194 | function ldapLogin($username, $password) { |
195 | 195 | # log user into LDAP |
196 | 196 | return false; |
— | — | @@ -199,7 +199,7 @@ |
200 | 200 | will normally be ignored. |
201 | 201 | |
202 | 202 | Note that none of the examples made use of create_function() as a way to |
203 | | -attach a function to a hook. This is known to cause problems (notably with |
| 203 | +attach a function to a hook. This is known to cause problems (notably with |
204 | 204 | Special:Version), and should be avoided when at all possible. |
205 | 205 | |
206 | 206 | ==Using hooks== |
— | — | @@ -207,7 +207,7 @@ |
208 | 208 | A calling function or method uses the wfRunHooks() function to run the hooks |
209 | 209 | related to a particular event, like so: |
210 | 210 | |
211 | | - class Article { |
| 211 | + class Article { |
212 | 212 | # ... |
213 | 213 | function protect() { |
214 | 214 | global $wgUser; |
— | — | @@ -217,7 +217,7 @@ |
218 | 218 | } |
219 | 219 | } |
220 | 220 | } |
221 | | - |
| 221 | + |
222 | 222 | wfRunHooks() returns true if the calling function should continue processing |
223 | 223 | (the hooks ran OK, or there are no hooks to run), or false if it shouldn't (an |
224 | 224 | error occurred, or one of the hooks handled the action already). Checking the |
— | — | @@ -396,7 +396,7 @@ |
397 | 397 | |
398 | 398 | 'ArticleMergeComplete': after merging to article using Special:Mergehistory |
399 | 399 | $targetTitle: target title (object) |
400 | | -$destTitle: destination title (object) |
| 400 | +$destTitle: destination title (object) |
401 | 401 | |
402 | 402 | 'ArticlePageDataAfter': after loading data of an article from the database |
403 | 403 | $article: article (object) whose data were loaded |
— | — | @@ -420,7 +420,7 @@ |
421 | 421 | $reason: Reason for protect |
422 | 422 | $moveonly: boolean whether it was for move only or not |
423 | 423 | |
424 | | -'ArticlePurge': before executing "&action=purge" |
| 424 | +'ArticlePurge': before executing "&action=purge" |
425 | 425 | $article: article (object) to purge |
426 | 426 | |
427 | 427 | 'ArticleRevisionVisiblitySet': called when changing visibility of one or more |
— | — | @@ -503,7 +503,7 @@ |
504 | 504 | |
505 | 505 | 'BeforeGalleryFindFile': before an image is fetched for a gallery |
506 | 506 | &$gallery,: the gallery object |
507 | | -&$nt: the image title |
| 507 | +&$nt: the image title |
508 | 508 | &$time: image timestamp |
509 | 509 | |
510 | 510 | 'BeforePageDisplay': Prior to outputting a page |
— | — | @@ -924,7 +924,7 @@ |
925 | 925 | &$comment: string that corresponds to logging.log_comment database field, and |
926 | 926 | which is displayed in the UI. |
927 | 927 | &$revert: string that is displayed in the UI, similar to $comment. |
928 | | -$time: timestamp of the log entry (added in 1.12) |
| 928 | +$time: timestamp of the log entry (added in 1.12) |
929 | 929 | |
930 | 930 | 'LogPageValidTypes': action being logged. |
931 | 931 | DEPRECATED: Use $wgLogTypes |
— | — | @@ -950,8 +950,8 @@ |
951 | 951 | $variableIDs: array of strings |
952 | 952 | |
953 | 953 | 'MakeGlobalVariablesScript': called right before Skin::makeVariablesScript |
954 | | -is executed |
955 | | -&$vars: variable (or multiple variables) to be added into the output |
| 954 | +is executed |
| 955 | +&$vars: variable (or multiple variables) to be added into the output |
956 | 956 | of Skin::makeVariablesScript |
957 | 957 | |
958 | 958 | 'MarkPatrolled': before an edit is marked patrolled |
— | — | @@ -1032,8 +1032,8 @@ |
1033 | 1033 | &$urls: array of associative arrays with Url element attributes |
1034 | 1034 | |
1035 | 1035 | 'OutputPageBeforeHTML': a page has been processed by the parser and |
1036 | | -the resulting HTML is about to be displayed. |
1037 | | -$parserOutput: the parserOutput (object) that corresponds to the page |
| 1036 | +the resulting HTML is about to be displayed. |
| 1037 | +$parserOutput: the parserOutput (object) that corresponds to the page |
1038 | 1038 | $text: the text that will be displayed, in HTML (string) |
1039 | 1039 | |
1040 | 1040 | 'OutputPageCheckLastModified': when checking if the page has been modified |
— | — | @@ -1074,7 +1074,7 @@ |
1075 | 1075 | 'ParserAfterStrip': Same as ParserBeforeStrip |
1076 | 1076 | |
1077 | 1077 | 'ParserAfterTidy': Called after Parser::tidy() in Parser::parse() |
1078 | | -$parser: Parser object being used |
| 1078 | +$parser: Parser object being used |
1079 | 1079 | $text: text that'll be returned |
1080 | 1080 | |
1081 | 1081 | 'ParserBeforeInternalParse': called at the beginning of Parser::internalParse() |
— | — | @@ -1089,7 +1089,7 @@ |
1090 | 1090 | $stripState: stripState used (object) |
1091 | 1091 | |
1092 | 1092 | 'ParserBeforeTidy': called before tidy and custom tags replacements |
1093 | | -$parser: Parser object being used |
| 1093 | +$parser: Parser object being used |
1094 | 1094 | $text: actual text |
1095 | 1095 | |
1096 | 1096 | 'ParserClearState': called at the end of Parser::clearState() |
— | — | @@ -1393,7 +1393,7 @@ |
1394 | 1394 | |
1395 | 1395 | 'UploadForm:initial': before the upload form is generated |
1396 | 1396 | $form: UploadForm object |
1397 | | -You might set the member-variables $uploadFormTextTop and |
| 1397 | +You might set the member-variables $uploadFormTextTop and |
1398 | 1398 | $uploadFormTextAfterSummary to inject text (HTML) either before |
1399 | 1399 | or after the editform. |
1400 | 1400 | |
— | — | @@ -1517,7 +1517,7 @@ |
1518 | 1518 | 'UserLoginComplete': after a user has logged in |
1519 | 1519 | $user: the user object that was created on login |
1520 | 1520 | $inject_html: Any HTML to inject after the "logged in" message. |
1521 | | - |
| 1521 | + |
1522 | 1522 | 'UserLoginForm': change to manipulate the login form |
1523 | 1523 | $template: SimpleTemplate instance for the form |
1524 | 1524 | |
— | — | @@ -1527,7 +1527,7 @@ |
1528 | 1528 | |
1529 | 1529 | 'UserLogout': before a user logs out |
1530 | 1530 | $user: the user object that is about to be logged out |
1531 | | - |
| 1531 | + |
1532 | 1532 | 'UserLogoutComplete': after a user has logged out |
1533 | 1533 | $user: the user object _after_ logout (won't have name, ID, etc.) |
1534 | 1534 | $inject_html: Any HTML to inject after the "logged out" message. |
— | — | @@ -1544,13 +1544,30 @@ |
1545 | 1545 | $user : User object of the current user |
1546 | 1546 | $addergroups : Array of groups that the user is in |
1547 | 1547 | &$groups : Array of groups that can be added or removed. In format of |
1548 | | - array( |
1549 | | - 'add' => array( addablegroups ), |
1550 | | - 'remove' => array( removablegroups ), |
| 1548 | + array( |
| 1549 | + 'add' => array( addablegroups ), |
| 1550 | + 'remove' => array( removablegroups ), |
1551 | 1551 | 'add-self' => array( addablegroups to self ), |
1552 | 1552 | 'remove-self' => array( removable groups from self ) |
1553 | 1553 | ) |
1554 | 1554 | |
| 1555 | +'UserrightsGroupCheckboxes': allows modification of the display of |
| 1556 | +checkboxes in the Special:UserRights interface. |
| 1557 | +$usergroups : Array of groups that the target user belongs to |
| 1558 | +&$columns : Array of checkboxes, in the form of |
| 1559 | + $columns['column name']['group name'] = array( |
| 1560 | + 'set' => is this checkbox checked by default? |
| 1561 | + 'disabled' => is this checkbox disabled? |
| 1562 | + 'irreversible' => can this action not be reversed? |
| 1563 | + ); |
| 1564 | + |
| 1565 | +'UserrightsSaveUserGroups': allow extensions to modify the added/removed groups |
| 1566 | +&$user : User object of the user being altered |
| 1567 | +$oldGroups : Array of groups that the user is currently in |
| 1568 | +&$add : Array of groups to add |
| 1569 | +&$remove : Array of groups to remove |
| 1570 | +$reason : Summary provided by user on the form |
| 1571 | + |
1555 | 1572 | 'UserRetrieveNewTalks': called when retrieving "You have new messages!" |
1556 | 1573 | message(s) |
1557 | 1574 | $user: user retrieving new talks messages |
Index: trunk/phase3/includes/specials/SpecialUserrights.php |
— | — | @@ -55,6 +55,16 @@ |
56 | 56 | $this->mTarget = $wgRequest->getVal( 'user' ); |
57 | 57 | } |
58 | 58 | |
| 59 | + /* |
| 60 | + * If the user is blocked and they only have "partial" access |
| 61 | + * (e.g. they don't have the userrights permission), then don't |
| 62 | + * allow them to use Special:UserRights. |
| 63 | + */ |
| 64 | + if( $wgUser->isBlocked() && !$wgUser->isAllowed( 'userrights' ) ) { |
| 65 | + $wgOut->blockedPage(); |
| 66 | + return; |
| 67 | + } |
| 68 | + |
59 | 69 | if (!$this->mTarget) { |
60 | 70 | /* |
61 | 71 | * If the user specified no target, and they can only |
— | — | @@ -102,9 +112,9 @@ |
103 | 113 | $this->mTarget, |
104 | 114 | $reason |
105 | 115 | ); |
106 | | - |
| 116 | + |
107 | 117 | global $wgOut; |
108 | | - |
| 118 | + |
109 | 119 | $url = $this->getSuccessURL(); |
110 | 120 | $wgOut->redirect( $url ); |
111 | 121 | return; |
— | — | @@ -117,7 +127,7 @@ |
118 | 128 | $this->editUserGroupsForm( $this->mTarget ); |
119 | 129 | } |
120 | 130 | } |
121 | | - |
| 131 | + |
122 | 132 | function getSuccessURL() { |
123 | 133 | return $this->getTitle( $this->mTarget )->getFullURL(); |
124 | 134 | } |
— | — | @@ -157,7 +167,7 @@ |
158 | 168 | |
159 | 169 | $this->doSaveUserGroups( $user, $addgroup, $removegroup, $reason ); |
160 | 170 | } |
161 | | - |
| 171 | + |
162 | 172 | /** |
163 | 173 | * Save user groups changes in the database. |
164 | 174 | * |
— | — | @@ -185,6 +195,10 @@ |
186 | 196 | |
187 | 197 | $oldGroups = $user->getGroups(); |
188 | 198 | $newGroups = $oldGroups; |
| 199 | + |
| 200 | + // Run a hook beforehand to allow extensions to modify the added/removed groups |
| 201 | + wfRunHook( 'UserrightsSaveUserGroups', array( &$user, $oldGroups, &$add, &$remove, $reason ) ); |
| 202 | + |
189 | 203 | // remove then add groups |
190 | 204 | if( $remove ) { |
191 | 205 | $newGroups = array_diff($newGroups, $remove); |
— | — | @@ -213,7 +227,7 @@ |
214 | 228 | return array( $add, $remove ); |
215 | 229 | } |
216 | 230 | |
217 | | - |
| 231 | + |
218 | 232 | /** |
219 | 233 | * Add a rights log entry for an action. |
220 | 234 | */ |
— | — | @@ -425,7 +439,7 @@ |
426 | 440 | $cache[$group] = User::makeGroupLinkHtml( $group, htmlspecialchars( User::getGroupName( $group ) ) ); |
427 | 441 | return $cache[$group]; |
428 | 442 | } |
429 | | - |
| 443 | + |
430 | 444 | /** |
431 | 445 | * Returns an array of all groups that may be edited |
432 | 446 | * @return array Array of groups that may be edited. |
— | — | @@ -444,11 +458,11 @@ |
445 | 459 | $allgroups = $this->getAllGroups(); |
446 | 460 | $ret = ''; |
447 | 461 | |
448 | | - $column = 1; |
449 | | - $settable_col = ''; |
450 | | - $unsettable_col = ''; |
| 462 | + # Put all column info into an associative array so that extensions can |
| 463 | + # more easily manage it. |
| 464 | + $columns = array( 'unchangeable' => array(), 'changeable' => array() ); |
451 | 465 | |
452 | | - foreach ($allgroups as $group) { |
| 466 | + foreach( $allgroups as $group ) { |
453 | 467 | $set = in_array( $group, $usergroups ); |
454 | 468 | # Should the checkbox be disabled? |
455 | 469 | $disabled = !( |
— | — | @@ -459,50 +473,50 @@ |
460 | 474 | ($set && !$this->canAdd( $group )) || |
461 | 475 | (!$set && !$this->canRemove( $group ) ) ); |
462 | 476 | |
463 | | - $attr = $disabled ? array( 'disabled' => 'disabled' ) : array(); |
464 | | - $text = $irreversible |
465 | | - ? wfMsgHtml( 'userrights-irreversible-marker', User::getGroupMember( $group ) ) |
466 | | - : User::getGroupMember( $group ); |
467 | | - $checkbox = Xml::checkLabel( $text, "wpGroup-$group", |
468 | | - "wpGroup-$group", $set, $attr ); |
469 | | - $checkbox = $disabled ? Xml::tags( 'span', array( 'class' => 'mw-userrights-disabled' ), $checkbox ) : $checkbox; |
| 477 | + $checkbox = array( |
| 478 | + 'set' => $set, |
| 479 | + 'disabled' => $disabled, |
| 480 | + 'irreversible' => $irreversible |
| 481 | + ); |
470 | 482 | |
471 | | - if ($disabled) { |
472 | | - $unsettable_col .= "$checkbox<br />\n"; |
| 483 | + if( $disabled ) { |
| 484 | + $columns['unchangeable'][$group] = $checkbox; |
473 | 485 | } else { |
474 | | - $settable_col .= "$checkbox<br />\n"; |
| 486 | + $columns['changeable'][$group] = $checkbox; |
475 | 487 | } |
476 | 488 | } |
477 | 489 | |
478 | | - if ($column) { |
479 | | - $ret .= Xml::openElement( 'table', array( 'border' => '0', 'class' => 'mw-userrights-groups' ) ) . |
480 | | - "<tr> |
481 | | -"; |
482 | | - if( $settable_col !== '' ) { |
483 | | - $ret .= xml::element( 'th', null, wfMsg( 'userrights-changeable-col' ) ); |
| 490 | + # Run a hook to allow extensions to modify the column listing |
| 491 | + wfRunHooks( 'UserrightsGroupCheckboxes', array( $usergroups, &$columns ) ); |
| 492 | + |
| 493 | + # Build the HTML table |
| 494 | + $ret .= Xml::openElement( 'table', array( 'border' => '0', 'class' => 'mw-userrights-groups' ) ) . |
| 495 | + "<tr>\n"; |
| 496 | + foreach( $columns as $name => $column ) { |
| 497 | + if( $column === array() ) |
| 498 | + continue; |
| 499 | + $ret .= xml::element( 'th', null, wfMsg( 'userrights-' . $name . '-col' ) ); |
| 500 | + } |
| 501 | + $ret.= "</tr>\n<tr>\n"; |
| 502 | + foreach( $columns as $column ) { |
| 503 | + if( $column === array() ) |
| 504 | + continue; |
| 505 | + $ret .= "\t<td style='vertical-align:top;'>\n"; |
| 506 | + foreach( $column as $group => $checkbox ) { |
| 507 | + $attr = $checkbox['disabled'] ? array( 'disabled' => 'disabled' ) : array(); |
| 508 | + $text = $checkbox['irreversible'] |
| 509 | + ? wfMsgHtml( 'userrights-irreversible-marker', User::getGroupMember( $group ) ) |
| 510 | + : User::getGroupMember( $group ); |
| 511 | + $checkboxHtml = Xml::checkLabel( $text, "wpGroup-" . $group, |
| 512 | + "wpGroup-" . $group, $checkbox['set'], $attr ); |
| 513 | + $ret .= "\t\t" . ( $checkbox['disabled'] |
| 514 | + ? Xml::tags( 'span', array( 'class' => 'mw-userrights-disabled' ), $checkboxHtml ) |
| 515 | + : $checkboxHtml |
| 516 | + ) . "<br />\n"; |
484 | 517 | } |
485 | | - if( $unsettable_col !== '' ) { |
486 | | - $ret .= xml::element( 'th', null, wfMsg( 'userrights-unchangeable-col' ) ); |
487 | | - } |
488 | | - $ret.= "</tr> |
489 | | - <tr> |
490 | | -"; |
491 | | - if( $settable_col !== '' ) { |
492 | | - $ret .= |
493 | | -" <td style='vertical-align:top;'> |
494 | | - $settable_col |
495 | | - </td> |
496 | | -"; |
497 | | - } |
498 | | - if( $unsettable_col !== '' ) { |
499 | | - $ret .= |
500 | | -" <td style='vertical-align:top;'> |
501 | | - $unsettable_col |
502 | | - </td> |
503 | | -"; |
504 | | - } |
505 | | - $ret .= Xml::closeElement( 'tr' ) . Xml::closeElement( 'table' ); |
| 518 | + $ret .= "\t</td>\n"; |
506 | 519 | } |
| 520 | + $ret .= Xml::closeElement( 'tr' ) . Xml::closeElement( 'table' ); |
507 | 521 | |
508 | 522 | return $ret; |
509 | 523 | } |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -83,6 +83,10 @@ |
84 | 84 | * DISPLAYTITLE now accepts a limited amount of wiki markup (the single-quote items) |
85 | 85 | * Special:Search now could search terms in all variant-forms. ONLY apply on |
86 | 86 | wikis with LanguageConverter |
| 87 | +* Add hook 'UserrightsGetCheckboxes' to give extensions the ability to modify |
| 88 | + the arrangement of checkboxes on the Special:UserRights form |
| 89 | +* Add hook 'UserrightsSaveUserGroups' to give extensions the ability to modify |
| 90 | + the groups being added and removed last-minute. |
87 | 91 | |
88 | 92 | === Bug fixes in 1.16 === |
89 | 93 | |
— | — | @@ -187,6 +191,8 @@ |
188 | 192 | * (bug 19160) maintenance/purgeOldText.inc is now compatible with PostgreSQL |
189 | 193 | * Fixed performance regression in "bad image list" feature |
190 | 194 | * Show user preference 'Use live preview' if $wgLivePreview is enabled only |
| 195 | +* (bug 17014) Blocked users can no longer use Special:UserRights if they do |
| 196 | + not have the 'userrights' permission. |
191 | 197 | |
192 | 198 | == API changes in 1.16 == |
193 | 199 | |