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