MediaWiki  master
ApiQueryUsers.php
Go to the documentation of this file.
1 <?php
25 
31 class ApiQueryUsers extends ApiQueryBase {
33 
35 
41  protected static $publicProps = [
42  // everything except 'blockinfo' which might show hidden records if the user
43  // making the request has the appropriate permissions
44  'groups',
45  'groupmemberships',
46  'implicitgroups',
47  'rights',
48  'editcount',
49  'registration',
50  'emailable',
51  'gender',
52  'centralids',
53  'cancreate',
54  ];
55 
56  public function __construct( ApiQuery $query, $moduleName ) {
57  parent::__construct( $query, $moduleName, 'us' );
58  }
59 
67  protected function getTokenFunctions() {
68  if ( isset( $this->tokenFunctions ) ) {
69  return $this->tokenFunctions;
70  }
71 
72  // If we're in a mode that breaks the same-origin policy, no tokens can
73  // be obtained
74  if ( $this->lacksSameOriginSecurity() ) {
75  return [];
76  }
77 
78  $this->tokenFunctions = [
79  'userrights' => [ self::class, 'getUserrightsToken' ],
80  ];
81 
82  return $this->tokenFunctions;
83  }
84 
92  public static function getUserrightsToken( User $actingUser, $targetUser ) {
93  // Since the permissions check for userrights is non-trivial,
94  // don't bother with it here
95  return $actingUser->getEditToken( $targetUser->getName() );
96  }
97 
98  public function execute() {
99  $db = $this->getDB();
100  $params = $this->extractRequestParams();
101  $this->requireMaxOneParameter( $params, 'userids', 'users' );
102 
103  if ( $params['prop'] !== null ) {
104  $this->prop = array_flip( $params['prop'] );
105  } else {
106  $this->prop = [];
107  }
108  $useNames = $params['users'] !== null;
109 
110  $users = (array)$params['users'];
111  $userids = (array)$params['userids'];
112 
113  $goodNames = $done = [];
114  $result = $this->getResult();
115  // Canonicalize user names
116  foreach ( $users as $u ) {
117  $n = User::getCanonicalName( $u );
118  if ( $n === false || $n === '' ) {
119  $vals = [ 'name' => $u, 'invalid' => true ];
120  $fit = $result->addValue( [ 'query', $this->getModuleName() ],
121  null, $vals );
122  if ( !$fit ) {
123  $this->setContinueEnumParameter( 'users',
124  implode( '|', array_diff( $users, $done ) ) );
125  $goodNames = [];
126  break;
127  }
128  $done[] = $u;
129  } else {
130  $goodNames[] = $n;
131  }
132  }
133 
134  if ( $useNames ) {
135  $parameters = &$goodNames;
136  } else {
137  $parameters = &$userids;
138  }
139 
140  $result = $this->getResult();
141 
142  if ( count( $parameters ) ) {
143  $userQuery = User::getQueryInfo();
144  $this->addTables( $userQuery['tables'] );
145  $this->addFields( $userQuery['fields'] );
146  $this->addJoinConds( $userQuery['joins'] );
147  if ( $useNames ) {
148  $this->addWhereFld( 'user_name', $goodNames );
149  } else {
150  $this->addWhereFld( 'user_id', $userids );
151  }
152 
153  $this->addBlockInfoToQuery( isset( $this->prop['blockinfo'] ) );
154 
155  $data = [];
156  $res = $this->select( __METHOD__ );
157  $this->resetQueryParams();
158 
159  // get user groups if needed
160  if ( isset( $this->prop['groups'] ) || isset( $this->prop['rights'] ) ) {
161  $userGroups = [];
162 
163  $this->addTables( 'user' );
164  if ( $useNames ) {
165  $this->addWhereFld( 'user_name', $goodNames );
166  } else {
167  $this->addWhereFld( 'user_id', $userids );
168  }
169 
170  $this->addTables( 'user_groups' );
171  $this->addJoinConds( [ 'user_groups' => [ 'JOIN', 'ug_user=user_id' ] ] );
172  $this->addFields( [ 'user_name' ] );
173  $this->addFields( MediaWikiServices::getInstance()
174  ->getUserGroupManager()
175  ->getQueryInfo()['fields']
176  );
177  $this->addWhere( 'ug_expiry IS NULL OR ug_expiry >= ' .
178  $db->addQuotes( $db->timestamp() ) );
179  $userGroupsRes = $this->select( __METHOD__ );
180 
181  foreach ( $userGroupsRes as $row ) {
182  $userGroups[$row->user_name][] = $row;
183  }
184  }
185 
186  foreach ( $res as $row ) {
187  // create user object and pass along $userGroups if set
188  // that reduces the number of database queries needed in User dramatically
189  if ( !isset( $userGroups ) ) {
190  $user = User::newFromRow( $row );
191  } else {
192  if ( !isset( $userGroups[$row->user_name] ) || !is_array( $userGroups[$row->user_name] ) ) {
193  $userGroups[$row->user_name] = [];
194  }
195  $user = User::newFromRow( $row, [ 'user_groups' => $userGroups[$row->user_name] ] );
196  }
197  if ( $useNames ) {
198  $key = $user->getName();
199  } else {
200  $key = $user->getId();
201  }
202  $data[$key]['userid'] = $user->getId();
203  $data[$key]['name'] = $user->getName();
204 
205  if ( isset( $this->prop['editcount'] ) ) {
206  $data[$key]['editcount'] = $user->getEditCount();
207  }
208 
209  if ( isset( $this->prop['registration'] ) ) {
210  $data[$key]['registration'] = wfTimestampOrNull( TS_ISO_8601, $user->getRegistration() );
211  }
212 
213  if ( isset( $this->prop['groups'] ) ) {
214  $data[$key]['groups'] = $user->getEffectiveGroups();
215  }
216 
217  if ( isset( $this->prop['groupmemberships'] ) ) {
218  $data[$key]['groupmemberships'] = array_map( function ( $ugm ) {
219  return [
220  'group' => $ugm->getGroup(),
221  'expiry' => ApiResult::formatExpiry( $ugm->getExpiry() ),
222  ];
223  }, $user->getGroupMemberships() );
224  }
225 
226  if ( isset( $this->prop['implicitgroups'] ) ) {
227  $data[$key]['implicitgroups'] = $user->getAutomaticGroups();
228  }
229 
230  if ( isset( $this->prop['rights'] ) ) {
231  $data[$key]['rights'] = $this->getPermissionManager()
232  ->getUserPermissions( $user );
233  }
234  if ( $row->ipb_deleted ) {
235  $data[$key]['hidden'] = true;
236  }
237  if ( isset( $this->prop['blockinfo'] ) && $row->ipb_by_text !== null ) {
238  $data[$key] += $this->getBlockDetails( DatabaseBlock::newFromRow( $row ) );
239  }
240 
241  if ( isset( $this->prop['emailable'] ) ) {
242  $data[$key]['emailable'] = $user->canReceiveEmail();
243  }
244 
245  if ( isset( $this->prop['gender'] ) ) {
246  $gender = $user->getOption( 'gender' );
247  if ( strval( $gender ) === '' ) {
248  $gender = 'unknown';
249  }
250  $data[$key]['gender'] = $gender;
251  }
252 
253  if ( isset( $this->prop['centralids'] ) ) {
255  $this->getConfig(), $user, $params['attachedwiki']
256  );
257  }
258 
259  if ( $params['token'] !== null ) {
261  foreach ( $params['token'] as $t ) {
262  $val = call_user_func(
264  $this->getUser(),
265  $user
266  );
267  if ( $val === false ) {
268  $this->addWarning( [ 'apiwarn-tokennotallowed', $t ] );
269  } else {
270  $data[$key][$t . 'token'] = $val;
271  }
272  }
273  }
274  }
275  }
276 
277  $context = $this->getContext();
278  // Second pass: add result data to $retval
279  foreach ( $parameters as $u ) {
280  if ( !isset( $data[$u] ) ) {
281  if ( $useNames ) {
282  $data[$u] = [ 'name' => $u ];
283  $urPage = new UserrightsPage;
284  $urPage->setContext( $context );
285 
286  $iwUser = $urPage->fetchUser( $u );
287 
288  if ( $iwUser instanceof UserRightsProxy ) {
289  $data[$u]['interwiki'] = true;
290 
291  if ( $params['token'] !== null ) {
293 
294  foreach ( $params['token'] as $t ) {
295  $val = call_user_func(
297  $this->getUser(),
298  $iwUser
299  );
300  if ( $val === false ) {
301  $this->addWarning( [ 'apiwarn-tokennotallowed', $t ] );
302  } else {
303  $data[$u][$t . 'token'] = $val;
304  }
305  }
306  }
307  } else {
308  $data[$u]['missing'] = true;
309  if ( isset( $this->prop['cancreate'] ) ) {
310  $status = MediaWikiServices::getInstance()->getAuthManager()
311  ->canCreateAccount( $u );
312  $data[$u]['cancreate'] = $status->isGood();
313  if ( !$status->isGood() ) {
314  $data[$u]['cancreateerror'] = $this->getErrorFormatter()->arrayFromStatus( $status );
315  }
316  }
317  }
318  } else {
319  $data[$u] = [ 'userid' => $u, 'missing' => true ];
320  }
321 
322  } else {
323  if ( isset( $this->prop['groups'] ) && isset( $data[$u]['groups'] ) ) {
324  ApiResult::setArrayType( $data[$u]['groups'], 'array' );
325  ApiResult::setIndexedTagName( $data[$u]['groups'], 'g' );
326  }
327  if ( isset( $this->prop['groupmemberships'] ) && isset( $data[$u]['groupmemberships'] ) ) {
328  ApiResult::setArrayType( $data[$u]['groupmemberships'], 'array' );
329  ApiResult::setIndexedTagName( $data[$u]['groupmemberships'], 'groupmembership' );
330  }
331  if ( isset( $this->prop['implicitgroups'] ) && isset( $data[$u]['implicitgroups'] ) ) {
332  ApiResult::setArrayType( $data[$u]['implicitgroups'], 'array' );
333  ApiResult::setIndexedTagName( $data[$u]['implicitgroups'], 'g' );
334  }
335  if ( isset( $this->prop['rights'] ) && isset( $data[$u]['rights'] ) ) {
336  ApiResult::setArrayType( $data[$u]['rights'], 'array' );
337  ApiResult::setIndexedTagName( $data[$u]['rights'], 'r' );
338  }
339  }
340 
341  $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $data[$u] );
342  if ( !$fit ) {
343  if ( $useNames ) {
344  $this->setContinueEnumParameter( 'users',
345  implode( '|', array_diff( $users, $done ) ) );
346  } else {
347  $this->setContinueEnumParameter( 'userids',
348  implode( '|', array_diff( $userids, $done ) ) );
349  }
350  break;
351  }
352  $done[] = $u;
353  }
354  $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'user' );
355  }
356 
357  public function getCacheMode( $params ) {
358  if ( isset( $params['token'] ) ) {
359  return 'private';
360  } elseif ( array_diff( (array)$params['prop'], static::$publicProps ) ) {
361  return 'anon-public-user-private';
362  } else {
363  return 'public';
364  }
365  }
366 
367  public function getAllowedParams() {
368  return [
369  'prop' => [
370  ApiBase::PARAM_ISMULTI => true,
372  'blockinfo',
373  'groups',
374  'groupmemberships',
375  'implicitgroups',
376  'rights',
377  'editcount',
378  'registration',
379  'emailable',
380  'gender',
381  'centralids',
382  'cancreate',
383  // When adding a prop, consider whether it should be added
384  // to self::$publicProps
385  ],
387  ],
388  'attachedwiki' => null,
389  'users' => [
391  ],
392  'userids' => [
393  ApiBase::PARAM_ISMULTI => true,
394  ApiBase::PARAM_TYPE => 'integer'
395  ],
396  'token' => [
398  ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ),
400  ],
401  ];
402  }
403 
404  protected function getExamplesMessages() {
405  return [
406  'action=query&list=users&ususers=Example&usprop=groups|editcount|gender'
407  => 'apihelp-query+users-example-simple',
408  ];
409  }
410 
411  public function getHelpUrls() {
412  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Users';
413  }
414 }
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:37
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:70
ApiQueryBase\addFields
addFields( $value)
Add a set of fields to select to the internal array.
Definition: ApiQueryBase.php:212
ContextSource\getContext
getContext()
Get the base IContextSource object.
Definition: ContextSource.php:45
ApiQuery
This is the main query class.
Definition: ApiQuery.php:37
ApiBase\addWarning
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1289
ApiQueryUsers
Query module to get information about a list of users.
Definition: ApiQueryUsers.php:31
ApiQueryBase\resetQueryParams
resetQueryParams()
Blank the internal arrays with query parameters.
Definition: ApiQueryBase.php:156
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:166
true
return true
Definition: router.php:90
ApiBase\PARAM_TYPE
const PARAM_TYPE
Definition: ApiBase.php:69
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:558
UserRightsProxy
Cut-down copy of User interface for local-interwiki-database user rights manipulation.
Definition: UserRightsProxy.php:32
$res
$res
Definition: testCompression.php:57
ContextSource\getUser
getUser()
Stable to override.
Definition: ContextSource.php:134
User\newFromRow
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition: User.php:700
ApiBase\lacksSameOriginSecurity
lacksSameOriginSecurity()
Returns true if the current request breaks the same-origin policy.
Definition: ApiBase.php:486
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
Definition: ApiBase.php:74
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:50
ApiQueryBlockInfoTrait
trait ApiQueryBlockInfoTrait
Definition: ApiQueryBlockInfoTrait.php:28
UserrightsPage
Special page to allow managing user group membership.
Definition: SpecialUserrights.php:36
ApiResult\setArrayType
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:713
ApiQueryBase
This is a base class for all Query modules.
Definition: ApiQueryBase.php:37
wfTimestampOrNull
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
Definition: GlobalFunctions.php:1847
ApiQueryBase\getDB
getDB()
Get the Query database connection (read-only) Stable to override.
Definition: ApiQueryBase.php:117
ApiQueryBase\addTables
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
Definition: ApiQueryBase.php:182
ApiQueryBase\select
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
Definition: ApiQueryBase.php:399
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:706
ApiQueryUsers\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiQueryUsers.php:411
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:601
ApiBase\requireMaxOneParameter
requireMaxOneParameter( $params,... $required)
Die if more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:878
ApiBase\getPermissionManager
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:627
ApiQueryUsers\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiQueryUsers.php:98
ApiQueryBase\addJoinConds
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
Definition: ApiQueryBase.php:201
ApiQueryUsers\__construct
__construct(ApiQuery $query, $moduleName)
Definition: ApiQueryUsers.php:56
ApiQueryBase\addWhereFld
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
Definition: ApiQueryBase.php:282
ApiQueryUsers\getTokenFunctions
getTokenFunctions()
Get an array mapping token names to their handler functions.
Definition: ApiQueryUsers.php:67
User\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new user object.
Definition: User.php:4360
ApiQueryUsers\getUserrightsToken
static getUserrightsToken(User $actingUser, $targetUser)
Definition: ApiQueryUsers.php:92
ApiQueryUsers\getCacheMode
getCacheMode( $params)
Get the cache mode for the data generated by this module.
Definition: ApiQueryUsers.php:357
ApiQueryUsers\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiQueryUsers.php:404
ApiQueryUsers\$tokenFunctions
$tokenFunctions
Definition: ApiQueryUsers.php:34
User\getCanonicalName
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
Definition: User.php:1137
SpecialPage\setContext
setContext( $context)
Sets the context this SpecialPage is executed in.
Definition: SpecialPage.php:752
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:437
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
Definition: ApiBase.php:68
ApiQueryUsers\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiQueryUsers.php:367
ApiResult\formatExpiry
static formatExpiry( $expiry, $infinity='infinity')
Format an expiry timestamp for API output.
Definition: ApiResult.php:1189
ApiQueryUsers\$publicProps
static array $publicProps
Properties whose contents does not depend on who is looking at them.
Definition: ApiQueryUsers.php:41
ApiQueryBase\addWhere
addWhere( $value)
Add a set of WHERE clauses to the internal array.
Definition: ApiQueryBase.php:245
ApiQueryUserInfo\getCentralUserInfo
static getCentralUserInfo(Config $config, User $user, $attachedWiki=null)
Get central user info.
Definition: ApiQueryUserInfo.php:69
$t
$t
Definition: testCompression.php:74
ApiQueryBase\setContinueEnumParameter
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
Definition: ApiQueryBase.php:515
ApiBase\PARAM_HELP_MSG_PER_VALUE
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:135
User\getEditToken
getEditToken( $salt='', $request=null)
Initialize (if necessary) and return a session token value which can be used in edit forms to show th...
Definition: User.php:3774
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:56
ApiQueryUsers\$prop
$prop
Definition: ApiQueryUsers.php:34
ApiBase\getErrorFormatter
getErrorFormatter()
Stable to override.
Definition: ApiBase.php:572
addBlockInfoToQuery
addBlockInfoToQuery( $showBlockInfo)
Filters hidden users (where the user doesn't have the right to view them) Also adds relevant block in...
Definition: ApiQueryBlockInfoTrait.php:38