Merge "Special:Upload should not crash on failing previews"
[mediawiki.git] / includes / api / ApiQuerySiteinfo.php
blob5093608297d91972934e8c74ceba33d24ad60128
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
26 use MediaWiki\MediaWikiServices;
28 /**
29 * A query action to return meta information about the wiki site.
31 * @ingroup API
33 class ApiQuerySiteinfo extends ApiQueryBase {
35 public function __construct( ApiQuery $query, $moduleName ) {
36 parent::__construct( $query, $moduleName, 'si' );
39 public function execute() {
40 $params = $this->extractRequestParams();
41 $done = [];
42 $fit = false;
43 foreach ( $params['prop'] as $p ) {
44 switch ( $p ) {
45 case 'general':
46 $fit = $this->appendGeneralInfo( $p );
47 break;
48 case 'namespaces':
49 $fit = $this->appendNamespaces( $p );
50 break;
51 case 'namespacealiases':
52 $fit = $this->appendNamespaceAliases( $p );
53 break;
54 case 'specialpagealiases':
55 $fit = $this->appendSpecialPageAliases( $p );
56 break;
57 case 'magicwords':
58 $fit = $this->appendMagicWords( $p );
59 break;
60 case 'interwikimap':
61 $filteriw = isset( $params['filteriw'] ) ? $params['filteriw'] : false;
62 $fit = $this->appendInterwikiMap( $p, $filteriw );
63 break;
64 case 'dbrepllag':
65 $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
66 break;
67 case 'statistics':
68 $fit = $this->appendStatistics( $p );
69 break;
70 case 'usergroups':
71 $fit = $this->appendUserGroups( $p, $params['numberingroup'] );
72 break;
73 case 'libraries':
74 $fit = $this->appendInstalledLibraries( $p );
75 break;
76 case 'extensions':
77 $fit = $this->appendExtensions( $p );
78 break;
79 case 'fileextensions':
80 $fit = $this->appendFileExtensions( $p );
81 break;
82 case 'rightsinfo':
83 $fit = $this->appendRightsInfo( $p );
84 break;
85 case 'restrictions':
86 $fit = $this->appendRestrictions( $p );
87 break;
88 case 'languages':
89 $fit = $this->appendLanguages( $p );
90 break;
91 case 'skins':
92 $fit = $this->appendSkins( $p );
93 break;
94 case 'extensiontags':
95 $fit = $this->appendExtensionTags( $p );
96 break;
97 case 'functionhooks':
98 $fit = $this->appendFunctionHooks( $p );
99 break;
100 case 'showhooks':
101 $fit = $this->appendSubscribedHooks( $p );
102 break;
103 case 'variables':
104 $fit = $this->appendVariables( $p );
105 break;
106 case 'protocols':
107 $fit = $this->appendProtocols( $p );
108 break;
109 case 'defaultoptions':
110 $fit = $this->appendDefaultOptions( $p );
111 break;
112 case 'uploaddialog':
113 $fit = $this->appendUploadDialog( $p );
114 break;
115 default:
116 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
118 if ( !$fit ) {
119 // Abuse siprop as a query-continue parameter
120 // and set it to all unprocessed props
121 $this->setContinueEnumParameter( 'prop', implode( '|',
122 array_diff( $params['prop'], $done ) ) );
123 break;
125 $done[] = $p;
129 protected function appendGeneralInfo( $property ) {
130 global $wgContLang;
132 $config = $this->getConfig();
134 $data = [];
135 $mainPage = Title::newMainPage();
136 $data['mainpage'] = $mainPage->getPrefixedText();
137 $data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
138 $data['sitename'] = $config->get( 'Sitename' );
140 // wgLogo can either be a relative or an absolute path
141 // make sure we always return an absolute path
142 $data['logo'] = wfExpandUrl( $config->get( 'Logo' ), PROTO_RELATIVE );
144 $data['generator'] = "MediaWiki {$config->get( 'Version' )}";
146 $data['phpversion'] = PHP_VERSION;
147 $data['phpsapi'] = PHP_SAPI;
148 if ( defined( 'HHVM_VERSION' ) ) {
149 $data['hhvmversion'] = HHVM_VERSION;
151 $data['dbtype'] = $config->get( 'DBtype' );
152 $data['dbversion'] = $this->getDB()->getServerVersion();
154 $allowFrom = [ '' ];
155 $allowException = true;
156 if ( !$config->get( 'AllowExternalImages' ) ) {
157 $data['imagewhitelistenabled'] = (bool)$config->get( 'EnableImageWhitelist' );
158 $allowFrom = $config->get( 'AllowExternalImagesFrom' );
159 $allowException = !empty( $allowFrom );
161 if ( $allowException ) {
162 $data['externalimages'] = (array)$allowFrom;
163 ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
166 $data['langconversion'] = !$config->get( 'DisableLangConversion' );
167 $data['titleconversion'] = !$config->get( 'DisableTitleConversion' );
169 if ( $wgContLang->linkPrefixExtension() ) {
170 $linkPrefixCharset = $wgContLang->linkPrefixCharset();
171 $data['linkprefixcharset'] = $linkPrefixCharset;
172 // For backwards compatibility
173 $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
174 } else {
175 $data['linkprefixcharset'] = '';
176 $data['linkprefix'] = '';
179 $linktrail = $wgContLang->linkTrail();
180 $data['linktrail'] = $linktrail ?: '';
182 $data['legaltitlechars'] = Title::legalChars();
183 $data['invalidusernamechars'] = $config->get( 'InvalidUsernameCharacters' );
185 $data['allunicodefixes'] = (bool)$config->get( 'AllUnicodeFixes' );
186 $data['fixarabicunicode'] = (bool)$config->get( 'FixArabicUnicode' );
187 $data['fixmalayalamunicode'] = (bool)$config->get( 'FixMalayalamUnicode' );
189 global $IP;
190 $git = SpecialVersion::getGitHeadSha1( $IP );
191 if ( $git ) {
192 $data['git-hash'] = $git;
193 $data['git-branch'] =
194 SpecialVersion::getGitCurrentBranch( $GLOBALS['IP'] );
197 // 'case-insensitive' option is reserved for future
198 $data['case'] = $config->get( 'CapitalLinks' ) ? 'first-letter' : 'case-sensitive';
199 $data['lang'] = $config->get( 'LanguageCode' );
201 $fallbacks = [];
202 foreach ( $wgContLang->getFallbackLanguages() as $code ) {
203 $fallbacks[] = [ 'code' => $code ];
205 $data['fallback'] = $fallbacks;
206 ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
208 if ( $wgContLang->hasVariants() ) {
209 $variants = [];
210 foreach ( $wgContLang->getVariants() as $code ) {
211 $variants[] = [
212 'code' => $code,
213 'name' => $wgContLang->getVariantname( $code ),
216 $data['variants'] = $variants;
217 ApiResult::setIndexedTagName( $data['variants'], 'lang' );
220 $data['rtl'] = $wgContLang->isRTL();
221 $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
223 $data['readonly'] = wfReadOnly();
224 if ( $data['readonly'] ) {
225 $data['readonlyreason'] = wfReadOnlyReason();
227 $data['writeapi'] = (bool)$config->get( 'EnableWriteAPI' );
229 $data['maxarticlesize'] = $config->get( 'MaxArticleSize' ) * 1024;
231 $tz = $config->get( 'Localtimezone' );
232 $offset = $config->get( 'LocalTZoffset' );
233 if ( is_null( $tz ) ) {
234 $tz = 'UTC';
235 $offset = 0;
236 } elseif ( is_null( $offset ) ) {
237 $offset = 0;
239 $data['timezone'] = $tz;
240 $data['timeoffset'] = intval( $offset );
241 $data['articlepath'] = $config->get( 'ArticlePath' );
242 $data['scriptpath'] = $config->get( 'ScriptPath' );
243 $data['script'] = $config->get( 'Script' );
244 $data['variantarticlepath'] = $config->get( 'VariantArticlePath' );
245 $data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
246 $data['server'] = $config->get( 'Server' );
247 $data['servername'] = $config->get( 'ServerName' );
248 $data['wikiid'] = wfWikiID();
249 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
251 $data['misermode'] = (bool)$config->get( 'MiserMode' );
253 $data['uploadsenabled'] = UploadBase::isEnabled();
254 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
255 $data['minuploadchunksize'] = (int)$config->get( 'MinUploadChunkSize' );
257 $data['galleryoptions'] = $config->get( 'GalleryOptions' );
259 $data['thumblimits'] = $config->get( 'ThumbLimits' );
260 ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
261 ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
262 $data['imagelimits'] = [];
263 ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
264 ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
265 foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
266 $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
269 $favicon = $config->get( 'Favicon' );
270 if ( !empty( $favicon ) ) {
271 // wgFavicon can either be a relative or an absolute path
272 // make sure we always return an absolute path
273 $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
276 $data['centralidlookupprovider'] = $config->get( 'CentralIdLookupProvider' );
277 $providerIds = array_keys( $config->get( 'CentralIdLookupProviders' ) );
278 $data['allcentralidlookupproviders'] = $providerIds;
280 $data['interwikimagic'] = (bool)$config->get( 'InterwikiMagic' );
281 $data['magiclinks'] = $config->get( 'EnableMagicLinks' );
283 Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
285 return $this->getResult()->addValue( 'query', $property, $data );
288 protected function appendNamespaces( $property ) {
289 global $wgContLang;
290 $data = [
291 ApiResult::META_TYPE => 'assoc',
293 foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
294 $data[$ns] = [
295 'id' => intval( $ns ),
296 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
298 ApiResult::setContentValue( $data[$ns], 'name', $title );
299 $canonical = MWNamespace::getCanonicalName( $ns );
301 $data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
303 if ( $canonical ) {
304 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
307 $data[$ns]['content'] = MWNamespace::isContent( $ns );
308 $data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
310 $contentmodel = MWNamespace::getNamespaceContentModel( $ns );
311 if ( $contentmodel ) {
312 $data[$ns]['defaultcontentmodel'] = $contentmodel;
316 ApiResult::setArrayType( $data, 'assoc' );
317 ApiResult::setIndexedTagName( $data, 'ns' );
319 return $this->getResult()->addValue( 'query', $property, $data );
322 protected function appendNamespaceAliases( $property ) {
323 global $wgContLang;
324 $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
325 $wgContLang->getNamespaceAliases() );
326 $namespaces = $wgContLang->getNamespaces();
327 $data = [];
328 foreach ( $aliases as $title => $ns ) {
329 if ( $namespaces[$ns] == $title ) {
330 // Don't list duplicates
331 continue;
333 $item = [
334 'id' => intval( $ns )
336 ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
337 $data[] = $item;
340 sort( $data );
342 ApiResult::setIndexedTagName( $data, 'ns' );
344 return $this->getResult()->addValue( 'query', $property, $data );
347 protected function appendSpecialPageAliases( $property ) {
348 global $wgContLang;
349 $data = [];
350 $aliases = $wgContLang->getSpecialPageAliases();
351 foreach ( SpecialPageFactory::getNames() as $specialpage ) {
352 if ( isset( $aliases[$specialpage] ) ) {
353 $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
354 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
355 $data[] = $arr;
358 ApiResult::setIndexedTagName( $data, 'specialpage' );
360 return $this->getResult()->addValue( 'query', $property, $data );
363 protected function appendMagicWords( $property ) {
364 global $wgContLang;
365 $data = [];
366 foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
367 $caseSensitive = array_shift( $aliases );
368 $arr = [ 'name' => $magicword, 'aliases' => $aliases ];
369 $arr['case-sensitive'] = (bool)$caseSensitive;
370 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
371 $data[] = $arr;
373 ApiResult::setIndexedTagName( $data, 'magicword' );
375 return $this->getResult()->addValue( 'query', $property, $data );
378 protected function appendInterwikiMap( $property, $filter ) {
379 $local = null;
380 if ( $filter === 'local' ) {
381 $local = 1;
382 } elseif ( $filter === '!local' ) {
383 $local = 0;
384 } elseif ( $filter ) {
385 ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
388 $params = $this->extractRequestParams();
389 $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
390 $langNames = Language::fetchLanguageNames( $langCode );
392 $getPrefixes = MediaWikiServices::getInstance()->getInterwikiLookup()->getAllPrefixes( $local );
393 $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
394 $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
395 $data = [];
397 foreach ( $getPrefixes as $row ) {
398 $prefix = $row['iw_prefix'];
399 $val = [];
400 $val['prefix'] = $prefix;
401 if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
402 $val['local'] = true;
404 if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
405 $val['trans'] = true;
408 if ( isset( $langNames[$prefix] ) ) {
409 $val['language'] = $langNames[$prefix];
411 if ( in_array( $prefix, $localInterwikis ) ) {
412 $val['localinterwiki'] = true;
414 if ( in_array( $prefix, $extraLangPrefixes ) ) {
415 $val['extralanglink'] = true;
417 $linktext = wfMessage( "interlanguage-link-$prefix" );
418 if ( !$linktext->isDisabled() ) {
419 $val['linktext'] = $linktext->text();
422 $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
423 if ( !$sitename->isDisabled() ) {
424 $val['sitename'] = $sitename->text();
428 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
429 $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
430 if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
431 $val['wikiid'] = $row['iw_wikiid'];
433 if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
434 $val['api'] = $row['iw_api'];
437 $data[] = $val;
440 ApiResult::setIndexedTagName( $data, 'iw' );
442 return $this->getResult()->addValue( 'query', $property, $data );
445 protected function appendDbReplLagInfo( $property, $includeAll ) {
446 $data = [];
447 $lb = wfGetLB();
448 $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
449 if ( $includeAll ) {
450 if ( !$showHostnames ) {
451 $this->dieWithError( 'apierror-siteinfo-includealldenied', 'includeAllDenied' );
454 $lags = $lb->getLagTimes();
455 foreach ( $lags as $i => $lag ) {
456 $data[] = [
457 'host' => $lb->getServerName( $i ),
458 'lag' => $lag
461 } else {
462 list( , $lag, $index ) = $lb->getMaxLag();
463 $data[] = [
464 'host' => $showHostnames
465 ? $lb->getServerName( $index )
466 : '',
467 'lag' => intval( $lag )
471 ApiResult::setIndexedTagName( $data, 'db' );
473 return $this->getResult()->addValue( 'query', $property, $data );
476 protected function appendStatistics( $property ) {
477 $data = [];
478 $data['pages'] = intval( SiteStats::pages() );
479 $data['articles'] = intval( SiteStats::articles() );
480 $data['edits'] = intval( SiteStats::edits() );
481 $data['images'] = intval( SiteStats::images() );
482 $data['users'] = intval( SiteStats::users() );
483 $data['activeusers'] = intval( SiteStats::activeUsers() );
484 $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
485 $data['jobs'] = intval( SiteStats::jobs() );
487 Hooks::run( 'APIQuerySiteInfoStatisticsInfo', [ &$data ] );
489 return $this->getResult()->addValue( 'query', $property, $data );
492 protected function appendUserGroups( $property, $numberInGroup ) {
493 $config = $this->getConfig();
495 $data = [];
496 $result = $this->getResult();
497 $allGroups = array_values( User::getAllGroups() );
498 foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
499 $arr = [
500 'name' => $group,
501 'rights' => array_keys( $permissions, true ),
504 if ( $numberInGroup ) {
505 $autopromote = $config->get( 'Autopromote' );
507 if ( $group == 'user' ) {
508 $arr['number'] = SiteStats::users();
509 // '*' and autopromote groups have no size
510 } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
511 $arr['number'] = SiteStats::numberingroup( $group );
515 $groupArr = [
516 'add' => $config->get( 'AddGroups' ),
517 'remove' => $config->get( 'RemoveGroups' ),
518 'add-self' => $config->get( 'GroupsAddToSelf' ),
519 'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
522 foreach ( $groupArr as $type => $rights ) {
523 if ( isset( $rights[$group] ) ) {
524 if ( $rights[$group] === true ) {
525 $groups = $allGroups;
526 } else {
527 $groups = array_intersect( $rights[$group], $allGroups );
529 if ( $groups ) {
530 $arr[$type] = $groups;
531 ApiResult::setArrayType( $arr[$type], 'BCarray' );
532 ApiResult::setIndexedTagName( $arr[$type], 'group' );
537 ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
538 $data[] = $arr;
541 ApiResult::setIndexedTagName( $data, 'group' );
543 return $result->addValue( 'query', $property, $data );
546 protected function appendFileExtensions( $property ) {
547 $data = [];
548 foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
549 $data[] = [ 'ext' => $ext ];
551 ApiResult::setIndexedTagName( $data, 'fe' );
553 return $this->getResult()->addValue( 'query', $property, $data );
556 protected function appendInstalledLibraries( $property ) {
557 global $IP;
558 $path = "$IP/vendor/composer/installed.json";
559 if ( !file_exists( $path ) ) {
560 return true;
563 $data = [];
564 $installed = new ComposerInstalled( $path );
565 foreach ( $installed->getInstalledDependencies() as $name => $info ) {
566 if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
567 // Skip any extensions or skins since they'll be listed
568 // in their proper section
569 continue;
571 $data[] = [
572 'name' => $name,
573 'version' => $info['version'],
576 ApiResult::setIndexedTagName( $data, 'library' );
578 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';