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