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