Removed raw Article->field accessing
[mediawiki.git] / includes / api / ApiQuerySiteinfo.php
blobb25b83ce1ce627d352ace71fc218bb7641b9e646
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 if ( !defined( 'MEDIAWIKI' ) ) {
28 // Eclipse helper - will be ignored in production
29 require_once( 'ApiQueryBase.php' );
32 /**
33 * A query action to return meta information about the wiki site.
35 * @ingroup API
37 class ApiQuerySiteinfo extends ApiQueryBase {
39 public function __construct( $query, $moduleName ) {
40 parent::__construct( $query, $moduleName, 'si' );
43 public function execute() {
44 $params = $this->extractRequestParams();
45 $done = array();
46 foreach ( $params['prop'] as $p ) {
47 switch ( $p ) {
48 case 'general':
49 $fit = $this->appendGeneralInfo( $p );
50 break;
51 case 'namespaces':
52 $fit = $this->appendNamespaces( $p );
53 break;
54 case 'namespacealiases':
55 $fit = $this->appendNamespaceAliases( $p );
56 break;
57 case 'specialpagealiases':
58 $fit = $this->appendSpecialPageAliases( $p );
59 break;
60 case 'magicwords':
61 $fit = $this->appendMagicWords( $p );
62 break;
63 case 'interwikimap':
64 $filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
65 $fit = $this->appendInterwikiMap( $p, $filteriw );
66 break;
67 case 'dbrepllag':
68 $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
69 break;
70 case 'statistics':
71 $fit = $this->appendStatistics( $p );
72 break;
73 case 'usergroups':
74 $fit = $this->appendUserGroups( $p, $params['numberingroup'] );
75 break;
76 case 'extensions':
77 $fit = $this->appendExtensions( $p );
78 break;
79 case 'fileextensions':
80 $fit = $this->appendFileExtensions( $p );
81 break;
82 case 'rightsinfo':
83 $fit = $this->appendRightsInfo( $p );
84 break;
85 case 'languages':
86 $fit = $this->appendLanguages( $p );
87 break;
88 case 'skins':
89 $fit = $this->appendSkins( $p );
90 break;
91 case 'extensiontags':
92 $fit = $this->appendExtensionTags( $p );
93 break;
94 case 'functionhooks':
95 $fit = $this->appendFunctionHooks( $p );
96 break;
97 case 'showhooks':
98 $fit = $this->appendSubscribedHooks( $p );
99 break;
100 default:
101 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
103 if ( !$fit ) {
104 // Abuse siprop as a query-continue parameter
105 // and set it to all unprocessed props
106 $this->setContinueEnumParameter( 'prop', implode( '|',
107 array_diff( $params['prop'], $done ) ) );
108 break;
110 $done[] = $p;
114 protected function appendGeneralInfo( $property ) {
115 global $wgContLang;
117 $data = array();
118 $mainPage = Title::newMainPage();
119 $data['mainpage'] = $mainPage->getPrefixedText();
120 $data['base'] = $mainPage->getFullUrl();
121 $data['sitename'] = $GLOBALS['wgSitename'];
122 $data['generator'] = "MediaWiki {$GLOBALS['wgVersion']}";
123 $data['phpversion'] = phpversion();
124 $data['phpsapi'] = php_sapi_name();
125 $data['dbtype'] = $GLOBALS['wgDBtype'];
126 $data['dbversion'] = $this->getDB()->getServerVersion();
128 $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] );
129 if ( $svn ) {
130 $data['rev'] = $svn;
133 // 'case-insensitive' option is reserved for future
134 $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive';
136 if ( isset( $GLOBALS['wgRightsCode'] ) ) {
137 $data['rightscode'] = $GLOBALS['wgRightsCode'];
139 $data['rights'] = $GLOBALS['wgRightsText'];
140 $data['lang'] = $GLOBALS['wgLanguageCode'];
141 if ( $wgContLang->isRTL() ) {
142 $data['rtl'] = '';
144 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
146 if ( wfReadOnly() ) {
147 $data['readonly'] = '';
148 $data['readonlyreason'] = wfReadOnlyReason();
150 if ( $GLOBALS['wgEnableWriteAPI'] ) {
151 $data['writeapi'] = '';
154 $tz = $GLOBALS['wgLocaltimezone'];
155 $offset = $GLOBALS['wgLocalTZoffset'];
156 if ( is_null( $tz ) ) {
157 $tz = 'UTC';
158 $offset = 0;
159 } elseif ( is_null( $offset ) ) {
160 $offset = 0;
162 $data['timezone'] = $tz;
163 $data['timeoffset'] = intval( $offset );
164 $data['articlepath'] = $GLOBALS['wgArticlePath'];
165 $data['scriptpath'] = $GLOBALS['wgScriptPath'];
166 $data['script'] = $GLOBALS['wgScript'];
167 $data['variantarticlepath'] = $GLOBALS['wgVariantArticlePath'];
168 $data['server'] = $GLOBALS['wgServer'];
169 $data['wikiid'] = wfWikiID();
170 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
172 if ( $GLOBALS['wgMiserMode'] ) {
173 $data['misermode'] = '';
176 wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
178 return $this->getResult()->addValue( 'query', $property, $data );
181 protected function appendNamespaces( $property ) {
182 global $wgContLang;
183 $data = array();
184 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
185 $data[$ns] = array(
186 'id' => intval( $ns ),
187 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
189 ApiResult::setContent( $data[$ns], $title );
190 $canonical = MWNamespace::getCanonicalName( $ns );
192 if ( MWNamespace::hasSubpages( $ns ) ) {
193 $data[$ns]['subpages'] = '';
196 if ( $canonical ) {
197 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
200 if ( MWNamespace::isContent( $ns ) ) {
201 $data[$ns]['content'] = '';
205 $this->getResult()->setIndexedTagName( $data, 'ns' );
206 return $this->getResult()->addValue( 'query', $property, $data );
209 protected function appendNamespaceAliases( $property ) {
210 global $wgNamespaceAliases, $wgContLang;
211 $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() );
212 $namespaces = $wgContLang->getNamespaces();
213 $data = array();
214 foreach ( $aliases as $title => $ns ) {
215 if ( $namespaces[$ns] == $title ) {
216 // Don't list duplicates
217 continue;
219 $item = array(
220 'id' => intval( $ns )
222 ApiResult::setContent( $item, strtr( $title, '_', ' ' ) );
223 $data[] = $item;
226 $this->getResult()->setIndexedTagName( $data, 'ns' );
227 return $this->getResult()->addValue( 'query', $property, $data );
230 protected function appendSpecialPageAliases( $property ) {
231 global $wgContLang;
232 $data = array();
233 foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) {
234 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
235 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
236 $data[] = $arr;
238 $this->getResult()->setIndexedTagName( $data, 'specialpage' );
239 return $this->getResult()->addValue( 'query', $property, $data );
242 protected function appendMagicWords( $property ) {
243 global $wgContLang;
244 $data = array();
245 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
246 $caseSensitive = array_shift( $aliases );
247 $arr = array( 'name' => $magicword, 'aliases' => $aliases );
248 if ( $caseSensitive ) {
249 $arr['case-sensitive'] = '';
251 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
252 $data[] = $arr;
254 $this->getResult()->setIndexedTagName( $data, 'magicword' );
255 return $this->getResult()->addValue( 'query', $property, $data );
258 protected function appendInterwikiMap( $property, $filter ) {
259 $this->resetQueryParams();
260 $this->addTables( 'interwiki' );
261 $this->addFields( array( 'iw_prefix', 'iw_local', 'iw_url', 'iw_wikiid', 'iw_api' ) );
263 if ( $filter === 'local' ) {
264 $this->addWhere( 'iw_local = 1' );
265 } elseif ( $filter === '!local' ) {
266 $this->addWhere( 'iw_local = 0' );
267 } elseif ( $filter ) {
268 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
271 $this->addOption( 'ORDER BY', 'iw_prefix' );
273 $res = $this->select( __METHOD__ );
275 $data = array();
276 $langNames = Language::getLanguageNames();
277 foreach ( $res as $row ) {
278 $val = array();
279 $val['prefix'] = $row->iw_prefix;
280 if ( $row->iw_local == '1' ) {
281 $val['local'] = '';
283 // $val['trans'] = intval( $row->iw_trans ); // should this be exposed?
284 if ( isset( $langNames[$row->iw_prefix] ) ) {
285 $val['language'] = $langNames[$row->iw_prefix];
287 $val['url'] = $row->iw_url;
288 $val['wikiid'] = $row->iw_wikiid;
289 $val['api'] = $row->iw_api;
291 $data[] = $val;
294 $this->getResult()->setIndexedTagName( $data, 'iw' );
295 return $this->getResult()->addValue( 'query', $property, $data );
298 protected function appendDbReplLagInfo( $property, $includeAll ) {
299 global $wgShowHostnames;
300 $data = array();
301 $lb = wfGetLB();
302 if ( $includeAll ) {
303 if ( !$wgShowHostnames ) {
304 $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' );
307 $lags = $lb->getLagTimes();
308 foreach ( $lags as $i => $lag ) {
309 $data[] = array(
310 'host' => $lb->getServerName( $i ),
311 'lag' => $lag
314 } else {
315 list( $host, $lag, $index ) = $lb->getMaxLag();
316 $data[] = array(
317 'host' => $wgShowHostnames
318 ? $lb->getServerName( $index )
319 : '',
320 'lag' => intval( $lag )
324 $result = $this->getResult();
325 $result->setIndexedTagName( $data, 'db' );
326 return $this->getResult()->addValue( 'query', $property, $data );
329 protected function appendStatistics( $property ) {
330 global $wgDisableCounters;
331 $data = array();
332 $data['pages'] = intval( SiteStats::pages() );
333 $data['articles'] = intval( SiteStats::articles() );
334 if ( !$wgDisableCounters ) {
335 $data['views'] = intval( SiteStats::views() );
337 $data['edits'] = intval( SiteStats::edits() );
338 $data['images'] = intval( SiteStats::images() );
339 $data['users'] = intval( SiteStats::users() );
340 $data['activeusers'] = intval( SiteStats::activeUsers() );
341 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
342 $data['jobs'] = intval( SiteStats::jobs() );
343 return $this->getResult()->addValue( 'query', $property, $data );
346 protected function appendUserGroups( $property, $numberInGroup ) {
347 global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
349 $data = array();
350 foreach ( $wgGroupPermissions as $group => $permissions ) {
351 $arr = array(
352 'name' => $group,
353 'rights' => array_keys( $permissions, true ),
356 if ( $numberInGroup ) {
357 global $wgAutopromote;
359 if ( $group == 'user' ) {
360 $arr['number'] = SiteStats::users();
362 // '*' and autopromote groups have no size
363 } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) {
364 $arr['number'] = SiteStats::numberInGroup( $group );
368 $groupArr = array(
369 'add' => $wgAddGroups,
370 'remove' => $wgRemoveGroups,
371 'add-self' => $wgGroupsAddToSelf,
372 'remove-self' => $wgGroupsRemoveFromSelf
375 foreach ( $groupArr as $type => $rights ) {
376 if ( isset( $rights[$group] ) ) {
377 $arr[$type] = $rights[$group];
378 $this->getResult()->setIndexedTagName( $arr[$type], 'group' );
382 $this->getResult()->setIndexedTagName( $arr['rights'], 'permission' );
383 $data[] = $arr;
386 $this->getResult()->setIndexedTagName( $data, 'group' );
387 return $this->getResult()->addValue( 'query', $property, $data );
390 protected function appendFileExtensions( $property ) {
391 global $wgFileExtensions;
393 $data = array();
394 foreach ( $wgFileExtensions as $ext ) {
395 $data[] = array( 'ext' => $ext );
397 $this->getResult()->setIndexedTagName( $data, 'fe' );
398 return $this->getResult()->addValue( 'query', $property, $data );
401 protected function appendExtensions( $property ) {
402 global $wgExtensionCredits;
403 $data = array();
404 foreach ( $wgExtensionCredits as $type => $extensions ) {
405 foreach ( $extensions as $ext ) {
406 $ret = array();
407 $ret['type'] = $type;
408 if ( isset( $ext['name'] ) ) {
409 $ret['name'] = $ext['name'];
411 if ( isset( $ext['description'] ) ) {
412 $ret['description'] = $ext['description'];
414 if ( isset( $ext['descriptionmsg'] ) ) {
415 // Can be a string or array( key, param1, param2, ... )
416 if ( is_array( $ext['descriptionmsg'] ) ) {
417 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
418 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
419 $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
420 } else {
421 $ret['descriptionmsg'] = $ext['descriptionmsg'];
424 if ( isset( $ext['author'] ) ) {
425 $ret['author'] = is_array( $ext['author'] ) ?
426 implode( ', ', $ext['author' ] ) : $ext['author'];
428 if ( isset( $ext['url'] ) ) {
429 $ret['url'] = $ext['url'];
431 if ( isset( $ext['version'] ) ) {
432 $ret['version'] = $ext['version'];
433 } elseif ( isset( $ext['svn-revision'] ) &&
434 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
435 $ext['svn-revision'], $m ) )
437 $ret['version'] = 'r' . $m[1];
439 $data[] = $ret;
443 $this->getResult()->setIndexedTagName( $data, 'ext' );
444 return $this->getResult()->addValue( 'query', $property, $data );
447 protected function appendRightsInfo( $property ) {
448 global $wgRightsPage, $wgRightsUrl, $wgRightsText;
449 $title = Title::newFromText( $wgRightsPage );
450 $url = $title ? $title->getFullURL() : $wgRightsUrl;
451 $text = $wgRightsText;
452 if ( !$text && $title ) {
453 $text = $title->getPrefixedText();
456 $data = array(
457 'url' => $url ? $url : '',
458 'text' => $text ? $text : ''
461 return $this->getResult()->addValue( 'query', $property, $data );
464 public function appendLanguages( $property ) {
465 $data = array();
466 foreach ( Language::getLanguageNames() as $code => $name ) {
467 $lang = array( 'code' => $code );
468 ApiResult::setContent( $lang, $name );
469 $data[] = $lang;
471 $this->getResult()->setIndexedTagName( $data, 'lang' );
472 return $this->getResult()->addValue( 'query', $property, $data );
475 public function appendSkins( $property ) {
476 $data = array();
477 foreach ( Skin::getSkinNames() as $name => $displayName ) {
478 $skin = array( 'code' => $name );
479 ApiResult::setContent( $skin, $displayName );
480 $data[] = $skin;
482 $this->getResult()->setIndexedTagName( $data, 'skin' );
483 return $this->getResult()->addValue( 'query', $property, $data );
486 public function appendExtensionTags( $property ) {
487 global $wgParser;
488 $wgParser->firstCallInit();
489 $tags = array_map( array( $this, 'formatParserTags'), $wgParser->getTags() );
490 $this->getResult()->setIndexedTagName( $tags, 't' );
491 return $this->getResult()->addValue( 'query', $property, $tags );
494 public function appendFunctionHooks( $property ) {
495 global $wgParser;
496 $wgParser->firstCallInit();
497 $hooks = $wgParser->getFunctionHooks();
498 $this->getResult()->setIndexedTagName( $hooks, 'h' );
499 return $this->getResult()->addValue( 'query', $property, $hooks );
502 private function formatParserTags( $item ) {
503 return "<{$item}>";
506 public function appendSubscribedHooks( $property ) {
507 global $wgHooks;
508 $myWgHooks = $wgHooks;
509 ksort( $myWgHooks );
511 $data = array();
512 foreach ( $myWgHooks as $hook => $hooks ) {
513 $arr = array(
514 'name' => $hook,
515 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $hooks ),
518 $this->getResult()->setIndexedTagName( $arr['subscribers'], 's' );
519 $data[] = $arr;
522 $this->getResult()->setIndexedTagName( $data, 'hook' );
523 return $this->getResult()->addValue( 'query', $property, $data );
526 public function getCacheMode( $params ) {
527 return 'public';
530 public function getAllowedParams() {
531 return array(
532 'prop' => array(
533 ApiBase::PARAM_DFLT => 'general',
534 ApiBase::PARAM_ISMULTI => true,
535 ApiBase::PARAM_TYPE => array(
536 'general',
537 'namespaces',
538 'namespacealiases',
539 'specialpagealiases',
540 'magicwords',
541 'interwikimap',
542 'dbrepllag',
543 'statistics',
544 'usergroups',
545 'extensions',
546 'fileextensions',
547 'rightsinfo',
548 'languages',
549 'skins',
550 'extensiontags',
551 'functionhooks',
552 'showhooks',
555 'filteriw' => array(
556 ApiBase::PARAM_TYPE => array(
557 'local',
558 '!local',
561 'showalldb' => false,
562 'numberingroup' => false,
566 public function getParamDescription() {
567 return array(
568 'prop' => array(
569 'Which sysinfo properties to get:',
570 ' general - Overall system information',
571 ' namespaces - List of registered namespaces and their canonical names',
572 ' namespacealiases - List of registered namespace aliases',
573 ' specialpagealiases - List of special page aliases',
574 ' magicwords - List of magic words and their aliases',
575 ' statistics - Returns site statistics',
576 ' interwikimap - Returns interwiki map (optionally filtered)',
577 ' dbrepllag - Returns database server with the highest replication lag',
578 ' usergroups - Returns user groups and the associated permissions',
579 ' extensions - Returns extensions installed on the wiki',
580 ' fileextensions - Returns list of file extensions allowed to be uploaded',
581 ' rightsinfo - Returns wiki rights (license) information if available',
582 ' languages - Returns a list of languages MediaWiki supports',
583 ' skins - Returns a list of all enabled skins',
584 ' extensiontags - Returns a list of parser extension tags',
585 ' functionhooks - Returns a list of parser function hooks',
586 ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)'
588 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
589 'showalldb' => 'List all database servers, not just the one lagging the most',
590 'numberingroup' => 'Lists the number of users in user groups',
594 public function getDescription() {
595 return 'Return general information about the site';
598 public function getPossibleErrors() {
599 return array_merge( parent::getPossibleErrors(), array(
600 array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ),
601 ) );
604 protected function getExamples() {
605 return array(
606 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics',
607 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local',
608 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=',
612 public function getVersion() {
613 return __CLASS__ . ': $Id$';