MediaWiki master
ActiveUsersPager.php
Go to the documentation of this file.
1<?php
9
26use Wikimedia\Timestamp\ConvertibleTimestamp;
27use Wikimedia\Timestamp\TimestampFormat as TS;
28
37
41 protected $opts;
42
46 protected $groups;
47
51 private $blockStatusByUid;
52
54 private $RCMaxAge;
55
57 private $excludegroups;
58
59 public function __construct(
60 IContextSource $context,
61 HookContainer $hookContainer,
62 LinkBatchFactory $linkBatchFactory,
63 IConnectionProvider $dbProvider,
64 UserGroupManager $userGroupManager,
65 UserIdentityLookup $userIdentityLookup,
66 HideUserUtils $hideUserUtils,
67 TempUserConfig $tempUserConfig,
68 private readonly RecentChangeLookup $recentChangeLookup,
70 ) {
71 parent::__construct(
72 $context,
73 $hookContainer,
74 $linkBatchFactory,
75 $dbProvider,
76 $userGroupManager,
77 $userIdentityLookup,
78 $hideUserUtils,
79 $tempUserConfig,
80 null,
81 null
82 );
83
84 $this->RCMaxAge = $this->getConfig()->get( MainConfigNames::ActiveUserDays );
85 $this->requestedUser = '';
86
87 $un = $opts->getValue( 'username' );
88 if ( $un != '' ) {
89 $username = Title::makeTitleSafe( NS_USER, $un );
90 if ( $username !== null ) {
91 $this->requestedUser = $username->getText();
92 }
93 }
94
95 $this->groups = $opts->getValue( 'groups' );
96 $this->excludegroups = $opts->getValue( 'excludegroups' );
97 // Backwards-compatibility with old URLs
98 if ( $opts->getValue( 'hidebots' ) ) {
99 $this->excludegroups[] = 'bot';
100 }
101 if ( $opts->getValue( 'hidesysops' ) ) {
102 $this->excludegroups[] = 'sysop';
103 }
104 }
105
107 public function getIndexField() {
108 return 'qcc_title';
109 }
110
112 public function getQueryInfo( $data = null ) {
113 $dbr = $this->getDatabase();
114
115 $activeUserSeconds = $this->getConfig()->get( MainConfigNames::ActiveUserDays ) * 86400;
116 $timestamp = $dbr->timestamp( (int)ConvertibleTimestamp::now( TS::UNIX ) - $activeUserSeconds );
117 $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
118
119 // Inner subselect to pull the active users out of querycachetwo
120 $subquery = $dbr->newSelectQueryBuilder()
121 ->select( [ 'qcc_title', 'user_id', 'actor_id' ] )
122 ->from( 'querycachetwo' )
123 ->join( 'user', null, 'user_name = qcc_title' )
124 ->join( 'actor', null, 'actor_user = user_id' )
125 ->where( [
126 'qcc_type' => 'activeusers',
127 'qcc_namespace' => NS_USER,
128 ] )
129 ->caller( $fname );
130 if ( $data !== null ) {
131 $subquery
132 ->orderBy( 'qcc_title', $data['order'] )
133 ->limit( $data['limit'] )
134 ->andWhere( $data['conds'] );
135 }
136 if ( $this->requestedUser != '' ) {
137 $subquery->andWhere( $dbr->expr( 'qcc_title', '>=', $this->requestedUser ) );
138 }
139 if ( $this->groups !== [] ) {
140 $subquery
141 ->join( 'user_groups', 'ug1', 'ug1.ug_user = user_id' )
142 ->andWhere( [
143 'ug1.ug_group' => $this->groups,
144 $dbr->expr( 'ug1.ug_expiry', '=', null )->or( 'ug1.ug_expiry', '>=', $dbr->timestamp() ),
145 ] );
146 }
147 if ( $this->excludegroups !== [] ) {
148 $subquery
149 ->leftJoin( 'user_groups', 'ug2', [
150 'ug2.ug_user = user_id',
151 'ug2.ug_group' => $this->excludegroups,
152 $dbr->expr( 'ug2.ug_expiry', '=', null )->or( 'ug2.ug_expiry', '>=', $dbr->timestamp() ),
153 ] )
154 ->andWhere( [ 'ug2.ug_user' => null ] );
155 }
156 if ( !$this->canSeeHideuser() ) {
157 $subquery->andWhere( $this->hideUserUtils->getExpression( $dbr ) );
158 }
159
160 // Outer query to select the recent edit counts for the selected active users
161 return [
162 'tables' => [ 'qcc_users' => new Subquery( $subquery->getSQL() ), 'recentchanges' ],
163 'fields' => [
164 'qcc_title',
165 'user_name' => 'qcc_title',
166 'user_id' => 'user_id',
167 'recentedits' => 'COUNT(DISTINCT rc_id)'
168 ],
169 'options' => [ 'GROUP BY' => [ 'qcc_title', 'user_id' ] ],
170 'conds' => [],
171 'join_conds' => [ 'recentchanges' => [ 'LEFT JOIN', [
172 'rc_actor = actor_id',
173 $dbr->expr( 'rc_source', '=', $this->recentChangeLookup->getPrimarySources() ),
174 $dbr->expr( 'rc_log_type', '=', null )->or( 'rc_log_type', '!=', 'newusers' ),
175 $dbr->expr( 'rc_timestamp', '>=', $timestamp ),
176 ] ] ],
177 ];
178 }
179
181 protected function buildQueryInfo( $offset, $limit, $order ) {
182 $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
183
184 $sortColumns = [ $this->mIndexField, ...$this->mExtraSortFields ];
185 if ( $order === self::QUERY_ASCENDING ) {
186 $dir = 'ASC';
187 $orderBy = $sortColumns;
188 $operator = $this->mIncludeOffset ? '>=' : '>';
189 } else {
190 $dir = 'DESC';
191 $orderBy = [];
192 foreach ( $sortColumns as $col ) {
193 $orderBy[] = $col . ' DESC';
194 }
195 $operator = $this->mIncludeOffset ? '<=' : '<';
196 }
197 $info = $this->getQueryInfo( [
198 'limit' => intval( $limit ),
199 'order' => $dir,
200 'conds' =>
201 $offset != '' ? [ $this->getDatabase()->expr( $this->mIndexField, $operator, $offset ) ] : [],
202 ] );
203
204 $tables = $info['tables'];
205 $fields = $info['fields'];
206 $conds = $info['conds'];
207 $options = $info['options'];
208 $join_conds = $info['join_conds'];
209 $options['ORDER BY'] = $orderBy;
210 return [ $tables, $fields, $conds, $fname, $options, $join_conds ];
211 }
212
213 protected function doBatchLookups() {
214 parent::doBatchLookups();
215
216 $uids = [];
217 foreach ( $this->mResult as $row ) {
218 $uids[] = (int)$row->user_id;
219 }
220 // Fetch the block status of the user for showing "(blocked)" text and for
221 // striking out names of suppressed users when privileged user views the list.
222 // Although the first query already hits the block table for un-privileged, this
223 // is done in two queries to avoid huge quicksorts and to make COUNT(*) correct.
224 $dbr = $this->getDatabase();
225 $blockedUsers = $dbr->newSelectQueryBuilder()
226 ->select( [
227 'bt_user',
228 'sitewide' => 'MAX(bl_sitewide)'
229 ] )
230 ->from( 'block_target' )
231 ->join( 'block', null, 'bl_target=bt_id' )
232 ->where( [
233 'bt_user' => $uids,
234 $dbr->expr( 'bl_expiry', '>=', $dbr->timestamp() ),
235 ] )
236 ->groupBy( [ 'bt_user' ] )
237 ->caller( __METHOD__ )
238 ->fetchResultSet();
239
240 $hiddenUsers = $dbr->newSelectQueryBuilder()
241 ->select( 'bt_user' )
242 ->from( 'block_target' )
243 ->join( 'block', null, 'bl_target=bt_id' )
244 ->where( [
245 'bt_user' => $uids,
246 'bl_deleted' => 1,
247 $dbr->expr( 'bl_expiry', '>=', $dbr->timestamp() ),
248 ] )
249 ->groupBy( 'bt_user' )
250 ->caller( __METHOD__ )
251 ->fetchFieldValues();
252
253 $this->blockStatusByUid = [];
254 foreach ( $blockedUsers as $row ) {
255 $this->blockStatusByUid[$row->bt_user] = [
256 'deleted' => in_array( $row->bt_user, $hiddenUsers, true ),
257 'sitewide' => $row->sitewide,
258 ];
259 }
260 $this->mResult->seek( 0 );
261 }
262
264 public function formatRow( $row ) {
265 $userName = $row->user_name;
266
267 $ulinks = Linker::userLink( $row->user_id, $userName );
268 $ulinks .= Linker::userToolLinks(
269 $row->user_id,
270 $userName,
271 // Should the contributions link be red if the user has no edits (using default)
272 false,
273 // Customisation flags (using default 0)
274 0,
275 // User edit count (using default)
276 null,
277 // do not wrap the message in parentheses (CSS will provide these)
278 false
279 );
280
281 $lang = $this->getLanguage();
282
283 $list = [];
284
285 $userIdentity = new UserIdentityValue( intval( $row->user_id ), $userName );
286 $ugms = $this->getGroupMemberships( $userIdentity );
287 foreach ( $ugms as $ugm ) {
288 $list[] = $this->buildGroupLink( $ugm, $userName );
289 }
290
291 $groups = $lang->commaList( $list );
292
293 $item = $lang->specialList( $ulinks, $groups );
294
295 // If there is a block, 'deleted' and 'sitewide' are both set on
296 // $this->blockStatusByUid[$row->user_id].
297 $blocked = '';
298 $isBlocked = isset( $this->blockStatusByUid[$row->user_id] );
299 if ( $isBlocked ) {
300 if ( $this->blockStatusByUid[$row->user_id]['deleted'] == 1 ) {
301 $item = "<span class=\"deleted\">$item</span>";
302 }
303 if ( $this->blockStatusByUid[$row->user_id]['sitewide'] == 1 ) {
304 $blocked = ' ' . $this->msg( 'listusers-blocked', $userName )->escaped();
305 }
306 }
307 $count = $this->msg( 'activeusers-count' )->numParams( $row->recentedits )
308 ->params( $userName )->numParams( $this->RCMaxAge )->escaped();
309
310 return Html::rawElement( 'li', [], "{$item} [{$count}]{$blocked}" );
311 }
312
313}
314
319class_alias( ActiveUsersPager::class, 'ActiveUsersPager' );
320
322class_alias( ActiveUsersPager::class, 'MediaWiki\\Pager\\ActiveUsersPager' );
const NS_USER
Definition Defines.php:53
Helpers for building queries that determine whether a user is hidden.
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.
getValue( $name)
Get the value for the given option name.
This class is a collection of static functions that serve two purposes:
Definition Html.php:44
Some internal bits split of from Skin.php.
Definition Linker.php:48
A class containing constants representing the names of configuration variables.
const ActiveUserDays
Name constant for the ActiveUserDays setting, for use with Config::get()
Factory for LinkBatch objects to batch query page metadata.
getDatabase()
Get the Database object in use.
string string[] $mIndexField
The index to actually be used for ordering.
getSqlComment()
Get some text to go in brackets in the "function name" part of the SQL comment.
This class is used to get a list of active users.
buildQueryInfo( $offset, $limit, $order)
Build variables to use by the database wrapper.For b/c, query direction is true for ascending and fal...
doBatchLookups()
Called from getBody(), before getStartBody() is called and after doQuery() was called....
__construct(IContextSource $context, HookContainer $hookContainer, LinkBatchFactory $linkBatchFactory, IConnectionProvider $dbProvider, UserGroupManager $userGroupManager, UserIdentityLookup $userIdentityLookup, HideUserUtils $hideUserUtils, TempUserConfig $tempUserConfig, private readonly RecentChangeLookup $recentChangeLookup, FormOptions $opts,)
This class is used to get a list of user.
getGroupMemberships( $user)
Get an associative array containing groups the specified user belongs to, and the relevant UserGroupM...
buildGroupLink( $group, $username)
Format a link to a group description page.
Represents a title within MediaWiki.
Definition Title.php:69
Manage user group memberships.
Value object representing a user's identity.
Interface for objects which can provide a MediaWiki context on request.
Interface for temporary user creation config and name matching.
Service for looking up UserIdentity.
Provide primary and replica IDatabase connections.