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