MediaWiki master
ApiQueryUserInfo.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Api;
24
38
45
47
48 private const WL_UNREAD_LIMIT = 1000;
49
51 private $params = [];
52
54 private $prop = [];
55
56 private TalkPageNotificationManager $talkPageNotificationManager;
57 private WatchedItemStore $watchedItemStore;
58 private UserEditTracker $userEditTracker;
59 private UserOptionsLookup $userOptionsLookup;
60 private UserGroupManager $userGroupManager;
61
62 public function __construct(
63 ApiQuery $query,
64 string $moduleName,
65 TalkPageNotificationManager $talkPageNotificationManager,
66 WatchedItemStore $watchedItemStore,
67 UserEditTracker $userEditTracker,
68 UserOptionsLookup $userOptionsLookup,
69 UserGroupManager $userGroupManager
70 ) {
71 parent::__construct( $query, $moduleName, 'ui' );
72 $this->talkPageNotificationManager = $talkPageNotificationManager;
73 $this->watchedItemStore = $watchedItemStore;
74 $this->userEditTracker = $userEditTracker;
75 $this->userOptionsLookup = $userOptionsLookup;
76 $this->userGroupManager = $userGroupManager;
77 }
78
79 public function execute() {
80 $this->params = $this->extractRequestParams();
81 $result = $this->getResult();
82
83 if ( $this->params['prop'] !== null ) {
84 $this->prop = array_fill_keys( $this->params['prop'], true );
85 }
86
87 $r = $this->getCurrentUserInfo();
88 $result->addValue( 'query', $this->getModuleName(), $r );
89 }
90
103 public static function getCentralUserInfo(
104 Config $config,
105 UserIdentity $user,
106 $attachedWiki = UserIdentity::LOCAL
107 ) {
108 $providerIds = array_keys( $config->get( MainConfigNames::CentralIdLookupProviders ) );
109
110 $ret = [
111 'centralids' => [],
112 'attachedlocal' => [],
113 ];
114 ApiResult::setArrayType( $ret['centralids'], 'assoc' );
115 ApiResult::setArrayType( $ret['attachedlocal'], 'assoc' );
116 if ( $attachedWiki ) {
117 $ret['attachedwiki'] = [];
118 ApiResult::setArrayType( $ret['attachedwiki'], 'assoc' );
119 }
120
121 $name = $user->getName();
122 $centralIdLookupFactory = MediaWikiServices::getInstance()
123 ->getCentralIdLookupFactory();
124 foreach ( $providerIds as $providerId ) {
125 $provider = $centralIdLookupFactory->getLookup( $providerId );
126 $ret['centralids'][$providerId] = $provider->centralIdFromName( $name );
127 $ret['attachedlocal'][$providerId] = $provider->isAttached( $user );
128 if ( $attachedWiki ) {
129 $ret['attachedwiki'][$providerId] = $provider->isAttached( $user, $attachedWiki );
130 }
131 }
132
133 return $ret;
134 }
135
136 protected function getCurrentUserInfo() {
137 $user = $this->getUser();
138 $vals = [];
139 $vals['id'] = $user->getId();
140 $vals['name'] = $user->getName();
141
142 if ( !$user->isRegistered() ) {
143 $vals['anon'] = true;
144 }
145
146 if ( $user->isTemp() ) {
147 $vals['temp'] = true;
148 }
149
150 if ( isset( $this->prop['blockinfo'] ) ) {
151 $block = $user->getBlock();
152 if ( $block ) {
153 $vals = array_merge( $vals, $this->getBlockDetails( $block ) );
154 }
155 }
156
157 if ( isset( $this->prop['hasmsg'] ) ) {
158 $vals['messages'] = $this->talkPageNotificationManager->userHasNewMessages( $user );
159 }
160
161 if ( isset( $this->prop['groups'] ) ) {
162 $vals['groups'] = $this->userGroupManager->getUserEffectiveGroups( $user );
163 ApiResult::setArrayType( $vals['groups'], 'array' ); // even if empty
164 ApiResult::setIndexedTagName( $vals['groups'], 'g' ); // even if empty
165 }
166
167 if ( isset( $this->prop['groupmemberships'] ) ) {
168 $ugms = $this->userGroupManager->getUserGroupMemberships( $user );
169 $vals['groupmemberships'] = [];
170 foreach ( $ugms as $group => $ugm ) {
171 $vals['groupmemberships'][] = [
172 'group' => $group,
173 'expiry' => ApiResult::formatExpiry( $ugm->getExpiry() ),
174 ];
175 }
176 ApiResult::setArrayType( $vals['groupmemberships'], 'array' ); // even if empty
177 ApiResult::setIndexedTagName( $vals['groupmemberships'], 'groupmembership' ); // even if empty
178 }
179
180 if ( isset( $this->prop['implicitgroups'] ) ) {
181 $vals['implicitgroups'] = $this->userGroupManager->getUserImplicitGroups( $user );
182 ApiResult::setArrayType( $vals['implicitgroups'], 'array' ); // even if empty
183 ApiResult::setIndexedTagName( $vals['implicitgroups'], 'g' ); // even if empty
184 }
185
186 if ( isset( $this->prop['rights'] ) ) {
187 $vals['rights'] = $this->getPermissionManager()->getUserPermissions( $user );
188 ApiResult::setArrayType( $vals['rights'], 'array' ); // even if empty
189 ApiResult::setIndexedTagName( $vals['rights'], 'r' ); // even if empty
190 }
191
192 if ( isset( $this->prop['changeablegroups'] ) ) {
193 $vals['changeablegroups'] = $this->userGroupManager->getGroupsChangeableBy( $this->getAuthority() );
194 ApiResult::setIndexedTagName( $vals['changeablegroups']['add'], 'g' );
195 ApiResult::setIndexedTagName( $vals['changeablegroups']['remove'], 'g' );
196 ApiResult::setIndexedTagName( $vals['changeablegroups']['add-self'], 'g' );
197 ApiResult::setIndexedTagName( $vals['changeablegroups']['remove-self'], 'g' );
198 }
199
200 if ( isset( $this->prop['options'] ) ) {
201 $vals['options'] = $this->userOptionsLookup->getOptions( $user );
202 $vals['options'][ApiResult::META_BC_BOOLS] = array_keys( $vals['options'] );
203 }
204
205 if ( isset( $this->prop['editcount'] ) ) {
206 // use intval to prevent null if a non-logged-in user calls
207 // api.php?format=jsonfm&action=query&meta=userinfo&uiprop=editcount
208 $vals['editcount'] = (int)$user->getEditCount();
209 }
210
211 if ( isset( $this->prop['ratelimits'] ) ) {
212 // true = real rate limits, taking User::isPingLimitable into account
213 $vals['ratelimits'] = $this->getRateLimits( true );
214 }
215 if ( isset( $this->prop['theoreticalratelimits'] ) ) {
216 // false = ignore User::isPingLimitable
217 $vals['theoreticalratelimits'] = $this->getRateLimits( false );
218 }
219
220 if ( isset( $this->prop['realname'] ) &&
221 !in_array( 'realname', $this->getConfig()->get( MainConfigNames::HiddenPrefs ) )
222 ) {
223 $vals['realname'] = $user->getRealName();
224 }
225
226 if ( $this->getAuthority()->isAllowed( 'viewmyprivateinfo' ) && isset( $this->prop['email'] ) ) {
227 $vals['email'] = $user->getEmail();
228 $auth = $user->getEmailAuthenticationTimestamp();
229 if ( $auth !== null ) {
230 $vals['emailauthenticated'] = wfTimestamp( TS_ISO_8601, $auth );
231 }
232 }
233
234 if ( isset( $this->prop['registrationdate'] ) ) {
235 $regDate = $user->getRegistration();
236 if ( $regDate !== false ) {
237 $vals['registrationdate'] = wfTimestampOrNull( TS_ISO_8601, $regDate );
238 }
239 }
240
241 if ( isset( $this->prop['acceptlang'] ) ) {
242 $langs = $this->getRequest()->getAcceptLang();
243 $acceptLang = [];
244 foreach ( $langs as $lang => $val ) {
245 $r = [ 'q' => $val ];
246 ApiResult::setContentValue( $r, 'code', $lang );
247 $acceptLang[] = $r;
248 }
249 ApiResult::setIndexedTagName( $acceptLang, 'lang' );
250 $vals['acceptlang'] = $acceptLang;
251 }
252
253 if ( isset( $this->prop['unreadcount'] ) ) {
254 $unreadNotifications = $this->watchedItemStore->countUnreadNotifications(
255 $user,
256 self::WL_UNREAD_LIMIT
257 );
258
259 if ( $unreadNotifications === true ) {
260 $vals['unreadcount'] = self::WL_UNREAD_LIMIT . '+';
261 } else {
262 $vals['unreadcount'] = $unreadNotifications;
263 }
264 }
265
266 if ( isset( $this->prop['centralids'] ) ) {
268 $this->getConfig(), $this->getUser(), $this->params['attachedwiki']
269 );
270 }
271
272 if ( isset( $this->prop['latestcontrib'] ) ) {
273 $ts = $this->getLatestContributionTime();
274 if ( $ts !== null ) {
275 $vals['latestcontrib'] = $ts;
276 }
277 }
278
279 if ( isset( $this->prop['cancreateaccount'] ) ) {
280 $status = PermissionStatus::newEmpty();
281 $vals['cancreateaccount'] = $user->definitelyCan( 'createaccount',
282 SpecialPage::getTitleFor( 'CreateAccount' ), $status );
283 if ( !$status->isGood() ) {
284 $vals['cancreateaccounterror'] = $this->getErrorFormatter()->arrayFromStatus( $status );
285 }
286 }
287
288 return $vals;
289 }
290
298 protected function getRateLimits( bool $applyNoRateLimit ) {
299 $retval = [
300 ApiResult::META_TYPE => 'assoc',
301 ];
302
303 $user = $this->getUser();
304 if ( $applyNoRateLimit && !$user->isPingLimitable() ) {
305 return $retval; // No limits
306 }
307
308 // Find out which categories we belong to
309 $categories = [];
310 if ( !$user->isRegistered() ) {
311 $categories[] = 'anon';
312 } else {
313 $categories[] = 'user';
314 }
315 if ( $user->isNewbie() ) {
316 $categories[] = 'ip';
317 $categories[] = 'subnet';
318 if ( $user->isRegistered() ) {
319 $categories[] = 'newbie';
320 }
321 }
322 $categories = array_merge( $categories, $this->userGroupManager->getUserGroups( $user ) );
323
324 // Now get the actual limits
325 foreach ( $this->getConfig()->get( MainConfigNames::RateLimits ) as $action => $limits ) {
326 foreach ( $categories as $cat ) {
327 if ( isset( $limits[$cat] ) ) {
328 $retval[$action][$cat]['hits'] = (int)$limits[$cat][0];
329 $retval[$action][$cat]['seconds'] = (int)$limits[$cat][1];
330 }
331 }
332 }
333
334 return $retval;
335 }
336
340 protected function getLatestContributionTime() {
341 $timestamp = $this->userEditTracker->getLatestEditTimestamp( $this->getUser() );
342 if ( $timestamp === false ) {
343 return null;
344 }
345 return MWTimestamp::convert( TS_ISO_8601, $timestamp );
346 }
347
348 public function getAllowedParams() {
349 return [
350 'prop' => [
351 ParamValidator::PARAM_ISMULTI => true,
352 ParamValidator::PARAM_ALL => true,
353 ParamValidator::PARAM_TYPE => [
354 'blockinfo',
355 'hasmsg',
356 'groups',
357 'groupmemberships',
358 'implicitgroups',
359 'rights',
360 'changeablegroups',
361 'options',
362 'editcount',
363 'ratelimits',
364 'theoreticalratelimits',
365 'email',
366 'realname',
367 'acceptlang',
368 'registrationdate',
369 'unreadcount',
370 'centralids',
371 'latestcontrib',
372 'cancreateaccount',
373 ],
375 'unreadcount' => [
376 'apihelp-query+userinfo-paramvalue-prop-unreadcount',
377 self::WL_UNREAD_LIMIT - 1,
378 self::WL_UNREAD_LIMIT . '+',
379 ],
380 ],
381 ],
382 'attachedwiki' => null,
383 ];
384 }
385
386 protected function getExamplesMessages() {
387 return [
388 'action=query&meta=userinfo'
389 => 'apihelp-query+userinfo-example-simple',
390 'action=query&meta=userinfo&uiprop=blockinfo|groups|rights|hasmsg'
391 => 'apihelp-query+userinfo-example-data',
392 ];
393 }
394
395 public function getHelpUrls() {
396 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Userinfo';
397 }
398}
399
401class_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: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
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.
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.
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.
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.
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.
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:32
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
Interface for objects representing user identity.