MediaWiki master
ApiQueryUsers.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Api;
24
32
40
42 private $prop;
43
44 private UserNameUtils $userNameUtils;
45 private UserFactory $userFactory;
46 private UserGroupManager $userGroupManager;
47 private GenderCache $genderCache;
48 private AuthManager $authManager;
49
55 protected static $publicProps = [
56 // everything except 'blockinfo' which might show hidden records if the user
57 // making the request has the appropriate permissions
58 'groups',
59 'groupmemberships',
60 'implicitgroups',
61 'rights',
62 'editcount',
63 'registration',
64 'emailable',
65 'gender',
66 'centralids',
67 'cancreate',
68 ];
69
70 public function __construct(
71 ApiQuery $query,
72 string $moduleName,
73 UserNameUtils $userNameUtils,
74 UserFactory $userFactory,
75 UserGroupManager $userGroupManager,
76 GenderCache $genderCache,
77 AuthManager $authManager
78 ) {
79 parent::__construct( $query, $moduleName, 'us' );
80 $this->userNameUtils = $userNameUtils;
81 $this->userFactory = $userFactory;
82 $this->userGroupManager = $userGroupManager;
83 $this->genderCache = $genderCache;
84 $this->authManager = $authManager;
85 }
86
87 public function execute() {
88 $db = $this->getDB();
90 $this->requireMaxOneParameter( $params, 'userids', 'users' );
91
92 if ( $params['prop'] !== null ) {
93 $this->prop = array_fill_keys( $params['prop'], true );
94 } else {
95 $this->prop = [];
96 }
97 $useNames = $params['users'] !== null;
98
99 $users = (array)$params['users'];
100 $userids = (array)$params['userids'];
101
102 $goodNames = $done = [];
103 $result = $this->getResult();
104 // Canonicalize user names
105 foreach ( $users as $u ) {
106 $n = $this->userNameUtils->getCanonical( $u );
107 if ( $n === false || $n === '' ) {
108 $vals = [ 'name' => $u, 'invalid' => true ];
109 $fit = $result->addValue( [ 'query', $this->getModuleName() ],
110 null, $vals );
111 if ( !$fit ) {
112 $this->setContinueEnumParameter( 'users',
113 implode( '|', array_diff( $users, $done ) ) );
114 $goodNames = [];
115 break;
116 }
117 $done[] = $u;
118 } else {
119 $goodNames[] = $n;
120 }
121 }
122
123 if ( $useNames ) {
124 $parameters = &$goodNames;
125 } else {
126 $parameters = &$userids;
127 }
128
129 $result = $this->getResult();
130
131 if ( count( $parameters ) ) {
132 $this->getQueryBuilder()->merge( User::newQueryBuilder( $db ) );
133 if ( $useNames ) {
134 $this->addWhereFld( 'user_name', $goodNames );
135 } else {
136 $this->addWhereFld( 'user_id', $userids );
137 }
138
139 $this->addDeletedUserFilter();
140
141 $data = [];
142 $res = $this->select( __METHOD__ );
143 $this->resetQueryParams();
144
145 // get user groups if needed
146 if ( isset( $this->prop['groups'] ) || isset( $this->prop['rights'] ) ) {
147 $userGroups = [];
148
149 $this->addTables( 'user' );
150 if ( $useNames ) {
151 $this->addWhereFld( 'user_name', $goodNames );
152 } else {
153 $this->addWhereFld( 'user_id', $userids );
154 }
155
156 $this->addTables( 'user_groups' );
157 $this->addJoinConds( [ 'user_groups' => [ 'JOIN', 'ug_user=user_id' ] ] );
158 $this->addFields( [ 'user_name' ] );
159 $this->addFields( [ 'ug_user', 'ug_group', 'ug_expiry' ] );
160 $this->addWhere(
161 $db->expr( 'ug_expiry', '=', null )->or( 'ug_expiry', '>=', $db->timestamp() )
162 );
163 $userGroupsRes = $this->select( __METHOD__ );
164
165 foreach ( $userGroupsRes as $row ) {
166 $userGroups[$row->user_name][] = $row;
167 }
168 }
169 if ( isset( $this->prop['gender'] ) ) {
170 $userNames = [];
171 foreach ( $res as $row ) {
172 $userNames[] = $row->user_name;
173 }
174 $this->genderCache->doQuery( $userNames, __METHOD__ );
175 }
176
177 if ( isset( $this->prop['blockinfo'] ) ) {
178 $blockInfos = $this->getBlockDetailsForRows( $res );
179 } else {
180 $blockInfos = null;
181 }
182
183 foreach ( $res as $row ) {
184 // create user object and pass along $userGroups if set
185 // that reduces the number of database queries needed in User dramatically
186 if ( !isset( $userGroups ) ) {
187 $user = $this->userFactory->newFromRow( $row );
188 } else {
189 if ( !isset( $userGroups[$row->user_name] ) || !is_array( $userGroups[$row->user_name] ) ) {
190 $userGroups[$row->user_name] = [];
191 }
192 $user = $this->userFactory->newFromRow( $row, [ 'user_groups' => $userGroups[$row->user_name] ] );
193 }
194 if ( $useNames ) {
195 $key = $user->getName();
196 } else {
197 $key = $user->getId();
198 }
199 $data[$key]['userid'] = $user->getId();
200 $data[$key]['name'] = $user->getName();
201
202 if ( $user->isSystemUser() ) {
203 $data[$key]['systemuser'] = true;
204 }
205
206 if ( isset( $this->prop['editcount'] ) ) {
207 $data[$key]['editcount'] = $user->getEditCount();
208 }
209
210 if ( isset( $this->prop['registration'] ) ) {
211 $data[$key]['registration'] = wfTimestampOrNull( TS_ISO_8601, $user->getRegistration() );
212 }
213
214 if ( isset( $this->prop['groups'] ) ) {
215 $data[$key]['groups'] = $this->userGroupManager->getUserEffectiveGroups( $user );
216 }
217
218 if ( isset( $this->prop['groupmemberships'] ) ) {
219 $data[$key]['groupmemberships'] = array_map( static function ( $ugm ) {
220 return [
221 'group' => $ugm->getGroup(),
222 'expiry' => ApiResult::formatExpiry( $ugm->getExpiry() ),
223 ];
224 }, $this->userGroupManager->getUserGroupMemberships( $user ) );
225 }
226
227 if ( isset( $this->prop['implicitgroups'] ) ) {
228 $data[$key]['implicitgroups'] = $this->userGroupManager->getUserImplicitGroups( $user );
229 }
230
231 if ( isset( $this->prop['rights'] ) ) {
232 $data[$key]['rights'] = $this->getPermissionManager()
233 ->getUserPermissions( $user );
234 }
235 if ( $row->hu_deleted ) {
236 $data[$key]['hidden'] = true;
237 }
238 if ( isset( $this->prop['blockinfo'] ) && isset( $blockInfos[$row->user_id] ) ) {
239 $data[$key] += $blockInfos[$row->user_id];
240 }
241
242 if ( isset( $this->prop['emailable'] ) ) {
243 $data[$key]['emailable'] = $user->canReceiveEmail();
244 }
245
246 if ( isset( $this->prop['gender'] ) ) {
247 $data[$key]['gender'] = $this->genderCache->getGenderOf( $user, __METHOD__ );
248 }
249
250 if ( isset( $this->prop['centralids'] ) ) {
252 $this->getConfig(), $user, $params['attachedwiki']
253 );
254 }
255 }
256 }
257
258 // Second pass: add result data to $retval
259 foreach ( $parameters as $u ) {
260 if ( !isset( $data[$u] ) ) {
261 if ( $useNames ) {
262 $data[$u] = [ 'name' => $u, 'missing' => true ];
263 if ( isset( $this->prop['cancreate'] ) ) {
264 $status = $this->authManager->canCreateAccount( $u );
265 $data[$u]['cancreate'] = $status->isGood();
266 if ( !$status->isGood() ) {
267 $data[$u]['cancreateerror'] = $this->getErrorFormatter()->arrayFromStatus( $status );
268 }
269 }
270 } else {
271 $data[$u] = [ 'userid' => $u, 'missing' => true ];
272 }
273
274 } else {
275 if ( isset( $this->prop['groups'] ) && isset( $data[$u]['groups'] ) ) {
276 ApiResult::setArrayType( $data[$u]['groups'], 'array' );
277 ApiResult::setIndexedTagName( $data[$u]['groups'], 'g' );
278 }
279 if ( isset( $this->prop['groupmemberships'] ) && isset( $data[$u]['groupmemberships'] ) ) {
280 ApiResult::setArrayType( $data[$u]['groupmemberships'], 'array' );
281 ApiResult::setIndexedTagName( $data[$u]['groupmemberships'], 'groupmembership' );
282 }
283 if ( isset( $this->prop['implicitgroups'] ) && isset( $data[$u]['implicitgroups'] ) ) {
284 ApiResult::setArrayType( $data[$u]['implicitgroups'], 'array' );
285 ApiResult::setIndexedTagName( $data[$u]['implicitgroups'], 'g' );
286 }
287 if ( isset( $this->prop['rights'] ) && isset( $data[$u]['rights'] ) ) {
288 ApiResult::setArrayType( $data[$u]['rights'], 'array' );
289 ApiResult::setIndexedTagName( $data[$u]['rights'], 'r' );
290 }
291 }
292
293 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $data[$u] );
294 if ( !$fit ) {
295 if ( $useNames ) {
296 $this->setContinueEnumParameter( 'users',
297 implode( '|', array_diff( $users, $done ) ) );
298 } else {
299 $this->setContinueEnumParameter( 'userids',
300 implode( '|', array_diff( $userids, $done ) ) );
301 }
302 break;
303 }
304 $done[] = $u;
305 }
306 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'user' );
307 }
308
309 public function getCacheMode( $params ) {
310 if ( array_diff( (array)$params['prop'], static::$publicProps ) ) {
311 return 'anon-public-user-private';
312 } else {
313 return 'public';
314 }
315 }
316
317 public function getAllowedParams() {
318 return [
319 'prop' => [
320 ParamValidator::PARAM_ISMULTI => true,
321 ParamValidator::PARAM_TYPE => [
322 'blockinfo',
323 'groups',
324 'groupmemberships',
325 'implicitgroups',
326 'rights',
327 'editcount',
328 'registration',
329 'emailable',
330 'gender',
331 'centralids',
332 'cancreate',
333 // When adding a prop, consider whether it should be added
334 // to self::$publicProps
335 ],
337 ],
338 'attachedwiki' => null,
339 'users' => [
340 ParamValidator::PARAM_ISMULTI => true
341 ],
342 'userids' => [
343 ParamValidator::PARAM_ISMULTI => true,
344 ParamValidator::PARAM_TYPE => 'integer'
345 ],
346 ];
347 }
348
349 protected function getExamplesMessages() {
350 return [
351 'action=query&list=users&ususers=Example&usprop=groups|editcount|gender'
352 => 'apihelp-query+users-example-simple',
353 ];
354 }
355
356 public function getHelpUrls() {
357 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Users';
358 }
359}
360
362class_alias( ApiQueryUsers::class, 'ApiQueryUsers' );
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
array $params
The job parameters.
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:571
getResult()
Get the result object.
Definition ApiBase.php:710
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:224
requireMaxOneParameter( $params,... $required)
Dies if more than one parameter from a certain set of parameters are set and not false.
Definition ApiBase.php:1025
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:851
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition ApiBase.php:770
This is a base class for all Query modules.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
getDB()
Get the Query database connection (read-only)
select( $method, $extraQuery=[], ?array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
addWhere( $value)
Add a set of WHERE clauses to the internal array.
getQueryBuilder()
Get the SelectQueryBuilder.
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
resetQueryParams()
Blank the internal arrays with query parameters.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addFields( $value)
Add a set of fields to select to the internal array.
static getCentralUserInfo(Config $config, UserIdentity $user, $attachedWiki=UserIdentity::LOCAL)
Get central user info.
Query module to get information about a list of users.
__construct(ApiQuery $query, string $moduleName, UserNameUtils $userNameUtils, UserFactory $userFactory, UserGroupManager $userGroupManager, GenderCache $genderCache, AuthManager $authManager)
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
static array $publicProps
Properties whose contents does not depend on who is looking at them.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getHelpUrls()
Return links to more detailed help pages about the module.
getExamplesMessages()
Returns usage examples for this module.
getCacheMode( $params)
Get the cache mode for the data generated by this module.
This is the main query class.
Definition ApiQuery.php:48
static formatExpiry( $expiry, $infinity='infinity')
Format an expiry timestamp for API output.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
This serves as the entry point to the authentication system.
Look up "gender" user preference.
Create User objects.
Manage user group memberships.
UserNameUtils service.
User class for the MediaWiki software.
Definition User.php:119
Service for formatting and validating API parameters.