MediaWiki  master
ActiveUsersPager.php
Go to the documentation of this file.
1 <?php
30 
39 
43  protected $opts;
44 
48  protected $groups;
49 
53  private $blockStatusByUid;
54 
56  private $RCMaxAge;
57 
59  private $excludegroups;
60 
70  public function __construct(
71  IContextSource $context,
72  HookContainer $hookContainer,
73  LinkBatchFactory $linkBatchFactory,
74  ILoadBalancer $loadBalancer,
75  UserGroupManager $userGroupManager,
76  UserIdentityLookup $userIdentityLookup,
78  ) {
79  parent::__construct(
80  $context,
81  $hookContainer,
82  $linkBatchFactory,
83  $loadBalancer,
84  $userGroupManager,
85  $userIdentityLookup,
86  null,
87  null
88  );
89 
90  $this->RCMaxAge = $this->getConfig()->get( MainConfigNames::ActiveUserDays );
91  $this->requestedUser = '';
92 
93  $un = $opts->getValue( 'username' );
94  if ( $un != '' ) {
95  $username = Title::makeTitleSafe( NS_USER, $un );
96  if ( $username !== null ) {
97  $this->requestedUser = $username->getText();
98  }
99  }
100 
101  $this->groups = $opts->getValue( 'groups' );
102  $this->excludegroups = $opts->getValue( 'excludegroups' );
103  // Backwards-compatibility with old URLs
104  if ( $opts->getValue( 'hidebots' ) ) {
105  $this->excludegroups[] = 'bot';
106  }
107  if ( $opts->getValue( 'hidesysops' ) ) {
108  $this->excludegroups[] = 'sysop';
109  }
110  }
111 
112  public function getIndexField() {
113  return 'qcc_title';
114  }
115 
116  public function getQueryInfo( $data = null ) {
117  $dbr = $this->getDatabase();
118 
119  $activeUserSeconds = $this->getConfig()->get( MainConfigNames::ActiveUserDays ) * 86400;
120  $timestamp = $dbr->timestamp( (int)wfTimestamp( TS_UNIX ) - $activeUserSeconds );
121  $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
122 
123  // Inner subselect to pull the active users out of querycachetwo
124  $tables = [ 'querycachetwo', 'user', 'actor' ];
125  $fields = [ 'qcc_title', 'user_id', 'actor_id' ];
126  $jconds = [
127  'user' => [ 'JOIN', 'user_name = qcc_title' ],
128  'actor' => [ 'JOIN', 'actor_user = user_id' ],
129  ];
130  $conds = [
131  'qcc_type' => 'activeusers',
132  'qcc_namespace' => NS_USER,
133  ];
134  $options = [];
135  if ( $data !== null ) {
136  $options['ORDER BY'] = 'qcc_title ' . $data['order'];
137  $options['LIMIT'] = $data['limit'];
138  $conds = array_merge( $conds, $data['conds'] );
139  }
140  if ( $this->requestedUser != '' ) {
141  $conds[] = 'qcc_title >= ' . $dbr->addQuotes( $this->requestedUser );
142  }
143  if ( $this->groups !== [] ) {
144  $tables['ug1'] = 'user_groups';
145  $jconds['ug1'] = [ 'JOIN', 'ug1.ug_user = user_id' ];
146  $conds['ug1.ug_group'] = $this->groups;
147  $conds[] = 'ug1.ug_expiry IS NULL OR ug1.ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() );
148  }
149  if ( $this->excludegroups !== [] ) {
150  $tables['ug2'] = 'user_groups';
151  $jconds['ug2'] = [ 'LEFT JOIN', [
152  'ug2.ug_user = user_id',
153  'ug2.ug_group' => $this->excludegroups,
154  'ug2.ug_expiry IS NULL OR ug2.ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ),
155  ] ];
156  $conds['ug2.ug_user'] = null;
157  }
158  if ( !$this->canSeeHideuser() ) {
159  $conds[] = 'NOT EXISTS (' . $dbr->selectSQLText(
160  'ipblocks', '1', [ 'ipb_user=user_id', 'ipb_deleted' => 1 ], __METHOD__
161  ) . ')';
162  }
163  $subquery = $dbr->buildSelectSubquery( $tables, $fields, $conds, $fname, $options, $jconds );
164 
165  // Outer query to select the recent edit counts for the selected active users
166  $tables = [ 'qcc_users' => $subquery, 'recentchanges' ];
167  $jconds = [ 'recentchanges' => [ 'LEFT JOIN', [
168  'rc_actor = actor_id',
169  'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ), // Don't count wikidata.
170  'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE ), // Don't count categorization changes.
171  'rc_log_type IS NULL OR rc_log_type != ' . $dbr->addQuotes( 'newusers' ),
172  'rc_timestamp >= ' . $dbr->addQuotes( $timestamp ),
173  ] ] ];
174  $conds = [];
175 
176  return [
177  'tables' => $tables,
178  'fields' => [
179  'qcc_title',
180  'user_name' => 'qcc_title',
181  'user_id' => 'user_id',
182  'recentedits' => 'COUNT(rc_id)'
183  ],
184  'options' => [ 'GROUP BY' => [ 'qcc_title', 'user_id' ] ],
185  'conds' => $conds,
186  'join_conds' => $jconds,
187  ];
188  }
189 
190  protected function buildQueryInfo( $offset, $limit, $order ) {
191  $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
192 
193  $sortColumns = array_merge( [ $this->mIndexField ], $this->mExtraSortFields );
194  if ( $order === self::QUERY_ASCENDING ) {
195  $dir = 'ASC';
196  $orderBy = $sortColumns;
197  $operator = $this->mIncludeOffset ? '>=' : '>';
198  } else {
199  $dir = 'DESC';
200  $orderBy = [];
201  foreach ( $sortColumns as $col ) {
202  $orderBy[] = $col . ' DESC';
203  }
204  $operator = $this->mIncludeOffset ? '<=' : '<';
205  }
206  $info = $this->getQueryInfo( [
207  'limit' => intval( $limit ),
208  'order' => $dir,
209  'conds' =>
210  $offset != '' ? [ $this->mIndexField . $operator . $this->getDatabase()->addQuotes( $offset ) ] : [],
211  ] );
212 
213  $tables = $info['tables'];
214  $fields = $info['fields'];
215  $conds = $info['conds'];
216  $options = $info['options'];
217  $join_conds = $info['join_conds'];
218  $options['ORDER BY'] = $orderBy;
219  return [ $tables, $fields, $conds, $fname, $options, $join_conds ];
220  }
221 
222  protected function doBatchLookups() {
223  parent::doBatchLookups();
224 
225  $uids = [];
226  foreach ( $this->mResult as $row ) {
227  $uids[] = $row->user_id;
228  }
229  // Fetch the block status of the user for showing "(blocked)" text and for
230  // striking out names of suppressed users when privileged user views the list.
231  // Although the first query already hits the block table for un-privileged, this
232  // is done in two queries to avoid huge quicksorts and to make COUNT(*) correct.
233  $dbr = $this->getDatabase();
234  $res = $dbr->select( 'ipblocks',
235  [ 'ipb_user', 'deleted' => 'MAX(ipb_deleted)', 'sitewide' => 'MAX(ipb_sitewide)' ],
236  [ 'ipb_user' => $uids ],
237  __METHOD__,
238  [ 'GROUP BY' => [ 'ipb_user' ] ]
239  );
240  $this->blockStatusByUid = [];
241  foreach ( $res as $row ) {
242  $this->blockStatusByUid[$row->ipb_user] = [
243  'deleted' => $row->deleted,
244  'sitewide' => $row->sitewide,
245  ];
246  }
247  $this->mResult->seek( 0 );
248  }
249 
250  public function formatRow( $row ) {
251  $userName = $row->user_name;
252 
253  $ulinks = Linker::userLink( $row->user_id, $userName );
254  $ulinks .= Linker::userToolLinks(
255  $row->user_id,
256  $userName,
257  // Should the contributions link be red if the user has no edits (using default)
258  false,
259  // Customisation flags (using default 0)
260  0,
261  // User edit count (using default)
262  null,
263  // do not wrap the message in parentheses (CSS will provide these)
264  false
265  );
266 
267  $lang = $this->getLanguage();
268 
269  $list = [];
270 
271  $userIdentity = new UserIdentityValue( intval( $row->user_id ), $userName );
272  $ugms = $this->getGroupMemberships( $userIdentity );
273  foreach ( $ugms as $ugm ) {
274  $list[] = $this->buildGroupLink( $ugm, $userName );
275  }
276 
277  $groups = $lang->commaList( $list );
278 
279  $item = $lang->specialList( $ulinks, $groups );
280 
281  // If there is a block, 'deleted' and 'sitewide' are both set on
282  // $this->blockStatusByUid[$row->user_id].
283  $blocked = '';
284  $isBlocked = isset( $this->blockStatusByUid[$row->user_id] );
285  if ( $isBlocked ) {
286  if ( $this->blockStatusByUid[$row->user_id]['deleted'] == 1 ) {
287  $item = "<span class=\"deleted\">$item</span>";
288  }
289  if ( $this->blockStatusByUid[$row->user_id]['sitewide'] == 1 ) {
290  $blocked = ' ' . $this->msg( 'listusers-blocked', $userName )->escaped();
291  }
292  }
293  $count = $this->msg( 'activeusers-count' )->numParams( $row->recentedits )
294  ->params( $userName )->numParams( $this->RCMaxAge )->escaped();
295 
296  return Html::rawElement( 'li', [], "{$item} [{$count}]{$blocked}" );
297  }
298 
299 }
const NS_USER
Definition: Defines.php:66
const RC_EXTERNAL
Definition: Defines.php:119
const RC_CATEGORIZE
Definition: Defines.php:120
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, UserIdentityLookup $userIdentityLookup, 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()
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
Some internal bits split of from Skin.php.
Definition: Linker.php:65
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:667
This class is used to get a list of user.
Definition: UsersPager.php:44
buildGroupLink( $group, $username)
Format a link to a group description page.
Definition: UsersPager.php:492
getGroupMemberships( $user)
Get an associative array containing groups the specified user belongs to, and the relevant UserGroupM...
Definition: UsersPager.php:477
Interface for objects which can provide a MediaWiki context on request.
This class is a delegate to ILBFactory for a given database cluster.
if(!isset( $args[0])) $lang