Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ParserHooks
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 5
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 transformHtml
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
 onParserOutputPostCacheTransform
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 onParserAfterTidy
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 onGetDoubleUnderscoreIDs
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * DiscussionTools parser hooks
4 *
5 * @file
6 * @ingroup Extensions
7 * @license MIT
8 */
9
10namespace MediaWiki\Extension\DiscussionTools\Hooks;
11
12use MediaWiki\Config\Config;
13use MediaWiki\Config\ConfigFactory;
14use MediaWiki\Extension\DiscussionTools\BatchModifyElements;
15use MediaWiki\Extension\DiscussionTools\CommentFormatter;
16use MediaWiki\Hook\GetDoubleUnderscoreIDsHook;
17use MediaWiki\Hook\ParserAfterTidyHook;
18use MediaWiki\Hook\ParserOutputPostCacheTransformHook;
19use MediaWiki\Parser\Parser;
20use MediaWiki\Parser\ParserOutput;
21use MediaWiki\Parser\ParserOutputFlags;
22use MediaWiki\Parser\Parsoid\PageBundleParserOutputConverter;
23use MediaWiki\Parser\Parsoid\ParsoidParser;
24use MediaWiki\Title\Title;
25
26class ParserHooks implements
27    ParserOutputPostCacheTransformHook,
28    GetDoubleUnderscoreIDsHook,
29    ParserAfterTidyHook
30{
31
32    private readonly Config $config;
33
34    public function __construct(
35        ConfigFactory $configFactory
36    ) {
37        $this->config = $configFactory->makeConfig( 'discussiontools' );
38    }
39
40    private function transformHtml(
41        ParserOutput $pout, string &$html, Title $title, bool $isPreview
42    ): void {
43        // This condition must be unreliant on current enablement config or user preference.
44        // In other words, include parser output of talk pages with DT disabled.
45        //
46        // This is similar to HookUtils::isAvailableForTitle, but instead of querying the
47        // database for the latest metadata of a page that exists, we check metadata of
48        // the given ParserOutput object only (this runs before the edit is saved).
49        if ( $title->isTalkPage() || $pout->getNewSection() ) {
50            $talkExpiry = $this->config->get( 'DiscussionToolsTalkPageParserCacheExpiry' );
51            // Override parser cache expiry of talk pages (T280605).
52            // Note, this can only shorten it. MediaWiki ignores values higher than the default.
53            // NOTE: this currently has no effect for Parsoid read
54            // views, since parsoid executes this method as a
55            // post-cache transform.  *However* future work may allow
56            // caching of intermediate results of the "post cache"
57            // transformation pipeline, in which case this code will
58            // again be effective. (More: T350626)
59            if ( $talkExpiry > 0 ) {
60                $pout->updateCacheExpiry( $talkExpiry );
61            }
62        }
63
64        // Always apply the DOM transform if DiscussionTools are available for this page,
65        // to allow linking to individual comments from Echo 'mention' and 'edit-user-talk'
66        // notifications (T253082, T281590), and to reduce parser cache fragmentation (T279864).
67        // The extra buttons are hidden in CSS (ext.discussionTools.init.styles module) when
68        // the user doesn't have DiscussionTools features enabled.
69        if ( HookUtils::isAvailableForTitle( $title ) ) {
70            // This modifies $html
71            CommentFormatter::addDiscussionTools( $html, $pout, $title );
72
73            if ( $isPreview ) {
74                $batchModifyElements = new BatchModifyElements();
75                CommentFormatter::removeInteractiveTools( $batchModifyElements );
76                $html = $batchModifyElements->apply( $html );
77                // Suppress the empty state
78                $pout->setExtensionData( 'DiscussionTools-isEmptyTalkPage', null );
79            }
80
81            $pout->addModuleStyles( [ 'ext.discussionTools.init.styles' ] );
82        }
83    }
84
85    /**
86     * For now, this hook only runs on Parsoid HTML. Eventually, this is likely
87     * to be run for legacy HTML but that requires ParserCache storage to be allocated
88     * for DiscussionTools HTML which will be purused separately.
89     *
90     * @inheritDoc
91     */
92    public function onParserOutputPostCacheTransform( $parserOutput, &$text, &$options ): void {
93        $isPreview = $parserOutput->getOutputFlag( ParserOutputFlags::IS_PREVIEW );
94
95        // We want to run this hook only on Parsoid HTML for now.
96        // (and leave the onParserAfterTidy handler for legacy HTML).
97        if ( PageBundleParserOutputConverter::hasPageBundle( $parserOutput ) ) {
98            $titleDbKey = $parserOutput->getExtensionData( ParsoidParser::PARSOID_TITLE_KEY );
99            $title = Title::newFromDBkey( $titleDbKey );
100            '@phan-var Title $title';
101            $this->transformHtml( $parserOutput, $text, $title, $isPreview );
102        }
103    }
104
105    /**
106     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserAfterTidy
107     *
108     * @param Parser $parser
109     * @param string &$text
110     */
111    public function onParserAfterTidy( $parser, &$text ) {
112        $pOpts = $parser->getOptions();
113        if ( $pOpts->isMessage() ) {
114            return;
115        }
116
117        $this->transformHtml(
118            $parser->getOutput(), $text, $parser->getTitle(), $pOpts->getIsPreview()
119        );
120    }
121
122    /**
123     * @see https://www.mediawiki.org/wiki/Manual:Hooks/GetDoubleUnderscoreIDs
124     *
125     * @param string[] &$doubleUnderscoreIDs
126     * @return bool|void
127     */
128    public function onGetDoubleUnderscoreIDs( &$doubleUnderscoreIDs ) {
129        $doubleUnderscoreIDs[] = 'archivedtalk';
130        $doubleUnderscoreIDs[] = 'notalk';
131    }
132}