* r109659: actually return the exact type we say we do
[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->isRTL() ) {
146 $data['rtl'] = '';
147 }
148 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
149
150 if ( wfReadOnly() ) {
151 $data['readonly'] = '';
152 $data['readonlyreason'] = wfReadOnlyReason();
153 }
154 if ( $GLOBALS['wgEnableWriteAPI'] ) {
155 $data['writeapi'] = '';
156 }
157
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;
165 }
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() );
175
176 if ( $GLOBALS['wgMiserMode'] ) {
177 $data['misermode'] = '';
178 }
179
180 wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
181
182 return $this->getResult()->addValue( 'query', $property, $data );
183 }
184
185 protected function appendNamespaces( $property ) {
186 global $wgContLang;
187 $data = array();
188 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
189 $data[$ns] = array(
190 'id' => intval( $ns ),
191 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
192 );
193 ApiResult::setContent( $data[$ns], $title );
194 $canonical = MWNamespace::getCanonicalName( $ns );
195
196 if ( MWNamespace::hasSubpages( $ns ) ) {
197 $data[$ns]['subpages'] = '';
198 }
199
200 if ( $canonical ) {
201 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
202 }
203
204 if ( MWNamespace::isContent( $ns ) ) {
205 $data[$ns]['content'] = '';
206 }
207 }
208
209 $this->getResult()->setIndexedTagName( $data, 'ns' );
210 return $this->getResult()->addValue( 'query', $property, $data );
211 }
212
213 protected function appendNamespaceAliases( $property ) {
214 global $wgNamespaceAliases, $wgContLang;
215 $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() );
216 $namespaces = $wgContLang->getNamespaces();
217 $data = array();
218 foreach ( $aliases as $title => $ns ) {
219 if ( $namespaces[$ns] == $title ) {
220 // Don't list duplicates
221 continue;
222 }
223 $item = array(
224 'id' => intval( $ns )
225 );
226 ApiResult::setContent( $item, strtr( $title, '_', ' ' ) );
227 $data[] = $item;
228 }
229
230 $this->getResult()->setIndexedTagName( $data, 'ns' );
231 return $this->getResult()->addValue( 'query', $property, $data );
232 }
233
234 protected function appendSpecialPageAliases( $property ) {
235 global $wgContLang;
236 $data = array();
237 foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) {
238 $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
239 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
240 $data[] = $arr;
241 }
242 $this->getResult()->setIndexedTagName( $data, 'specialpage' );
243 return $this->getResult()->addValue( 'query', $property, $data );
244 }
245
246 protected function appendMagicWords( $property ) {
247 global $wgContLang;
248 $data = array();
249 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
250 $caseSensitive = array_shift( $aliases );
251 $arr = array( 'name' => $magicword, 'aliases' => $aliases );
252 if ( $caseSensitive ) {
253 $arr['case-sensitive'] = '';
254 }
255 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
256 $data[] = $arr;
257 }
258 $this->getResult()->setIndexedTagName( $data, 'magicword' );
259 return $this->getResult()->addValue( 'query', $property, $data );
260 }
261
262 protected function appendInterwikiMap( $property, $filter ) {
263 $local = null;
264 if ( $filter === 'local' ) {
265 $local = 1;
266 } elseif ( $filter === '!local' ) {
267 $local = 0;
268 } elseif ( $filter ) {
269 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
270 }
271
272 $params = $this->extractRequestParams();
273 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
274
275 if( $langCode ) {
276 $langNames = Language::getTranslatedLanguageNames( $langCode );
277 } else {
278 $langNames = Language::getLanguageNames();
279 }
280
281 $getPrefixes = Interwiki::getAllPrefixes( $local );
282 $data = array();
283
284 foreach ( $getPrefixes as $row ) {
285 $prefix = $row['iw_prefix'];
286 $val = array();
287 $val['prefix'] = $prefix;
288 if ( $row['iw_local'] == '1' ) {
289 $val['local'] = '';
290 }
291 // $val['trans'] = intval( $row['iw_trans'] ); // should this be exposed?
292 if ( isset( $langNames[$prefix] ) ) {
293 $val['language'] = $langNames[$prefix];
294 }
295 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
296 if( isset( $row['iw_wikiid'] ) ) {
297 $val['wikiid'] = $row['iw_wikiid'];
298 }
299 if( isset( $row['iw_api'] ) ) {
300 $val['api'] = $row['iw_api'];
301 }
302
303 $data[] = $val;
304 }
305
306 $this->getResult()->setIndexedTagName( $data, 'iw' );
307 return $this->getResult()->addValue( 'query', $property, $data );
308 }
309
310 protected function appendDbReplLagInfo( $property, $includeAll ) {
311 global $wgShowHostnames;
312 $data = array();
313 $lb = wfGetLB();
314 if ( $includeAll ) {
315 if ( !$wgShowHostnames ) {
316 $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' );
317 }
318
319 $lags = $lb->getLagTimes();
320 foreach ( $lags as $i => $lag ) {
321 $data[] = array(
322 'host' => $lb->getServerName( $i ),
323 'lag' => $lag
324 );
325 }
326 } else {
327 list( $host, $lag, $index ) = $lb->getMaxLag();
328 $data[] = array(
329 'host' => $wgShowHostnames
330 ? $lb->getServerName( $index )
331 : '',
332 'lag' => intval( $lag )
333 );
334 }
335
336 $result = $this->getResult();
337 $result->setIndexedTagName( $data, 'db' );
338 return $this->getResult()->addValue( 'query', $property, $data );
339 }
340
341 protected function appendStatistics( $property ) {
342 global $wgDisableCounters;
343 $data = array();
344 $data['pages'] = intval( SiteStats::pages() );
345 $data['articles'] = intval( SiteStats::articles() );
346 if ( !$wgDisableCounters ) {
347 $data['views'] = intval( SiteStats::views() );
348 }
349 $data['edits'] = intval( SiteStats::edits() );
350 $data['images'] = intval( SiteStats::images() );
351 $data['users'] = intval( SiteStats::users() );
352 $data['activeusers'] = intval( SiteStats::activeUsers() );
353 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
354 $data['jobs'] = intval( SiteStats::jobs() );
355 return $this->getResult()->addValue( 'query', $property, $data );
356 }
357
358 protected function appendUserGroups( $property, $numberInGroup ) {
359 global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
360
361 $data = array();
362 $result = $this->getResult();
363 foreach ( $wgGroupPermissions as $group => $permissions ) {
364 $arr = array(
365 'name' => $group,
366 'rights' => array_keys( $permissions, true ),
367 );
368
369 if ( $numberInGroup ) {
370 global $wgAutopromote;
371
372 if ( $group == 'user' ) {
373 $arr['number'] = SiteStats::users();
374
375 // '*' and autopromote groups have no size
376 } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) {
377 $arr['number'] = SiteStats::numberInGroup( $group );
378 }
379 }
380
381 $groupArr = array(
382 'add' => $wgAddGroups,
383 'remove' => $wgRemoveGroups,
384 'add-self' => $wgGroupsAddToSelf,
385 'remove-self' => $wgGroupsRemoveFromSelf
386 );
387
388 foreach ( $groupArr as $type => $rights ) {
389 if ( isset( $rights[$group] ) ) {
390 $arr[$type] = $rights[$group];
391 $result->setIndexedTagName( $arr[$type], 'group' );
392 }
393 }
394
395 $result->setIndexedTagName( $arr['rights'], 'permission' );
396 $data[] = $arr;
397 }
398
399 $result->setIndexedTagName( $data, 'group' );
400 return $result->addValue( 'query', $property, $data );
401 }
402
403 protected function appendFileExtensions( $property ) {
404 global $wgFileExtensions;
405
406 $data = array();
407 foreach ( $wgFileExtensions as $ext ) {
408 $data[] = array( 'ext' => $ext );
409 }
410 $this->getResult()->setIndexedTagName( $data, 'fe' );
411 return $this->getResult()->addValue( 'query', $property, $data );
412 }
413
414 protected function appendExtensions( $property ) {
415 global $wgExtensionCredits;
416 $data = array();
417 foreach ( $wgExtensionCredits as $type => $extensions ) {
418 foreach ( $extensions as $ext ) {
419 $ret = array();
420 $ret['type'] = $type;
421 if ( isset( $ext['name'] ) ) {
422 $ret['name'] = $ext['name'];
423 }
424 if ( isset( $ext['description'] ) ) {
425 $ret['description'] = $ext['description'];
426 }
427 if ( isset( $ext['descriptionmsg'] ) ) {
428 // Can be a string or array( key, param1, param2, ... )
429 if ( is_array( $ext['descriptionmsg'] ) ) {
430 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
431 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
432 $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
433 } else {
434 $ret['descriptionmsg'] = $ext['descriptionmsg'];
435 }
436 }
437 if ( isset( $ext['author'] ) ) {
438 $ret['author'] = is_array( $ext['author'] ) ?
439 implode( ', ', $ext['author' ] ) : $ext['author'];
440 }
441 if ( isset( $ext['url'] ) ) {
442 $ret['url'] = $ext['url'];
443 }
444 if ( isset( $ext['version'] ) ) {
445 $ret['version'] = $ext['version'];
446 } elseif ( isset( $ext['svn-revision'] ) &&
447 preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
448 $ext['svn-revision'], $m ) )
449 {
450 $ret['version'] = 'r' . $m[1];
451 }
452 $data[] = $ret;
453 }
454 }
455
456 $this->getResult()->setIndexedTagName( $data, 'ext' );
457 return $this->getResult()->addValue( 'query', $property, $data );
458 }
459
460 protected function appendRightsInfo( $property ) {
461 global $wgRightsPage, $wgRightsUrl, $wgRightsText;
462 $title = Title::newFromText( $wgRightsPage );
463 $url = $title ? wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ) : $wgRightsUrl;
464 $text = $wgRightsText;
465 if ( !$text && $title ) {
466 $text = $title->getPrefixedText();
467 }
468
469 $data = array(
470 'url' => $url ? $url : '',
471 'text' => $text ? $text : ''
472 );
473
474 return $this->getResult()->addValue( 'query', $property, $data );
475 }
476
477 public function appendLanguages( $property ) {
478 $params = $this->extractRequestParams();
479 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
480
481 if( $langCode ) {
482 $langNames = Language::getTranslatedLanguageNames( $langCode );
483 } else {
484 $langNames = Language::getLanguageNames();
485 }
486
487 $data = array();
488
489 foreach ( $langNames as $code => $name ) {
490 $lang = array( 'code' => $code );
491 ApiResult::setContent( $lang, $name );
492 $data[] = $lang;
493 }
494 $this->getResult()->setIndexedTagName( $data, 'lang' );
495 return $this->getResult()->addValue( 'query', $property, $data );
496 }
497
498 public function appendSkins( $property ) {
499 $data = array();
500 foreach ( Skin::getSkinNames() as $name => $displayName ) {
501 $skin = array( 'code' => $name );
502 ApiResult::setContent( $skin, $displayName );
503 $data[] = $skin;
504 }
505 $this->getResult()->setIndexedTagName( $data, 'skin' );
506 return $this->getResult()->addValue( 'query', $property, $data );
507 }
508
509 public function appendExtensionTags( $property ) {
510 global $wgParser;
511 $wgParser->firstCallInit();
512 $tags = array_map( array( $this, 'formatParserTags'), $wgParser->getTags() );
513 $this->getResult()->setIndexedTagName( $tags, 't' );
514 return $this->getResult()->addValue( 'query', $property, $tags );
515 }
516
517 public function appendFunctionHooks( $property ) {
518 global $wgParser;
519 $wgParser->firstCallInit();
520 $hooks = $wgParser->getFunctionHooks();
521 $this->getResult()->setIndexedTagName( $hooks, 'h' );
522 return $this->getResult()->addValue( 'query', $property, $hooks );
523 }
524
525 private function formatParserTags( $item ) {
526 return "<{$item}>";
527 }
528
529 public function appendSubscribedHooks( $property ) {
530 global $wgHooks;
531 $myWgHooks = $wgHooks;
532 ksort( $myWgHooks );
533
534 $data = array();
535 foreach ( $myWgHooks as $hook => $hooks ) {
536 $arr = array(
537 'name' => $hook,
538 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $hooks ),
539 );
540
541 $this->getResult()->setIndexedTagName( $arr['subscribers'], 's' );
542 $data[] = $arr;
543 }
544
545 $this->getResult()->setIndexedTagName( $data, 'hook' );
546 return $this->getResult()->addValue( 'query', $property, $data );
547 }
548
549 public function getCacheMode( $params ) {
550 return 'public';
551 }
552
553 public function getAllowedParams() {
554 return array(
555 'prop' => array(
556 ApiBase::PARAM_DFLT => 'general',
557 ApiBase::PARAM_ISMULTI => true,
558 ApiBase::PARAM_TYPE => array(
559 'general',
560 'namespaces',
561 'namespacealiases',
562 'specialpagealiases',
563 'magicwords',
564 'interwikimap',
565 'dbrepllag',
566 'statistics',
567 'usergroups',
568 'extensions',
569 'fileextensions',
570 'rightsinfo',
571 'languages',
572 'skins',
573 'extensiontags',
574 'functionhooks',
575 'showhooks',
576 )
577 ),
578 'filteriw' => array(
579 ApiBase::PARAM_TYPE => array(
580 'local',
581 '!local',
582 )
583 ),
584 'showalldb' => false,
585 'numberingroup' => false,
586 'inlanguagecode' => null,
587 );
588 }
589
590 public function getParamDescription() {
591 $p = $this->getModulePrefix();
592 return array(
593 'prop' => array(
594 'Which sysinfo properties to get:',
595 ' general - Overall system information',
596 ' namespaces - List of registered namespaces and their canonical names',
597 ' namespacealiases - List of registered namespace aliases',
598 ' specialpagealiases - List of special page aliases',
599 ' magicwords - List of magic words and their aliases',
600 ' statistics - Returns site statistics',
601 " interwikimap - Returns interwiki map (optionally filtered, (optionally localised by using {$p}inlanguagecode))",
602 ' dbrepllag - Returns database server with the highest replication lag',
603 ' usergroups - Returns user groups and the associated permissions',
604 ' extensions - Returns extensions installed on the wiki',
605 ' fileextensions - Returns list of file extensions allowed to be uploaded',
606 ' rightsinfo - Returns wiki rights (license) information if available',
607 " languages - Returns a list of languages MediaWiki supports (optionally localised by using {$p}inlanguagecode)",
608 ' skins - Returns a list of all enabled skins',
609 ' extensiontags - Returns a list of parser extension tags',
610 ' functionhooks - Returns a list of parser function hooks',
611 ' showhooks - Returns a list of all subscribed hooks (contents of $wgHooks)'
612 ),
613 'filteriw' => 'Return only local or only nonlocal entries of the interwiki map',
614 'showalldb' => 'List all database servers, not just the one lagging the most',
615 'numberingroup' => 'Lists the number of users in user groups',
616 'inlanguagecode' => 'Language code for localised language names (best effort, use CLDR extension)',
617 );
618 }
619
620 public function getDescription() {
621 return 'Return general information about the site';
622 }
623
624 public function getPossibleErrors() {
625 return array_merge( parent::getPossibleErrors(), array(
626 array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ),
627 ) );
628 }
629
630 public function getExamples() {
631 return array(
632 'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics',
633 'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local',
634 'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=',
635 );
636 }
637
638 public function getHelpUrls() {
639 return 'https://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si';
640 }
641
642 public function getVersion() {
643 return __CLASS__ . ': $Id$';
644 }
645 }