MediaWiki master
ApiQueryUserInfo.php
Go to the documentation of this file.
1<?php
9namespace MediaWiki\Api;
10
24
31
33
34 private const WL_UNREAD_LIMIT = 1000;
35
37 private $params = [];
38
40 private $prop = [];
41
42 private TalkPageNotificationManager $talkPageNotificationManager;
43 private WatchedItemStore $watchedItemStore;
44 private UserEditTracker $userEditTracker;
45 private UserOptionsLookup $userOptionsLookup;
46 private UserGroupManager $userGroupManager;
47
48 public function __construct(
49 ApiQuery $query,
50 string $moduleName,
51 TalkPageNotificationManager $talkPageNotificationManager,
52 WatchedItemStore $watchedItemStore,
53 UserEditTracker $userEditTracker,
54 UserOptionsLookup $userOptionsLookup,
55 UserGroupManager $userGroupManager
56 ) {
57 parent::__construct( $query, $moduleName, 'ui' );
58 $this->talkPageNotificationManager = $talkPageNotificationManager;
59 $this->watchedItemStore = $watchedItemStore;
60 $this->userEditTracker = $userEditTracker;
61 $this->userOptionsLookup = $userOptionsLookup;
62 $this->userGroupManager = $userGroupManager;
63 }
64
65 public function execute() {
66 $this->params = $this->extractRequestParams();
67 $result = $this->getResult();
68
69 if ( $this->params['prop'] !== null ) {
70 $this->prop = array_fill_keys( $this->params['prop'], true );
71 }
72
73 $r = $this->getCurrentUserInfo();
74 $result->addValue( 'query', $this->getModuleName(), $r );
75 }
76
89 public static function getCentralUserInfo(
90 Config $config,
91 UserIdentity $user,
92 $attachedWiki = UserIdentity::LOCAL
93 ) {
94 $providerIds = array_keys( $config->get( MainConfigNames::CentralIdLookupProviders ) );
95
96 $ret = [
97 'centralids' => [],
98 'attachedlocal' => [],
99 ];
100 ApiResult::setArrayType( $ret['centralids'], 'assoc' );
101 ApiResult::setArrayType( $ret['attachedlocal'], 'assoc' );
102 if ( $attachedWiki ) {
103 $ret['attachedwiki'] = [];
104 ApiResult::setArrayType( $ret['attachedwiki'], 'assoc' );
105 }
106
107 $name = $user->getName();
108 $centralIdLookupFactory = MediaWikiServices::getInstance()
109 ->getCentralIdLookupFactory();
110 foreach ( $providerIds as $providerId ) {
111 $provider = $centralIdLookupFactory->getLookup( $providerId );
112 $ret['centralids'][$providerId] = $provider->centralIdFromName( $name );
113 $ret['attachedlocal'][$providerId] = $provider->isAttached( $user );
114 if ( $attachedWiki ) {
115 $ret['attachedwiki'][$providerId] = $provider->isAttached( $user, $attachedWiki );
116 }
117 }
118
119 return $ret;
120 }
121
122 protected function getCurrentUserInfo(): array {
123 $user = $this->getUser();
124 $vals = [];
125 $vals['id'] = $user->getId();
126 $vals['name'] = $user->getName();
127
128 if ( !$user->isRegistered() ) {
129 $vals['anon'] = true;
130 }
131
132 if ( $user->isTemp() ) {
133 $vals['temp'] = true;
134 }
135
136 if ( isset( $this->prop['blockinfo'] ) ) {
137 $block = $user->getBlock();
138 if ( $block ) {
139 $vals = array_merge( $vals, $this->getBlockDetails( $block ) );
140 }
141 }
142
143 if ( isset( $this->prop['hasmsg'] ) ) {
144 $vals['messages'] = $this->talkPageNotificationManager->userHasNewMessages( $user );
145 }
146
147 if ( isset( $this->prop['groups'] ) ) {
148 $vals['groups'] = $this->userGroupManager->getUserEffectiveGroups( $user );
149 ApiResult::setArrayType( $vals['groups'], 'array' ); // even if empty
150 ApiResult::setIndexedTagName( $vals['groups'], 'g' ); // even if empty
151 }
152
153 if ( isset( $this->prop['groupmemberships'] ) ) {
154 $ugms = $this->userGroupManager->getUserGroupMemberships( $user );
155 $vals['groupmemberships'] = [];
156 foreach ( $ugms as $group => $ugm ) {
157 $vals['groupmemberships'][] = [
158 'group' => $group,
159 'expiry' => ApiResult::formatExpiry( $ugm->getExpiry() ),
160 ];
161 }
162 ApiResult::setArrayType( $vals['groupmemberships'], 'array' ); // even if empty
163 ApiResult::setIndexedTagName( $vals['groupmemberships'], 'groupmembership' ); // even if empty
164 }
165
166 if ( isset( $this->prop['implicitgroups'] ) ) {
167 $vals['implicitgroups'] = $this->userGroupManager->getUserImplicitGroups( $user );
168 ApiResult::setArrayType( $vals['implicitgroups'], 'array' ); // even if empty
169 ApiResult::setIndexedTagName( $vals['implicitgroups'], 'g' ); // even if empty
170 }
171
172 if ( isset( $this->prop['rights'] ) ) {
173 $vals['rights'] = $this->getPermissionManager()->getUserPermissions( $user );
174 ApiResult::setArrayType( $vals['rights'], 'array' ); // even if empty
175 ApiResult::setIndexedTagName( $vals['rights'], 'r' ); // even if empty
176 }
177
178 if ( isset( $this->prop['changeablegroups'] ) ) {
179 $vals['changeablegroups'] = $this->userGroupManager->getGroupsChangeableBy( $this->getAuthority() );
180 ApiResult::setIndexedTagName( $vals['changeablegroups']['add'], 'g' );
181 ApiResult::setIndexedTagName( $vals['changeablegroups']['remove'], 'g' );
182 ApiResult::setIndexedTagName( $vals['changeablegroups']['add-self'], 'g' );
183 ApiResult::setIndexedTagName( $vals['changeablegroups']['remove-self'], 'g' );
184 }
185
186 if ( isset( $this->prop['options'] ) ) {
187 $vals['options'] = $this->userOptionsLookup->getOptions( $user );
188 $vals['options'][ApiResult::META_BC_BOOLS] = array_keys( $vals['options'] );
189 }
190
191 if ( isset( $this->prop['editcount'] ) ) {
192 // use intval to prevent null if a non-logged-in user calls
193 // api.php?format=jsonfm&action=query&meta=userinfo&uiprop=editcount
194 $vals['editcount'] = (int)$user->getEditCount();
195 }
196
197 if ( isset( $this->prop['ratelimits'] ) ) {
198 // true = real rate limits, taking User::isPingLimitable into account
199 $vals['ratelimits'] = $this->getRateLimits( true );
200 }
201 if ( isset( $this->prop['theoreticalratelimits'] ) ) {
202 // false = ignore User::isPingLimitable
203 $vals['theoreticalratelimits'] = $this->getRateLimits( false );
204 }
205
206 if ( isset( $this->prop['realname'] ) &&
207 !in_array( 'realname', $this->getConfig()->get( MainConfigNames::HiddenPrefs ) )
208 ) {
209 $vals['realname'] = $user->getRealName();
210 }
211
212 if ( $this->getAuthority()->isAllowed( 'viewmyprivateinfo' ) && isset( $this->prop['email'] ) ) {
213 $vals['email'] = $user->getEmail();
214 $auth = $user->getEmailAuthenticationTimestamp();
215 if ( $auth !== null ) {
216 $vals['emailauthenticated'] = wfTimestamp( TS_ISO_8601, $auth );
217 }
218 }
219
220 if ( isset( $this->prop['registrationdate'] ) ) {
221 $regDate = $user->getRegistration();
222 if ( $regDate !== false ) {
223 $vals['registrationdate'] = wfTimestampOrNull( TS_ISO_8601, $regDate );
224 }
225 }
226
227 if ( isset( $this->prop['acceptlang'] ) ) {
228 $langs = $this->getRequest()->getAcceptLang();
229 $acceptLang = [];
230 foreach ( $langs as $lang => $val ) {
231 $r = [ 'q' => $val ];
232 ApiResult::setContentValue( $r, 'code', $lang );
233 $acceptLang[] = $r;
234 }
235 ApiResult::setIndexedTagName( $acceptLang, 'lang' );
236 $vals['acceptlang'] = $acceptLang;
237 }
238
239 if ( isset( $this->prop['unreadcount'] ) ) {
240 $unreadNotifications = $this->watchedItemStore->countUnreadNotifications(
241 $user,
242 self::WL_UNREAD_LIMIT
243 );
244
245 if ( $unreadNotifications === true ) {
246 $vals['unreadcount'] = self::WL_UNREAD_LIMIT . '+';
247 } else {
248 $vals['unreadcount'] = $unreadNotifications;
249 }
250 }
251
252 if ( isset( $this->prop['centralids'] ) ) {
254 $this->getConfig(), $this->getUser(), $this->params['attachedwiki']
255 );
256 }
257
258 if ( isset( $this->prop['latestcontrib'] ) ) {
259 $ts = $this->getLatestContributionTime();
260 if ( $ts !== null ) {
261 $vals['latestcontrib'] = $ts;
262 }
263 }
264
265 if ( isset( $this->prop['cancreateaccount'] ) ) {
266 $status = PermissionStatus::newEmpty();
267 $vals['cancreateaccount'] = $user->definitelyCan( 'createaccount',
268 SpecialPage::getTitleFor( 'CreateAccount' ), $status );
269 if ( !$status->isGood() ) {
270 $vals['cancreateaccounterror'] = $this->getErrorFormatter()->arrayFromStatus( $status );
271 }
272 }
273
274 return $vals;
275 }
276
284 protected function getRateLimits( bool $applyNoRateLimit ) {
285 $retval = [
286 ApiResult::META_TYPE => 'assoc',
287 ];
288
289 $user = $this->getUser();
290 if ( $applyNoRateLimit && !$user->isPingLimitable() ) {
291 return $retval; // No limits
292 }
293
294 // Find out which categories we belong to
295 $categories = [];
296 if ( !$user->isRegistered() ) {
297 $categories[] = 'anon';
298 } else {
299 $categories[] = 'user';
300 }
301 if ( $user->isNewbie() ) {
302 $categories[] = 'ip';
303 $categories[] = 'subnet';
304 if ( $user->isRegistered() ) {
305 $categories[] = 'newbie';
306 }
307 }
308 $categories = array_merge( $categories, $this->userGroupManager->getUserGroups( $user ) );
309
310 // Now get the actual limits
311 foreach ( $this->getConfig()->get( MainConfigNames::RateLimits ) as $action => $limits ) {
312 foreach ( $categories as $cat ) {
313 if ( isset( $limits[$cat] ) ) {
314 $retval[$action][$cat]['hits'] = (int)$limits[$cat][0];
315 $retval[$action][$cat]['seconds'] = (int)$limits[$cat][1];
316 }
317 }
318 }
319
320 return $retval;
321 }
322
326 protected function getLatestContributionTime() {
327 $timestamp = $this->userEditTracker->getLatestEditTimestamp( $this->getUser() );
328 if ( $timestamp === false ) {
329 return null;
330 }
331 return MWTimestamp::convert( TS_ISO_8601, $timestamp );
332 }
333
335 public function getAllowedParams() {
336 return [
337 'prop' => [
338 ParamValidator::PARAM_ISMULTI => true,
339 ParamValidator::PARAM_ALL => true,
340 ParamValidator::PARAM_TYPE => [
341 'blockinfo',
342 'hasmsg',
343 'groups',
344 'groupmemberships',
345 'implicitgroups',
346 'rights',
347 'changeablegroups',
348 'options',
349 'editcount',
350 'ratelimits',
351 'theoreticalratelimits',
352 'email',
353 'realname',
354 'acceptlang',
355 'registrationdate',
356 'unreadcount',
357 'centralids',
358 'latestcontrib',
359 'cancreateaccount',
360 ],
362 'unreadcount' => [
363 'apihelp-query+userinfo-paramvalue-prop-unreadcount',
364 self::WL_UNREAD_LIMIT - 1,
365 self::WL_UNREAD_LIMIT . '+',
366 ],
367 ],
368 ],
369 'attachedwiki' => null,
370 ];
371 }
372
374 protected function getExamplesMessages() {
375 return [
376 'action=query&meta=userinfo'
377 => 'apihelp-query+userinfo-example-simple',
378 'action=query&meta=userinfo&uiprop=blockinfo|groups|rights|hasmsg'
379 => 'apihelp-query+userinfo-example-data',
380 ];
381 }
382
384 public function getHelpUrls() {
385 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Userinfo';
386 }
387}
388
390class_alias( ApiQueryUserInfo::class, 'ApiQueryUserInfo' );
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:543
getResult()
Get the result object.
Definition ApiBase.php:682
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:207
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:823
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition ApiBase.php:742
This is a base class for all Query modules.
Query module to get information about the currently logged-in user.
getRateLimits(bool $applyNoRateLimit)
Get the rate limits that apply to the user, or the rate limits that would apply if the user didn't ha...
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getHelpUrls()
Return links to more detailed help pages about the module.1.25, returning boolean false is deprecated...
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
static getCentralUserInfo(Config $config, UserIdentity $user, $attachedWiki=UserIdentity::LOCAL)
Get central user info.
__construct(ApiQuery $query, string $moduleName, TalkPageNotificationManager $talkPageNotificationManager, WatchedItemStore $watchedItemStore, UserEditTracker $userEditTracker, UserOptionsLookup $userOptionsLookup, UserGroupManager $userGroupManager)
getExamplesMessages()
Returns usage examples for this module.Return value has query strings as keys, with values being eith...
This is the main query class.
Definition ApiQuery.php:36
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.
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
const META_BC_BOOLS
Key for the 'BC bools' metadata item.
const META_TYPE
Key for the 'type' metadata item.
A class containing constants representing the names of configuration variables.
const HiddenPrefs
Name constant for the HiddenPrefs setting, for use with Config::get()
const CentralIdLookupProviders
Name constant for the CentralIdLookupProviders setting, for use with Config::get()
const RateLimits
Name constant for the RateLimits setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
A StatusValue for permission errors.
Parent class for all special pages.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Provides access to user options.
Track info about user edit counts and timings.
Manage user group memberships.
Library for creating and parsing MW-style timestamps.
Storage layer class for WatchedItems.
Service for formatting and validating API parameters.
Interface for configuration instances.
Definition Config.php:18
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Interface for objects representing user identity.