Use adaptive CDN TTLs for page views
[mediawiki.git] / includes / api / ApiQuerySiteinfo.php
blob5d324973b6a6cfc60a533319ca3230e9d78f072e
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['thumblimits'] = $config->get( 'ThumbLimits' );
257 ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
258 ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
259 $data['imagelimits'] = [];
260 ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
261 ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
262 foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
263 $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
266 $favicon = $config->get( 'Favicon' );
267 if ( !empty( $favicon ) ) {
268 // wgFavicon can either be a relative or an absolute path
269 // make sure we always return an absolute path
270 $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
273 $data['centralidlookupprovider'] = $config->get( 'CentralIdLookupProvider' );
274 $providerIds = array_keys( $config->get( 'CentralIdLookupProviders' ) );
275 $data['allcentralidlookupproviders'] = $providerIds;
277 $data['interwikimagic'] = (bool)$config->get( 'InterwikiMagic' );
279 Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
281 return $this->getResult()->addValue( 'query', $property, $data );
284 protected function appendNamespaces( $property ) {
285 global $wgContLang;
286 $data = [
287 ApiResult::META_TYPE => 'assoc',
289 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
290 $data[$ns] = [
291 'id' => intval( $ns ),
292 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
294 ApiResult::setContentValue( $data[$ns], 'name', $title );
295 $canonical = MWNamespace::getCanonicalName( $ns );
297 $data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
299 if ( $canonical ) {
300 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
303 $data[$ns]['content'] = MWNamespace::isContent( $ns );
304 $data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
306 $contentmodel = MWNamespace::getNamespaceContentModel( $ns );
307 if ( $contentmodel ) {
308 $data[$ns]['defaultcontentmodel'] = $contentmodel;
312 ApiResult::setArrayType( $data, 'assoc' );
313 ApiResult::setIndexedTagName( $data, 'ns' );
315 return $this->getResult()->addValue( 'query', $property, $data );
318 protected function appendNamespaceAliases( $property ) {
319 global $wgContLang;
320 $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
321 $wgContLang->getNamespaceAliases() );
322 $namespaces = $wgContLang->getNamespaces();
323 $data = [];
324 foreach ( $aliases as $title => $ns ) {
325 if ( $namespaces[$ns] == $title ) {
326 // Don't list duplicates
327 continue;
329 $item = [
330 'id' => intval( $ns )
332 ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
333 $data[] = $item;
336 sort( $data );
338 ApiResult::setIndexedTagName( $data, 'ns' );
340 return $this->getResult()->addValue( 'query', $property, $data );
343 protected function appendSpecialPageAliases( $property ) {
344 global $wgContLang;
345 $data = [];
346 $aliases = $wgContLang->getSpecialPageAliases();
347 foreach ( SpecialPageFactory::getNames() as $specialpage ) {
348 if ( isset( $aliases[$specialpage] ) ) {
349 $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
350 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
351 $data[] = $arr;
354 ApiResult::setIndexedTagName( $data, 'specialpage' );
356 return $this->getResult()->addValue( 'query', $property, $data );
359 protected function appendMagicWords( $property ) {
360 global $wgContLang;
361 $data = [];
362 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
363 $caseSensitive = array_shift( $aliases );
364 $arr = [ 'name' => $magicword, 'aliases' => $aliases ];
365 $arr['case-sensitive'] = (bool)$caseSensitive;
366 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
367 $data[] = $arr;
369 ApiResult::setIndexedTagName( $data, 'magicword' );
371 return $this->getResult()->addValue( 'query', $property, $data );
374 protected function appendInterwikiMap( $property, $filter ) {
375 $local = null;
376 if ( $filter === 'local' ) {
377 $local = 1;
378 } elseif ( $filter === '!local' ) {
379 $local = 0;
380 } elseif ( $filter ) {
381 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
384 $params = $this->extractRequestParams();
385 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
386 $langNames = Language::fetchLanguageNames( $langCode );
388 $getPrefixes = Interwiki::getAllPrefixes( $local );
389 $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
390 $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
391 $data = [];
393 foreach ( $getPrefixes as $row ) {
394 $prefix = $row['iw_prefix'];
395 $val = [];
396 $val['prefix'] = $prefix;
397 if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
398 $val['local'] = true;
400 if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
401 $val['trans'] = true;
404 if ( isset( $langNames[$prefix] ) ) {
405 $val['language'] = $langNames[$prefix];
407 if ( in_array( $prefix, $localInterwikis ) ) {
408 $val['localinterwiki'] = true;
410 if ( in_array( $prefix, $extraLangPrefixes ) ) {
411 $val['extralanglink'] = true;
413 $linktext = wfMessage( "interlanguage-link-$prefix" );
414 if ( !$linktext->isDisabled() ) {
415 $val['linktext'] = $linktext->text();
418 $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
419 if ( !$sitename->isDisabled() ) {
420 $val['sitename'] = $sitename->text();
424 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
425 $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
426 if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
427 $val['wikiid'] = $row['iw_wikiid'];
429 if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
430 $val['api'] = $row['iw_api'];
433 $data[] = $val;
436 ApiResult::setIndexedTagName( $data, 'iw' );
438 return $this->getResult()->addValue( 'query', $property, $data );
441 protected function appendDbReplLagInfo( $property, $includeAll ) {
442 $data = [];
443 $lb = wfGetLB();
444 $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
445 if ( $includeAll ) {
446 if ( !$showHostnames ) {
447 $this->dieUsage(
448 'Cannot view all servers info unless $wgShowHostnames is true',
449 '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 );
581 protected function appendExtensions( $property ) {
582 $data = [];
583 foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
584 foreach ( $extensions as $ext ) {
585 $ret = [];
586 $ret['type'] = $type;
587 if ( isset( $ext['name'] ) ) {
588 $ret['name'] = $ext['name'];
590 if ( isset( $ext['namemsg'] ) ) {
591 $ret['namemsg'] = $ext['namemsg'];
593 if ( isset( $ext['description'] ) ) {
594 $ret['description'] = $ext['description'];
596 if ( isset( $ext['descriptionmsg'] ) ) {
597 // Can be a string or [ key, param1, param2, ... ]
598 if ( is_array( $ext['descriptionmsg'] ) ) {
599 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
600 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
601 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
602 } else {
603 $ret['descriptionmsg'] = $ext['descriptionmsg'];
606 if ( isset( $ext['author'] ) ) {
607 $ret['author'] = is_array( $ext['author'] ) ?
608 implode( ', ', $ext['author'] ) : $ext['author'];
610 if ( isset( $ext['url'] ) ) {
611 $ret['url'] = $ext['url'];
613 if ( isset( $ext['version'] ) ) {
614 $ret['version'] = $ext['version'];
616 if ( isset( $ext['path'] ) ) {
617 $extensionPath = dirname( $ext['path'] );
618 $gitInfo = new GitInfo( $extensionPath );
619 $vcsVersion = $gitInfo->getHeadSHA1();
620 if ( $vcsVersion !== false ) {
621 $ret['vcs-system'] = 'git';
622 $ret['vcs-version'] = $vcsVersion;
623 $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
624 $vcsDate = $gitInfo->getHeadCommitDate();
625 if ( $vcsDate !== false ) {
626 $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
630 if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
631 $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
632 $ret['license'] = SpecialPage::getTitleFor(
633 'Version',
634 "License/{$ext['name']}"
635 )->getLinkURL();
638 if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
639 $ret['credits'] = SpecialPage::getTitleFor(
640 'Version',
641 "Credits/{$ext['name']}"
642 )->getLinkURL();
645 $data[] = $ret;
649 ApiResult::setIndexedTagName( $data, 'ext' );
651 return $this->getResult()->addValue( 'query', $property, $data );
654 protected function appendRightsInfo( $property ) {
655 $config = $this->getConfig();
656 $rightsPage = $config->get( 'RightsPage' );
657 if ( is_string( $rightsPage ) ) {
658 $title = Title::newFromText( $rightsPage );
659 $url = wfExpandUrl( $title, PROTO_CURRENT );
660 } else {
661 $title = false;
662 $url = $config->get( 'RightsUrl' );
664 $text = $config->get( 'RightsText' );
665 if ( !$text && $title ) {
666 $text = $title->getPrefixedText();
669 $data = [
670 'url' => $url ?: '',
671 'text' => $text ?: ''
674 return $this->getResult()->addValue( 'query', $property, $data );
677 protected function appendRestrictions( $property ) {
678 $config = $this->getConfig();
679 $data = [
680 'types' => $config->get( 'RestrictionTypes' ),
681 'levels' => $config->get( 'RestrictionLevels' ),
682 'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
683 'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
686 ApiResult::setArrayType( $data['types'], 'BCarray' );
687 ApiResult::setArrayType( $data['levels'], 'BCarray' );
688 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
689 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
691 ApiResult::setIndexedTagName( $data['types'], 'type' );
692 ApiResult::setIndexedTagName( $data['levels'], 'level' );
693 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
694 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
696 return $this->getResult()->addValue( 'query', $property, $data );
699 public function appendLanguages( $property ) {
700 $params = $this->extractRequestParams();
701 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
702 $langNames = Language::fetchLanguageNames( $langCode );
704 $data = [];
706 foreach ( $langNames as $code => $name ) {
707 $lang = [ 'code' => $code ];
708 ApiResult::setContentValue( $lang, 'name', $name );
709 $data[] = $lang;
711 ApiResult::setIndexedTagName( $data, 'lang' );
713 return $this->getResult()->addValue( 'query', $property, $data );
716 public function appendSkins( $property ) {
717 $data = [];
718 $allowed = Skin::getAllowedSkins();
719 $default = Skin::normalizeKey( 'default' );
720 foreach ( Skin::getSkinNames() as $name => $displayName ) {
721 $msg = $this->msg( "skinname-{$name}" );
722 $code = $this->getParameter( 'inlanguagecode' );
723 if ( $code && Language::isValidCode( $code ) ) {
724 $msg->inLanguage( $code );
725 } else {
726 $msg->inContentLanguage();
728 if ( $msg->exists() ) {
729 $displayName = $msg->text();
731 $skin = [ 'code' => $name ];
732 ApiResult::setContentValue( $skin, 'name', $displayName );
733 if ( !isset( $allowed[$name] ) ) {
734 $skin['unusable'] = true;
736 if ( $name === $default ) {
737 $skin['default'] = true;
739 $data[] = $skin;
741 ApiResult::setIndexedTagName( $data, 'skin' );
743 return $this->getResult()->addValue( 'query', $property, $data );
746 public function appendExtensionTags( $property ) {
747 global $wgParser;
748 $wgParser->firstCallInit();
749 $tags = array_map( [ $this, 'formatParserTags' ], $wgParser->getTags() );
750 ApiResult::setArrayType( $tags, 'BCarray' );
751 ApiResult::setIndexedTagName( $tags, 't' );
753 return $this->getResult()->addValue( 'query', $property, $tags );
756 public function appendFunctionHooks( $property ) {
757 global $wgParser;
758 $wgParser->firstCallInit();
759 $hooks = $wgParser->getFunctionHooks();
760 ApiResult::setArrayType( $hooks, 'BCarray' );
761 ApiResult::setIndexedTagName( $hooks, 'h' );
763 return $this->getResult()->addValue( 'query', $property, $hooks );
766 public function appendVariables( $property ) {
767 $variables = MagicWord::getVariableIDs();
768 ApiResult::setArrayType( $variables, 'BCarray' );
769 ApiResult::setIndexedTagName( $variables, 'v' );
771 return $this->getResult()->addValue( 'query', $property, $variables );
774 public function appendProtocols( $property ) {
775 // Make a copy of the global so we don't try to set the _element key of it - bug 45130
776 $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
777 ApiResult::setArrayType( $protocols, 'BCarray' );
778 ApiResult::setIndexedTagName( $protocols, 'p' );
780 return $this->getResult()->addValue( 'query', $property, $protocols );
783 public function appendDefaultOptions( $property ) {
784 $options = User::getDefaultOptions();
785 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
786 return $this->getResult()->addValue( 'query', $property, $options );
789 public function appendUploadDialog( $property ) {
790 $config = $this->getConfig()->get( 'UploadDialog' );
791 return $this->getResult()->addValue( 'query', $property, $config );
794 private function formatParserTags( $item ) {
795 return "<{$item}>";
798 public function appendSubscribedHooks( $property ) {
799 $hooks = $this->getConfig()->get( 'Hooks' );
800 $myWgHooks = $hooks;
801 ksort( $myWgHooks );
803 $data = [];
804 foreach ( $myWgHooks as $name => $subscribers ) {
805 $arr = [
806 'name' => $name,
807 'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
810 ApiResult::setArrayType( $arr['subscribers'], 'array' );
811 ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
812 $data[] = $arr;
815 ApiResult::setIndexedTagName( $data, 'hook' );
817 return $this->getResult()->addValue( 'query', $property, $data );
820 public function getCacheMode( $params ) {
821 // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
822 if (
823 count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
824 !is_null( $params['prop'] ) &&
825 in_array( 'interwikimap', $params['prop'] )
827 return 'anon-public-user-private';
830 return 'public';
833 public function getAllowedParams() {
834 return [
835 'prop' => [
836 ApiBase::PARAM_DFLT => 'general',
837 ApiBase::PARAM_ISMULTI => true,
838 ApiBase::PARAM_TYPE => [
839 'general',
840 'namespaces',
841 'namespacealiases',
842 'specialpagealiases',
843 'magicwords',
844 'interwikimap',
845 'dbrepllag',
846 'statistics',
847 'usergroups',
848 'libraries',
849 'extensions',
850 'fileextensions',
851 'rightsinfo',
852 'restrictions',
853 'languages',
854 'skins',
855 'extensiontags',
856 'functionhooks',
857 'showhooks',
858 'variables',
859 'protocols',
860 'defaultoptions',
861 'uploaddialog',
863 ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
865 'filteriw' => [
866 ApiBase::PARAM_TYPE => [
867 'local',
868 '!local',
871 'showalldb' => false,
872 'numberingroup' => false,
873 'inlanguagecode' => null,
877 protected function getExamplesMessages() {
878 return [
879 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
880 => 'apihelp-query+siteinfo-example-simple',
881 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
882 => 'apihelp-query+siteinfo-example-interwiki',
883 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
884 => 'apihelp-query+siteinfo-example-replag',
888 public function getHelpUrls() {
889 return 'https://www.mediawiki.org/wiki/API:Siteinfo';