Back off from job types longer for DB read-only errors
[mediawiki.git] / includes / api / ApiQuerySiteinfo.php
blob6fc6aa370c0044200e8c020d5f3791afe4ae41af
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 case 'uploaddialog':
112 $fit = $this->appendUploadDialog( $p );
113 break;
114 default:
115 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
117 if ( !$fit ) {
118 // Abuse siprop as a query-continue parameter
119 // and set it to all unprocessed props
120 $this->setContinueEnumParameter( 'prop', implode( '|',
121 array_diff( $params['prop'], $done ) ) );
122 break;
124 $done[] = $p;
128 protected function appendGeneralInfo( $property ) {
129 global $wgContLang;
131 $config = $this->getConfig();
133 $data = [];
134 $mainPage = Title::newMainPage();
135 $data['mainpage'] = $mainPage->getPrefixedText();
136 $data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
137 $data['sitename'] = $config->get( 'Sitename' );
139 // wgLogo can either be a relative or an absolute path
140 // make sure we always return an absolute path
141 $data['logo'] = wfExpandUrl( $config->get( 'Logo' ), PROTO_RELATIVE );
143 $data['generator'] = "MediaWiki {$config->get( 'Version' )}";
145 $data['phpversion'] = PHP_VERSION;
146 $data['phpsapi'] = PHP_SAPI;
147 if ( defined( 'HHVM_VERSION' ) ) {
148 $data['hhvmversion'] = HHVM_VERSION;
150 $data['dbtype'] = $config->get( 'DBtype' );
151 $data['dbversion'] = $this->getDB()->getServerVersion();
153 $allowFrom = [ '' ];
154 $allowException = true;
155 if ( !$config->get( 'AllowExternalImages' ) ) {
156 $data['imagewhitelistenabled'] = (bool)$config->get( 'EnableImageWhitelist' );
157 $allowFrom = $config->get( 'AllowExternalImagesFrom' );
158 $allowException = !empty( $allowFrom );
160 if ( $allowException ) {
161 $data['externalimages'] = (array)$allowFrom;
162 ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
165 $data['langconversion'] = !$config->get( 'DisableLangConversion' );
166 $data['titleconversion'] = !$config->get( 'DisableTitleConversion' );
168 if ( $wgContLang->linkPrefixExtension() ) {
169 $linkPrefixCharset = $wgContLang->linkPrefixCharset();
170 $data['linkprefixcharset'] = $linkPrefixCharset;
171 // For backwards compatibility
172 $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
173 } else {
174 $data['linkprefixcharset'] = '';
175 $data['linkprefix'] = '';
178 $linktrail = $wgContLang->linkTrail();
179 $data['linktrail'] = $linktrail ?: '';
181 $data['legaltitlechars'] = Title::legalChars();
182 $data['invalidusernamechars'] = $config->get( 'InvalidUsernameCharacters' );
184 $data['allunicodefixes'] = (bool)$config->get( 'AllUnicodeFixes' );
185 $data['fixarabicunicode'] = (bool)$config->get( 'FixArabicUnicode' );
186 $data['fixmalayalamunicode'] = (bool)$config->get( 'FixMalayalamUnicode' );
188 global $IP;
189 $git = SpecialVersion::getGitHeadSha1( $IP );
190 if ( $git ) {
191 $data['git-hash'] = $git;
192 $data['git-branch'] =
193 SpecialVersion::getGitCurrentBranch( $GLOBALS['IP'] );
196 // 'case-insensitive' option is reserved for future
197 $data['case'] = $config->get( 'CapitalLinks' ) ? 'first-letter' : 'case-sensitive';
198 $data['lang'] = $config->get( 'LanguageCode' );
200 $fallbacks = [];
201 foreach ( $wgContLang->getFallbackLanguages() as $code ) {
202 $fallbacks[] = [ 'code' => $code ];
204 $data['fallback'] = $fallbacks;
205 ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
207 if ( $wgContLang->hasVariants() ) {
208 $variants = [];
209 foreach ( $wgContLang->getVariants() as $code ) {
210 $variants[] = [
211 'code' => $code,
212 'name' => $wgContLang->getVariantname( $code ),
215 $data['variants'] = $variants;
216 ApiResult::setIndexedTagName( $data['variants'], 'lang' );
219 $data['rtl'] = $wgContLang->isRTL();
220 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
222 $data['readonly'] = wfReadOnly();
223 if ( $data['readonly'] ) {
224 $data['readonlyreason'] = wfReadOnlyReason();
226 $data['writeapi'] = (bool)$config->get( 'EnableWriteAPI' );
228 $data['maxarticlesize'] = $config->get( 'MaxArticleSize' ) * 1024;
230 $tz = $config->get( 'Localtimezone' );
231 $offset = $config->get( 'LocalTZoffset' );
232 if ( is_null( $tz ) ) {
233 $tz = 'UTC';
234 $offset = 0;
235 } elseif ( is_null( $offset ) ) {
236 $offset = 0;
238 $data['timezone'] = $tz;
239 $data['timeoffset'] = intval( $offset );
240 $data['articlepath'] = $config->get( 'ArticlePath' );
241 $data['scriptpath'] = $config->get( 'ScriptPath' );
242 $data['script'] = $config->get( 'Script' );
243 $data['variantarticlepath'] = $config->get( 'VariantArticlePath' );
244 $data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
245 $data['server'] = $config->get( 'Server' );
246 $data['servername'] = $config->get( 'ServerName' );
247 $data['wikiid'] = wfWikiID();
248 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
250 $data['misermode'] = (bool)$config->get( 'MiserMode' );
252 $data['uploadsenabled'] = UploadBase::isEnabled();
253 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
254 $data['minuploadchunksize'] = (int)$config->get( 'MinUploadChunkSize' );
256 $data['galleryoptions'] = $config->get( 'GalleryOptions' );
258 $data['thumblimits'] = $config->get( 'ThumbLimits' );
259 ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
260 ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
261 $data['imagelimits'] = [];
262 ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
263 ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
264 foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
265 $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
268 $favicon = $config->get( 'Favicon' );
269 if ( !empty( $favicon ) ) {
270 // wgFavicon can either be a relative or an absolute path
271 // make sure we always return an absolute path
272 $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
275 $data['centralidlookupprovider'] = $config->get( 'CentralIdLookupProvider' );
276 $providerIds = array_keys( $config->get( 'CentralIdLookupProviders' ) );
277 $data['allcentralidlookupproviders'] = $providerIds;
279 $data['interwikimagic'] = (bool)$config->get( 'InterwikiMagic' );
280 $data['magiclinks'] = $config->get( 'EnableMagicLinks' );
282 Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
284 return $this->getResult()->addValue( 'query', $property, $data );
287 protected function appendNamespaces( $property ) {
288 global $wgContLang;
289 $data = [
290 ApiResult::META_TYPE => 'assoc',
292 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
293 $data[$ns] = [
294 'id' => intval( $ns ),
295 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
297 ApiResult::setContentValue( $data[$ns], 'name', $title );
298 $canonical = MWNamespace::getCanonicalName( $ns );
300 $data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
302 if ( $canonical ) {
303 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
306 $data[$ns]['content'] = MWNamespace::isContent( $ns );
307 $data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
309 $contentmodel = MWNamespace::getNamespaceContentModel( $ns );
310 if ( $contentmodel ) {
311 $data[$ns]['defaultcontentmodel'] = $contentmodel;
315 ApiResult::setArrayType( $data, 'assoc' );
316 ApiResult::setIndexedTagName( $data, 'ns' );
318 return $this->getResult()->addValue( 'query', $property, $data );
321 protected function appendNamespaceAliases( $property ) {
322 global $wgContLang;
323 $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
324 $wgContLang->getNamespaceAliases() );
325 $namespaces = $wgContLang->getNamespaces();
326 $data = [];
327 foreach ( $aliases as $title => $ns ) {
328 if ( $namespaces[$ns] == $title ) {
329 // Don't list duplicates
330 continue;
332 $item = [
333 'id' => intval( $ns )
335 ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
336 $data[] = $item;
339 sort( $data );
341 ApiResult::setIndexedTagName( $data, 'ns' );
343 return $this->getResult()->addValue( 'query', $property, $data );
346 protected function appendSpecialPageAliases( $property ) {
347 global $wgContLang;
348 $data = [];
349 $aliases = $wgContLang->getSpecialPageAliases();
350 foreach ( SpecialPageFactory::getNames() as $specialpage ) {
351 if ( isset( $aliases[$specialpage] ) ) {
352 $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
353 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
354 $data[] = $arr;
357 ApiResult::setIndexedTagName( $data, 'specialpage' );
359 return $this->getResult()->addValue( 'query', $property, $data );
362 protected function appendMagicWords( $property ) {
363 global $wgContLang;
364 $data = [];
365 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
366 $caseSensitive = array_shift( $aliases );
367 $arr = [ 'name' => $magicword, 'aliases' => $aliases ];
368 $arr['case-sensitive'] = (bool)$caseSensitive;
369 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
370 $data[] = $arr;
372 ApiResult::setIndexedTagName( $data, 'magicword' );
374 return $this->getResult()->addValue( 'query', $property, $data );
377 protected function appendInterwikiMap( $property, $filter ) {
378 $local = null;
379 if ( $filter === 'local' ) {
380 $local = 1;
381 } elseif ( $filter === '!local' ) {
382 $local = 0;
383 } elseif ( $filter ) {
384 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
387 $params = $this->extractRequestParams();
388 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
389 $langNames = Language::fetchLanguageNames( $langCode );
391 $getPrefixes = Interwiki::getAllPrefixes( $local );
392 $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
393 $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
394 $data = [];
396 foreach ( $getPrefixes as $row ) {
397 $prefix = $row['iw_prefix'];
398 $val = [];
399 $val['prefix'] = $prefix;
400 if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
401 $val['local'] = true;
403 if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
404 $val['trans'] = true;
407 if ( isset( $langNames[$prefix] ) ) {
408 $val['language'] = $langNames[$prefix];
410 if ( in_array( $prefix, $localInterwikis ) ) {
411 $val['localinterwiki'] = true;
413 if ( in_array( $prefix, $extraLangPrefixes ) ) {
414 $val['extralanglink'] = true;
416 $linktext = wfMessage( "interlanguage-link-$prefix" );
417 if ( !$linktext->isDisabled() ) {
418 $val['linktext'] = $linktext->text();
421 $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
422 if ( !$sitename->isDisabled() ) {
423 $val['sitename'] = $sitename->text();
427 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
428 $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
429 if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
430 $val['wikiid'] = $row['iw_wikiid'];
432 if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
433 $val['api'] = $row['iw_api'];
436 $data[] = $val;
439 ApiResult::setIndexedTagName( $data, 'iw' );
441 return $this->getResult()->addValue( 'query', $property, $data );
444 protected function appendDbReplLagInfo( $property, $includeAll ) {
445 $data = [];
446 $lb = wfGetLB();
447 $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
448 if ( $includeAll ) {
449 if ( !$showHostnames ) {
450 $this->dieWithError( 'apierror-siteinfo-includealldenied', 'includeAllDenied' );
453 $lags = $lb->getLagTimes();
454 foreach ( $lags as $i => $lag ) {
455 $data[] = [
456 'host' => $lb->getServerName( $i ),
457 'lag' => $lag
460 } else {
461 list( , $lag, $index ) = $lb->getMaxLag();
462 $data[] = [
463 'host' => $showHostnames
464 ? $lb->getServerName( $index )
465 : '',
466 'lag' => intval( $lag )
470 ApiResult::setIndexedTagName( $data, 'db' );
472 return $this->getResult()->addValue( 'query', $property, $data );
475 protected function appendStatistics( $property ) {
476 $data = [];
477 $data['pages'] = intval( SiteStats::pages() );
478 $data['articles'] = intval( SiteStats::articles() );
479 $data['edits'] = intval( SiteStats::edits() );
480 $data['images'] = intval( SiteStats::images() );
481 $data['users'] = intval( SiteStats::users() );
482 $data['activeusers'] = intval( SiteStats::activeUsers() );
483 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
484 $data['jobs'] = intval( SiteStats::jobs() );
486 Hooks::run( 'APIQuerySiteInfoStatisticsInfo', [ &$data ] );
488 return $this->getResult()->addValue( 'query', $property, $data );
491 protected function appendUserGroups( $property, $numberInGroup ) {
492 $config = $this->getConfig();
494 $data = [];
495 $result = $this->getResult();
496 $allGroups = array_values( User::getAllGroups() );
497 foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
498 $arr = [
499 'name' => $group,
500 'rights' => array_keys( $permissions, true ),
503 if ( $numberInGroup ) {
504 $autopromote = $config->get( 'Autopromote' );
506 if ( $group == 'user' ) {
507 $arr['number'] = SiteStats::users();
508 // '*' and autopromote groups have no size
509 } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
510 $arr['number'] = SiteStats::numberingroup( $group );
514 $groupArr = [
515 'add' => $config->get( 'AddGroups' ),
516 'remove' => $config->get( 'RemoveGroups' ),
517 'add-self' => $config->get( 'GroupsAddToSelf' ),
518 'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
521 foreach ( $groupArr as $type => $rights ) {
522 if ( isset( $rights[$group] ) ) {
523 if ( $rights[$group] === true ) {
524 $groups = $allGroups;
525 } else {
526 $groups = array_intersect( $rights[$group], $allGroups );
528 if ( $groups ) {
529 $arr[$type] = $groups;
530 ApiResult::setArrayType( $arr[$type], 'BCarray' );
531 ApiResult::setIndexedTagName( $arr[$type], 'group' );
536 ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
537 $data[] = $arr;
540 ApiResult::setIndexedTagName( $data, 'group' );
542 return $result->addValue( 'query', $property, $data );
545 protected function appendFileExtensions( $property ) {
546 $data = [];
547 foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
548 $data[] = [ 'ext' => $ext ];
550 ApiResult::setIndexedTagName( $data, 'fe' );
552 return $this->getResult()->addValue( 'query', $property, $data );
555 protected function appendInstalledLibraries( $property ) {
556 global $IP;
557 $path = "$IP/vendor/composer/installed.json";
558 if ( !file_exists( $path ) ) {
559 return true;
562 $data = [];
563 $installed = new ComposerInstalled( $path );
564 foreach ( $installed->getInstalledDependencies() as $name => $info ) {
565 if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
566 // Skip any extensions or skins since they'll be listed
567 // in their proper section
568 continue;
570 $data[] = [
571 'name' => $name,
572 'version' => $info['version'],
575 ApiResult::setIndexedTagName( $data, 'library' );
577 return $this->getResult()->addValue( 'query', $property, $data );
580 protected function appendExtensions( $property ) {
581 $data = [];
582 foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
583 foreach ( $extensions as $ext ) {
584 $ret = [];
585 $ret['type'] = $type;
586 if ( isset( $ext['name'] ) ) {
587 $ret['name'] = $ext['name'];
589 if ( isset( $ext['namemsg'] ) ) {
590 $ret['namemsg'] = $ext['namemsg'];
592 if ( isset( $ext['description'] ) ) {
593 $ret['description'] = $ext['description'];
595 if ( isset( $ext['descriptionmsg'] ) ) {
596 // Can be a string or [ key, param1, param2, ... ]
597 if ( is_array( $ext['descriptionmsg'] ) ) {
598 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
599 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
600 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
601 } else {
602 $ret['descriptionmsg'] = $ext['descriptionmsg'];
605 if ( isset( $ext['author'] ) ) {
606 $ret['author'] = is_array( $ext['author'] ) ?
607 implode( ', ', $ext['author'] ) : $ext['author'];
609 if ( isset( $ext['url'] ) ) {
610 $ret['url'] = $ext['url'];
612 if ( isset( $ext['version'] ) ) {
613 $ret['version'] = $ext['version'];
615 if ( isset( $ext['path'] ) ) {
616 $extensionPath = dirname( $ext['path'] );
617 $gitInfo = new GitInfo( $extensionPath );
618 $vcsVersion = $gitInfo->getHeadSHA1();
619 if ( $vcsVersion !== false ) {
620 $ret['vcs-system'] = 'git';
621 $ret['vcs-version'] = $vcsVersion;
622 $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
623 $vcsDate = $gitInfo->getHeadCommitDate();
624 if ( $vcsDate !== false ) {
625 $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
629 if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
630 $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
631 $ret['license'] = SpecialPage::getTitleFor(
632 'Version',
633 "License/{$ext['name']}"
634 )->getLinkURL();
637 if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
638 $ret['credits'] = SpecialPage::getTitleFor(
639 'Version',
640 "Credits/{$ext['name']}"
641 )->getLinkURL();
644 $data[] = $ret;
648 ApiResult::setIndexedTagName( $data, 'ext' );
650 return $this->getResult()->addValue( 'query', $property, $data );
653 protected function appendRightsInfo( $property ) {
654 $config = $this->getConfig();
655 $rightsPage = $config->get( 'RightsPage' );
656 if ( is_string( $rightsPage ) ) {
657 $title = Title::newFromText( $rightsPage );
658 $url = wfExpandUrl( $title, PROTO_CURRENT );
659 } else {
660 $title = false;
661 $url = $config->get( 'RightsUrl' );
663 $text = $config->get( 'RightsText' );
664 if ( !$text && $title ) {
665 $text = $title->getPrefixedText();
668 $data = [
669 'url' => $url ?: '',
670 'text' => $text ?: ''
673 return $this->getResult()->addValue( 'query', $property, $data );
676 protected function appendRestrictions( $property ) {
677 $config = $this->getConfig();
678 $data = [
679 'types' => $config->get( 'RestrictionTypes' ),
680 'levels' => $config->get( 'RestrictionLevels' ),
681 'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
682 'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
685 ApiResult::setArrayType( $data['types'], 'BCarray' );
686 ApiResult::setArrayType( $data['levels'], 'BCarray' );
687 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
688 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
690 ApiResult::setIndexedTagName( $data['types'], 'type' );
691 ApiResult::setIndexedTagName( $data['levels'], 'level' );
692 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
693 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
695 return $this->getResult()->addValue( 'query', $property, $data );
698 public function appendLanguages( $property ) {
699 $params = $this->extractRequestParams();
700 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
701 $langNames = Language::fetchLanguageNames( $langCode );
703 $data = [];
705 foreach ( $langNames as $code => $name ) {
706 $lang = [ 'code' => $code ];
707 ApiResult::setContentValue( $lang, 'name', $name );
708 $data[] = $lang;
710 ApiResult::setIndexedTagName( $data, 'lang' );
712 return $this->getResult()->addValue( 'query', $property, $data );
715 public function appendSkins( $property ) {
716 $data = [];
717 $allowed = Skin::getAllowedSkins();
718 $default = Skin::normalizeKey( 'default' );
719 foreach ( Skin::getSkinNames() as $name => $displayName ) {
720 $msg = $this->msg( "skinname-{$name}" );
721 $code = $this->getParameter( 'inlanguagecode' );
722 if ( $code && Language::isValidCode( $code ) ) {
723 $msg->inLanguage( $code );
724 } else {
725 $msg->inContentLanguage();
727 if ( $msg->exists() ) {
728 $displayName = $msg->text();
730 $skin = [ 'code' => $name ];
731 ApiResult::setContentValue( $skin, 'name', $displayName );
732 if ( !isset( $allowed[$name] ) ) {
733 $skin['unusable'] = true;
735 if ( $name === $default ) {
736 $skin['default'] = true;
738 $data[] = $skin;
740 ApiResult::setIndexedTagName( $data, 'skin' );
742 return $this->getResult()->addValue( 'query', $property, $data );
745 public function appendExtensionTags( $property ) {
746 global $wgParser;
747 $wgParser->firstCallInit();
748 $tags = array_map( [ $this, 'formatParserTags' ], $wgParser->getTags() );
749 ApiResult::setArrayType( $tags, 'BCarray' );
750 ApiResult::setIndexedTagName( $tags, 't' );
752 return $this->getResult()->addValue( 'query', $property, $tags );
755 public function appendFunctionHooks( $property ) {
756 global $wgParser;
757 $wgParser->firstCallInit();
758 $hooks = $wgParser->getFunctionHooks();
759 ApiResult::setArrayType( $hooks, 'BCarray' );
760 ApiResult::setIndexedTagName( $hooks, 'h' );
762 return $this->getResult()->addValue( 'query', $property, $hooks );
765 public function appendVariables( $property ) {
766 $variables = MagicWord::getVariableIDs();
767 ApiResult::setArrayType( $variables, 'BCarray' );
768 ApiResult::setIndexedTagName( $variables, 'v' );
770 return $this->getResult()->addValue( 'query', $property, $variables );
773 public function appendProtocols( $property ) {
774 // Make a copy of the global so we don't try to set the _element key of it - bug 45130
775 $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
776 ApiResult::setArrayType( $protocols, 'BCarray' );
777 ApiResult::setIndexedTagName( $protocols, 'p' );
779 return $this->getResult()->addValue( 'query', $property, $protocols );
782 public function appendDefaultOptions( $property ) {
783 $options = User::getDefaultOptions();
784 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
785 return $this->getResult()->addValue( 'query', $property, $options );
788 public function appendUploadDialog( $property ) {
789 $config = $this->getConfig()->get( 'UploadDialog' );
790 return $this->getResult()->addValue( 'query', $property, $config );
793 private function formatParserTags( $item ) {
794 return "<{$item}>";
797 public function appendSubscribedHooks( $property ) {
798 $hooks = $this->getConfig()->get( 'Hooks' );
799 $myWgHooks = $hooks;
800 ksort( $myWgHooks );
802 $data = [];
803 foreach ( $myWgHooks as $name => $subscribers ) {
804 $arr = [
805 'name' => $name,
806 'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
809 ApiResult::setArrayType( $arr['subscribers'], 'array' );
810 ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
811 $data[] = $arr;
814 ApiResult::setIndexedTagName( $data, 'hook' );
816 return $this->getResult()->addValue( 'query', $property, $data );
819 public function getCacheMode( $params ) {
820 // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
821 if (
822 count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
823 !is_null( $params['prop'] ) &&
824 in_array( 'interwikimap', $params['prop'] )
826 return 'anon-public-user-private';
829 return 'public';
832 public function getAllowedParams() {
833 return [
834 'prop' => [
835 ApiBase::PARAM_DFLT => 'general',
836 ApiBase::PARAM_ISMULTI => true,
837 ApiBase::PARAM_TYPE => [
838 'general',
839 'namespaces',
840 'namespacealiases',
841 'specialpagealiases',
842 'magicwords',
843 'interwikimap',
844 'dbrepllag',
845 'statistics',
846 'usergroups',
847 'libraries',
848 'extensions',
849 'fileextensions',
850 'rightsinfo',
851 'restrictions',
852 'languages',
853 'skins',
854 'extensiontags',
855 'functionhooks',
856 'showhooks',
857 'variables',
858 'protocols',
859 'defaultoptions',
860 'uploaddialog',
862 ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
864 'filteriw' => [
865 ApiBase::PARAM_TYPE => [
866 'local',
867 '!local',
870 'showalldb' => false,
871 'numberingroup' => false,
872 'inlanguagecode' => null,
876 protected function getExamplesMessages() {
877 return [
878 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
879 => 'apihelp-query+siteinfo-example-simple',
880 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
881 => 'apihelp-query+siteinfo-example-interwiki',
882 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
883 => 'apihelp-query+siteinfo-example-replag',
887 public function getHelpUrls() {
888 return 'https://www.mediawiki.org/wiki/API:Siteinfo';