MediaWiki  1.23.1
SpecialActiveusers.php
Go to the documentation of this file.
1 <?php
34 
38  protected $opts;
39 
43  protected $hideGroups = array();
44 
48  protected $hideRights = array();
49 
55  function __construct( IContextSource $context = null, $group = null, $par = null ) {
56  global $wgActiveUserDays;
57 
58  parent::__construct( $context );
59 
60  $this->RCMaxAge = $wgActiveUserDays;
61  $un = $this->getRequest()->getText( 'username', $par );
62  $this->requestedUser = '';
63  if ( $un != '' ) {
64  $username = Title::makeTitleSafe( NS_USER, $un );
65  if ( !is_null( $username ) ) {
66  $this->requestedUser = $username->getText();
67  }
68  }
69 
70  $this->setupOptions();
71  }
72 
73  public function setupOptions() {
74  $this->opts = new FormOptions();
75 
76  $this->opts->add( 'hidebots', false, FormOptions::BOOL );
77  $this->opts->add( 'hidesysops', false, FormOptions::BOOL );
78 
79  $this->opts->fetchValuesFromRequest( $this->getRequest() );
80 
81  if ( $this->opts->getValue( 'hidebots' ) == 1 ) {
82  $this->hideRights[] = 'bot';
83  }
84  if ( $this->opts->getValue( 'hidesysops' ) == 1 ) {
85  $this->hideGroups[] = 'sysop';
86  }
87  }
88 
89  function getIndexField() {
90  return 'qcc_title';
91  }
92 
93  function getQueryInfo() {
94  $dbr = $this->getDatabase();
95 
96  $conds = array(
97  'qcc_type' => 'activeusers',
98  'qcc_namespace' => NS_USER,
99  'user_name = qcc_title',
100  'rc_user_text = qcc_title',
101  'rc_type != ' . $dbr->addQuotes( RC_EXTERNAL ) // Don't count wikidata.
102  );
103  if ( $this->requestedUser != '' ) {
104  $conds[] = 'qcc_title >= ' . $dbr->addQuotes( $this->requestedUser );
105  }
106  if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
107  $conds[] = 'NOT EXISTS (' . $dbr->selectSQLText(
108  'ipblocks', '1', array( 'ipb_user=user_id', 'ipb_deleted' => 1 )
109  ) . ')';
110  }
111 
112  return array(
113  'tables' => array( 'querycachetwo', 'user', 'recentchanges' ),
114  'fields' => array( 'user_name', 'user_id', 'recentedits' => 'COUNT(*)', 'qcc_title' ),
115  'options' => array( 'GROUP BY' => array( 'qcc_title' ) ),
116  'conds' => $conds
117  );
118  }
119 
120  function doBatchLookups() {
121  $uids = array();
122  foreach ( $this->mResult as $row ) {
123  $uids[] = $row->user_id;
124  }
125  // Fetch the block status of the user for showing "(blocked)" text and for
126  // striking out names of suppressed users when privileged user views the list.
127  // Although the first query already hits the block table for un-privileged, this
128  // is done in two queries to avoid huge quicksorts and to make COUNT(*) correct.
129  $dbr = $this->getDatabase();
130  $res = $dbr->select( 'ipblocks',
131  array( 'ipb_user', 'MAX(ipb_deleted) AS block_status' ),
132  array( 'ipb_user' => $uids ),
133  __METHOD__,
134  array( 'GROUP BY' => array( 'ipb_user' ) )
135  );
136  $this->blockStatusByUid = array();
137  foreach ( $res as $row ) {
138  $this->blockStatusByUid[$row->ipb_user] = $row->block_status; // 0 or 1
139  }
140  $this->mResult->seek( 0 );
141  }
142 
143  function formatRow( $row ) {
144  $userName = $row->user_name;
145 
146  $ulinks = Linker::userLink( $row->user_id, $userName );
147  $ulinks .= Linker::userToolLinks( $row->user_id, $userName );
148 
149  $lang = $this->getLanguage();
150 
151  $list = array();
152  $user = User::newFromId( $row->user_id );
153 
154  // User right filter
155  foreach ( $this->hideRights as $right ) {
156  // Calling User::getRights() within the loop so that
157  // if the hideRights() filter is empty, we don't have to
158  // trigger the lazy-init of the big userrights array in the
159  // User object
160  if ( in_array( $right, $user->getRights() ) ) {
161  return '';
162  }
163  }
164 
165  // User group filter
166  // Note: This is a different loop than for user rights,
167  // because we're reusing it to build the group links
168  // at the same time
169  foreach ( $user->getGroups() as $group ) {
170  if ( in_array( $group, $this->hideGroups ) ) {
171  return '';
172  }
173  $list[] = self::buildGroupLink( $group, $userName );
174  }
175 
176  $groups = $lang->commaList( $list );
177 
178  $item = $lang->specialList( $ulinks, $groups );
179 
180  $isBlocked = isset( $this->blockStatusByUid[$row->user_id] );
181  if ( $isBlocked && $this->blockStatusByUid[$row->user_id] == 1 ) {
182  $item = "<span class=\"deleted\">$item</span>";
183  }
184  $count = $this->msg( 'activeusers-count' )->numParams( $row->recentedits )
185  ->params( $userName )->numParams( $this->RCMaxAge )->escaped();
186  $blocked = $isBlocked ? ' ' . $this->msg( 'listusers-blocked', $userName )->escaped() : '';
187 
188  return Html::rawElement( 'li', array(), "{$item} [{$count}]{$blocked}" );
189  }
190 
191  function getPageHeader() {
192  global $wgScript;
193 
194  $self = $this->getTitle();
195  $limit = $this->mLimit ? Html::hidden( 'limit', $this->mLimit ) : '';
196 
197  $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); # Form tag
198  $out .= Xml::fieldset( $this->msg( 'activeusers' )->text() ) . "\n";
199  $out .= Html::hidden( 'title', $self->getPrefixedDBkey() ) . $limit . "\n";
200 
201  $out .= Xml::inputLabel( $this->msg( 'activeusers-from' )->text(),
202  'username', 'offset', 20, $this->requestedUser, array( 'tabindex' => 1 ) ) . '<br />';# Username field
203 
204  $out .= Xml::checkLabel( $this->msg( 'activeusers-hidebots' )->text(),
205  'hidebots', 'hidebots', $this->opts->getValue( 'hidebots' ), array( 'tabindex' => 2 ) );
206 
207  $out .= Xml::checkLabel( $this->msg( 'activeusers-hidesysops' )->text(),
208  'hidesysops', 'hidesysops', $this->opts->getValue( 'hidesysops' ), array( 'tabindex' => 3 ) ) . '<br />';
209 
210  $out .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text(), array( 'tabindex' => 4 ) ) . "\n";# Submit button and form bottom
211  $out .= Xml::closeElement( 'fieldset' );
212  $out .= Xml::closeElement( 'form' );
213 
214  return $out;
215  }
216 }
217 
221 class SpecialActiveUsers extends SpecialPage {
222 
226  public function __construct() {
227  parent::__construct( 'Activeusers' );
228  }
229 
235  public function execute( $par ) {
236  global $wgActiveUserDays;
237 
238  $this->setHeaders();
239  $this->outputHeader();
240 
241  $out = $this->getOutput();
242  $out->wrapWikiMsg( "<div class='mw-activeusers-intro'>\n$1\n</div>",
243  array( 'activeusers-intro', $this->getLanguage()->formatNum( $wgActiveUserDays ) ) );
244 
245  // Occasionally merge in new updates
246  $seconds = min( self::mergeActiveUsers( 600 ), $wgActiveUserDays * 86400 );
247  // Mention the level of staleness
248  $out->addWikiMsg( 'cachedspecial-viewing-cached-ttl',
249  $this->getLanguage()->formatDuration( $seconds ) );
250 
251  $up = new ActiveUsersPager( $this->getContext(), null, $par );
252 
253  # getBody() first to check, if empty
254  $usersbody = $up->getBody();
255 
256  $out->addHTML( $up->getPageHeader() );
257  if ( $usersbody ) {
258  $out->addHTML(
259  $up->getNavigationBar() .
260  Html::rawElement( 'ul', array(), $usersbody ) .
261  $up->getNavigationBar()
262  );
263  } else {
264  $out->addWikiMsg( 'activeusers-noresult' );
265  }
266  }
267 
268  protected function getGroupName() {
269  return 'users';
270  }
271 
276  public static function mergeActiveUsers( $period ) {
277  global $wgActiveUserDays;
278 
279  $dbr = wfGetDB( DB_SLAVE );
280  $cTime = $dbr->selectField( 'querycache_info',
281  'qci_timestamp',
282  array( 'qci_type' => 'activeusers' )
283  );
284  if ( !wfReadOnly() ) {
285  if ( !$cTime || ( time() - wfTimestamp( TS_UNIX, $cTime ) ) > $period ) {
286  $dbw = wfGetDB( DB_MASTER );
287  if ( $dbw->estimateRowCount( 'recentchanges' ) <= 10000 ) {
288  $window = $wgActiveUserDays * 86400; // small wiki
289  } else {
290  $window = $period * 2;
291  }
292  self::doQueryCacheUpdate( $dbw, $window );
293  }
294  }
295 
296  return ( time() -
297  ( $cTime ? wfTimestamp( TS_UNIX, $cTime ) : $wgActiveUserDays * 86400 ) );
298  }
299 
304  public static function cacheUpdate( DatabaseBase $dbw ) {
305  global $wgActiveUserDays;
306 
307  self::doQueryCacheUpdate( $dbw, $wgActiveUserDays * 86400 );
308  }
309 
317  protected static function doQueryCacheUpdate( DatabaseBase $dbw, $window ) {
318  global $wgActiveUserDays;
319 
320  $lockKey = wfWikiID() . '-activeusers';
321  if ( !$dbw->lock( $lockKey, __METHOD__, 1 ) ) {
322  return false; // exclusive update (avoids duplicate entries)
323  }
324 
325  $now = time();
326  $cTime = $dbw->selectField( 'querycache_info',
327  'qci_timestamp',
328  array( 'qci_type' => 'activeusers' )
329  );
330  $cTimeUnix = $cTime ? wfTimestamp( TS_UNIX, $cTime ) : 1;
331 
332  // Pick the date range to fetch from. This is normally from the last
333  // update to till the present time, but has a limited window for sanity.
334  // If the window is limited, multiple runs are need to fully populate it.
335  $sTimestamp = max( $cTimeUnix, $now - $wgActiveUserDays * 86400 );
336  $eTimestamp = min( $sTimestamp + $window, $now );
337 
338  // Get all the users active since the last update
339  $res = $dbw->select(
340  array( 'recentchanges' ),
341  array( 'rc_user_text', 'lastedittime' => 'MAX(rc_timestamp)' ),
342  array(
343  'rc_user > 0', // actual accounts
344  'rc_type != ' . $dbw->addQuotes( RC_EXTERNAL ), // no wikidata
345  'rc_log_type IS NULL OR rc_log_type != ' . $dbw->addQuotes( 'newusers' ),
346  'rc_timestamp >= ' . $dbw->addQuotes( $dbw->timestamp( $sTimestamp ) ),
347  'rc_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $eTimestamp ) )
348  ),
349  __METHOD__,
350  array(
351  'GROUP BY' => array( 'rc_user_text' ),
352  'ORDER BY' => 'NULL' // avoid filesort
353  )
354  );
355  $names = array();
356  foreach ( $res as $row ) {
357  $names[$row->rc_user_text] = $row->lastedittime;
358  }
359 
360  // Rotate out users that have not edited in too long (according to old data set)
361  $dbw->delete( 'querycachetwo',
362  array(
363  'qcc_type' => 'activeusers',
364  'qcc_value < ' . $dbw->addQuotes( $now - $wgActiveUserDays * 86400 ) // TS_UNIX
365  ),
366  __METHOD__
367  );
368 
369  // Find which of the recently active users are already accounted for
370  if ( count( $names ) ) {
371  $res = $dbw->select( 'querycachetwo',
372  array( 'user_name' => 'qcc_title' ),
373  array(
374  'qcc_type' => 'activeusers',
375  'qcc_namespace' => NS_USER,
376  'qcc_title' => array_keys( $names ) ),
377  __METHOD__
378  );
379  foreach ( $res as $row ) {
380  unset( $names[$row->user_name] );
381  }
382  }
383 
384  // Insert the users that need to be added to the list (which their last edit time
385  if ( count( $names ) ) {
386  $newRows = array();
387  foreach ( $names as $name => $lastEditTime ) {
388  $newRows[] = array(
389  'qcc_type' => 'activeusers',
390  'qcc_namespace' => NS_USER,
391  'qcc_title' => $name,
392  'qcc_value' => wfTimestamp( TS_UNIX, $lastEditTime ),
393  'qcc_namespacetwo' => 0, // unused
394  'qcc_titletwo' => '' // unused
395  );
396  }
397  foreach ( array_chunk( $newRows, 500 ) as $rowBatch ) {
398  $dbw->insert( 'querycachetwo', $rowBatch, __METHOD__ );
399  wfWaitForSlaves();
400  }
401  }
402 
403  // Touch the data freshness timestamp
404  $dbw->replace( 'querycache_info',
405  array( 'qci_type' ),
406  array( 'qci_type' => 'activeusers',
407  'qci_timestamp' => $dbw->timestamp( $eTimestamp ) ), // not always $now
408  __METHOD__
409  );
410 
411  $dbw->unlock( $lockKey, __METHOD__ );
412 
413  return true;
414  }
415 }
Xml\checkLabel
static checkLabel( $label, $name, $id, $checked=false, $attribs=array())
Convenience function to build an HTML checkbox with a label.
Definition: Xml.php:433
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
DatabaseBase\replace
replace( $table, $uniqueIndexes, $rows, $fname=__METHOD__)
REPLACE query wrapper.
Definition: Database.php:2630
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:411
RC_EXTERNAL
const RC_EXTERNAL
Definition: Defines.php:183
DB_MASTER
const DB_MASTER
Definition: Defines.php:56
SpecialActiveUsers\execute
execute( $par)
Show the special page.
Definition: SpecialActiveusers.php:232
UsersPager\buildGroupLink
static buildGroupLink( $group, $username)
Format a link to a group description page.
Definition: SpecialListusers.php:353
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
SpecialActiveUsers\getGroupName
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Definition: SpecialActiveusers.php:265
SpecialActiveUsers\cacheUpdate
static cacheUpdate(DatabaseBase $dbw)
Definition: SpecialActiveusers.php:301
ContextSource\msg
msg()
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:175
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:1072
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:535
ActiveUsersPager\getQueryInfo
getQueryInfo()
Definition: SpecialActiveusers.php:90
wfGetDB
& wfGetDB( $db, $groups=array(), $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3650
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
ActiveUsersPager\setupOptions
setupOptions()
Definition: SpecialActiveusers.php:70
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2483
IndexPager\getDatabase
getDatabase()
Get the Database object in use.
Definition: Pager.php:191
$right
return false if a UserGetRights hook might remove the named right $right
Definition: hooks.txt:2798
$limit
if( $sleep) $limit
Definition: importImages.php:99
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1313
Html\hidden
static hidden( $name, $value, $attribs=array())
Convenience function to produce an input element with type=hidden.
Definition: Html.php:662
SpecialActiveUsers\__construct
__construct()
Constructor.
Definition: SpecialActiveusers.php:223
ActiveUsersPager\getPageHeader
getPageHeader()
Definition: SpecialActiveusers.php:188
ContextSource\getRequest
getRequest()
Get the WebRequest object.
Definition: ContextSource.php:77
SpecialPage\getLanguage
getLanguage()
Shortcut to get user's language.
Definition: SpecialPage.php:578
ContextSource\getUser
getUser()
Get the User object.
Definition: ContextSource.php:132
ContextSource\getTitle
getTitle()
Get the Title object.
Definition: ContextSource.php:87
DatabaseBase\timestamp
timestamp( $ts=0)
Convert a timestamp in one of the formats accepted by wfTimestamp() to the format used for inserting ...
Definition: Database.php:3607
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:109
SpecialActiveUsers\mergeActiveUsers
static mergeActiveUsers( $period)
Definition: SpecialActiveusers.php:273
$dbr
$dbr
Definition: testCompression.php:48
ContextSource\getLanguage
getLanguage()
Get the Language object.
Definition: ContextSource.php:154
ActiveUsersPager
This class is used to get a list of active users.
Definition: SpecialActiveusers.php:33
ActiveUsersPager\__construct
__construct(IContextSource $context=null, $group=null, $par=null)
Definition: SpecialActiveusers.php:52
DatabaseBase\addQuotes
addQuotes( $s)
Adds quotes and backslashes.
Definition: Database.php:2477
DatabaseBase\selectField
selectField( $table, $var, $cond='', $fname=__METHOD__, $options=array())
A SELECT wrapper which returns a single field from a single result row.
Definition: Database.php:1281
DatabaseBase\unlock
unlock( $lockName, $method)
Release a lock.
Definition: Database.php:4019
$out
$out
Definition: UtfNormalGenerate.php:167
DatabaseBase\select
select( $table, $vars, $conds='', $fname=__METHOD__, $options=array(), $join_conds=array())
Execute a SELECT query constructed using the various parameters provided.
Definition: Database.php:1575
ActiveUsersPager\$hideGroups
Array $hideGroups
Definition: SpecialActiveusers.php:41
SpecialActiveUsers\doQueryCacheUpdate
static doQueryCacheUpdate(DatabaseBase $dbw, $window)
Update the query cache as needed.
Definition: SpecialActiveusers.php:314
UsersPager
This class is used to get a list of user.
Definition: SpecialListusers.php:35
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
SpecialPage\setHeaders
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
Definition: SpecialPage.php:352
Xml\inputLabel
static inputLabel( $label, $name, $id, $size=false, $value=false, $attribs=array())
Convenience function to build an HTML text input field with a label.
Definition: Xml.php:398
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
form
null means default in associative array form
Definition: hooks.txt:1530
wfWaitForSlaves
wfWaitForSlaves( $maxLag=false, $wiki=false, $cluster=false)
Modern version of wfWaitForSlaves().
Definition: GlobalFunctions.php:3795
ActiveUsersPager\formatRow
formatRow( $row)
Definition: SpecialActiveusers.php:140
SpecialPage\getContext
getContext()
Gets the context this SpecialPage is executed in.
Definition: SpecialPage.php:508
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:422
wfWikiID
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
Definition: GlobalFunctions.php:3604
ActiveUsersPager\getIndexField
getIndexField()
Definition: SpecialActiveusers.php:86
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
Linker\userToolLinks
static userToolLinks( $userId, $userText, $redContribsWhenNoEdits=false, $flags=0, $edits=null)
Generate standard user tool links (talk, contributions, block link, etc.)
Definition: Linker.php:1100
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:33
DatabaseBase
Database abstraction object.
Definition: Database.php:219
ActiveUsersPager\$opts
FormOptions $opts
Definition: SpecialActiveusers.php:37
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
IContextSource
Interface for objects which can provide a context on request.
Definition: IContextSource.php:29
$count
$count
Definition: UtfNormalTest2.php:96
$self
$self
Definition: doMaintenance.php:54
SpecialActiveUsers
Definition: SpecialActiveusers.php:218
DatabaseBase\lock
lock( $lockName, $method, $timeout=5)
Acquire a named lock.
Definition: Database.php:4005
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:55
Xml\closeElement
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:118
DatabaseBase\delete
delete( $table, $conds, $fname=__METHOD__)
DELETE query wrapper.
Definition: Database.php:2884
TS_UNIX
const TS_UNIX
Unix time - the number of seconds since 1970-01-01 00:00:00 UTC.
Definition: GlobalFunctions.php:2426
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
NS_USER
const NS_USER
Definition: Defines.php:81
ActiveUsersPager\$hideRights
Array $hideRights
Definition: SpecialActiveusers.php:45
Xml\submitButton
static submitButton( $value, $attribs=array())
Convenience function to build an HTML submit button.
Definition: Xml.php:463
FormOptions
Helper class to keep track of options when mixing links and form elements.
Definition: FormOptions.php:35
Html\rawElement
static rawElement( $element, $attribs=array(), $contents='')
Returns an HTML element in a string.
Definition: Html.php:124
ActiveUsersPager\doBatchLookups
doBatchLookups()
Called from getBody(), before getStartBody() is called and after doQuery() was called.
Definition: SpecialActiveusers.php:117
$res
$res
Definition: database.txt:21
SpecialPage\outputHeader
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Definition: SpecialPage.php:443
Xml\fieldset
static fieldset( $legend=false, $content=false, $attribs=array())
Shortcut for creating fieldsets.
Definition: Xml.php:563
FormOptions\BOOL
const BOOL
Boolean type, maps guessType() to WebRequest::getBool()
Definition: FormOptions.php:50
DatabaseBase\insert
insert( $table, $a, $fname=__METHOD__, $options=array())
INSERT wrapper, inserts an array into a table.
Definition: Database.php:1860