MediaWiki  1.33.0
ActiveUsersPager.php
Go to the documentation of this file.
1 <?php
30 
34  protected $opts;
35 
39  protected $groups;
40 
45 
50  public function __construct( IContextSource $context = null, FormOptions $opts ) {
51  parent::__construct( $context );
52 
53  $this->RCMaxAge = $this->getConfig()->get( 'ActiveUserDays' );
54  $this->requestedUser = '';
55 
56  $un = $opts->getValue( 'username' );
57  if ( $un != '' ) {
59  if ( !is_null( $username ) ) {
60  $this->requestedUser = $username->getText();
61  }
62  }
63 
64  $this->groups = $opts->getValue( 'groups' );
65  $this->excludegroups = $opts->getValue( 'excludegroups' );
66  // Backwards-compatibility with old URLs
67  if ( $opts->getValue( 'hidebots' ) ) {
68  $this->excludegroups[] = 'bot';
69  }
70  if ( $opts->getValue( 'hidesysops' ) ) {
71  $this->excludegroups[] = 'sysop';
72  }
73  }
74 
75  function getIndexField() {
76  return 'qcc_title';
77  }
78 
79  function getQueryInfo( $data = null ) {
80  $dbr = $this->getDatabase();
81 
82  $useActor = (bool)(
83  $this->getConfig()->get( 'ActorTableSchemaMigrationStage' ) & SCHEMA_COMPAT_READ_NEW
84  );
85 
86  $activeUserSeconds = $this->getConfig()->get( 'ActiveUserDays' ) * 86400;
87  $timestamp = $dbr->timestamp( wfTimestamp( TS_UNIX ) - $activeUserSeconds );
88  $fname = __METHOD__ . ' (' . $this->getSqlComment() . ')';
89 
90  // Inner subselect to pull the active users out of querycachetwo
91  $tables = [ 'querycachetwo', 'user' ];
92  $fields = [ 'qcc_title', 'user_id' ];
93  $jconds = [
94  'user' => [ 'JOIN', 'user_name = qcc_title' ],
95  ];
96  $conds = [
97  'qcc_type' => 'activeusers',
98  'qcc_namespace' => NS_USER,
99  ];
100  $options = [];
101  if ( $data !== null ) {
102  $options['ORDER BY'] = 'qcc_title ' . $data['order'];
103  $options['LIMIT'] = $data['limit'];
104  $conds = array_merge( $conds, $data['conds'] );
105  }
106  if ( $this->requestedUser != '' ) {
107  $conds[] = 'qcc_title >= ' . $dbr->addQuotes( $this->requestedUser );
108  }
109  if ( $this->groups !== [] ) {
110  $tables['ug1'] = 'user_groups';
111  $jconds['ug1'] = [ 'JOIN', 'ug1.ug_user = user_id' ];
112  $conds['ug1.ug_group'] = $this->groups;
113  $conds[] = 'ug1.ug_expiry IS NULL OR ug1.ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() );
114  }
115  if ( $this->excludegroups !== [] ) {
116  $tables['ug2'] = 'user_groups';
117  $jconds['ug2'] = [ 'LEFT JOIN', [
118  'ug2.ug_user = user_id',
119  'ug2.ug_group' => $this->excludegroups,
120  'ug2.ug_expiry IS NULL OR ug2.ug_expiry >= ' . $dbr->addQuotes( $dbr->timestamp() ),
121  ] ];
122  $conds['ug2.ug_user'] = null;
123  }
124  if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
125  $conds[] = 'NOT EXISTS (' . $dbr->selectSQLText(
126  'ipblocks', '1', [ 'ipb_user=user_id', 'ipb_deleted' => 1 ]
127  ) . ')';
128  }
129  if ( $useActor ) {
130  $tables[] = 'actor';
131  $jconds['actor'] = [
132  'JOIN',
133  'actor_user = user_id',
134  ];
135  $fields[] = 'actor_id';
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  $useActor ? 'rc_actor = actor_id' : 'rc_user_text = qcc_title',
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' ] ],
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  function formatRow( $row ) {
225  $userName = $row->user_name;
226 
227  $ulinks = Linker::userLink( $row->user_id, $userName );
228  $ulinks .= Linker::userToolLinks( $row->user_id, $userName );
229 
230  $lang = $this->getLanguage();
231 
232  $list = [];
233 
234  $ugms = self::getGroupMemberships( intval( $row->user_id ), $this->userGroupCache );
235  foreach ( $ugms as $ugm ) {
236  $list[] = $this->buildGroupLink( $ugm, $userName );
237  }
238 
239  $groups = $lang->commaList( $list );
240 
241  $item = $lang->specialList( $ulinks, $groups );
242 
243  // If there is a block, 'deleted' and 'sitewide' are both set on
244  // $this->blockStatusByUid[$row->user_id].
245  $blocked = '';
246  $isBlocked = isset( $this->blockStatusByUid[$row->user_id] );
247  if ( $isBlocked ) {
248  if ( $this->blockStatusByUid[$row->user_id]['deleted'] == 1 ) {
249  $item = "<span class=\"deleted\">$item</span>";
250  }
251  if ( $this->blockStatusByUid[$row->user_id]['sitewide'] == 1 ) {
252  $blocked = ' ' . $this->msg( 'listusers-blocked', $userName )->escaped();
253  }
254  }
255  $count = $this->msg( 'activeusers-count' )->numParams( $row->recentedits )
256  ->params( $userName )->numParams( $this->RCMaxAge )->escaped();
257 
258  return Html::rawElement( 'li', [], "{$item} [{$count}]{$blocked}" );
259  }
260 
261 }
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:33
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:63
ActiveUsersPager\__construct
__construct(IContextSource $context=null, FormOptions $opts)
Definition: ActiveUsersPager.php:50
RC_EXTERNAL
const RC_EXTERNAL
Definition: Defines.php:145
SCHEMA_COMPAT_READ_NEW
const SCHEMA_COMPAT_READ_NEW
Definition: Defines.php:287
Linker\userLink
static userLink( $userId, $userName, $altUserName=false)
Make user link (or user contributions for unregistered users)
Definition: Linker.php:892
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
FormOptions\getValue
getValue( $name)
Get the value for the given option name.
Definition: FormOptions.php:180
ActiveUsersPager\buildQueryInfo
buildQueryInfo( $offset, $limit, $order)
Build variables to use by the database wrapper.
Definition: ActiveUsersPager.php:164
ContextSource\msg
msg( $key)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:168
UsersPager\buildGroupLink
buildGroupLink( $group, $username)
Format a link to a group description page.
Definition: UsersPager.php:427
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1912
Linker\userToolLinks
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:931
ActiveUsersPager\$blockStatusByUid
array $blockStatusByUid
Definition: ActiveUsersPager.php:44
$tables
this hook is for auditing only RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
Definition: hooks.txt:979
IndexPager\getDatabase
getDatabase()
Get the Database object in use.
Definition: IndexPager.php:217
ActiveUsersPager\getQueryInfo
getQueryInfo( $data=null)
Definition: ActiveUsersPager.php:79
$res
$res
Definition: database.txt:21
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
$dbr
$dbr
Definition: testCompression.php:50
ContextSource\getLanguage
getLanguage()
Definition: ContextSource.php:128
ActiveUsersPager
This class is used to get a list of active users.
Definition: ActiveUsersPager.php:29
$data
$data
Utility to generate mapping file used in mw.Title (phpCharToUpper.json)
Definition: generatePhpCharToUpperMappings.php:13
UsersPager
This class is used to get a list of user.
Definition: UsersPager.php:33
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
ActiveUsersPager\formatRow
formatRow( $row)
Definition: ActiveUsersPager.php:224
$fname
if(defined( 'MW_SETUP_CALLBACK')) $fname
Customization point after all loading (constants, functions, classes, DefaultSettings,...
Definition: Setup.php:123
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:604
ActiveUsersPager\getIndexField
getIndexField()
Definition: ActiveUsersPager.php:75
groups
this hook is for auditing only RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing groups(accessed through $special->getFilterGroup)
ActiveUsersPager\$opts
FormOptions $opts
Definition: ActiveUsersPager.php:34
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
UsersPager\getGroupMemberships
static getGroupMemberships( $uid, $cache=null)
Get an associative array containing groups the specified user belongs to, and the relevant UserGroupM...
Definition: UsersPager.php:411
IndexPager\getSqlComment
getSqlComment()
Get some text to go in brackets in the "function name" part of the SQL comment.
Definition: IndexPager.php:393
UsersPager\getQueryInfo
getQueryInfo()
Definition: UsersPager.php:103
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1985
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:66
ActiveUsersPager\$groups
string[] $groups
Definition: ActiveUsersPager.php:39
RC_CATEGORIZE
const RC_CATEGORIZE
Definition: Defines.php:146
FormOptions
Helper class to keep track of options when mixing links and form elements.
Definition: FormOptions.php:35
ActiveUsersPager\doBatchLookups
doBatchLookups()
Called from getBody(), before getStartBody() is called and after doQuery() was called.
Definition: ActiveUsersPager.php:196
$username
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:780