MediaWiki  master
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  $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;
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 ) {
731  $langNames = LanguageConverter::$languagesWithVariants;
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 = MediaWikiServices::getInstance()->getLanguageFactory()
741  ->getLanguage( $langCode );
742  if ( $lang->getConverter() instanceof FakeConverter ) {
743  // Only languages which do not return instances of
744  // FakeConverter implement language conversion.
745  continue;
746  }
747  $data[$langCode] = [];
748  ApiResult::setIndexedTagName( $data[$langCode], 'variant' );
749  ApiResult::setArrayType( $data[$langCode], 'kvp', 'code' );
750 
751  $variants = $lang->getVariants();
752  sort( $variants );
753  foreach ( $variants as $v ) {
754  $fallbacks = $lang->getConverter()->getVariantFallbacks( $v );
755  if ( !is_array( $fallbacks ) ) {
756  $fallbacks = [ $fallbacks ];
757  }
758  $data[$langCode][$v] = [
759  'fallbacks' => $fallbacks,
760  ];
762  $data[$langCode][$v]['fallbacks'], 'variant'
763  );
764  }
765  }
766  ApiResult::setIndexedTagName( $data, 'lang' );
767  ApiResult::setArrayType( $data, 'kvp', 'code' );
768 
769  return $this->getResult()->addValue( 'query', $property, $data );
770  }
771 
772  public function appendSkins( $property ) {
773  $data = [];
774  $allowed = Skin::getAllowedSkins();
775  $default = Skin::normalizeKey( 'default' );
776  foreach ( Skin::getSkinNames() as $name => $displayName ) {
777  $msg = $this->msg( "skinname-{$name}" );
778  $code = $this->getParameter( 'inlanguagecode' );
779  if ( $code && Language::isValidCode( $code ) ) {
780  $msg->inLanguage( $code );
781  } else {
782  $msg->inContentLanguage();
783  }
784  if ( $msg->exists() ) {
785  $displayName = $msg->text();
786  }
787  $skin = [ 'code' => $name ];
788  ApiResult::setContentValue( $skin, 'name', $displayName );
789  if ( !isset( $allowed[$name] ) ) {
790  $skin['unusable'] = true;
791  }
792  if ( $name === $default ) {
793  $skin['default'] = true;
794  }
795  $data[] = $skin;
796  }
797  ApiResult::setIndexedTagName( $data, 'skin' );
798 
799  return $this->getResult()->addValue( 'query', $property, $data );
800  }
801 
802  public function appendExtensionTags( $property ) {
803  $tags = array_map(
804  function ( $item ) {
805  return "<$item>";
806  },
807  MediaWikiServices::getInstance()->getParser()->getTags()
808  );
809  ApiResult::setArrayType( $tags, 'BCarray' );
810  ApiResult::setIndexedTagName( $tags, 't' );
811 
812  return $this->getResult()->addValue( 'query', $property, $tags );
813  }
814 
815  public function appendFunctionHooks( $property ) {
816  $hooks = MediaWikiServices::getInstance()->getParser()->getFunctionHooks();
817  ApiResult::setArrayType( $hooks, 'BCarray' );
818  ApiResult::setIndexedTagName( $hooks, 'h' );
819 
820  return $this->getResult()->addValue( 'query', $property, $hooks );
821  }
822 
823  public function appendVariables( $property ) {
824  $variables = MediaWikiServices::getInstance()->getMagicWordFactory()->getVariableIDs();
825  ApiResult::setArrayType( $variables, 'BCarray' );
826  ApiResult::setIndexedTagName( $variables, 'v' );
827 
828  return $this->getResult()->addValue( 'query', $property, $variables );
829  }
830 
831  public function appendProtocols( $property ) {
832  // Make a copy of the global so we don't try to set the _element key of it - T47130
833  $protocols = array_values( $this->getConfig()->get( 'UrlProtocols' ) );
834  ApiResult::setArrayType( $protocols, 'BCarray' );
835  ApiResult::setIndexedTagName( $protocols, 'p' );
836 
837  return $this->getResult()->addValue( 'query', $property, $protocols );
838  }
839 
840  public function appendDefaultOptions( $property ) {
841  $options = User::getDefaultOptions();
842  $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
843  return $this->getResult()->addValue( 'query', $property, $options );
844  }
845 
846  public function appendUploadDialog( $property ) {
847  $config = $this->getConfig()->get( 'UploadDialog' );
848  return $this->getResult()->addValue( 'query', $property, $config );
849  }
850 
851  public function appendSubscribedHooks( $property ) {
852  $hooks = $this->getConfig()->get( 'Hooks' );
853  $myWgHooks = $hooks;
854  ksort( $myWgHooks );
855 
856  $data = [];
857  foreach ( $myWgHooks as $name => $subscribers ) {
858  $arr = [
859  'name' => $name,
860  'subscribers' => array_map( [ SpecialVersion::class, 'arrayToString' ], $subscribers ),
861  ];
862 
863  ApiResult::setArrayType( $arr['subscribers'], 'array' );
864  ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
865  $data[] = $arr;
866  }
867 
868  ApiResult::setIndexedTagName( $data, 'hook' );
869 
870  return $this->getResult()->addValue( 'query', $property, $data );
871  }
872 
873  public function getCacheMode( $params ) {
874  // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
875  if (
876  count( $this->getConfig()->get( 'ExtraInterlanguageLinkPrefixes' ) ) &&
877  $params['prop'] !== null &&
878  in_array( 'interwikimap', $params['prop'] )
879  ) {
880  return 'anon-public-user-private';
881  }
882 
883  return 'public';
884  }
885 
886  public function getAllowedParams() {
887  return [
888  'prop' => [
889  ApiBase::PARAM_DFLT => 'general',
890  ApiBase::PARAM_ISMULTI => true,
892  'general',
893  'namespaces',
894  'namespacealiases',
895  'specialpagealiases',
896  'magicwords',
897  'interwikimap',
898  'dbrepllag',
899  'statistics',
900  'usergroups',
901  'libraries',
902  'extensions',
903  'fileextensions',
904  'rightsinfo',
905  'restrictions',
906  'languages',
907  'languagevariants',
908  'skins',
909  'extensiontags',
910  'functionhooks',
911  'showhooks',
912  'variables',
913  'protocols',
914  'defaultoptions',
915  'uploaddialog',
916  ],
918  ],
919  'filteriw' => [
921  'local',
922  '!local',
923  ]
924  ],
925  'showalldb' => false,
926  'numberingroup' => false,
927  'inlanguagecode' => null,
928  ];
929  }
930 
931  protected function getExamplesMessages() {
932  return [
933  'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
934  => 'apihelp-query+siteinfo-example-simple',
935  'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
936  => 'apihelp-query+siteinfo-example-interwiki',
937  'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
938  => 'apihelp-query+siteinfo-example-replag',
939  ];
940  }
941 
942  public function getHelpUrls() {
943  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Siteinfo';
944  }
945 }
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:317
ApiQuery
This is the main query class.
Definition: ApiQuery.php:37
WikiMap\getCurrentWikiDbDomain
static getCurrentWikiDbDomain()
Definition: WikiMap.php:293
SpecialVersion\getExtAuthorsFileName
static getExtAuthorsFileName( $extDir)
Obtains the full path of an extensions authors or credits file if one exists.
Definition: SpecialVersion.php:1079
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:130
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:35
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:2015
ApiBase\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiBase.php:373
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1871
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:95
SiteStats\pages
static pages()
Definition: SiteStats.php:112
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:644
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:1170
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1263
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:646
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:1704
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:269
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
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:765
$title
$title
Definition: testCompression.php:36
ApiBase\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiBase.php:382
SpecialVersion\getExtLicenseFileName
static getExtLicenseFileName( $extDir)
Obtains the full path of an extensions copying or license file if one exists.
Definition: SpecialVersion.php:1108
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:1192
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:1182
User\getAllGroups
static getAllGroups()
Return the set of defined explicit groups.
Definition: User.php:4884
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:405
$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:1183
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:56
ApiBase\getParameter
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition: ApiBase.php:881
LanguageCode\bcp47
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: LanguageCode.php:176
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:59
ApiBase\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiBase.php:398
$ext
if(!is_readable( $file)) $ext
Definition: router.php:48
Title\legalChars
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:692
ApiQueryBase\setContinueEnumParameter
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
Definition: ApiQueryBase.php:511
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:854
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:165
$IP
$IP
Definition: WebStart.php:41
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:2222
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:50