MediaWiki  master
UsersPager.php
Go to the documentation of this file.
1 <?php
32 
40 class UsersPager extends AlphabeticPager {
41 
45  protected $userGroupCache;
46 
49 
51  protected $editsOnly;
52 
55 
57  protected $creationSort;
58 
60  protected $including;
61 
63  protected $requestedUser;
64 
67 
69  private $hookRunner;
70 
73 
76 
88  public function __construct(
90  $par,
91  $including,
93  HookContainer $hookContainer,
95  ILoadBalancer $loadBalancer,
97  ) {
98  if ( $context ) {
99  $this->setContext( $context );
100  }
101 
102  $request = $this->getRequest();
103  $par = $par ?? '';
104  $parms = explode( '/', $par );
105  $symsForAll = [ '*', 'user' ];
106 
107  if ( $parms[0] != '' &&
108  ( in_array( $par, $userGroupManager->listAllGroups() ) || in_array( $par, $symsForAll ) )
109  ) {
110  $this->requestedGroup = $par;
111  $un = $request->getText( 'username' );
112  } elseif ( count( $parms ) == 2 ) {
113  $this->requestedGroup = $parms[0];
114  $un = $parms[1];
115  } else {
116  $this->requestedGroup = $request->getVal( 'group' );
117  $un = ( $par != '' ) ? $par : $request->getText( 'username' );
118  }
119 
120  if ( in_array( $this->requestedGroup, $symsForAll ) ) {
121  $this->requestedGroup = '';
122  }
123  $this->editsOnly = $request->getBool( 'editsOnly' );
124  $this->temporaryGroupsOnly = $request->getBool( 'temporaryGroupsOnly' );
125  $this->creationSort = $request->getBool( 'creationSort' );
126  $this->including = $including;
127  $this->mDefaultDirection = $request->getBool( 'desc' )
130 
131  $this->requestedUser = '';
132 
133  if ( $un != '' ) {
134  $username = Title::makeTitleSafe( NS_USER, $un );
135 
136  if ( $username !== null ) {
137  $this->requestedUser = $username->getText();
138  }
139  }
140 
141  // Set database before parent constructor to avoid setting it there with wfGetDB
142  $this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA );
143  parent::__construct();
144  $this->linkBatchFactory = $linkBatchFactory;
145  $this->hookRunner = new HookRunner( $hookContainer );
146  $this->permissionManager = $permissionManager;
147  $this->userGroupManager = $userGroupManager;
148  }
149 
153  public function getIndexField() {
154  return $this->creationSort ? 'user_id' : 'user_name';
155  }
156 
160  public function getQueryInfo() {
161  $dbr = $this->getDatabase();
162  $conds = [];
163 
164  // Don't show hidden names
165  if ( !$this->canSeeHideuser() ) {
166  $conds[] = 'ipb_deleted IS NULL OR ipb_deleted = 0';
167  }
168 
169  $options = [];
170 
171  if ( $this->requestedGroup != '' || $this->temporaryGroupsOnly ) {
172  $conds[] = 'ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ) .
173  ( !$this->temporaryGroupsOnly ? ' OR ug_expiry IS NULL' : '' );
174  }
175 
176  if ( $this->requestedGroup != '' ) {
177  $conds['ug_group'] = $this->requestedGroup;
178  }
179 
180  if ( $this->requestedUser != '' ) {
181  # Sorted either by account creation or name
182  if ( $this->creationSort ) {
183  $conds[] = 'user_id >= ' . intval( User::idFromName( $this->requestedUser ) );
184  } else {
185  $conds[] = 'user_name >= ' . $dbr->addQuotes( $this->requestedUser );
186  }
187  }
188 
189  if ( $this->editsOnly ) {
190  $conds[] = 'user_editcount > 0';
191  }
192 
193  $options['GROUP BY'] = $this->creationSort ? 'user_id' : 'user_name';
194 
195  $query = [
196  'tables' => [ 'user', 'user_groups', 'ipblocks' ],
197  'fields' => [
198  'user_name' => $this->creationSort ? 'MAX(user_name)' : 'user_name',
199  'user_id' => $this->creationSort ? 'user_id' : 'MAX(user_id)',
200  'edits' => 'MAX(user_editcount)',
201  'creation' => 'MIN(user_registration)',
202  'ipb_deleted' => 'MAX(ipb_deleted)', // block/hide status
203  'ipb_sitewide' => 'MAX(ipb_sitewide)'
204  ],
205  'options' => $options,
206  'join_conds' => [
207  'user_groups' => [ 'LEFT JOIN', 'user_id=ug_user' ],
208  'ipblocks' => [
209  'LEFT JOIN', [
210  'user_id=ipb_user',
211  'ipb_auto' => 0
212  ]
213  ],
214  ],
215  'conds' => $conds
216  ];
217 
218  $this->hookRunner->onSpecialListusersQueryInfo( $this, $query );
219 
220  return $query;
221  }
222 
227  public function formatRow( $row ) {
228  if ( $row->user_id == 0 ) { # T18487
229  return '';
230  }
231 
232  $userName = $row->user_name;
233 
234  $ulinks = Linker::userLink( $row->user_id, $userName );
236  $row->user_id,
237  $userName,
238  (int)$row->edits,
239  // don't render parentheses in HTML markup (CSS will provide)
240  false
241  );
242 
243  $lang = $this->getLanguage();
244 
245  $groups = '';
246  $ugms = self::getGroupMemberships( intval( $row->user_id ), $this->userGroupCache );
247 
248  if ( !$this->including && count( $ugms ) > 0 ) {
249  $list = [];
250  foreach ( $ugms as $ugm ) {
251  $list[] = $this->buildGroupLink( $ugm, $userName );
252  }
253  $groups = $lang->commaList( $list );
254  }
255 
256  $item = $lang->specialList( $ulinks, $groups );
257 
258  if ( $row->ipb_deleted ) {
259  $item = "<span class=\"deleted\">$item</span>";
260  }
261 
262  $edits = '';
263  if ( !$this->including && $this->getConfig()->get( 'Edititis' ) ) {
264  $count = $this->msg( 'usereditcount' )->numParams( $row->edits )->escaped();
265  $edits = $this->msg( 'word-separator' )->escaped() . $this->msg( 'brackets', $count )->escaped();
266  }
267 
268  $created = '';
269  # Some rows may be null
270  if ( !$this->including && $row->creation ) {
271  $user = $this->getUser();
272  $d = $lang->userDate( $row->creation, $user );
273  $t = $lang->userTime( $row->creation, $user );
274  $created = $this->msg( 'usercreated', $d, $t, $row->user_name )->escaped();
275  $created = ' ' . $this->msg( 'parentheses' )->rawParams( $created )->escaped();
276  }
277 
278  $blocked = $row->ipb_deleted !== null && $row->ipb_sitewide === '1' ?
279  ' ' . $this->msg( 'listusers-blocked', $userName )->escaped() :
280  '';
281 
282  $this->hookRunner->onSpecialListusersFormatRow( $item, $row );
283 
284  return Html::rawElement( 'li', [], "{$item}{$edits}{$created}{$blocked}" );
285  }
286 
287  protected function doBatchLookups() {
288  $batch = $this->linkBatchFactory->newLinkBatch();
289  $userIds = [];
290  # Give some pointers to make user links
291  foreach ( $this->mResult as $row ) {
292  $batch->add( NS_USER, $row->user_name );
293  $batch->add( NS_USER_TALK, $row->user_name );
294  $userIds[] = $row->user_id;
295  }
296 
297  // Lookup groups for all the users
298  $dbr = $this->getDatabase();
299  $groupsQueryInfo = $this->userGroupManager->getQueryInfo();
300  $groupRes = $dbr->select(
301  $groupsQueryInfo['tables'],
302  $groupsQueryInfo['fields'],
303  [ 'ug_user' => $userIds ],
304  __METHOD__,
305  $groupsQueryInfo['joins']
306  );
307  $cache = [];
308  $groups = [];
309  foreach ( $groupRes as $row ) {
310  $ugm = $this->userGroupManager->newGroupMembershipFromRow( $row );
311  if ( !$ugm->isExpired() ) {
312  $cache[$row->ug_user][$row->ug_group] = $ugm;
313  $groups[$row->ug_group] = true;
314  }
315  }
316 
317  // Give extensions a chance to add things like global user group data
318  // into the cache array to ensure proper output later on
319  $this->hookRunner->onUsersPagerDoBatchLookups( $dbr, $userIds, $cache, $groups );
320 
321  $this->userGroupCache = $cache;
322 
323  // Add page of groups to link batch
324  foreach ( $groups as $group => $unused ) {
325  $groupPage = UserGroupMembership::getGroupPage( $group );
326  if ( $groupPage ) {
327  $batch->addObj( $groupPage );
328  }
329  }
330 
331  $batch->execute();
332  $this->mResult->rewind();
333  }
334 
338  public function getPageHeader() {
339  $self = explode( '/', $this->getTitle()->getPrefixedDBkey(), 2 )[0];
340 
341  $groupOptions = [ $this->msg( 'group-all' )->text() => '' ];
342  foreach ( $this->getAllGroups() as $group => $groupText ) {
343  $groupOptions[ $groupText ] = $group;
344  }
345 
346  $formDescriptor = [
347  'user' => [
348  'class' => HTMLUserTextField::class,
349  'label' => $this->msg( 'listusersfrom' )->text(),
350  'name' => 'username',
351  'default' => $this->requestedUser,
352  ],
353  'dropdown' => [
354  'label' => $this->msg( 'group' )->text(),
355  'name' => 'group',
356  'default' => $this->requestedGroup,
357  'class' => HTMLSelectField::class,
358  'options' => $groupOptions,
359  ],
360  'editsOnly' => [
361  'type' => 'check',
362  'label' => $this->msg( 'listusers-editsonly' )->text(),
363  'name' => 'editsOnly',
364  'id' => 'editsOnly',
365  'default' => $this->editsOnly
366  ],
367  'temporaryGroupsOnly' => [
368  'type' => 'check',
369  'label' => $this->msg( 'listusers-temporarygroupsonly' )->text(),
370  'name' => 'temporaryGroupsOnly',
371  'id' => 'temporaryGroupsOnly',
372  'default' => $this->temporaryGroupsOnly
373  ],
374  'creationSort' => [
375  'type' => 'check',
376  'label' => $this->msg( 'listusers-creationsort' )->text(),
377  'name' => 'creationSort',
378  'id' => 'creationSort',
379  'default' => $this->creationSort
380  ],
381  'desc' => [
382  'type' => 'check',
383  'label' => $this->msg( 'listusers-desc' )->text(),
384  'name' => 'desc',
385  'id' => 'desc',
386  'default' => $this->mDefaultDirection
387  ],
388  'limithiddenfield' => [
389  'class' => HTMLHiddenField::class,
390  'name' => 'limit',
391  'default' => $this->mLimit
392  ]
393  ];
394 
395  $beforeSubmitButtonHookOut = '';
396  $this->hookRunner->onSpecialListusersHeaderForm( $this, $beforeSubmitButtonHookOut );
397 
398  if ( $beforeSubmitButtonHookOut !== '' ) {
399  $formDescriptor[ 'beforeSubmitButtonHookOut' ] = [
400  'class' => HTMLInfoField::class,
401  'raw' => true,
402  'default' => $beforeSubmitButtonHookOut
403  ];
404  }
405 
406  $formDescriptor[ 'submit' ] = [
407  'class' => HTMLSubmitField::class,
408  'buttonlabel-message' => 'listusers-submit',
409  ];
410 
411  $beforeClosingFieldsetHookOut = '';
412  $this->hookRunner->onSpecialListusersHeader( $this, $beforeClosingFieldsetHookOut );
413 
414  if ( $beforeClosingFieldsetHookOut !== '' ) {
415  $formDescriptor[ 'beforeClosingFieldsetHookOut' ] = [
416  'class' => HTMLInfoField::class,
417  'raw' => true,
418  'default' => $beforeClosingFieldsetHookOut
419  ];
420  }
421 
422  $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
423  $htmlForm
424  ->setMethod( 'get' )
425  ->setAction( Title::newFromText( $self )->getLocalURL() )
426  ->setId( 'mw-listusers-form' )
427  ->setFormIdentifier( 'mw-listusers-form' )
428  ->suppressDefaultSubmit()
429  ->setWrapperLegendMsg( 'listusers' );
430  return $htmlForm->prepareForm()->getHTML( true );
431  }
432 
433  protected function canSeeHideuser() {
434  return $this->permissionManager->userHasRight( $this->getUser(), 'hideuser' );
435  }
436 
441  private function getAllGroups() {
442  $result = [];
443  foreach ( $this->userGroupManager->listAllGroups() as $group ) {
444  $result[$group] = UserGroupMembership::getGroupName( $group );
445  }
446  asort( $result );
447 
448  return $result;
449  }
450 
455  public function getDefaultQuery() {
456  $query = parent::getDefaultQuery();
457  if ( $this->requestedGroup != '' ) {
458  $query['group'] = $this->requestedGroup;
459  }
460  if ( $this->requestedUser != '' ) {
461  $query['username'] = $this->requestedUser;
462  }
463  $this->hookRunner->onSpecialListusersDefaultQuery( $this, $query );
464 
465  return $query;
466  }
467 
476  protected static function getGroupMemberships( $uid, $cache = null ) {
477  if ( $cache === null ) {
478  $user = User::newFromId( $uid );
479  return $user->getGroupMemberships();
480  } else {
481  return $cache[$uid] ?? [];
482  }
483  }
484 
492  protected function buildGroupLink( $group, $username ) {
493  return UserGroupMembership::getLink( $group, $this->getContext(), 'html', $username );
494  }
495 }
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:37
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:70
IndexPager\$mLimit
int $mLimit
The maximum number of entries to show.
Definition: IndexPager.php:96
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:584
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:328
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:45
IndexPager\$mDefaultDirection
bool $mDefaultDirection
$mDefaultDirection gives the direction to use when sorting results: DIR_ASCENDING or DIR_DESCENDING.
Definition: IndexPager.php:133
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:896
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
UsersPager\$creationSort
bool $creationSort
Definition: UsersPager.php:57
UsersPager\getIndexField
getIndexField()
Definition: UsersPager.php:153
UsersPager\buildGroupLink
buildGroupLink( $group, $username)
Format a link to a group description page.
Definition: UsersPager.php:492
UsersPager\$linkBatchFactory
LinkBatchFactory $linkBatchFactory
Definition: UsersPager.php:66
UserGroupMembership\getGroupName
static getGroupName( $group)
Gets the localized friendly name for a group, if it exists.
Definition: UserGroupMembership.php:186
UsersPager\$editsOnly
bool $editsOnly
Definition: UsersPager.php:51
IndexPager\getDatabase
getDatabase()
Get the Database object in use.
Definition: IndexPager.php:244
UsersPager\$requestedGroup
string $requestedGroup
Definition: UsersPager.php:48
AlphabeticPager
IndexPager with an alphabetic list and a formatted navigation bar Stable to extend.
Definition: AlphabeticPager.php:29
ContextSource\getRequest
getRequest()
Definition: ContextSource.php:79
UsersPager\__construct
__construct(?IContextSource $context, $par, $including, LinkBatchFactory $linkBatchFactory, HookContainer $hookContainer, PermissionManager $permissionManager, ILoadBalancer $loadBalancer, UserGroupManager $userGroupManager)
Definition: UsersPager.php:88
ContextSource\getUser
getUser()
Stable to override.
Definition: ContextSource.php:134
ContextSource\getTitle
getTitle()
Definition: ContextSource.php:88
UserGroupMembership\getGroupPage
static getGroupPage( $group)
Gets the title of a page describing a particular user group.
Definition: UserGroupMembership.php:211
IndexPager\DIR_ASCENDING
const DIR_ASCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
Definition: IndexPager.php:78
UsersPager\getPageHeader
getPageHeader()
Definition: UsersPager.php:338
Wikimedia\Rdbms\ILoadBalancer\getConnectionRef
getConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Get a live database handle reference for a real or virtual (DB_MASTER/DB_REPLICA) server index.
UsersPager\$hookRunner
HookRunner $hookRunner
Definition: UsersPager.php:69
UsersPager\$requestedUser
string $requestedUser
Definition: UsersPager.php:63
$dbr
$dbr
Definition: testCompression.php:54
ContextSource\getLanguage
getLanguage()
Definition: ContextSource.php:143
MediaWiki\User\UserGroupManager
Managers user groups.
Definition: UserGroupManager.php:51
UsersPager\getDefaultQuery
getDefaultQuery()
Preserve group and username offset parameters when paging.
Definition: UsersPager.php:455
UserGroupMembership\getLink
static getLink( $ugm, IContextSource $context, $format, $userName=null)
Gets a link for a user group, possibly including the expiry date if relevant.
Definition: UserGroupMembership.php:121
UsersPager\doBatchLookups
doBatchLookups()
Called from getBody(), before getStartBody() is called and after doQuery() was called.
Definition: UsersPager.php:287
MediaWiki\Cache\LinkBatchFactory
Definition: LinkBatchFactory.php:38
UsersPager\$including
bool null $including
Definition: UsersPager.php:60
UsersPager
This class is used to get a list of user.
Definition: UsersPager.php:40
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
UsersPager\$permissionManager
PermissionManager $permissionManager
Definition: UsersPager.php:72
UsersPager\$temporaryGroupsOnly
bool $temporaryGroupsOnly
Definition: UsersPager.php:54
ContextSource\setContext
setContext(IContextSource $context)
Definition: ContextSource.php:61
UsersPager\getAllGroups
getAllGroups()
Get a list of all explicit groups.
Definition: UsersPager.php:441
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:187
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:617
MediaWiki\Permissions\PermissionManager
A service class for checking permissions To obtain an instance, use MediaWikiServices::getInstance()-...
Definition: PermissionManager.php:50
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:72
UsersPager\formatRow
formatRow( $row)
Definition: UsersPager.php:227
UsersPager\$userGroupManager
UserGroupManager $userGroupManager
Definition: UsersPager.php:75
Linker\userToolLinksRedContribs
static userToolLinksRedContribs( $userId, $userText, $edits=null, $useParentheses=true)
Alias for userToolLinks( $userId, $userText, true );.
Definition: Linker.php:1023
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
UsersPager\getGroupMemberships
static getGroupMemberships( $uid, $cache=null)
Get an associative array containing groups the specified user belongs to, and the relevant UserGroupM...
Definition: UsersPager.php:476
IndexPager\DIR_DESCENDING
const DIR_DESCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
Definition: IndexPager.php:80
UsersPager\$userGroupCache
array[] $userGroupCache
A array with user ids as key and a array of groups as value.
Definition: UsersPager.php:45
$self
$self
Definition: doMaintenance.php:63
UsersPager\getQueryInfo
getQueryInfo()
Definition: UsersPager.php:160
$cache
$cache
Definition: mcc.php:33
User\idFromName
static idFromName( $name, $flags=self::READ_NORMAL)
Get database id given a user name.
Definition: User.php:892
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:212
NS_USER
const NS_USER
Definition: Defines.php:71
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:567
$t
$t
Definition: testCompression.php:74
MediaWiki\User\UserGroupManager\listAllGroups
listAllGroups()
Return the set of defined explicit groups.
Definition: UserGroupManager.php:174
HTMLForm\factory
static factory( $displayFormat,... $arguments)
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:316
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
UsersPager\canSeeHideuser
canSeeHideuser()
Definition: UsersPager.php:433