MediaWiki  master
ApiQuerySiteinfo.php
Go to the documentation of this file.
1 <?php
23 
30 
31  public function __construct( ApiQuery $query, $moduleName ) {
32  parent::__construct( $query, $moduleName, 'si' );
33  }
34 
35  public function execute() {
36  $params = $this->extractRequestParams();
37  $done = [];
38  $fit = false;
39  foreach ( $params['prop'] as $p ) {
40  switch ( $p ) {
41  case 'general':
42  $fit = $this->appendGeneralInfo( $p );
43  break;
44  case 'namespaces':
45  $fit = $this->appendNamespaces( $p );
46  break;
47  case 'namespacealiases':
48  $fit = $this->appendNamespaceAliases( $p );
49  break;
50  case 'specialpagealiases':
51  $fit = $this->appendSpecialPageAliases( $p );
52  break;
53  case 'magicwords':
54  $fit = $this->appendMagicWords( $p );
55  break;
56  case 'interwikimap':
57  $fit = $this->appendInterwikiMap( $p, $params['filteriw'] );
58  break;
59  case 'dbrepllag':
60  $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
61  break;
62  case 'statistics':
63  $fit = $this->appendStatistics( $p );
64  break;
65  case 'usergroups':
66  $fit = $this->appendUserGroups( $p, $params['numberingroup'] );
67  break;
68  case 'libraries':
69  $fit = $this->appendInstalledLibraries( $p );
70  break;
71  case 'extensions':
72  $fit = $this->appendExtensions( $p );
73  break;
74  case 'fileextensions':
75  $fit = $this->appendFileExtensions( $p );
76  break;
77  case 'rightsinfo':
78  $fit = $this->appendRightsInfo( $p );
79  break;
80  case 'restrictions':
81  $fit = $this->appendRestrictions( $p );
82  break;
83  case 'languages':
84  $fit = $this->appendLanguages( $p );
85  break;
86  case 'languagevariants':
87  $fit = $this->appendLanguageVariants( $p );
88  break;
89  case 'skins':
90  $fit = $this->appendSkins( $p );
91  break;
92  case 'extensiontags':
93  $fit = $this->appendExtensionTags( $p );
94  break;
95  case 'functionhooks':
96  $fit = $this->appendFunctionHooks( $p );
97  break;
98  case 'showhooks':
99  $fit = $this->appendSubscribedHooks( $p );
100  break;
101  case 'variables':
102  $fit = $this->appendVariables( $p );
103  break;
104  case 'protocols':
105  $fit = $this->appendProtocols( $p );
106  break;
107  case 'defaultoptions':
108  $fit = $this->appendDefaultOptions( $p );
109  break;
110  case 'uploaddialog':
111  $fit = $this->appendUploadDialog( $p );
112  break;
113  default:
114  ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" ); // @codeCoverageIgnore
115  }
116  if ( !$fit ) {
117  // Abuse siprop as a query-continue parameter
118  // and set it to all unprocessed props
119  $this->setContinueEnumParameter( 'prop', implode( '|',
120  array_diff( $params['prop'], $done ) ) );
121  break;
122  }
123  $done[] = $p;
124  }
125  }
126 
127  protected function appendGeneralInfo( $property ) {
128  $config = $this->getConfig();
129 
130  $data = [];
131  $mainPage = Title::newMainPage();
132  $data['mainpage'] = $mainPage->getPrefixedText();
133  $data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
134  $data['sitename'] = $config->get( 'Sitename' );
135  $data['mainpageisdomainroot'] = (bool)$config->get( 'MainPageIsDomainRoot' );
136 
137  // wgLogo can either be a relative or an absolute path
138  // make sure we always return an absolute path
139  $data['logo'] = wfExpandUrl( $config->get( 'Logo' ), PROTO_RELATIVE );
140 
141  $data['generator'] = "MediaWiki {$config->get( 'Version' )}";
142 
143  $data['phpversion'] = PHP_VERSION;
144  $data['phpsapi'] = PHP_SAPI;
145  $data['dbtype'] = $config->get( 'DBtype' );
146  $data['dbversion'] = $this->getDB()->getServerVersion();
147 
148  $allowFrom = [ '' ];
149  $allowException = true;
150  if ( !$config->get( 'AllowExternalImages' ) ) {
151  $data['imagewhitelistenabled'] = (bool)$config->get( 'EnableImageWhitelist' );
152  $allowFrom = $config->get( 'AllowExternalImagesFrom' );
153  $allowException = !empty( $allowFrom );
154  }
155  if ( $allowException ) {
156  $data['externalimages'] = (array)$allowFrom;
157  ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
158  }
159 
160  $data['langconversion'] = !$config->get( 'DisableLangConversion' );
161  $data['titleconversion'] = !$config->get( 'DisableTitleConversion' );
162 
163  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
164  if ( $contLang->linkPrefixExtension() ) {
165  $linkPrefixCharset = $contLang->linkPrefixCharset();
166  $data['linkprefixcharset'] = $linkPrefixCharset;
167  // For backwards compatibility
168  $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
169  } else {
170  $data['linkprefixcharset'] = '';
171  $data['linkprefix'] = '';
172  }
173 
174  $linktrail = $contLang->linkTrail();
175  $data['linktrail'] = $linktrail ?: '';
176 
177  $data['legaltitlechars'] = Title::legalChars();
178  $data['invalidusernamechars'] = $config->get( 'InvalidUsernameCharacters' );
179 
180  $data['allunicodefixes'] = (bool)$config->get( 'AllUnicodeFixes' );
181  $data['fixarabicunicode'] = (bool)$config->get( 'FixArabicUnicode' );
182  $data['fixmalayalamunicode'] = (bool)$config->get( 'FixMalayalamUnicode' );
183 
184  global $IP;
185  $git = SpecialVersion::getGitHeadSha1( $IP );
186  if ( $git ) {
187  $data['git-hash'] = $git;
188  $data['git-branch'] =
190  }
191 
192  // 'case-insensitive' option is reserved for future
193  $data['case'] = $config->get( 'CapitalLinks' ) ? 'first-letter' : 'case-sensitive';
194  $data['lang'] = $config->get( 'LanguageCode' );
195 
196  $fallbacks = [];
197  foreach ( $contLang->getFallbackLanguages() as $code ) {
198  $fallbacks[] = [ 'code' => $code ];
199  }
200  $data['fallback'] = $fallbacks;
201  ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
202 
203  if ( $contLang->hasVariants() ) {
204  $variants = [];
205  foreach ( $contLang->getVariants() as $code ) {
206  $variants[] = [
207  'code' => $code,
208  'name' => $contLang->getVariantname( $code ),
209  ];
210  }
211  $data['variants'] = $variants;
212  ApiResult::setIndexedTagName( $data['variants'], 'lang' );
213  }
214 
215  $data['rtl'] = $contLang->isRTL();
216  $data['fallback8bitEncoding'] = $contLang->fallback8bitEncoding();
217 
218  $data['readonly'] = wfReadOnly();
219  if ( $data['readonly'] ) {
220  $data['readonlyreason'] = wfReadOnlyReason();
221  }
222  $data['writeapi'] = true; // Deprecated since MW 1.32
223 
224  $data['maxarticlesize'] = $config->get( 'MaxArticleSize' ) * 1024;
225 
226  $tz = $config->get( 'Localtimezone' );
227  $offset = $config->get( 'LocalTZoffset' );
228  $data['timezone'] = $tz;
229  $data['timeoffset'] = (int)$offset;
230  $data['articlepath'] = $config->get( 'ArticlePath' );
231  $data['scriptpath'] = $config->get( 'ScriptPath' );
232  $data['script'] = $config->get( 'Script' );
233  $data['variantarticlepath'] = $config->get( 'VariantArticlePath' );
234  $data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
235  $data['server'] = $config->get( 'Server' );
236  $data['servername'] = $config->get( 'ServerName' );
238  $data['time'] = wfTimestamp( TS_ISO_8601, time() );
239 
240  $data['misermode'] = (bool)$config->get( 'MiserMode' );
241 
242  $data['uploadsenabled'] = UploadBase::isEnabled();
243  $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
244  $data['minuploadchunksize'] = (int)$config->get( 'MinUploadChunkSize' );
245 
246  $data['galleryoptions'] = $config->get( 'GalleryOptions' );
247 
248  $data['thumblimits'] = $config->get( 'ThumbLimits' );
249  ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
250  ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
251  $data['imagelimits'] = [];
252  ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
253  ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
254  foreach ( $config->get( 'ImageLimits' ) as $k => $limit ) {
255  $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
256  }
257 
258  $favicon = $config->get( 'Favicon' );
259  if ( !empty( $favicon ) ) {
260  // wgFavicon can either be a relative or an absolute path
261  // make sure we always return an absolute path
262  $data['favicon'] = wfExpandUrl( $favicon, PROTO_RELATIVE );
263  }
264 
265  $data['centralidlookupprovider'] = $config->get( 'CentralIdLookupProvider' );
266  $providerIds = array_keys( $config->get( 'CentralIdLookupProviders' ) );
267  $data['allcentralidlookupproviders'] = $providerIds;
268 
269  $data['interwikimagic'] = (bool)$config->get( 'InterwikiMagic' );
270  $data['magiclinks'] = $config->get( 'EnableMagicLinks' );
271 
272  $data['categorycollation'] = $config->get( 'CategoryCollation' );
273 
274  Hooks::run( 'APIQuerySiteInfoGeneralInfo', [ $this, &$data ] );
275 
276  return $this->getResult()->addValue( 'query', $property, $data );
277  }
278 
279  protected function appendNamespaces( $property ) {
280  $nsProtection = $this->getConfig()->get( 'NamespaceProtection' );
281 
282  $data = [
283  ApiResult::META_TYPE => 'assoc',
284  ];
285  $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
286  foreach (
287  MediaWikiServices::getInstance()->getContentLanguage()->getFormattedNamespaces()
288  as $ns => $title
289  ) {
290  $data[$ns] = [
291  'id' => (int)$ns,
292  'case' => $nsInfo->isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
293  ];
294  ApiResult::setContentValue( $data[$ns], 'name', $title );
295  $canonical = $nsInfo->getCanonicalName( $ns );
296 
297  $data[$ns]['subpages'] = $nsInfo->hasSubpages( $ns );
298 
299  if ( $canonical ) {
300  $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
301  }
302 
303  $data[$ns]['content'] = $nsInfo->isContent( $ns );
304  $data[$ns]['nonincludable'] = $nsInfo->isNonincludable( $ns );
305 
306  if ( isset( $nsProtection[$ns] ) ) {
307  if ( is_array( $nsProtection[$ns] ) ) {
308  $specificNs = implode( "|", array_filter( $nsProtection[$ns] ) );
309  } elseif ( $nsProtection[$ns] !== '' ) {
310  $specificNs = $nsProtection[$ns];
311  }
312  if ( isset( $specificNs ) && $specificNs !== '' ) {
313  $data[$ns]['namespaceprotection'] = $specificNs;
314  }
315  }
316 
317  $contentmodel = $nsInfo->getNamespaceContentModel( $ns );
318  if ( $contentmodel ) {
319  $data[$ns]['defaultcontentmodel'] = $contentmodel;
320  }
321  }
322 
323  ApiResult::setArrayType( $data, 'assoc' );
324  ApiResult::setIndexedTagName( $data, 'ns' );
325 
326  return $this->getResult()->addValue( 'query', $property, $data );
327  }
328 
329  protected function appendNamespaceAliases( $property ) {
330  $contLang = MediaWikiServices::getInstance()->getContentLanguage();
331  $aliases = array_merge( $this->getConfig()->get( 'NamespaceAliases' ),
332  $contLang->getNamespaceAliases() );
333  $namespaces = $contLang->getNamespaces();
334  $data = [];
335  foreach ( $aliases as $title => $ns ) {
336  if ( $namespaces[$ns] == $title ) {
337  // Don't list duplicates
338  continue;
339  }
340  $item = [
341  'id' => (int)$ns
342  ];
343  ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
344  $data[] = $item;
345  }
346 
347  sort( $data );
348 
349  ApiResult::setIndexedTagName( $data, 'ns' );
350 
351  return $this->getResult()->addValue( 'query', $property, $data );
352  }
353 
354  protected function appendSpecialPageAliases( $property ) {
355  $data = [];
356  $services = MediaWikiServices::getInstance();
357  $aliases = $services->getContentLanguage()->getSpecialPageAliases();
358  foreach ( $services->getSpecialPageFactory()->getNames() as $specialpage ) {
359  if ( isset( $aliases[$specialpage] ) ) {
360  $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
361  ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
362  $data[] = $arr;
363  }
364  }
365  ApiResult::setIndexedTagName( $data, 'specialpage' );
366 
367  return $this->getResult()->addValue( 'query', $property, $data );
368  }
369 
370  protected function appendMagicWords( $property ) {
371  $data = [];
372  foreach (
373  MediaWikiServices::getInstance()->getContentLanguage()->getMagicWords()
374  as $magicword => $aliases
375  ) {
376  $caseSensitive = array_shift( $aliases );
377  $arr = [ 'name' => $magicword, 'aliases' => $aliases ];
378  $arr['case-sensitive'] = (bool)$caseSensitive;
379  ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
380  $data[] = $arr;
381  }
382  ApiResult::setIndexedTagName( $data, 'magicword' );
383 
384  return $this->getResult()->addValue( 'query', $property, $data );
385  }
386 
387  protected function appendInterwikiMap( $property, $filter ) {
388  if ( $filter === 'local' ) {
389  $local = 1;
390  } elseif ( $filter === '!local' ) {
391  $local = 0;
392  } else {
393  // $filter === null
394  $local = null;
395  }
396 
397  $params = $this->extractRequestParams();
398  $langCode = $params['inlanguagecode'] ?? '';
399  $langNames = Language::fetchLanguageNames( $langCode );
400 
401  $getPrefixes = MediaWikiServices::getInstance()->getInterwikiLookup()->getAllPrefixes( $local );
402  $extraLangPrefixes = $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' );
403  $localInterwikis = $this->getConfig()->get( 'LocalInterwikis' );
404  $data = [];
405 
406  foreach ( $getPrefixes as $row ) {
407  $prefix = $row['iw_prefix'];
408  $val = [];
409  $val['prefix'] = $prefix;
410  if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
411  $val['local'] = true;
412  }
413  if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
414  $val['trans'] = true;
415  }
416 
417  if ( isset( $langNames[$prefix] ) ) {
418  $val['language'] = $langNames[$prefix];
419  }
420  if ( in_array( $prefix, $localInterwikis ) ) {
421  $val['localinterwiki'] = true;
422  }
423  if ( in_array( $prefix, $extraLangPrefixes ) ) {
424  $val['extralanglink'] = true;
425 
426  $linktext = wfMessage( "interlanguage-link-$prefix" );
427  if ( !$linktext->isDisabled() ) {
428  $val['linktext'] = $linktext->text();
429  }
430 
431  $sitename = wfMessage( "interlanguage-link-sitename-$prefix" );
432  if ( !$sitename->isDisabled() ) {
433  $val['sitename'] = $sitename->text();
434  }
435  }
436 
437  $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
438  $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
439  if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
440  $val['wikiid'] = $row['iw_wikiid'];
441  }
442  if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
443  $val['api'] = $row['iw_api'];
444  }
445 
446  $data[] = $val;
447  }
448 
449  ApiResult::setIndexedTagName( $data, 'iw' );
450 
451  return $this->getResult()->addValue( 'query', $property, $data );
452  }
453 
454  protected function appendDbReplLagInfo( $property, $includeAll ) {
455  $data = [];
456  $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
457  $showHostnames = $this->getConfig()->get( 'ShowHostnames' );
458  if ( $includeAll ) {
459  if ( !$showHostnames ) {
460  $this->dieWithError( 'apierror-siteinfo-includealldenied', 'includeAllDenied' );
461  }
462 
463  $lags = $lb->getLagTimes();
464  foreach ( $lags as $i => $lag ) {
465  $data[] = [
466  'host' => $lb->getServerName( $i ),
467  'lag' => $lag
468  ];
469  }
470  } else {
471  list( , $lag, $index ) = $lb->getMaxLag();
472  $data[] = [
473  'host' => $showHostnames
474  ? $lb->getServerName( $index )
475  : '',
476  'lag' => $lag
477  ];
478  }
479 
480  ApiResult::setIndexedTagName( $data, 'db' );
481 
482  return $this->getResult()->addValue( 'query', $property, $data );
483  }
484 
485  protected function appendStatistics( $property ) {
486  $data = [];
487  $data['pages'] = (int)SiteStats::pages();
488  $data['articles'] = (int)SiteStats::articles();
489  $data['edits'] = (int)SiteStats::edits();
490  $data['images'] = (int)SiteStats::images();
491  $data['users'] = (int)SiteStats::users();
492  $data['activeusers'] = (int)SiteStats::activeUsers();
493  $data['admins'] = (int)SiteStats::numberingroup( 'sysop' );
494  $data['jobs'] = (int)SiteStats::jobs();
495 
496  Hooks::run( 'APIQuerySiteInfoStatisticsInfo', [ &$data ] );
497 
498  return $this->getResult()->addValue( 'query', $property, $data );
499  }
500 
501  protected function appendUserGroups( $property, $numberInGroup ) {
502  $config = $this->getConfig();
503 
504  $data = [];
505  $result = $this->getResult();
506  $allGroups = array_values( User::getAllGroups() );
507  foreach ( $config->get( 'GroupPermissions' ) as $group => $permissions ) {
508  $arr = [
509  'name' => $group,
510  'rights' => array_keys( $permissions, true ),
511  ];
512 
513  if ( $numberInGroup ) {
514  $autopromote = $config->get( 'Autopromote' );
515 
516  if ( $group == 'user' ) {
517  $arr['number'] = SiteStats::users();
518  // '*' and autopromote groups have no size
519  } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
520  $arr['number'] = SiteStats::numberingroup( $group );
521  }
522  }
523 
524  $groupArr = [
525  'add' => $config->get( 'AddGroups' ),
526  'remove' => $config->get( 'RemoveGroups' ),
527  'add-self' => $config->get( 'GroupsAddToSelf' ),
528  'remove-self' => $config->get( 'GroupsRemoveFromSelf' )
529  ];
530 
531  foreach ( $groupArr as $type => $rights ) {
532  if ( isset( $rights[$group] ) ) {
533  if ( $rights[$group] === true ) {
534  $groups = $allGroups;
535  } else {
536  $groups = array_intersect( $rights[$group], $allGroups );
537  }
538  if ( $groups ) {
539  $arr[$type] = $groups;
540  ApiResult::setArrayType( $arr[$type], 'BCarray' );
541  ApiResult::setIndexedTagName( $arr[$type], 'group' );
542  }
543  }
544  }
545 
546  ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
547  $data[] = $arr;
548  }
549 
550  ApiResult::setIndexedTagName( $data, 'group' );
551 
552  return $result->addValue( 'query', $property, $data );
553  }
554 
555  protected function appendFileExtensions( $property ) {
556  $data = [];
557  foreach ( array_unique( $this->getConfig()->get( 'FileExtensions' ) ) as $ext ) {
558  $data[] = [ 'ext' => $ext ];
559  }
560  ApiResult::setIndexedTagName( $data, 'fe' );
561 
562  return $this->getResult()->addValue( 'query', $property, $data );
563  }
564 
565  protected function appendInstalledLibraries( $property ) {
566  global $IP;
567  $path = "$IP/vendor/composer/installed.json";
568  if ( !file_exists( $path ) ) {
569  return true;
570  }
571 
572  $data = [];
573  $installed = new ComposerInstalled( $path );
574  foreach ( $installed->getInstalledDependencies() as $name => $info ) {
575  if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
576  // Skip any extensions or skins since they'll be listed
577  // in their proper section
578  continue;
579  }
580  $data[] = [
581  'name' => $name,
582  'version' => $info['version'],
583  ];
584  }
585  ApiResult::setIndexedTagName( $data, 'library' );
586 
587  return $this->getResult()->addValue( 'query', $property, $data );
588  }
589 
590  protected function appendExtensions( $property ) {
591  $data = [];
592  foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $type => $extensions ) {
593  foreach ( $extensions as $ext ) {
594  $ret = [];
595  $ret['type'] = $type;
596  if ( isset( $ext['name'] ) ) {
597  $ret['name'] = $ext['name'];
598  }
599  if ( isset( $ext['namemsg'] ) ) {
600  $ret['namemsg'] = $ext['namemsg'];
601  }
602  if ( isset( $ext['description'] ) ) {
603  $ret['description'] = $ext['description'];
604  }
605  if ( isset( $ext['descriptionmsg'] ) ) {
606  // Can be a string or [ key, param1, param2, ... ]
607  if ( is_array( $ext['descriptionmsg'] ) ) {
608  $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
609  $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
610  ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
611  } else {
612  $ret['descriptionmsg'] = $ext['descriptionmsg'];
613  }
614  }
615  if ( isset( $ext['author'] ) ) {
616  $ret['author'] = is_array( $ext['author'] ) ?
617  implode( ', ', $ext['author'] ) : $ext['author'];
618  }
619  if ( isset( $ext['url'] ) ) {
620  $ret['url'] = $ext['url'];
621  }
622  if ( isset( $ext['version'] ) ) {
623  $ret['version'] = $ext['version'];
624  }
625  if ( isset( $ext['path'] ) ) {
626  $extensionPath = dirname( $ext['path'] );
627  $gitInfo = new GitInfo( $extensionPath );
628  $vcsVersion = $gitInfo->getHeadSHA1();
629  if ( $vcsVersion !== false ) {
630  $ret['vcs-system'] = 'git';
631  $ret['vcs-version'] = $vcsVersion;
632  $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
633  $vcsDate = $gitInfo->getHeadCommitDate();
634  if ( $vcsDate !== false ) {
635  $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
636  }
637  }
638 
639  if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
640  $ret['license-name'] = $ext['license-name'] ?? '';
641  $ret['license'] = SpecialPage::getTitleFor(
642  'Version',
643  "License/{$ext['name']}"
644  )->getLinkURL();
645  }
646 
647  if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
648  $ret['credits'] = SpecialPage::getTitleFor(
649  'Version',
650  "Credits/{$ext['name']}"
651  )->getLinkURL();
652  }
653  }
654  $data[] = $ret;
655  }
656  }
657 
658  ApiResult::setIndexedTagName( $data, 'ext' );
659 
660  return $this->getResult()->addValue( 'query', $property, $data );
661  }
662 
663  protected function appendRightsInfo( $property ) {
664  $config = $this->getConfig();
665  $rightsPage = $config->get( 'RightsPage' );
666  if ( is_string( $rightsPage ) ) {
667  $title = Title::newFromText( $rightsPage );
668  $url = wfExpandUrl( $title, PROTO_CURRENT );
669  } else {
670  $title = false;
671  $url = $config->get( 'RightsUrl' );
672  }
673  $text = $config->get( 'RightsText' );
674  if ( $title && !strlen( $text ) ) {
675  $text = $title->getPrefixedText();
676  }
677 
678  $data = [
679  'url' => (string)$url,
680  'text' => (string)$text,
681  ];
682 
683  return $this->getResult()->addValue( 'query', $property, $data );
684  }
685 
686  protected function appendRestrictions( $property ) {
687  $config = $this->getConfig();
688  $data = [
689  'types' => $config->get( 'RestrictionTypes' ),
690  'levels' => $config->get( 'RestrictionLevels' ),
691  'cascadinglevels' => $config->get( 'CascadingRestrictionLevels' ),
692  'semiprotectedlevels' => $config->get( 'SemiprotectedRestrictionLevels' ),
693  ];
694 
695  ApiResult::setArrayType( $data['types'], 'BCarray' );
696  ApiResult::setArrayType( $data['levels'], 'BCarray' );
697  ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
698  ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
699 
700  ApiResult::setIndexedTagName( $data['types'], 'type' );
701  ApiResult::setIndexedTagName( $data['levels'], 'level' );
702  ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
703  ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
704 
705  return $this->getResult()->addValue( 'query', $property, $data );
706  }
707 
708  public function appendLanguages( $property ) {
709  $params = $this->extractRequestParams();
710  $langCode = $params['inlanguagecode'] ?? '';
711  $langNames = Language::fetchLanguageNames( $langCode );
712 
713  $data = [];
714 
715  foreach ( $langNames as $code => $name ) {
716  $lang = [
717  'code' => $code,
718  'bcp47' => LanguageCode::bcp47( $code ),
719  ];
720  ApiResult::setContentValue( $lang, 'name', $name );
721  $data[] = $lang;
722  }
723  ApiResult::setIndexedTagName( $data, 'lang' );
724 
725  return $this->getResult()->addValue( 'query', $property, $data );
726  }
727 
728  // Export information about which page languages will trigger
729  // language conversion. (T153341)
730  public function appendLanguageVariants( $property ) {
732  if ( $this->getConfig()->get( 'DisableLangConversion' ) ) {
733  // Ensure result is empty if language conversion is disabled.
734  $langNames = [];
735  }
736  sort( $langNames );
737 
738  $data = [];
739  foreach ( $langNames as $langCode ) {
740  $lang = Language::factory( $langCode );
741  if ( $lang->getConverter() instanceof FakeConverter ) {
742  // Only languages which do not return instances of
743  // FakeConverter implement language conversion.
744  continue;
745  }
746  $data[$langCode] = [];
747  ApiResult::setIndexedTagName( $data[$langCode], 'variant' );
748  ApiResult::setArrayType( $data[$langCode], 'kvp', 'code' );
749 
750  $variants = $lang->getVariants();
751  sort( $variants );
752  foreach ( $variants as $v ) {
753  $fallbacks = $lang->getConverter()->getVariantFallbacks( $v );
754  if ( !is_array( $fallbacks ) ) {
755  $fallbacks = [ $fallbacks ];
756  }
757  $data[$langCode][$v] = [
758  'fallbacks' => $fallbacks,
759  ];
761  $data[$langCode][$v]['fallbacks'], 'variant'
762  );
763  }
764  }
765  ApiResult::setIndexedTagName( $data, 'lang' );
766  ApiResult::setArrayType( $data, 'kvp', 'code' );
767 
768  return $this->getResult()->addValue( 'query', $property, $data );
769  }
770 
771  public function appendSkins( $property ) {
772  $data = [];
773  $allowed = Skin::getAllowedSkins();
774  $default = Skin::normalizeKey( 'default' );
775  foreach ( Skin::getSkinNames() as $name => $displayName ) {
776  $msg = $this->msg( "skinname-{$name}" );
777  $code = $this->getParameter( 'inlanguagecode' );
778  if ( $code && Language::isValidCode( $code ) ) {
779  $msg->inLanguage( $code );
780  } else {
781  $msg->inContentLanguage();
782  }
783  if ( $msg->exists() ) {
784  $displayName = $msg->text();
785  }
786  $skin = [ 'code' => $name ];
787  ApiResult::setContentValue( $skin, 'name', $displayName );
788  if ( !isset( $allowed[$name] ) ) {
789  $skin['unusable'] = true;
790  }
791  if ( $name === $default ) {
792  $skin['default'] = true;
793  }
794  $data[] = $skin;
795  }
796  ApiResult::setIndexedTagName( $data, 'skin' );
797 
798  return $this->getResult()->addValue( 'query', $property, $data );
799  }
800 
801  public function appendExtensionTags( $property ) {
802  $tags = array_map(
803  function ( $item ) {
804  return "<$item>";
805  },
806  MediaWikiServices::getInstance()->getParser()->getTags()
807  );
808  ApiResult::setArrayType( $tags, 'BCarray' );
809  ApiResult::setIndexedTagName( $tags, 't' );
810 
811  return $this->getResult()->addValue( 'query', $property, $tags );
812  }
813 
814  public function appendFunctionHooks( $property ) {
815  $hooks = MediaWikiServices::getInstance()->getParser()->getFunctionHooks();
816  ApiResult::setArrayType( $hooks, 'BCarray' );
817  ApiResult::setIndexedTagName( $hooks, 'h' );
818 
819  return $this->getResult()->addValue( 'query', $property, $hooks );
820  }
821 
822  public function appendVariables( $property ) {
823  $variables = MediaWikiServices::getInstance()->getMagicWordFactory()->getVariableIDs();
824  ApiResult::setArrayType( $variables, 'BCarray' );
825  ApiResult::setIndexedTagName( $variables, 'v' );
826 
827  return $this->getResult()->addValue( 'query', $property, $variables );
828  }
829 
830  public function appendProtocols( $property ) {
831  // Make a copy of the global so we don't try to set the _element key of it - T47130
832  $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
833  ApiResult::setArrayType( $protocols, 'BCarray' );
834  ApiResult::setIndexedTagName( $protocols, 'p' );
835 
836  return $this->getResult()->addValue( 'query', $property, $protocols );
837  }
838 
839  public function appendDefaultOptions( $property ) {
841  $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
842  return $this->getResult()->addValue( 'query', $property, $options );
843  }
844 
845  public function appendUploadDialog( $property ) {
846  $config = $this->getConfig()->get( 'UploadDialog' );
847  return $this->getResult()->addValue( 'query', $property, $config );
848  }
849 
850  public function appendSubscribedHooks( $property ) {
851  $hooks = $this->getConfig()->get( 'Hooks' );
852  $myWgHooks = $hooks;
853  ksort( $myWgHooks );
854 
855  $data = [];
856  foreach ( $myWgHooks as $name => $subscribers ) {
857  $arr = [
858  'name' => $name,
859  'subscribers' => array_map( [ SpecialVersion::class, 'arrayToString' ], $subscribers ),
860  ];
861 
862  ApiResult::setArrayType( $arr['subscribers'], 'array' );
863  ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
864  $data[] = $arr;
865  }
866 
867  ApiResult::setIndexedTagName( $data, 'hook' );
868 
869  return $this->getResult()->addValue( 'query', $property, $data );
870  }
871 
872  public function getCacheMode( $params ) {
873  // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
874  if (
875  count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
876  !is_null( $params['prop'] ) &&
877  in_array( 'interwikimap', $params['prop'] )
878  ) {
879  return 'anon-public-user-private';
880  }
881 
882  return 'public';
883  }
884 
885  public function getAllowedParams() {
886  return [
887  'prop' => [
888  ApiBase::PARAM_DFLT => 'general',
889  ApiBase::PARAM_ISMULTI => true,
891  'general',
892  'namespaces',
893  'namespacealiases',
894  'specialpagealiases',
895  'magicwords',
896  'interwikimap',
897  'dbrepllag',
898  'statistics',
899  'usergroups',
900  'libraries',
901  'extensions',
902  'fileextensions',
903  'rightsinfo',
904  'restrictions',
905  'languages',
906  'languagevariants',
907  'skins',
908  'extensiontags',
909  'functionhooks',
910  'showhooks',
911  'variables',
912  'protocols',
913  'defaultoptions',
914  'uploaddialog',
915  ],
917  ],
918  'filteriw' => [
920  'local',
921  '!local',
922  ]
923  ],
924  'showalldb' => false,
925  'numberingroup' => false,
926  'inlanguagecode' => null,
927  ];
928  }
929 
930  protected function getExamplesMessages() {
931  return [
932  'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
933  => 'apihelp-query+siteinfo-example-simple',
934  'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
935  => 'apihelp-query+siteinfo-example-interwiki',
936  'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
937  => 'apihelp-query+siteinfo-example-replag',
938  ];
939  }
940 
941  public function getHelpUrls() {
942  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Siteinfo';
943  }
944 }
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below...
Definition: ApiBase.php:94
getDB()
Get the Query database connection (read-only)
static fetchLanguageNames( $inLanguage=self::AS_AUTONYMS, $include='mw')
Get an array of language names, indexed by code.
Definition: Language.php:814
getResult()
Get the result object.
Definition: ApiBase.php:640
static normalizeKey( $key)
Normalize a skin preference value to a form that can be loaded.
Definition: Skin.php:104
static jobs()
Total number of jobs in the job queue.
Definition: SiteStats.php:179
appendMagicWords( $property)
appendDbReplLagInfo( $property, $includeAll)
appendLanguageVariants( $property)
appendLanguages( $property)
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:648
$IP
Definition: WebStart.php:41
appendUserGroups( $property, $numberInGroup)
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:55
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
appendDefaultOptions( $property)
if(!isset( $args[0])) $lang
This is a base class for all Query modules.
static getWikiIdFromDbDomain( $domain)
Get the wiki ID of a database domain.
Definition: WikiMap.php:269
static getSkinNames()
Fetch the set of available skins.
Definition: Skin.php:57
appendUploadDialog( $property)
appendStatistics( $property)
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:2005
const PROTO_CURRENT
Definition: Defines.php:202
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user...
Definition: ApiBase.php:761
const META_TYPE
Key for the &#39;type&#39; metadata item.
Definition: ApiResult.php:110
A fake language variant converter.
static activeUsers()
Definition: SiteStats.php:130
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:164
appendInstalledLibraries( $property)
appendNamespaces( $property)
static getMaxUploadSize( $forType=null)
Get the MediaWiki maximum uploaded file size for given type of upload, based on $wgMaxUploadSize.
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
Definition: ApiResult.php:478
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:616
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition: ApiBase.php:876
static getAllGroups()
Return the set of defined explicit groups.
Definition: User.php:4805
static numberingroup( $group)
Find the number of users in a given user group.
Definition: SiteStats.php:150
appendSubscribedHooks( $property)
static edits()
Definition: SiteStats.php:94
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfReadOnly()
Check whether the wiki is in read-only mode.
appendSkins( $property)
static getExtAuthorsFileName( $extDir)
Obtains the full path of an extensions authors or credits file if one exists.
__construct(ApiQuery $query, $moduleName)
appendRestrictions( $property)
appendGeneralInfo( $property)
appendVariables( $property)
static getGitHeadSha1( $dir)
$filter
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:212
const PROTO_RELATIVE
Definition: Defines.php:201
static getExtLicenseFileName( $extDir)
Obtains the full path of an extensions copying or license file if one exists.
$GLOBALS['IP']
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don&#39;t need a full Title object...
Definition: SpecialPage.php:83
This is the main query class.
Definition: ApiQuery.php:37
static isEnabled()
Returns true if uploads are enabled.
Definition: UploadBase.php:135
appendExtensionTags( $property)
static images()
Definition: SiteStats.php:139
static getCurrentWikiDbDomain()
Definition: WikiMap.php:293
$linkPrefixCharset
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
appendSpecialPageAliases( $property)
static articles()
Definition: SiteStats.php:103
static pages()
Definition: SiteStats.php:112
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:58
appendFunctionHooks( $property)
if(!is_readable( $file)) $ext
Definition: router.php:48
const META_BC_BOOLS
Key for the &#39;BC bools&#39; metadata item.
Definition: ApiResult.php:136
appendProtocols( $property)
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:2211
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:694
appendInterwikiMap( $property, $filter)
Reads an installed.json file and provides accessors to get what is installed.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
static getGitCurrentBranch( $dir)
static getDefaultOptions()
Combine the language default options with any site-specific options and add the default language vari...
Definition: User.php:1648
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:728
appendFileExtensions( $property)
A query action to return meta information about the wiki site.
static isValidCode( $code)
Returns true if a language code string is of a valid form, whether or not it exists.
Definition: Language.php:379
static array $languagesWithVariants
languages supporting variants
static getAllowedSkins()
Fetch the list of user-selectable skins in regards to $wgSkipSkins.
Definition: Skin.php:83
static users()
Definition: SiteStats.php:121
appendNamespaceAliases( $property)
appendRightsInfo( $property)
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:319
appendExtensions( $property)
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.