53 private $userGroupManager;
56 private $userNameUtils;
59 private $userNamePrefixSearch;
76 parent::__construct(
'Userrights' );
77 $services = MediaWikiServices::getInstance();
79 $this->userNameUtils = $userNameUtils ?? $services->getUserNameUtils();
80 $this->userNamePrefixSearch = $userNamePrefixSearch ?? $services->getUserNamePrefixSearch();
81 $this->userFactory = $userFactory ?? $services->getUserFactory();
84 $this->userGroupManager = ( $userGroupManagerFactory ?? $services->getUserGroupManagerFactory() )
85 ->getUserGroupManager(
false );
105 if ( $targetUser->
getId() === 0 ) {
109 if ( $available[
'add'] || $available[
'remove'] ) {
114 if ( ( $available[
'add-self'] || $available[
'remove-self'] )
115 && (
$isself || !$checkIfSelf )
134 $session = $request->getSession();
137 $out->addModules( [
'mediawiki.special.userrights' ] );
139 $this->mTarget = $par ?? $request->getVal(
'user' );
141 if ( is_string( $this->mTarget ) ) {
142 $this->mTarget = trim( $this->mTarget );
145 if ( $this->mTarget !==
null && $this->userNameUtils->getCanonical( $this->mTarget ) === $user->getName() ) {
146 $this->isself =
true;
149 $fetchedStatus = $this->mTarget ===
null ? Status::newFatal(
'nouserspecified' ) :
150 $this->
fetchUser( $this->mTarget,
true );
151 if ( $fetchedStatus->isOK() ) {
152 $this->mFetchedUser = $fetchedStatus->value;
153 if ( $this->mFetchedUser instanceof
User ) {
156 $this->
getSkin()->setRelevantUser( $this->mFetchedUser );
162 $session->get(
'specialUserrightsSaveSuccess' ) &&
163 $this->mFetchedUser !==
null
166 $session->remove(
'specialUserrightsSaveSuccess' );
168 $out->addModuleStyles(
'mediawiki.notification.convertmessagebox.styles' );
174 $this->
msg(
'savedrights', $this->mFetchedUser->getName() )->text()
184 $out->addModuleStyles(
'mediawiki.special' );
185 $this->
addHelpLink(
'Help:Assigning permissions' );
190 $request->wasPosted() &&
191 $request->getCheck(
'saveusergroups' ) &&
192 $this->mTarget !==
null &&
193 $user->matchEditToken( $request->getVal(
'wpEditToken' ), $this->mTarget )
200 if ( !$this->
getAuthority()->isAllowed(
'userrights' ) ) {
201 $block = $user->getBlock();
202 if ( $block && $block->isSitewide() ) {
215 if ( !$fetchedStatus->isOK() ) {
216 $this->
getOutput()->addWikiTextAsInterface(
217 $fetchedStatus->getWikiText(
false,
false, $this->getLanguage() )
224 if ( $targetUser instanceof
User ) {
228 $conflictCheck = $request->getVal(
'conflictcheck-originalgroups' );
229 $conflictCheck = ( $conflictCheck ===
'' ) ? [] : explode(
',', $conflictCheck );
230 $userGroups = $targetUser->getGroups();
232 if ( $userGroups !== $conflictCheck ) {
233 $out->addHTML( Html::errorBox(
234 $this->
msg(
'userrights-conflict' )->parse()
239 $request->getVal(
'user-reason' ),
243 if ( $status->isOK() ) {
245 $session->set(
'specialUserrightsSaveSuccess', 1 );
247 $out->redirect( $this->getSuccessURL() );
251 $out->wrapWikiTextAsInterface(
252 'error', $status->getWikiText(
false,
false, $this->getLanguage() )
259 if ( $this->mTarget !==
null ) {
260 $this->editUserGroupsForm( $this->mTarget );
264 private function getSuccessURL() {
265 return $this->
getPageTitle( $this->mTarget )->getFullURL();
292 $unix = strtotime( $expiry );
294 if ( !$unix || $unix === -1 ) {
317 $existingUGMs = $user->getGroupMemberships();
321 foreach ( $allgroups as $group ) {
324 if ( $this->
getRequest()->getCheck(
"wpGroup-$group" ) ) {
325 $addgroup[] = $group;
329 $expiryDropdown = $this->
getRequest()->getVal(
"wpExpiry-$group" );
330 if ( $expiryDropdown ===
'existing' ) {
334 if ( $expiryDropdown ===
'other' ) {
335 $expiryValue = $this->
getRequest()->getVal(
"wpExpiry-$group-other" );
337 $expiryValue = $expiryDropdown;
343 if ( $groupExpiries[$group] ===
false ) {
344 return Status::newFatal(
'userrights-invalid-expiry', $group );
348 if ( $groupExpiries[$group] && $groupExpiries[$group] <
wfTimestampNow() ) {
349 return Status::newFatal(
'userrights-expiry-in-past', $group );
354 if ( !$this->canRemove( $group ) &&
355 isset( $existingUGMs[$group] ) &&
356 ( $existingUGMs[$group]->getExpiry() ?:
'infinity' ) >
357 ( $groupExpiries[$group] ?:
'infinity' )
359 return Status::newFatal(
'userrights-cannot-shorten-expiry', $group );
363 $removegroup[] = $group;
367 $this->
doSaveUserGroups( $user, $addgroup, $removegroup, $reason, [], $groupExpiries );
369 return Status::newGood();
386 array $tags = [], array $groupExpiries = []
390 $groups = $user->getGroups();
391 $ugms = $user->getGroupMemberships();
393 $addable = array_merge( $changeable[
'add'],
$isself ? $changeable[
'add-self'] : [] );
394 $removable = array_merge( $changeable[
'remove'],
$isself ? $changeable[
'remove-self'] : [] );
396 $remove = array_unique( array_intersect( $remove, $removable, $groups ) );
397 $add = array_intersect( $add, $addable );
402 $add = array_filter( $add,
403 static function ( $group ) use ( $groups, $groupExpiries, $removable, $ugms ) {
404 if ( isset( $groupExpiries[$group] ) &&
405 !in_array( $group, $removable ) &&
406 isset( $ugms[$group] ) &&
407 ( $ugms[$group]->getExpiry() ?:
'infinity' ) >
408 ( $groupExpiries[$group] ?:
'infinity' )
412 return !in_array( $group, $groups ) || array_key_exists( $group, $groupExpiries );
417 $oldGroups = $groups;
418 $oldUGMs = $user->getGroupMemberships();
419 $newGroups = $oldGroups;
423 foreach ( $remove as $index => $group ) {
424 if ( !$user->removeGroup( $group ) ) {
425 unset( $remove[$index] );
428 $newGroups = array_diff( $newGroups, $remove );
431 foreach ( $add as $index => $group ) {
432 $expiry = $groupExpiries[$group] ??
null;
433 if ( !$user->addGroup( $group, $expiry ) ) {
434 unset( $add[$index] );
437 $newGroups = array_merge( $newGroups, $add );
439 $newGroups = array_unique( $newGroups );
440 $newUGMs = $user->getGroupMemberships();
443 $user->invalidateCache();
446 $this->
getHookRunner()->onUserGroupsChanged( $user, $add, $remove,
447 $this->
getUser(), $reason, $oldUGMs, $newUGMs );
449 wfDebug(
'oldGroups: ' . print_r( $oldGroups,
true ) );
450 wfDebug(
'newGroups: ' . print_r( $newGroups,
true ) );
451 wfDebug(
'oldUGMs: ' . print_r( $oldUGMs,
true ) );
452 wfDebug(
'newUGMs: ' . print_r( $newUGMs,
true ) );
455 if ( $newGroups != $oldGroups || $newUGMs != $oldUGMs ) {
456 $this->
addLogEntry( $user, $oldGroups, $newGroups, $reason, $tags, $oldUGMs, $newUGMs );
459 return [ $add, $remove ];
473 return [
'expiry' => $ugm->getExpiry() ];
486 protected function addLogEntry( $user, array $oldGroups, array $newGroups, $reason,
487 array $tags, array $oldUGMs, array $newUGMs
491 $oldUGMs = array_map(
function ( $group ) use ( $oldUGMs ) {
492 return isset( $oldUGMs[$group] ) ?
496 $newUGMs = array_map(
function ( $group ) use ( $newUGMs ) {
497 return isset( $newUGMs[$group] ) ?
503 $logEntry->setPerformer( $this->
getUser() );
504 $logEntry->setTarget( $user->getUserPage() );
505 $logEntry->setComment( is_string( $reason ) ? $reason :
"" );
506 $logEntry->setParameters( [
507 '4::oldgroups' => $oldGroups,
508 '5::newgroups' => $newGroups,
509 'oldmetadata' => $oldUGMs,
510 'newmetadata' => $newUGMs,
512 $logid = $logEntry->insert();
513 if ( count( $tags ) ) {
514 $logEntry->addTags( $tags );
516 $logEntry->publish( $logid );
523 private function editUserGroupsForm( $username ) {
524 $status = $this->
fetchUser( $username,
true );
525 if ( !$status->isOK() ) {
526 $this->
getOutput()->addWikiTextAsInterface(
527 $status->getWikiText(
false,
false, $this->getLanguage() )
534 $user = $status->value;
535 '@phan-var User $user';
537 $groups = $user->getGroups();
538 $groupMemberships = $user->getGroupMemberships();
555 public function fetchUser( $username, $writing =
true ) {
556 $parts = explode( $this->
getConfig()->
get( MainConfigNames::UserrightsInterwikiDelimiter ),
558 if ( count( $parts ) < 2 ) {
559 $name = trim( $username );
562 list( $name, $dbDomain ) = array_map(
'trim', $parts );
564 if ( WikiMap::isCurrentWikiId( $dbDomain ) ) {
568 !$this->
getAuthority()->isAllowed(
'userrights-interwiki' )
570 return Status::newFatal(
'userrights-no-interwiki' );
573 return Status::newFatal(
'userrights-nodatabase', $dbDomain );
578 if ( $name ===
'' ) {
579 return Status::newFatal(
'nouserspecified' );
582 if ( $name[0] ==
'#' ) {
585 $id = intval( substr( $name, 1 ) );
587 if ( $dbDomain ==
'' ) {
594 return Status::newFatal(
'noname' );
597 $name = $this->userNameUtils->getCanonical( $name );
598 if ( $name ===
false ) {
600 return Status::newFatal(
'nosuchusershort', $username );
604 if ( $dbDomain ==
'' ) {
605 $user = $this->userFactory->newFromName( $name );
610 if ( !$user || $user->isAnon() ) {
611 return Status::newFatal(
'nosuchusershort', $username );
614 if ( $user instanceof
User &&
616 !$this->getAuthority()->isAllowed(
'hideuser' )
619 return Status::newFatal(
'nosuchusershort', $username );
622 return Status::newGood( $user );
633 if ( empty( $ids ) ) {
634 return $this->
msg(
'rightsnone' )->inContentLanguage()->text();
636 return implode(
', ', $ids );
644 $this->
getOutput()->addModules(
'mediawiki.userSuggest' );
653 'id' =>
'mw-userrights-form1'
656 Html::hidden(
'title', $this->
getPageTitle()->getPrefixedText() ) .
657 Xml::fieldset( $this->
msg(
'userrights-lookup-user' )->text() ) .
659 $this->
msg(
'userrights-user-editname' )->text(),
663 $this->mTarget ? str_replace(
'_',
' ', $this->mTarget ) :
'',
665 'class' =>
'mw-autocomplete-user',
668 $this->mFetchedUser ===
null ? [
'autofocus' =>
'' ] : []
672 $this->
msg(
'editusergroup' )->text()
674 Html::closeElement(
'fieldset' ) .
675 Html::closeElement(
'form' ) .
"\n"
689 $list = $membersList = $tempList = $tempMembersList = [];
690 foreach ( $groupMemberships as $ugm ) {
691 $linkG = UserGroupMembership::getLink( $ugm, $this->
getContext(),
'html' );
692 $linkM = UserGroupMembership::getLink( $ugm, $this->
getContext(),
'html',
694 if ( $ugm->getExpiry() ) {
695 $tempList[] = $linkG;
696 $tempMembersList[] = $linkM;
699 $membersList[] = $linkM;
705 $autoMembersList = [];
707 $isUserInstance = $user instanceof
User;
709 if ( $isUserInstance ) {
710 foreach ( $this->userGroupManager->getUserAutopromoteGroups( $user ) as $group ) {
711 $autoList[] = UserGroupMembership::getLink( $group, $this->
getContext(),
'html' );
712 $autoMembersList[] = UserGroupMembership::getLink( $group, $this->
getContext(),
713 'html', $user->getName() );
718 $displayedList = $this->
msg(
'userrights-groupsmember-type' )
720 $language->commaList( array_merge( $tempList, $list ) ),
721 $language->commaList( array_merge( $tempMembersList, $membersList ) )
723 $displayedAutolist = $this->
msg(
'userrights-groupsmember-type' )
725 $language->commaList( $autoList ),
726 $language->commaList( $autoMembersList )
730 $count = count( $list ) + count( $tempList );
732 $grouplist = $this->
msg(
'userrights-groupsmember' )
733 ->numParams( $count )
734 ->params( $user->getName() )
736 $grouplist =
'<p>' . $grouplist .
' ' . $displayedList .
"</p>\n";
739 $count = count( $autoList );
741 $autogrouplistintro = $this->
msg(
'userrights-groupsmember-auto' )
742 ->numParams( $count )
743 ->params( $user->getName() )
745 $grouplist .=
'<p>' . $autogrouplistintro .
' ' . $displayedAutolist .
"</p>\n";
748 $systemUser = $isUserInstance && $user->isSystemUser();
750 $systemusernote = $this->
msg(
'userrights-systemuser' )
751 ->params( $user->getName() )
753 $grouplist .=
'<p>' . $systemusernote .
"</p>\n";
765 list( $groupCheckboxes, $canChangeAny ) =
766 $this->groupCheckboxes( $groupMemberships, $user );
773 'name' =>
'editGroup',
774 'id' =>
'mw-userrights-form2'
777 Html::hidden(
'user', $this->mTarget ) .
778 Html::hidden(
'wpEditToken', $this->
getUser()->getEditToken( $this->mTarget ) ) .
780 'conflictcheck-originalgroups',
781 implode(
',', $user->getGroups() )
783 Xml::openElement(
'fieldset' ) .
788 $canChangeAny ?
'userrights-editusergroup' :
'userrights-viewusergroup',
793 $canChangeAny ?
'editinguser' :
'viewinguserrights'
795 ->rawParams( $userToolLinks )->parse()
797 if ( $canChangeAny ) {
799 $this->
msg(
'userrights-groups-help', $user->getName() )->parse() .
802 Xml::openElement(
'table', [
'id' =>
'mw-userrights-table-outer' ] ) .
804 <td class='mw-label'>" .
805 Xml::label( $this->msg(
'userrights-reason' )->text(),
'wpReason' ) .
807 <td class='mw-input'>" .
808 Xml::input(
'user-reason', 60, $this->getRequest()->getVal(
'user-reason' ) ??
false, [
813 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
819 <td class='mw-submit'>" .
820 Xml::submitButton( $this->msg(
'saveusergroups', $user->getName() )->text(),
821 [
'name' =>
'saveusergroups' ] +
826 Xml::closeElement(
'table' ) .
"\n"
829 $this->
getOutput()->addHTML( $grouplist );
832 Xml::closeElement(
'fieldset' ) .
833 Xml::closeElement(
'form' ) .
"\n"
842 return MediaWikiServices::getInstance()
843 ->getUserGroupManagerFactory()
844 ->getUserGroupManager(
false )
857 private function groupCheckboxes( $usergroups, $user ) {
862 $expiryOptionsMsg = $this->
msg(
'userrights-expiry-options' )->inContentLanguage();
863 $expiryOptions = $expiryOptionsMsg->isDisabled()
869 $columns = [
'unchangeable' => [],
'changeable' => [] ];
871 foreach ( $allgroups as $group ) {
872 $set = isset( $usergroups[$group] );
876 $canOnlyLengthenExpiry = ( $set && $this->canAdd( $group ) &&
877 !$this->canRemove( $group ) && $usergroups[$group]->getExpiry() );
879 $disabledCheckbox = !(
880 ( $set && $this->canRemove( $group ) ) ||
881 ( !$set && $this->canAdd( $group ) ) );
883 $disabledExpiry = $disabledCheckbox && !$canOnlyLengthenExpiry;
885 $irreversible = !$disabledCheckbox && (
886 ( $set && !$this->canAdd( $group ) ) ||
887 ( !$set && !$this->canRemove( $group ) ) );
891 'disabled' => $disabledCheckbox,
892 'disabled-expiry' => $disabledExpiry,
893 'irreversible' => $irreversible
896 if ( $disabledCheckbox && $disabledExpiry ) {
897 $columns[
'unchangeable'][$group] = $checkbox;
899 $columns[
'changeable'][$group] = $checkbox;
904 $ret .=
Xml::openElement(
'table', [
'class' =>
'mw-userrights-groups' ] ) .
906 foreach ( $columns as $name => $column ) {
907 if ( $column === [] ) {
914 $this->
msg(
'userrights-' . $name .
'-col', count( $column ) )->text()
918 $ret .=
"</tr>\n<tr>\n";
920 $userName = $user->getName();
921 foreach ( $columns as $column ) {
922 if ( $column === [] ) {
925 $ret .=
"\t<td style='vertical-align:top;'>\n";
926 foreach ( $column as $group => $checkbox ) {
927 $attr = [
'class' =>
'mw-userrights-groupcheckbox' ];
928 if ( $checkbox[
'disabled'] ) {
929 $attr[
'disabled'] =
'disabled';
932 $member = $uiLanguage->getGroupMemberName( $group, $userName );
933 if ( $checkbox[
'irreversible'] ) {
934 $text = $this->
msg(
'userrights-irreversible-marker', $member )->text();
935 } elseif ( $checkbox[
'disabled'] && !$checkbox[
'disabled-expiry'] ) {
936 $text = $this->
msg(
'userrights-no-shorten-expiry-marker', $member )->text();
941 "wpGroup-" . $group, $checkbox[
'set'], $attr );
946 $currentExpiry = isset( $usergroups[$group] ) ?
947 $usergroups[$group]->getExpiry() :
952 if ( $checkbox[
'set'] &&
953 ( $checkbox[
'irreversible'] || $checkbox[
'disabled-expiry'] )
955 if ( $currentExpiry ) {
956 $expiryFormatted = $uiLanguage->userTimeAndDate( $currentExpiry, $uiUser );
957 $expiryFormattedD = $uiLanguage->userDate( $currentExpiry, $uiUser );
958 $expiryFormattedT = $uiLanguage->userTime( $currentExpiry, $uiUser );
960 $this->
msg(
'userrights-expiry-current' )->params(
961 $expiryFormatted, $expiryFormattedD, $expiryFormattedT )->text() );
964 $this->
msg(
'userrights-expiry-none' )->text() );
969 $currentExpiry ?
'existing' :
'infinite' );
970 $expiryHtml .=
"<br />\n";
973 $this->
msg(
'userrights-expiry' )->text() );
979 "mw-input-wpExpiry-$group",
980 $currentExpiry ?
'existing' :
'infinite'
982 if ( $checkbox[
'disabled-expiry'] ) {
983 $expiryFormOptions->
setAttribute(
'disabled',
'disabled' );
986 if ( $currentExpiry ) {
987 $timestamp = $uiLanguage->userTimeAndDate( $currentExpiry, $uiUser );
988 $d = $uiLanguage->userDate( $currentExpiry, $uiUser );
989 $t = $uiLanguage->userTime( $currentExpiry, $uiUser );
990 $existingExpiryMessage = $this->
msg(
'userrights-expiry-existing',
991 $timestamp, $d,
$t );
992 $expiryFormOptions->addOption( $existingExpiryMessage->text(),
'existing' );
995 $expiryFormOptions->addOption(
996 $this->
msg(
'userrights-expiry-none' )->text(),
999 $expiryFormOptions->addOption(
1000 $this->
msg(
'userrights-expiry-othertime' )->text(),
1004 $expiryFormOptions->addOptions( $expiryOptions );
1007 $expiryHtml .= $expiryFormOptions->getHTML() .
'<br />';
1011 'id' =>
"mw-input-wpExpiry-$group-other",
1012 'class' =>
'mw-userrights-expiryfield',
1014 if ( $checkbox[
'disabled-expiry'] ) {
1015 $attribs[
'disabled'] =
'disabled';
1017 $expiryHtml .=
Xml::input(
"wpExpiry-$group-other", 30,
'', $attribs );
1021 if ( $checkbox[
'set'] && $checkbox[
'disabled'] ) {
1029 'id' =>
"mw-userrights-nested-wpGroup-$group",
1030 'class' =>
'mw-userrights-nested',
1032 $checkboxHtml .=
"\t\t\t" .
Xml::tags(
'div', $divAttribs, $expiryHtml ) .
"\n";
1034 $ret .=
"\t\t" . ( ( $checkbox[
'disabled'] && $checkbox[
'disabled-expiry'] )
1035 ? Xml::tags(
'div', [
'class' =>
'mw-userrights-disabled' ], $checkboxHtml )
1039 $ret .=
"\t</td>\n";
1043 return [ $ret, (bool)$columns[
'changeable'] ];
1050 private function canRemove( $group ) {
1055 $groups[
'remove'] ) || ( $this->isself && in_array( $group, $groups[
'remove-self'] )
1063 private function canAdd( $group ) {
1068 $groups[
'add'] ) || ( $this->isself && in_array( $group, $groups[
'add-self'] )
1091 $rightsLogPage =
new LogPage(
'rights' );
1092 $output->addHTML( Xml::element(
'h2',
null, $rightsLogPage->getName()->text() ) );
1093 LogEventsList::showLogExtract( $output,
'rights', $user->getUserPage() );
1105 $search = $this->userNameUtils->getCanonical( $search );
1111 return $this->userNamePrefixSearch
1112 ->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset );
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 hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
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, $localizer=null, $user=null, $config=null, $relevantTitle=null)
Returns the attributes for the tooltip and access key.
Class to simplify the use of log pages.
Class for creating new log entries and inserting them into the database.
A class containing constants representing the names of configuration variables.
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.
getAuthority()
Shortcut to get the Authority executing this instance.
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 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.
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
isHidden()
Check if user account is hidden.
static whoIs( $id)
Get the username corresponding to a given user ID.
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.
__construct(UserGroupManagerFactory $userGroupManagerFactory=null, UserNameUtils $userNameUtils=null, UserNamePrefixSearch $userNamePrefixSearch=null, UserFactory $userFactory=null)
switchForm()
Output a form to allow searching for a user.
null string $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.
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...
null User $mFetchedUser
The user object of the target username or null.
showLogFragment( $user, $output)
Show a rights log fragment for the specified user.
userCanChangeRights(UserIdentity $targetUser, $checkIfSelf=true)
Check whether the current user (from context) can change the target user's rights.
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.
Class for generating HTML <select> or <datalist> elements.
static parseOptionsMessage(string $msg)
Parse labels and values out of a comma- and colon-separated list of options, such as is used for expi...
setAttribute( $name, $value)
static closeElement( $element)
Shortcut to close an XML element.
static openElement( $element, $attribs=null)
This opens an XML element.
static input( $name, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML text input field.
static checkLabel( $label, $name, $id, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox with a label.
static tags( $element, $attribs, $contents)
Same as Xml::element(), but does not escape contents.
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.