MediaWiki  master
ActiveUsersPager.php
Go to the documentation of this file.
1 <?php
28 
37 
41  protected $opts;
42 
46  protected $groups;
47 
52 
54  private $RCMaxAge;
55 
57  private $excludegroups;
58 
67  public function __construct(
69  HookContainer $hookContainer,
71  ILoadBalancer $loadBalancer,
74  ) {
75  parent::__construct(
76  $context,
77  $hookContainer,
79  $loadBalancer,
81  null,
82  null
83  );
84 
85  $this->RCMaxAge = $this->getConfig()->get( MainConfigNames::ActiveUserDays );
86  $this->requestedUser = '';
87 
88  $un = $opts->getValue( 'username' );
89  if ( $un != '' ) {
90  $username = Title::makeTitleSafe( NS_USER, $un );
91  if ( $username !== null ) {
92  $this->requestedUser = $username->getText();
93  }
94  }
95 
96  $this->groups = $opts->getValue( 'groups' );
97  $this->excludegroups = $opts->getValue( 'excludegroups' );
98  // Backwards-compatibility with old URLs
99  if ( $opts->getValue( 'hidebots' ) ) {
100  $this->excludegroups[] = 'bot';
101  }
102  if ( $opts->getValue( 'hidesysops' ) ) {
103  $this->excludegroups[] = 'sysop';
104  }
105  }
106 
107  public function getIndexField() {
108  return 'qcc_title';
109  }
110 
111  public function getQueryInfo( $data = null ) {
112  $dbr = $this->getDatabase();
113 
114  $activeUserSeconds = $this->getConfig()->get( MainConfigNames::ActiveUserDays ) * 86400;
115  $timestamp = $dbr->timestamp( (int)wfTimestamp( TS_UNIX ) - $activeUserSeconds );
116  $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
117 
118  // Inner subselect to pull the active users out of querycachetwo
119  $tables = [ 'querycachetwo', 'user', 'actor' ];
120  $fields = [ 'qcc_title', 'user_id', 'actor_id' ];
121  $jconds = [
122  'user' => [ 'JOIN', 'user_name = qcc_title' ],
123  'actor' => [ 'JOIN', 'actor_user = user_id' ],
124  ];
125  $conds = [
126  'qcc_type' => 'activeusers',
127  'qcc_namespace' => NS_USER,
128  ];
129  $options = [];
130  if ( $data !== null ) {
131  $options['ORDER BY'] = 'qcc_title ' . $data['order'];
132  $options['LIMIT'] = $data['limit'];
133  $conds = array_merge( $conds, $data['conds'] );
134  }
135  if ( $this->requestedUser != '' ) {
136  $conds[] = 'qcc_title >= ' . $dbr->addQuotes( $this->requestedUser );
137  }
138  if ( $this->groups !== [] ) {
139  $tables['ug1'] = 'user_groups';
140  $jconds['ug1'] = [ 'JOIN', 'ug1.ug_user = user_id' ];
141  $conds['ug1.ug_group'] = $this->groups;
142  $conds[] = 'ug1.ug_expiry IS NULL OR ug1.ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() );
143  }
144  if ( $this->excludegroups !== [] ) {
145  $tables['ug2'] = 'user_groups';
146  $jconds['ug2'] = [ 'LEFT JOIN', [
147  'ug2.ug_user = user_id',
148  'ug2.ug_group' => $this->excludegroups,
149  'ug2.ug_expiry IS NULL OR ug2.ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ),
150  ] ];
151  $conds['ug2.ug_user'] = null;
152  }
153  if ( !$this->canSeeHideuser() ) {
154  $conds[] = 'NOT EXISTS (' . $dbr->selectSQLText(
155  'ipblocks', '1', [ 'ipb_user=user_id', 'ipb_deleted' => 1 ], __METHOD__
156  ) . ')';
157  }
158  $subquery = $dbr->buildSelectSubquery( $tables, $fields, $conds, $fname, $options, $jconds );
159 
160  // Outer query to select the recent edit counts for the selected active users
161  $tables = [ 'qcc_users' => $subquery, 'recentchanges' ];
162  $jconds = [ 'recentchanges' => [ 'LEFT JOIN', [
163  'rc_actor = actor_id',
164  'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Don't count wikidata.
165  'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE ), // Don't count categorization changes.
166  'rc_log_type IS NULL OR rc_log_type != ' . $dbr->addQuotes( 'newusers' ),
167  'rc_timestamp >= ' . $dbr->addQuotes( $timestamp ),
168  ] ] ];
169  $conds = [];
170 
171  return [
172  'tables' => $tables,
173  'fields' => [
174  'qcc_title',
175  'user_name' => 'qcc_title',
176  'user_id' => 'user_id',
177  'recentedits' => 'COUNT(rc_id)'
178  ],
179  'options' => [ 'GROUP BY' => [ 'qcc_title', 'user_id' ] ],
180  'conds' => $conds,
181  'join_conds' => $jconds,
182  ];
183  }
184 
185  protected function buildQueryInfo( $offset, $limit, $order ) {
186  $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
187 
188  $sortColumns = array_merge( [ $this->mIndexField ], $this->mExtraSortFields );
189  if ( $order === self::QUERY_ASCENDING ) {
190  $dir = 'ASC';
191  $orderBy = $sortColumns;
192  $operator = $this->mIncludeOffset ? '>=' : '>';
193  } else {
194  $dir = 'DESC';
195  $orderBy = [];
196  foreach ( $sortColumns as $col ) {
197  $orderBy[] = $col . ' DESC';
198  }
199  $operator = $this->mIncludeOffset ? '<=' : '<';
200  }
201  $info = $this->getQueryInfo( [
202  'limit' => intval( $limit ),
203  'order' => $dir,
204  'conds' =>
205  $offset != '' ? [ $this->mIndexField . $operator . $this->getDatabase()->addQuotes( $offset ) ] : [],
206  ] );
207 
208  $tables = $info['tables'];
209  $fields = $info['fields'];
210  $conds = $info['conds'];
211  $options = $info['options'];
212  $join_conds = $info['join_conds'];
213  $options['ORDER BY'] = $orderBy;
214  return [ $tables, $fields, $conds, $fname, $options, $join_conds ];
215  }
216 
217  protected function doBatchLookups() {
218  parent::doBatchLookups();
219 
220  $uids = [];
221  foreach ( $this->mResult as $row ) {
222  $uids[] = $row->user_id;
223  }
224  // Fetch the block status of the user for showing "(blocked)" text and for
225  // striking out names of suppressed users when privileged user views the list.
226  // Although the first query already hits the block table for un-privileged, this
227  // is done in two queries to avoid huge quicksorts and to make COUNT(*) correct.
228  $dbr = $this->getDatabase();
229  $res = $dbr->select( 'ipblocks',
230  [ 'ipb_user', 'deleted' => 'MAX(ipb_deleted)', 'sitewide' => 'MAX(ipb_sitewide)' ],
231  [ 'ipb_user' => $uids ],
232  __METHOD__,
233  [ 'GROUP BY' => [ 'ipb_user' ] ]
234  );
235  $this->blockStatusByUid = [];
236  foreach ( $res as $row ) {
237  $this->blockStatusByUid[$row->ipb_user] = [
238  'deleted' => $row->deleted,
239  'sitewide' => $row->sitewide,
240  ];
241  }
242  $this->mResult->seek( 0 );
243  }
244 
245  public function formatRow( $row ) {
246  $userName = $row->user_name;
247 
248  $ulinks = Linker::userLink( $row->user_id, $userName );
249  $ulinks .= Linker::userToolLinks(
250  $row->user_id,
251  $userName,
252  // Should the contributions link be red if the user has no edits (using default)
253  false,
254  // Customisation flags (using default 0)
255  0,
256  // User edit count (using default)
257  null,
258  // do not wrap the message in parentheses (CSS will provide these)
259  false
260  );
261 
262  $lang = $this->getLanguage();
263 
264  $list = [];
265 
266  $userIdentity = new UserIdentityValue( intval( $row->user_id ), $userName );
267  $ugms = $this->getGroupMemberships( $userIdentity );
268  foreach ( $ugms as $ugm ) {
269  $list[] = $this->buildGroupLink( $ugm, $userName );
270  }
271 
272  $groups = $lang->commaList( $list );
273 
274  $item = $lang->specialList( $ulinks, $groups );
275 
276  // If there is a block, 'deleted' and 'sitewide' are both set on
277  // $this->blockStatusByUid[$row->user_id].
278  $blocked = '';
279  $isBlocked = isset( $this->blockStatusByUid[$row->user_id] );
280  if ( $isBlocked ) {
281  if ( $this->blockStatusByUid[$row->user_id]['deleted'] == 1 ) {
282  $item = "<span class=\"deleted\">$item</span>";
283  }
284  if ( $this->blockStatusByUid[$row->user_id]['sitewide'] == 1 ) {
285  $blocked = ' ' . $this->msg( 'listusers-blocked', $userName )->escaped();
286  }
287  }
288  $count = $this->msg( 'activeusers-count' )->numParams( $row->recentedits )
289  ->params( $userName )->numParams( $this->RCMaxAge )->escaped();
290 
291  return Html::rawElement( 'li', [], "{$item} [{$count}]{$blocked}" );
292  }
293 
294 }
const NS_USER
Definition: Defines.php:66
const RC_EXTERNAL
Definition: Defines.php:118
const RC_CATEGORIZE
Definition: Defines.php:119
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
This class is used to get a list of active users.
getQueryInfo( $data=null)
__construct(IContextSource $context, HookContainer $hookContainer, LinkBatchFactory $linkBatchFactory, ILoadBalancer $loadBalancer, UserGroupManager $userGroupManager, FormOptions $opts)
buildQueryInfo( $offset, $limit, $order)
Build variables to use by the database wrapper.
doBatchLookups()
Called from getBody(), before getStartBody() is called and after doQuery() was called.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
IContextSource $context
Helper class to keep track of options when mixing links and form elements.
Definition: FormOptions.php:35
getValue( $name)
Get the value for the given option name.
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:214
getSqlComment()
Get some text to go in brackets in the "function name" part of the SQL comment.
Definition: IndexPager.php:439
getDatabase()
Get the Database object in use.
Definition: IndexPager.php:248
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:1069
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null, $useParentheses=true)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition: Linker.php:1114
A class containing constants representing the names of configuration variables.
Value object representing a user's identity.
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:664
This class is used to get a list of user.
Definition: UsersPager.php:42
buildGroupLink( $group, $username)
Format a link to a group description page.
Definition: UsersPager.php:486
getGroupMemberships( $user)
Get an associative array containing groups the specified user belongs to, and the relevant UserGroupM...
Definition: UsersPager.php:471
LinkBatchFactory $linkBatchFactory
Definition: UsersPager.php:71
UserGroupManager $userGroupManager
Definition: UsersPager.php:74
Interface for objects which can provide a MediaWiki context on request.
Database cluster connection, tracking, load balancing, and transaction manager interface.
if(!isset( $args[0])) $lang