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