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