Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 79
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialDiscussionToolsDebug
0.00% covered (danger)
0.00%
0 / 79
0.00% covered (danger)
0.00%
0 / 8
240
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
 getDescription
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFormFields
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 getSubpageField
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDisplayFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 requiresPost
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onSubmit
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
20
 formatComments
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace MediaWiki\Extension\DiscussionTools;
4
5use MediaWiki\Extension\DiscussionTools\ThreadItem\ContentCommentItem;
6use MediaWiki\Extension\DiscussionTools\ThreadItem\ContentHeadingItem;
7use MediaWiki\Extension\DiscussionTools\ThreadItem\ContentThreadItem;
8use MediaWiki\Html\Html;
9use MediaWiki\Languages\LanguageFactory;
10use MediaWiki\Linker\Linker;
11use MediaWiki\Page\ParserOutputAccess;
12use MediaWiki\Parser\ParserOptions;
13use MediaWiki\SpecialPage\FormSpecialPage;
14use MediaWiki\SpecialPage\SpecialPage;
15use MediaWiki\Title\Title;
16use MediaWiki\Utils\MWTimestamp;
17use Wikimedia\Assert\Assert;
18use Wikimedia\Parsoid\Utils\DOMCompat;
19use Wikimedia\Parsoid\Utils\DOMUtils;
20
21class SpecialDiscussionToolsDebug extends FormSpecialPage {
22
23    public function __construct(
24        private readonly LanguageFactory $languageFactory,
25        private readonly ParserOutputAccess $parserOutputAccess,
26        private readonly CommentParser $commentParser,
27    ) {
28        parent::__construct( 'DiscussionToolsDebug' );
29    }
30
31    /**
32     * @inheritDoc
33     */
34    public function getDescription() {
35        return $this->msg( 'discussiontoolsdebug-title' );
36    }
37
38    /**
39     * @inheritDoc
40     */
41    protected function getFormFields() {
42        return [
43            'pagetitle' => [
44                'label-message' => 'discussiontoolsdebug-pagetitle',
45                'name' => 'pagetitle',
46                'type' => 'title',
47                'required' => true,
48                'exists' => true,
49            ],
50        ];
51    }
52
53    /**
54     * @inheritDoc
55     */
56    protected function getSubpageField() {
57        return 'pagetitle';
58    }
59
60    /**
61     * @inheritDoc
62     */
63    protected function getDisplayFormat() {
64        return 'ooui';
65    }
66
67    /**
68     * @inheritDoc
69     */
70    public function requiresPost() {
71        return false;
72    }
73
74    /**
75     * @inheritDoc
76     */
77    public function onSubmit( array $data ) {
78        $title = Title::newFromText( $data['pagetitle'] );
79
80        $parserOptions = ParserOptions::newFromAnon();
81        $status = $this->parserOutputAccess->getParserOutput(
82            $title->toPageRecord(),
83            $parserOptions
84        );
85        if ( !$status->isOK() ) {
86            return $status;
87        }
88
89        $parserOutput = $status->getValue();
90        $html = $parserOutput->runOutputPipeline( $parserOptions, [
91            'enableSectionEditLinks' => false,
92        ] )->getContentHolderText();
93
94        $doc = DOMUtils::parseHTML( $html );
95        $container = DOMCompat::getBody( $doc );
96        $threadItemSet = $this->commentParser->parse( $container, $title->getTitleValue() );
97
98        $out = $this->getOutput();
99
100        $out->addHTML( $this->msg(
101            'discussiontoolsdebug-intro',
102            $title->getPrefixedText(),
103            SpecialPage::getTitleFor(
104                'ApiSandbox',
105                false,
106                'action=discussiontoolspageinfo&prop=threaditemshtml&excludesignatures=1&page='
107                    . urlencode( $title->getPrefixedText() )
108            )->getFullText()
109        )->parseAsBlock() );
110
111        $parsedLanguage = $parserOutput->getLanguage();
112        $pageLang = $parsedLanguage !== null ?
113            $this->languageFactory->getLanguage( $parsedLanguage ) : $this->getLanguage();
114        $pageLangAttribs = [
115            'lang' => $pageLang->getHtmlCode(),
116            'dir' => $pageLang->getDir(),
117            'class' => 'mw-content-' . $pageLang->getDir(),
118        ];
119
120        foreach ( $threadItemSet->getThreadsStructured() as $thread ) {
121            $out->addHTML( $this->formatComments( $thread, $pageLangAttribs ) );
122        }
123
124        $out->addModuleStyles( 'ext.discussionTools.debug.styles' );
125
126        return true;
127    }
128
129    /**
130     * Format a thread item with replies.
131     *
132     * @return string HTML
133     */
134    private function formatComments( ContentThreadItem $comment, array $pageLangAttribs ) {
135        if ( $comment instanceof ContentHeadingItem ) {
136            $contents = '<span class="mw-dt-heading">' . $comment->getHTML() . '</span>';
137        } else {
138            Assert::precondition( $comment instanceof ContentCommentItem, 'Must be ContentCommentItem' );
139            $contents =
140                '<span class="mw-dt-comment-signature">' .
141                    '<span class="mw-dt-comment-author">' .
142                        Linker::userLink( 0, $comment->getAuthor() ) .
143                    '</span>' . ' ' .
144                    '(' . Linker::userTalkLink( 0, $comment->getAuthor() ) . ') ' .
145                    '<span class="mw-dt-comment-timestamp">' .
146                        htmlspecialchars( $this->getLanguage()->getHumanTimestamp(
147                            new MWTimestamp( $comment->getTimestamp()->getTimestamp() )
148                        ) ) .
149                    '</span>' .
150                '</span>' .
151                Html::rawElement( 'div', $pageLangAttribs,
152                    '<div class="mw-dt-comment-body mw-parser-output">' . $comment->getBodyHTML( true ) . '</div>'
153                );
154        }
155        $level = $comment->getLevel();
156
157        $replies = '';
158        foreach ( $comment->getReplies() as $reply ) {
159            $replies .= $this->formatComments( $reply, $pageLangAttribs );
160        }
161
162        return Html::rawElement( $replies ? 'details' : 'div', [
163            'open' => (bool)$replies,
164            'class' => 'mw-dt-comment',
165            'data-level' => $level,
166        ], ( $replies ? Html::rawElement( 'summary', [], $contents ) : $contents ) . $replies );
167    }
168
169}