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