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