]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blobdiff - includes/api/ApiQuerySiteinfo.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / api / ApiQuerySiteinfo.php
index 9e856888f9777b193799b8a6f1dce849cbe28c07..6b896c95ad9f6196d42d7b72a088a23a6011e4fb 100644 (file)
@@ -1,10 +1,10 @@
 <?php
 /**
- * API for MediaWiki 1.8+
+ *
  *
  * Created on Sep 25, 2006
  *
- * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * @file
  */
-
-if ( !defined( 'MEDIAWIKI' ) ) {
-       // Eclipse helper - will be ignored in production
-       require_once( 'ApiQueryBase.php' );
-}
+use MediaWiki\MediaWikiServices;
 
 /**
  * A query action to return meta information about the wiki site.
@@ -36,13 +32,14 @@ if ( !defined( 'MEDIAWIKI' ) ) {
  */
 class ApiQuerySiteinfo extends ApiQueryBase {
 
-       public function __construct( $query, $moduleName ) {
+       public function __construct( ApiQuery $query, $moduleName ) {
                parent::__construct( $query, $moduleName, 'si' );
        }
 
        public function execute() {
                $params = $this->extractRequestParams();
-               $done = array();
+               $done = [];
+               $fit = false;
                foreach ( $params['prop'] as $p ) {
                        switch ( $p ) {
                                case 'general':
@@ -73,6 +70,9 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                                case 'usergroups':
                                        $fit = $this->appendUserGroups( $p, $params['numberingroup'] );
                                        break;
+                               case 'libraries':
+                                       $fit = $this->appendInstalledLibraries( $p );
+                                       break;
                                case 'extensions':
                                        $fit = $this->appendExtensions( $p );
                                        break;
@@ -82,9 +82,39 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                                case 'rightsinfo':
                                        $fit = $this->appendRightsInfo( $p );
                                        break;
+                               case 'restrictions':
+                                       $fit = $this->appendRestrictions( $p );
+                                       break;
                                case 'languages':
                                        $fit = $this->appendLanguages( $p );
                                        break;
+                               case 'languagevariants':
+                                       $fit = $this->appendLanguageVariants( $p );
+                                       break;
+                               case 'skins':
+                                       $fit = $this->appendSkins( $p );
+                                       break;
+                               case 'extensiontags':
+                                       $fit = $this->appendExtensionTags( $p );
+                                       break;
+                               case 'functionhooks':
+                                       $fit = $this->appendFunctionHooks( $p );
+                                       break;
+                               case 'showhooks':
+                                       $fit = $this->appendSubscribedHooks( $p );
+                                       break;
+                               case 'variables':
+                                       $fit = $this->appendVariables( $p );
+                                       break;
+                               case 'protocols':
+                                       $fit = $this->appendProtocols( $p );
+                                       break;
+                               case 'defaultoptions':
+                                       $fit = $this->appendDefaultOptions( $p );
+                                       break;
+                               case 'uploaddialog':
+                                       $fit = $this->appendUploadDialog( $p );
+                                       break;
                                default:
                                        ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" );
                        }
@@ -92,7 +122,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                                // Abuse siprop as a query-continue parameter
                                // and set it to all unprocessed props
                                $this->setContinueEnumParameter( 'prop', implode( '|',
-                                               array_diff( $params['prop'], $done ) ) );
+                                       array_diff( $params['prop'], $done ) ) );
                                break;
                        }
                        $done[] = $p;
@@ -102,45 +132,107 @@ class ApiQuerySiteinfo extends ApiQueryBase {
        protected function appendGeneralInfo( $property ) {
                global $wgContLang;
 
-               $data = array();
+               $config = $this->getConfig();
+
+               $data = [];
                $mainPage = Title::newMainPage();
                $data['mainpage'] = $mainPage->getPrefixedText();
-               $data['base'] = $mainPage->getFullUrl();
-               $data['sitename'] = $GLOBALS['wgSitename'];
-               $data['generator'] = "MediaWiki {$GLOBALS['wgVersion']}";
-               $data['phpversion'] = phpversion();
-               $data['phpsapi'] = php_sapi_name();
-               $data['dbtype'] = $GLOBALS['wgDBtype'];
+               $data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
+               $data['sitename'] = $config->get( 'Sitename' );
+
+               // wgLogo can either be a relative or an absolute path
+               // make sure we always return an absolute path
+               $data['logo'] = wfExpandUrl( $config->get( 'Logo' ), PROTO_RELATIVE );
+
+               $data['generator'] = "MediaWiki {$config->get( 'Version' )}";
+
+               $data['phpversion'] = PHP_VERSION;
+               $data['phpsapi'] = PHP_SAPI;
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $data['hhvmversion'] = HHVM_VERSION;
+               }
+               $data['dbtype'] = $config->get( 'DBtype' );
                $data['dbversion'] = $this->getDB()->getServerVersion();
 
-               $svn = SpecialVersion::getSvnRevision( $GLOBALS['IP'] );
-               if ( $svn ) {
-                       $data['rev'] = $svn;
+               $allowFrom = [ '' ];
+               $allowException = true;
+               if ( !$config->get( 'AllowExternalImages' ) ) {
+                       $data['imagewhitelistenabled'] = (bool)$config->get( 'EnableImageWhitelist' );
+                       $allowFrom = $config->get( 'AllowExternalImagesFrom' );
+                       $allowException = !empty( $allowFrom );
+               }
+               if ( $allowException ) {
+                       $data['externalimages'] = (array)$allowFrom;
+                       ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
+               }
+
+               $data['langconversion'] = !$config->get( 'DisableLangConversion' );
+               $data['titleconversion'] = !$config->get( 'DisableTitleConversion' );
+
+               if ( $wgContLang->linkPrefixExtension() ) {
+                       $linkPrefixCharset = $wgContLang->linkPrefixCharset();
+                       $data['linkprefixcharset'] = $linkPrefixCharset;
+                       // For backwards compatibility
+                       $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
+               } else {
+                       $data['linkprefixcharset'] = '';
+                       $data['linkprefix'] = '';
+               }
+
+               $linktrail = $wgContLang->linkTrail();
+               $data['linktrail'] = $linktrail ?: '';
+
+               $data['legaltitlechars'] = Title::legalChars();
+               $data['invalidusernamechars'] = $config->get( 'InvalidUsernameCharacters' );
+
+               $data['allunicodefixes'] = (bool)$config->get( 'AllUnicodeFixes' );
+               $data['fixarabicunicode'] = (bool)$config->get( 'FixArabicUnicode' );
+               $data['fixmalayalamunicode'] = (bool)$config->get( 'FixMalayalamUnicode' );
+
+               global $IP;
+               $git = SpecialVersion::getGitHeadSha1( $IP );
+               if ( $git ) {
+                       $data['git-hash'] = $git;
+                       $data['git-branch'] =
+                               SpecialVersion::getGitCurrentBranch( $GLOBALS['IP'] );
                }
 
                // 'case-insensitive' option is reserved for future
-               $data['case'] = $GLOBALS['wgCapitalLinks'] ? 'first-letter' : 'case-sensitive';
+               $data['case'] = $config->get( 'CapitalLinks' ) ? 'first-letter' : 'case-sensitive';
+               $data['lang'] = $config->get( 'LanguageCode' );
 
-               if ( isset( $GLOBALS['wgRightsCode'] ) ) {
-                       $data['rightscode'] = $GLOBALS['wgRightsCode'];
+               $fallbacks = [];
+               foreach ( $wgContLang->getFallbackLanguages() as $code ) {
+                       $fallbacks[] = [ 'code' => $code ];
                }
-               $data['rights'] = $GLOBALS['wgRightsText'];
-               $data['lang'] = $GLOBALS['wgLanguageCode'];
-               if ( $wgContLang->isRTL() ) {
-                       $data['rtl'] = '';
+               $data['fallback'] = $fallbacks;
+               ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
+
+               if ( $wgContLang->hasVariants() ) {
+                       $variants = [];
+                       foreach ( $wgContLang->getVariants() as $code ) {
+                               $variants[] = [
+                                       'code' => $code,
+                                       'name' => $wgContLang->getVariantname( $code ),
+                               ];
+                       }
+                       $data['variants'] = $variants;
+                       ApiResult::setIndexedTagName( $data['variants'], 'lang' );
                }
+
+               $data['rtl'] = $wgContLang->isRTL();
                $data['fallback8bitEncoding'] = $wgContLang->fallback8bitEncoding();
 
-               if ( wfReadOnly() ) {
-                       $data['readonly'] = '';
+               $data['readonly'] = wfReadOnly();
+               if ( $data['readonly'] ) {
                        $data['readonlyreason'] = wfReadOnlyReason();
                }
-               if ( $GLOBALS['wgEnableWriteAPI'] ) {
-                       $data['writeapi'] = '';
-               }
+               $data['writeapi'] = (bool)$config->get( 'EnableWriteAPI' );
 
-               $tz = $GLOBALS['wgLocaltimezone'];
-               $offset = $GLOBALS['wgLocalTZoffset'];
+               $data['maxarticlesize'] = $config->get( 'MaxArticleSize' ) * 1024;
+
+               $tz = $config->get( 'Localtimezone' );
+               $offset = $config->get( 'LocalTZoffset' );
                if ( is_null( $tz ) ) {
                        $tz = 'UTC';
                        $offset = 0;
@@ -149,319 +241,647 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                }
                $data['timezone'] = $tz;
                $data['timeoffset'] = intval( $offset );
-               $data['articlepath'] = $GLOBALS['wgArticlePath'];
-               $data['scriptpath'] = $GLOBALS['wgScriptPath'];
-               $data['script'] = $GLOBALS['wgScript'];
-               $data['variantarticlepath'] = $GLOBALS['wgVariantArticlePath'];
-               $data['server'] = $GLOBALS['wgServer'];
+               $data['articlepath'] = $config->get( 'ArticlePath' );
+               $data['scriptpath'] = $config->get( 'ScriptPath' );
+               $data['script'] = $config->get( 'Script' );
+               $data['variantarticlepath'] = $config->get( 'VariantArticlePath' );
+               $data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
+               $data['server'] = $config->get( 'Server' );
+               $data['servername'] = $config->get( 'ServerName' );
                $data['wikiid'] = wfWikiID();
                $data['time'] = wfTimestamp( TS_ISO_8601, time() );
 
+               $data['misermode'] = (bool)$config->get( 'MiserMode' );
+
+               $data['uploadsenabled'] = UploadBase::isEnabled();
+               $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
+               $data['minuploadchunksize'] = (int)$config->get( 'MinUploadChunkSize' );
+
+               $data['galleryoptions'] = $config->get( 'GalleryOptions' );
+
+               $data['thumblimits'] = $config->get( 'ThumbLimits' );
+               ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
+               ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
+               $data['imagelimits'] = [];
+               ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
+               ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
+               foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
+                       $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
+               }
+
+               $favicon = $config->get( 'Favicon' );
+               if ( !empty( $favicon ) ) {
+                       // wgFavicon can either be a relative or an absolute path
+                       // make sure we always return an absolute path
+                       $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
+               }
+
+               $data['centralidlookupprovider'] = $config->get( 'CentralIdLookupProvider' );
+               $providerIds = array_keys( $config->get( 'CentralIdLookupProviders' ) );
+               $data['allcentralidlookupproviders'] = $providerIds;
+
+               $data['interwikimagic'] = (bool)$config->get( 'InterwikiMagic' );
+               $data['magiclinks'] = $config->get( 'EnableMagicLinks' );
+
+               Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendNamespaces( $property ) {
                global $wgContLang;
-               $data = array();
+               $data = [
+                       ApiResult::META_TYPE => 'assoc',
+               ];
                foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
-                       $data[$ns] = array(
+                       $data[$ns] = [
                                'id' => intval( $ns ),
                                'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
-                       );
-                       ApiResult::setContent( $data[$ns], $title );
+                       ];
+                       ApiResult::setContentValue( $data[$ns], 'name', $title );
                        $canonical = MWNamespace::getCanonicalName( $ns );
 
-                       if ( MWNamespace::hasSubpages( $ns ) ) {
-                               $data[$ns]['subpages'] = '';
-                       }
+                       $data[$ns]['subpages'] = MWNamespace::hasSubpages( $ns );
 
                        if ( $canonical ) {
                                $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
                        }
 
-                       if ( MWNamespace::isContent( $ns ) ) {
-                               $data[$ns]['content'] = '';
+                       $data[$ns]['content'] = MWNamespace::isContent( $ns );
+                       $data[$ns]['nonincludable'] = MWNamespace::isNonincludable( $ns );
+
+                       $contentmodel = MWNamespace::getNamespaceContentModel( $ns );
+                       if ( $contentmodel ) {
+                               $data[$ns]['defaultcontentmodel'] = $contentmodel;
                        }
                }
 
-               $this->getResult()->setIndexedTagName( $data, 'ns' );
+               ApiResult::setArrayType( $data, 'assoc' );
+               ApiResult::setIndexedTagName( $data, 'ns' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendNamespaceAliases( $property ) {
-               global $wgNamespaceAliases, $wgContLang;
-               $aliases = array_merge( $wgNamespaceAliases, $wgContLang->getNamespaceAliases() );
+               global $wgContLang;
+               $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
+                       $wgContLang->getNamespaceAliases() );
                $namespaces = $wgContLang->getNamespaces();
-               $data = array();
+               $data = [];
                foreach ( $aliases as $title => $ns ) {
                        if ( $namespaces[$ns] == $title ) {
                                // Don't list duplicates
                                continue;
                        }
-                       $item = array(
+                       $item = [
                                'id' => intval( $ns )
-                       );
-                       ApiResult::setContent( $item, strtr( $title, '_', ' ' ) );
+                       ];
+                       ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
                        $data[] = $item;
                }
 
-               $this->getResult()->setIndexedTagName( $data, 'ns' );
+               sort( $data );
+
+               ApiResult::setIndexedTagName( $data, 'ns' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendSpecialPageAliases( $property ) {
                global $wgContLang;
-               $data = array();
-               foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases )
-               {
-                       $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
-                       $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
-                       $data[] = $arr;
+               $data = [];
+               $aliases = $wgContLang->getSpecialPageAliases();
+               foreach ( SpecialPageFactory::getNames() as $specialpage ) {
+                       if ( isset( $aliases[$specialpage] ) ) {
+                               $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
+                               ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
+                               $data[] = $arr;
+                       }
                }
-               $this->getResult()->setIndexedTagName( $data, 'specialpage' );
+               ApiResult::setIndexedTagName( $data, 'specialpage' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendMagicWords( $property ) {
                global $wgContLang;
-               $data = array();
+               $data = [];
                foreach ( $wgContLang->getMagicWords() as $magicword => $aliases ) {
                        $caseSensitive = array_shift( $aliases );
-                       $arr = array( 'name' => $magicword, 'aliases' => $aliases );
-                       if ( $caseSensitive ) {
-                               $arr['case-sensitive'] = '';
-                       }
-                       $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
+                       $arr = [ 'name' => $magicword, 'aliases' => $aliases ];
+                       $arr['case-sensitive'] = (bool)$caseSensitive;
+                       ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
                        $data[] = $arr;
                }
-               $this->getResult()->setIndexedTagName( $data, 'magicword' );
+               ApiResult::setIndexedTagName( $data, 'magicword' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendInterwikiMap( $property, $filter ) {
-               $this->resetQueryParams();
-               $this->addTables( 'interwiki' );
-               $this->addFields( array( 'iw_prefix', 'iw_local', 'iw_url' ) );
-
+               $local = null;
                if ( $filter === 'local' ) {
-                       $this->addWhere( 'iw_local = 1' );
+                       $local = 1;
                } elseif ( $filter === '!local' ) {
-                       $this->addWhere( 'iw_local = 0' );
+                       $local = 0;
                } elseif ( $filter ) {
                        ApiBase::dieDebug( __METHOD__, "Unknown filter=$filter" );
                }
 
-               $this->addOption( 'ORDER BY', 'iw_prefix' );
+               $params = $this->extractRequestParams();
+               $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
+               $langNames = Language::fetchLanguageNames( $langCode );
+
+               $getPrefixes = MediaWikiServices::getInstance()->getInterwikiLookup()->getAllPrefixes( $local );
+               $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
+               $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
+               $data = [];
+
+               foreach ( $getPrefixes as $row ) {
+                       $prefix = $row['iw_prefix'];
+                       $val = [];
+                       $val['prefix'] = $prefix;
+                       if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
+                               $val['local'] = true;
+                       }
+                       if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
+                               $val['trans'] = true;
+                       }
+
+                       if ( isset( $langNames[$prefix] ) ) {
+                               $val['language'] = $langNames[$prefix];
+                       }
+                       if ( in_array( $prefix, $localInterwikis ) ) {
+                               $val['localinterwiki'] = true;
+                       }
+                       if ( in_array( $prefix, $extraLangPrefixes ) ) {
+                               $val['extralanglink'] = true;
+
+                               $linktext = wfMessage( "interlanguage-link-$prefix" );
+                               if ( !$linktext->isDisabled() ) {
+                                       $val['linktext'] = $linktext->text();
+                               }
 
-               $res = $this->select( __METHOD__ );
+                               $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
+                               if ( !$sitename->isDisabled() ) {
+                                       $val['sitename'] = $sitename->text();
+                               }
+                       }
 
-               $data = array();
-               $langNames = Language::getLanguageNames();
-               foreach ( $res as $row ) {
-                       $val = array();
-                       $val['prefix'] = $row->iw_prefix;
-                       if ( $row->iw_local == '1' ) {
-                               $val['local'] = '';
+                       $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
+                       $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
+                       if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
+                               $val['wikiid'] = $row['iw_wikiid'];
                        }
-                       // $val['trans'] = intval( $row->iw_trans ); // should this be exposed?
-                       if ( isset( $langNames[$row->iw_prefix] ) ) {
-                               $val['language'] = $langNames[$row->iw_prefix];
+                       if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
+                               $val['api'] = $row['iw_api'];
                        }
-                       $val['url'] = $row->iw_url;
 
                        $data[] = $val;
                }
 
-               $this->getResult()->setIndexedTagName( $data, 'iw' );
+               ApiResult::setIndexedTagName( $data, 'iw' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendDbReplLagInfo( $property, $includeAll ) {
-               global $wgShowHostnames;
-               $data = array();
+               $data = [];
+               $lb = wfGetLB();
+               $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
                if ( $includeAll ) {
-                       if ( !$wgShowHostnames ) {
-                               $this->dieUsage( 'Cannot view all servers info unless $wgShowHostnames is true', 'includeAllDenied' );
+                       if ( !$showHostnames ) {
+                               $this->dieWithError( 'apierror-siteinfo-includealldenied', 'includeAllDenied' );
                        }
 
-                       $lb = wfGetLB();
                        $lags = $lb->getLagTimes();
                        foreach ( $lags as $i => $lag ) {
-                               $data[] = array(
+                               $data[] = [
                                        'host' => $lb->getServerName( $i ),
                                        'lag' => $lag
-                               );
+                               ];
                        }
                } else {
-                       list( $host, $lag ) = wfGetLB()->getMaxLag();
-                       $data[] = array(
-                               'host' => $wgShowHostnames ? $host : '',
+                       list( , $lag, $index ) = $lb->getMaxLag();
+                       $data[] = [
+                               'host' => $showHostnames
+                                               ? $lb->getServerName( $index )
+                                               : '',
                                'lag' => intval( $lag )
-                       );
+                       ];
                }
 
-               $result = $this->getResult();
-               $result->setIndexedTagName( $data, 'db' );
+               ApiResult::setIndexedTagName( $data, 'db' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendStatistics( $property ) {
-               global $wgDisableCounters;
-               $data = array();
+               $data = [];
                $data['pages'] = intval( SiteStats::pages() );
                $data['articles'] = intval( SiteStats::articles() );
-               if ( !$wgDisableCounters ) {
-                       $data['views'] = intval( SiteStats::views() );
-               }
                $data['edits'] = intval( SiteStats::edits() );
                $data['images'] = intval( SiteStats::images() );
                $data['users'] = intval( SiteStats::users() );
                $data['activeusers'] = intval( SiteStats::activeUsers() );
                $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
                $data['jobs'] = intval( SiteStats::jobs() );
+
+               Hooks::run( 'APIQuerySiteInfoStatisticsInfo', [ &$data ] );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendUserGroups( $property, $numberInGroup ) {
-               global $wgGroupPermissions, $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf;
-               
-               $data = array();
-               foreach ( $wgGroupPermissions as $group => $permissions ) {
-                       $arr = array(
+               $config = $this->getConfig();
+
+               $data = [];
+               $result = $this->getResult();
+               $allGroups = array_values( User::getAllGroups() );
+               foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
+                       $arr = [
                                'name' => $group,
                                'rights' => array_keys( $permissions, true ),
-                       );
-                       
+                       ];
+
                        if ( $numberInGroup ) {
-                               global $wgAutopromote;
-                       
+                               $autopromote = $config->get( 'Autopromote' );
+
                                if ( $group == 'user' ) {
                                        $arr['number'] = SiteStats::users();
-                                       
                                // '*' and autopromote groups have no size
-                               } elseif ( $group !== '*' && !isset( $wgAutopromote[$group] ) ) {
-                                       $arr['number'] = SiteStats::numberInGroup( $group );
+                               } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
+                                       $arr['number'] = SiteStats::numberingroup( $group );
                                }
                        }
-                       
-                       $groupArr = array(
-                               'add' => $wgAddGroups,
-                               'remove' => $wgRemoveGroups,
-                               'add-self' => $wgGroupsAddToSelf,
-                               'remove-self' => $wgGroupsRemoveFromSelf
-                       );
-                       
-                       foreach( $groupArr as $type => $rights ) {
-                               if( isset( $rights[$group] ) ) {
-                                       $arr[$type] = $rights[$group];
-                                       $this->getResult()->setIndexedTagName( $arr[$type], 'group' );
+
+                       $groupArr = [
+                               'add' => $config->get( 'AddGroups' ),
+                               'remove' => $config->get( 'RemoveGroups' ),
+                               'add-self' => $config->get( 'GroupsAddToSelf' ),
+                               'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
+                       ];
+
+                       foreach ( $groupArr as $type => $rights ) {
+                               if ( isset( $rights[$group] ) ) {
+                                       if ( $rights[$group] === true ) {
+                                               $groups = $allGroups;
+                                       } else {
+                                               $groups = array_intersect( $rights[$group], $allGroups );
+                                       }
+                                       if ( $groups ) {
+                                               $arr[$type] = $groups;
+                                               ApiResult::setArrayType( $arr[$type], 'BCarray' );
+                                               ApiResult::setIndexedTagName( $arr[$type], 'group' );
+                                       }
                                }
                        }
-                       
-                       $this->getResult()->setIndexedTagName( $arr['rights'], 'permission' );
+
+                       ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
                        $data[] = $arr;
                }
-               
-               $this->getResult()->setIndexedTagName( $data, 'group' );
-               return $this->getResult()->addValue( 'query', $property, $data );
+
+               ApiResult::setIndexedTagName( $data, 'group' );
+
+               return $result->addValue( 'query', $property, $data );
        }
 
        protected function appendFileExtensions( $property ) {
-               global $wgFileExtensions;
+               $data = [];
+               foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
+                       $data[] = [ 'ext' => $ext ];
+               }
+               ApiResult::setIndexedTagName( $data, 'fe' );
+
+               return $this->getResult()->addValue( 'query', $property, $data );
+       }
 
-               $data = array();
-               foreach ( $wgFileExtensions as $ext ) {
-                       $data[] = array( 'ext' => $ext );
+       protected function appendInstalledLibraries( $property ) {
+               global $IP;
+               $path = "$IP/vendor/composer/installed.json";
+               if ( !file_exists( $path ) ) {
+                       return true;
                }
-               $this->getResult()->setIndexedTagName( $data, 'fe' );
+
+               $data = [];
+               $installed = new ComposerInstalled( $path );
+               foreach ( $installed->getInstalledDependencies() as $name => $info ) {
+                       if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
+                               // Skip any extensions or skins since they'll be listed
+                               // in their proper section
+                               continue;
+                       }
+                       $data[] = [
+                               'name' => $name,
+                               'version' => $info['version'],
+                       ];
+               }
+               ApiResult::setIndexedTagName( $data, 'library' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        protected function appendExtensions( $property ) {
-               global $wgExtensionCredits;
-               $data = array();
-               foreach ( $wgExtensionCredits as $type => $extensions ) {
+               $data = [];
+               foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
                        foreach ( $extensions as $ext ) {
-                               $ret = array();
+                               $ret = [];
                                $ret['type'] = $type;
                                if ( isset( $ext['name'] ) ) {
                                        $ret['name'] = $ext['name'];
                                }
+                               if ( isset( $ext['namemsg'] ) ) {
+                                       $ret['namemsg'] = $ext['namemsg'];
+                               }
                                if ( isset( $ext['description'] ) ) {
                                        $ret['description'] = $ext['description'];
                                }
                                if ( isset( $ext['descriptionmsg'] ) ) {
-                                       // Can be a string or array( key, param1, param2, ... )
+                                       // Can be a string or [ key, param1, param2, ... ]
                                        if ( is_array( $ext['descriptionmsg'] ) ) {
                                                $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
                                                $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
-                                               $this->getResult()->setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
+                                               ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
                                        } else {
                                                $ret['descriptionmsg'] = $ext['descriptionmsg'];
                                        }
                                }
                                if ( isset( $ext['author'] ) ) {
                                        $ret['author'] = is_array( $ext['author'] ) ?
-                                               implode( ', ', $ext['author' ] ) : $ext['author'];
+                                               implode( ', ', $ext['author'] ) : $ext['author'];
                                }
                                if ( isset( $ext['url'] ) ) {
                                        $ret['url'] = $ext['url'];
                                }
                                if ( isset( $ext['version'] ) ) {
-                                               $ret['version'] = $ext['version'];
-                               } elseif ( isset( $ext['svn-revision'] ) &&
-                                       preg_match( '/\$(?:Rev|LastChangedRevision|Revision): *(\d+)/',
-                                               $ext['svn-revision'], $m ) )
-                               {
-                                               $ret['version'] = 'r' . $m[1];
+                                       $ret['version'] = $ext['version'];
+                               }
+                               if ( isset( $ext['path'] ) ) {
+                                       $extensionPath = dirname( $ext['path'] );
+                                       $gitInfo = new GitInfo( $extensionPath );
+                                       $vcsVersion = $gitInfo->getHeadSHA1();
+                                       if ( $vcsVersion !== false ) {
+                                               $ret['vcs-system'] = 'git';
+                                               $ret['vcs-version'] = $vcsVersion;
+                                               $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
+                                               $vcsDate = $gitInfo->getHeadCommitDate();
+                                               if ( $vcsDate !== false ) {
+                                                       $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
+                                               }
+                                       }
+
+                                       if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
+                                               $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
+                                               $ret['license'] = SpecialPage::getTitleFor(
+                                                       'Version',
+                                                       "License/{$ext['name']}"
+                                               )->getLinkURL();
+                                       }
+
+                                       if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
+                                               $ret['credits'] = SpecialPage::getTitleFor(
+                                                       'Version',
+                                                       "Credits/{$ext['name']}"
+                                               )->getLinkURL();
+                                       }
                                }
                                $data[] = $ret;
                        }
                }
 
-               $this->getResult()->setIndexedTagName( $data, 'ext' );
+               ApiResult::setIndexedTagName( $data, 'ext' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
-
        protected function appendRightsInfo( $property ) {
-               global $wgRightsPage, $wgRightsUrl, $wgRightsText;
-               $title = Title::newFromText( $wgRightsPage );
-               $url = $title ? $title->getFullURL() : $wgRightsUrl;
-               $text = $wgRightsText;
+               $config = $this->getConfig();
+               $rightsPage = $config->get( 'RightsPage' );
+               if ( is_string( $rightsPage ) ) {
+                       $title = Title::newFromText( $rightsPage );
+                       $url = wfExpandUrl( $title, PROTO_CURRENT );
+               } else {
+                       $title = false;
+                       $url = $config->get( 'RightsUrl' );
+               }
+               $text = $config->get( 'RightsText' );
                if ( !$text && $title ) {
                        $text = $title->getPrefixedText();
                }
 
-               $data = array(
-                       'url' => $url ? $url : '',
-                       'text' => $text ?  $text : ''
-               );
+               $data = [
+                       'url' => $url ?: '',
+                       'text' => $text ?: ''
+               ];
+
+               return $this->getResult()->addValue( 'query', $property, $data );
+       }
+
+       protected function appendRestrictions( $property ) {
+               $config = $this->getConfig();
+               $data = [
+                       'types' => $config->get( 'RestrictionTypes' ),
+                       'levels' => $config->get( 'RestrictionLevels' ),
+                       'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
+                       'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
+               ];
+
+               ApiResult::setArrayType( $data['types'], 'BCarray' );
+               ApiResult::setArrayType( $data['levels'], 'BCarray' );
+               ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
+               ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
+
+               ApiResult::setIndexedTagName( $data['types'], 'type' );
+               ApiResult::setIndexedTagName( $data['levels'], 'level' );
+               ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
+               ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
 
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        public function appendLanguages( $property ) {
-               $data = array();
-               foreach ( Language::getLanguageNames() as $code => $name ) {
-                       $lang = array( 'code' => $code );
-                       ApiResult::setContent( $lang, $name );
+               $params = $this->extractRequestParams();
+               $langCode = isset( $params['inlanguagecode'] ) ? $params['inlanguagecode'] : '';
+               $langNames = Language::fetchLanguageNames( $langCode );
+
+               $data = [];
+
+               foreach ( $langNames as $code => $name ) {
+                       $lang = [ 'code' => $code ];
+                       ApiResult::setContentValue( $lang, 'name', $name );
                        $data[] = $lang;
                }
-               $this->getResult()->setIndexedTagName( $data, 'lang' );
+               ApiResult::setIndexedTagName( $data, 'lang' );
+
+               return $this->getResult()->addValue( 'query', $property, $data );
+       }
+
+       // Export information about which page languages will trigger
+       // language conversion. (T153341)
+       public function appendLanguageVariants( $property ) {
+               $langNames = LanguageConverter::$languagesWithVariants;
+               if ( $this->getConfig()->get( 'DisableLangConversion' ) ) {
+                       // Ensure result is empty if language conversion is disabled.
+                       $langNames = [];
+               }
+               sort( $langNames );
+
+               $data = [];
+               foreach ( $langNames as $langCode ) {
+                       $lang = Language::factory( $langCode );
+                       if ( $lang->getConverter() instanceof FakeConverter ) {
+                               // Only languages which do not return instances of
+                               // FakeConverter implement language conversion.
+                               continue;
+                       }
+                       $data[$langCode] = [];
+                       ApiResult::setIndexedTagName( $data[$langCode], 'variant' );
+                       ApiResult::setArrayType( $data[$langCode], 'kvp', 'code' );
+
+                       $variants = $lang->getVariants();
+                       sort( $variants );
+                       foreach ( $variants as $v ) {
+                               $fallbacks = $lang->getConverter()->getVariantFallbacks( $v );
+                               if ( !is_array( $fallbacks ) ) {
+                                       $fallbacks = [ $fallbacks ];
+                               }
+                               $data[$langCode][$v] = [
+                                       'fallbacks' => $fallbacks,
+                               ];
+                               ApiResult::setIndexedTagName(
+                                       $data[$langCode][$v]['fallbacks'], 'variant'
+                               );
+                       }
+               }
+               ApiResult::setIndexedTagName( $data, 'lang' );
+               ApiResult::setArrayType( $data, 'kvp', 'code' );
+
+               return $this->getResult()->addValue( 'query', $property, $data );
+       }
+
+       public function appendSkins( $property ) {
+               $data = [];
+               $allowed = Skin::getAllowedSkins();
+               $default = Skin::normalizeKey( 'default' );
+               foreach ( Skin::getSkinNames() as $name => $displayName ) {
+                       $msg = $this->msg( "skinname-{$name}" );
+                       $code = $this->getParameter( 'inlanguagecode' );
+                       if ( $code && Language::isValidCode( $code ) ) {
+                               $msg->inLanguage( $code );
+                       } else {
+                               $msg->inContentLanguage();
+                       }
+                       if ( $msg->exists() ) {
+                               $displayName = $msg->text();
+                       }
+                       $skin = [ 'code' => $name ];
+                       ApiResult::setContentValue( $skin, 'name', $displayName );
+                       if ( !isset( $allowed[$name] ) ) {
+                               $skin['unusable'] = true;
+                       }
+                       if ( $name === $default ) {
+                               $skin['default'] = true;
+                       }
+                       $data[] = $skin;
+               }
+               ApiResult::setIndexedTagName( $data, 'skin' );
+
+               return $this->getResult()->addValue( 'query', $property, $data );
+       }
+
+       public function appendExtensionTags( $property ) {
+               global $wgParser;
+               $wgParser->firstCallInit();
+               $tags = array_map( [ $this, 'formatParserTags' ], $wgParser->getTags() );
+               ApiResult::setArrayType( $tags, 'BCarray' );
+               ApiResult::setIndexedTagName( $tags, 't' );
+
+               return $this->getResult()->addValue( 'query', $property, $tags );
+       }
+
+       public function appendFunctionHooks( $property ) {
+               global $wgParser;
+               $wgParser->firstCallInit();
+               $hooks = $wgParser->getFunctionHooks();
+               ApiResult::setArrayType( $hooks, 'BCarray' );
+               ApiResult::setIndexedTagName( $hooks, 'h' );
+
+               return $this->getResult()->addValue( 'query', $property, $hooks );
+       }
+
+       public function appendVariables( $property ) {
+               $variables = MagicWord::getVariableIDs();
+               ApiResult::setArrayType( $variables, 'BCarray' );
+               ApiResult::setIndexedTagName( $variables, 'v' );
+
+               return $this->getResult()->addValue( 'query', $property, $variables );
+       }
+
+       public function appendProtocols( $property ) {
+               // Make a copy of the global so we don't try to set the _element key of it - T47130
+               $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
+               ApiResult::setArrayType( $protocols, 'BCarray' );
+               ApiResult::setIndexedTagName( $protocols, 'p' );
+
+               return $this->getResult()->addValue( 'query', $property, $protocols );
+       }
+
+       public function appendDefaultOptions( $property ) {
+               $options = User::getDefaultOptions();
+               $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
+               return $this->getResult()->addValue( 'query', $property, $options );
+       }
+
+       public function appendUploadDialog( $property ) {
+               $config = $this->getConfig()->get( 'UploadDialog' );
+               return $this->getResult()->addValue( 'query', $property, $config );
+       }
+
+       private function formatParserTags( $item ) {
+               return "<{$item}>";
+       }
+
+       public function appendSubscribedHooks( $property ) {
+               $hooks = $this->getConfig()->get( 'Hooks' );
+               $myWgHooks = $hooks;
+               ksort( $myWgHooks );
+
+               $data = [];
+               foreach ( $myWgHooks as $name => $subscribers ) {
+                       $arr = [
+                               'name' => $name,
+                               'subscribers' => array_map( [ 'SpecialVersion', 'arrayToString' ], $subscribers ),
+                       ];
+
+                       ApiResult::setArrayType( $arr['subscribers'], 'array' );
+                       ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
+                       $data[] = $arr;
+               }
+
+               ApiResult::setIndexedTagName( $data, 'hook' );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
        public function getCacheMode( $params ) {
+               // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
+               if (
+                       count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
+                       !is_null( $params['prop'] ) &&
+                       in_array( 'interwikimap', $params['prop'] )
+               ) {
+                       return 'anon-public-user-private';
+               }
+
                return 'public';
        }
 
        public function getAllowedParams() {
-               return array(
-                       'prop' => array(
+               return [
+                       'prop' => [
                                ApiBase::PARAM_DFLT => 'general',
                                ApiBase::PARAM_ISMULTI => true,
-                               ApiBase::PARAM_TYPE => array(
+                               ApiBase::PARAM_TYPE => [
                                        'general',
                                        'namespaces',
                                        'namespacealiases',
@@ -471,66 +891,48 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                                        'dbrepllag',
                                        'statistics',
                                        'usergroups',
+                                       'libraries',
                                        'extensions',
                                        'fileextensions',
                                        'rightsinfo',
+                                       'restrictions',
                                        'languages',
-                               )
-                       ),
-                       'filteriw' => array(
-                               ApiBase::PARAM_TYPE => array(
+                                       'languagevariants',
+                                       'skins',
+                                       'extensiontags',
+                                       'functionhooks',
+                                       'showhooks',
+                                       'variables',
+                                       'protocols',
+                                       'defaultoptions',
+                                       'uploaddialog',
+                               ],
+                               ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
+                       ],
+                       'filteriw' => [
+                               ApiBase::PARAM_TYPE => [
                                        'local',
                                        '!local',
-                               )
-                       ),
+                               ]
+                       ],
                        'showalldb' => false,
                        'numberingroup' => false,
-               );
-       }
-
-       public function getParamDescription() {
-               return array(
-                       'prop' => array(
-                               'Which sysinfo properties to get:',
-                               ' general               - Overall system information',
-                               ' namespaces            - List of registered namespaces and their canonical names',
-                               ' namespacealiases      - List of registered namespace aliases',
-                               ' specialpagealiases    - List of special page aliases',
-                               ' magicwords            - List of magic words and their aliases',
-                               ' statistics            - Returns site statistics',
-                               ' interwikimap          - Returns interwiki map (optionally filtered)',
-                               ' dbrepllag             - Returns database server with the highest replication lag',
-                               ' usergroups            - Returns user groups and the associated permissions',
-                               ' extensions            - Returns extensions installed on the wiki',
-                               ' fileextensions        - Returns list of file extensions allowed to be uploaded',
-                               ' rightsinfo            - Returns wiki rights (license) information if available',
-                               ' languages             - Returns a list of languages MediaWiki supports',
-                       ),
-                       'filteriw' =>  'Return only local or only nonlocal entries of the interwiki map',
-                       'showalldb' => 'List all database servers, not just the one lagging the most',
-                       'numberingroup' => 'Lists the number of users in user groups',
-               );
-       }
-
-       public function getDescription() {
-               return 'Return general information about the site';
-       }
-
-       public function getPossibleErrors() {
-               return array_merge( parent::getPossibleErrors(), array(
-                       array( 'code' => 'includeAllDenied', 'info' => 'Cannot view all servers info unless $wgShowHostnames is true' ),
-               ) );
+                       'inlanguagecode' => null,
+               ];
        }
 
-       protected function getExamples() {
-               return array(
-                       'api.php?action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics',
-                       'api.php?action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local',
-                       'api.php?action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb=',
-               );
+       protected function getExamplesMessages() {
+               return [
+                       'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
+                               => 'apihelp-query+siteinfo-example-simple',
+                       'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
+                               => 'apihelp-query+siteinfo-example-interwiki',
+                       'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
+                               => 'apihelp-query+siteinfo-example-replag',
+               ];
        }
 
-       public function getVersion() {
-               return __CLASS__ . ': $Id$';
+       public function getHelpUrls() {
+               return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Siteinfo';
        }
 }