Merge "Minor adjustments to align with WikimediaUI color palette"
[lhc/web/wiklou.git] / includes / api / ApiQueryUserInfo.php
1 <?php
2 /**
3 *
4 *
5 * Created on July 30, 2007
6 *
7 * Copyright © 2007 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * http://www.gnu.org/copyleft/gpl.html
23 *
24 * @file
25 */
26
27 use MediaWiki\MediaWikiServices;
28
29 /**
30 * Query module to get information about the currently logged-in user
31 *
32 * @ingroup API
33 */
34 class ApiQueryUserInfo extends ApiQueryBase {
35
36 const WL_UNREAD_LIMIT = 1000;
37
38 private $params = [];
39 private $prop = [];
40
41 public function __construct( ApiQuery $query, $moduleName ) {
42 parent::__construct( $query, $moduleName, 'ui' );
43 }
44
45 public function execute() {
46 $this->params = $this->extractRequestParams();
47 $result = $this->getResult();
48
49 if ( !is_null( $this->params['prop'] ) ) {
50 $this->prop = array_flip( $this->params['prop'] );
51 }
52
53 $r = $this->getCurrentUserInfo();
54 $result->addValue( 'query', $this->getModuleName(), $r );
55 }
56
57 /**
58 * Get basic info about a given block
59 * @param Block $block
60 * @return array Array containing several keys:
61 * - blockid - ID of the block
62 * - blockedby - username of the blocker
63 * - blockedbyid - user ID of the blocker
64 * - blockreason - reason provided for the block
65 * - blockedtimestamp - timestamp for when the block was placed/modified
66 * - blockexpiry - expiry time of the block
67 * - systemblocktype - system block type, if any
68 */
69 public static function getBlockInfo( Block $block ) {
70 global $wgContLang;
71 $vals = [];
72 $vals['blockid'] = $block->getId();
73 $vals['blockedby'] = $block->getByName();
74 $vals['blockedbyid'] = $block->getBy();
75 $vals['blockreason'] = $block->mReason;
76 $vals['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $block->mTimestamp );
77 $vals['blockexpiry'] = $wgContLang->formatExpiry(
78 $block->getExpiry(), TS_ISO_8601, 'infinite'
79 );
80 if ( $block->getSystemBlockType() !== null ) {
81 $vals['systemblocktype'] = $block->getSystemBlockType();
82 }
83 return $vals;
84 }
85
86 /**
87 * Get central user info
88 * @param Config $config
89 * @param User $user
90 * @param string|null $attachedWiki
91 * @return array Central user info
92 * - centralids: Array mapping non-local Central ID provider names to IDs
93 * - attachedlocal: Array mapping Central ID provider names to booleans
94 * indicating whether the local user is attached.
95 * - attachedwiki: Array mapping Central ID provider names to booleans
96 * indicating whether the user is attached to $attachedWiki.
97 */
98 public static function getCentralUserInfo( Config $config, User $user, $attachedWiki = null ) {
99 $providerIds = array_keys( $config->get( 'CentralIdLookupProviders' ) );
100
101 $ret = [
102 'centralids' => [],
103 'attachedlocal' => [],
104 ];
105 ApiResult::setArrayType( $ret['centralids'], 'assoc' );
106 ApiResult::setArrayType( $ret['attachedlocal'], 'assoc' );
107 if ( $attachedWiki ) {
108 $ret['attachedwiki'] = [];
109 ApiResult::setArrayType( $ret['attachedwiki'], 'assoc' );
110 }
111
112 $name = $user->getName();
113 foreach ( $providerIds as $providerId ) {
114 $provider = CentralIdLookup::factory( $providerId );
115 $ret['centralids'][$providerId] = $provider->centralIdFromName( $name );
116 $ret['attachedlocal'][$providerId] = $provider->isAttached( $user );
117 if ( $attachedWiki ) {
118 $ret['attachedwiki'][$providerId] = $provider->isAttached( $user, $attachedWiki );
119 }
120 }
121
122 return $ret;
123 }
124
125 protected function getCurrentUserInfo() {
126 $user = $this->getUser();
127 $vals = [];
128 $vals['id'] = intval( $user->getId() );
129 $vals['name'] = $user->getName();
130
131 if ( $user->isAnon() ) {
132 $vals['anon'] = true;
133 }
134
135 if ( isset( $this->prop['blockinfo'] ) && $user->isBlocked() ) {
136 $vals = array_merge( $vals, self::getBlockInfo( $user->getBlock() ) );
137 }
138
139 if ( isset( $this->prop['hasmsg'] ) ) {
140 $vals['messages'] = $user->getNewtalk();
141 }
142
143 if ( isset( $this->prop['groups'] ) ) {
144 $vals['groups'] = $user->getEffectiveGroups();
145 ApiResult::setArrayType( $vals['groups'], 'array' ); // even if empty
146 ApiResult::setIndexedTagName( $vals['groups'], 'g' ); // even if empty
147 }
148
149 if ( isset( $this->prop['implicitgroups'] ) ) {
150 $vals['implicitgroups'] = $user->getAutomaticGroups();
151 ApiResult::setArrayType( $vals['implicitgroups'], 'array' ); // even if empty
152 ApiResult::setIndexedTagName( $vals['implicitgroups'], 'g' ); // even if empty
153 }
154
155 if ( isset( $this->prop['rights'] ) ) {
156 // User::getRights() may return duplicate values, strip them
157 $vals['rights'] = array_values( array_unique( $user->getRights() ) );
158 ApiResult::setArrayType( $vals['rights'], 'array' ); // even if empty
159 ApiResult::setIndexedTagName( $vals['rights'], 'r' ); // even if empty
160 }
161
162 if ( isset( $this->prop['changeablegroups'] ) ) {
163 $vals['changeablegroups'] = $user->changeableGroups();
164 ApiResult::setIndexedTagName( $vals['changeablegroups']['add'], 'g' );
165 ApiResult::setIndexedTagName( $vals['changeablegroups']['remove'], 'g' );
166 ApiResult::setIndexedTagName( $vals['changeablegroups']['add-self'], 'g' );
167 ApiResult::setIndexedTagName( $vals['changeablegroups']['remove-self'], 'g' );
168 }
169
170 if ( isset( $this->prop['options'] ) ) {
171 $vals['options'] = $user->getOptions();
172 $vals['options'][ApiResult::META_BC_BOOLS] = array_keys( $vals['options'] );
173 }
174
175 if ( isset( $this->prop['preferencestoken'] ) ) {
176 $p = $this->getModulePrefix();
177 $this->addDeprecation(
178 [
179 'apiwarn-deprecation-withreplacement',
180 "{$p}prop=preferencestoken",
181 'action=query&meta=tokens',
182 ],
183 "meta=userinfo&{$p}prop=preferencestoken"
184 );
185 }
186 if ( isset( $this->prop['preferencestoken'] ) &&
187 !$this->lacksSameOriginSecurity() &&
188 $user->isAllowed( 'editmyoptions' )
189 ) {
190 $vals['preferencestoken'] = $user->getEditToken( '', $this->getMain()->getRequest() );
191 }
192
193 if ( isset( $this->prop['editcount'] ) ) {
194 // use intval to prevent null if a non-logged-in user calls
195 // api.php?format=jsonfm&action=query&meta=userinfo&uiprop=editcount
196 $vals['editcount'] = intval( $user->getEditCount() );
197 }
198
199 if ( isset( $this->prop['ratelimits'] ) ) {
200 $vals['ratelimits'] = $this->getRateLimits();
201 }
202
203 if ( isset( $this->prop['realname'] ) &&
204 !in_array( 'realname', $this->getConfig()->get( 'HiddenPrefs' ) )
205 ) {
206 $vals['realname'] = $user->getRealName();
207 }
208
209 if ( $user->isAllowed( 'viewmyprivateinfo' ) ) {
210 if ( isset( $this->prop['email'] ) ) {
211 $vals['email'] = $user->getEmail();
212 $auth = $user->getEmailAuthenticationTimestamp();
213 if ( !is_null( $auth ) ) {
214 $vals['emailauthenticated'] = wfTimestamp( TS_ISO_8601, $auth );
215 }
216 }
217 }
218
219 if ( isset( $this->prop['registrationdate'] ) ) {
220 $regDate = $user->getRegistration();
221 if ( $regDate !== false ) {
222 $vals['registrationdate'] = wfTimestamp( TS_ISO_8601, $regDate );
223 }
224 }
225
226 if ( isset( $this->prop['acceptlang'] ) ) {
227 $langs = $this->getRequest()->getAcceptLang();
228 $acceptLang = [];
229 foreach ( $langs as $lang => $val ) {
230 $r = [ 'q' => $val ];
231 ApiResult::setContentValue( $r, 'code', $lang );
232 $acceptLang[] = $r;
233 }
234 ApiResult::setIndexedTagName( $acceptLang, 'lang' );
235 $vals['acceptlang'] = $acceptLang;
236 }
237
238 if ( isset( $this->prop['unreadcount'] ) ) {
239 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
240 $unreadNotifications = $store->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'] ) ) {
253 $vals += self::getCentralUserInfo(
254 $this->getConfig(), $this->getUser(), $this->params['attachedwiki']
255 );
256 }
257
258 return $vals;
259 }
260
261 protected function getRateLimits() {
262 $retval = [
263 ApiResult::META_TYPE => 'assoc',
264 ];
265
266 $user = $this->getUser();
267 if ( !$user->isPingLimitable() ) {
268 return $retval; // No limits
269 }
270
271 // Find out which categories we belong to
272 $categories = [];
273 if ( $user->isAnon() ) {
274 $categories[] = 'anon';
275 } else {
276 $categories[] = 'user';
277 }
278 if ( $user->isNewbie() ) {
279 $categories[] = 'ip';
280 $categories[] = 'subnet';
281 if ( !$user->isAnon() ) {
282 $categories[] = 'newbie';
283 }
284 }
285 $categories = array_merge( $categories, $user->getGroups() );
286
287 // Now get the actual limits
288 foreach ( $this->getConfig()->get( 'RateLimits' ) as $action => $limits ) {
289 foreach ( $categories as $cat ) {
290 if ( isset( $limits[$cat] ) && !is_null( $limits[$cat] ) ) {
291 $retval[$action][$cat]['hits'] = intval( $limits[$cat][0] );
292 $retval[$action][$cat]['seconds'] = intval( $limits[$cat][1] );
293 }
294 }
295 }
296
297 return $retval;
298 }
299
300 public function getAllowedParams() {
301 return [
302 'prop' => [
303 ApiBase::PARAM_ISMULTI => true,
304 ApiBase::PARAM_TYPE => [
305 'blockinfo',
306 'hasmsg',
307 'groups',
308 'implicitgroups',
309 'rights',
310 'changeablegroups',
311 'options',
312 'preferencestoken',
313 'editcount',
314 'ratelimits',
315 'email',
316 'realname',
317 'acceptlang',
318 'registrationdate',
319 'unreadcount',
320 'centralids',
321 ],
322 ApiBase::PARAM_HELP_MSG_PER_VALUE => [
323 'unreadcount' => [
324 'apihelp-query+userinfo-paramvalue-prop-unreadcount',
325 self::WL_UNREAD_LIMIT - 1,
326 self::WL_UNREAD_LIMIT . '+',
327 ],
328 ],
329 ],
330 'attachedwiki' => null,
331 ];
332 }
333
334 protected function getExamplesMessages() {
335 return [
336 'action=query&meta=userinfo'
337 => 'apihelp-query+userinfo-example-simple',
338 'action=query&meta=userinfo&uiprop=blockinfo|groups|rights|hasmsg'
339 => 'apihelp-query+userinfo-example-data',
340 ];
341 }
342
343 public function getHelpUrls() {
344 return 'https://www.mediawiki.org/wiki/API:Userinfo';
345 }
346 }