34use Symfony\Component\Yaml\Yaml;
35use Wikimedia\Parsoid\Core\SectionMetadata;
36use Wikimedia\Parsoid\Core\TOCData;
80 parent::__construct(
'Version' );
81 $this->parser = $parser;
82 $this->urlUtils = $urlUtils;
95 $credits[$credit[
'type']][] = $credit;
106 $config = $this->getConfig();
107 $credits = self::getCredits( ExtensionRegistry::getInstance(), $config );
110 $this->outputHeader();
111 $out = $this->getOutput();
112 $out->setPreventClickjacking(
false );
115 $parts = explode(
'/', (
string)$par );
117 if ( isset( $parts[1] ) ) {
118 $extName = str_replace(
'_',
' ', $parts[1] );
120 foreach ( $credits as $extensions ) {
121 foreach ( $extensions as
$ext ) {
122 if ( isset(
$ext[
'name'] ) && (
$ext[
'name'] === $extName ) ) {
129 $out->setStatusCode( 404 );
132 $extName =
'MediaWiki';
136 switch ( strtolower( $parts[0] ) ) {
138 $out->addModuleStyles(
'mediawiki.special' );
140 $wikiText =
'{{int:version-credits-not-found}}';
141 if ( $extName ===
'MediaWiki' ) {
142 $wikiText = file_get_contents(
$IP .
'/CREDITS' );
144 $wikiText = str_replace(
145 [
'<!-- BEGIN CONTRIBUTOR LIST -->',
'<!-- END CONTRIBUTOR LIST -->' ],
146 [
'<div class="mw-version-credits">',
'</div>' ],
148 } elseif ( ( $extNode !==
null ) && isset( $extNode[
'path'] ) ) {
149 $file = ExtensionInfo::getAuthorsFileName( dirname( $extNode[
'path'] ) );
151 $wikiText = file_get_contents(
$file );
152 if ( str_ends_with(
$file,
'.txt' ) ) {
153 $wikiText = Html::element(
165 $out->setPageTitle( $this->msg(
'version-credits-title', $extName ) );
166 $out->addWikiTextAsInterface( $wikiText );
170 $out->setPageTitle( $this->msg(
'version-license-title', $extName ) );
172 $licenseFound =
false;
174 if ( $extName ===
'MediaWiki' ) {
175 $out->addWikiTextAsInterface(
176 file_get_contents(
$IP .
'/COPYING' )
178 $licenseFound =
true;
179 } elseif ( ( $extNode !==
null ) && isset( $extNode[
'path'] ) ) {
180 $files = ExtensionInfo::getLicenseFileNames( dirname( $extNode[
'path'] ) );
182 $licenseFound =
true;
183 foreach ( $files as
$file ) {
184 $out->addWikiTextAsInterface(
191 file_get_contents(
$file )
197 if ( !$licenseFound ) {
198 $out->addWikiTextAsInterface(
'{{int:version-license-not-found}}' );
203 $out->addModuleStyles(
'mediawiki.special' );
205 $out->addHTML( $this->getMediaWikiCredits() );
207 $this->tocData =
new TOCData();
208 $this->tocLength = 0;
212 [
'html', $this->softwareInformation() ],
213 [
'wikitext', $this->getEntryPointInfo() ],
214 [
'html', $this->getSkinCredits( $credits ) ],
215 [
'html', $this->getExtensionCredits( $credits ) ],
216 [
'html', $this->getExternalLibraries( $credits ) ],
217 [
'html', $this->getClientSideLibraries() ],
218 [
'html', $this->getParserTags() ],
219 [
'html', $this->getParserFunctionHooks() ],
220 [
'wikitext', $this->getHooks() ],
221 [
'html', $this->IPInfo() ],
227 $pout->setOutputFlag( ParserOutputFlags::SHOW_TOC );
228 $pout->setText( Parser::TOC_PLACEHOLDER );
229 $out->addParserOutputText( $pout );
232 foreach ( $sections as [ $mode,
$content ] ) {
233 if ( $mode ===
'wikitext' ) {
234 $out->addWikiTextAsInterface(
$content );
235 } elseif ( $mode ===
'html' ) {
243 $out->addParserOutputMetadata( $pout );
256 private function addTocSection( $labelMsg, $id ) {
258 $this->tocData->addSection(
new SectionMetadata(
261 $this->msg( $labelMsg )->escaped(),
262 $this->getLanguage()->formatNum( $this->tocLength ),
263 (
string)$this->tocLength,
276 private function getMediaWikiCredits() {
279 $ret = Html::element(
281 [
'id' =>
'mw-version-license' ],
282 $this->msg(
'version-license' )->text()
285 $ret .= Html::rawElement(
'div', [
'class' =>
'plainlinks' ],
286 $this->msg(
new RawMessage( self::getCopyrightAndAuthorList() ) )->parseAsBlock() .
287 Html::rawElement(
'div', [
'class' =>
'mw-version-license-info' ],
288 $this->msg(
'version-license-info' )->parseAsBlock()
302 if ( defined(
'MEDIAWIKI_INSTALL' ) ) {
303 $othersLink =
'[https://www.mediawiki.org/wiki/Special:Version/Credits ' .
304 wfMessage(
'version-poweredby-others' )->plain() .
']';
306 $othersLink =
'[[Special:Version/Credits|' .
307 wfMessage(
'version-poweredby-others' )->plain() .
']]';
310 $translatorsLink =
'[https://translatewiki.net/wiki/Translating:MediaWiki/Credits ' .
311 wfMessage(
'version-poweredby-translators' )->plain() .
']';
314 'Magnus Manske',
'Brion Vibber',
'Lee Daniel Crocker',
315 'Tim Starling',
'Erik Möller',
'Gabriel Wicke',
'Ævar Arnfjörð Bjarmason',
316 'Niklas Laxström',
'Domas Mituzas',
'Rob Church',
'Yuri Astrakhan',
317 'Aryeh Gregor',
'Aaron Schulz',
'Andrew Garrett',
'Raimond Spekking',
318 'Alexandre Emsenhuber',
'Siebrand Mazeland',
'Chad Horohoe',
319 'Roan Kattouw',
'Trevor Parscal',
'Bryan Tong Minh',
'Sam Reed',
320 'Victor Vasiliev',
'Rotem Liss',
'Platonides',
'Antoine Musso',
321 'Timo Tijhof',
'Daniel Kinzler',
'Jeroen De Dauw',
'Brad Jorsch',
322 'Bartosz Dziewoński',
'Ed Sanders',
'Moriel Schottlender',
323 'Kunal Mehta',
'James D. Forrester',
'Brian Wolff',
'Adam Shorland',
324 'DannyS712',
'Ori Livneh',
325 $othersLink, $translatorsLink
328 return wfMessage(
'version-poweredby-credits', MWTimestamp::getLocalInstance()->format(
'Y' ),
337 private static function getSoftwareInformation() {
344 '[https://www.mediawiki.org/ MediaWiki]' => self::getVersionLinked(),
345 '[https://php.net/ PHP]' => PHP_VERSION .
" (" . PHP_SAPI .
")",
346 '[https://icu.unicode.org/ ICU]' => INTL_ICU_VERSION,
347 $dbr->getSoftwareLink() =>
$dbr->getServerInfo(),
351 Hooks::runner()->onSoftwareInfo( $software );
361 private function softwareInformation() {
362 $this->addTocSection(
'version-software',
'mw-version-software' );
364 $out = Html::element(
366 [
'id' =>
'mw-version-software' ],
367 $this->msg(
'version-software' )->text()
370 $out .= Html::openElement(
'table', [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-software' ] );
372 $out .= Html::rawElement(
'tr', [],
373 Html::element(
'th', [], $this->msg(
'version-software-product' )->text() ) .
374 Html::element(
'th', [], $this->msg(
'version-software-version' )->text() )
377 foreach ( self::getSoftwareInformation() as $name => $version ) {
378 $out .= Html::rawElement(
'tr', [],
379 Html::rawElement(
'td', [], $this->msg(
new RawMessage( $name ) )->parse() ) .
380 Html::rawElement(
'td', [
'dir' =>
'ltr' ], $this->msg(
new RawMessage( $version ) )->parse() )
384 $out .= Html::closeElement(
'table' );
400 $gitInfo = self::getGitHeadSha1(
$IP );
403 } elseif ( $flags ===
'nodb' ) {
404 $shortSha1 = substr( $gitInfo, 0, 7 );
407 $shortSha1 = substr( $gitInfo, 0, 7 );
409 if (
$lang !==
null ) {
410 $msg->inLanguage(
$lang );
412 $shortSha1 = $msg->params( $shortSha1 )->escaped();
427 $gitVersion = self::getVersionLinkedGit();
440 private static function getMWVersionLinked() {
442 if ( Hooks::runner()->onSpecialVersionVersionUrl(
MW_VERSION, $versionUrl ) ) {
444 preg_match(
"/^(\d+\.\d+)/",
MW_VERSION, $versionParts );
445 $versionUrl =
"https://www.mediawiki.org/wiki/MediaWiki_{$versionParts[1]}";
448 return '[' . $versionUrl .
' ' .
MW_VERSION .
']';
456 private static function getVersionLinkedGit() {
460 $headSHA1 = $gitInfo->getHeadSHA1();
465 $shortSHA1 =
'(' . substr( $headSHA1, 0, 7 ) .
')';
467 $gitHeadUrl = $gitInfo->getHeadViewUrl();
468 if ( $gitHeadUrl !==
false ) {
469 $shortSHA1 =
"[$gitHeadUrl $shortSHA1]";
472 $gitHeadCommitDate = $gitInfo->getHeadCommitDate();
473 if ( $gitHeadCommitDate ) {
474 $shortSHA1 .= Html::element(
'br' ) .
$wgLang->timeanddate( (
string)$gitHeadCommitDate,
true );
477 return self::getMWVersionLinked() .
" $shortSHA1";
490 if ( self::$extensionTypes === false ) {
491 self::$extensionTypes = [
492 'specialpage' =>
wfMessage(
'version-specialpages' )->text(),
493 'editor' =>
wfMessage(
'version-editors' )->text(),
494 'parserhook' =>
wfMessage(
'version-parserhooks' )->text(),
495 'variable' =>
wfMessage(
'version-variables' )->text(),
496 'media' =>
wfMessage(
'version-mediahandlers' )->text(),
497 'antispam' =>
wfMessage(
'version-antispam' )->text(),
498 'skin' =>
wfMessage(
'version-skins' )->text(),
499 'api' =>
wfMessage(
'version-api' )->text(),
500 'other' =>
wfMessage(
'version-other' )->text(),
503 Hooks::runner()->onExtensionTypes( self::$extensionTypes );
506 return self::$extensionTypes;
519 $types = self::getExtensionTypes();
521 return $types[
$type] ?? $types[
'other'];
530 private function getExtensionCredits( array $credits ) {
534 ( count( $credits ) === 1 && isset( $credits[
'skin'] ) )
539 $extensionTypes = self::getExtensionTypes();
541 $this->addTocSection(
'version-extensions',
'mw-version-ext' );
545 [
'id' =>
'mw-version-ext' ],
546 $this->msg(
'version-extensions' )->text()
548 Xml::openElement(
'table', [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-ext' ] );
551 if ( !array_key_exists(
'other', $credits ) ) {
552 $credits[
'other'] = [];
556 foreach ( $credits as
$type => $extensions ) {
557 if ( !array_key_exists(
$type, $extensionTypes ) ) {
558 $credits[
'other'] = array_merge( $credits[
'other'], $extensions );
562 $this->firstExtOpened =
false;
564 foreach ( $extensionTypes as
$type => $text ) {
566 if (
$type !==
'other' &&
$type !==
'skin' ) {
567 $out .= $this->getExtensionCategory(
$type, $text, $credits[
$type] ?? [] );
572 $out .= $this->getExtensionCategory(
'other', $extensionTypes[
'other'], $credits[
'other'] );
585 private function getSkinCredits( array $credits ) {
586 if ( !isset( $credits[
'skin'] ) || !$credits[
'skin'] ) {
590 $this->addTocSection(
'version-skins',
'mw-version-skin' );
592 $out = Html::element(
594 [
'id' =>
'mw-version-skin' ],
595 $this->msg(
'version-skins' )->text()
597 Html::openElement(
'table', [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-skin' ] );
599 $this->firstExtOpened =
false;
600 $out .= $this->getExtensionCategory(
'skin',
null, $credits[
'skin'] );
602 $out .= Html::closeElement(
'table' );
616 "$IP/vendor/composer/installed.json"
619 $extensionTypes = self::getExtensionTypes();
620 foreach ( $extensionTypes as
$type => $message ) {
621 if ( !isset( $credits[
$type] ) || $credits[
$type] === [] ) {
624 foreach ( $credits[
$type] as $extension ) {
625 if ( !isset( $extension[
'path'] ) ) {
628 $paths[] = dirname( $extension[
'path'] ) .
'/vendor/composer/installed.json';
634 foreach ( $paths as
$path ) {
635 if ( !file_exists(
$path ) ) {
641 $dependencies += $installed->getInstalledDependencies();
644 if ( $dependencies === [] ) {
648 ksort( $dependencies );
650 $this->addTocSection(
'version-libraries',
'mw-version-libraries' );
652 $out = Html::element(
654 [
'id' =>
'mw-version-libraries' ],
655 $this->msg(
'version-libraries' )->text()
657 $out .= Html::openElement(
659 [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-libraries' ]
661 $out .= Html::openElement(
'tr' )
662 . Html::element(
'th', [], $this->msg(
'version-libraries-library' )->text() )
663 . Html::element(
'th', [], $this->msg(
'version-libraries-version' )->text() )
664 . Html::element(
'th', [], $this->msg(
'version-libraries-license' )->text() )
665 . Html::element(
'th', [], $this->msg(
'version-libraries-description' )->text() )
666 . Html::element(
'th', [], $this->msg(
'version-libraries-authors' )->text() )
667 . Html::closeElement(
'tr' );
669 foreach ( $dependencies as $name => $info ) {
670 if ( !is_array( $info ) || str_starts_with( $info[
'type'],
'mediawiki-' ) ) {
675 $authors = array_map(
static function ( $arr ) {
677 if ( isset( $arr[
'homepage'] ) ) {
678 return "[{$arr['homepage']} {$arr['name']}]";
681 }, $info[
'authors'] );
682 $authors = $this->listAuthors( $authors,
false,
"$IP/vendor/$name" );
687 $out .= Html::openElement(
'tr', [
690 'id' => Sanitizer::escapeIdForAttribute(
691 "mw-version-library-$name"
696 Linker::makeExternalLink(
697 "https://packagist.org/packages/$name", $name,
699 [
'class' =>
'mw-version-library-name' ]
702 . Html::element(
'td', [
'dir' =>
'auto' ], $info[
'version'] )
703 . Html::element(
'td', [
'dir' =>
'auto' ], $this->listToText( $info[
'licenses'] ) )
704 . Html::element(
'td', [
'lang' =>
'en',
'dir' =>
'ltr' ], $info[
'description'] )
705 . Html::rawElement(
'td', [], $authors )
706 . Html::closeElement(
'tr' );
708 $out .= Html::closeElement(
'table' );
718 private function getClientSideLibraries() {
720 $registryFile =
"{$IP}/resources/lib/foreign-resources.yaml";
721 $modules = Yaml::parseFile( $registryFile );
724 $this->addTocSection(
'version-libraries-client',
'mw-version-libraries-client' );
726 $out = Html::element(
728 [
'id' =>
'mw-version-libraries-client' ],
729 $this->msg(
'version-libraries-client' )->text()
731 $out .= Html::openElement(
733 [
'class' =>
'wikitable plainlinks',
'id' =>
'sv-libraries-client' ]
735 $out .= Html::openElement(
'tr' )
736 . Html::element(
'th', [], $this->msg(
'version-libraries-library' )->text() )
737 . Html::element(
'th', [], $this->msg(
'version-libraries-version' )->text() )
738 . Html::element(
'th', [], $this->msg(
'version-libraries-license' )->text() )
739 . Html::closeElement(
'tr' );
741 foreach (
$modules as $name => $info ) {
745 $out .= Html::openElement(
'tr', [
748 'id' => Sanitizer::escapeIdForAttribute(
749 "mw-version-library-$name"
754 Linker::makeExternalLink(
755 $info[
'homepage'], $name,
757 [
'class' =>
'mw-version-library-name' ]
760 . Html::element(
'td', [
'dir' =>
'auto' ], $info[
'version'] )
761 . Html::element(
'td', [
'dir' =>
'auto' ], $info[
'license'] )
762 . Html::closeElement(
'tr' );
764 $out .= Html::closeElement(
'table' );
775 $tags = $this->parser->getTags();
780 $this->addTocSection(
'version-parser-extensiontags',
'mw-version-parser-extensiontags' );
782 $out = Html::rawElement(
784 [
'id' =>
'mw-version-parser-extensiontags' ],
787 [
'class' =>
'plainlinks' ],
788 Linker::makeExternalLink(
789 'https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Tag_extensions',
790 $this->msg(
'version-parser-extensiontags' )->text()
795 array_walk( $tags,
static function ( &$value ) {
797 $value = Html::element(
801 'style' =>
'white-space: nowrap;',
807 $out .= $this->listToText( $tags );
818 $funcHooks = $this->parser->getFunctionHooks();
823 $this->addTocSection(
'version-parser-function-hooks',
'mw-version-parser-function-hooks' );
825 $out = Html::rawElement(
827 [
'id' =>
'mw-version-parser-function-hooks' ],
830 [
'class' =>
'plainlinks' ],
831 Linker::makeExternalLink(
832 'https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Parser_functions',
833 $this->msg(
'version-parser-function-hooks' )->text()
838 $out .= $this->listToText( $funcHooks );
855 if ( $creditsGroup ) {
856 $out .= $this->openExtType( $text,
'credits-' .
$type );
858 usort( $creditsGroup, [ $this,
'compare' ] );
860 foreach ( $creditsGroup as $extension ) {
861 $out .= $this->getCreditsForExtension(
$type, $extension );
875 return $this->getLanguage()->lc( $a[
'name'] ) <=> $this->getLanguage()->lc( $b[
'name'] );
897 $out = $this->getOutput();
901 if ( isset( $extension[
'namemsg'] ) ) {
903 $extensionName = $this->msg( $extension[
'namemsg'] )->text();
904 } elseif ( isset( $extension[
'name'] ) ) {
906 $extensionName = $extension[
'name'];
908 $extensionName = $this->msg(
'version-no-ext-name' )->text();
911 if ( isset( $extension[
'url'] ) ) {
912 $extensionNameLink = Linker::makeExternalLink(
917 [
'class' =>
'mw-version-ext-name' ]
920 $extensionNameLink = htmlspecialchars( $extensionName );
926 $canonicalVersion =
'–';
927 $extensionPath =
null;
932 if ( isset( $extension[
'version'] ) ) {
933 $canonicalVersion = $out->parseInlineAsInterface( $extension[
'version'] );
936 if ( isset( $extension[
'path'] ) ) {
938 $extensionPath = dirname( $extension[
'path'] );
939 if ( $this->coreId ==
'' ) {
940 wfDebug(
'Looking up core head id' );
941 $coreHeadSHA1 = self::getGitHeadSha1(
$IP );
942 if ( $coreHeadSHA1 ) {
943 $this->coreId = $coreHeadSHA1;
947 $memcKey = $cache->makeKey(
948 'specialversion-ext-version-text', $extension[
'path'], $this->coreId
950 [ $vcsVersion, $vcsLink, $vcsDate ] = $cache->get( $memcKey );
952 if ( !$vcsVersion ) {
953 wfDebug(
"Getting VCS info for extension {$extension['name']}" );
954 $gitInfo =
new GitInfo( $extensionPath );
955 $vcsVersion = $gitInfo->getHeadSHA1();
956 if ( $vcsVersion !==
false ) {
957 $vcsVersion = substr( $vcsVersion, 0, 7 );
958 $vcsLink = $gitInfo->getHeadViewUrl();
959 $vcsDate = $gitInfo->getHeadCommitDate();
961 $cache->set( $memcKey, [ $vcsVersion, $vcsLink, $vcsDate ], 60 * 60 * 24 );
963 wfDebug(
"Pulled VCS info for extension {$extension['name']} from cache" );
967 $versionString = Html::rawElement(
969 [
'class' =>
'mw-version-ext-version' ],
975 $vcsVerString = Linker::makeExternalLink(
977 $this->msg(
'version-version', $vcsVersion )->text(),
980 [
'class' =>
'mw-version-ext-vcs-version' ]
983 $vcsVerString = Html::element(
'span',
984 [
'class' =>
'mw-version-ext-vcs-version' ],
988 $versionString .=
" {$vcsVerString}";
991 $versionString .=
' ' . Html::element(
'span', [
992 'class' =>
'mw-version-ext-vcs-timestamp',
993 'dir' => $this->getLanguage()->getDir(),
994 ], $this->getLanguage()->timeanddate( $vcsDate,
true ) );
996 $versionString = Html::rawElement(
'span',
997 [
'class' =>
'mw-version-ext-meta-version' ],
1005 if ( isset( $extension[
'name'] ) ) {
1006 $licenseName =
null;
1007 if ( isset( $extension[
'license-name'] ) ) {
1008 $licenseName =
new HtmlArmor( $out->parseInlineAsInterface( $extension[
'license-name'] ) );
1009 } elseif ( $extensionPath !==
null && ExtensionInfo::getLicenseFileNames( $extensionPath ) ) {
1010 $licenseName = $this->msg(
'version-ext-license' )->text();
1012 if ( $licenseName !==
null ) {
1013 $licenseLink = $this->getLinkRenderer()->makeLink(
1014 $this->getPageTitle(
'License/' . $extension[
'name'] ),
1017 'class' =>
'mw-version-ext-license',
1027 if ( isset( $extension[
'descriptionmsg'] ) ) {
1029 $descriptionMsg = $extension[
'descriptionmsg'];
1031 if ( is_array( $descriptionMsg ) ) {
1032 $descriptionMsgKey = array_shift( $descriptionMsg );
1033 $descriptionMsg = array_map(
'htmlspecialchars', $descriptionMsg );
1034 $description = $this->msg( $descriptionMsgKey, ...$descriptionMsg )->text();
1036 $description = $this->msg( $descriptionMsg )->text();
1038 } elseif ( isset( $extension[
'description'] ) ) {
1040 $description = $extension[
'description'];
1044 $description = $out->parseInlineAsInterface( $description );
1047 $authors = $extension[
'author'] ?? [];
1049 $authors = $this->listAuthors( $authors, $extension[
'name'], $extensionPath );
1052 $html = Html::openElement(
'tr', [
1053 'class' =>
'mw-version-ext',
1054 'id' => Sanitizer::escapeIdForAttribute(
'mw-version-ext-' .
$type .
'-' . $extension[
'name'] )
1058 $html .= Html::rawElement(
'td', [], $extensionNameLink );
1059 $html .= Html::rawElement(
'td', [], $versionString );
1060 $html .= Html::rawElement(
'td', [], $licenseLink );
1061 $html .= Html::rawElement(
'td', [
'class' =>
'mw-version-ext-description' ], $description );
1062 $html .= Html::rawElement(
'td', [
'class' =>
'mw-version-ext-authors' ], $authors );
1064 $html .= Html::closeElement(
'tr' );
1074 private function getHooks() {
1075 if ( $this->getConfig()->
get( MainConfigNames::SpecialVersionShowHooks ) ) {
1076 $hookContainer = MediaWikiServices::getInstance()->getHookContainer();
1077 $hookNames = $hookContainer->getHookNames();
1081 $ret[] =
'== {{int:version-hooks}} ==';
1082 $ret[] = Html::openElement(
'table', [
'class' =>
'wikitable',
'id' =>
'sv-hooks' ] );
1083 $ret[] = Html::openElement(
'tr' );
1084 $ret[] = Html::element(
'th', [], $this->msg(
'version-hook-name' )->text() );
1085 $ret[] = Html::element(
'th', [], $this->msg(
'version-hook-subscribedby' )->text() );
1086 $ret[] = Html::closeElement(
'tr' );
1088 foreach ( $hookNames as $hook ) {
1089 $hooks = $hookContainer->getLegacyHandlers( $hook );
1093 $ret[] = Html::openElement(
'tr' );
1094 $ret[] = Html::element(
'td', [], $hook );
1095 $ret[] = Html::element(
'td', [], $this->listToText( $hooks ) );
1096 $ret[] = Html::closeElement(
'tr' );
1099 $ret[] = Html::closeElement(
'table' );
1101 return implode(
"\n", $ret );
1107 private function openExtType(
string $text =
null,
string $name =
null ) {
1110 $opt = [
'colspan' => 5 ];
1111 if ( $this->firstExtOpened ) {
1113 $out .= Html::rawElement(
'tr', [
'class' =>
'sv-space' ],
1114 Html::element(
'td', $opt )
1117 $this->firstExtOpened =
true;
1120 $opt[
'id'] =
"sv-$name";
1123 if ( $text !==
null ) {
1124 $out .= Html::rawElement(
'tr', [],
1125 Html::element(
'th', $opt, $text )
1129 $firstHeadingMsg = ( $name ===
'credits-skin' )
1130 ?
'version-skin-colheader-name'
1131 :
'version-ext-colheader-name';
1132 $out .= Html::openElement(
'tr' );
1133 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1134 $this->msg( $firstHeadingMsg )->text() );
1135 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1136 $this->msg(
'version-ext-colheader-version' )->text() );
1137 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1138 $this->msg(
'version-ext-colheader-license' )->text() );
1139 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1140 $this->msg(
'version-ext-colheader-description' )->text() );
1141 $out .= Html::element(
'th', [
'class' =>
'mw-version-ext-col-label' ],
1142 $this->msg(
'version-ext-colheader-credits' )->text() );
1143 $out .= Html::closeElement(
'tr' );
1153 private function IPInfo() {
1154 $ip = str_replace(
'--',
' - ', htmlspecialchars( $this->
getRequest()->getIP() ) );
1156 return "<!-- visited from $ip -->\n<span style='display:none'>visited from $ip</span>";
1181 $linkRenderer = $this->getLinkRenderer();
1184 $authors = (array)$authors;
1189 if ( count( $authors ) === 1 && $authors[0] ===
'...' ) {
1192 if ( $extName && ExtensionInfo::getAuthorsFileName( $extDir ) ) {
1193 return $linkRenderer->makeLink(
1194 $this->getPageTitle(
"Credits/$extName" ),
1195 $this->msg(
'version-poweredby-various' )->text()
1198 return $this->msg(
'version-poweredby-various' )->escaped();
1204 foreach ( $authors as $item ) {
1205 if ( $item ==
'...' ) {
1208 if ( $extName && ExtensionInfo::getAuthorsFileName( $extDir ) ) {
1209 $text = $linkRenderer->makeLink(
1210 $this->getPageTitle(
"Credits/$extName" ),
1211 $this->msg(
'version-poweredby-others' )->text()
1214 $text = $this->msg(
'version-poweredby-others' )->escaped();
1217 } elseif ( str_ends_with( $item,
' ...]' ) ) {
1219 $list[] = $this->getOutput()->parseInlineAsInterface(
1220 substr( $item, 0, -4 ) . $this->msg(
'version-poweredby-others' )->text() .
"]"
1223 $list[] = $this->getOutput()->parseInlineAsInterface( $item );
1227 if ( $extName && !$hasOthers && ExtensionInfo::getAuthorsFileName( $extDir ) ) {
1228 $list[] = $linkRenderer->makeLink(
1229 $this->getPageTitle(
"Credits/$extName" ),
1230 $this->msg(
'version-poweredby-others' )->text()
1234 return $this->listToText( $list,
false );
1244 private function listToText( array $list,
bool $sort =
true ): string {
1252 return $this->getLanguage()
1253 ->listToText( array_map( [ __CLASS__,
'arrayToString' ], $list ) );
1265 if ( is_array( $list ) && count( $list ) == 1 ) {
1268 if ( $list instanceof Closure ) {
1271 } elseif ( is_object( $list ) ) {
1272 return wfMessage(
'parentheses' )->params( get_class( $list ) )->escaped();
1273 } elseif ( !is_array( $list ) ) {
1276 if ( is_object( $list[0] ) ) {
1277 $class = get_class( $list[0] );
1282 return wfMessage(
'parentheses' )->params(
"$class, {$list[1]}" )->escaped();
1291 return (
new GitInfo( $dir ) )->getHeadSHA1();
1299 return (
new GitInfo( $dir ) )->getCurrentBranch();
1307 $config = $this->getConfig();
1308 $scriptPath = $config->get( MainConfigNames::ScriptPath ) ?:
'/';
1311 'version-entrypoints-articlepath' => $config->get( MainConfigNames::ArticlePath ),
1312 'version-entrypoints-scriptpath' => $scriptPath,
1313 'version-entrypoints-index-php' =>
wfScript(
'index' ),
1314 'version-entrypoints-api-php' =>
wfScript(
'api' ),
1315 'version-entrypoints-rest-php' =>
wfScript(
'rest' ),
1318 $language = $this->getLanguage();
1320 'dir' => $language->getDir(),
1321 'lang' => $language->getHtmlCode()
1324 $this->addTocSection(
'version-entrypoints',
'mw-version-entrypoints' );
1326 $out = Html::element(
1328 [
'id' =>
'mw-version-entrypoints' ],
1329 $this->msg(
'version-entrypoints' )->text()
1331 Html::openElement(
'table',
1333 'class' =>
'wikitable plainlinks',
1334 'id' =>
'mw-version-entrypoints-table',
1339 Html::openElement(
'tr' ) .
1343 $this->msg(
'version-entrypoints-header-entrypoint' )->text()
1348 $this->msg(
'version-entrypoints-header-url' )->text()
1350 Html::closeElement(
'tr' );
1352 foreach ( $entryPoints as $message => $value ) {
1354 $out .= Html::openElement(
'tr' ) .
1357 Html::rawElement(
'td', [], $this->msg( $message )->plain() ) .
1358 Html::rawElement(
'td', [], Html::rawElement(
'code', [],
"[$url $value]" ) ) .
1359 Html::closeElement(
'tr' );
1362 $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'))
The persistent session ID (if any) loaded at startup.
Reads an installed.json file and provides accessors to get what is installed.
The Registry loads JSON files, and uses a Processor to extract information from them.
getAllThings()
Get credits information about all installed extensions and skins.
Marks HTML that shouldn't be escaped.
A class containing constants representing the names of configuration variables.
static listParam(array $list, $type='text')
setTOCData(TOCData $tocData)
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
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