46 parent::__construct(
'Userrights' );
66 if ( $targetUser->getId() === 0 ) {
70 if ( $available[
'add'] || $available[
'remove'] ) {
75 if ( ( $available[
'add-self'] || $available[
'remove-self'] )
96 $session = $request->getSession();
99 $out->addModules( [
'mediawiki.special.userrights' ] );
101 $this->mTarget = $par ?? $request->getVal(
'user' );
103 if ( is_string( $this->mTarget ) ) {
104 $this->mTarget = trim( $this->mTarget );
107 if ( $this->mTarget !==
null && User::getCanonicalName( $this->mTarget ) === $user->getName() ) {
108 $this->isself =
true;
111 $fetchedStatus = $this->
fetchUser( $this->mTarget,
true );
112 if ( $fetchedStatus->isOK() ) {
113 $this->mFetchedUser = $fetchedStatus->value;
114 if ( $this->mFetchedUser instanceof
User ) {
117 $this->
getSkin()->setRelevantUser( $this->mFetchedUser );
123 $session->get(
'specialUserrightsSaveSuccess' ) &&
124 $this->mFetchedUser !==
null
127 $session->remove(
'specialUserrightsSaveSuccess' );
129 $out->addModuleStyles(
'mediawiki.notification.convertmessagebox.styles' );
134 'class' =>
'mw-notify-success successbox',
135 'id' =>
'mw-preferences-success',
136 'data-mw-autohide' =>
'false',
141 $this->
msg(
'savedrights', $this->mFetchedUser->getName() )->text()
150 $out->addModuleStyles(
'mediawiki.special' );
151 $this->
addHelpLink(
'Help:Assigning permissions' );
156 $request->wasPosted() &&
157 $request->getCheck(
'saveusergroups' ) &&
158 $this->mTarget !==
null &&
159 $user->matchEditToken( $request->getVal(
'wpEditToken' ), $this->mTarget )
166 if ( !MediaWikiServices::getInstance()
168 ->userHasRight( $user,
'userrights' )
170 $block = $user->getBlock();
171 if ( $block && $block->isSitewide() ) {
179 if ( !$fetchedStatus->isOK() ) {
180 $this->
getOutput()->addWikiTextAsInterface(
181 $fetchedStatus->getWikiText(
false,
false, $this->getLanguage() )
188 if ( $targetUser instanceof
User ) {
189 $targetUser->clearInstanceCache();
192 $conflictCheck = $request->getVal(
'conflictcheck-originalgroups' );
193 $conflictCheck = ( $conflictCheck ===
'' ) ? [] : explode(
',', $conflictCheck );
194 $userGroups = $targetUser->getGroups();
196 if ( $userGroups !== $conflictCheck ) {
197 $out->wrapWikiMsg(
'<span class="error">$1</span>',
'userrights-conflict' );
201 $request->getVal(
'user-reason' ),
205 if ( $status->isOK() ) {
207 $session->set(
'specialUserrightsSaveSuccess', 1 );
213 $out->wrapWikiTextAsInterface(
214 'error', $status->getWikiText(
false,
false, $this->getLanguage() )
221 if ( $this->mTarget !==
null ) {
227 return $this->
getPageTitle( $this->mTarget )->getFullURL();
254 $unix = strtotime( $expiry );
256 if ( !$unix || $unix === -1 ) {
279 $existingUGMs = $user->getGroupMemberships();
283 foreach ( $allgroups as $group ) {
286 if ( $this->
getRequest()->getCheck(
"wpGroup-$group" ) ) {
287 $addgroup[] = $group;
291 $expiryDropdown = $this->
getRequest()->getVal(
"wpExpiry-$group" );
292 if ( $expiryDropdown ===
'existing' ) {
296 if ( $expiryDropdown ===
'other' ) {
297 $expiryValue = $this->
getRequest()->getVal(
"wpExpiry-$group-other" );
299 $expiryValue = $expiryDropdown;
305 if ( $groupExpiries[$group] ===
false ) {
306 return Status::newFatal(
'userrights-invalid-expiry', $group );
310 if ( $groupExpiries[$group] && $groupExpiries[$group] <
wfTimestampNow() ) {
311 return Status::newFatal(
'userrights-expiry-in-past', $group );
317 isset( $existingUGMs[$group] ) &&
318 ( $existingUGMs[$group]->getExpiry() ?:
'infinity' ) >
319 ( $groupExpiries[$group] ?:
'infinity' )
321 return Status::newFatal(
'userrights-cannot-shorten-expiry', $group );
325 $removegroup[] = $group;
329 $this->
doSaveUserGroups( $user, $addgroup, $removegroup, $reason, [], $groupExpiries );
331 return Status::newGood();
348 array $tags = [], array $groupExpiries = []
352 $groups = $user->getGroups();
353 $ugms = $user->getGroupMemberships();
355 $addable = array_merge( $changeable[
'add'],
$isself ? $changeable[
'add-self'] : [] );
356 $removable = array_merge( $changeable[
'remove'],
$isself ? $changeable[
'remove-self'] : [] );
358 $remove = array_unique(
359 array_intersect( (array)$remove, $removable, $groups ) );
360 $add = array_intersect( (array)$add, $addable );
365 $add = array_filter( $add,
366 function ( $group ) use ( $groups, $groupExpiries, $removable, $ugms ) {
367 if ( isset( $groupExpiries[$group] ) &&
368 !in_array( $group, $removable ) &&
369 isset( $ugms[$group] ) &&
370 ( $ugms[$group]->getExpiry() ?:
'infinity' ) >
371 ( $groupExpiries[$group] ?:
'infinity' )
375 return !in_array( $group, $groups ) || array_key_exists( $group, $groupExpiries );
378 Hooks::run(
'ChangeUserGroups', [ $this->
getUser(), $user, &$add, &$remove ] );
380 $oldGroups = $groups;
381 $oldUGMs = $user->getGroupMemberships();
382 $newGroups = $oldGroups;
386 foreach ( $remove as $index => $group ) {
387 if ( !$user->removeGroup( $group ) ) {
388 unset( $remove[$index] );
391 $newGroups = array_diff( $newGroups, $remove );
394 foreach ( $add as $index => $group ) {
395 $expiry = $groupExpiries[$group] ??
null;
396 if ( !$user->addGroup( $group, $expiry ) ) {
397 unset( $add[$index] );
400 $newGroups = array_merge( $newGroups, $add );
402 $newGroups = array_unique( $newGroups );
403 $newUGMs = $user->getGroupMemberships();
406 $user->invalidateCache();
409 Hooks::run(
'UserGroupsChanged', [ $user, $add, $remove, $this->
getUser(),
410 $reason, $oldUGMs, $newUGMs ] );
412 wfDebug(
'oldGroups: ' . print_r( $oldGroups,
true ) .
"\n" );
413 wfDebug(
'newGroups: ' . print_r( $newGroups,
true ) .
"\n" );
414 wfDebug(
'oldUGMs: ' . print_r( $oldUGMs,
true ) .
"\n" );
415 wfDebug(
'newUGMs: ' . print_r( $newUGMs,
true ) .
"\n" );
418 if ( $newGroups != $oldGroups || $newUGMs != $oldUGMs ) {
419 $this->
addLogEntry( $user, $oldGroups, $newGroups, $reason, $tags, $oldUGMs, $newUGMs );
422 return [ $add, $remove ];
436 return [
'expiry' => $ugm->getExpiry() ];
449 protected function addLogEntry( $user, array $oldGroups, array $newGroups, $reason,
450 array $tags, array $oldUGMs, array $newUGMs
454 $oldUGMs = array_map(
function ( $group ) use ( $oldUGMs ) {
455 return isset( $oldUGMs[$group] ) ?
459 $newUGMs = array_map(
function ( $group ) use ( $newUGMs ) {
460 return isset( $newUGMs[$group] ) ?
466 $logEntry->setPerformer( $this->
getUser() );
467 $logEntry->setTarget( $user->getUserPage() );
468 $logEntry->setComment( $reason );
469 $logEntry->setParameters( [
470 '4::oldgroups' => $oldGroups,
471 '5::newgroups' => $newGroups,
472 'oldmetadata' => $oldUGMs,
473 'newmetadata' => $newUGMs,
475 $logid = $logEntry->insert();
476 if ( count( $tags ) ) {
477 $logEntry->addTags( $tags );
479 $logEntry->publish( $logid );
487 $status = $this->
fetchUser( $username,
true );
488 if ( !$status->isOK() ) {
489 $this->
getOutput()->addWikiTextAsInterface(
490 $status->getWikiText(
false,
false, $this->getLanguage() )
497 $user = $status->value;
498 '@phan-var User $user';
500 $groups = $user->getGroups();
501 $groupMemberships = $user->getGroupMemberships();
518 public function fetchUser( $username, $writing =
true ) {
519 $parts = explode( $this->
getConfig()->
get(
'UserrightsInterwikiDelimiter' ), $username );
520 if ( count( $parts ) < 2 ) {
521 $name = trim( $username );
524 list( $name, $dbDomain ) = array_map(
'trim', $parts );
526 if ( WikiMap::isCurrentWikiId( $dbDomain ) ) {
529 if ( $writing && !MediaWikiServices::getInstance()
531 ->userHasRight( $this->
getUser(),
'userrights-interwiki' )
533 return Status::newFatal(
'userrights-no-interwiki' );
536 return Status::newFatal(
'userrights-nodatabase', $dbDomain );
541 if ( $name ===
'' ) {
542 return Status::newFatal(
'nouserspecified' );
545 if ( $name[0] ==
'#' ) {
548 $id = intval( substr( $name, 1 ) );
550 if ( $dbDomain ==
'' ) {
551 $name = User::whoIs( $id );
557 return Status::newFatal(
'noname' );
560 $name = User::getCanonicalName( $name );
561 if ( $name ===
false ) {
563 return Status::newFatal(
'nosuchusershort', $username );
567 if ( $dbDomain ==
'' ) {
568 $user = User::newFromName( $name );
573 if ( !$user || $user->isAnon() ) {
574 return Status::newFatal(
'nosuchusershort', $username );
577 if ( $user instanceof
User &&
579 !MediaWikiServices::getInstance()
580 ->getPermissionManager()
581 ->userHasRight( $this->getUser(),
'hideuser' )
584 return Status::newFatal(
'nosuchusershort', $username );
587 return Status::newGood( $user );
598 if ( empty( $ids ) ) {
599 return $this->
msg(
'rightsnone' )->inContentLanguage()->text();
601 return implode(
', ', $ids );
609 $this->
getOutput()->addModules(
'mediawiki.userSuggest' );
618 'id' =>
'mw-userrights-form1'
621 Html::hidden(
'title', $this->
getPageTitle()->getPrefixedText() ) .
622 Xml::fieldset( $this->
msg(
'userrights-lookup-user' )->text() ) .
624 $this->
msg(
'userrights-user-editname' )->text(),
628 str_replace(
'_',
' ', $this->mTarget ),
630 'class' =>
'mw-autocomplete-user',
633 $this->mFetchedUser ===
null ? [
'autofocus' =>
'' ] : []
637 $this->
msg(
'editusergroup' )->text()
639 Html::closeElement(
'fieldset' ) .
640 Html::closeElement(
'form' ) .
"\n"
654 $list = $membersList = $tempList = $tempMembersList = [];
655 foreach ( $groupMemberships as $ugm ) {
656 $linkG = UserGroupMembership::getLink( $ugm, $this->
getContext(),
'html' );
657 $linkM = UserGroupMembership::getLink( $ugm, $this->
getContext(),
'html',
659 if ( $ugm->getExpiry() ) {
660 $tempList[] = $linkG;
661 $tempMembersList[] = $linkM;
664 $membersList[] = $linkM;
670 $autoMembersList = [];
671 if ( $user instanceof
User ) {
673 $autoList[] = UserGroupMembership::getLink( $group, $this->
getContext(),
'html' );
674 $autoMembersList[] = UserGroupMembership::getLink( $group, $this->
getContext(),
675 'html', $user->getName() );
680 $displayedList = $this->
msg(
'userrights-groupsmember-type' )
682 $language->commaList( array_merge( $tempList, $list ) ),
683 $language->commaList( array_merge( $tempMembersList, $membersList ) )
685 $displayedAutolist = $this->
msg(
'userrights-groupsmember-type' )
687 $language->commaList( $autoList ),
688 $language->commaList( $autoMembersList )
692 $count = count( $list ) + count( $tempList );
694 $grouplist = $this->
msg(
'userrights-groupsmember' )
695 ->numParams( $count )
696 ->params( $user->getName() )
698 $grouplist =
'<p>' . $grouplist .
' ' . $displayedList .
"</p>\n";
701 $count = count( $autoList );
703 $autogrouplistintro = $this->
msg(
'userrights-groupsmember-auto' )
704 ->numParams( $count )
705 ->params( $user->getName() )
707 $grouplist .=
'<p>' . $autogrouplistintro .
' ' . $displayedAutolist .
"</p>\n";
717 list( $groupCheckboxes, $canChangeAny ) =
725 'name' =>
'editGroup',
726 'id' =>
'mw-userrights-form2'
729 Html::hidden(
'user', $this->mTarget ) .
730 Html::hidden(
'wpEditToken', $this->
getUser()->getEditToken( $this->mTarget ) ) .
732 'conflictcheck-originalgroups',
733 implode(
',', $user->getGroups() )
735 Xml::openElement(
'fieldset' ) .
740 $canChangeAny ?
'userrights-editusergroup' :
'userrights-viewusergroup',
745 $canChangeAny ?
'editinguser' :
'viewinguserrights'
747 ->rawParams( $userToolLinks )->parse()
749 if ( $canChangeAny ) {
751 $this->
msg(
'userrights-groups-help', $user->getName() )->parse() .
754 Xml::openElement(
'table', [
'id' =>
'mw-userrights-table-outer' ] ) .
756 <td class='mw-label'>" .
757 Xml::label( $this->msg(
'userrights-reason' )->text(),
'wpReason' ) .
759 <td class='mw-input'>" .
760 Xml::input(
'user-reason', 60, $this->getRequest()->getVal(
'user-reason',
false ), [
765 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
771 <td class='mw-submit'>" .
772 Xml::submitButton( $this->msg(
'saveusergroups', $user->getName() )->text(),
773 [
'name' =>
'saveusergroups' ] +
778 Xml::closeElement(
'table' ) .
"\n"
781 $this->
getOutput()->addHTML( $grouplist );
784 Xml::closeElement(
'fieldset' ) .
785 Xml::closeElement(
'form' ) .
"\n"
794 return User::getAllGroups();
811 $expiryOptionsMsg = $this->
msg(
'userrights-expiry-options' )->inContentLanguage();
812 $expiryOptions = $expiryOptionsMsg->isDisabled() ?
814 explode(
',', $expiryOptionsMsg->text() );
818 $columns = [
'unchangeable' => [],
'changeable' => [] ];
820 foreach ( $allgroups as $group ) {
821 $set = isset( $usergroups[$group] );
825 $canOnlyLengthenExpiry = ( $set && $this->
canAdd( $group ) &&
826 !$this->
canRemove( $group ) && $usergroups[$group]->getExpiry() );
828 $disabledCheckbox = !(
829 ( $set && $this->
canRemove( $group ) ) ||
830 ( !$set && $this->
canAdd( $group ) ) );
832 $disabledExpiry = $disabledCheckbox && !$canOnlyLengthenExpiry;
834 $irreversible = !$disabledCheckbox && (
835 ( $set && !$this->
canAdd( $group ) ) ||
836 ( !$set && !$this->
canRemove( $group ) ) );
840 'disabled' => $disabledCheckbox,
841 'disabled-expiry' => $disabledExpiry,
842 'irreversible' => $irreversible
845 if ( $disabledCheckbox && $disabledExpiry ) {
846 $columns[
'unchangeable'][$group] = $checkbox;
848 $columns[
'changeable'][$group] = $checkbox;
853 $ret .= Xml::openElement(
'table', [
'class' =>
'mw-userrights-groups' ] ) .
855 foreach ( $columns as $name => $column ) {
856 if ( $column === [] ) {
860 $ret .= Xml::element(
863 $this->
msg(
'userrights-' . $name .
'-col', count( $column ) )->text()
867 $ret .=
"</tr>\n<tr>\n";
868 foreach ( $columns as $column ) {
869 if ( $column === [] ) {
872 $ret .=
"\t<td style='vertical-align:top;'>\n";
873 foreach ( $column as $group => $checkbox ) {
874 $attr = [
'class' =>
'mw-userrights-groupcheckbox' ];
875 if ( $checkbox[
'disabled'] ) {
876 $attr[
'disabled'] =
'disabled';
879 $member = UserGroupMembership::getGroupMemberName( $group, $user->getName() );
880 if ( $checkbox[
'irreversible'] ) {
881 $text = $this->
msg(
'userrights-irreversible-marker', $member )->text();
882 } elseif ( $checkbox[
'disabled'] && !$checkbox[
'disabled-expiry'] ) {
883 $text = $this->
msg(
'userrights-no-shorten-expiry-marker', $member )->text();
887 $checkboxHtml = Xml::checkLabel( $text,
"wpGroup-" . $group,
888 "wpGroup-" . $group, $checkbox[
'set'], $attr );
894 $currentExpiry = isset( $usergroups[$group] ) ?
895 $usergroups[$group]->getExpiry() :
900 if ( $checkbox[
'set'] &&
901 ( $checkbox[
'irreversible'] || $checkbox[
'disabled-expiry'] )
903 if ( $currentExpiry ) {
904 $expiryFormatted = $uiLanguage->userTimeAndDate( $currentExpiry, $uiUser );
905 $expiryFormattedD = $uiLanguage->userDate( $currentExpiry, $uiUser );
906 $expiryFormattedT = $uiLanguage->userTime( $currentExpiry, $uiUser );
907 $expiryHtml = $this->
msg(
'userrights-expiry-current' )->params(
908 $expiryFormatted, $expiryFormattedD, $expiryFormattedT )->text();
910 $expiryHtml = $this->
msg(
'userrights-expiry-none' )->text();
914 $expiryHtml .= Html::hidden(
"wpExpiry-$group",
915 $currentExpiry ?
'existing' :
'infinite' );
916 $expiryHtml .=
"<br />\n";
918 $expiryHtml = Xml::element(
'span',
null,
919 $this->
msg(
'userrights-expiry' )->text() );
920 $expiryHtml .= Xml::openElement(
'span' );
925 "mw-input-wpExpiry-$group",
926 $currentExpiry ?
'existing' :
'infinite'
928 if ( $checkbox[
'disabled-expiry'] ) {
929 $expiryFormOptions->
setAttribute(
'disabled',
'disabled' );
932 if ( $currentExpiry ) {
933 $timestamp = $uiLanguage->userTimeAndDate( $currentExpiry, $uiUser );
934 $d = $uiLanguage->userDate( $currentExpiry, $uiUser );
935 $t = $uiLanguage->userTime( $currentExpiry, $uiUser );
936 $existingExpiryMessage = $this->
msg(
'userrights-expiry-existing',
937 $timestamp, $d,
$t );
938 $expiryFormOptions->addOption( $existingExpiryMessage->text(),
'existing' );
941 $expiryFormOptions->addOption(
942 $this->
msg(
'userrights-expiry-none' )->text(),
945 $expiryFormOptions->addOption(
946 $this->
msg(
'userrights-expiry-othertime' )->text(),
949 foreach ( $expiryOptions as $option ) {
950 if ( strpos( $option,
":" ) ===
false ) {
951 $displayText = $value = $option;
953 list( $displayText, $value ) = explode(
":", $option );
955 $expiryFormOptions->addOption( $displayText, htmlspecialchars( $value ) );
959 $expiryHtml .= $expiryFormOptions->getHTML() .
'<br />';
963 'id' =>
"mw-input-wpExpiry-$group-other",
964 'class' =>
'mw-userrights-expiryfield',
966 if ( $checkbox[
'disabled-expiry'] ) {
967 $attribs[
'disabled'] =
'disabled';
969 $expiryHtml .= Xml::input(
"wpExpiry-$group-other", 30,
'', $attribs );
973 if ( $checkbox[
'set'] && $checkbox[
'disabled'] ) {
974 $expiryHtml .= Html::hidden(
"wpGroup-$group", 1 );
977 $expiryHtml .= Xml::closeElement(
'span' );
981 'id' =>
"mw-userrights-nested-wpGroup-$group",
982 'class' =>
'mw-userrights-nested',
984 $checkboxHtml .=
"\t\t\t" . Xml::tags(
'div', $divAttribs, $expiryHtml ) .
"\n";
986 $ret .=
"\t\t" . ( ( $checkbox[
'disabled'] && $checkbox[
'disabled-expiry'] )
987 ? Xml::tags(
'div', [
'class' =>
'mw-userrights-disabled' ], $checkboxHtml )
988 : Xml::tags(
'div', [], $checkboxHtml )
993 $ret .= Xml::closeElement(
'tr' ) . Xml::closeElement(
'table' );
995 return [ $ret, (bool)$columns[
'changeable'] ];
1007 $groups[
'remove'] ) || ( $this->isself && in_array( $group, $groups[
'remove-self'] )
1020 $groups[
'add'] ) || ( $this->isself && in_array( $group, $groups[
'add-self'] )
1035 return $this->
getUser()->changeableGroups();
1045 $rightsLogPage =
new LogPage(
'rights' );
1046 $output->addHTML( Xml::element(
'h2',
null, $rightsLogPage->getName()->text() ) );
1059 $user = User::newFromName( $search );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null, $useParentheses=true)
Generate standard user tool links (talk, contributions, block link, etc.)
static tooltipAndAccesskeyAttribs( $name, array $msgParams=[], $options=null)
Returns the attributes for the tooltip and access key.
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Class for creating new log entries and inserting them into the database.
Parent class for all special pages.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
getSkin()
Shortcut to get the skin being used for this instance.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getConfig()
Shortcut to get main config object.
getRequest()
Get the WebRequest being used for this instance.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getPageTitle( $subpage=false)
Get a self-referential title object.
getLanguage()
Shortcut to get user's language.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Show an error when the user tries to do something whilst blocked.
Represents a "user group membership" – a specific instance of a user belonging to a group.
static search( $audience, $search, $limit, $offset=0)
Do a prefix search of user names and return a list of matching user names.
static whoIs( $dbDomain, $id, $ignoreInvalidDB=false)
Same as User::whoIs()
static newFromName( $dbDomain, $name, $ignoreInvalidDB=false)
Factory function; get a remote user entry by name.
static validDatabase( $dbDomain)
Confirm the selected database name is a valid local interwiki database name.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
isHidden()
Check if user account is hidden.
Special page to allow managing user group membership.
doSaveUserGroups( $user, array $add, array $remove, $reason='', array $tags=[], array $groupExpiries=[])
Save user groups changes in the database.
static expiryToTimestamp( $expiry)
Converts a user group membership expiry string into a timestamp.
showEditUserGroupsForm( $user, $groups, $groupMemberships)
Show the form to edit group memberships.
static getAllGroups()
Returns an array of all groups that may be edited.
switchForm()
Output a form to allow searching for a user.
$mTarget
The target of the local right-adjuster's interest.
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
userCanChangeRights( $targetUser, $checkIfSelf=true)
Check whether the current user (from context) can change the target user's rights.
editUserGroupsForm( $username)
Edit user groups membership.
groupCheckboxes( $usergroups, $user)
Adds a table with checkboxes where you can select what groups to add/remove.
canProcessExpiries()
Returns true if this user rights form can set and change user group expiries.
fetchUser( $username, $writing=true)
Normalize the input username, which may be local or remote, and return a user (or proxy) object for m...
showLogFragment( $user, $output)
Show a rights log fragment for the specified user.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
execute( $par)
Manage forms to be shown according to posted data.
saveUserGroups( $username, $reason, $user)
Save user groups changes in the database.
static serialiseUgmForLog( $ugm)
Serialise a UserGroupMembership object for storage in the log_params section of the logging table.
doesWrites()
Indicates whether this special page may perform database writes.
addLogEntry( $user, array $oldGroups, array $newGroups, $reason, array $tags, array $oldUGMs, array $newUGMs)
Add a rights log entry for an action.
changeableGroups()
Returns $this->getUser()->changeableGroups()
Class for generating HTML <select> or <datalist> elements.
setAttribute( $name, $value)