35 use Symfony\Component\Yaml\Yaml;
36 use Wikimedia\Parsoid\Core\SectionMetadata;
37 use Wikimedia\Parsoid\Core\TOCData;
81 parent::__construct(
'Version' );
82 $this->parser = $parser;
83 $this->urlUtils = $urlUtils;
96 $credits[$credit[
'type']][] = $credit;
107 $config = $this->getConfig();
111 $this->outputHeader();
112 $out = $this->getOutput();
113 $out->setPreventClickjacking(
false );
116 $parts = explode(
'/', (
string)$par );
118 if ( isset( $parts[1] ) ) {
119 $extName = str_replace(
'_',
' ', $parts[1] );
121 foreach ( $credits as $extensions ) {
122 foreach ( $extensions as
$ext ) {
123 if ( isset(
$ext[
'name'] ) && (
$ext[
'name'] === $extName ) ) {
130 $out->setStatusCode( 404 );
133 $extName =
'MediaWiki';
137 switch ( strtolower( $parts[0] ) ) {
139 $out->addModuleStyles(
'mediawiki.special' );
141 $wikiText =
'{{int:version-credits-not-found}}';
142 if ( $extName ===
'MediaWiki' ) {
143 $wikiText = file_get_contents(
$IP .
'/CREDITS' );
145 $wikiText = str_replace(
146 [
'<!-- BEGIN CONTRIBUTOR LIST -->',
'<!-- END CONTRIBUTOR LIST -->' ],
147 [
'<div class="mw-version-credits">',
'</div>' ],
149 } elseif ( ( $extNode !==
null ) && isset( $extNode[
'path'] ) ) {
150 $file = ExtensionInfo::getAuthorsFileName( dirname( $extNode[
'path'] ) );
152 $wikiText = file_get_contents(
$file );
153 if ( str_ends_with(
$file,
'.txt' ) ) {
154 $wikiText = Html::element(
166 $out->setPageTitle( $this->msg(
'version-credits-title', $extName ) );
167 $out->addWikiTextAsInterface( $wikiText );
171 $out->setPageTitle( $this->msg(
'version-license-title', $extName ) );
173 $licenseFound =
false;
175 if ( $extName ===
'MediaWiki' ) {
176 $out->addWikiTextAsInterface(
177 file_get_contents(
$IP .
'/COPYING' )
179 $licenseFound =
true;
180 } elseif ( ( $extNode !==
null ) && isset( $extNode[
'path'] ) ) {
181 $files = ExtensionInfo::getLicenseFileNames( dirname( $extNode[
'path'] ) );
183 $licenseFound =
true;
184 foreach ( $files as
$file ) {
185 $out->addWikiTextAsInterface(
192 file_get_contents(
$file )
198 if ( !$licenseFound ) {
199 $out->addWikiTextAsInterface(
'{{int:version-license-not-found}}' );
204 $out->addModuleStyles(
'mediawiki.special' );
206 $out->addHTML( $this->getMediaWikiCredits() );
208 $this->tocData =
new TOCData();
209 $this->tocLength = 0;
213 $this->softwareInformation(),
214 $this->getEntryPointInfo(),
215 $this->getSkinCredits( $credits ),
216 $this->getExtensionCredits( $credits ),
217 $this->getExternalLibraries( $credits ),
218 $this->getClientSideLibraries(),
219 $this->getParserTags(),
220 $this->getParserFunctionHooks(),
228 $pout->setOutputFlag( ParserOutputFlags::SHOW_TOC );
229 $pout->setText( Parser::TOC_PLACEHOLDER );
230 $out->addParserOutput( $pout );
248 private function addTocSection( $labelMsg, $id ) {
250 $this->tocData->addSection(
new SectionMetadata(
253 $this->msg( $labelMsg )->escaped(),
254 $this->getLanguage()->formatNum( $this->tocLength ),
255 (
string)$this->tocLength,
268 private function getMediaWikiCredits() {
271 $ret = Html::element(
273 [
'id' =>
'mw-version-license' ],
274 $this->msg(
'version-license' )->text()
277 $ret .= Html::rawElement(
'div', [
'class' =>
'plainlinks' ],
278 $this->msg(
new RawMessage( self::getCopyrightAndAuthorList() ) )->parseAsBlock() .
279 Html::rawElement(
'div', [
'class' =>
'mw-version-license-info' ],
280 $this->msg(
'version-license-info' )->parseAsBlock()
294 if ( defined(
'MEDIAWIKI_INSTALL' ) ) {
295 $othersLink =
'[https://www.mediawiki.org/wiki/Special:Version/Credits ' .
296 wfMessage(
'version-poweredby-others' )->plain() .
']';
298 $othersLink =
'[[Special:Version/Credits|' .
299 wfMessage(
'version-poweredby-others' )->plain() .
']]';
302 $translatorsLink =
'[https://translatewiki.net/wiki/Translating:MediaWiki/Credits ' .
303 wfMessage(
'version-poweredby-translators' )->plain() .
']';
306 'Magnus Manske',
'Brion Vibber',
'Lee Daniel Crocker',
307 'Tim Starling',
'Erik Möller',
'Gabriel Wicke',
'Ævar Arnfjörð Bjarmason',
308 'Niklas Laxström',
'Domas Mituzas',
'Rob Church',
'Yuri Astrakhan',
309 'Aryeh Gregor',
'Aaron Schulz',
'Andrew Garrett',
'Raimond Spekking',
310 'Alexandre Emsenhuber',
'Siebrand Mazeland',
'Chad Horohoe',
311 'Roan Kattouw',
'Trevor Parscal',
'Bryan Tong Minh',
'Sam Reed',
312 'Victor Vasiliev',
'Rotem Liss',
'Platonides',
'Antoine Musso',
313 'Timo Tijhof',
'Daniel Kinzler',
'Jeroen De Dauw',
'Brad Jorsch',
314 'Bartosz Dziewoński',
'Ed Sanders',
'Moriel Schottlender',
315 'Kunal Mehta',
'James D. Forrester',
'Brian Wolff',
'Adam Shorland',
316 'DannyS712',
'Ori Livneh',
'Max Semenik',
'Amir Sarabadani',
317 'Derk-Jan Hartman',
'Petr Pchelko',
318 $othersLink, $translatorsLink
330 private function getSoftwareInformation() {
337 '[https://www.mediawiki.org/ MediaWiki]' => self::getVersionLinked(),
338 '[https://php.net/ PHP]' => PHP_VERSION .
" (" . PHP_SAPI .
")",
339 '[https://icu.unicode.org/ ICU]' => INTL_ICU_VERSION,
340 $dbr->getSoftwareLink() =>
$dbr->getServerInfo(),
344 $this->getHookRunner()->onSoftwareInfo( $software );
354 private function softwareInformation() {
355 $this->addTocSection(
'version-software',
'mw-version-software' );
357 $out = Html::element(
359 [
'id' =>
'mw-version-software' ],
360 $this->msg(
'version-software' )->text()
363 $out .= Html::openElement(
'table', [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-software' ] );
365 $out .= Html::rawElement(
'tr', [],
366 Html::element(
'th', [], $this->msg(
'version-software-product' )->text() ) .
367 Html::element(
'th', [], $this->msg(
'version-software-version' )->text() )
370 foreach ( $this->getSoftwareInformation() as $name => $version ) {
371 $out .= Html::rawElement(
'tr', [],
372 Html::rawElement(
'td', [], $this->msg(
new RawMessage( $name ) )->parse() ) .
373 Html::rawElement(
'td', [
'dir' =>
'ltr' ], $this->msg(
new RawMessage( $version ) )->parse() )
377 $out .= Html::closeElement(
'table' );
393 $gitInfo = self::getGitHeadSha1(
$IP );
396 } elseif ( $flags ===
'nodb' ) {
397 $shortSha1 = substr( $gitInfo, 0, 7 );
400 $shortSha1 = substr( $gitInfo, 0, 7 );
402 if (
$lang !==
null ) {
403 $msg->inLanguage(
$lang );
405 $shortSha1 = $msg->params( $shortSha1 )->escaped();
420 $gitVersion = self::getVersionLinkedGit();
433 private static function getMWVersionLinked() {
435 $hookRunner =
new HookRunner( MediaWikiServices::getInstance()->getHookContainer() );
436 if ( $hookRunner->onSpecialVersionVersionUrl(
MW_VERSION, $versionUrl ) ) {
438 preg_match(
"/^(\d+\.\d+)/",
MW_VERSION, $versionParts );
439 $versionUrl =
"https://www.mediawiki.org/wiki/MediaWiki_{$versionParts[1]}";
442 return '[' . $versionUrl .
' ' .
MW_VERSION .
']';
450 private static function getVersionLinkedGit() {
454 $headSHA1 = $gitInfo->getHeadSHA1();
459 $shortSHA1 =
'(' . substr( $headSHA1, 0, 7 ) .
')';
461 $gitHeadUrl = $gitInfo->getHeadViewUrl();
462 if ( $gitHeadUrl !==
false ) {
463 $shortSHA1 =
"[$gitHeadUrl $shortSHA1]";
466 $gitHeadCommitDate = $gitInfo->getHeadCommitDate();
467 if ( $gitHeadCommitDate ) {
468 $shortSHA1 .= Html::element(
'br' ) .
$wgLang->timeanddate( (
string)$gitHeadCommitDate,
true );
471 return self::getMWVersionLinked() .
" $shortSHA1";
484 if ( self::$extensionTypes === false ) {
485 self::$extensionTypes = [
486 'specialpage' =>
wfMessage(
'version-specialpages' )->text(),
487 'editor' =>
wfMessage(
'version-editors' )->text(),
488 'parserhook' =>
wfMessage(
'version-parserhooks' )->text(),
489 'variable' =>
wfMessage(
'version-variables' )->text(),
490 'media' =>
wfMessage(
'version-mediahandlers' )->text(),
491 'antispam' =>
wfMessage(
'version-antispam' )->text(),
492 'skin' =>
wfMessage(
'version-skins' )->text(),
493 'api' =>
wfMessage(
'version-api' )->text(),
494 'other' =>
wfMessage(
'version-other' )->text(),
497 (
new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )
498 ->onExtensionTypes( self::$extensionTypes );
501 return self::$extensionTypes;
514 $types = self::getExtensionTypes();
516 return $types[
$type] ?? $types[
'other'];
525 private function getExtensionCredits( array $credits ) {
529 ( count( $credits ) === 1 && isset( $credits[
'skin'] ) )
534 $extensionTypes = self::getExtensionTypes();
536 $this->addTocSection(
'version-extensions',
'mw-version-ext' );
540 [
'id' =>
'mw-version-ext' ],
541 $this->msg(
'version-extensions' )->text()
543 Xml::openElement(
'table', [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-ext' ] );
546 if ( !array_key_exists(
'other', $credits ) ) {
547 $credits[
'other'] = [];
551 foreach ( $credits as
$type => $extensions ) {
552 if ( !array_key_exists(
$type, $extensionTypes ) ) {
553 $credits[
'other'] = array_merge( $credits[
'other'], $extensions );
557 $this->firstExtOpened =
false;
559 foreach ( $extensionTypes as
$type => $text ) {
561 if (
$type !==
'other' &&
$type !==
'skin' ) {
562 $out .= $this->getExtensionCategory(
$type, $text, $credits[
$type] ?? [] );
567 $out .= $this->getExtensionCategory(
'other', $extensionTypes[
'other'], $credits[
'other'] );
580 private function getSkinCredits( array $credits ) {
581 if ( !isset( $credits[
'skin'] ) || !$credits[
'skin'] ) {
585 $this->addTocSection(
'version-skins',
'mw-version-skin' );
587 $out = Html::element(
589 [
'id' =>
'mw-version-skin' ],
590 $this->msg(
'version-skins' )->text()
592 Html::openElement(
'table', [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-skin' ] );
594 $this->firstExtOpened =
false;
595 $out .= $this->getExtensionCategory(
'skin',
null, $credits[
'skin'] );
597 $out .= Html::closeElement(
'table' );
611 "$IP/vendor/composer/installed.json"
614 $extensionTypes = self::getExtensionTypes();
615 foreach ( $extensionTypes as
$type => $message ) {
616 if ( !isset( $credits[
$type] ) || $credits[
$type] === [] ) {
619 foreach ( $credits[
$type] as $extension ) {
620 if ( !isset( $extension[
'path'] ) ) {
623 $paths[] = dirname( $extension[
'path'] ) .
'/vendor/composer/installed.json';
629 foreach ( $paths as
$path ) {
630 if ( !file_exists(
$path ) ) {
636 $dependencies += $installed->getInstalledDependencies();
639 if ( $dependencies === [] ) {
643 ksort( $dependencies );
645 $this->addTocSection(
'version-libraries',
'mw-version-libraries' );
647 $out = Html::element(
649 [
'id' =>
'mw-version-libraries' ],
650 $this->msg(
'version-libraries' )->text()
652 $out .= Html::openElement(
654 [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-libraries' ]
656 $out .= Html::openElement(
'tr' )
657 . Html::element(
'th', [], $this->msg(
'version-libraries-library' )->text() )
658 . Html::element(
'th', [], $this->msg(
'version-libraries-version' )->text() )
659 . Html::element(
'th', [], $this->msg(
'version-libraries-license' )->text() )
660 . Html::element(
'th', [], $this->msg(
'version-libraries-description' )->text() )
661 . Html::element(
'th', [], $this->msg(
'version-libraries-authors' )->text() )
662 . Html::closeElement(
'tr' );
664 foreach ( $dependencies as $name => $info ) {
665 if ( !is_array( $info ) || str_starts_with( $info[
'type'],
'mediawiki-' ) ) {
670 $authors = array_map(
static function ( $arr ) {
672 if ( isset( $arr[
'homepage'] ) ) {
673 return "[{$arr['homepage']} {$arr['name']}]";
676 }, $info[
'authors'] );
677 $authors = $this->listAuthors( $authors,
false,
"$IP/vendor/$name" );
682 $out .= Html::openElement(
'tr', [
686 "mw-version-library-$name"
691 Linker::makeExternalLink(
692 "https://packagist.org/packages/$name", $name,
694 [
'class' =>
'mw-version-library-name' ]
697 . Html::element(
'td', [
'dir' =>
'auto' ], $info[
'version'] )
698 . Html::element(
'td', [
'dir' =>
'auto' ], $this->listToText( $info[
'licenses'] ) )
699 . Html::element(
'td', [
'lang' =>
'en',
'dir' =>
'ltr' ], $info[
'description'] )
700 . Html::rawElement(
'td', [], $authors )
701 . Html::closeElement(
'tr' );
703 $out .= Html::closeElement(
'table' );
713 private function getClientSideLibraries() {
715 $registryDirs = [
'MediaWiki' =>
"{$IP}/resources/lib" ]
719 foreach ( $registryDirs as
$source => $registryDir ) {
720 $foreignResources = Yaml::parseFile(
"$registryDir/foreign-resources.yaml" );
721 foreach ( $foreignResources as $name => $module ) {
722 $key = $name . $module[
'version'];
727 $modules[$key] = $module + [
'name' => $name,
'source' => [
$source ] ];
732 $this->addTocSection(
'version-libraries-client',
'mw-version-libraries-client' );
734 $out = Html::element(
736 [
'id' =>
'mw-version-libraries-client' ],
737 $this->msg(
'version-libraries-client' )->text()
739 $out .= Html::openElement(
741 [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-libraries-client' ]
743 $out .= Html::openElement(
'tr' )
744 . Html::element(
'th', [], $this->msg(
'version-libraries-library' )->text() )
745 . Html::element(
'th', [], $this->msg(
'version-libraries-version' )->text() )
746 . Html::element(
'th', [], $this->msg(
'version-libraries-license' )->text() )
747 . Html::element(
'th', [], $this->msg(
'version-libraries-authors' )->text() )
748 . Html::element(
'th', [], $this->msg(
'version-libraries-source' )->text() )
749 . Html::closeElement(
'tr' );
751 foreach (
$modules as $name => $info ) {
755 $out .= Html::openElement(
'tr', [
759 "mw-version-library-$name"
764 Linker::makeExternalLink(
765 $info[
'homepage'], $info[
'name'],
767 [
'class' =>
'mw-version-library-name' ]
770 . Html::element(
'td', [
'dir' =>
'auto' ], $info[
'version'] )
771 . Html::element(
'td', [
'dir' =>
'auto' ], $info[
'license'] )
772 . Html::element(
'td', [
'dir' =>
'auto' ], $info[
'authors'] ??
'—' )
773 . Html::element(
'td', [
'dir' =>
'auto' ], $this->listToText( $info[
'source'] ) )
774 . Html::closeElement(
'tr' );
776 $out .= Html::closeElement(
'table' );
787 $tags = $this->parser->getTags();
792 $this->addTocSection(
'version-parser-extensiontags',
'mw-version-parser-extensiontags' );
794 $out = Html::rawElement(
796 [
'id' =>
'mw-version-parser-extensiontags' ],
799 [
'class' =>
'plainlinks' ],
800 Linker::makeExternalLink(
801 'https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Tag_extensions',
802 $this->msg(
'version-parser-extensiontags' )->text()
807 array_walk( $tags,
static function ( &$value ) {
809 $value = Html::element(
813 'style' =>
'white-space: nowrap;',
819 $out .= $this->listToText( $tags );
830 $funcHooks = $this->parser->getFunctionHooks();
835 $this->addTocSection(
'version-parser-function-hooks',
'mw-version-parser-function-hooks' );
837 $out = Html::rawElement(
839 [
'id' =>
'mw-version-parser-function-hooks' ],
842 [
'class' =>
'plainlinks' ],
843 Linker::makeExternalLink(
844 'https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Parser_functions',
845 $this->msg(
'version-parser-function-hooks' )->text()
850 $out .= $this->listToText( $funcHooks );
867 if ( $creditsGroup ) {
868 $out .= $this->openExtType( $text,
'credits-' .
$type );
870 usort( $creditsGroup, [ $this,
'compare' ] );
872 foreach ( $creditsGroup as $extension ) {
873 $out .= $this->getCreditsForExtension(
$type, $extension );
887 return $this->getLanguage()->lc( $a[
'name'] ) <=> $this->getLanguage()->lc( $b[
'name'] );
909 $out = $this->getOutput();
913 if ( isset( $extension[
'namemsg'] ) ) {
915 $extensionName = $this->msg( $extension[
'namemsg'] )->text();
916 } elseif ( isset( $extension[
'name'] ) ) {
918 $extensionName = $extension[
'name'];
920 $extensionName = $this->msg(
'version-no-ext-name' )->text();
923 if ( isset( $extension[
'url'] ) ) {
924 $extensionNameLink = Linker::makeExternalLink(
929 [
'class' =>
'mw-version-ext-name' ]
932 $extensionNameLink = htmlspecialchars( $extensionName );
938 $canonicalVersion =
'–';
939 $extensionPath =
null;
944 if ( isset( $extension[
'version'] ) ) {
945 $canonicalVersion = $out->parseInlineAsInterface( $extension[
'version'] );
948 if ( isset( $extension[
'path'] ) ) {
950 $extensionPath = dirname( $extension[
'path'] );
951 if ( $this->coreId ==
'' ) {
952 wfDebug(
'Looking up core head id' );
953 $coreHeadSHA1 = self::getGitHeadSha1(
$IP );
954 if ( $coreHeadSHA1 ) {
955 $this->coreId = $coreHeadSHA1;
959 $memcKey = $cache->makeKey(
960 'specialversion-ext-version-text', $extension[
'path'], $this->coreId
962 [ $vcsVersion, $vcsLink, $vcsDate ] = $cache->get( $memcKey );
964 if ( !$vcsVersion ) {
965 wfDebug(
"Getting VCS info for extension {$extension['name']}" );
966 $gitInfo =
new GitInfo( $extensionPath );
967 $vcsVersion = $gitInfo->getHeadSHA1();
968 if ( $vcsVersion !==
false ) {
969 $vcsVersion = substr( $vcsVersion, 0, 7 );
970 $vcsLink = $gitInfo->getHeadViewUrl();
971 $vcsDate = $gitInfo->getHeadCommitDate();
973 $cache->set( $memcKey, [ $vcsVersion, $vcsLink, $vcsDate ], 60 * 60 * 24 );
975 wfDebug(
"Pulled VCS info for extension {$extension['name']} from cache" );
979 $versionString = Html::rawElement(
981 [
'class' =>
'mw-version-ext-version' ],
987 $vcsVerString = Linker::makeExternalLink(
989 $this->msg(
'version-version', $vcsVersion )->text(),
992 [
'class' =>
'mw-version-ext-vcs-version' ]
995 $vcsVerString = Html::element(
'span',
996 [
'class' =>
'mw-version-ext-vcs-version' ],
1000 $versionString .=
" {$vcsVerString}";
1003 $versionString .=
' ' . Html::element(
'span', [
1004 'class' =>
'mw-version-ext-vcs-timestamp',
1005 'dir' => $this->getLanguage()->getDir(),
1006 ], $this->getLanguage()->timeanddate( $vcsDate,
true ) );
1008 $versionString = Html::rawElement(
'span',
1009 [
'class' =>
'mw-version-ext-meta-version' ],
1017 if ( isset( $extension[
'name'] ) ) {
1018 $licenseName =
null;
1019 if ( isset( $extension[
'license-name'] ) ) {
1020 $licenseName =
new HtmlArmor( $out->parseInlineAsInterface( $extension[
'license-name'] ) );
1021 } elseif ( $extensionPath !==
null && ExtensionInfo::getLicenseFileNames( $extensionPath ) ) {
1022 $licenseName = $this->msg(
'version-ext-license' )->text();
1024 if ( $licenseName !==
null ) {
1025 $licenseLink = $this->getLinkRenderer()->makeLink(
1026 $this->getPageTitle(
'License/' . $extension[
'name'] ),
1029 'class' =>
'mw-version-ext-license',
1039 if ( isset( $extension[
'descriptionmsg'] ) ) {
1041 $descriptionMsg = $extension[
'descriptionmsg'];
1043 if ( is_array( $descriptionMsg ) ) {
1044 $descriptionMsgKey = array_shift( $descriptionMsg );
1045 $descriptionMsg = array_map(
'htmlspecialchars', $descriptionMsg );
1046 $description = $this->msg( $descriptionMsgKey, ...$descriptionMsg )->text();
1048 $description = $this->msg( $descriptionMsg )->text();
1050 } elseif ( isset( $extension[
'description'] ) ) {
1052 $description = $extension[
'description'];
1056 $description = $out->parseInlineAsInterface( $description );
1059 $authors = $extension[
'author'] ?? [];
1061 $authors = $this->listAuthors( $authors, $extension[
'name'], $extensionPath );
1064 $html = Html::openElement(
'tr', [
1065 'class' =>
'mw-version-ext',
1070 $html .= Html::rawElement(
'td', [], $extensionNameLink );
1071 $html .= Html::rawElement(
'td', [], $versionString );
1072 $html .= Html::rawElement(
'td', [], $licenseLink );
1073 $html .= Html::rawElement(
'td', [
'class' =>
'mw-version-ext-description' ], $description );
1074 $html .= Html::rawElement(
'td', [
'class' =>
'mw-version-ext-authors' ], $authors );
1076 $html .= Html::closeElement(
'tr' );
1086 private function getHooks() {
1087 if ( $this->getConfig()->
get( MainConfigNames::SpecialVersionShowHooks ) ) {
1088 $hookContainer = MediaWikiServices::getInstance()->getHookContainer();
1089 $hookNames = $hookContainer->getHookNames();
1093 $this->addTocSection(
'version-hooks',
'mw-version-hooks' );
1094 $ret[] = Html::element(
1096 [
'id' =>
'mw-version-hooks' ],
1097 $this->msg(
'version-hooks' )->text()
1099 $ret[] = Html::openElement(
'table', [
'class' =>
'wikitable',
'id' =>
'sv-hooks' ] );
1100 $ret[] = Html::openElement(
'tr' );
1101 $ret[] = Html::element(
'th', [], $this->msg(
'version-hook-name' )->text() );
1102 $ret[] = Html::element(
'th', [], $this->msg(
'version-hook-subscribedby' )->text() );
1103 $ret[] = Html::closeElement(
'tr' );
1105 foreach ( $hookNames as $hook ) {
1106 $hooks = $hookContainer->getLegacyHandlers( $hook );
1110 $ret[] = Html::openElement(
'tr' );
1111 $ret[] = Html::element(
'td', [], $hook );
1112 $ret[] = Html::element(
'td', [], $this->listToText( $hooks ) );
1113 $ret[] = Html::closeElement(
'tr' );
1116 $ret[] = Html::closeElement(
'table' );
1118 return implode(
"\n", $ret );
1124 private function openExtType(
string $text =
null,
string $name =
null ) {
1127 $opt = [
'colspan' => 5 ];
1128 if ( $this->firstExtOpened ) {
1130 $out .= Html::rawElement(
'tr', [
'class' =>
'sv-space' ],
1131 Html::element(
'td', $opt )
1134 $this->firstExtOpened =
true;
1137 $opt[
'id'] =
"sv-$name";
1140 if ( $text !==
null ) {
1141 $out .= Html::rawElement(
'tr', [],
1142 Html::element(
'th', $opt, $text )
1146 $firstHeadingMsg = ( $name ===
'credits-skin' )
1147 ?
'version-skin-colheader-name'
1148 :
'version-ext-colheader-name';
1149 $out .= Html::openElement(
'tr' );
1150 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1151 $this->msg( $firstHeadingMsg )->text() );
1152 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1153 $this->msg(
'version-ext-colheader-version' )->text() );
1154 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1155 $this->msg(
'version-ext-colheader-license' )->text() );
1156 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1157 $this->msg(
'version-ext-colheader-description' )->text() );
1158 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1159 $this->msg(
'version-ext-colheader-credits' )->text() );
1160 $out .= Html::closeElement(
'tr' );
1170 private function IPInfo() {
1171 $ip = str_replace(
'--',
' - ', htmlspecialchars( $this->
getRequest()->getIP() ) );
1173 return "<!-- visited from $ip -->\n<span style='display:none'>visited from $ip</span>";
1198 $linkRenderer = $this->getLinkRenderer();
1201 $authors = (array)$authors;
1206 if ( count( $authors ) === 1 && $authors[0] ===
'...' ) {
1209 if ( $extName && ExtensionInfo::getAuthorsFileName( $extDir ) ) {
1210 return $linkRenderer->makeLink(
1211 $this->getPageTitle(
"Credits/$extName" ),
1212 $this->msg(
'version-poweredby-various' )->text()
1215 return $this->msg(
'version-poweredby-various' )->escaped();
1221 foreach ( $authors as $item ) {
1222 if ( $item ==
'...' ) {
1225 if ( $extName && ExtensionInfo::getAuthorsFileName( $extDir ) ) {
1226 $text = $linkRenderer->makeLink(
1227 $this->getPageTitle(
"Credits/$extName" ),
1228 $this->msg(
'version-poweredby-others' )->text()
1231 $text = $this->msg(
'version-poweredby-others' )->escaped();
1234 } elseif ( str_ends_with( $item,
' ...]' ) ) {
1236 $list[] = $this->getOutput()->parseInlineAsInterface(
1237 substr( $item, 0, -4 ) . $this->msg(
'version-poweredby-others' )->text() .
"]"
1240 $list[] = $this->getOutput()->parseInlineAsInterface( $item );
1244 if ( $extName && !$hasOthers && ExtensionInfo::getAuthorsFileName( $extDir ) ) {
1245 $list[] = $linkRenderer->makeLink(
1246 $this->getPageTitle(
"Credits/$extName" ),
1247 $this->msg(
'version-poweredby-others' )->text()
1251 return $this->listToText( $list,
false );
1261 private function listToText( array $list,
bool $sort =
true ): string {
1269 return $this->getLanguage()
1270 ->listToText( array_map( [ __CLASS__,
'arrayToString' ], $list ) );
1282 if ( is_array( $list ) && count( $list ) == 1 ) {
1285 if ( $list instanceof Closure ) {
1288 } elseif ( is_object( $list ) ) {
1289 return wfMessage(
'parentheses' )->params( get_class( $list ) )->escaped();
1290 } elseif ( !is_array( $list ) ) {
1293 if ( is_object( $list[0] ) ) {
1294 $class = get_class( $list[0] );
1299 return wfMessage(
'parentheses' )->params(
"$class, {$list[1]}" )->escaped();
1308 return (
new GitInfo( $dir ) )->getHeadSHA1();
1316 return (
new GitInfo( $dir ) )->getCurrentBranch();
1324 $config = $this->getConfig();
1325 $scriptPath = $config->get( MainConfigNames::ScriptPath ) ?:
'/';
1328 'version-entrypoints-articlepath' => $config->get( MainConfigNames::ArticlePath ),
1329 'version-entrypoints-scriptpath' => $scriptPath,
1330 'version-entrypoints-index-php' =>
wfScript(
'index' ),
1331 'version-entrypoints-api-php' =>
wfScript(
'api' ),
1332 'version-entrypoints-rest-php' =>
wfScript(
'rest' ),
1335 $language = $this->getLanguage();
1337 'dir' => $language->getDir(),
1338 'lang' => $language->getHtmlCode()
1341 $this->addTocSection(
'version-entrypoints',
'mw-version-entrypoints' );
1343 $out = Html::element(
1345 [
'id' =>
'mw-version-entrypoints' ],
1346 $this->msg(
'version-entrypoints' )->text()
1348 Html::openElement(
'table',
1350 'class' =>
'wikitable plainlinks',
1351 'id' =>
'mw-version-entrypoints-table',
1356 Html::openElement(
'tr' ) .
1360 $this->msg(
'version-entrypoints-header-entrypoint' )->text()
1365 $this->msg(
'version-entrypoints-header-url' )->text()
1367 Html::closeElement(
'tr' );
1369 foreach ( $entryPoints as $message => $value ) {
1371 $out .= Html::openElement(
'tr' ) .
1372 Html::rawElement(
'td', [], $this->msg( $message )->parse() ) .
1373 Html::rawElement(
'td', [], Html::rawElement(
'code', [],
1374 $this->msg(
new RawMessage(
"[$url $value]" ) )->parse() ) ) .
1375 Html::closeElement(
'tr' );
1378 $out .= Html::closeElement(
'table' );
const MW_VERSION
The running version of MediaWiki.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined( 'MEDIAWIKI')) if(ini_get( 'mbstring.func_overload')) if(!defined( 'MW_ENTRY_POINT')) global $IP
Environment checks.
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) $wgLang
if(!defined('MW_SETUP_CALLBACK'))
Reads an installed.json file and provides accessors to get what is installed.
Load JSON files, and uses a Processor to extract information.
getAllThings()
Get credits information about all installed extensions and skins.
Marks HTML that shouldn't be escaped.
static getLocalInstance( $ts=false)
Get a timestamp instance in the server local timezone ($wgLocaltimezone)
A class containing constants representing the names of configuration variables.
static listParam(array $list, $type='text')
static getInstance( $id)
Get a cached instance of the specified type of cache object.
setTOCData(TOCData $tocData)
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
static escapeIdForAttribute( $id, $mode=self::ID_PRIMARY)
Given a section name or other user-generated or otherwise unsafe string, escapes it to be a valid HTM...
Parent class for all special pages.
Give information about the version of MediaWiki, PHP, the DB and extensions.
static getVersionLinked()
Return a wikitext-formatted string of the MediaWiki version with a link to the Git SHA1 of head if av...
static getExtensionTypeName( $type)
Returns the internationalized name for an extension type.
static getGitHeadSha1( $dir)
getParserFunctionHooks()
Obtains a list of installed parser function hooks and the associated H2 header.
getExternalLibraries(array $credits)
Generate an HTML table for external libraries that are installed.
getEntryPointInfo()
Get the list of entry points and their URLs.
static getCredits(ExtensionRegistry $reg, Config $conf)
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
listAuthors( $authors, $extName, $extDir)
Return a formatted unsorted list of authors.
string $coreId
The current rev id/SHA hash of MediaWiki core.
static getExtensionTypes()
Returns an array with the base extension types.
__construct(Parser $parser, UrlUtils $urlUtils)
getCreditsForExtension( $type, array $extension)
Creates and formats a version line for a single extension.
getParserTags()
Obtains a list of installed parser tags and the associated H2 header.
compare( $a, $b)
Callback to sort extensions by type.
static getVersion( $flags='', $lang=null)
Return a string of the MediaWiki version with Git revision if available.
static getCopyrightAndAuthorList()
Get the "MediaWiki is copyright 2001-20xx by lots of cool folks" text.
static getGitCurrentBranch( $dir)
static string[] false $extensionTypes
Lazy initialized key/value with message content.
static arrayToString( $list)
Convert an array or object to a string for display.
getExtensionCategory( $type, ?string $text, array $creditsGroup)
Creates and returns the HTML for a single extension category.
static closeElement( $element)
Shortcut to close an XML element.
static openElement( $element, $attribs=null)
This opens an XML element.
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Interface for configuration instances.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
if(!is_readable( $file)) $ext
if(!isset( $args[0])) $lang