Merge "Remove unused $wgDebugDBTransactions"
[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 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
181
182 wfRunHooks( 'APIQuerySiteInfoGeneralInfo', array( $this, &$data ) );
183
184 return $this->getResult()->addValue( 'query', $property, $data );
185 }
186
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',
194 );
195 ApiResult::setContent( $data[$ns], $title );
196 $canonical = MWNamespace::getCanonicalName( $ns );
197
198 if ( MWNamespace::hasSubpages( $ns ) ) {
199 $data[$ns]['subpages'] = '';
200 }
201
202 if ( $canonical ) {
203 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
204 }
205
206 if ( MWNamespace::isContent( $ns ) ) {
207 $data[$ns]['content'] = '';
208 }
209 }
210
211 $this->getResult()->setIndexedTagName( $data, 'ns' );
212 return $this->getResult()->addValue( 'query', $property, $data );
213 }
214
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;
224 }
225 $item = array(
226 'id' => intval( $ns )
227 );
228 ApiResult::setContent( $item, strtr( $title, '_', ' ' ) );
229 $data[] = $item;
230 }
231
232 $this->getResult()->setIndexedTagName( $data, 'ns' );
233 return $this->getResult()->addValue( 'query', $property, $data );
234 }
235
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;
243 }
244 $this->getResult()->setIndexedTagName( $data, 'specialpage' );
245 return $this->getResult()->addValue( 'query', $property, $data );
246 }
247
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'] = '';
256 }
257 $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
258 $data[] = $arr;
259 }
260 $this->getResult()->setIndexedTagName( $data, 'magicword' );
261 return $this->getResult()->addValue( 'query', $property, $data );
262 }
263
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" );
272 }
273
274 $params = $this->extractRequestParams();
275 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
276 $langNames = Language::fetchLanguageNames( $langCode );
277
278 $getPrefixes = Interwiki::getAllPrefixes( $local );
279 $data = array();
280
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'] = '';
287 }
288 // $val['trans'] = intval( $row['iw_trans'] ); // should this be exposed?
289 if ( isset( $langNames[$prefix] ) ) {
290 $val['language'] = $langNames[$prefix];
291 }
292 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
293 if( isset( $row['iw_wikiid'] ) ) {
294 $val['wikiid'] = $row['iw_wikiid'];
295 }
296 if( isset( $row['iw_api'] ) ) {
297 $val['api'] = $row['iw_api'];
298 }
299
300 $data[] = $val;
301 }
302
303 $this->getResult()->setIndexedTagName( $data, 'iw' );
304 return $this->getResult()->addValue( 'query', $property, $data );
305 }
306
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' );
314 }
315
316 $lags = $lb->getLagTimes();
317 foreach ( $lags as $i => $lag ) {
318 $data[] = array(
319 'host' => $lb->getServerName( $i ),
320 'lag' => $lag
321 );
322 }
323 } else {
324 list( $host, $lag, $index ) = $lb->getMaxLag();
325 $data[] = array(
326 'host' => $wgShowHostnames
327 ? $lb->getServerName( $index )
328 : '',
329 'lag' => intval( $lag )
330 );
331 }
332
333 $result = $this->getResult();
334 $result->setIndexedTagName( $data, 'db' );
335 return $this->getResult()->addValue( 'query', $property, $data );
336 }
337
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() );
345 }
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 );
353 }
354
355 protected function appendUserGroups( $property, $numberInGroup ) {
356 global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
357
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 ),
364 );
365
366 if ( $numberInGroup ) {
367 global $wgAutopromote;
368
369 if ( $group == 'user' ) {
370 $arr['number'] = SiteStats::users();
371
372 // '*' and autopromote groups have no size
373 } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) {
374 $arr['number'] = SiteStats::numberInGroup( $group );
375 }
376 }
377
378 $groupArr = array(
379 'add' => $wgAddGroups,
380 'remove' => $wgRemoveGroups,
381 'add-self' => $wgGroupsAddToSelf,
382 'remove-self' => $wgGroupsRemoveFromSelf
383 );
384
385 foreach ( $groupArr as $type => $rights ) {
386 if ( isset( $rights[$group] ) ) {
387 $arr[$type] = $rights[$group];
388 $result->setIndexedTagName( $arr[$type], 'group' );
389 }
390 }
391
392 $result->setIndexedTagName( $arr['rights'], 'permission' );
393 $data[] = $arr;
394 }
395
396 $result->setIndexedTagName( $data, 'group' );
397 return $result->addValue( 'query', $property, $data );
398 }
399
400 protected function appendFileExtensions( $property ) {
401 global $wgFileExtensions;
402
403 $data = array();
404 foreach ( $wgFileExtensions as $ext ) {
405 $data[] = array( 'ext' => $ext );
406 }
407 $this->getResult()->setIndexedTagName( $data, 'fe' );
408 return $this->getResult()->addValue( 'query', $property, $data );
409 }
410
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'];
420 }
421 if ( isset( $ext['description'] ) ) {
422 $ret['description'] = $ext['description'];
423 }
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'];
432 }
433 }
434 if ( isset( $ext['author'] ) ) {
435 $ret['author'] = is_array( $ext['author'] ) ?
436 implode( ', ', $ext['author' ] ) : $ext['author'];
437 }
438 if ( isset( $ext['url'] ) ) {
439 $ret['url'] = $ext['url'];
440 }
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 ) )
446 {
447 $ret['version'] = 'r' . $m[1];
448 }
449 $data[] = $ret;
450 }
451 }
452
453 $this->getResult()->setIndexedTagName( $data, 'ext' );
454 return $this->getResult()->addValue( 'query', $property, $data );
455 }
456
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();
464 }
465
466 $data = array(
467 'url' => $url ? $url : '',
468 'text' => $text ? $text : ''
469 );
470
471 return $this->getResult()->addValue( 'query', $property, $data );
472 }
473
474 public function appendLanguages( $property ) {
475 $params = $this->extractRequestParams();
476 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
477 $langNames = Language::fetchLanguageNames( $langCode );
478
479 $data = array();
480
481 foreach ( $langNames as $code => $name ) {
482 $lang = array( 'code' => $code );
483 ApiResult::setContent( $lang, $name );
484 $data[] = $lang;
485 }
486 $this->getResult()->setIndexedTagName( $data, 'lang' );
487 return $this->getResult()->addValue( 'query', $property, $data );
488 }
489
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;
496 }
497 $this->getResult()->setIndexedTagName( $data, 'skin' );
498 return $this->getResult()->addValue( 'query', $property, $data );
499 }
500
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 );
507 }
508
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 );
515 }
516
517 private function formatParserTags( $item ) {
518 return "<{$item}>";
519 }
520
521 public function appendSubscribedHooks( $property ) {
522 global $wgHooks;
523 $myWgHooks = $wgHooks;
524 ksort( $myWgHooks );
525
526 $data = array();
527 foreach ( $myWgHooks as $hook => $hooks ) {
528 $arr = array(
529 'name' => $hook,
530 'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $hooks ),
531 );
532
533 $this->getResult()->setIndexedTagName( $arr['subscribers'], 's' );
534 $data[] = $arr;
535 }
536
537 $this->getResult()->setIndexedTagName( $data, 'hook' );
538 return $this->getResult()->addValue( 'query', $property, $data );
539 }
540
541 public function getCacheMode( $params ) {
542 return 'public';
543 }
544
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',
568 )
569 ),
570 'filteriw' => array(
571 ApiBase::PARAM_TYPE => array(
572 'local',
573 '!local',
574 )
575 ),
576 'showalldb' => false,
577 'numberingroup' => false,
578 'inlanguagecode' => null,
579 );
580 }
581
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)'
604 ),
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)',
609 );
610 }
611
612 public function getDescription() {
613 return 'Return general information about the site';
614 }
615
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 ) );
620 }
621
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=',
627 );
628 }
629
630 public function getHelpUrls() {
631 return 'https://www.mediawiki.org/wiki/API:Meta#siteinfo_.2F_si';
632 }
633
634 public function getVersion() {
635 return __CLASS__ . ': $Id$';
636 }
637 }