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