75 private $linkBatchFactory;
78 private $userGroupManager;
81 private $userIdentityLookup;
108 $parms = explode(
'/', $par );
109 $symsForAll = [
'*',
'user' ];
111 if ( $parms[0] !=
'' &&
112 ( in_array( $par, $userGroupManager->
listAllGroups() ) || in_array( $par, $symsForAll ) )
114 $this->requestedGroup = $par;
115 $un = $request->getText(
'username' );
116 } elseif ( count( $parms ) == 2 ) {
117 $this->requestedGroup = $parms[0];
120 $this->requestedGroup = $request->getVal(
'group' );
121 $un = ( $par !=
'' ) ? $par : $request->getText(
'username' );
124 if ( in_array( $this->requestedGroup, $symsForAll ) ) {
125 $this->requestedGroup =
'';
127 $this->editsOnly = $request->getBool(
'editsOnly' );
128 $this->temporaryGroupsOnly = $request->getBool(
'temporaryGroupsOnly' );
129 $this->creationSort = $request->getBool(
'creationSort' );
131 $this->mDefaultDirection = $request->getBool(
'desc' )
135 $this->requestedUser =
'';
138 $username = Title::makeTitleSafe(
NS_USER, $un );
140 if ( $username !==
null ) {
141 $this->requestedUser = $username->getText();
147 parent::__construct();
148 $this->userGroupManager = $userGroupManager;
149 $this->hookRunner =
new HookRunner( $hookContainer );
150 $this->linkBatchFactory = $linkBatchFactory;
151 $this->userIdentityLookup = $userIdentityLookup;
158 return $this->creationSort ?
'user_id' :
'user_name';
170 $conds[] =
'ipb_deleted IS NULL OR ipb_deleted = 0';
175 if ( $this->requestedGroup !=
'' || $this->temporaryGroupsOnly ) {
176 $conds[] =
'ug_expiry >= ' .
$dbr->addQuotes(
$dbr->timestamp() ) .
177 ( !$this->temporaryGroupsOnly ?
' OR ug_expiry IS NULL' :
'' );
180 if ( $this->requestedGroup !=
'' ) {
184 if ( $this->requestedUser !=
'' ) {
185 # Sorted either by account creation or name
186 if ( $this->creationSort ) {
187 $userIdentity = $this->userIdentityLookup->getUserIdentityByName( $this->requestedUser );
188 if ( $userIdentity && $userIdentity->isRegistered() ) {
189 $conds[] =
'user_id >= ' . $userIdentity->getId();
192 $conds[] =
'user_name >= ' .
$dbr->addQuotes( $this->requestedUser );
196 if ( $this->editsOnly ) {
197 $conds[] =
'user_editcount > 0';
200 $options[
'GROUP BY'] = $this->creationSort ?
'user_id' :
'user_name';
203 'tables' => [
'user',
'user_groups',
'ipblocks' ],
205 'user_name' => $this->creationSort ?
'MAX(user_name)' :
'user_name',
206 'user_id' => $this->creationSort ?
'user_id' :
'MAX(user_id)',
207 'edits' =>
'MAX(user_editcount)',
208 'creation' =>
'MIN(user_registration)',
209 'ipb_deleted' =>
'MAX(ipb_deleted)',
210 'ipb_sitewide' =>
'MAX(ipb_sitewide)'
212 'options' => $options,
214 'user_groups' => [
'LEFT JOIN',
'user_id=ug_user' ],
225 $this->hookRunner->onSpecialListusersQueryInfo( $this, $query );
235 if ( $row->user_id == 0 ) { # T18487
239 $userName = $row->user_name;
241 $ulinks = Linker::userLink( $row->user_id, $userName );
242 $ulinks .= Linker::userToolLinksRedContribs(
256 if ( !$this->including && count( $ugms ) > 0 ) {
258 foreach ( $ugms as $ugm ) {
261 $groups =
$lang->commaList( $list );
264 $item =
$lang->specialList( $ulinks, $groups );
266 if ( $row->ipb_deleted ) {
267 $item =
"<span class=\"deleted\">$item</span>";
271 if ( !$this->including && $this->
getConfig()->
get( MainConfigNames::Edititis ) ) {
272 $count = $this->
msg(
'usereditcount' )->numParams( $row->edits )->escaped();
273 $edits = $this->
msg(
'word-separator' )->escaped() . $this->
msg(
'brackets', $count )->escaped();
277 # Some rows may be null
278 if ( !$this->including && $row->creation ) {
280 $d =
$lang->userDate( $row->creation, $user );
281 $t =
$lang->userTime( $row->creation, $user );
282 $created = $this->
msg(
'usercreated', $d,
$t, $row->user_name )->escaped();
283 $created =
' ' . $this->
msg(
'parentheses' )->rawParams( $created )->escaped();
286 $blocked = $row->ipb_deleted !==
null && $row->ipb_sitewide ===
'1' ?
287 ' ' . $this->
msg(
'listusers-blocked', $userName )->escaped() :
290 $this->hookRunner->onSpecialListusersFormatRow( $item, $row );
292 return Html::rawElement(
'li', [],
"{$item}{$edits}{$created}{$blocked}" );
296 $batch = $this->linkBatchFactory->newLinkBatch();
298 # Give some pointers to make user links
299 foreach ( $this->mResult as $row ) {
300 $batch->add(
NS_USER, $row->user_name );
302 $userIds[] = $row->user_id;
306 $queryBuilder = $this->userGroupManager->newQueryBuilder( $this->
getDatabase() );
307 $groupRes = $queryBuilder->where( [
'ug_user' => $userIds ] )
308 ->caller( __METHOD__ )
312 foreach ( $groupRes as $row ) {
313 $ugm = $this->userGroupManager->newGroupMembershipFromRow( $row );
314 if ( !$ugm->isExpired() ) {
315 $cache[$row->ug_user][$row->ug_group] = $ugm;
316 $groups[$row->ug_group] =
true;
322 $this->hookRunner->onUsersPagerDoBatchLookups( $this->
getDatabase(), $userIds, $cache, $groups );
324 $this->userGroupCache = $cache;
327 foreach ( $groups as $group => $unused ) {
330 $batch->addObj( $groupPage );
335 $this->mResult->rewind();
342 $self = explode(
'/', $this->
getTitle()->getPrefixedDBkey(), 2 )[0];
344 $groupOptions = [ $this->
msg(
'group-all' )->text() =>
'' ];
345 foreach ( $this->getAllGroups() as $group => $groupText ) {
346 $groupOptions[ $groupText ] = $group;
351 'class' => HTMLUserTextField::class,
352 'label' => $this->
msg(
'listusersfrom' )->text(),
353 'name' =>
'username',
357 'label' => $this->
msg(
'group' )->text(),
360 'class' => HTMLSelectField::class,
361 'options' => $groupOptions,
365 'label' => $this->
msg(
'listusers-editsonly' )->text(),
366 'name' =>
'editsOnly',
370 'temporaryGroupsOnly' => [
372 'label' => $this->
msg(
'listusers-temporarygroupsonly' )->text(),
373 'name' =>
'temporaryGroupsOnly',
374 'id' =>
'temporaryGroupsOnly',
379 'label' => $this->
msg(
'listusers-creationsort' )->text(),
380 'name' =>
'creationSort',
381 'id' =>
'creationSort',
386 'label' => $this->
msg(
'listusers-desc' )->text(),
391 'limithiddenfield' => [
392 'class' => HTMLHiddenField::class,
398 $beforeSubmitButtonHookOut =
'';
399 $this->hookRunner->onSpecialListusersHeaderForm( $this, $beforeSubmitButtonHookOut );
401 if ( $beforeSubmitButtonHookOut !==
'' ) {
402 $formDescriptor[
'beforeSubmitButtonHookOut' ] = [
403 'class' => HTMLInfoField::class,
405 'default' => $beforeSubmitButtonHookOut
409 $formDescriptor[
'submit' ] = [
410 'class' => HTMLSubmitField::class,
411 'buttonlabel-message' =>
'listusers-submit',
414 $beforeClosingFieldsetHookOut =
'';
415 $this->hookRunner->onSpecialListusersHeader( $this, $beforeClosingFieldsetHookOut );
417 if ( $beforeClosingFieldsetHookOut !==
'' ) {
418 $formDescriptor[
'beforeClosingFieldsetHookOut' ] = [
419 'class' => HTMLInfoField::class,
421 'default' => $beforeClosingFieldsetHookOut
428 ->setTitle( Title::newFromText(
$self ) )
429 ->setId(
'mw-listusers-form' )
430 ->setFormIdentifier(
'mw-listusers-form' )
431 ->suppressDefaultSubmit()
432 ->setWrapperLegendMsg(
'listusers' );
433 return $htmlForm->prepareForm()->getHTML(
true );
444 private function getAllGroups() {
447 foreach ( $this->userGroupManager->listAllGroups() as $group ) {
448 $result[$group] =
$lang->getGroupName( $group );
460 $query = parent::getDefaultQuery();
461 if ( $this->requestedGroup !=
'' ) {
464 if ( $this->requestedUser !=
'' ) {
467 $this->hookRunner->onSpecialListusersDefaultQuery( $this, $query );
480 if ( $this->userGroupCache ===
null ) {
481 return $this->userGroupManager->getUserGroupMemberships( $user );
483 return $this->userGroupCache[$user->getId()] ?? [];
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
getContext()
Get the base IContextSource object.
setContext(IContextSource $context)
A class containing constants representing the names of configuration variables.
static getGroupPage( $group)
Gets the title of a page describing a particular user group.
static getLink( $ugm, IContextSource $context, $format, $userName=null)
Gets a link for a user group, possibly including the expiry date if relevant.
Interface for objects which can provide a MediaWiki context on request.
if(!isset( $args[0])) $lang