MediaWiki REL1_39
ActiveUsersPager.php
Go to the documentation of this file.
1<?php
28
37
41 protected $opts;
42
46 protected $groups;
47
51 private $blockStatusByUid;
52
54 private $RCMaxAge;
55
57 private $excludegroups;
58
67 public function __construct(
68 IContextSource $context,
69 HookContainer $hookContainer,
70 LinkBatchFactory $linkBatchFactory,
71 ILoadBalancer $loadBalancer,
72 UserGroupManager $userGroupManager,
74 ) {
75 parent::__construct(
76 $context,
77 $hookContainer,
78 $linkBatchFactory,
79 $loadBalancer,
80 $userGroupManager,
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(DISTINCT 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: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, 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.
getValue( $name)
Get the value for the given option name.
getSqlComment()
Get some text to go in brackets in the "function name" part of the SQL comment.
getDatabase()
Get the Database object in use.
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition Linker.php:1114
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:1159
A class containing constants representing the names of configuration variables.
Value object representing a user's identity.
This class is used to get a list of user.
buildGroupLink( $group, $username)
Format a link to a group description page.
getGroupMemberships( $user)
Get an associative array containing groups the specified user belongs to, and the relevant UserGroupM...
Interface for objects which can provide a MediaWiki context on request.
Create and track the database connections and transactions for a given database cluster.
if(!isset( $args[0])) $lang