Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.90% covered (success)
93.90%
616 / 656
71.79% covered (warning)
71.79%
28 / 39
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQuerySiteinfo
94.05% covered (success)
94.05%
616 / 655
71.79% covered (warning)
71.79%
28 / 39
146.26
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
1 / 1
3
 appendGeneralInfo
100.00% covered (success)
100.00%
104 / 104
100.00% covered (success)
100.00%
1 / 1
13
 appendNamespaces
96.00% covered (success)
96.00%
24 / 25
0.00% covered (danger)
0.00%
0 / 1
7
 appendNamespaceAliases
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
3.01
 appendSpecialPageAliases
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 appendMagicWords
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 appendInterwikiMap
97.78% covered (success)
97.78%
44 / 45
0.00% covered (danger)
0.00%
0 / 1
16
 appendDbReplLagInfo
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
5
 appendStatistics
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 appendUserGroups
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
8
 appendAutoCreateTempUser
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 appendFileExtensions
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 appendInstalledClientLibraries
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 appendInstalledLibraries
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
3.00
 appendExtensions
88.46% covered (warning)
88.46%
46 / 52
0.00% covered (danger)
0.00%
0 / 1
17.44
 appendRightsInfo
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 appendRestrictions
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
1
 appendLanguages
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
 appendLanguageVariants
95.83% covered (success)
95.83%
23 / 24
0.00% covered (danger)
0.00%
0 / 1
5
 appendSkins
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
7
 appendExtensionTags
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 appendFunctionHooks
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 appendVariables
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 appendDoubleUnderscores
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 appendProtocols
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 appendDefaultOptions
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 appendUploadDialog
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAutoPromoteConds
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 processAutoPromote
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 appendAutoPromote
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 appendAutoPromoteOnce
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 appendCopyUploadDomains
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 recAutopromote
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
9
 appendSubscribedHooks
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
 getCacheMode
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getAllowedParams
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
1 / 1
1
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Api;
10
11use MediaWiki\HookContainer\HookContainer;
12use MediaWiki\Interwiki\InterwikiLookup;
13use MediaWiki\Language\Language;
14use MediaWiki\Language\LanguageCode;
15use MediaWiki\Language\LanguageConverter;
16use MediaWiki\Languages\LanguageConverterFactory;
17use MediaWiki\Languages\LanguageFactory;
18use MediaWiki\Languages\LanguageNameUtils;
19use MediaWiki\MainConfigNames;
20use MediaWiki\Parser\MagicWordFactory;
21use MediaWiki\Parser\ParserFactory;
22use MediaWiki\Permissions\GroupPermissionsLookup;
23use MediaWiki\Registration\ExtensionRegistry;
24use MediaWiki\ResourceLoader\SkinModule;
25use MediaWiki\SiteStats\SiteStats;
26use MediaWiki\Skin\Skin;
27use MediaWiki\Skin\SkinFactory;
28use MediaWiki\SpecialPage\SpecialPage;
29use MediaWiki\SpecialPage\SpecialPageFactory;
30use MediaWiki\Specials\SpecialVersion;
31use MediaWiki\Title\NamespaceInfo;
32use MediaWiki\Title\Title;
33use MediaWiki\Upload\UploadBase;
34use MediaWiki\Upload\UploadFromUrl;
35use MediaWiki\User\Options\UserOptionsLookup;
36use MediaWiki\User\TempUser\TempUserConfig;
37use MediaWiki\User\UserGroupManager;
38use MediaWiki\User\UserRequirementsConditionChecker;
39use MediaWiki\Utils\ExtensionInfo;
40use MediaWiki\Utils\GitInfo;
41use MediaWiki\Utils\UrlUtils;
42use MediaWiki\WikiMap\WikiMap;
43use Wikimedia\ParamValidator\ParamValidator;
44use Wikimedia\Rdbms\ILoadBalancer;
45use Wikimedia\Rdbms\ReadOnlyMode;
46
47/**
48 * A query action to return meta information about the wiki site.
49 *
50 * @ingroup API
51 */
52class ApiQuerySiteinfo extends ApiQueryBase {
53
54    private UserOptionsLookup $userOptionsLookup;
55    private UserGroupManager $userGroupManager;
56    private HookContainer $hookContainer;
57    private LanguageConverterFactory $languageConverterFactory;
58    private LanguageFactory $languageFactory;
59    private LanguageNameUtils $languageNameUtils;
60    private Language $contentLanguage;
61    private NamespaceInfo $namespaceInfo;
62    private InterwikiLookup $interwikiLookup;
63    private ParserFactory $parserFactory;
64    private MagicWordFactory $magicWordFactory;
65    private SpecialPageFactory $specialPageFactory;
66    private SkinFactory $skinFactory;
67    private ILoadBalancer $loadBalancer;
68    private ReadOnlyMode $readOnlyMode;
69    private UrlUtils $urlUtils;
70    private TempUserConfig $tempUserConfig;
71    private GroupPermissionsLookup $groupPermissionsLookup;
72
73    public function __construct(
74        ApiQuery $query,
75        string $moduleName,
76        UserOptionsLookup $userOptionsLookup,
77        UserGroupManager $userGroupManager,
78        HookContainer $hookContainer,
79        LanguageConverterFactory $languageConverterFactory,
80        LanguageFactory $languageFactory,
81        LanguageNameUtils $languageNameUtils,
82        Language $contentLanguage,
83        NamespaceInfo $namespaceInfo,
84        InterwikiLookup $interwikiLookup,
85        ParserFactory $parserFactory,
86        MagicWordFactory $magicWordFactory,
87        SpecialPageFactory $specialPageFactory,
88        SkinFactory $skinFactory,
89        ILoadBalancer $loadBalancer,
90        ReadOnlyMode $readOnlyMode,
91        UrlUtils $urlUtils,
92        TempUserConfig $tempUserConfig,
93        GroupPermissionsLookup $groupPermissionsLookup
94    ) {
95        parent::__construct( $query, $moduleName, 'si' );
96        $this->userOptionsLookup = $userOptionsLookup;
97        $this->userGroupManager = $userGroupManager;
98        $this->hookContainer = $hookContainer;
99        $this->languageConverterFactory = $languageConverterFactory;
100        $this->languageFactory = $languageFactory;
101        $this->languageNameUtils = $languageNameUtils;
102        $this->contentLanguage = $contentLanguage;
103        $this->namespaceInfo = $namespaceInfo;
104        $this->interwikiLookup = $interwikiLookup;
105        $this->parserFactory = $parserFactory;
106        $this->magicWordFactory = $magicWordFactory;
107        $this->specialPageFactory = $specialPageFactory;
108        $this->skinFactory = $skinFactory;
109        $this->loadBalancer = $loadBalancer;
110        $this->readOnlyMode = $readOnlyMode;
111        $this->urlUtils = $urlUtils;
112        $this->tempUserConfig = $tempUserConfig;
113        $this->groupPermissionsLookup = $groupPermissionsLookup;
114    }
115
116    public function execute() {
117        $params = $this->extractRequestParams();
118        $done = [];
119        foreach ( $params['prop'] as $p ) {
120            $fit = match ( $p ) {
121                'general' => $this->appendGeneralInfo( $p ),
122                'namespaces' => $this->appendNamespaces( $p ),
123                'namespacealiases' => $this->appendNamespaceAliases( $p ),
124                'specialpagealiases' => $this->appendSpecialPageAliases( $p ),
125                'magicwords' => $this->appendMagicWords( $p ),
126                'interwikimap' => $this->appendInterwikiMap( $p, $params['filteriw'] ),
127                'dbrepllag' => $this->appendDbReplLagInfo( $p, $params['showalldb'] ),
128                'statistics' => $this->appendStatistics( $p ),
129                'usergroups' => $this->appendUserGroups( $p, $params['numberingroup'] ),
130                'autocreatetempuser' => $this->appendAutoCreateTempUser( $p ),
131                'clientlibraries' => $this->appendInstalledClientLibraries( $p ),
132                'libraries' => $this->appendInstalledLibraries( $p ),
133                'extensions' => $this->appendExtensions( $p ),
134                'fileextensions' => $this->appendFileExtensions( $p ),
135                'rightsinfo' => $this->appendRightsInfo( $p ),
136                'restrictions' => $this->appendRestrictions( $p ),
137                'languages' => $this->appendLanguages( $p ),
138                'languagevariants' => $this->appendLanguageVariants( $p ),
139                'skins' => $this->appendSkins( $p ),
140                'extensiontags' => $this->appendExtensionTags( $p ),
141                'functionhooks' => $this->appendFunctionHooks( $p ),
142                'showhooks' => $this->appendSubscribedHooks( $p ),
143                'variables' => $this->appendVariables( $p ),
144                'doubleunderscores' => $this->appendDoubleUnderscores( $p ),
145                'protocols' => $this->appendProtocols( $p ),
146                'defaultoptions' => $this->appendDefaultOptions( $p ),
147                'uploaddialog' => $this->appendUploadDialog( $p ),
148                'autopromote' => $this->appendAutoPromote( $p ),
149                'autopromoteonce' => $this->appendAutoPromoteOnce( $p ),
150                'copyuploaddomains' => $this->appendCopyUploadDomains( $p ),
151                // @phan-suppress-next-line PhanUseReturnValueOfNever
152                default => ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" ) // @codeCoverageIgnore
153            };
154            if ( !$fit ) {
155                // Abuse siprop as a query-continue parameter
156                // and set it to all unprocessed props
157                $this->setContinueEnumParameter( 'prop', implode( '|',
158                    array_diff( $params['prop'], $done ) ) );
159                break;
160            }
161            $done[] = $p;
162        }
163    }
164
165    protected function appendGeneralInfo( string $property ): bool {
166        $config = $this->getConfig();
167        $mainPage = Title::newMainPage();
168        $logo = SkinModule::getAvailableLogos( $config, $this->getLanguage()->getCode() );
169
170        $data = [
171            'mainpage' => $mainPage->getPrefixedText(),
172            'base' => (string)$this->urlUtils->expand( $mainPage->getFullURL(), PROTO_CURRENT ),
173            'sitename' => $config->get( MainConfigNames::Sitename ),
174            'mainpageisdomainroot' => (bool)$config->get( MainConfigNames::MainPageIsDomainRoot ),
175
176            // A logo can either be a relative or an absolute path
177            // make sure we always return an absolute path
178            'logo' => (string)$this->urlUtils->expand( $logo['1x'], PROTO_RELATIVE ),
179
180            'generator' => 'MediaWiki ' . MW_VERSION,
181
182            'phpversion' => PHP_VERSION,
183            'phpsapi' => PHP_SAPI,
184            'dbtype' => $config->get( MainConfigNames::DBtype ),
185            'dbversion' => $this->getDB()->getServerVersion(),
186        ];
187
188        $allowFrom = [ '' ];
189        $allowException = true;
190        if ( !$config->get( MainConfigNames::AllowExternalImages ) ) {
191            $data['imagewhitelistenabled'] =
192                (bool)$config->get( MainConfigNames::EnableImageWhitelist );
193            $allowFrom = $config->get( MainConfigNames::AllowExternalImagesFrom );
194            $allowException = (bool)$allowFrom;
195        }
196        if ( $allowException ) {
197            $data['externalimages'] = (array)$allowFrom;
198            ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
199        }
200
201        $data['langconversion'] = !$this->languageConverterFactory->isConversionDisabled();
202        $data['linkconversion'] = !$this->languageConverterFactory->isLinkConversionDisabled();
203        // For backwards compatibility (soft deprecated since MW 1.36)
204        $data['titleconversion'] = $data['linkconversion'];
205
206        $contLangConverter = $this->languageConverterFactory->getLanguageConverter( $this->contentLanguage );
207        if ( $this->contentLanguage->linkPrefixExtension() ) {
208            $linkPrefixCharset = $this->contentLanguage->linkPrefixCharset();
209            $data['linkprefixcharset'] = $linkPrefixCharset;
210            // For backwards compatibility
211            $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
212        } else {
213            $data['linkprefixcharset'] = '';
214            $data['linkprefix'] = '';
215        }
216
217        $data['linktrail'] = $this->contentLanguage->linkTrail() ?: '';
218
219        $data['legaltitlechars'] = Title::legalChars();
220        $data['invalidusernamechars'] = $config->get( MainConfigNames::InvalidUsernameCharacters );
221        $data['allunicodefixes'] = (bool)$config->get( MainConfigNames::AllUnicodeFixes );
222
223        $git = GitInfo::repo()->getHeadSHA1();
224        if ( $git ) {
225            $data['git-hash'] = $git;
226            $data['git-branch'] = GitInfo::repo()->getCurrentBranch();
227        }
228
229        // 'case-insensitive' option is reserved for future
230        $data['case'] =
231            $config->get( MainConfigNames::CapitalLinks ) ? 'first-letter' : 'case-sensitive';
232        $data['lang'] = $config->get( MainConfigNames::LanguageCode );
233
234        $data['fallback'] = [];
235        foreach ( $this->contentLanguage->getFallbackLanguages() as $code ) {
236            $data['fallback'][] = [ 'code' => $code ];
237        }
238        ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
239
240        if ( $contLangConverter->hasVariants() ) {
241            $data['variants'] = [];
242            foreach ( $contLangConverter->getVariants() as $code ) {
243                $data['variants'][] = [
244                    'code' => $code,
245                    'name' => $this->contentLanguage->getVariantname( $code ),
246                ];
247            }
248            ApiResult::setIndexedTagName( $data['variants'], 'lang' );
249        }
250
251        $data['rtl'] = $this->contentLanguage->isRTL();
252        $data['fallback8bitEncoding'] = $this->contentLanguage->fallback8bitEncoding();
253
254        $data['readonly'] = $this->readOnlyMode->isReadOnly();
255        if ( $data['readonly'] ) {
256            $data['readonlyreason'] = $this->readOnlyMode->getReason();
257        }
258        $data['writeapi'] = true; // Deprecated since MW 1.32
259
260        $data['maxarticlesize'] = $config->get( MainConfigNames::MaxArticleSize ) * 1024;
261
262        $data['timezone'] = $config->get( MainConfigNames::Localtimezone );
263        $data['timeoffset'] = (int)( $config->get( MainConfigNames::LocalTZoffset ) );
264        $data['articlepath'] = $config->get( MainConfigNames::ArticlePath );
265        $data['scriptpath'] = $config->get( MainConfigNames::ScriptPath );
266        $data['script'] = $config->get( MainConfigNames::Script );
267        $data['variantarticlepath'] = $config->get( MainConfigNames::VariantArticlePath );
268        $data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
269        $data['server'] = $config->get( MainConfigNames::Server );
270        $data['servername'] = $config->get( MainConfigNames::ServerName );
271        $data['wikiid'] = WikiMap::getCurrentWikiId();
272        $data['time'] = wfTimestamp( TS_ISO_8601, time() );
273
274        $data['misermode'] = (bool)$config->get( MainConfigNames::MiserMode );
275
276        $data['uploadsenabled'] = UploadBase::isEnabled();
277        $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
278        $data['minuploadchunksize'] = ApiUpload::getMinUploadChunkSize( $config );
279
280        $data['galleryoptions'] = $config->get( MainConfigNames::GalleryOptions );
281
282        $data['thumblimits'] = $config->get( MainConfigNames::ThumbLimits );
283        ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
284        ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
285        $data['imagelimits'] = [];
286        foreach ( $config->get( MainConfigNames::ImageLimits ) as $k => $limit ) {
287            $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
288        }
289        ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
290        ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
291
292        $favicon = $config->get( MainConfigNames::Favicon );
293        if ( $favicon ) {
294            // Expand any local path to full URL to improve API usability (T77093).
295            $data['favicon'] = (string)$this->urlUtils->expand( $favicon );
296        }
297
298        $data['centralidlookupprovider'] = $config->get( MainConfigNames::CentralIdLookupProvider );
299        $providerIds = array_keys( $config->get( MainConfigNames::CentralIdLookupProviders ) );
300        $data['allcentralidlookupproviders'] = $providerIds;
301
302        $data['interwikimagic'] = (bool)$config->get( MainConfigNames::InterwikiMagic );
303        $data['magiclinks'] = $config->get( MainConfigNames::EnableMagicLinks );
304
305        $data['categorycollation'] = $config->get( MainConfigNames::CategoryCollation );
306
307        $data['nofollowlinks'] = $config->get( MainConfigNames::NoFollowLinks );
308        $data['nofollownsexceptions'] = $config->get( MainConfigNames::NoFollowNsExceptions );
309        $data['nofollowdomainexceptions'] = $config->get( MainConfigNames::NoFollowDomainExceptions );
310        $data['externallinktarget'] = $config->get( MainConfigNames::ExternalLinkTarget );
311
312        $this->getHookRunner()->onAPIQuerySiteInfoGeneralInfo( $this, $data );
313
314        return $this->getResult()->addValue( 'query', $property, $data );
315    }
316
317    protected function appendNamespaces( string $property ): bool {
318        $nsProtection = $this->getConfig()->get( MainConfigNames::NamespaceProtection );
319
320        $data = [ ApiResult::META_TYPE => 'assoc' ];
321        foreach ( $this->contentLanguage->getFormattedNamespaces() as $ns => $title ) {
322            $data[$ns] = [
323                'id' => (int)$ns,
324                'case' => $this->namespaceInfo->isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
325            ];
326            ApiResult::setContentValue( $data[$ns], 'name', $title );
327            $canonical = $this->namespaceInfo->getCanonicalName( $ns );
328
329            $data[$ns]['subpages'] = $this->namespaceInfo->hasSubpages( $ns );
330
331            if ( $canonical ) {
332                $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
333            }
334
335            $data[$ns]['content'] = $this->namespaceInfo->isContent( $ns );
336            $data[$ns]['nonincludable'] = $this->namespaceInfo->isNonincludable( $ns );
337
338            $specificNs = $nsProtection[$ns] ?? '';
339            if ( is_array( $specificNs ) ) {
340                $specificNs = implode( "|", array_filter( $specificNs ) );
341            }
342            if ( $specificNs !== '' ) {
343                $data[$ns]['namespaceprotection'] = $specificNs;
344            }
345
346            $contentmodel = $this->namespaceInfo->getNamespaceContentModel( $ns );
347            if ( $contentmodel ) {
348                $data[$ns]['defaultcontentmodel'] = $contentmodel;
349            }
350        }
351
352        ApiResult::setArrayType( $data, 'assoc' );
353        ApiResult::setIndexedTagName( $data, 'ns' );
354
355        return $this->getResult()->addValue( 'query', $property, $data );
356    }
357
358    protected function appendNamespaceAliases( string $property ): bool {
359        $aliases = $this->contentLanguage->getNamespaceAliases();
360        $namespaces = $this->contentLanguage->getNamespaces();
361        $data = [];
362        foreach ( $aliases as $title => $ns ) {
363            if ( $namespaces[$ns] == $title ) {
364                // Don't list duplicates
365                continue;
366            }
367            $item = [ 'id' => (int)$ns ];
368            ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
369            $data[] = $item;
370        }
371
372        sort( $data );
373
374        ApiResult::setIndexedTagName( $data, 'ns' );
375
376        return $this->getResult()->addValue( 'query', $property, $data );
377    }
378
379    protected function appendSpecialPageAliases( string $property ): bool {
380        $data = [];
381        $aliases = $this->contentLanguage->getSpecialPageAliases();
382        foreach ( $this->specialPageFactory->getNames() as $specialpage ) {
383            if ( isset( $aliases[$specialpage] ) ) {
384                $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
385                ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
386                $data[] = $arr;
387            }
388        }
389        ApiResult::setIndexedTagName( $data, 'specialpage' );
390
391        return $this->getResult()->addValue( 'query', $property, $data );
392    }
393
394    protected function appendMagicWords( string $property ): bool {
395        $data = [];
396        foreach ( $this->contentLanguage->getMagicWords() as $name => $aliases ) {
397            $caseSensitive = (bool)array_shift( $aliases );
398            $arr = [
399                'name' => $name,
400                'aliases' => $aliases,
401                'case-sensitive' => $caseSensitive,
402            ];
403            ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
404            $data[] = $arr;
405        }
406        ApiResult::setIndexedTagName( $data, 'magicword' );
407
408        return $this->getResult()->addValue( 'query', $property, $data );
409    }
410
411    protected function appendInterwikiMap( string $property, ?string $filter ): bool {
412        $local = $filter ? $filter === 'local' : null;
413
414        $params = $this->extractRequestParams();
415        $langCode = $params['inlanguagecode'] ?? '';
416        $interwikiMagic = $this->getConfig()->get( MainConfigNames::InterwikiMagic );
417
418        if ( $interwikiMagic ) {
419            $langNames = $this->languageNameUtils->getLanguageNames( $langCode );
420        }
421
422        $extraLangPrefixes = $this->getConfig()->get( MainConfigNames::ExtraInterlanguageLinkPrefixes );
423        $extraLangCodeMap = $this->getConfig()->get( MainConfigNames::InterlanguageLinkCodeMap );
424        $localInterwikis = $this->getConfig()->get( MainConfigNames::LocalInterwikis );
425        $data = [];
426
427        foreach ( $this->interwikiLookup->getAllPrefixes( $local ) as $row ) {
428            $prefix = $row['iw_prefix'];
429            $val = [];
430            $val['prefix'] = $prefix;
431            if ( $row['iw_local'] ?? false ) {
432                $val['local'] = true;
433            }
434            if ( $row['iw_trans'] ?? false ) {
435                $val['trans'] = true;
436            }
437
438            if ( $interwikiMagic && isset( $langNames[$prefix] ) ) {
439                $val['language'] = $langNames[$prefix];
440                $standard = LanguageCode::replaceDeprecatedCodes( $prefix );
441                if ( $standard !== $prefix ) {
442                    # Note that even if this code is deprecated, it should
443                    # only be remapped if extralanglink (set below) is false.
444                    $val['deprecated'] = $standard;
445                }
446                $val['bcp47'] = LanguageCode::bcp47( $standard );
447            }
448            if ( in_array( $prefix, $localInterwikis ) ) {
449                $val['localinterwiki'] = true;
450            }
451            if ( $interwikiMagic && in_array( $prefix, $extraLangPrefixes ) ) {
452                $val['extralanglink'] = true;
453                $val['code'] = $extraLangCodeMap[$prefix] ?? $prefix;
454                $val['bcp47'] = LanguageCode::bcp47( $val['code'] );
455
456                $linktext = $this->msg( "interlanguage-link-$prefix" );
457                if ( !$linktext->isDisabled() ) {
458                    $val['linktext'] = $linktext->text();
459                }
460
461                $sitename = $this->msg( "interlanguage-link-sitename-$prefix" );
462                if ( !$sitename->isDisabled() ) {
463                    $val['sitename'] = $sitename->text();
464                }
465            }
466
467            $val['url'] = (string)$this->urlUtils->expand( $row['iw_url'], PROTO_CURRENT );
468            $val['protorel'] = str_starts_with( $row['iw_url'], '//' );
469            if ( ( $row['iw_wikiid'] ?? '' ) !== '' ) {
470                $val['wikiid'] = $row['iw_wikiid'];
471            }
472            if ( ( $row['iw_api'] ?? '' ) !== '' ) {
473                $val['api'] = $row['iw_api'];
474            }
475
476            $data[] = $val;
477        }
478
479        ApiResult::setIndexedTagName( $data, 'iw' );
480
481        return $this->getResult()->addValue( 'query', $property, $data );
482    }
483
484    protected function appendDbReplLagInfo( string $property, bool $includeAll ): bool {
485        $data = [];
486        $showHostnames = $this->getConfig()->get( MainConfigNames::ShowHostnames );
487        if ( $includeAll ) {
488            if ( !$showHostnames ) {
489                $this->dieWithError( 'apierror-siteinfo-includealldenied', 'includeAllDenied' );
490            }
491
492            foreach ( $this->loadBalancer->getLagTimes() as $i => $lag ) {
493                $data[] = [
494                    'host' => $this->loadBalancer->getServerName( $i ),
495                    'lag' => $lag
496                ];
497            }
498        } else {
499            [ , $lag, $index ] = $this->loadBalancer->getMaxLag();
500            $data[] = [
501                'host' => $showHostnames ? $this->loadBalancer->getServerName( $index ) : '',
502                'lag' => $lag
503            ];
504        }
505
506        ApiResult::setIndexedTagName( $data, 'db' );
507
508        return $this->getResult()->addValue( 'query', $property, $data );
509    }
510
511    protected function appendStatistics( string $property ): bool {
512        $data = [
513            'pages' => SiteStats::pages(),
514            'articles' => SiteStats::articles(),
515            'edits' => SiteStats::edits(),
516            'images' => SiteStats::images(),
517            'users' => SiteStats::users(),
518            'activeusers' => SiteStats::activeUsers(),
519            'admins' => SiteStats::numberingroup( 'sysop' ),
520            'jobs' => SiteStats::jobs(),
521        ];
522
523        $this->getHookRunner()->onAPIQuerySiteInfoStatisticsInfo( $data );
524
525        return $this->getResult()->addValue( 'query', $property, $data );
526    }
527
528    protected function appendUserGroups( string $property, bool $numberInGroup ): bool {
529        $config = $this->getConfig();
530
531        $data = [];
532        $result = $this->getResult();
533        $allGroups = $this->userGroupManager->listAllGroups();
534        $allImplicitGroups = $this->userGroupManager->listAllImplicitGroups();
535        foreach ( array_merge( $allImplicitGroups, $allGroups ) as $group ) {
536            $arr = [
537                'name' => $group,
538                'rights' => $this->groupPermissionsLookup->getGrantedPermissions( $group ),
539                // TODO: Also expose the list of revoked permissions somehow.
540            ];
541
542            if ( $numberInGroup ) {
543                $autopromote = $config->get( MainConfigNames::Autopromote );
544
545                if ( $group == 'user' ) {
546                    $arr['number'] = SiteStats::users();
547                // '*' and autopromote groups have no size
548                } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
549                    $arr['number'] = SiteStats::numberingroup( $group );
550                }
551            }
552
553            $groupArr = $this->userGroupManager->getGroupsChangeableByGroup( $group );
554
555            foreach ( $groupArr as $type => $groups ) {
556                $groups = array_values( array_intersect( $groups, $allGroups ) );
557                if ( $groups ) {
558                    $arr[$type] = $groups;
559                    ApiResult::setArrayType( $arr[$type], 'BCarray' );
560                    ApiResult::setIndexedTagName( $arr[$type], 'group' );
561                }
562            }
563
564            ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
565            $data[] = $arr;
566        }
567
568        ApiResult::setIndexedTagName( $data, 'group' );
569
570        return $result->addValue( 'query', $property, $data );
571    }
572
573    protected function appendAutoCreateTempUser( string $property ): bool {
574        $data = [ 'enabled' => $this->tempUserConfig->isEnabled() ];
575        if ( $this->tempUserConfig->isKnown() ) {
576            $data['matchPatterns'] = $this->tempUserConfig->getMatchPatterns();
577        }
578        return $this->getResult()->addValue( 'query', $property, $data );
579    }
580
581    protected function appendFileExtensions( string $property ): bool {
582        $data = [];
583        foreach (
584            array_unique( $this->getConfig()->get( MainConfigNames::FileExtensions ) ) as $ext
585        ) {
586            $data[] = [ 'ext' => $ext ];
587        }
588        ApiResult::setIndexedTagName( $data, 'fe' );
589
590        return $this->getResult()->addValue( 'query', $property, $data );
591    }
592
593    protected function appendInstalledClientLibraries( string $property ): bool {
594        $data = [];
595        foreach ( SpecialVersion::parseForeignResources() as $name => $info ) {
596            $data[] = [
597                // Can't use $name as it is version suffixed (as multiple versions
598                // of a library may exist, provided by different skins/extensions)
599                'name' => $info['name'],
600                'version' => $info['version'],
601            ];
602        }
603        ApiResult::setIndexedTagName( $data, 'library' );
604        return $this->getResult()->addValue( 'query', $property, $data );
605    }
606
607    protected function appendInstalledLibraries( string $property ): bool {
608        $credits = SpecialVersion::getCredits(
609            ExtensionRegistry::getInstance(),
610            $this->getConfig()
611        );
612        $data = [];
613        foreach ( SpecialVersion::parseComposerInstalled( $credits ) as $name => $info ) {
614            if ( str_starts_with( $info['type'], 'mediawiki-' ) ) {
615                // Skip any extensions or skins since they'll be listed
616                // in their proper section
617                continue;
618            }
619            $data[] = [
620                'name' => $name,
621                'version' => $info['version'],
622            ];
623        }
624        ApiResult::setIndexedTagName( $data, 'library' );
625
626        return $this->getResult()->addValue( 'query', $property, $data );
627    }
628
629    protected function appendExtensions( string $property ): bool {
630        $data = [];
631        $credits = SpecialVersion::getCredits(
632            ExtensionRegistry::getInstance(),
633            $this->getConfig()
634        );
635        foreach ( $credits as $type => $extensions ) {
636            foreach ( $extensions as $ext ) {
637                $ret = [ 'type' => $type ];
638                if ( isset( $ext['name'] ) ) {
639                    $ret['name'] = $ext['name'];
640                }
641                if ( isset( $ext['namemsg'] ) ) {
642                    $ret['namemsg'] = $ext['namemsg'];
643                }
644                if ( isset( $ext['description'] ) ) {
645                    $ret['description'] = $ext['description'];
646                }
647                if ( isset( $ext['descriptionmsg'] ) ) {
648                    // Can be a string or [ key, param1, param2, ... ]
649                    if ( is_array( $ext['descriptionmsg'] ) ) {
650                        $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
651                        $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
652                        ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
653                    } else {
654                        $ret['descriptionmsg'] = $ext['descriptionmsg'];
655                    }
656                }
657                if ( isset( $ext['author'] ) ) {
658                    $ret['author'] = is_array( $ext['author'] ) ?
659                        implode( ', ', $ext['author'] ) : $ext['author'];
660                }
661                if ( isset( $ext['url'] ) ) {
662                    $ret['url'] = $ext['url'];
663                }
664                if ( isset( $ext['version'] ) ) {
665                    $ret['version'] = $ext['version'];
666                }
667                if ( isset( $ext['path'] ) ) {
668                    $extensionPath = dirname( $ext['path'] );
669                    $gitInfo = new GitInfo( $extensionPath );
670                    $vcsVersion = $gitInfo->getHeadSHA1();
671                    if ( $vcsVersion !== false ) {
672                        $ret['vcs-system'] = 'git';
673                        $ret['vcs-version'] = $vcsVersion;
674                        $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
675                        $vcsDate = $gitInfo->getHeadCommitDate();
676                        if ( $vcsDate !== false ) {
677                            $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
678                        }
679                    }
680
681                    if ( ExtensionInfo::getLicenseFileNames( $extensionPath ) ) {
682                        $ret['license-name'] = $ext['license-name'] ?? '';
683                        $ret['license'] = SpecialPage::getTitleFor(
684                            'Version',
685                            "License/{$ext['name']}"
686                        )->getLinkURL();
687                    }
688
689                    if ( ExtensionInfo::getAuthorsFileName( $extensionPath ) ) {
690                        $ret['credits'] = SpecialPage::getTitleFor(
691                            'Version',
692                            "Credits/{$ext['name']}"
693                        )->getLinkURL();
694                    }
695                }
696                $data[] = $ret;
697            }
698        }
699
700        ApiResult::setIndexedTagName( $data, 'ext' );
701
702        return $this->getResult()->addValue( 'query', $property, $data );
703    }
704
705    protected function appendRightsInfo( string $property ): bool {
706        $config = $this->getConfig();
707        $title = Title::newFromText( $config->get( MainConfigNames::RightsPage ) );
708        if ( $title ) {
709            $url = $this->urlUtils->expand( $title->getLinkURL(), PROTO_CURRENT );
710        } else {
711            $url = $config->get( MainConfigNames::RightsUrl );
712        }
713        $text = $config->get( MainConfigNames::RightsText ) ?? '';
714        if ( $text === '' && $title ) {
715            $text = $title->getPrefixedText();
716        }
717
718        $data = [
719            'url' => (string)$url,
720            'text' => (string)$text,
721        ];
722
723        return $this->getResult()->addValue( 'query', $property, $data );
724    }
725
726    protected function appendRestrictions( string $property ): bool {
727        $config = $this->getConfig();
728        $data = [
729            'types' => $config->get( MainConfigNames::RestrictionTypes ),
730            'levels' => $config->get( MainConfigNames::RestrictionLevels ),
731            'cascadinglevels' => $config->get( MainConfigNames::CascadingRestrictionLevels ),
732            'semiprotectedlevels' => $config->get( MainConfigNames::SemiprotectedRestrictionLevels ),
733        ];
734
735        ApiResult::setArrayType( $data['types'], 'BCarray' );
736        ApiResult::setArrayType( $data['levels'], 'BCarray' );
737        ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
738        ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
739
740        ApiResult::setIndexedTagName( $data['types'], 'type' );
741        ApiResult::setIndexedTagName( $data['levels'], 'level' );
742        ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
743        ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
744
745        return $this->getResult()->addValue( 'query', $property, $data );
746    }
747
748    public function appendLanguages( string $property ): bool {
749        $params = $this->extractRequestParams();
750        $langCode = $params['inlanguagecode'] ?? '';
751        $langNames = $this->languageNameUtils->getLanguageNames( $langCode );
752
753        $data = [];
754
755        foreach ( $langNames as $code => $name ) {
756            $lang = [
757                'code' => $code,
758                'bcp47' => LanguageCode::bcp47( $code ),
759            ];
760            ApiResult::setContentValue( $lang, 'name', $name );
761            $data[] = $lang;
762        }
763        ApiResult::setIndexedTagName( $data, 'lang' );
764
765        return $this->getResult()->addValue( 'query', $property, $data );
766    }
767
768    // Export information about which page languages will trigger
769    // language conversion. (T153341)
770    public function appendLanguageVariants( string $property ): bool {
771        $langNames = $this->languageConverterFactory->isConversionDisabled() ? [] :
772            LanguageConverter::$languagesWithVariants;
773        sort( $langNames );
774
775        $data = [];
776        foreach ( $langNames as $langCode ) {
777            $lang = $this->languageFactory->getLanguage( $langCode );
778            $langConverter = $this->languageConverterFactory->getLanguageConverter( $lang );
779            if ( !$langConverter->hasVariants() ) {
780                // Only languages which have variants should be listed
781                continue;
782            }
783            $data[$langCode] = [];
784            ApiResult::setIndexedTagName( $data[$langCode], 'variant' );
785            ApiResult::setArrayType( $data[$langCode], 'kvp', 'code' );
786
787            $variants = $langConverter->getVariants();
788            sort( $variants );
789            foreach ( $variants as $v ) {
790                $data[$langCode][$v] = [
791                    'fallbacks' => (array)$langConverter->getVariantFallbacks( $v ),
792                ];
793                ApiResult::setIndexedTagName(
794                    $data[$langCode][$v]['fallbacks'], 'variant'
795                );
796            }
797        }
798        ApiResult::setIndexedTagName( $data, 'lang' );
799        ApiResult::setArrayType( $data, 'kvp', 'code' );
800
801        return $this->getResult()->addValue( 'query', $property, $data );
802    }
803
804    public function appendSkins( string $property ): bool {
805        $data = [];
806        $allowed = $this->skinFactory->getAllowedSkins();
807        $default = Skin::normalizeKey( 'default' );
808
809        foreach ( $this->skinFactory->getInstalledSkins() as $name => $displayName ) {
810            $msg = $this->msg( "skinname-{$name}" );
811            $code = $this->getParameter( 'inlanguagecode' );
812            if ( $code && $this->languageNameUtils->isValidCode( $code ) ) {
813                $msg->inLanguage( $code );
814            } else {
815                $msg->inContentLanguage();
816            }
817            if ( $msg->exists() ) {
818                $displayName = $msg->text();
819            }
820            $skin = [ 'code' => $name ];
821            ApiResult::setContentValue( $skin, 'name', $displayName );
822            if ( !isset( $allowed[$name] ) ) {
823                $skin['unusable'] = true;
824            }
825            if ( $name === $default ) {
826                $skin['default'] = true;
827            }
828            $data[] = $skin;
829        }
830        ApiResult::setIndexedTagName( $data, 'skin' );
831
832        return $this->getResult()->addValue( 'query', $property, $data );
833    }
834
835    public function appendExtensionTags( string $property ): bool {
836        $tags = array_map(
837            static function ( $item ) {
838                return "<$item>";
839            },
840            $this->parserFactory->getMainInstance()->getTags()
841        );
842        ApiResult::setArrayType( $tags, 'BCarray' );
843        ApiResult::setIndexedTagName( $tags, 't' );
844
845        return $this->getResult()->addValue( 'query', $property, $tags );
846    }
847
848    public function appendFunctionHooks( string $property ): bool {
849        $hooks = $this->parserFactory->getMainInstance()->getFunctionHooks();
850        ApiResult::setArrayType( $hooks, 'BCarray' );
851        ApiResult::setIndexedTagName( $hooks, 'h' );
852
853        return $this->getResult()->addValue( 'query', $property, $hooks );
854    }
855
856    public function appendVariables( string $property ): bool {
857        $variables = $this->magicWordFactory->getVariableIDs();
858        ApiResult::setArrayType( $variables, 'BCarray' );
859        ApiResult::setIndexedTagName( $variables, 'v' );
860
861        return $this->getResult()->addValue( 'query', $property, $variables );
862    }
863
864    public function appendDoubleUnderscores( string $property ): bool {
865        $ids = $this->magicWordFactory->getDoubleUnderscoreArray()->getNames();
866        ApiResult::setArrayType( $ids, 'BCarray' );
867        ApiResult::setIndexedTagName( $ids, 'v' );
868
869        return $this->getResult()->addValue( 'query', $property, $ids );
870    }
871
872    public function appendProtocols( string $property ): bool {
873        // Make a copy of the global so we don't try to set the _element key of it - T47130
874        $protocols = array_values( $this->getConfig()->get( MainConfigNames::UrlProtocols ) );
875        ApiResult::setArrayType( $protocols, 'BCarray' );
876        ApiResult::setIndexedTagName( $protocols, 'p' );
877
878        return $this->getResult()->addValue( 'query', $property, $protocols );
879    }
880
881    public function appendDefaultOptions( string $property ): bool {
882        $options = $this->userOptionsLookup->getDefaultOptions( null );
883        $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
884        return $this->getResult()->addValue( 'query', $property, $options );
885    }
886
887    public function appendUploadDialog( string $property ): bool {
888        $config = $this->getConfig()->get( MainConfigNames::UploadDialog );
889        return $this->getResult()->addValue( 'query', $property, $config );
890    }
891
892    private function getAutoPromoteConds(): array {
893        $allowedConditions = [];
894        foreach ( get_defined_constants() as $constantName => $constantValue ) {
895            if ( str_contains( $constantName, 'APCOND_' ) ) {
896                $allowedConditions[$constantName] = $constantValue;
897            }
898        }
899        return $allowedConditions;
900    }
901
902    private function processAutoPromote( array $input, array $allowedConditions ): array {
903        $data = [];
904        foreach ( $input as $groupName => $conditions ) {
905            $row = $this->recAutopromote( $conditions, $allowedConditions );
906            if ( !isset( $row[0] ) || is_string( $row ) ) {
907                $row = [ $row ];
908            }
909            $data[$groupName] = $row;
910        }
911        return $data;
912    }
913
914    private function appendAutoPromote( string $property ): bool {
915        return $this->getResult()->addValue(
916            'query',
917            $property,
918            $this->processAutoPromote(
919                $this->getConfig()->get( MainConfigNames::Autopromote ),
920                $this->getAutoPromoteConds()
921            )
922        );
923    }
924
925    private function appendAutoPromoteOnce( string $property ): bool {
926        $allowedConditions = $this->getAutoPromoteConds();
927        $data = [];
928        foreach ( $this->getConfig()->get( MainConfigNames::AutopromoteOnce ) as $key => $value ) {
929            $data[$key] = $this->processAutoPromote( $value, $allowedConditions );
930        }
931        return $this->getResult()->addValue( 'query', $property, $data );
932    }
933
934    private function appendCopyUploadDomains( string $property ): bool {
935        $allowedHosts = UploadFromUrl::getAllowedHosts();
936        ApiResult::setIndexedTagName( $allowedHosts, 'domain' );
937        return $this->getResult()->addValue(
938            'query',
939            $property,
940            $allowedHosts
941        );
942    }
943
944    /**
945     * @param array|int|string $cond
946     * @param array $allowedConditions
947     * @return array|string
948     */
949    private function recAutopromote( $cond, $allowedConditions ) {
950        $config = [];
951        // First, checks if $cond is an array
952        if ( is_array( $cond ) ) {
953            // Checks if $cond[0] is a valid operand
954            if ( in_array( $cond[0], UserRequirementsConditionChecker::VALID_OPS, true ) ) {
955                $config['operand'] = $cond[0];
956                // Traversal checks conditions
957                foreach ( array_slice( $cond, 1 ) as $value ) {
958                    $config[] = $this->recAutopromote( $value, $allowedConditions );
959                }
960            } elseif ( is_string( $cond[0] ) ) {
961                // Returns $cond directly, if $cond[0] is a string
962                $config = $cond;
963            } else {
964                // When $cond is equal to an APCOND_ constant value
965                $params = array_slice( $cond, 1 );
966                if ( $params === [ null ] ) {
967                    // Special casing for these conditions and their default of null,
968                    // to replace their values with $wgAutoConfirmCount/$wgAutoConfirmAge as appropriate
969                    if ( $cond[0] === APCOND_EDITCOUNT ) {
970                        $params = [ $this->getConfig()->get( MainConfigNames::AutoConfirmCount ) ];
971                    } elseif ( $cond[0] === APCOND_AGE ) {
972                        $params = [ $this->getConfig()->get( MainConfigNames::AutoConfirmAge ) ];
973                    }
974                }
975                $config = [
976                    'condname' => array_search( $cond[0], $allowedConditions ),
977                    'params' => $params
978                ];
979                ApiResult::setIndexedTagName( $config, 'params' );
980            }
981        } elseif ( is_string( $cond ) ) {
982            $config = $cond;
983        } else {
984            // When $cond is equal to an APCOND_ constant value
985            $config = [
986                'condname' => array_search( $cond, $allowedConditions ),
987                'params' => []
988            ];
989            ApiResult::setIndexedTagName( $config, 'params' );
990        }
991
992        return $config;
993    }
994
995    public function appendSubscribedHooks( string $property ): bool {
996        $hookNames = $this->hookContainer->getHookNames();
997        sort( $hookNames );
998
999        $data = [];
1000        foreach ( $hookNames as $name ) {
1001            $arr = [
1002                'name' => $name,
1003                'subscribers' => $this->hookContainer->getHandlerDescriptions( $name ),
1004            ];
1005
1006            ApiResult::setArrayType( $arr['subscribers'], 'array' );
1007            ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
1008            $data[] = $arr;
1009        }
1010
1011        ApiResult::setIndexedTagName( $data, 'hook' );
1012
1013        return $this->getResult()->addValue( 'query', $property, $data );
1014    }
1015
1016    /** @inheritDoc */
1017    public function getCacheMode( $params ) {
1018        // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
1019        if ( $this->getConfig()->get( MainConfigNames::ExtraInterlanguageLinkPrefixes ) &&
1020            in_array( 'interwikimap', $params['prop'] ?? [] )
1021        ) {
1022            return 'anon-public-user-private';
1023        }
1024
1025        return 'public';
1026    }
1027
1028    /** @inheritDoc */
1029    public function getAllowedParams() {
1030        return [
1031            'prop' => [
1032                ParamValidator::PARAM_DEFAULT => 'general',
1033                ParamValidator::PARAM_ISMULTI => true,
1034                ParamValidator::PARAM_TYPE => [
1035                    'general',
1036                    'namespaces',
1037                    'namespacealiases',
1038                    'specialpagealiases',
1039                    'magicwords',
1040                    'interwikimap',
1041                    'dbrepllag',
1042                    'statistics',
1043                    'usergroups',
1044                    'autocreatetempuser',
1045                    'clientlibraries',
1046                    'libraries',
1047                    'extensions',
1048                    'fileextensions',
1049                    'rightsinfo',
1050                    'restrictions',
1051                    'languages',
1052                    'languagevariants',
1053                    'skins',
1054                    'extensiontags',
1055                    'functionhooks',
1056                    'showhooks',
1057                    'variables',
1058                    'doubleunderscores',
1059                    'protocols',
1060                    'defaultoptions',
1061                    'uploaddialog',
1062                    'autopromote',
1063                    'autopromoteonce',
1064                    'copyuploaddomains',
1065                ],
1066                ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
1067            ],
1068            'filteriw' => [
1069                ParamValidator::PARAM_TYPE => [
1070                    'local',
1071                    '!local',
1072                ]
1073            ],
1074            'showalldb' => false,
1075            'numberingroup' => false,
1076            'inlanguagecode' => null,
1077        ];
1078    }
1079
1080    /** @inheritDoc */
1081    protected function getExamplesMessages() {
1082        return [
1083            'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
1084                => 'apihelp-query+siteinfo-example-simple',
1085            'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
1086                => 'apihelp-query+siteinfo-example-interwiki',
1087            'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
1088                => 'apihelp-query+siteinfo-example-replag',
1089        ];
1090    }
1091
1092    /** @inheritDoc */
1093    public function getHelpUrls() {
1094        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Siteinfo';
1095    }
1096}
1097
1098/** @deprecated class alias since 1.43 */
1099class_alias( ApiQuerySiteinfo::class, 'ApiQuerySiteinfo' );