73 private $userGroupManagerFactory;
76 private $userGroupManager =
null;
79 private $userNameUtils;
82 private $userNamePrefixSearch;
99 parent::__construct(
'Userrights' );
102 $this->userNameUtils = $userNameUtils ?? $services->getUserNameUtils();
103 $this->userNamePrefixSearch = $userNamePrefixSearch ?? $services->getUserNamePrefixSearch();
104 $this->userFactory = $userFactory ?? $services->getUserFactory();
105 $this->userGroupManagerFactory = $userGroupManagerFactory ?? $services->getUserGroupManagerFactory();
126 $userGroupManager = $this->userGroupManagerFactory
127 ->getUserGroupManager( $targetUser->
getWikiId() );
129 if ( $targetUser->
getId() === 0 ) {
133 if ( $available[
'add'] || $available[
'remove'] ) {
138 if ( ( $available[
'add-self'] || $available[
'remove-self'] )
139 && (
$isself || !$checkIfSelf )
158 $session = $request->getSession();
161 $out->addModules( [
'mediawiki.special.userrights' ] );
163 $this->mTarget = $par ?? $request->getVal(
'user' );
165 if ( is_string( $this->mTarget ) ) {
166 $this->mTarget = trim( $this->mTarget );
169 if ( $this->mTarget !==
null && $this->userNameUtils->getCanonical( $this->mTarget ) === $user->getName() ) {
170 $this->isself =
true;
173 $fetchedStatus = $this->mTarget ===
null ? Status::newFatal(
'nouserspecified' ) :
174 $this->
fetchUser( $this->mTarget,
true );
175 if ( $fetchedStatus->isOK() ) {
176 $this->mFetchedUser = $fetchedUser = $fetchedStatus->value;
178 '@phan-var UserIdentity $fetchedUser';
179 $wikiId = $fetchedUser->getWikiId();
180 if ( $wikiId === UserIdentity::LOCAL ) {
183 $this->
getSkin()->setRelevantUser( $this->mFetchedUser );
185 $this->userGroupManager = $this->userGroupManagerFactory
186 ->getUserGroupManager( $wikiId );
191 $session->get(
'specialUserrightsSaveSuccess' ) &&
192 $this->mFetchedUser !==
null
195 $session->remove(
'specialUserrightsSaveSuccess' );
197 $out->addModuleStyles(
'mediawiki.notification.convertmessagebox.styles' );
203 $this->
msg(
'savedrights', $this->mFetchedUser->getName() )->text()
213 $out->addModuleStyles(
'mediawiki.special' );
214 $this->
addHelpLink(
'Help:Assigning permissions' );
219 $request->wasPosted() &&
220 $request->getCheck(
'saveusergroups' ) &&
221 $this->mTarget !==
null &&
222 $user->matchEditToken( $request->getVal(
'wpEditToken' ), $this->mTarget )
229 if ( !$this->
getAuthority()->isAllowed(
'userrights' ) ) {
230 $block = $user->getBlock();
231 if ( $block && $block->isSitewide() ) {
244 if ( !$fetchedStatus->isOK() ) {
245 $this->
getOutput()->addWikiTextAsInterface(
246 $fetchedStatus->getWikiText(
false,
false, $this->getLanguage() )
253 $conflictCheck = $request->getVal(
'conflictcheck-originalgroups' );
254 $conflictCheck = ( $conflictCheck ===
'' ) ? [] : explode(
',', $conflictCheck );
255 $userGroups = $this->userGroupManager->getUserGroups( $targetUser, UserGroupManager::READ_LATEST );
257 if ( $userGroups !== $conflictCheck ) {
258 $out->addHTML( Html::errorBox(
259 $this->
msg(
'userrights-conflict' )->parse()
264 $request->getVal(
'user-reason' ),
268 if ( $status->isOK() ) {
270 $session->set(
'specialUserrightsSaveSuccess', 1 );
272 $out->redirect( $this->getSuccessURL() );
276 $out->wrapWikiTextAsInterface(
277 'error', $status->getWikiText(
false,
false, $this->getLanguage() )
284 if ( $this->mTarget !==
null ) {
285 $this->editUserGroupsForm( $this->mTarget );
289 private function getSuccessURL() {
290 return $this->
getPageTitle( $this->mTarget )->getFullURL();
317 $unix = strtotime( $expiry );
319 if ( !$unix || $unix === -1 ) {
338 $allgroups = $this->userGroupManager->listAllGroups();
342 $existingUGMs = $this->userGroupManager->getUserGroupMemberships( $user );
346 foreach ( $allgroups as $group ) {
349 if ( $this->
getRequest()->getCheck(
"wpGroup-$group" ) ) {
350 $addgroup[] = $group;
354 $expiryDropdown = $this->
getRequest()->getVal(
"wpExpiry-$group" );
355 if ( $expiryDropdown ===
'existing' ) {
359 if ( $expiryDropdown ===
'other' ) {
360 $expiryValue = $this->
getRequest()->getVal(
"wpExpiry-$group-other" );
362 $expiryValue = $expiryDropdown;
368 if ( $groupExpiries[$group] ===
false ) {
369 return Status::newFatal(
'userrights-invalid-expiry', $group );
373 if ( $groupExpiries[$group] && $groupExpiries[$group] <
wfTimestampNow() ) {
374 return Status::newFatal(
'userrights-expiry-in-past', $group );
379 if ( !$this->canRemove( $group ) &&
380 isset( $existingUGMs[$group] ) &&
381 ( $existingUGMs[$group]->getExpiry() ?:
'infinity' ) >
382 ( $groupExpiries[$group] ?:
'infinity' )
384 return Status::newFatal(
'userrights-cannot-shorten-expiry', $group );
388 $removegroup[] = $group;
392 $this->
doSaveUserGroups( $user, $addgroup, $removegroup, $reason, [], $groupExpiries );
394 return Status::newGood();
413 array $tags = [], array $groupExpiries = []
417 if ( $this->userGroupManager !==
null ) {
419 $userGroupManager = $this->userGroupManager;
422 $userGroupManager = $this->userGroupManagerFactory
423 ->getUserGroupManager( $user->getWikiId() );
428 $addable = array_merge( $changeable[
'add'],
$isself ? $changeable[
'add-self'] : [] );
429 $removable = array_merge( $changeable[
'remove'],
$isself ? $changeable[
'remove-self'] : [] );
431 $remove = array_unique( array_intersect( $remove, $removable, $groups ) );
432 $add = array_intersect( $add, $addable );
437 $add = array_filter( $add,
438 static function ( $group ) use ( $groups, $groupExpiries, $removable, $ugms ) {
439 if ( isset( $groupExpiries[$group] ) &&
440 !in_array( $group, $removable ) &&
441 isset( $ugms[$group] ) &&
442 ( $ugms[$group]->getExpiry() ?:
'infinity' ) >
443 ( $groupExpiries[$group] ?:
'infinity' )
447 return !in_array( $group, $groups ) || array_key_exists( $group, $groupExpiries );
452 $oldGroups = $groups;
454 $newGroups = $oldGroups;
458 foreach ( $remove as $index => $group ) {
460 unset( $remove[$index] );
463 $newGroups = array_diff( $newGroups, $remove );
466 foreach ( $add as $index => $group ) {
467 $expiry = $groupExpiries[$group] ??
null;
468 if ( !$userGroupManager->
addUserToGroup( $user, $group, $expiry,
true ) ) {
469 unset( $add[$index] );
472 $newGroups = array_merge( $newGroups, $add );
474 $newGroups = array_unique( $newGroups );
478 $user->invalidateCache();
481 $this->
getHookRunner()->onUserGroupsChanged( $user, $add, $remove,
482 $this->
getUser(), $reason, $oldUGMs, $newUGMs );
484 wfDebug(
'oldGroups: ' . print_r( $oldGroups,
true ) );
485 wfDebug(
'newGroups: ' . print_r( $newGroups,
true ) );
486 wfDebug(
'oldUGMs: ' . print_r( $oldUGMs,
true ) );
487 wfDebug(
'newUGMs: ' . print_r( $newUGMs,
true ) );
490 if ( $newGroups != $oldGroups || $newUGMs != $oldUGMs ) {
491 $this->
addLogEntry( $user, $oldGroups, $newGroups, $reason, $tags, $oldUGMs, $newUGMs );
494 return [ $add, $remove ];
508 return [
'expiry' => $ugm->getExpiry() ];
521 protected function addLogEntry( $user, array $oldGroups, array $newGroups, $reason,
522 array $tags, array $oldUGMs, array $newUGMs
526 $oldUGMs = array_map(
function ( $group ) use ( $oldUGMs ) {
527 return isset( $oldUGMs[$group] ) ?
531 $newUGMs = array_map(
function ( $group ) use ( $newUGMs ) {
532 return isset( $newUGMs[$group] ) ?
538 $logEntry->setPerformer( $this->
getUser() );
540 $logEntry->setComment( is_string( $reason ) ? $reason :
"" );
541 $logEntry->setParameters( [
542 '4::oldgroups' => $oldGroups,
543 '5::newgroups' => $newGroups,
544 'oldmetadata' => $oldUGMs,
545 'newmetadata' => $newUGMs,
547 $logid = $logEntry->insert();
548 if ( count( $tags ) ) {
549 $logEntry->addTags( $tags );
551 $logEntry->publish( $logid );
558 private function editUserGroupsForm( $username ) {
559 $status = $this->
fetchUser( $username,
true );
560 if ( !$status->isOK() ) {
561 $this->
getOutput()->addWikiTextAsInterface(
562 $status->getWikiText(
false,
false, $this->getLanguage() )
569 $user = $status->value;
570 '@phan-var UserIdentity $user';
572 $groups = $this->userGroupManager->getUserGroups( $user );
573 $groupMemberships = $this->userGroupManager->getUserGroupMemberships( $user );
590 public function fetchUser( $username, $writing =
true ) {
593 if ( count( $parts ) < 2 ) {
594 $name = trim( $username );
597 [ $name, $dbDomain ] = array_map(
'trim', $parts );
599 if ( WikiMap::isCurrentWikiId( $dbDomain ) ) {
603 !$this->
getAuthority()->isAllowed(
'userrights-interwiki' )
605 return Status::newFatal(
'userrights-no-interwiki' );
608 return Status::newFatal(
'userrights-nodatabase', $dbDomain );
613 if ( $name ===
'' ) {
614 return Status::newFatal(
'nouserspecified' );
617 if ( $name[0] ==
'#' ) {
620 $id = intval( substr( $name, 1 ) );
622 if ( $dbDomain ==
'' ) {
629 return Status::newFatal(
'noname' );
632 $name = $this->userNameUtils->getCanonical( $name );
633 if ( $name ===
false ) {
635 return Status::newFatal(
'nosuchusershort', $username );
639 if ( $dbDomain ==
'' ) {
640 $user = $this->userFactory->newFromName( $name );
645 if ( !$user || $user->isAnon() ) {
646 return Status::newFatal(
'nosuchusershort', $username );
649 if ( $user->getWikiId() === UserIdentity::LOCAL &&
650 $this->userFactory->newFromUserIdentity( $user )->isHidden() &&
651 !$this->getAuthority()->isAllowed(
'hideuser' )
654 return Status::newFatal(
'nosuchusershort', $username );
657 return Status::newGood( $user );
668 if ( empty( $ids ) ) {
669 return $this->
msg(
'rightsnone' )->inContentLanguage()->text();
671 return implode(
', ', $ids );
679 $this->
getOutput()->addModules(
'mediawiki.userSuggest' );
688 'id' =>
'mw-userrights-form1'
691 Html::hidden(
'title', $this->
getPageTitle()->getPrefixedText() ) .
692 Xml::fieldset( $this->
msg(
'userrights-lookup-user' )->text() ) .
694 $this->
msg(
'userrights-user-editname' )->text(),
698 $this->mTarget ? str_replace(
'_',
' ', $this->mTarget ) :
'',
700 'class' =>
'mw-autocomplete-user',
703 $this->mFetchedUser ===
null ? [
'autofocus' =>
'' ] : []
707 $this->
msg(
'editusergroup' )->text()
709 Html::closeElement(
'fieldset' ) .
710 Html::closeElement(
'form' ) .
"\n"
724 $list = $membersList = $tempList = $tempMembersList = [];
725 foreach ( $groupMemberships as $ugm ) {
726 $linkG = UserGroupMembership::getLink( $ugm, $this->
getContext(),
'html' );
727 $linkM = UserGroupMembership::getLink( $ugm, $this->
getContext(),
'html',
729 if ( $ugm->getExpiry() ) {
730 $tempList[] = $linkG;
731 $tempMembersList[] = $linkM;
734 $membersList[] = $linkM;
740 $autoMembersList = [];
742 if ( $user->getWikiId() === UserIdentity::LOCAL ) {
744 foreach ( $this->userGroupManager->getUserAutopromoteGroups( $user ) as $group ) {
745 $autoList[] = UserGroupMembership::getLink( $group, $this->
getContext(),
'html' );
746 $autoMembersList[] = UserGroupMembership::getLink( $group, $this->
getContext(),
747 'html', $user->getName() );
752 $displayedList = $this->
msg(
'userrights-groupsmember-type' )
754 $language->commaList( array_merge( $tempList, $list ) ),
755 $language->commaList( array_merge( $tempMembersList, $membersList ) )
757 $displayedAutolist = $this->
msg(
'userrights-groupsmember-type' )
759 $language->commaList( $autoList ),
760 $language->commaList( $autoMembersList )
764 $count = count( $list ) + count( $tempList );
766 $grouplist = $this->
msg(
'userrights-groupsmember' )
767 ->numParams( $count )
768 ->params( $user->getName() )
770 $grouplist =
'<p>' . $grouplist .
' ' . $displayedList .
"</p>\n";
773 $count = count( $autoList );
775 $autogrouplistintro = $this->
msg(
'userrights-groupsmember-auto' )
776 ->numParams( $count )
777 ->params( $user->getName() )
779 $grouplist .=
'<p>' . $autogrouplistintro .
' ' . $displayedAutolist .
"</p>\n";
782 $systemUser = $user->getWikiId() === UserIdentity::LOCAL
783 && $this->userFactory->newFromUserIdentity( $user )->isSystemUser();
785 $systemusernote = $this->
msg(
'userrights-systemuser' )
786 ->params( $user->getName() )
788 $grouplist .=
'<p>' . $systemusernote .
"</p>\n";
792 $flags = $systemUser ? 0 : Linker::TOOL_LINKS_EMAIL;
793 $userToolLinks = Linker::userToolLinks(
800 [ $groupCheckboxes, $canChangeAny ] =
801 $this->groupCheckboxes( $groupMemberships, $user );
808 'name' =>
'editGroup',
809 'id' =>
'mw-userrights-form2'
812 Html::hidden(
'user', $this->mTarget ) .
813 Html::hidden(
'wpEditToken', $this->
getUser()->getEditToken( $this->mTarget ) ) .
815 'conflictcheck-originalgroups',
816 implode(
',', $this->userGroupManager->getUserGroups( $user ) )
818 Xml::openElement(
'fieldset' ) .
823 $canChangeAny ?
'userrights-editusergroup' :
'userrights-viewusergroup',
828 $canChangeAny ?
'editinguser' :
'viewinguserrights'
830 ->rawParams( $userToolLinks )->parse()
832 if ( $canChangeAny ) {
834 $this->
msg(
'userrights-groups-help', $user->getName() )->parse() .
837 Xml::openElement(
'table', [
'id' =>
'mw-userrights-table-outer' ] ) .
839 <td class='mw-label'>" .
840 Xml::label( $this->msg(
'userrights-reason' )->text(),
'wpReason' ) .
842 <td class='mw-input'>" .
843 Xml::input(
'user-reason', 60, $this->getRequest()->getVal(
'user-reason' ) ??
false, [
848 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT,
854 <td class='mw-submit'>" .
855 Xml::submitButton( $this->msg(
'saveusergroups', $user->getName() )->text(),
856 [
'name' =>
'saveusergroups' ] +
857 Linker::tooltipAndAccesskeyAttribs(
'userrights-set' )
861 Xml::closeElement(
'table' ) .
"\n"
864 $this->
getOutput()->addHTML( $grouplist );
867 Xml::closeElement(
'fieldset' ) .
868 Xml::closeElement(
'form' ) .
"\n"
881 private function groupCheckboxes( $usergroups, $user ) {
882 $allgroups = $this->userGroupManager->listAllGroups();
886 $expiryOptionsMsg = $this->
msg(
'userrights-expiry-options' )->inContentLanguage();
887 $expiryOptions = $expiryOptionsMsg->isDisabled()
889 : XmlSelect::parseOptionsMessage( $expiryOptionsMsg->text() );
893 $columns = [
'unchangeable' => [],
'changeable' => [] ];
895 foreach ( $allgroups as $group ) {
896 $set = isset( $usergroups[$group] );
900 $canOnlyLengthenExpiry = ( $set && $this->canAdd( $group ) &&
901 !$this->canRemove( $group ) && $usergroups[$group]->getExpiry() );
903 $disabledCheckbox = !(
904 ( $set && $this->canRemove( $group ) ) ||
905 ( !$set && $this->canAdd( $group ) ) );
907 $disabledExpiry = $disabledCheckbox && !$canOnlyLengthenExpiry;
909 $irreversible = !$disabledCheckbox && (
910 ( $set && !$this->canAdd( $group ) ) ||
911 ( !$set && !$this->canRemove( $group ) ) );
915 'disabled' => $disabledCheckbox,
916 'disabled-expiry' => $disabledExpiry,
917 'irreversible' => $irreversible
920 if ( $disabledCheckbox && $disabledExpiry ) {
921 $columns[
'unchangeable'][$group] = $checkbox;
923 $columns[
'changeable'][$group] = $checkbox;
928 $ret .=
Xml::openElement(
'table', [
'class' =>
'mw-userrights-groups' ] ) .
930 foreach ( $columns as $name => $column ) {
931 if ( $column === [] ) {
938 $this->
msg(
'userrights-' . $name .
'-col', count( $column ) )->text()
942 $ret .=
"</tr>\n<tr>\n";
944 $userName = $user->getName();
945 foreach ( $columns as $column ) {
946 if ( $column === [] ) {
949 $ret .=
"\t<td style='vertical-align:top;'>\n";
950 foreach ( $column as $group => $checkbox ) {
951 $attr = [
'class' =>
'mw-userrights-groupcheckbox' ];
952 if ( $checkbox[
'disabled'] ) {
953 $attr[
'disabled'] =
'disabled';
956 $member = $uiLanguage->getGroupMemberName( $group, $userName );
957 if ( $checkbox[
'irreversible'] ) {
958 $text = $this->
msg(
'userrights-irreversible-marker', $member )->text();
959 } elseif ( $checkbox[
'disabled'] && !$checkbox[
'disabled-expiry'] ) {
960 $text = $this->
msg(
'userrights-no-shorten-expiry-marker', $member )->text();
965 "wpGroup-" . $group, $checkbox[
'set'], $attr );
970 $currentExpiry = isset( $usergroups[$group] ) ?
971 $usergroups[$group]->getExpiry() :
976 if ( $checkbox[
'set'] &&
977 ( $checkbox[
'irreversible'] || $checkbox[
'disabled-expiry'] )
979 if ( $currentExpiry ) {
980 $expiryFormatted = $uiLanguage->userTimeAndDate( $currentExpiry, $uiUser );
981 $expiryFormattedD = $uiLanguage->userDate( $currentExpiry, $uiUser );
982 $expiryFormattedT = $uiLanguage->userTime( $currentExpiry, $uiUser );
984 $this->
msg(
'userrights-expiry-current' )->params(
985 $expiryFormatted, $expiryFormattedD, $expiryFormattedT )->text() );
988 $this->
msg(
'userrights-expiry-none' )->text() );
992 $expiryHtml .= Html::hidden(
"wpExpiry-$group",
993 $currentExpiry ?
'existing' :
'infinite' );
994 $expiryHtml .=
"<br />\n";
997 $this->
msg(
'userrights-expiry' )->text() );
1003 "mw-input-wpExpiry-$group",
1004 $currentExpiry ?
'existing' :
'infinite'
1006 if ( $checkbox[
'disabled-expiry'] ) {
1007 $expiryFormOptions->
setAttribute(
'disabled',
'disabled' );
1010 if ( $currentExpiry ) {
1011 $timestamp = $uiLanguage->userTimeAndDate( $currentExpiry, $uiUser );
1012 $d = $uiLanguage->userDate( $currentExpiry, $uiUser );
1013 $t = $uiLanguage->userTime( $currentExpiry, $uiUser );
1014 $existingExpiryMessage = $this->
msg(
'userrights-expiry-existing',
1015 $timestamp, $d,
$t );
1016 $expiryFormOptions->addOption( $existingExpiryMessage->text(),
'existing' );
1019 $expiryFormOptions->addOption(
1020 $this->
msg(
'userrights-expiry-none' )->text(),
1023 $expiryFormOptions->addOption(
1024 $this->
msg(
'userrights-expiry-othertime' )->text(),
1028 $expiryFormOptions->addOptions( $expiryOptions );
1031 $expiryHtml .= $expiryFormOptions->getHTML() .
'<br />';
1035 'id' =>
"mw-input-wpExpiry-$group-other",
1036 'class' =>
'mw-userrights-expiryfield',
1038 if ( $checkbox[
'disabled-expiry'] ) {
1039 $attribs[
'disabled'] =
'disabled';
1041 $expiryHtml .=
Xml::input(
"wpExpiry-$group-other", 30,
'', $attribs );
1045 if ( $checkbox[
'set'] && $checkbox[
'disabled'] ) {
1046 $expiryHtml .= Html::hidden(
"wpGroup-$group", 1 );
1053 'id' =>
"mw-userrights-nested-wpGroup-$group",
1054 'class' =>
'mw-userrights-nested',
1056 $checkboxHtml .=
"\t\t\t" .
Xml::tags(
'div', $divAttribs, $expiryHtml ) .
"\n";
1058 $ret .=
"\t\t" . ( ( $checkbox[
'disabled'] && $checkbox[
'disabled-expiry'] )
1059 ? Xml::tags(
'div', [
'class' =>
'mw-userrights-disabled' ], $checkboxHtml )
1063 $ret .=
"\t</td>\n";
1067 return [ $ret, (bool)$columns[
'changeable'] ];
1074 private function canRemove( $group ) {
1079 $groups[
'remove'] ) || ( $this->isself && in_array( $group, $groups[
'remove-self'] )
1087 private function canAdd( $group ) {
1092 $groups[
'add'] ) || ( $this->isself && in_array( $group, $groups[
'add-self'] )
1115 $rightsLogPage =
new LogPage(
'rights' );
1116 $output->addHTML( Xml::element(
'h2',
null, $rightsLogPage->getName()->text() ) );
1129 $search = $this->userNameUtils->getCanonical( $search );
1135 return $this->userNamePrefixSearch
1136 ->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset );
1148class_alias( SpecialUserRights::class,
'UserrightsPage' );
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,...
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.
const UserrightsInterwikiDelimiter
Name constant for the UserrightsInterwikiDelimiter setting, for use with Config::get()
This is one of the Core classes and should be read at least once by any new developers.
Show an error when a user tries to do something they do not have the necessary permissions for.
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.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
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.
Cut-down copy of User interface for local-interwiki-database user rights manipulation.
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.
static whoIs( $id)
Get the username corresponding to a given user ID.
Class for generating HTML <select> or <datalist> elements.
setAttribute( $name, $value)
Module of static functions for generating XML.
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.