No more undefined usage of rev{Start,End}Id warnings
[mediawiki.git] / includes / api / ApiQuerySiteinfo.php
blobe11d1105262af5ec919568c1240067a9cfe94558
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 default:
97 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
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;
106 $done[] = $p;
110 protected function appendGeneralInfo( $property ) {
111 global $wgContLang;
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();
124 $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] );
125 if ( $svn ) {
126 $data['rev'] = $svn;
129 // 'case-insensitive' option is reserved for future
130 $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive';
132 if ( isset( $GLOBALS['wgRightsCode'] ) ) {
133 $data['rightscode'] = $GLOBALS['wgRightsCode'];
135 $data['rights'] = $GLOBALS['wgRightsText'];
136 $data['lang'] = $GLOBALS['wgLanguageCode'];
138 $fallbacks = array();
139 foreach( $wgContLang->getFallbackLanguages() as $code ) {
140 $fallbacks[] = array( 'code' => $code );
142 $data['fallback'] = $fallbacks;
143 $this->getResult()->setIndexedTagName( $data['fallback'], 'lang' );
145 if ( $wgContLang->isRTL() ) {
146 $data['rtl'] = '';
148 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
150 if ( wfReadOnly() ) {
151 $data['readonly'] = '';
152 $data['readonlyreason'] = wfReadOnlyReason();
154 if ( $GLOBALS['wgEnableWriteAPI'] ) {
155 $data['writeapi'] = '';
158 $tz = $GLOBALS['wgLocaltimezone'];
159 $offset = $GLOBALS['wgLocalTZoffset'];
160 if ( is_null( $tz ) ) {
161 $tz = 'UTC';
162 $offset = 0;
163 } elseif ( is_null( $offset ) ) {
164 $offset = 0;
166 $data['timezone'] = $tz;
167 $data['timeoffset'] = intval( $offset );
168 $data['articlepath'] = $GLOBALS['wgArticlePath'];
169 $data['scriptpath'] = $GLOBALS['wgScriptPath'];
170 $data['script'] = $GLOBALS['wgScript'];
171 $data['variantarticlepath'] = $GLOBALS['wgVariantArticlePath'];
172 $data['server'] = $GLOBALS['wgServer'];
173 $data['wikiid'] = wfWikiID();
174 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
176 if ( $GLOBALS['wgMiserMode'] ) {
177 $data['misermode'] = '';
180 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
182 wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
184 return $this->getResult()->addValue( 'query', $property, $data );
187 protected function appendNamespaces( $property ) {
188 global $wgContLang;
189 $data = array();
190 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
191 $data[$ns] = array(
192 'id' => intval( $ns ),
193 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
195 ApiResult::setContent( $data[$ns], $title );
196 $canonical = MWNamespace::getCanonicalName( $ns );
198 if ( MWNamespace::hasSubpages( $ns ) ) {
199 $data[$ns]['subpages'] = '';
202 if ( $canonical ) {
203 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
206 if ( MWNamespace::isContent( $ns ) ) {
207 $data[$ns]['content'] = '';
211 $this->getResult()->setIndexedTagName( $data, 'ns' );
212 return $this->getResult()->addValue( 'query', $property, $data );
215 protected function appendNamespaceAliases( $property ) {
216 global $wgNamespaceAliases, $wgContLang;
217 $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() );
218 $namespaces = $wgContLang->getNamespaces();
219 $data = array();
220 foreach ( $aliases as $title => $ns ) {
221 if ( $namespaces[$ns] == $title ) {
222 // Don't list duplicates
223 continue;
225 $item = array(
226 'id' => intval( $ns )
228 ApiResult::setContent( $item, strtr( $title, '_', ' ' ) );
229 $data[] = $item;
232 $this->getResult()->setIndexedTagName( $data, 'ns' );
233 return $this->getResult()->addValue( 'query', $property, $data );
236 protected function appendSpecialPageAliases( $property ) {
237 global $wgContLang;
238 $data = array();
239 foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) {
240 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
241 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
242 $data[] = $arr;
244 $this->getResult()->setIndexedTagName( $data, 'specialpage' );
245 return $this->getResult()->addValue( 'query', $property, $data );
248 protected function appendMagicWords( $property ) {
249 global $wgContLang;
250 $data = array();
251 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
252 $caseSensitive = array_shift( $aliases );
253 $arr = array( 'name' => $magicword, 'aliases' => $aliases );
254 if ( $caseSensitive ) {
255 $arr['case-sensitive'] = '';
257 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
258 $data[] = $arr;
260 $this->getResult()->setIndexedTagName( $data, 'magicword' );
261 return $this->getResult()->addValue( 'query', $property, $data );
264 protected function appendInterwikiMap( $property, $filter ) {
265 $local = null;
266 if ( $filter === 'local' ) {
267 $local = 1;
268 } elseif ( $filter === '!local' ) {
269 $local = 0;
270 } elseif ( $filter ) {
271 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
274 $params = $this->extractRequestParams();
275 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
276 $langNames = Language::fetchLanguageNames( $langCode );
278 $getPrefixes = Interwiki::getAllPrefixes( $local );
279 $data = array();
281 foreach ( $getPrefixes as $row ) {
282 $prefix = $row['iw_prefix'];
283 $val = array();
284 $val['prefix'] = $prefix;
285 if ( $row['iw_local'] == '1' ) {
286 $val['local'] = '';
288 // $val['trans'] = intval( $row['iw_trans'] ); // should this be exposed?
289 if ( isset( $langNames[$prefix] ) ) {
290 $val['language'] = $langNames[$prefix];
292 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
293 if( isset( $row['iw_wikiid'] ) ) {
294 $val['wikiid'] = $row['iw_wikiid'];
296 if( isset( $row['iw_api'] ) ) {
297 $val['api'] = $row['iw_api'];
300 $data[] = $val;
303 $this->getResult()->setIndexedTagName( $data, 'iw' );
304 return $this->getResult()->addValue( 'query', $property, $data );
307 protected function appendDbReplLagInfo( $property, $includeAll ) {
308 global $wgShowHostnames;
309 $data = array();
310 $lb = wfGetLB();
311 if ( $includeAll ) {
312 if ( !$wgShowHostnames ) {
313 $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' );
316 $lags = $lb->getLagTimes();
317 foreach ( $lags as $i => $lag ) {
318 $data[] = array(
319 'host' => $lb->getServerName( $i ),
320 'lag' => $lag
323 } else {
324 list( $host, $lag, $index ) = $lb->getMaxLag();
325 $data[] = array(
326 'host' => $wgShowHostnames
327 ? $lb->getServerName( $index )
328 : '',
329 'lag' => intval( $lag )
333 $result = $this->getResult();
334 $result->setIndexedTagName( $data, 'db' );
335 return $this->getResult()->addValue( 'query', $property, $data );
338 protected function appendStatistics( $property ) {
339 global $wgDisableCounters;
340 $data = array();
341 $data['pages'] = intval( SiteStats::pages() );
342 $data['articles'] = intval( SiteStats::articles() );
343 if ( !$wgDisableCounters ) {
344 $data['views'] = intval( SiteStats::views() );
346 $data['edits'] = intval( SiteStats::edits() );
347 $data['images'] = intval( SiteStats::images() );
348 $data['users'] = intval( SiteStats::users() );
349 $data['activeusers'] = intval( SiteStats::activeUsers() );
350 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
351 $data['jobs'] = intval( SiteStats::jobs() );
352 return $this->getResult()->addValue( 'query', $property, $data );
355 protected function appendUserGroups( $property, $numberInGroup ) {
356 global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
358 $data = array();
359 $result = $this->getResult();
360 foreach ( $wgGroupPermissions as $group => $permissions ) {
361 $arr = array(
362 'name' => $group,
363 'rights' => array_keys( $permissions, true ),
366 if ( $numberInGroup ) {
367 global $wgAutopromote;
369 if ( $group == 'user' ) {
370 $arr['number'] = SiteStats::users();
372 // '*' and autopromote groups have no size
373 } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) {
374 $arr['number'] = SiteStats::numberInGroup( $group );
378 $groupArr = array(
379 'add' => $wgAddGroups,
380 'remove' => $wgRemoveGroups,
381 'add-self' => $wgGroupsAddToSelf,
382 'remove-self' => $wgGroupsRemoveFromSelf
385 foreach ( $groupArr as $type => $rights ) {
386 if ( isset( $rights[$group] ) ) {
387 $arr[$type] = $rights[$group];
388 $result->setIndexedTagName( $arr[$type], 'group' );
392 $result->setIndexedTagName( $arr['rights'], 'permission' );
393 $data[] = $arr;
396 $result->setIndexedTagName( $data, 'group' );
397 return $result->addValue( 'query', $property, $data );
400 protected function appendFileExtensions( $property ) {
401 global $wgFileExtensions;
403 $data = array();
404 foreach ( $wgFileExtensions as $ext ) {
405 $data[] = array( 'ext' => $ext );
407 $this->getResult()->setIndexedTagName( $data, 'fe' );
408 return $this->getResult()->addValue( 'query', $property, $data );
411 protected function appendExtensions( $property ) {
412 global $wgExtensionCredits;
413 $data = array();
414 foreach ( $wgExtensionCredits as $type => $extensions ) {
415 foreach ( $extensions as $ext ) {
416 $ret = array();
417 $ret['type'] = $type;
418 if ( isset( $ext['name'] ) ) {
419 $ret['name'] = $ext['name'];
421 if ( isset( $ext['description'] ) ) {
422 $ret['description'] = $ext['description'];
424 if ( isset( $ext['descriptionmsg'] ) ) {
425 // Can be a string or array( key, param1, param2, ... )
426 if ( is_array( $ext['descriptionmsg'] ) ) {
427 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
428 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
429 $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
430 } else {
431 $ret['descriptionmsg'] = $ext['descriptionmsg'];
434 if ( isset( $ext['author'] ) ) {
435 $ret['author'] = is_array( $ext['author'] ) ?
436 implode( ', ', $ext['author' ] ) : $ext['author'];
438 if ( isset( $ext['url'] ) ) {
439 $ret['url'] = $ext['url'];
441 if ( isset( $ext['version'] ) ) {
442 $ret['version'] = $ext['version'];
443 } elseif ( isset( $ext['svn-revision'] ) &&
444 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
445 $ext['svn-revision'], $m ) )
447 $ret['version'] = 'r' . $m[1];
449 $data[] = $ret;
453 $this->getResult()->setIndexedTagName( $data, 'ext' );
454 return $this->getResult()->addValue( 'query', $property, $data );
457 protected function appendRightsInfo( $property ) {
458 global $wgRightsPage, $wgRightsUrl, $wgRightsText;
459 $title = Title::newFromText( $wgRightsPage );
460 $url = $title ? wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ) : $wgRightsUrl;
461 $text = $wgRightsText;
462 if ( !$text && $title ) {
463 $text = $title->getPrefixedText();
466 $data = array(
467 'url' => $url ? $url : '',
468 'text' => $text ? $text : ''
471 return $this->getResult()->addValue( 'query', $property, $data );
474 public function appendLanguages( $property ) {
475 $params = $this->extractRequestParams();
476 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
477 $langNames = Language::fetchLanguageNames( $langCode );
479 $data = array();
481 foreach ( $langNames as $code => $name ) {
482 $lang = array( 'code' => $code );
483 ApiResult::setContent( $lang, $name );
484 $data[] = $lang;
486 $this->getResult()->setIndexedTagName( $data, 'lang' );
487 return $this->getResult()->addValue( 'query', $property, $data );
490 public function appendSkins( $property ) {
491 $data = array();
492 foreach ( Skin::getSkinNames() as $name => $displayName ) {
493 $skin = array( 'code' => $name );
494 ApiResult::setContent( $skin, $displayName );
495 $data[] = $skin;
497 $this->getResult()->setIndexedTagName( $data, 'skin' );
498 return $this->getResult()->addValue( 'query', $property, $data );
501 public function appendExtensionTags( $property ) {
502 global $wgParser;
503 $wgParser->firstCallInit();
504 $tags = array_map( array( $this, 'formatParserTags'), $wgParser->getTags() );
505 $this->getResult()->setIndexedTagName( $tags, 't' );
506 return $this->getResult()->addValue( 'query', $property, $tags );
509 public function appendFunctionHooks( $property ) {
510 global $wgParser;
511 $wgParser->firstCallInit();
512 $hooks = $wgParser->getFunctionHooks();
513 $this->getResult()->setIndexedTagName( $hooks, 'h' );
514 return $this->getResult()->addValue( 'query', $property, $hooks );
517 private function formatParserTags( $item ) {
518 return "<{$item}>";
521 public function appendSubscribedHooks( $property ) {
522 global $wgHooks;
523 $myWgHooks = $wgHooks;
524 ksort( $myWgHooks );
526 $data = array();
527 foreach ( $myWgHooks as $hook => $hooks ) {
528 $arr = array(
529 'name' => $hook,
530 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $hooks ),
533 $this->getResult()->setIndexedTagName( $arr['subscribers'], 's' );
534 $data[] = $arr;
537 $this->getResult()->setIndexedTagName( $data, 'hook' );
538 return $this->getResult()->addValue( 'query', $property, $data );
541 public function getCacheMode( $params ) {
542 return 'public';
545 public function getAllowedParams() {
546 return array(
547 'prop' => array(
548 ApiBase::PARAM_DFLT => 'general',
549 ApiBase::PARAM_ISMULTI => true,
550 ApiBase::PARAM_TYPE => array(
551 'general',
552 'namespaces',
553 'namespacealiases',
554 'specialpagealiases',
555 'magicwords',
556 'interwikimap',
557 'dbrepllag',
558 'statistics',
559 'usergroups',
560 'extensions',
561 'fileextensions',
562 'rightsinfo',
563 'languages',
564 'skins',
565 'extensiontags',
566 'functionhooks',
567 'showhooks',
570 'filteriw' => array(
571 ApiBase::PARAM_TYPE => array(
572 'local',
573 '!local',
576 'showalldb' => false,
577 'numberingroup' => false,
578 'inlanguagecode' => null,
582 public function getParamDescription() {
583 $p = $this->getModulePrefix();
584 return array(
585 'prop' => array(
586 'Which sysinfo properties to get:',
587 ' general - Overall system information',
588 ' namespaces - List of registered namespaces and their canonical names',
589 ' namespacealiases - List of registered namespace aliases',
590 ' specialpagealiases - List of special page aliases',
591 ' magicwords - List of magic words and their aliases',
592 ' statistics - Returns site statistics',
593 " interwikimap - Returns interwiki map (optionally filtered, (optionally localised by using {$p}inlanguagecode))",
594 ' dbrepllag - Returns database server with the highest replication lag',
595 ' usergroups - Returns user groups and the associated permissions',
596 ' extensions - Returns extensions installed on the wiki',
597 ' fileextensions - Returns list of file extensions allowed to be uploaded',
598 ' rightsinfo - Returns wiki rights (license) information if available',
599 " languages - Returns a list of languages MediaWiki supports (optionally localised by using {$p}inlanguagecode)",
600 ' skins - Returns a list of all enabled skins',
601 ' extensiontags - Returns a list of parser extension tags',
602 ' functionhooks - Returns a list of parser function hooks',
603 ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)'
605 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
606 'showalldb' => 'List all database servers, not just the one lagging the most',
607 'numberingroup' => 'Lists the number of users in user groups',
608 'inlanguagecode' => 'Language code for localised language names (best effort, use CLDR extension)',
612 public function getDescription() {
613 return 'Return general information about the site';
616 public function getPossibleErrors() {
617 return array_merge( parent::getPossibleErrors(), array(
618 array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ),
619 ) );
622 public function getExamples() {
623 return array(
624 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics',
625 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local',
626 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=',
630 public function getHelpUrls() {
631 return 'https://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si';
634 public function getVersion() {
635 return __CLASS__ . ': $Id$';