Merge "Add missing wfProfileOut()"
[mediawiki.git] / includes / api / ApiQuerySiteinfo.php
blobec503d64a5104cc985572313fba0e4c8ad692b7c
1 <?php
2 /**
5 * Created on Sep 25, 2006
7 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
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.
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.
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
24 * @file
27 /**
28 * A query action to return meta information about the wiki site.
30 * @ingroup API
32 class ApiQuerySiteinfo extends ApiQueryBase {
34 public function __construct( $query, $moduleName ) {
35 parent::__construct( $query, $moduleName, 'si' );
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 case 'variables':
97 $fit = $this->appendVariables( $p );
98 break;
99 default:
100 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
102 if ( !$fit ) {
103 // Abuse siprop as a query-continue parameter
104 // and set it to all unprocessed props
105 $this->setContinueEnumParameter( 'prop', implode( '|',
106 array_diff( $params['prop'], $done ) ) );
107 break;
109 $done[] = $p;
113 protected function appendGeneralInfo( $property ) {
114 global $wgContLang;
116 $data = array();
117 $mainPage = Title::newMainPage();
118 $data['mainpage'] = $mainPage->getPrefixedText();
119 $data['base'] = wfExpandUrl( $mainPage->getFullUrl(), PROTO_CURRENT );
120 $data['sitename'] = $GLOBALS['wgSitename'];
121 $data['generator'] = "MediaWiki {$GLOBALS['wgVersion']}";
122 $data['phpversion'] = phpversion();
123 $data['phpsapi'] = php_sapi_name();
124 $data['dbtype'] = $GLOBALS['wgDBtype'];
125 $data['dbversion'] = $this->getDB()->getServerVersion();
127 $git = SpecialVersion::getGitHeadSha1( $GLOBALS['IP'] );
128 if ( $git ) {
129 $data['git-hash'] = $git;
130 } else {
131 $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] );
132 if ( $svn ) {
133 $data['rev'] = $svn;
137 // 'case-insensitive' option is reserved for future
138 $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive';
140 if ( isset( $GLOBALS['wgRightsCode'] ) ) {
141 $data['rightscode'] = $GLOBALS['wgRightsCode'];
143 $data['rights'] = $GLOBALS['wgRightsText'];
144 $data['lang'] = $GLOBALS['wgLanguageCode'];
146 $fallbacks = array();
147 foreach( $wgContLang->getFallbackLanguages() as $code ) {
148 $fallbacks[] = array( 'code' => $code );
150 $data['fallback'] = $fallbacks;
151 $this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
153 if( $wgContLang->hasVariants() ) {
154 $variants = array();
155 foreach( $wgContLang->getVariants() as $code ) {
156 $variants[] = array( 'code' => $code );
158 $data['variants'] = $variants;
159 $this->getResult()->setIndexedTagName( $data['variants'], 'lang' );
162 if ( $wgContLang->isRTL() ) {
163 $data['rtl'] = '';
165 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
167 if ( wfReadOnly() ) {
168 $data['readonly'] = '';
169 $data['readonlyreason'] = wfReadOnlyReason();
171 if ( $GLOBALS['wgEnableWriteAPI'] ) {
172 $data['writeapi'] = '';
175 $tz = $GLOBALS['wgLocaltimezone'];
176 $offset = $GLOBALS['wgLocalTZoffset'];
177 if ( is_null( $tz ) ) {
178 $tz = 'UTC';
179 $offset = 0;
180 } elseif ( is_null( $offset ) ) {
181 $offset = 0;
183 $data['timezone'] = $tz;
184 $data['timeoffset'] = intval( $offset );
185 $data['articlepath'] = $GLOBALS['wgArticlePath'];
186 $data['scriptpath'] = $GLOBALS['wgScriptPath'];
187 $data['script'] = $GLOBALS['wgScript'];
188 $data['variantarticlepath'] = $GLOBALS['wgVariantArticlePath'];
189 $data['server'] = $GLOBALS['wgServer'];
190 $data['wikiid'] = wfWikiID();
191 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
193 if ( $GLOBALS['wgMiserMode'] ) {
194 $data['misermode'] = '';
197 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
199 wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
201 return $this->getResult()->addValue( 'query', $property, $data );
204 protected function appendNamespaces( $property ) {
205 global $wgContLang;
206 $data = array();
207 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
208 $data[$ns] = array(
209 'id' => intval( $ns ),
210 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
212 ApiResult::setContent( $data[$ns], $title );
213 $canonical = MWNamespace::getCanonicalName( $ns );
215 if ( MWNamespace::hasSubpages( $ns ) ) {
216 $data[$ns]['subpages'] = '';
219 if ( $canonical ) {
220 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
223 if ( MWNamespace::isContent( $ns ) ) {
224 $data[$ns]['content'] = '';
227 if ( MWNamespace::isNonincludable( $ns ) ) {
228 $data[$ns]['nonincludable'] = '';
232 $this->getResult()->setIndexedTagName( $data, 'ns' );
233 return $this->getResult()->addValue( 'query', $property, $data );
236 protected function appendNamespaceAliases( $property ) {
237 global $wgNamespaceAliases, $wgContLang;
238 $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() );
239 $namespaces = $wgContLang->getNamespaces();
240 $data = array();
241 foreach ( $aliases as $title => $ns ) {
242 if ( $namespaces[$ns] == $title ) {
243 // Don't list duplicates
244 continue;
246 $item = array(
247 'id' => intval( $ns )
249 ApiResult::setContent( $item, strtr( $title, '_', ' ' ) );
250 $data[] = $item;
253 $this->getResult()->setIndexedTagName( $data, 'ns' );
254 return $this->getResult()->addValue( 'query', $property, $data );
257 protected function appendSpecialPageAliases( $property ) {
258 global $wgContLang;
259 $data = array();
260 $aliases = $wgContLang->getSpecialPageAliases();
261 foreach ( SpecialPageFactory::getList() as $specialpage => $stuff ) {
262 if ( isset( $aliases[$specialpage] ) ) {
263 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] );
264 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
265 $data[] = $arr;
268 $this->getResult()->setIndexedTagName( $data, 'specialpage' );
269 return $this->getResult()->addValue( 'query', $property, $data );
272 protected function appendMagicWords( $property ) {
273 global $wgContLang;
274 $data = array();
275 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
276 $caseSensitive = array_shift( $aliases );
277 $arr = array( 'name' => $magicword, 'aliases' => $aliases );
278 if ( $caseSensitive ) {
279 $arr['case-sensitive'] = '';
281 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
282 $data[] = $arr;
284 $this->getResult()->setIndexedTagName( $data, 'magicword' );
285 return $this->getResult()->addValue( 'query', $property, $data );
288 protected function appendInterwikiMap( $property, $filter ) {
289 $local = null;
290 if ( $filter === 'local' ) {
291 $local = 1;
292 } elseif ( $filter === '!local' ) {
293 $local = 0;
294 } elseif ( $filter ) {
295 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
298 $params = $this->extractRequestParams();
299 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
300 $langNames = Language::fetchLanguageNames( $langCode );
302 $getPrefixes = Interwiki::getAllPrefixes( $local );
303 $data = array();
305 foreach ( $getPrefixes as $row ) {
306 $prefix = $row['iw_prefix'];
307 $val = array();
308 $val['prefix'] = $prefix;
309 if ( $row['iw_local'] == '1' ) {
310 $val['local'] = '';
312 // $val['trans'] = intval( $row['iw_trans'] ); // should this be exposed?
313 if ( isset( $langNames[$prefix] ) ) {
314 $val['language'] = $langNames[$prefix];
316 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
317 if( isset( $row['iw_wikiid'] ) ) {
318 $val['wikiid'] = $row['iw_wikiid'];
320 if( isset( $row['iw_api'] ) ) {
321 $val['api'] = $row['iw_api'];
324 $data[] = $val;
327 $this->getResult()->setIndexedTagName( $data, 'iw' );
328 return $this->getResult()->addValue( 'query', $property, $data );
331 protected function appendDbReplLagInfo( $property, $includeAll ) {
332 global $wgShowHostnames;
333 $data = array();
334 $lb = wfGetLB();
335 if ( $includeAll ) {
336 if ( !$wgShowHostnames ) {
337 $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' );
340 $lags = $lb->getLagTimes();
341 foreach ( $lags as $i => $lag ) {
342 $data[] = array(
343 'host' => $lb->getServerName( $i ),
344 'lag' => $lag
347 } else {
348 list( $host, $lag, $index ) = $lb->getMaxLag();
349 $data[] = array(
350 'host' => $wgShowHostnames
351 ? $lb->getServerName( $index )
352 : '',
353 'lag' => intval( $lag )
357 $result = $this->getResult();
358 $result->setIndexedTagName( $data, 'db' );
359 return $this->getResult()->addValue( 'query', $property, $data );
362 protected function appendStatistics( $property ) {
363 global $wgDisableCounters;
364 $data = array();
365 $data['pages'] = intval( SiteStats::pages() );
366 $data['articles'] = intval( SiteStats::articles() );
367 if ( !$wgDisableCounters ) {
368 $data['views'] = intval( SiteStats::views() );
370 $data['edits'] = intval( SiteStats::edits() );
371 $data['images'] = intval( SiteStats::images() );
372 $data['users'] = intval( SiteStats::users() );
373 $data['activeusers'] = intval( SiteStats::activeUsers() );
374 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
375 $data['jobs'] = intval( SiteStats::jobs() );
376 return $this->getResult()->addValue( 'query', $property, $data );
379 protected function appendUserGroups( $property, $numberInGroup ) {
380 global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
382 $data = array();
383 $result = $this->getResult();
384 foreach ( $wgGroupPermissions as $group => $permissions ) {
385 $arr = array(
386 'name' => $group,
387 'rights' => array_keys( $permissions, true ),
390 if ( $numberInGroup ) {
391 global $wgAutopromote;
393 if ( $group == 'user' ) {
394 $arr['number'] = SiteStats::users();
396 // '*' and autopromote groups have no size
397 } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) {
398 $arr['number'] = SiteStats::numberInGroup( $group );
402 $groupArr = array(
403 'add' => $wgAddGroups,
404 'remove' => $wgRemoveGroups,
405 'add-self' => $wgGroupsAddToSelf,
406 'remove-self' => $wgGroupsRemoveFromSelf
409 foreach ( $groupArr as $type => $rights ) {
410 if ( isset( $rights[$group] ) ) {
411 $arr[$type] = $rights[$group];
412 $result->setIndexedTagName( $arr[$type], 'group' );
416 $result->setIndexedTagName( $arr['rights'], 'permission' );
417 $data[] = $arr;
420 $result->setIndexedTagName( $data, 'group' );
421 return $result->addValue( 'query', $property, $data );
424 protected function appendFileExtensions( $property ) {
425 global $wgFileExtensions;
427 $data = array();
428 foreach ( $wgFileExtensions as $ext ) {
429 $data[] = array( 'ext' => $ext );
431 $this->getResult()->setIndexedTagName( $data, 'fe' );
432 return $this->getResult()->addValue( 'query', $property, $data );
435 protected function appendExtensions( $property ) {
436 global $wgExtensionCredits;
437 $data = array();
438 foreach ( $wgExtensionCredits as $type => $extensions ) {
439 foreach ( $extensions as $ext ) {
440 $ret = array();
441 $ret['type'] = $type;
442 if ( isset( $ext['name'] ) ) {
443 $ret['name'] = $ext['name'];
445 if ( isset( $ext['description'] ) ) {
446 $ret['description'] = $ext['description'];
448 if ( isset( $ext['descriptionmsg'] ) ) {
449 // Can be a string or array( key, param1, param2, ... )
450 if ( is_array( $ext['descriptionmsg'] ) ) {
451 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
452 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
453 $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
454 } else {
455 $ret['descriptionmsg'] = $ext['descriptionmsg'];
458 if ( isset( $ext['author'] ) ) {
459 $ret['author'] = is_array( $ext['author'] ) ?
460 implode( ', ', $ext['author' ] ) : $ext['author'];
462 if ( isset( $ext['url'] ) ) {
463 $ret['url'] = $ext['url'];
465 if ( isset( $ext['version'] ) ) {
466 $ret['version'] = $ext['version'];
467 } elseif ( isset( $ext['svn-revision'] ) &&
468 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
469 $ext['svn-revision'], $m ) )
471 $ret['version'] = 'r' . $m[1];
473 $data[] = $ret;
477 $this->getResult()->setIndexedTagName( $data, 'ext' );
478 return $this->getResult()->addValue( 'query', $property, $data );
481 protected function appendRightsInfo( $property ) {
482 global $wgRightsPage, $wgRightsUrl, $wgRightsText;
483 $title = Title::newFromText( $wgRightsPage );
484 $url = $title ? wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ) : $wgRightsUrl;
485 $text = $wgRightsText;
486 if ( !$text && $title ) {
487 $text = $title->getPrefixedText();
490 $data = array(
491 'url' => $url ? $url : '',
492 'text' => $text ? $text : ''
495 return $this->getResult()->addValue( 'query', $property, $data );
498 public function appendLanguages( $property ) {
499 $params = $this->extractRequestParams();
500 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
501 $langNames = Language::fetchLanguageNames( $langCode );
503 $data = array();
505 foreach ( $langNames as $code => $name ) {
506 $lang = array( 'code' => $code );
507 ApiResult::setContent( $lang, $name );
508 $data[] = $lang;
510 $this->getResult()->setIndexedTagName( $data, 'lang' );
511 return $this->getResult()->addValue( 'query', $property, $data );
514 public function appendSkins( $property ) {
515 $data = array();
516 foreach ( Skin::getSkinNames() as $name => $displayName ) {
517 $skin = array( 'code' => $name );
518 ApiResult::setContent( $skin, $displayName );
519 $data[] = $skin;
521 $this->getResult()->setIndexedTagName( $data, 'skin' );
522 return $this->getResult()->addValue( 'query', $property, $data );
525 public function appendExtensionTags( $property ) {
526 global $wgParser;
527 $wgParser->firstCallInit();
528 $tags = array_map( array( $this, 'formatParserTags'), $wgParser->getTags() );
529 $this->getResult()->setIndexedTagName( $tags, 't' );
530 return $this->getResult()->addValue( 'query', $property, $tags );
533 public function appendFunctionHooks( $property ) {
534 global $wgParser;
535 $wgParser->firstCallInit();
536 $hooks = $wgParser->getFunctionHooks();
537 $this->getResult()->setIndexedTagName( $hooks, 'h' );
538 return $this->getResult()->addValue( 'query', $property, $hooks );
541 public function appendVariables( $property ) {
542 $variables = MagicWord::getVariableIDs();
543 $this->getResult()->setIndexedTagName( $variables, 'v' );
544 return $this->getResult()->addValue( 'query', $property, $variables );
547 private function formatParserTags( $item ) {
548 return "<{$item}>";
551 public function appendSubscribedHooks( $property ) {
552 global $wgHooks;
553 $myWgHooks = $wgHooks;
554 ksort( $myWgHooks );
556 $data = array();
557 foreach ( $myWgHooks as $hook => $hooks ) {
558 $arr = array(
559 'name' => $hook,
560 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $hooks ),
563 $this->getResult()->setIndexedTagName( $arr['subscribers'], 's' );
564 $data[] = $arr;
567 $this->getResult()->setIndexedTagName( $data, 'hook' );
568 return $this->getResult()->addValue( 'query', $property, $data );
571 public function getCacheMode( $params ) {
572 return 'public';
575 public function getAllowedParams() {
576 return array(
577 'prop' => array(
578 ApiBase::PARAM_DFLT => 'general',
579 ApiBase::PARAM_ISMULTI => true,
580 ApiBase::PARAM_TYPE => array(
581 'general',
582 'namespaces',
583 'namespacealiases',
584 'specialpagealiases',
585 'magicwords',
586 'interwikimap',
587 'dbrepllag',
588 'statistics',
589 'usergroups',
590 'extensions',
591 'fileextensions',
592 'rightsinfo',
593 'languages',
594 'skins',
595 'extensiontags',
596 'functionhooks',
597 'showhooks',
598 'variables',
601 'filteriw' => array(
602 ApiBase::PARAM_TYPE => array(
603 'local',
604 '!local',
607 'showalldb' => false,
608 'numberingroup' => false,
609 'inlanguagecode' => null,
613 public function getParamDescription() {
614 $p = $this->getModulePrefix();
615 return array(
616 'prop' => array(
617 'Which sysinfo properties to get:',
618 ' general - Overall system information',
619 ' namespaces - List of registered namespaces and their canonical names',
620 ' namespacealiases - List of registered namespace aliases',
621 ' specialpagealiases - List of special page aliases',
622 ' magicwords - List of magic words and their aliases',
623 ' statistics - Returns site statistics',
624 " interwikimap - Returns interwiki map (optionally filtered, (optionally localised by using {$p}inlanguagecode))",
625 ' dbrepllag - Returns database server with the highest replication lag',
626 ' usergroups - Returns user groups and the associated permissions',
627 ' extensions - Returns extensions installed on the wiki',
628 ' fileextensions - Returns list of file extensions allowed to be uploaded',
629 ' rightsinfo - Returns wiki rights (license) information if available',
630 " languages - Returns a list of languages MediaWiki supports (optionally localised by using {$p}inlanguagecode)",
631 ' skins - Returns a list of all enabled skins',
632 ' extensiontags - Returns a list of parser extension tags',
633 ' functionhooks - Returns a list of parser function hooks',
634 ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)',
635 ' variables - Returns a list of variable IDs',
637 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
638 'showalldb' => 'List all database servers, not just the one lagging the most',
639 'numberingroup' => 'Lists the number of users in user groups',
640 'inlanguagecode' => 'Language code for localised language names (best effort, use CLDR extension)',
644 public function getDescription() {
645 return 'Return general information about the site';
648 public function getPossibleErrors() {
649 return array_merge( parent::getPossibleErrors(), array(
650 array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ),
651 ) );
654 public function getExamples() {
655 return array(
656 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics',
657 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local',
658 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=',
662 public function getHelpUrls() {
663 return 'https://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si';
666 public function getVersion() {
667 return __CLASS__ . ': $Id$';