diff --git a/RELEASE-NOTES-1.42 b/RELEASE-NOTES-1.42 index b897159578c..1741c7a88b7 100644 --- a/RELEASE-NOTES-1.42 +++ b/RELEASE-NOTES-1.42 @@ -616,6 +616,8 @@ because of Phabricator reports. Vuex 4 will remain accessible for the foreseeable future. Pinia should be used for new projects. * Linker::makeHeadline() has been deprecated. +* Linker::generateTOC(), Linker::tocIndent(), Linker::tocUnindent(), + Linker::tocLine(), Linker::tocLineEnd(), Linker::tocList() are deprecated. * Title::getBrokenLinksFrom() has been deprecated. * ReplicatedBagOStuff has been deprecated since 1.42. * The third argument to ContentRenderer::getParserOutput() now accepts a diff --git a/includes/OutputTransform/Stages/HandleTOCMarkers.php b/includes/OutputTransform/Stages/HandleTOCMarkers.php index d7c4efc6a10..20c007e6b6d 100644 --- a/includes/OutputTransform/Stages/HandleTOCMarkers.php +++ b/includes/OutputTransform/Stages/HandleTOCMarkers.php @@ -4,13 +4,16 @@ namespace MediaWiki\OutputTransform\Stages; use Language; use MediaWiki\Context\RequestContext; -use MediaWiki\Linker\Linker; +use MediaWiki\Html\Html; +use MediaWiki\MainConfigNames; +use MediaWiki\MediaWikiServices; use MediaWiki\OutputTransform\ContentTextTransformStage; use MediaWiki\Parser\Parser; use MediaWiki\Parser\ParserOutput; use MediaWiki\Parser\Sanitizer; use MediaWiki\Tidy\TidyDriverBase; use ParserOptions; +use Wikimedia\Parsoid\Core\TOCData; /** * Inject table of contents (or empty string if there's no sections) @@ -45,9 +48,9 @@ class HandleTOCMarkers extends ContentTextTransformStage { if ( $numSections === 0 ) { $toc = ''; } else { - $toc = Linker::generateTOC( $tocData, $lang ); - $toc = - $this->tidy->tidy( $toc, [ Sanitizer::class, 'armorFrenchSpaces' ] ); + $toc = self::generateTOC( $tocData, $lang ); + // TODO: This may no longer be needed since Ic0a805f29c928d0c2edf266ea045b0d29bb45a28 + $toc = $this->tidy->tidy( $toc, [ Sanitizer::class, 'armorFrenchSpaces' ] ); } return Parser::replaceTableOfContentsMarker( $text, $toc ); @@ -72,4 +75,147 @@ class HandleTOCMarkers extends ContentTextTransformStage { } return $userLang; } + + /** + * Add another level to the Table of Contents + * + * @return string + */ + private static function tocIndent() { + return "\n\n\n", $level > 0 ? $level : 0 ); + } + + /** + * parameter level defines if we are on an indentation level + * + * @param string $linkAnchor Identifier + * @param string $tocline Properly escaped HTML + * @param string $tocnumber Unescaped text + * @param int $level + * @param string|false $sectionIndex + * @return string + */ + private static function tocLine( $linkAnchor, $tocline, $tocnumber, $level, $sectionIndex = false ) { + $classes = "toclevel-$level"; + + // Parser.php used to suppress tocLine by setting $sectionindex to false. + // In those circumstances, we can now encounter '' or a "T-" prefixed index + // for when the section comes from templates. + if ( $sectionIndex !== false && $sectionIndex !== '' && !str_starts_with( $sectionIndex, "T-" ) ) { + $classes .= " tocsection-$sectionIndex"; + } + + //
  • + // $tocnumber $tocline + return Html::openElement( 'li', [ 'class' => $classes ] ) + . Html::rawElement( 'a', + [ 'href' => "#$linkAnchor" ], + Html::element( 'span', [ 'class' => 'tocnumber' ], $tocnumber ) + . ' ' + . Html::rawElement( 'span', [ 'class' => 'toctext' ], $tocline ) + ); + } + + /** + * End a Table Of Contents line. + * tocUnindent() will be used instead if we're ending a line below + * the new level. + * @return string + */ + private static function tocLineEnd() { + return "
  • \n"; + } + + /** + * Wraps the TOC in a div with ARIA navigation role and provides the hide/collapse JavaScript. + * + * @param string $toc Html of the Table Of Contents + * @param Language|null $lang Language for the toc title, defaults to user language + * @return string Full html of the TOC + */ + private static function tocList( $toc, Language $lang = null ) { + $lang ??= RequestContext::getMain()->getLanguage(); + + $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped(); + + return '' + . $toc + . "\n\n"; + } + + /** + * Generate a table of contents from a section tree. + * + * @param ?TOCData $tocData Return value of ParserOutput::getSections() + * @param Language|null $lang Language for the toc title, defaults to user language + * @param array $options + * - 'maxtoclevel' Max TOC level to generate + * @return string HTML fragment + */ + private static function generateTOC( ?TOCData $tocData, Language $lang = null, array $options = [] ): string { + $toc = ''; + $lastLevel = 0; + $maxTocLevel = $options['maxtoclevel'] ?? null; + if ( $maxTocLevel === null ) { + // Use wiki-configured default + $services = MediaWikiServices::getInstance(); + $config = $services->getMainConfig(); + $maxTocLevel = $config->get( MainConfigNames::MaxTocLevel ); + } + foreach ( ( $tocData ? $tocData->getSections() : [] ) as $section ) { + $tocLevel = $section->tocLevel; + if ( $tocLevel < $maxTocLevel ) { + if ( $tocLevel > $lastLevel ) { + $toc .= self::tocIndent(); + } elseif ( $tocLevel < $lastLevel ) { + if ( $lastLevel < $maxTocLevel ) { + $toc .= self::tocUnindent( + $lastLevel - $tocLevel ); + } else { + $toc .= self::tocLineEnd(); + } + } else { + $toc .= self::tocLineEnd(); + } + + $toc .= self::tocLine( $section->linkAnchor, + $section->line, $section->number, + $tocLevel, $section->index ); + $lastLevel = $tocLevel; + } + } + if ( $lastLevel < $maxTocLevel && $lastLevel > 0 ) { + $toc .= self::tocUnindent( $lastLevel - 1 ); + } + return self::tocList( $toc, $lang ); + } } diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index 27b4f0b833b..7ac078cd715 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -2194,7 +2194,7 @@ abstract class ApiBase extends ContextSource { * @param string[] &$help Array of help data * @param array $options Options passed to ApiHelp::getHelp * @param array &$tocData If a TOC is being generated, this array has keys - * as anchors in the page and values as for Linker::generateTOC(). + * as anchors in the page and values as for SectionMetadata::fromLegacy(). */ public function modifyHelp( array &$help, array $options, array &$tocData ) { } diff --git a/includes/api/ApiHelp.php b/includes/api/ApiHelp.php index 289ddc1ff8f..cb6aa1adcbc 100644 --- a/includes/api/ApiHelp.php +++ b/includes/api/ApiHelp.php @@ -24,10 +24,11 @@ use MediaWiki\Context\DerivativeContext; use MediaWiki\Context\IContextSource; use MediaWiki\Html\Html; use MediaWiki\Html\HtmlHelper; -use MediaWiki\Linker\Linker; use MediaWiki\MainConfigNames; use MediaWiki\MediaWikiServices; use MediaWiki\Output\OutputPage; +use MediaWiki\Parser\ParserOutput; +use MediaWiki\Parser\ParserOutputFlags; use MediaWiki\SpecialPage\SpecialPage; use MediaWiki\Specials\SpecialVersion; use MediaWiki\Title\Title; @@ -190,8 +191,11 @@ class ApiHelp extends ApiBase { $haveModules = []; $html = self::getHelpInternal( $context, $modules, $options, $haveModules ); if ( !empty( $options['toc'] ) && $haveModules ) { - $tocData = TOCData::fromLegacy( array_values( $haveModules ) ); - $out->addHTML( Linker::generateTOC( $tocData, $context->getLanguage() ) ); + $pout = new ParserOutput; + $pout->setTOCData( TOCData::fromLegacy( array_values( $haveModules ) ) ); + $pout->setOutputFlag( ParserOutputFlags::SHOW_TOC ); + $pout->setText( Parser::TOC_PLACEHOLDER ); + $out->addParserOutput( $pout ); } $out->addHTML( $html ); diff --git a/includes/api/Hook/APIHelpModifyOutputHook.php b/includes/api/Hook/APIHelpModifyOutputHook.php index c46d9eadee0..2559e5afb7c 100644 --- a/includes/api/Hook/APIHelpModifyOutputHook.php +++ b/includes/api/Hook/APIHelpModifyOutputHook.php @@ -21,7 +21,7 @@ interface APIHelpModifyOutputHook { * @param string[] &$help Array of HTML strings to be joined for the output * @param array $options Array of formatting options passed to ApiHelp::getHelp * @param array &$tocData If a TOC is being generated, this array has keys as anchors in - * the page and values as for Linker::generateTOC(). + * the page and values as for SectionMetadata::fromLegacy(). * @return bool|void True or no return value to continue or false to abort */ public function onAPIHelpModifyOutput( $module, &$help, $options, &$tocData ); diff --git a/includes/linker/Linker.php b/includes/linker/Linker.php index 2b35ebc0330..4b561f646af 100644 --- a/includes/linker/Linker.php +++ b/includes/linker/Linker.php @@ -1777,27 +1777,32 @@ class Linker { /** * Add another level to the Table of Contents * + * @deprecated since 1.42 * @since 1.16.3 * @return string */ public static function tocIndent() { + wfDeprecated( __METHOD__, '1.42' ); return "\n\n\n", $level > 0 ? $level : 0 ); } /** * parameter level defines if we are on an indentation level * + * @deprecated since 1.42 * @since 1.16.3 * @param string $linkAnchor Identifier * @param string $tocline Properly escaped HTML @@ -1807,6 +1812,7 @@ class Linker { * @return string */ public static function tocLine( $linkAnchor, $tocline, $tocnumber, $level, $sectionIndex = false ) { + wfDeprecated( __METHOD__, '1.42' ); $classes = "toclevel-$level"; // Parser.php used to suppress tocLine by setting $sectionindex to false. @@ -1831,22 +1837,26 @@ class Linker { * End a Table Of Contents line. * tocUnindent() will be used instead if we're ending a line below * the new level. + * @deprecated since 1.42 * @since 1.16.3 * @return string */ public static function tocLineEnd() { + wfDeprecated( __METHOD__, '1.42' ); return "\n"; } /** * Wraps the TOC in a div with ARIA navigation role and provides the hide/collapse JavaScript. * + * @deprecated since 1.42 * @since 1.16.3 * @param string $toc Html of the Table Of Contents * @param Language|null $lang Language for the toc title, defaults to user language * @return string Full html of the TOC */ public static function tocList( $toc, Language $lang = null ) { + wfDeprecated( __METHOD__, '1.42' ); $lang ??= RequestContext::getMain()->getLanguage(); $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped(); @@ -1879,6 +1889,7 @@ class Linker { * @internal For use by ParserOutput and API modules * Generate a table of contents from a section tree. * + * @deprecated since 1.42 * @since 1.16.3. $lang added in 1.17. Parameters changed in 1.40. * @param ?TOCData $tocData Return value of ParserOutput::getSections() * @param Language|null $lang Language for the toc title, defaults to user language