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