MediaWiki REL1_39
ApiQuerySiteinfo.php
Go to the documentation of this file.
1<?php
35
42
44 private $userOptionsLookup;
45
47 private $userGroupManager;
48
50 private $languageConverterFactory;
51
53 private $languageFactory;
54
56 private $languageNameUtils;
57
59 private $contentLanguage;
60
62 private $namespaceInfo;
63
65 private $interwikiLookup;
66
68 private $parser;
69
71 private $magicWordFactory;
72
74 private $specialPageFactory;
75
77 private $skinFactory;
78
80 private $loadBalancer;
81
83 private $readOnlyMode;
84
103 public function __construct(
104 ApiQuery $query,
105 $moduleName,
106 UserOptionsLookup $userOptionsLookup,
107 UserGroupManager $userGroupManager,
108 LanguageConverterFactory $languageConverterFactory,
109 LanguageFactory $languageFactory,
110 LanguageNameUtils $languageNameUtils,
111 Language $contentLanguage,
112 NamespaceInfo $namespaceInfo,
113 InterwikiLookup $interwikiLookup,
114 Parser $parser,
115 MagicWordFactory $magicWordFactory,
116 SpecialPageFactory $specialPageFactory,
117 SkinFactory $skinFactory,
118 ILoadBalancer $loadBalancer,
119 ReadOnlyMode $readOnlyMode
120 ) {
121 parent::__construct( $query, $moduleName, 'si' );
122 $this->userOptionsLookup = $userOptionsLookup;
123 $this->userGroupManager = $userGroupManager;
124 $this->languageConverterFactory = $languageConverterFactory;
125 $this->languageFactory = $languageFactory;
126 $this->languageNameUtils = $languageNameUtils;
127 $this->contentLanguage = $contentLanguage;
128 $this->namespaceInfo = $namespaceInfo;
129 $this->interwikiLookup = $interwikiLookup;
130 $this->parser = $parser;
131 $this->magicWordFactory = $magicWordFactory;
132 $this->specialPageFactory = $specialPageFactory;
133 $this->skinFactory = $skinFactory;
134 $this->loadBalancer = $loadBalancer;
135 $this->readOnlyMode = $readOnlyMode;
136 }
137
138 public function execute() {
139 $params = $this->extractRequestParams();
140 $done = [];
141 $fit = false;
142 foreach ( $params['prop'] as $p ) {
143 switch ( $p ) {
144 case 'general':
145 $fit = $this->appendGeneralInfo( $p );
146 break;
147 case 'namespaces':
148 $fit = $this->appendNamespaces( $p );
149 break;
150 case 'namespacealiases':
151 $fit = $this->appendNamespaceAliases( $p );
152 break;
153 case 'specialpagealiases':
154 $fit = $this->appendSpecialPageAliases( $p );
155 break;
156 case 'magicwords':
157 $fit = $this->appendMagicWords( $p );
158 break;
159 case 'interwikimap':
160 $fit = $this->appendInterwikiMap( $p, $params['filteriw'] );
161 break;
162 case 'dbrepllag':
163 $fit = $this->appendDbReplLagInfo( $p, $params['showalldb'] );
164 break;
165 case 'statistics':
166 $fit = $this->appendStatistics( $p );
167 break;
168 case 'usergroups':
169 $fit = $this->appendUserGroups( $p, $params['numberingroup'] );
170 break;
171 case 'libraries':
172 $fit = $this->appendInstalledLibraries( $p );
173 break;
174 case 'extensions':
175 $fit = $this->appendExtensions( $p );
176 break;
177 case 'fileextensions':
178 $fit = $this->appendFileExtensions( $p );
179 break;
180 case 'rightsinfo':
181 $fit = $this->appendRightsInfo( $p );
182 break;
183 case 'restrictions':
184 $fit = $this->appendRestrictions( $p );
185 break;
186 case 'languages':
187 $fit = $this->appendLanguages( $p );
188 break;
189 case 'languagevariants':
190 $fit = $this->appendLanguageVariants( $p );
191 break;
192 case 'skins':
193 $fit = $this->appendSkins( $p );
194 break;
195 case 'extensiontags':
196 $fit = $this->appendExtensionTags( $p );
197 break;
198 case 'functionhooks':
199 $fit = $this->appendFunctionHooks( $p );
200 break;
201 case 'showhooks':
202 $fit = $this->appendSubscribedHooks( $p );
203 break;
204 case 'variables':
205 $fit = $this->appendVariables( $p );
206 break;
207 case 'protocols':
208 $fit = $this->appendProtocols( $p );
209 break;
210 case 'defaultoptions':
211 $fit = $this->appendDefaultOptions( $p );
212 break;
213 case 'uploaddialog':
214 $fit = $this->appendUploadDialog( $p );
215 break;
216 default:
217 ApiBase::dieDebug( __METHOD__, "Unknown prop=$p" ); // @codeCoverageIgnore
218 }
219 if ( !$fit ) {
220 // Abuse siprop as a query-continue parameter
221 // and set it to all unprocessed props
222 $this->setContinueEnumParameter( 'prop', implode( '|',
223 array_diff( $params['prop'], $done ) ) );
224 break;
225 }
226 $done[] = $p;
227 }
228 }
229
230 protected function appendGeneralInfo( $property ) {
231 $config = $this->getConfig();
232
233 $data = [];
234 $mainPage = Title::newMainPage();
235 $data['mainpage'] = $mainPage->getPrefixedText();
236 $data['base'] = wfExpandUrl( $mainPage->getFullURL(), PROTO_CURRENT );
237 $data['sitename'] = $config->get( MainConfigNames::Sitename );
238 $data['mainpageisdomainroot'] = (bool)$config->get( MainConfigNames::MainPageIsDomainRoot );
239
240 // A logo can either be a relative or an absolute path
241 // make sure we always return an absolute path
242 $logo = SkinModule::getAvailableLogos( $config );
243 $data['logo'] = wfExpandUrl( $logo['1x'], PROTO_RELATIVE );
244
245 $data['generator'] = 'MediaWiki ' . MW_VERSION;
246
247 $data['phpversion'] = PHP_VERSION;
248 $data['phpsapi'] = PHP_SAPI;
249 $data['dbtype'] = $config->get( MainConfigNames::DBtype );
250 $data['dbversion'] = $this->getDB()->getServerVersion();
251
252 $allowFrom = [ '' ];
253 $allowException = true;
254 if ( !$config->get( MainConfigNames::AllowExternalImages ) ) {
255 $data['imagewhitelistenabled'] =
256 (bool)$config->get( MainConfigNames::EnableImageWhitelist );
257 $allowFrom = $config->get( MainConfigNames::AllowExternalImagesFrom );
258 $allowException = !empty( $allowFrom );
259 }
260 if ( $allowException ) {
261 $data['externalimages'] = (array)$allowFrom;
262 ApiResult::setIndexedTagName( $data['externalimages'], 'prefix' );
263 }
264
265 $data['langconversion'] = !$this->languageConverterFactory->isConversionDisabled();
266 $data['linkconversion'] = !$this->languageConverterFactory->isLinkConversionDisabled();
267 // For backwards compatibility (soft deprecated since MW 1.36)
268 $data['titleconversion'] = $data['linkconversion'];
269
270 $contLangConverter = $this->languageConverterFactory->getLanguageConverter( $this->contentLanguage );
271 if ( $this->contentLanguage->linkPrefixExtension() ) {
272 $linkPrefixCharset = $this->contentLanguage->linkPrefixCharset();
273 $data['linkprefixcharset'] = $linkPrefixCharset;
274 // For backwards compatibility
275 $data['linkprefix'] = "/^((?>.*[^$linkPrefixCharset]|))(.+)$/sDu";
276 } else {
277 $data['linkprefixcharset'] = '';
278 $data['linkprefix'] = '';
279 }
280
281 $linktrail = $this->contentLanguage->linkTrail();
282 $data['linktrail'] = $linktrail ?: '';
283
284 $data['legaltitlechars'] = Title::legalChars();
285 $data['invalidusernamechars'] = $config->get( MainConfigNames::InvalidUsernameCharacters );
286
287 $data['allunicodefixes'] = (bool)$config->get( MainConfigNames::AllUnicodeFixes );
288 $data['fixarabicunicode'] = true; // Config removed in 1.35, always true
289 $data['fixmalayalamunicode'] = true; // Config removed in 1.35, always true
290
291 $baseDir = $this->getConfig()->get( MainConfigNames::BaseDirectory );
292 $git = SpecialVersion::getGitHeadSha1( $baseDir );
293 if ( $git ) {
294 $data['git-hash'] = $git;
295 $data['git-branch'] =
297 }
298
299 // 'case-insensitive' option is reserved for future
300 $data['case'] =
301 $config->get( MainConfigNames::CapitalLinks ) ? 'first-letter' : 'case-sensitive';
302 $data['lang'] = $config->get( MainConfigNames::LanguageCode );
303
304 $fallbacks = [];
305 foreach ( $this->contentLanguage->getFallbackLanguages() as $code ) {
306 $fallbacks[] = [ 'code' => $code ];
307 }
308 $data['fallback'] = $fallbacks;
309 ApiResult::setIndexedTagName( $data['fallback'], 'lang' );
310
311 if ( $contLangConverter->hasVariants() ) {
312 $variants = [];
313 foreach ( $contLangConverter->getVariants() as $code ) {
314 $variants[] = [
315 'code' => $code,
316 'name' => $this->contentLanguage->getVariantname( $code ),
317 ];
318 }
319 $data['variants'] = $variants;
320 ApiResult::setIndexedTagName( $data['variants'], 'lang' );
321 }
322
323 $data['rtl'] = $this->contentLanguage->isRTL();
324 $data['fallback8bitEncoding'] = $this->contentLanguage->fallback8bitEncoding();
325
326 $data['readonly'] = $this->readOnlyMode->isReadOnly();
327 if ( $data['readonly'] ) {
328 $data['readonlyreason'] = $this->readOnlyMode->getReason();
329 }
330 $data['writeapi'] = true; // Deprecated since MW 1.32
331
332 $data['maxarticlesize'] = $config->get( MainConfigNames::MaxArticleSize ) * 1024;
333
334 $tz = $config->get( MainConfigNames::Localtimezone );
335 $offset = $config->get( MainConfigNames::LocalTZoffset );
336 $data['timezone'] = $tz;
337 $data['timeoffset'] = (int)$offset;
338 $data['articlepath'] = $config->get( MainConfigNames::ArticlePath );
339 $data['scriptpath'] = $config->get( MainConfigNames::ScriptPath );
340 $data['script'] = $config->get( MainConfigNames::Script );
341 $data['variantarticlepath'] = $config->get( MainConfigNames::VariantArticlePath );
342 $data[ApiResult::META_BC_BOOLS][] = 'variantarticlepath';
343 $data['server'] = $config->get( MainConfigNames::Server );
344 $data['servername'] = $config->get( MainConfigNames::ServerName );
345 $data['wikiid'] = WikiMap::getCurrentWikiId();
346 $data['time'] = wfTimestamp( TS_ISO_8601, time() );
347
348 $data['misermode'] = (bool)$config->get( MainConfigNames::MiserMode );
349
350 $data['uploadsenabled'] = UploadBase::isEnabled();
351 $data['maxuploadsize'] = UploadBase::getMaxUploadSize();
352 $data['minuploadchunksize'] = ApiUpload::getMinUploadChunkSize( $config );
353
354 $data['galleryoptions'] = $config->get( MainConfigNames::GalleryOptions );
355
356 $data['thumblimits'] = $config->get( MainConfigNames::ThumbLimits );
357 ApiResult::setArrayType( $data['thumblimits'], 'BCassoc' );
358 ApiResult::setIndexedTagName( $data['thumblimits'], 'limit' );
359 $data['imagelimits'] = [];
360 ApiResult::setArrayType( $data['imagelimits'], 'BCassoc' );
361 ApiResult::setIndexedTagName( $data['imagelimits'], 'limit' );
362 foreach ( $config->get( MainConfigNames::ImageLimits ) as $k => $limit ) {
363 $data['imagelimits'][$k] = [ 'width' => $limit[0], 'height' => $limit[1] ];
364 }
365
366 $favicon = $config->get( MainConfigNames::Favicon );
367 if ( $favicon ) {
368 // Expand any local path to full URL to improve API usability (T77093).
369 $data['favicon'] = wfExpandUrl( $favicon );
370 }
371
372 $data['centralidlookupprovider'] =
373 $config->get( MainConfigNames::CentralIdLookupProvider );
374 $providerIds = array_keys( $config->get( MainConfigNames::CentralIdLookupProviders ) );
375 $data['allcentralidlookupproviders'] = $providerIds;
376
377 $data['interwikimagic'] = (bool)$config->get( MainConfigNames::InterwikiMagic );
378 $data['magiclinks'] = $config->get( MainConfigNames::EnableMagicLinks );
379
380 $data['categorycollation'] = $config->get( MainConfigNames::CategoryCollation );
381
382 $data['nofollowlinks'] = $config->get( MainConfigNames::NoFollowLinks );
383 $data['nofollownsexceptions'] = $config->get( MainConfigNames::NoFollowNsExceptions );
384 $data['nofollowdomainexceptions'] = $config->get( MainConfigNames::NoFollowDomainExceptions );
385 $data['externallinktarget'] = $config->get( MainConfigNames::ExternalLinkTarget );
386
387 $this->getHookRunner()->onAPIQuerySiteInfoGeneralInfo( $this, $data );
388
389 return $this->getResult()->addValue( 'query', $property, $data );
390 }
391
392 protected function appendNamespaces( $property ) {
393 $nsProtection = $this->getConfig()->get( MainConfigNames::NamespaceProtection );
394
395 $data = [
396 ApiResult::META_TYPE => 'assoc',
397 ];
398 foreach (
399 $this->contentLanguage->getFormattedNamespaces()
400 as $ns => $title
401 ) {
402 $data[$ns] = [
403 'id' => (int)$ns,
404 'case' => $this->namespaceInfo->isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
405 ];
406 ApiResult::setContentValue( $data[$ns], 'name', $title );
407 $canonical = $this->namespaceInfo->getCanonicalName( $ns );
408
409 $data[$ns]['subpages'] = $this->namespaceInfo->hasSubpages( $ns );
410
411 if ( $canonical ) {
412 $data[$ns]['canonical'] = strtr( $canonical, '_', ' ' );
413 }
414
415 $data[$ns]['content'] = $this->namespaceInfo->isContent( $ns );
416 $data[$ns]['nonincludable'] = $this->namespaceInfo->isNonincludable( $ns );
417
418 if ( isset( $nsProtection[$ns] ) ) {
419 if ( is_array( $nsProtection[$ns] ) ) {
420 $specificNs = implode( "|", array_filter( $nsProtection[$ns] ) );
421 } elseif ( $nsProtection[$ns] !== '' ) {
422 $specificNs = $nsProtection[$ns];
423 }
424 if ( isset( $specificNs ) && $specificNs !== '' ) {
425 $data[$ns]['namespaceprotection'] = $specificNs;
426 }
427 }
428
429 $contentmodel = $this->namespaceInfo->getNamespaceContentModel( $ns );
430 if ( $contentmodel ) {
431 $data[$ns]['defaultcontentmodel'] = $contentmodel;
432 }
433 }
434
435 ApiResult::setArrayType( $data, 'assoc' );
436 ApiResult::setIndexedTagName( $data, 'ns' );
437
438 return $this->getResult()->addValue( 'query', $property, $data );
439 }
440
441 protected function appendNamespaceAliases( $property ) {
442 $aliases = $this->contentLanguage->getNamespaceAliases();
443 $namespaces = $this->contentLanguage->getNamespaces();
444 $data = [];
445 foreach ( $aliases as $title => $ns ) {
446 if ( $namespaces[$ns] == $title ) {
447 // Don't list duplicates
448 continue;
449 }
450 $item = [
451 'id' => (int)$ns
452 ];
453 ApiResult::setContentValue( $item, 'alias', strtr( $title, '_', ' ' ) );
454 $data[] = $item;
455 }
456
457 sort( $data );
458
459 ApiResult::setIndexedTagName( $data, 'ns' );
460
461 return $this->getResult()->addValue( 'query', $property, $data );
462 }
463
464 protected function appendSpecialPageAliases( $property ) {
465 $data = [];
466 $aliases = $this->contentLanguage->getSpecialPageAliases();
467 foreach ( $this->specialPageFactory->getNames() as $specialpage ) {
468 if ( isset( $aliases[$specialpage] ) ) {
469 $arr = [ 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] ];
470 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
471 $data[] = $arr;
472 }
473 }
474 ApiResult::setIndexedTagName( $data, 'specialpage' );
475
476 return $this->getResult()->addValue( 'query', $property, $data );
477 }
478
479 protected function appendMagicWords( $property ) {
480 $data = [];
481 foreach (
482 $this->contentLanguage->getMagicWords()
483 as $magicword => $aliases
484 ) {
485 $caseSensitive = array_shift( $aliases );
486 $arr = [ 'name' => $magicword, 'aliases' => $aliases ];
487 $arr['case-sensitive'] = (bool)$caseSensitive;
488 ApiResult::setIndexedTagName( $arr['aliases'], 'alias' );
489 $data[] = $arr;
490 }
491 ApiResult::setIndexedTagName( $data, 'magicword' );
492
493 return $this->getResult()->addValue( 'query', $property, $data );
494 }
495
496 protected function appendInterwikiMap( $property, $filter ) {
497 if ( $filter === 'local' ) {
498 $local = true;
499 } elseif ( $filter === '!local' ) {
500 $local = false;
501 } else {
502 // $filter === null
503 $local = null;
504 }
505
506 $params = $this->extractRequestParams();
507 $langCode = $params['inlanguagecode'] ?? '';
508 $interwikiMagic = $this->getConfig()->get( MainConfigNames::InterwikiMagic );
509
510 if ( $interwikiMagic ) {
511 $langNames = $this->languageNameUtils->getLanguageNames( $langCode );
512 }
513
514 $getPrefixes = $this->interwikiLookup->getAllPrefixes( $local );
515 $extraLangPrefixes = $this->getConfig()->get( MainConfigNames::ExtraInterlanguageLinkPrefixes );
516 $localInterwikis = $this->getConfig()->get( MainConfigNames::LocalInterwikis );
517 $data = [];
518
519 foreach ( $getPrefixes as $row ) {
520 $prefix = $row['iw_prefix'];
521 $val = [];
522 $val['prefix'] = $prefix;
523 if ( isset( $row['iw_local'] ) && $row['iw_local'] == '1' ) {
524 $val['local'] = true;
525 }
526 if ( isset( $row['iw_trans'] ) && $row['iw_trans'] == '1' ) {
527 $val['trans'] = true;
528 }
529
530 if ( $interwikiMagic && isset( $langNames[$prefix] ) ) {
531 $val['language'] = $langNames[$prefix];
532 }
533 if ( in_array( $prefix, $localInterwikis ) ) {
534 $val['localinterwiki'] = true;
535 }
536 if ( $interwikiMagic && in_array( $prefix, $extraLangPrefixes ) ) {
537 $val['extralanglink'] = true;
538
539 $linktext = $this->msg( "interlanguage-link-$prefix" );
540 if ( !$linktext->isDisabled() ) {
541 $val['linktext'] = $linktext->text();
542 }
543
544 $sitename = $this->msg( "interlanguage-link-sitename-$prefix" );
545 if ( !$sitename->isDisabled() ) {
546 $val['sitename'] = $sitename->text();
547 }
548 }
549
550 $val['url'] = wfExpandUrl( $row['iw_url'], PROTO_CURRENT );
551 $val['protorel'] = substr( $row['iw_url'], 0, 2 ) == '//';
552 if ( isset( $row['iw_wikiid'] ) && $row['iw_wikiid'] !== '' ) {
553 $val['wikiid'] = $row['iw_wikiid'];
554 }
555 if ( isset( $row['iw_api'] ) && $row['iw_api'] !== '' ) {
556 $val['api'] = $row['iw_api'];
557 }
558
559 $data[] = $val;
560 }
561
562 ApiResult::setIndexedTagName( $data, 'iw' );
563
564 return $this->getResult()->addValue( 'query', $property, $data );
565 }
566
567 protected function appendDbReplLagInfo( $property, $includeAll ) {
568 $data = [];
569 $showHostnames = $this->getConfig()->get( MainConfigNames::ShowHostnames );
570 if ( $includeAll ) {
571 if ( !$showHostnames ) {
572 $this->dieWithError( 'apierror-siteinfo-includealldenied', 'includeAllDenied' );
573 }
574
575 $lags = $this->loadBalancer->getLagTimes();
576 foreach ( $lags as $i => $lag ) {
577 $data[] = [
578 'host' => $this->loadBalancer->getServerName( $i ),
579 'lag' => $lag
580 ];
581 }
582 } else {
583 list( , $lag, $index ) = $this->loadBalancer->getMaxLag();
584 $data[] = [
585 'host' => $showHostnames
586 ? $this->loadBalancer->getServerName( $index )
587 : '',
588 'lag' => $lag
589 ];
590 }
591
592 ApiResult::setIndexedTagName( $data, 'db' );
593
594 return $this->getResult()->addValue( 'query', $property, $data );
595 }
596
597 protected function appendStatistics( $property ) {
598 $data = [];
599 $data['pages'] = (int)SiteStats::pages();
600 $data['articles'] = (int)SiteStats::articles();
601 $data['edits'] = (int)SiteStats::edits();
602 $data['images'] = (int)SiteStats::images();
603 $data['users'] = (int)SiteStats::users();
604 $data['activeusers'] = (int)SiteStats::activeUsers();
605 $data['admins'] = (int)SiteStats::numberingroup( 'sysop' );
606 $data['jobs'] = (int)SiteStats::jobs();
607
608 $this->getHookRunner()->onAPIQuerySiteInfoStatisticsInfo( $data );
609
610 return $this->getResult()->addValue( 'query', $property, $data );
611 }
612
613 protected function appendUserGroups( $property, $numberInGroup ) {
614 $config = $this->getConfig();
615
616 $data = [];
617 $result = $this->getResult();
618 $allGroups = array_values( $this->userGroupManager->listAllGroups() );
619 foreach ( $config->get( MainConfigNames::GroupPermissions ) as $group => $permissions ) {
620 $arr = [
621 'name' => $group,
622 'rights' => array_keys( $permissions, true ),
623 ];
624
625 if ( $numberInGroup ) {
626 $autopromote = $config->get( MainConfigNames::Autopromote );
627
628 if ( $group == 'user' ) {
629 $arr['number'] = SiteStats::users();
630 // '*' and autopromote groups have no size
631 } elseif ( $group !== '*' && !isset( $autopromote[$group] ) ) {
632 $arr['number'] = SiteStats::numberingroup( $group );
633 }
634 }
635
636 $groupArr = [
637 'add' => $config->get( MainConfigNames::AddGroups ),
638 'remove' => $config->get( MainConfigNames::RemoveGroups ),
639 'add-self' => $config->get( MainConfigNames::GroupsAddToSelf ),
640 'remove-self' => $config->get( MainConfigNames::GroupsRemoveFromSelf )
641 ];
642
643 foreach ( $groupArr as $type => $rights ) {
644 if ( isset( $rights[$group] ) ) {
645 if ( $rights[$group] === true ) {
646 $groups = $allGroups;
647 } else {
648 $groups = array_intersect( $rights[$group], $allGroups );
649 }
650 if ( $groups ) {
651 $arr[$type] = $groups;
652 ApiResult::setArrayType( $arr[$type], 'BCarray' );
653 ApiResult::setIndexedTagName( $arr[$type], 'group' );
654 }
655 }
656 }
657
658 ApiResult::setIndexedTagName( $arr['rights'], 'permission' );
659 $data[] = $arr;
660 }
661
662 ApiResult::setIndexedTagName( $data, 'group' );
663
664 return $result->addValue( 'query', $property, $data );
665 }
666
667 protected function appendFileExtensions( $property ) {
668 $data = [];
669 foreach (
670 array_unique( $this->getConfig()->get( MainConfigNames::FileExtensions ) ) as $ext
671 ) {
672 $data[] = [ 'ext' => $ext ];
673 }
674 ApiResult::setIndexedTagName( $data, 'fe' );
675
676 return $this->getResult()->addValue( 'query', $property, $data );
677 }
678
679 protected function appendInstalledLibraries( $property ) {
680 $baseDir = $this->getConfig()->get( MainConfigNames::BaseDirectory );
681 $path = "$baseDir/vendor/composer/installed.json";
682 if ( !file_exists( $path ) ) {
683 return true;
684 }
685
686 $data = [];
687 $installed = new ComposerInstalled( $path );
688 foreach ( $installed->getInstalledDependencies() as $name => $info ) {
689 if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
690 // Skip any extensions or skins since they'll be listed
691 // in their proper section
692 continue;
693 }
694 $data[] = [
695 'name' => $name,
696 'version' => $info['version'],
697 ];
698 }
699 ApiResult::setIndexedTagName( $data, 'library' );
700
701 return $this->getResult()->addValue( 'query', $property, $data );
702 }
703
704 protected function appendExtensions( $property ) {
705 $data = [];
707 ExtensionRegistry::getInstance(),
708 $this->getConfig()
709 );
710 foreach ( $credits as $type => $extensions ) {
711 foreach ( $extensions as $ext ) {
712 $ret = [];
713 $ret['type'] = $type;
714 if ( isset( $ext['name'] ) ) {
715 $ret['name'] = $ext['name'];
716 }
717 if ( isset( $ext['namemsg'] ) ) {
718 $ret['namemsg'] = $ext['namemsg'];
719 }
720 if ( isset( $ext['description'] ) ) {
721 $ret['description'] = $ext['description'];
722 }
723 if ( isset( $ext['descriptionmsg'] ) ) {
724 // Can be a string or [ key, param1, param2, ... ]
725 if ( is_array( $ext['descriptionmsg'] ) ) {
726 $ret['descriptionmsg'] = $ext['descriptionmsg'][0];
727 $ret['descriptionmsgparams'] = array_slice( $ext['descriptionmsg'], 1 );
728 ApiResult::setIndexedTagName( $ret['descriptionmsgparams'], 'param' );
729 } else {
730 $ret['descriptionmsg'] = $ext['descriptionmsg'];
731 }
732 }
733 if ( isset( $ext['author'] ) ) {
734 $ret['author'] = is_array( $ext['author'] ) ?
735 implode( ', ', $ext['author'] ) : $ext['author'];
736 }
737 if ( isset( $ext['url'] ) ) {
738 $ret['url'] = $ext['url'];
739 }
740 if ( isset( $ext['version'] ) ) {
741 $ret['version'] = $ext['version'];
742 }
743 if ( isset( $ext['path'] ) ) {
744 $extensionPath = dirname( $ext['path'] );
745 $gitInfo = new GitInfo( $extensionPath );
746 $vcsVersion = $gitInfo->getHeadSHA1();
747 if ( $vcsVersion !== false ) {
748 $ret['vcs-system'] = 'git';
749 $ret['vcs-version'] = $vcsVersion;
750 $ret['vcs-url'] = $gitInfo->getHeadViewUrl();
751 $vcsDate = $gitInfo->getHeadCommitDate();
752 if ( $vcsDate !== false ) {
753 $ret['vcs-date'] = wfTimestamp( TS_ISO_8601, $vcsDate );
754 }
755 }
756
757 if ( ExtensionInfo::getLicenseFileNames( $extensionPath ) ) {
758 $ret['license-name'] = $ext['license-name'] ?? '';
759 $ret['license'] = SpecialPage::getTitleFor(
760 'Version',
761 "License/{$ext['name']}"
762 )->getLinkURL();
763 }
764
765 if ( ExtensionInfo::getAuthorsFileName( $extensionPath ) ) {
766 $ret['credits'] = SpecialPage::getTitleFor(
767 'Version',
768 "Credits/{$ext['name']}"
769 )->getLinkURL();
770 }
771 }
772 $data[] = $ret;
773 }
774 }
775
776 ApiResult::setIndexedTagName( $data, 'ext' );
777
778 return $this->getResult()->addValue( 'query', $property, $data );
779 }
780
781 protected function appendRightsInfo( $property ) {
782 $config = $this->getConfig();
783 $rightsPage = $config->get( MainConfigNames::RightsPage );
784 // The default value is null, but the installer sets it to empty string
785 if ( strlen( (string)$rightsPage ) ) {
786 $title = Title::newFromText( $rightsPage );
787 $url = wfExpandUrl( $title->getLinkURL(), PROTO_CURRENT );
788 } else {
789 $title = false;
790 $url = $config->get( MainConfigNames::RightsUrl );
791 }
792 $text = $config->get( MainConfigNames::RightsText );
793 if ( $title && !strlen( (string)$text ) ) {
794 $text = $title->getPrefixedText();
795 }
796
797 $data = [
798 'url' => (string)$url,
799 'text' => (string)$text,
800 ];
801
802 return $this->getResult()->addValue( 'query', $property, $data );
803 }
804
805 protected function appendRestrictions( $property ) {
806 $config = $this->getConfig();
807 $data = [
808 'types' => $config->get( MainConfigNames::RestrictionTypes ),
809 'levels' => $config->get( MainConfigNames::RestrictionLevels ),
810 'cascadinglevels' => $config->get( MainConfigNames::CascadingRestrictionLevels ),
811 'semiprotectedlevels' => $config->get( MainConfigNames::SemiprotectedRestrictionLevels ),
812 ];
813
814 ApiResult::setArrayType( $data['types'], 'BCarray' );
815 ApiResult::setArrayType( $data['levels'], 'BCarray' );
816 ApiResult::setArrayType( $data['cascadinglevels'], 'BCarray' );
817 ApiResult::setArrayType( $data['semiprotectedlevels'], 'BCarray' );
818
819 ApiResult::setIndexedTagName( $data['types'], 'type' );
820 ApiResult::setIndexedTagName( $data['levels'], 'level' );
821 ApiResult::setIndexedTagName( $data['cascadinglevels'], 'level' );
822 ApiResult::setIndexedTagName( $data['semiprotectedlevels'], 'level' );
823
824 return $this->getResult()->addValue( 'query', $property, $data );
825 }
826
827 public function appendLanguages( $property ) {
828 $params = $this->extractRequestParams();
829 $langCode = $params['inlanguagecode'] ?? '';
830 $langNames = $this->languageNameUtils->getLanguageNames( $langCode );
831
832 $data = [];
833
834 foreach ( $langNames as $code => $name ) {
835 $lang = [
836 'code' => $code,
837 'bcp47' => LanguageCode::bcp47( $code ),
838 ];
839 ApiResult::setContentValue( $lang, 'name', $name );
840 $data[] = $lang;
841 }
842 ApiResult::setIndexedTagName( $data, 'lang' );
843
844 return $this->getResult()->addValue( 'query', $property, $data );
845 }
846
847 // Export information about which page languages will trigger
848 // language conversion. (T153341)
849 public function appendLanguageVariants( $property ) {
850 $langNames = LanguageConverter::$languagesWithVariants;
851 if ( $this->languageConverterFactory->isConversionDisabled() ) {
852 // Ensure result is empty if language conversion is disabled.
853 $langNames = [];
854 }
855 sort( $langNames );
856
857 $data = [];
858 foreach ( $langNames as $langCode ) {
859 $lang = $this->languageFactory->getLanguage( $langCode );
860 $langConverter = $this->languageConverterFactory->getLanguageConverter( $lang );
861 if ( !$langConverter->hasVariants() ) {
862 // Only languages which have variants should be listed
863 continue;
864 }
865 $data[$langCode] = [];
866 ApiResult::setIndexedTagName( $data[$langCode], 'variant' );
867 ApiResult::setArrayType( $data[$langCode], 'kvp', 'code' );
868
869 $variants = $langConverter->getVariants();
870 sort( $variants );
871 foreach ( $variants as $v ) {
872 $fallbacks = $langConverter->getVariantFallbacks( $v );
873 if ( !is_array( $fallbacks ) ) {
874 $fallbacks = [ $fallbacks ];
875 }
876 $data[$langCode][$v] = [
877 'fallbacks' => $fallbacks,
878 ];
879 ApiResult::setIndexedTagName(
880 $data[$langCode][$v]['fallbacks'], 'variant'
881 );
882 }
883 }
884 ApiResult::setIndexedTagName( $data, 'lang' );
885 ApiResult::setArrayType( $data, 'kvp', 'code' );
886
887 return $this->getResult()->addValue( 'query', $property, $data );
888 }
889
890 public function appendSkins( $property ) {
891 $data = [];
892 $allowed = $this->skinFactory->getAllowedSkins();
893 $default = Skin::normalizeKey( 'default' );
894 $skinNames = $this->skinFactory->getInstalledSkins();
895
896 foreach ( $skinNames as $name => $displayName ) {
897 $msg = $this->msg( "skinname-{$name}" );
898 $code = $this->getParameter( 'inlanguagecode' );
899 if ( $code && $this->languageNameUtils->isValidCode( $code ) ) {
900 $msg->inLanguage( $code );
901 } else {
902 $msg->inContentLanguage();
903 }
904 if ( $msg->exists() ) {
905 $displayName = $msg->text();
906 }
907 $skin = [ 'code' => $name ];
908 ApiResult::setContentValue( $skin, 'name', $displayName );
909 if ( !isset( $allowed[$name] ) ) {
910 $skin['unusable'] = true;
911 }
912 if ( $name === $default ) {
913 $skin['default'] = true;
914 }
915 $data[] = $skin;
916 }
917 ApiResult::setIndexedTagName( $data, 'skin' );
918
919 return $this->getResult()->addValue( 'query', $property, $data );
920 }
921
922 public function appendExtensionTags( $property ) {
923 $tags = array_map(
924 static function ( $item ) {
925 return "<$item>";
926 },
927 $this->parser->getTags()
928 );
929 ApiResult::setArrayType( $tags, 'BCarray' );
930 ApiResult::setIndexedTagName( $tags, 't' );
931
932 return $this->getResult()->addValue( 'query', $property, $tags );
933 }
934
935 public function appendFunctionHooks( $property ) {
936 $hooks = $this->parser->getFunctionHooks();
937 ApiResult::setArrayType( $hooks, 'BCarray' );
938 ApiResult::setIndexedTagName( $hooks, 'h' );
939
940 return $this->getResult()->addValue( 'query', $property, $hooks );
941 }
942
943 public function appendVariables( $property ) {
944 $variables = $this->magicWordFactory->getVariableIDs();
945 ApiResult::setArrayType( $variables, 'BCarray' );
946 ApiResult::setIndexedTagName( $variables, 'v' );
947
948 return $this->getResult()->addValue( 'query', $property, $variables );
949 }
950
951 public function appendProtocols( $property ) {
952 // Make a copy of the global so we don't try to set the _element key of it - T47130
953 $protocols = array_values( $this->getConfig()->get( MainConfigNames::UrlProtocols ) );
954 ApiResult::setArrayType( $protocols, 'BCarray' );
955 ApiResult::setIndexedTagName( $protocols, 'p' );
956
957 return $this->getResult()->addValue( 'query', $property, $protocols );
958 }
959
960 public function appendDefaultOptions( $property ) {
961 $options = $this->userOptionsLookup->getDefaultOptions();
962 $options[ApiResult::META_BC_BOOLS] = array_keys( $options );
963 return $this->getResult()->addValue( 'query', $property, $options );
964 }
965
966 public function appendUploadDialog( $property ) {
967 $config = $this->getConfig()->get( MainConfigNames::UploadDialog );
968 return $this->getResult()->addValue( 'query', $property, $config );
969 }
970
971 public function appendSubscribedHooks( $property ) {
972 $hooks = $this->getConfig()->get( MainConfigNames::Hooks );
973 $myWgHooks = $hooks;
974 ksort( $myWgHooks );
975
976 $data = [];
977 foreach ( $myWgHooks as $name => $subscribers ) {
978 $arr = [
979 'name' => $name,
980 'subscribers' => array_map( [ SpecialVersion::class, 'arrayToString' ], $subscribers ),
981 ];
982
983 ApiResult::setArrayType( $arr['subscribers'], 'array' );
984 ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
985 $data[] = $arr;
986 }
987
988 ApiResult::setIndexedTagName( $data, 'hook' );
989
990 return $this->getResult()->addValue( 'query', $property, $data );
991 }
992
993 public function getCacheMode( $params ) {
994 // Messages for $wgExtraInterlanguageLinkPrefixes depend on user language
995 if (
996 count( $this->getConfig()->get( MainConfigNames::ExtraInterlanguageLinkPrefixes ) ) &&
997 $params['prop'] !== null &&
998 in_array( 'interwikimap', $params['prop'] )
999 ) {
1000 return 'anon-public-user-private';
1001 }
1002
1003 return 'public';
1004 }
1005
1006 public function getAllowedParams() {
1007 return [
1008 'prop' => [
1009 ParamValidator::PARAM_DEFAULT => 'general',
1010 ParamValidator::PARAM_ISMULTI => true,
1011 ParamValidator::PARAM_TYPE => [
1012 'general',
1013 'namespaces',
1014 'namespacealiases',
1015 'specialpagealiases',
1016 'magicwords',
1017 'interwikimap',
1018 'dbrepllag',
1019 'statistics',
1020 'usergroups',
1021 'libraries',
1022 'extensions',
1023 'fileextensions',
1024 'rightsinfo',
1025 'restrictions',
1026 'languages',
1027 'languagevariants',
1028 'skins',
1029 'extensiontags',
1030 'functionhooks',
1031 'showhooks',
1032 'variables',
1033 'protocols',
1034 'defaultoptions',
1035 'uploaddialog',
1036 ],
1038 ],
1039 'filteriw' => [
1040 ParamValidator::PARAM_TYPE => [
1041 'local',
1042 '!local',
1043 ]
1044 ],
1045 'showalldb' => false,
1046 'numberingroup' => false,
1047 'inlanguagecode' => null,
1048 ];
1049 }
1050
1051 protected function getExamplesMessages() {
1052 return [
1053 'action=query&meta=siteinfo&siprop=general|namespaces|namespacealiases|statistics'
1054 => 'apihelp-query+siteinfo-example-simple',
1055 'action=query&meta=siteinfo&siprop=interwikimap&sifilteriw=local'
1056 => 'apihelp-query+siteinfo-example-interwiki',
1057 'action=query&meta=siteinfo&siprop=dbrepllag&sishowalldb='
1058 => 'apihelp-query+siteinfo-example-replag',
1059 ];
1060 }
1061
1062 public function getHelpUrls() {
1063 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Siteinfo';
1064 }
1065}
getDB()
const PROTO_CURRENT
Definition Defines.php:198
const MW_VERSION
The running version of MediaWiki.
Definition Defines.php:36
const PROTO_RELATIVE
Definition Defines.php:195
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
$linkPrefixCharset
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1454
getParameter( $paramName, $parseLimit=true)
Get a value for the given parameter.
Definition ApiBase.php:886
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition ApiBase.php:1656
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:196
getResult()
Get the result object.
Definition ApiBase.php:629
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:765
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:711
This is a base class for all Query modules.
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
A query action to return meta information about the wiki site.
appendLanguageVariants( $property)
appendLanguages( $property)
appendInterwikiMap( $property, $filter)
appendGeneralInfo( $property)
appendRightsInfo( $property)
getExamplesMessages()
Returns usage examples for this module.
appendInstalledLibraries( $property)
appendVariables( $property)
__construct(ApiQuery $query, $moduleName, UserOptionsLookup $userOptionsLookup, UserGroupManager $userGroupManager, LanguageConverterFactory $languageConverterFactory, LanguageFactory $languageFactory, LanguageNameUtils $languageNameUtils, Language $contentLanguage, NamespaceInfo $namespaceInfo, InterwikiLookup $interwikiLookup, Parser $parser, MagicWordFactory $magicWordFactory, SpecialPageFactory $specialPageFactory, SkinFactory $skinFactory, ILoadBalancer $loadBalancer, ReadOnlyMode $readOnlyMode)
appendUserGroups( $property, $numberInGroup)
appendFileExtensions( $property)
appendNamespaces( $property)
appendDefaultOptions( $property)
appendMagicWords( $property)
getHelpUrls()
Return links to more detailed help pages about the module.
appendExtensions( $property)
getCacheMode( $params)
Get the cache mode for the data generated by this module.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
appendRestrictions( $property)
appendExtensionTags( $property)
appendUploadDialog( $property)
appendProtocols( $property)
appendStatistics( $property)
appendSpecialPageAliases( $property)
appendDbReplLagInfo( $property, $includeAll)
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
appendSubscribedHooks( $property)
appendFunctionHooks( $property)
appendNamespaceAliases( $property)
This is the main query class.
Definition ApiQuery.php:41
static getMinUploadChunkSize(Config $config)
Reads an installed.json file and provides accessors to get what is installed.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Base class for language-specific code.
Definition Language.php:53
A factory that stores information about MagicWords, and creates them on demand with caching.
An interface for creating language converters.
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
A service that provides utilities to do with language names and codes.
A class containing constants representing the names of configuration variables.
Module for skin stylesheets.
Factory for handling the special page list and generating SpecialPage objects.
Provides access to user options.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:96
A service class for fetching the wiki's current read-only mode.
static articles()
static jobs()
Total number of jobs in the job queue.
static images()
static edits()
Definition SiteStats.php:95
static users()
static pages()
static numberingroup( $group)
Find the number of users in a given user group.
static activeUsers()
Factory class to create Skin objects.
static normalizeKey( $key)
Normalize a skin preference value to a form that can be loaded.
Definition Skin.php:192
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,...
static getGitHeadSha1( $dir)
static getCredits(ExtensionRegistry $reg, Config $conf)
static getGitCurrentBranch( $dir)
Service for formatting and validating API parameters.
Service interface for looking up Interwiki records.
Create and track the database connections and transactions for a given database cluster.
if(!is_readable( $file)) $ext
Definition router.php:48
if(!isset( $args[0])) $lang