MediaWiki REL1_39
ApiQueryUsers.php
Go to the documentation of this file.
1<?php
29
37
38 private $prop;
39
41 private $userNameUtils;
42
44 private $userFactory;
45
47 private $userGroupManager;
48
50 private $genderCache;
51
53 private $authManager;
54
60 protected static $publicProps = [
61 // everything except 'blockinfo' which might show hidden records if the user
62 // making the request has the appropriate permissions
63 'groups',
64 'groupmemberships',
65 'implicitgroups',
66 'rights',
67 'editcount',
68 'registration',
69 'emailable',
70 'gender',
71 'centralids',
72 'cancreate',
73 ];
74
84 public function __construct(
85 ApiQuery $query,
86 $moduleName,
87 UserNameUtils $userNameUtils,
88 UserFactory $userFactory,
89 UserGroupManager $userGroupManager,
90 GenderCache $genderCache,
91 AuthManager $authManager
92 ) {
93 parent::__construct( $query, $moduleName, 'us' );
94 $this->userNameUtils = $userNameUtils;
95 $this->userFactory = $userFactory;
96 $this->userGroupManager = $userGroupManager;
97 $this->genderCache = $genderCache;
98 $this->authManager = $authManager;
99 }
100
101 public function execute() {
102 $db = $this->getDB();
103 $params = $this->extractRequestParams();
104 $this->requireMaxOneParameter( $params, 'userids', 'users' );
105
106 if ( $params['prop'] !== null ) {
107 $this->prop = array_fill_keys( $params['prop'], true );
108 } else {
109 $this->prop = [];
110 }
111 $useNames = $params['users'] !== null;
112
113 $users = (array)$params['users'];
114 $userids = (array)$params['userids'];
115
116 $goodNames = $done = [];
117 $result = $this->getResult();
118 // Canonicalize user names
119 foreach ( $users as $u ) {
120 $n = $this->userNameUtils->getCanonical( $u );
121 if ( $n === false || $n === '' ) {
122 $vals = [ 'name' => $u, 'invalid' => true ];
123 $fit = $result->addValue( [ 'query', $this->getModuleName() ],
124 null, $vals );
125 if ( !$fit ) {
126 $this->setContinueEnumParameter( 'users',
127 implode( '|', array_diff( $users, $done ) ) );
128 $goodNames = [];
129 break;
130 }
131 $done[] = $u;
132 } else {
133 $goodNames[] = $n;
134 }
135 }
136
137 if ( $useNames ) {
138 $parameters = &$goodNames;
139 } else {
140 $parameters = &$userids;
141 }
142
143 $result = $this->getResult();
144
145 if ( count( $parameters ) ) {
146 $userQuery = User::getQueryInfo();
147 $this->addTables( $userQuery['tables'] );
148 $this->addFields( $userQuery['fields'] );
149 $this->addJoinConds( $userQuery['joins'] );
150 if ( $useNames ) {
151 $this->addWhereFld( 'user_name', $goodNames );
152 } else {
153 $this->addWhereFld( 'user_id', $userids );
154 }
155
156 $this->addBlockInfoToQuery( isset( $this->prop['blockinfo'] ) );
157
158 $data = [];
159 $res = $this->select( __METHOD__ );
160 $this->resetQueryParams();
161
162 // get user groups if needed
163 if ( isset( $this->prop['groups'] ) || isset( $this->prop['rights'] ) ) {
164 $userGroups = [];
165
166 $this->addTables( 'user' );
167 if ( $useNames ) {
168 $this->addWhereFld( 'user_name', $goodNames );
169 } else {
170 $this->addWhereFld( 'user_id', $userids );
171 }
172
173 $this->addTables( 'user_groups' );
174 $this->addJoinConds( [ 'user_groups' => [ 'JOIN', 'ug_user=user_id' ] ] );
175 $this->addFields( [ 'user_name' ] );
176 $this->addFields( $this->userGroupManager->getQueryInfo()['fields'] );
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 if ( isset( $this->prop['gender'] ) ) {
186 $userNames = [];
187 foreach ( $res as $row ) {
188 $userNames[] = $row->user_name;
189 }
190 $this->genderCache->doQuery( $userNames, __METHOD__ );
191 }
192
193 foreach ( $res as $row ) {
194 // create user object and pass along $userGroups if set
195 // that reduces the number of database queries needed in User dramatically
196 if ( !isset( $userGroups ) ) {
197 $user = $this->userFactory->newFromRow( $row );
198 } else {
199 if ( !isset( $userGroups[$row->user_name] ) || !is_array( $userGroups[$row->user_name] ) ) {
200 $userGroups[$row->user_name] = [];
201 }
202 $user = $this->userFactory->newFromRow( $row, [ 'user_groups' => $userGroups[$row->user_name] ] );
203 }
204 if ( $useNames ) {
205 $key = $user->getName();
206 } else {
207 $key = $user->getId();
208 }
209 $data[$key]['userid'] = $user->getId();
210 $data[$key]['name'] = $user->getName();
211
212 if ( isset( $this->prop['editcount'] ) ) {
213 $data[$key]['editcount'] = $user->getEditCount();
214 }
215
216 if ( isset( $this->prop['registration'] ) ) {
217 $data[$key]['registration'] = wfTimestampOrNull( TS_ISO_8601, $user->getRegistration() );
218 }
219
220 if ( isset( $this->prop['groups'] ) ) {
221 $data[$key]['groups'] = $this->userGroupManager->getUserEffectiveGroups( $user );
222 }
223
224 if ( isset( $this->prop['groupmemberships'] ) ) {
225 $data[$key]['groupmemberships'] = array_map( static function ( $ugm ) {
226 return [
227 'group' => $ugm->getGroup(),
228 'expiry' => ApiResult::formatExpiry( $ugm->getExpiry() ),
229 ];
230 }, $this->userGroupManager->getUserGroupMemberships( $user ) );
231 }
232
233 if ( isset( $this->prop['implicitgroups'] ) ) {
234 $data[$key]['implicitgroups'] = $this->userGroupManager->getUserImplicitGroups( $user );
235 }
236
237 if ( isset( $this->prop['rights'] ) ) {
238 $data[$key]['rights'] = $this->getPermissionManager()
239 ->getUserPermissions( $user );
240 }
241 if ( $row->ipb_deleted ) {
242 $data[$key]['hidden'] = true;
243 }
244 if ( isset( $this->prop['blockinfo'] ) && $row->ipb_by_text !== null ) {
245 $data[$key] += $this->getBlockDetails( DatabaseBlock::newFromRow( $row ) );
246 }
247
248 if ( isset( $this->prop['emailable'] ) ) {
249 $data[$key]['emailable'] = $user->canReceiveEmail();
250 }
251
252 if ( isset( $this->prop['gender'] ) ) {
253 $data[$key]['gender'] = $this->genderCache->getGenderOf( $user, __METHOD__ );
254 }
255
256 if ( isset( $this->prop['centralids'] ) ) {
258 $this->getConfig(), $user, $params['attachedwiki']
259 );
260 }
261 }
262 }
263
264 $context = $this->getContext();
265 // Second pass: add result data to $retval
266 foreach ( $parameters as $u ) {
267 if ( !isset( $data[$u] ) ) {
268 if ( $useNames ) {
269 $data[$u] = [ 'name' => $u ];
270 $urPage = new UserrightsPage;
271 $urPage->setContext( $context );
272
273 $iwUser = $urPage->fetchUser( $u );
274
275 if ( $iwUser instanceof UserRightsProxy ) {
276 $data[$u]['interwiki'] = true;
277 } else {
278 $data[$u]['missing'] = true;
279 if ( isset( $this->prop['cancreate'] ) ) {
280 $status = $this->authManager->canCreateAccount( $u );
281 $data[$u]['cancreate'] = $status->isGood();
282 if ( !$status->isGood() ) {
283 $data[$u]['cancreateerror'] = $this->getErrorFormatter()->arrayFromStatus( $status );
284 }
285 }
286 }
287 } else {
288 $data[$u] = [ 'userid' => $u, 'missing' => true ];
289 }
290
291 } else {
292 if ( isset( $this->prop['groups'] ) && isset( $data[$u]['groups'] ) ) {
293 ApiResult::setArrayType( $data[$u]['groups'], 'array' );
294 ApiResult::setIndexedTagName( $data[$u]['groups'], 'g' );
295 }
296 if ( isset( $this->prop['groupmemberships'] ) && isset( $data[$u]['groupmemberships'] ) ) {
297 ApiResult::setArrayType( $data[$u]['groupmemberships'], 'array' );
298 ApiResult::setIndexedTagName( $data[$u]['groupmemberships'], 'groupmembership' );
299 }
300 if ( isset( $this->prop['implicitgroups'] ) && isset( $data[$u]['implicitgroups'] ) ) {
301 ApiResult::setArrayType( $data[$u]['implicitgroups'], 'array' );
302 ApiResult::setIndexedTagName( $data[$u]['implicitgroups'], 'g' );
303 }
304 if ( isset( $this->prop['rights'] ) && isset( $data[$u]['rights'] ) ) {
305 ApiResult::setArrayType( $data[$u]['rights'], 'array' );
306 ApiResult::setIndexedTagName( $data[$u]['rights'], 'r' );
307 }
308 }
309
310 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $data[$u] );
311 if ( !$fit ) {
312 if ( $useNames ) {
313 $this->setContinueEnumParameter( 'users',
314 implode( '|', array_diff( $users, $done ) ) );
315 } else {
316 $this->setContinueEnumParameter( 'userids',
317 implode( '|', array_diff( $userids, $done ) ) );
318 }
319 break;
320 }
321 $done[] = $u;
322 }
323 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'user' );
324 }
325
326 public function getCacheMode( $params ) {
327 if ( array_diff( (array)$params['prop'], static::$publicProps ) ) {
328 return 'anon-public-user-private';
329 } else {
330 return 'public';
331 }
332 }
333
334 public function getAllowedParams() {
335 return [
336 'prop' => [
337 ParamValidator::PARAM_ISMULTI => true,
338 ParamValidator::PARAM_TYPE => [
339 'blockinfo',
340 'groups',
341 'groupmemberships',
342 'implicitgroups',
343 'rights',
344 'editcount',
345 'registration',
346 'emailable',
347 'gender',
348 'centralids',
349 'cancreate',
350 // When adding a prop, consider whether it should be added
351 // to self::$publicProps
352 ],
354 ],
355 'attachedwiki' => null,
356 'users' => [
357 ParamValidator::PARAM_ISMULTI => true
358 ],
359 'userids' => [
360 ParamValidator::PARAM_ISMULTI => true,
361 ParamValidator::PARAM_TYPE => 'integer'
362 ],
363 ];
364 }
365
366 protected function getExamplesMessages() {
367 return [
368 'action=query&list=users&ususers=Example&usprop=groups|editcount|gender'
369 => 'apihelp-query+users-example-simple',
370 ];
371 }
372
373 public function getHelpUrls() {
374 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Users';
375 }
376}
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
getErrorFormatter()
Definition ApiBase.php:640
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition ApiBase.php:686
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:196
requireMaxOneParameter( $params,... $required)
Die if more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:938
getResult()
Get the result object.
Definition ApiBase.php:629
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:765
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:498
This is a base class for all Query modules.
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
resetQueryParams()
Blank the internal arrays with query parameters.
addFields( $value)
Add a set of fields to select to the internal array.
addTables( $tables, $alias=null)
Add a set of tables 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.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addWhere( $value)
Add a set of WHERE clauses 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.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
static array $publicProps
Properties whose contents does not depend on who is looking at them.
getHelpUrls()
Return links to more detailed help pages about the module.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
getExamplesMessages()
Returns usage examples for this module.
__construct(ApiQuery $query, $moduleName, UserNameUtils $userNameUtils, UserFactory $userFactory, UserGroupManager $userGroupManager, GenderCache $genderCache, AuthManager $authManager)
getCacheMode( $params)
Get the cache mode for the data generated by this module.
This is the main query class.
Definition ApiQuery.php:41
getContext()
Get the base IContextSource object.
Caches user genders when needed to use correct namespace aliases.
This serves as the entry point to the authentication system.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Creates User objects.
UserNameUtils service.
setContext( $context)
Sets the context this SpecialPage is executed in.
Cut-down copy of User interface for local-interwiki-database user rights manipulation.
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new user object.
Definition User.php:3370
Special page to allow managing user group membership.
Service for formatting and validating API parameters.
trait ApiQueryBlockInfoTrait
return true
Definition router.php:92