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