Merge "Add username as parameter for gender support"
[lhc/web/wiklou.git] / includes / api / ApiQuerySiteinfo.php
1 <?php
2 /**
3 *
4 *
5 * Created on Sep 25, 2006
6 *
7 * Copyright © 2006 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 /**
28 * A query action to return meta information about the wiki site.
29 *
30 * @ingroup API
31 */
32 class ApiQuerySiteinfo extends ApiQueryBase {
33
34 public function __construct( $query, $moduleName ) {
35 parent::__construct( $query, $moduleName, 'si' );
36 }
37
38 public function execute() {
39 $params = $this->extractRequestParams();
40 $done = array();
41 $fit = false;
42 foreach ( $params['prop'] as $p ) {
43 switch ( $p ) {
44 case 'general':
45 $fit = $this->appendGeneralInfo( $p );
46 break;
47 case 'namespaces':
48 $fit = $this->appendNamespaces( $p );
49 break;
50 case 'namespacealiases':
51 $fit = $this->appendNamespaceAliases( $p );
52 break;
53 case 'specialpagealiases':
54 $fit = $this->appendSpecialPageAliases( $p );
55 break;
56 case 'magicwords':
57 $fit = $this->appendMagicWords( $p );
58 break;
59 case 'interwikimap':
60 $filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
61 $fit = $this->appendInterwikiMap( $p, $filteriw );
62 break;
63 case 'dbrepllag':
64 $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
65 break;
66 case 'statistics':
67 $fit = $this->appendStatistics( $p );
68 break;
69 case 'usergroups':
70 $fit = $this->appendUserGroups( $p, $params['numberingroup'] );
71 break;
72 case 'extensions':
73 $fit = $this->appendExtensions( $p );
74 break;
75 case 'fileextensions':
76 $fit = $this->appendFileExtensions( $p );
77 break;
78 case 'rightsinfo':
79 $fit = $this->appendRightsInfo( $p );
80 break;
81 case 'languages':
82 $fit = $this->appendLanguages( $p );
83 break;
84 case 'skins':
85 $fit = $this->appendSkins( $p );
86 break;
87 case 'extensiontags':
88 $fit = $this->appendExtensionTags( $p );
89 break;
90 case 'functionhooks':
91 $fit = $this->appendFunctionHooks( $p );
92 break;
93 case 'showhooks':
94 $fit = $this->appendSubscribedHooks( $p );
95 break;
96 default:
97 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
98 }
99 if ( !$fit ) {
100 // Abuse siprop as a query-continue parameter
101 // and set it to all unprocessed props
102 $this->setContinueEnumParameter( 'prop', implode( '|',
103 array_diff( $params['prop'], $done ) ) );
104 break;
105 }
106 $done[] = $p;
107 }
108 }
109
110 protected function appendGeneralInfo( $property ) {
111 global $wgContLang;
112
113 $data = array();
114 $mainPage = Title::newMainPage();
115 $data['mainpage'] = $mainPage->getPrefixedText();
116 $data['base'] = wfExpandUrl( $mainPage->getFullUrl(), PROTO_CURRENT );
117 $data['sitename'] = $GLOBALS['wgSitename'];
118 $data['generator'] = "MediaWiki {$GLOBALS['wgVersion']}";
119 $data['phpversion'] = phpversion();
120 $data['phpsapi'] = php_sapi_name();
121 $data['dbtype'] = $GLOBALS['wgDBtype'];
122 $data['dbversion'] = $this->getDB()->getServerVersion();
123
124 $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] );
125 if ( $svn ) {
126 $data['rev'] = $svn;
127 }
128
129 // 'case-insensitive' option is reserved for future
130 $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive';
131
132 if ( isset( $GLOBALS['wgRightsCode'] ) ) {
133 $data['rightscode'] = $GLOBALS['wgRightsCode'];
134 }
135 $data['rights'] = $GLOBALS['wgRightsText'];
136 $data['lang'] = $GLOBALS['wgLanguageCode'];
137
138 $fallbacks = array();
139 foreach( $wgContLang->getFallbackLanguages() as $code ) {
140 $fallbacks[] = array( 'code' => $code );
141 }
142 $data['fallback'] = $fallbacks;
143 $this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
144
145 if( $wgContLang->hasVariants() ) {
146 $variants = array();
147 foreach( $wgContLang->getVariants() as $code ) {
148 $variants[] = array( 'code' => $code );
149 }
150 $data['variants'] = $variants;
151 $this->getResult()->setIndexedTagName( $data['variants'], 'lang' );
152 }
153
154 if ( $wgContLang->isRTL() ) {
155 $data['rtl'] = '';
156 }
157 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
158
159 if ( wfReadOnly() ) {
160 $data['readonly'] = '';
161 $data['readonlyreason'] = wfReadOnlyReason();
162 }
163 if ( $GLOBALS['wgEnableWriteAPI'] ) {
164 $data['writeapi'] = '';
165 }
166
167 $tz = $GLOBALS['wgLocaltimezone'];
168 $offset = $GLOBALS['wgLocalTZoffset'];
169 if ( is_null( $tz ) ) {
170 $tz = 'UTC';
171 $offset = 0;
172 } elseif ( is_null( $offset ) ) {
173 $offset = 0;
174 }
175 $data['timezone'] = $tz;
176 $data['timeoffset'] = intval( $offset );
177 $data['articlepath'] = $GLOBALS['wgArticlePath'];
178 $data['scriptpath'] = $GLOBALS['wgScriptPath'];
179 $data['script'] = $GLOBALS['wgScript'];
180 $data['variantarticlepath'] = $GLOBALS['wgVariantArticlePath'];
181 $data['server'] = $GLOBALS['wgServer'];
182 $data['wikiid'] = wfWikiID();
183 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
184
185 if ( $GLOBALS['wgMiserMode'] ) {
186 $data['misermode'] = '';
187 }
188
189 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
190
191 wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
192
193 return $this->getResult()->addValue( 'query', $property, $data );
194 }
195
196 protected function appendNamespaces( $property ) {
197 global $wgContLang;
198 $data = array();
199 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
200 $data[$ns] = array(
201 'id' => intval( $ns ),
202 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
203 );
204 ApiResult::setContent( $data[$ns], $title );
205 $canonical = MWNamespace::getCanonicalName( $ns );
206
207 if ( MWNamespace::hasSubpages( $ns ) ) {
208 $data[$ns]['subpages'] = '';
209 }
210
211 if ( $canonical ) {
212 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
213 }
214
215 if ( MWNamespace::isContent( $ns ) ) {
216 $data[$ns]['content'] = '';
217 }
218
219 if ( MWNamespace::isNonincludable( $ns ) ) {
220 $data[$ns]['nonincludable'] = '';
221 }
222 }
223
224 $this->getResult()->setIndexedTagName( $data, 'ns' );
225 return $this->getResult()->addValue( 'query', $property, $data );
226 }
227
228 protected function appendNamespaceAliases( $property ) {
229 global $wgNamespaceAliases, $wgContLang;
230 $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() );
231 $namespaces = $wgContLang->getNamespaces();
232 $data = array();
233 foreach ( $aliases as $title => $ns ) {
234 if ( $namespaces[$ns] == $title ) {
235 // Don't list duplicates
236 continue;
237 }
238 $item = array(
239 'id' => intval( $ns )
240 );
241 ApiResult::setContent( $item, strtr( $title, '_', ' ' ) );
242 $data[] = $item;
243 }
244
245 $this->getResult()->setIndexedTagName( $data, 'ns' );
246 return $this->getResult()->addValue( 'query', $property, $data );
247 }
248
249 protected function appendSpecialPageAliases( $property ) {
250 global $wgContLang;
251 $data = array();
252 foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) {
253 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
254 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
255 $data[] = $arr;
256 }
257 $this->getResult()->setIndexedTagName( $data, 'specialpage' );
258 return $this->getResult()->addValue( 'query', $property, $data );
259 }
260
261 protected function appendMagicWords( $property ) {
262 global $wgContLang;
263 $data = array();
264 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
265 $caseSensitive = array_shift( $aliases );
266 $arr = array( 'name' => $magicword, 'aliases' => $aliases );
267 if ( $caseSensitive ) {
268 $arr['case-sensitive'] = '';
269 }
270 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
271 $data[] = $arr;
272 }
273 $this->getResult()->setIndexedTagName( $data, 'magicword' );
274 return $this->getResult()->addValue( 'query', $property, $data );
275 }
276
277 protected function appendInterwikiMap( $property, $filter ) {
278 $local = null;
279 if ( $filter === 'local' ) {
280 $local = 1;
281 } elseif ( $filter === '!local' ) {
282 $local = 0;
283 } elseif ( $filter ) {
284 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
285 }
286
287 $params = $this->extractRequestParams();
288 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
289 $langNames = Language::fetchLanguageNames( $langCode );
290
291 $getPrefixes = Interwiki::getAllPrefixes( $local );
292 $data = array();
293
294 foreach ( $getPrefixes as $row ) {
295 $prefix = $row['iw_prefix'];
296 $val = array();
297 $val['prefix'] = $prefix;
298 if ( $row['iw_local'] == '1' ) {
299 $val['local'] = '';
300 }
301 // $val['trans'] = intval( $row['iw_trans'] ); // should this be exposed?
302 if ( isset( $langNames[$prefix] ) ) {
303 $val['language'] = $langNames[$prefix];
304 }
305 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
306 if( isset( $row['iw_wikiid'] ) ) {
307 $val['wikiid'] = $row['iw_wikiid'];
308 }
309 if( isset( $row['iw_api'] ) ) {
310 $val['api'] = $row['iw_api'];
311 }
312
313 $data[] = $val;
314 }
315
316 $this->getResult()->setIndexedTagName( $data, 'iw' );
317 return $this->getResult()->addValue( 'query', $property, $data );
318 }
319
320 protected function appendDbReplLagInfo( $property, $includeAll ) {
321 global $wgShowHostnames;
322 $data = array();
323 $lb = wfGetLB();
324 if ( $includeAll ) {
325 if ( !$wgShowHostnames ) {
326 $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' );
327 }
328
329 $lags = $lb->getLagTimes();
330 foreach ( $lags as $i => $lag ) {
331 $data[] = array(
332 'host' => $lb->getServerName( $i ),
333 'lag' => $lag
334 );
335 }
336 } else {
337 list( $host, $lag, $index ) = $lb->getMaxLag();
338 $data[] = array(
339 'host' => $wgShowHostnames
340 ? $lb->getServerName( $index )
341 : '',
342 'lag' => intval( $lag )
343 );
344 }
345
346 $result = $this->getResult();
347 $result->setIndexedTagName( $data, 'db' );
348 return $this->getResult()->addValue( 'query', $property, $data );
349 }
350
351 protected function appendStatistics( $property ) {
352 global $wgDisableCounters;
353 $data = array();
354 $data['pages'] = intval( SiteStats::pages() );
355 $data['articles'] = intval( SiteStats::articles() );
356 if ( !$wgDisableCounters ) {
357 $data['views'] = intval( SiteStats::views() );
358 }
359 $data['edits'] = intval( SiteStats::edits() );
360 $data['images'] = intval( SiteStats::images() );
361 $data['users'] = intval( SiteStats::users() );
362 $data['activeusers'] = intval( SiteStats::activeUsers() );
363 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
364 $data['jobs'] = intval( SiteStats::jobs() );
365 return $this->getResult()->addValue( 'query', $property, $data );
366 }
367
368 protected function appendUserGroups( $property, $numberInGroup ) {
369 global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
370
371 $data = array();
372 $result = $this->getResult();
373 foreach ( $wgGroupPermissions as $group => $permissions ) {
374 $arr = array(
375 'name' => $group,
376 'rights' => array_keys( $permissions, true ),
377 );
378
379 if ( $numberInGroup ) {
380 global $wgAutopromote;
381
382 if ( $group == 'user' ) {
383 $arr['number'] = SiteStats::users();
384
385 // '*' and autopromote groups have no size
386 } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) {
387 $arr['number'] = SiteStats::numberInGroup( $group );
388 }
389 }
390
391 $groupArr = array(
392 'add' => $wgAddGroups,
393 'remove' => $wgRemoveGroups,
394 'add-self' => $wgGroupsAddToSelf,
395 'remove-self' => $wgGroupsRemoveFromSelf
396 );
397
398 foreach ( $groupArr as $type => $rights ) {
399 if ( isset( $rights[$group] ) ) {
400 $arr[$type] = $rights[$group];
401 $result->setIndexedTagName( $arr[$type], 'group' );
402 }
403 }
404
405 $result->setIndexedTagName( $arr['rights'], 'permission' );
406 $data[] = $arr;
407 }
408
409 $result->setIndexedTagName( $data, 'group' );
410 return $result->addValue( 'query', $property, $data );
411 }
412
413 protected function appendFileExtensions( $property ) {
414 global $wgFileExtensions;
415
416 $data = array();
417 foreach ( $wgFileExtensions as $ext ) {
418 $data[] = array( 'ext' => $ext );
419 }
420 $this->getResult()->setIndexedTagName( $data, 'fe' );
421 return $this->getResult()->addValue( 'query', $property, $data );
422 }
423
424 protected function appendExtensions( $property ) {
425 global $wgExtensionCredits;
426 $data = array();
427 foreach ( $wgExtensionCredits as $type => $extensions ) {
428 foreach ( $extensions as $ext ) {
429 $ret = array();
430 $ret['type'] = $type;
431 if ( isset( $ext['name'] ) ) {
432 $ret['name'] = $ext['name'];
433 }
434 if ( isset( $ext['description'] ) ) {
435 $ret['description'] = $ext['description'];
436 }
437 if ( isset( $ext['descriptionmsg'] ) ) {
438 // Can be a string or array( key, param1, param2, ... )
439 if ( is_array( $ext['descriptionmsg'] ) ) {
440 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
441 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
442 $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
443 } else {
444 $ret['descriptionmsg'] = $ext['descriptionmsg'];
445 }
446 }
447 if ( isset( $ext['author'] ) ) {
448 $ret['author'] = is_array( $ext['author'] ) ?
449 implode( ', ', $ext['author' ] ) : $ext['author'];
450 }
451 if ( isset( $ext['url'] ) ) {
452 $ret['url'] = $ext['url'];
453 }
454 if ( isset( $ext['version'] ) ) {
455 $ret['version'] = $ext['version'];
456 } elseif ( isset( $ext['svn-revision'] ) &&
457 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
458 $ext['svn-revision'], $m ) )
459 {
460 $ret['version'] = 'r' . $m[1];
461 }
462 $data[] = $ret;
463 }
464 }
465
466 $this->getResult()->setIndexedTagName( $data, 'ext' );
467 return $this->getResult()->addValue( 'query', $property, $data );
468 }
469
470 protected function appendRightsInfo( $property ) {
471 global $wgRightsPage, $wgRightsUrl, $wgRightsText;
472 $title = Title::newFromText( $wgRightsPage );
473 $url = $title ? wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ) : $wgRightsUrl;
474 $text = $wgRightsText;
475 if ( !$text && $title ) {
476 $text = $title->getPrefixedText();
477 }
478
479 $data = array(
480 'url' => $url ? $url : '',
481 'text' => $text ? $text : ''
482 );
483
484 return $this->getResult()->addValue( 'query', $property, $data );
485 }
486
487 public function appendLanguages( $property ) {
488 $params = $this->extractRequestParams();
489 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
490 $langNames = Language::fetchLanguageNames( $langCode );
491
492 $data = array();
493
494 foreach ( $langNames as $code => $name ) {
495 $lang = array( 'code' => $code );
496 ApiResult::setContent( $lang, $name );
497 $data[] = $lang;
498 }
499 $this->getResult()->setIndexedTagName( $data, 'lang' );
500 return $this->getResult()->addValue( 'query', $property, $data );
501 }
502
503 public function appendSkins( $property ) {
504 $data = array();
505 foreach ( Skin::getSkinNames() as $name => $displayName ) {
506 $skin = array( 'code' => $name );
507 ApiResult::setContent( $skin, $displayName );
508 $data[] = $skin;
509 }
510 $this->getResult()->setIndexedTagName( $data, 'skin' );
511 return $this->getResult()->addValue( 'query', $property, $data );
512 }
513
514 public function appendExtensionTags( $property ) {
515 global $wgParser;
516 $wgParser->firstCallInit();
517 $tags = array_map( array( $this, 'formatParserTags'), $wgParser->getTags() );
518 $this->getResult()->setIndexedTagName( $tags, 't' );
519 return $this->getResult()->addValue( 'query', $property, $tags );
520 }
521
522 public function appendFunctionHooks( $property ) {
523 global $wgParser;
524 $wgParser->firstCallInit();
525 $hooks = $wgParser->getFunctionHooks();
526 $this->getResult()->setIndexedTagName( $hooks, 'h' );
527 return $this->getResult()->addValue( 'query', $property, $hooks );
528 }
529
530 private function formatParserTags( $item ) {
531 return "<{$item}>";
532 }
533
534 public function appendSubscribedHooks( $property ) {
535 global $wgHooks;
536 $myWgHooks = $wgHooks;
537 ksort( $myWgHooks );
538
539 $data = array();
540 foreach ( $myWgHooks as $hook => $hooks ) {
541 $arr = array(
542 'name' => $hook,
543 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $hooks ),
544 );
545
546 $this->getResult()->setIndexedTagName( $arr['subscribers'], 's' );
547 $data[] = $arr;
548 }
549
550 $this->getResult()->setIndexedTagName( $data, 'hook' );
551 return $this->getResult()->addValue( 'query', $property, $data );
552 }
553
554 public function getCacheMode( $params ) {
555 return 'public';
556 }
557
558 public function getAllowedParams() {
559 return array(
560 'prop' => array(
561 ApiBase::PARAM_DFLT => 'general',
562 ApiBase::PARAM_ISMULTI => true,
563 ApiBase::PARAM_TYPE => array(
564 'general',
565 'namespaces',
566 'namespacealiases',
567 'specialpagealiases',
568 'magicwords',
569 'interwikimap',
570 'dbrepllag',
571 'statistics',
572 'usergroups',
573 'extensions',
574 'fileextensions',
575 'rightsinfo',
576 'languages',
577 'skins',
578 'extensiontags',
579 'functionhooks',
580 'showhooks',
581 )
582 ),
583 'filteriw' => array(
584 ApiBase::PARAM_TYPE => array(
585 'local',
586 '!local',
587 )
588 ),
589 'showalldb' => false,
590 'numberingroup' => false,
591 'inlanguagecode' => null,
592 );
593 }
594
595 public function getParamDescription() {
596 $p = $this->getModulePrefix();
597 return array(
598 'prop' => array(
599 'Which sysinfo properties to get:',
600 ' general - Overall system information',
601 ' namespaces - List of registered namespaces and their canonical names',
602 ' namespacealiases - List of registered namespace aliases',
603 ' specialpagealiases - List of special page aliases',
604 ' magicwords - List of magic words and their aliases',
605 ' statistics - Returns site statistics',
606 " interwikimap - Returns interwiki map (optionally filtered, (optionally localised by using {$p}inlanguagecode))",
607 ' dbrepllag - Returns database server with the highest replication lag',
608 ' usergroups - Returns user groups and the associated permissions',
609 ' extensions - Returns extensions installed on the wiki',
610 ' fileextensions - Returns list of file extensions allowed to be uploaded',
611 ' rightsinfo - Returns wiki rights (license) information if available',
612 " languages - Returns a list of languages MediaWiki supports (optionally localised by using {$p}inlanguagecode)",
613 ' skins - Returns a list of all enabled skins',
614 ' extensiontags - Returns a list of parser extension tags',
615 ' functionhooks - Returns a list of parser function hooks',
616 ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)'
617 ),
618 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
619 'showalldb' => 'List all database servers, not just the one lagging the most',
620 'numberingroup' => 'Lists the number of users in user groups',
621 'inlanguagecode' => 'Language code for localised language names (best effort, use CLDR extension)',
622 );
623 }
624
625 public function getDescription() {
626 return 'Return general information about the site';
627 }
628
629 public function getPossibleErrors() {
630 return array_merge( parent::getPossibleErrors(), array(
631 array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ),
632 ) );
633 }
634
635 public function getExamples() {
636 return array(
637 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics',
638 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local',
639 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=',
640 );
641 }
642
643 public function getHelpUrls() {
644 return 'https://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si';
645 }
646
647 public function getVersion() {
648 return __CLASS__ . ': $Id$';
649 }
650 }