User::isSafeToLoad() should return false if MW_NO_SESSION
[mediawiki.git] / includes / api / ApiQuerySiteinfo.php
blob4befad66c72c885b64fbc82d6914c84485e033b8
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( ApiQuery $query, $moduleName ) {
35 parent::__construct( $query, $moduleName, 'si' );
38 public function execute() {
39 $params = $this->extractRequestParams();
40 $done = [];
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 'libraries':
73 $fit = $this->appendInstalledLibraries( $p );
74 break;
75 case 'extensions':
76 $fit = $this->appendExtensions( $p );
77 break;
78 case 'fileextensions':
79 $fit = $this->appendFileExtensions( $p );
80 break;
81 case 'rightsinfo':
82 $fit = $this->appendRightsInfo( $p );
83 break;
84 case 'restrictions':
85 $fit = $this->appendRestrictions( $p );
86 break;
87 case 'languages':
88 $fit = $this->appendLanguages( $p );
89 break;
90 case 'skins':
91 $fit = $this->appendSkins( $p );
92 break;
93 case 'extensiontags':
94 $fit = $this->appendExtensionTags( $p );
95 break;
96 case 'functionhooks':
97 $fit = $this->appendFunctionHooks( $p );
98 break;
99 case 'showhooks':
100 $fit = $this->appendSubscribedHooks( $p );
101 break;
102 case 'variables':
103 $fit = $this->appendVariables( $p );
104 break;
105 case 'protocols':
106 $fit = $this->appendProtocols( $p );
107 break;
108 case 'defaultoptions':
109 $fit = $this->appendDefaultOptions( $p );
110 break;
111 default:
112 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
114 if ( !$fit ) {
115 // Abuse siprop as a query-continue parameter
116 // and set it to all unprocessed props
117 $this->setContinueEnumParameter( 'prop', implode( '|',
118 array_diff( $params['prop'], $done ) ) );
119 break;
121 $done[] = $p;
125 protected function appendGeneralInfo( $property ) {
126 global $wgContLang;
128 $config = $this->getConfig();
130 $data = [];
131 $mainPage = Title::newMainPage();
132 $data['mainpage'] = $mainPage->getPrefixedText();
133 $data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
134 $data['sitename'] = $config->get( 'Sitename' );
136 // wgLogo can either be a relative or an absolute path
137 // make sure we always return an absolute path
138 $data['logo'] = wfExpandUrl( $config->get( 'Logo' ), PROTO_RELATIVE );
140 $data['generator'] = "MediaWiki {$config->get( 'Version' )}";
142 $data['phpversion'] = PHP_VERSION;
143 $data['phpsapi'] = PHP_SAPI;
144 if ( defined( 'HHVM_VERSION' ) ) {
145 $data['hhvmversion'] = HHVM_VERSION;
147 $data['dbtype'] = $config->get( 'DBtype' );
148 $data['dbversion'] = $this->getDB()->getServerVersion();
150 $allowFrom = [ '' ];
151 $allowException = true;
152 if ( !$config->get( 'AllowExternalImages' ) ) {
153 $data['imagewhitelistenabled'] = (bool)$config->get( 'EnableImageWhitelist' );
154 $allowFrom = $config->get( 'AllowExternalImagesFrom' );
155 $allowException = !empty( $allowFrom );
157 if ( $allowException ) {
158 $data['externalimages'] = (array)$allowFrom;
159 ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
162 $data['langconversion'] = !$config->get( 'DisableLangConversion' );
163 $data['titleconversion'] = !$config->get( 'DisableTitleConversion' );
165 if ( $wgContLang->linkPrefixExtension() ) {
166 $linkPrefixCharset = $wgContLang->linkPrefixCharset();
167 $data['linkprefixcharset'] = $linkPrefixCharset;
168 // For backwards compatibility
169 $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
170 } else {
171 $data['linkprefixcharset'] = '';
172 $data['linkprefix'] = '';
175 $linktrail = $wgContLang->linkTrail();
176 $data['linktrail'] = $linktrail ?: '';
178 $data['legaltitlechars'] = Title::legalChars();
179 $data['invalidusernamechars'] = $config->get( 'InvalidUsernameCharacters' );
181 global $IP;
182 $git = SpecialVersion::getGitHeadSha1( $IP );
183 if ( $git ) {
184 $data['git-hash'] = $git;
185 $data['git-branch'] =
186 SpecialVersion::getGitCurrentBranch( $GLOBALS['IP'] );
189 // 'case-insensitive' option is reserved for future
190 $data['case'] = $config->get( 'CapitalLinks' ) ? 'first-letter' : 'case-sensitive';
191 $data['lang'] = $config->get( 'LanguageCode' );
193 $fallbacks = [];
194 foreach ( $wgContLang->getFallbackLanguages() as $code ) {
195 $fallbacks[] = [ 'code' => $code ];
197 $data['fallback'] = $fallbacks;
198 ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
200 if ( $wgContLang->hasVariants() ) {
201 $variants = [];
202 foreach ( $wgContLang->getVariants() as $code ) {
203 $variants[] = [
204 'code' => $code,
205 'name' => $wgContLang->getVariantname( $code ),
208 $data['variants'] = $variants;
209 ApiResult::setIndexedTagName( $data['variants'], 'lang' );
212 $data['rtl'] = $wgContLang->isRTL();
213 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
215 $data['readonly'] = wfReadOnly();
216 if ( $data['readonly'] ) {
217 $data['readonlyreason'] = wfReadOnlyReason();
219 $data['writeapi'] = (bool)$config->get( 'EnableWriteAPI' );
221 $tz = $config->get( 'Localtimezone' );
222 $offset = $config->get( 'LocalTZoffset' );
223 if ( is_null( $tz ) ) {
224 $tz = 'UTC';
225 $offset = 0;
226 } elseif ( is_null( $offset ) ) {
227 $offset = 0;
229 $data['timezone'] = $tz;
230 $data['timeoffset'] = intval( $offset );
231 $data['articlepath'] = $config->get( 'ArticlePath' );
232 $data['scriptpath'] = $config->get( 'ScriptPath' );
233 $data['script'] = $config->get( 'Script' );
234 $data['variantarticlepath'] = $config->get( 'VariantArticlePath' );
235 $data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
236 $data['server'] = $config->get( 'Server' );
237 $data['servername'] = $config->get( 'ServerName' );
238 $data['wikiid'] = wfWikiID();
239 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
241 $data['misermode'] = (bool)$config->get( 'MiserMode' );
243 $data['uploadsenabled'] = UploadBase::isEnabled();
244 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
245 $data['minuploadchunksize'] = (int)$this->getConfig()->get( 'MinUploadChunkSize' );
247 $data['thumblimits'] = $config->get( 'ThumbLimits' );
248 ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
249 ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
250 $data['imagelimits'] = [];
251 ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
252 ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
253 foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
254 $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
257 $favicon = $config->get( 'Favicon' );
258 if ( !empty( $favicon ) ) {
259 // wgFavicon can either be a relative or an absolute path
260 // make sure we always return an absolute path
261 $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
264 $data['centralidlookupprovider'] = $this->getConfig()->get( 'CentralIdLookupProvider' );
265 $providerIds = array_keys( $this->getConfig()->get( 'CentralIdLookupProviders' ) );
266 $data['allcentralidlookupproviders'] = $providerIds;
268 Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
270 return $this->getResult()->addValue( 'query', $property, $data );
273 protected function appendNamespaces( $property ) {
274 global $wgContLang;
275 $data = [
276 ApiResult::META_TYPE => 'assoc',
278 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
279 $data[$ns] = [
280 'id' => intval( $ns ),
281 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
283 ApiResult::setContentValue( $data[$ns], 'name', $title );
284 $canonical = MWNamespace::getCanonicalName( $ns );
286 $data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
288 if ( $canonical ) {
289 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
292 $data[$ns]['content'] = MWNamespace::isContent( $ns );
293 $data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
295 $contentmodel = MWNamespace::getNamespaceContentModel( $ns );
296 if ( $contentmodel ) {
297 $data[$ns]['defaultcontentmodel'] = $contentmodel;
301 ApiResult::setArrayType( $data, 'assoc' );
302 ApiResult::setIndexedTagName( $data, 'ns' );
304 return $this->getResult()->addValue( 'query', $property, $data );
307 protected function appendNamespaceAliases( $property ) {
308 global $wgContLang;
309 $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
310 $wgContLang->getNamespaceAliases() );
311 $namespaces = $wgContLang->getNamespaces();
312 $data = [];
313 foreach ( $aliases as $title => $ns ) {
314 if ( $namespaces[$ns] == $title ) {
315 // Don't list duplicates
316 continue;
318 $item = [
319 'id' => intval( $ns )
321 ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
322 $data[] = $item;
325 sort( $data );
327 ApiResult::setIndexedTagName( $data, 'ns' );
329 return $this->getResult()->addValue( 'query', $property, $data );
332 protected function appendSpecialPageAliases( $property ) {
333 global $wgContLang;
334 $data = [];
335 $aliases = $wgContLang->getSpecialPageAliases();
336 foreach ( SpecialPageFactory::getNames() as $specialpage ) {
337 if ( isset( $aliases[$specialpage] ) ) {
338 $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
339 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
340 $data[] = $arr;
343 ApiResult::setIndexedTagName( $data, 'specialpage' );
345 return $this->getResult()->addValue( 'query', $property, $data );
348 protected function appendMagicWords( $property ) {
349 global $wgContLang;
350 $data = [];
351 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
352 $caseSensitive = array_shift( $aliases );
353 $arr = [ 'name' => $magicword, 'aliases' => $aliases ];
354 $arr['case-sensitive'] = (bool)$caseSensitive;
355 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
356 $data[] = $arr;
358 ApiResult::setIndexedTagName( $data, 'magicword' );
360 return $this->getResult()->addValue( 'query', $property, $data );
363 protected function appendInterwikiMap( $property, $filter ) {
364 $local = null;
365 if ( $filter === 'local' ) {
366 $local = 1;
367 } elseif ( $filter === '!local' ) {
368 $local = 0;
369 } elseif ( $filter ) {
370 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
373 $params = $this->extractRequestParams();
374 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
375 $langNames = Language::fetchLanguageNames( $langCode );
377 $getPrefixes = Interwiki::getAllPrefixes( $local );
378 $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
379 $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
380 $data = [];
382 foreach ( $getPrefixes as $row ) {
383 $prefix = $row['iw_prefix'];
384 $val = [];
385 $val['prefix'] = $prefix;
386 if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
387 $val['local'] = true;
389 if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
390 $val['trans'] = true;
393 if ( isset( $langNames[$prefix] ) ) {
394 $val['language'] = $langNames[$prefix];
396 if ( in_array( $prefix, $localInterwikis ) ) {
397 $val['localinterwiki'] = true;
399 if ( in_array( $prefix, $extraLangPrefixes ) ) {
400 $val['extralanglink'] = true;
402 $linktext = wfMessage( "interlanguage-link-$prefix" );
403 if ( !$linktext->isDisabled() ) {
404 $val['linktext'] = $linktext->text();
407 $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
408 if ( !$sitename->isDisabled() ) {
409 $val['sitename'] = $sitename->text();
413 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
414 $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
415 if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
416 $val['wikiid'] = $row['iw_wikiid'];
418 if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
419 $val['api'] = $row['iw_api'];
422 $data[] = $val;
425 ApiResult::setIndexedTagName( $data, 'iw' );
427 return $this->getResult()->addValue( 'query', $property, $data );
430 protected function appendDbReplLagInfo( $property, $includeAll ) {
431 $data = [];
432 $lb = wfGetLB();
433 $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
434 if ( $includeAll ) {
435 if ( !$showHostnames ) {
436 $this->dieUsage(
437 'Cannot view all servers info unless $wgShowHostnames is true',
438 'includeAllDenied'
442 $lags = $lb->getLagTimes();
443 foreach ( $lags as $i => $lag ) {
444 $data[] = [
445 'host' => $lb->getServerName( $i ),
446 'lag' => $lag
449 } else {
450 list( , $lag, $index ) = $lb->getMaxLag();
451 $data[] = [
452 'host' => $showHostnames
453 ? $lb->getServerName( $index )
454 : '',
455 'lag' => intval( $lag )
459 ApiResult::setIndexedTagName( $data, 'db' );
461 return $this->getResult()->addValue( 'query', $property, $data );
464 protected function appendStatistics( $property ) {
465 $data = [];
466 $data['pages'] = intval( SiteStats::pages() );
467 $data['articles'] = intval( SiteStats::articles() );
468 $data['edits'] = intval( SiteStats::edits() );
469 $data['images'] = intval( SiteStats::images() );
470 $data['users'] = intval( SiteStats::users() );
471 $data['activeusers'] = intval( SiteStats::activeUsers() );
472 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
473 $data['jobs'] = intval( SiteStats::jobs() );
475 Hooks::run( 'APIQuerySiteInfoStatisticsInfo', [ &$data ] );
477 return $this->getResult()->addValue( 'query', $property, $data );
480 protected function appendUserGroups( $property, $numberInGroup ) {
481 $config = $this->getConfig();
483 $data = [];
484 $result = $this->getResult();
485 $allGroups = User::getAllGroups();
486 foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
487 $arr = [
488 'name' => $group,
489 'rights' => array_keys( $permissions, true ),
492 if ( $numberInGroup ) {
493 $autopromote = $config->get( 'Autopromote' );
495 if ( $group == 'user' ) {
496 $arr['number'] = SiteStats::users();
497 // '*' and autopromote groups have no size
498 } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
499 $arr['number'] = SiteStats::numberingroup( $group );
503 $groupArr = [
504 'add' => $config->get( 'AddGroups' ),
505 'remove' => $config->get( 'RemoveGroups' ),
506 'add-self' => $config->get( 'GroupsAddToSelf' ),
507 'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
510 foreach ( $groupArr as $type => $rights ) {
511 if ( isset( $rights[$group] ) ) {
512 $groups = array_intersect( $rights[$group], $allGroups );
513 if ( $groups ) {
514 $arr[$type] = $groups;
515 ApiResult::setArrayType( $arr[$type], 'BCarray' );
516 ApiResult::setIndexedTagName( $arr[$type], 'group' );
521 ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
522 $data[] = $arr;
525 ApiResult::setIndexedTagName( $data, 'group' );
527 return $result->addValue( 'query', $property, $data );
530 protected function appendFileExtensions( $property ) {
531 $data = [];
532 foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
533 $data[] = [ 'ext' => $ext ];
535 ApiResult::setIndexedTagName( $data, 'fe' );
537 return $this->getResult()->addValue( 'query', $property, $data );
540 protected function appendInstalledLibraries( $property ) {
541 global $IP;
542 $path = "$IP/vendor/composer/installed.json";
543 if ( !file_exists( $path ) ) {
544 return true;
547 $data = [];
548 $installed = new ComposerInstalled( $path );
549 foreach ( $installed->getInstalledDependencies() as $name => $info ) {
550 if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
551 // Skip any extensions or skins since they'll be listed
552 // in their proper section
553 continue;
555 $data[] = [
556 'name' => $name,
557 'version' => $info['version'],
560 ApiResult::setIndexedTagName( $data, 'library' );
562 return $this->getResult()->addValue( 'query', $property, $data );
566 protected function appendExtensions( $property ) {
567 $data = [];
568 foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
569 foreach ( $extensions as $ext ) {
570 $ret = [];
571 $ret['type'] = $type;
572 if ( isset( $ext['name'] ) ) {
573 $ret['name'] = $ext['name'];
575 if ( isset( $ext['namemsg'] ) ) {
576 $ret['namemsg'] = $ext['namemsg'];
578 if ( isset( $ext['description'] ) ) {
579 $ret['description'] = $ext['description'];
581 if ( isset( $ext['descriptionmsg'] ) ) {
582 // Can be a string or array( key, param1, param2, ... )
583 if ( is_array( $ext['descriptionmsg'] ) ) {
584 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
585 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
586 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
587 } else {
588 $ret['descriptionmsg'] = $ext['descriptionmsg'];
591 if ( isset( $ext['author'] ) ) {
592 $ret['author'] = is_array( $ext['author'] ) ?
593 implode( ', ', $ext['author'] ) : $ext['author'];
595 if ( isset( $ext['url'] ) ) {
596 $ret['url'] = $ext['url'];
598 if ( isset( $ext['version'] ) ) {
599 $ret['version'] = $ext['version'];
601 if ( isset( $ext['path'] ) ) {
602 $extensionPath = dirname( $ext['path'] );
603 $gitInfo = new GitInfo( $extensionPath );
604 $vcsVersion = $gitInfo->getHeadSHA1();
605 if ( $vcsVersion !== false ) {
606 $ret['vcs-system'] = 'git';
607 $ret['vcs-version'] = $vcsVersion;
608 $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
609 $vcsDate = $gitInfo->getHeadCommitDate();
610 if ( $vcsDate !== false ) {
611 $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
615 if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
616 $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
617 $ret['license'] = SpecialPage::getTitleFor(
618 'Version',
619 "License/{$ext['name']}"
620 )->getLinkURL();
623 if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
624 $ret['credits'] = SpecialPage::getTitleFor(
625 'Version',
626 "Credits/{$ext['name']}"
627 )->getLinkURL();
630 $data[] = $ret;
634 ApiResult::setIndexedTagName( $data, 'ext' );
636 return $this->getResult()->addValue( 'query', $property, $data );
639 protected function appendRightsInfo( $property ) {
640 $config = $this->getConfig();
641 $rightsPage = $config->get( 'RightsPage' );
642 if ( is_string( $rightsPage ) ) {
643 $title = Title::newFromText( $rightsPage );
644 $url = wfExpandUrl( $title, PROTO_CURRENT );
645 } else {
646 $title = false;
647 $url = $config->get( 'RightsUrl' );
649 $text = $config->get( 'RightsText' );
650 if ( !$text && $title ) {
651 $text = $title->getPrefixedText();
654 $data = [
655 'url' => $url ? $url : '',
656 'text' => $text ? $text : ''
659 return $this->getResult()->addValue( 'query', $property, $data );
662 protected function appendRestrictions( $property ) {
663 $config = $this->getConfig();
664 $data = [
665 'types' => $config->get( 'RestrictionTypes' ),
666 'levels' => $config->get( 'RestrictionLevels' ),
667 'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
668 'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
671 ApiResult::setArrayType( $data['types'], 'BCarray' );
672 ApiResult::setArrayType( $data['levels'], 'BCarray' );
673 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
674 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
676 ApiResult::setIndexedTagName( $data['types'], 'type' );
677 ApiResult::setIndexedTagName( $data['levels'], 'level' );
678 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
679 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
681 return $this->getResult()->addValue( 'query', $property, $data );
684 public function appendLanguages( $property ) {
685 $params = $this->extractRequestParams();
686 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
687 $langNames = Language::fetchLanguageNames( $langCode );
689 $data = [];
691 foreach ( $langNames as $code => $name ) {
692 $lang = [ 'code' => $code ];
693 ApiResult::setContentValue( $lang, 'name', $name );
694 $data[] = $lang;
696 ApiResult::setIndexedTagName( $data, 'lang' );
698 return $this->getResult()->addValue( 'query', $property, $data );
701 public function appendSkins( $property ) {
702 $data = [];
703 $allowed = Skin::getAllowedSkins();
704 $default = Skin::normalizeKey( 'default' );
705 foreach ( Skin::getSkinNames() as $name => $displayName ) {
706 $msg = $this->msg( "skinname-{$name}" );
707 $code = $this->getParameter( 'inlanguagecode' );
708 if ( $code && Language::isValidCode( $code ) ) {
709 $msg->inLanguage( $code );
710 } else {
711 $msg->inContentLanguage();
713 if ( $msg->exists() ) {
714 $displayName = $msg->text();
716 $skin = [ 'code' => $name ];
717 ApiResult::setContentValue( $skin, 'name', $displayName );
718 if ( !isset( $allowed[$name] ) ) {
719 $skin['unusable'] = true;
721 if ( $name === $default ) {
722 $skin['default'] = true;
724 $data[] = $skin;
726 ApiResult::setIndexedTagName( $data, 'skin' );
728 return $this->getResult()->addValue( 'query', $property, $data );
731 public function appendExtensionTags( $property ) {
732 global $wgParser;
733 $wgParser->firstCallInit();
734 $tags = array_map( [ $this, 'formatParserTags' ], $wgParser->getTags() );
735 ApiResult::setArrayType( $tags, 'BCarray' );
736 ApiResult::setIndexedTagName( $tags, 't' );
738 return $this->getResult()->addValue( 'query', $property, $tags );
741 public function appendFunctionHooks( $property ) {
742 global $wgParser;
743 $wgParser->firstCallInit();
744 $hooks = $wgParser->getFunctionHooks();
745 ApiResult::setArrayType( $hooks, 'BCarray' );
746 ApiResult::setIndexedTagName( $hooks, 'h' );
748 return $this->getResult()->addValue( 'query', $property, $hooks );
751 public function appendVariables( $property ) {
752 $variables = MagicWord::getVariableIDs();
753 ApiResult::setArrayType( $variables, 'BCarray' );
754 ApiResult::setIndexedTagName( $variables, 'v' );
756 return $this->getResult()->addValue( 'query', $property, $variables );
759 public function appendProtocols( $property ) {
760 // Make a copy of the global so we don't try to set the _element key of it - bug 45130
761 $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
762 ApiResult::setArrayType( $protocols, 'BCarray' );
763 ApiResult::setIndexedTagName( $protocols, 'p' );
765 return $this->getResult()->addValue( 'query', $property, $protocols );
768 public function appendDefaultOptions( $property ) {
769 $options = User::getDefaultOptions();
770 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
771 return $this->getResult()->addValue( 'query', $property, $options );
774 private function formatParserTags( $item ) {
775 return "<{$item}>";
778 public function appendSubscribedHooks( $property ) {
779 $hooks = $this->getConfig()->get( 'Hooks' );
780 $myWgHooks = $hooks;
781 ksort( $myWgHooks );
783 $data = [];
784 foreach ( $myWgHooks as $name => $subscribers ) {
785 $arr = [
786 'name' => $name,
787 'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
790 ApiResult::setArrayType( $arr['subscribers'], 'array' );
791 ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
792 $data[] = $arr;
795 ApiResult::setIndexedTagName( $data, 'hook' );
797 return $this->getResult()->addValue( 'query', $property, $data );
800 public function getCacheMode( $params ) {
801 // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
802 if (
803 count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
804 !is_null( $params['prop'] ) &&
805 in_array( 'interwikimap', $params['prop'] )
807 return 'anon-public-user-private';
810 return 'public';
813 public function getAllowedParams() {
814 return [
815 'prop' => [
816 ApiBase::PARAM_DFLT => 'general',
817 ApiBase::PARAM_ISMULTI => true,
818 ApiBase::PARAM_TYPE => [
819 'general',
820 'namespaces',
821 'namespacealiases',
822 'specialpagealiases',
823 'magicwords',
824 'interwikimap',
825 'dbrepllag',
826 'statistics',
827 'usergroups',
828 'libraries',
829 'extensions',
830 'fileextensions',
831 'rightsinfo',
832 'restrictions',
833 'languages',
834 'skins',
835 'extensiontags',
836 'functionhooks',
837 'showhooks',
838 'variables',
839 'protocols',
840 'defaultoptions',
842 ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
844 'filteriw' => [
845 ApiBase::PARAM_TYPE => [
846 'local',
847 '!local',
850 'showalldb' => false,
851 'numberingroup' => false,
852 'inlanguagecode' => null,
856 protected function getExamplesMessages() {
857 return [
858 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
859 => 'apihelp-query+siteinfo-example-simple',
860 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
861 => 'apihelp-query+siteinfo-example-interwiki',
862 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
863 => 'apihelp-query+siteinfo-example-replag',
867 public function getHelpUrls() {
868 return 'https://www.mediawiki.org/wiki/API:Siteinfo';