Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiDiscussionToolsTrait
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 4
240
0.00% covered (danger)
0.00%
0 / 1
 previewMessage
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 1
156
 getUserForPreview
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getParsoidClient
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 requestRestbasePageHtml
n/a
0 / 0
n/a
0 / 0
0
 transformHTML
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0
 getContext
n/a
0 / 0
n/a
0 / 0
0
 getUser
n/a
0 / 0
n/a
0 / 0
0
 getRequest
n/a
0 / 0
n/a
0 / 0
0
1<?php
2
3namespace MediaWiki\Extension\DiscussionTools;
4
5use ApiMain;
6use ApiResult;
7use DerivativeContext;
8use IContextSource;
9use MediaWiki\Extension\VisualEditor\ParsoidClient;
10use MediaWiki\Extension\VisualEditor\VisualEditorParsoidClientFactory;
11use MediaWiki\Request\DerivativeRequest;
12use MediaWiki\Request\WebRequest;
13use MediaWiki\Revision\RevisionRecord;
14use MediaWiki\Title\Title;
15use MediaWiki\User\TempUser\TempUserCreator;
16use MediaWiki\User\User;
17use MediaWiki\User\UserFactory;
18use Wikimedia\Parsoid\Utils\DOMCompat;
19use Wikimedia\Parsoid\Utils\DOMUtils;
20
21/**
22 * Random methods we want to share between API modules.
23 *
24 * @property VisualEditorParsoidClientFactory $parsoidClientFactory
25 * @property CommentParser $commentParser
26 * @property TempUserCreator $tempUserCreator
27 * @property UserFactory $userFactory
28 */
29trait ApiDiscussionToolsTrait {
30
31    /**
32     * Given parameters describing a reply or new topic, transform them into wikitext using Parsoid,
33     * then preview the wikitext using the legacy parser.
34     *
35     * @param string $type 'topic' or 'reply'
36     * @param Title $title Context title for wikitext transformations
37     * @param array $params Associative array with the following keys:
38     *  - `wikitext` (string|null) Content of the message, mutually exclusive with `html`
39     *  - `html` (string|null) Content of the message, mutually exclusive with `wikitext`
40     *  - `sectiontitle` (string) Content of the title, when `type` is 'topic'
41     *  - `signature` (string|null) Wikitext signature to add to the message
42     * @param array $originalParams Original params from the source API call
43     * @return ApiResult action=parse API result
44     */
45    protected function previewMessage(
46        string $type, Title $title, array $params, array $originalParams = []
47    ): ApiResult {
48        $wikitext = $params['wikitext'] ?? null;
49        $html = $params['html'] ?? null;
50        $signature = $params['signature'] ?? null;
51
52        switch ( $type ) {
53            case 'topic':
54                if ( $wikitext !== null ) {
55                    if ( $signature !== null ) {
56                        $wikitext = CommentModifier::appendSignatureWikitext( $wikitext, $signature );
57                    }
58                } else {
59                    $doc = DOMUtils::parseHTML( '' );
60                    $container = DOMUtils::parseHTMLToFragment( $doc, $html );
61                    if ( $signature !== null ) {
62                        CommentModifier::appendSignature( $container, $signature );
63                    }
64                    $html = DOMUtils::getFragmentInnerHTML( $container );
65                    $wikitext = $this->transformHTML( $title, $html )[ 'body' ];
66                }
67
68                if ( $params['sectiontitle'] ) {
69                    $wikitext = "== " . $params['sectiontitle'] . " ==\n" . $wikitext;
70                }
71
72                break;
73
74            case 'reply':
75                $doc = DOMUtils::parseHTML( '' );
76
77                if ( $wikitext !== null ) {
78                    $container = CommentModifier::prepareWikitextReply( $doc, $wikitext );
79                } else {
80                    $container = CommentModifier::prepareHtmlReply( $doc, $html );
81                }
82
83                if ( $signature !== null ) {
84                    CommentModifier::appendSignature( $container, $signature );
85                }
86                $list = CommentModifier::transferReply( $container );
87                $html = DOMCompat::getOuterHTML( $list );
88
89                $wikitext = $this->transformHTML( $title, $html )[ 'body' ];
90
91                break;
92        }
93
94        $apiParams = [
95            'action' => 'parse',
96            'title' => $title->getPrefixedText(),
97            'text' => $wikitext,
98            'pst' => '1',
99            'preview' => '1',
100            'disableeditsection' => '1',
101            'prop' => 'text|modules|jsconfigvars',
102        ];
103        if ( isset( $originalParams['useskin'] ) ) {
104            $apiParams['useskin'] = $originalParams['useskin'];
105        }
106        if ( isset( $originalParams['mobileformat'] ) && $originalParams['mobileformat'] ) {
107            $apiParams['mobileformat'] = '1';
108        }
109
110        $context = new DerivativeContext( $this->getContext() );
111        $context->setRequest(
112            new DerivativeRequest(
113                $context->getRequest(),
114                $apiParams,
115                /* was posted? */ true
116            )
117        );
118        $api = new ApiMain(
119            $context,
120            /* enable write? */ false
121        );
122
123        $api->execute();
124        return $api->getResult();
125    }
126
127    /**
128     * @see ApiParse::getUserForPreview
129     * @return User
130     */
131    private function getUserForPreview() {
132        $user = $this->getUser();
133        if ( $this->tempUserCreator->shouldAutoCreate( $user, 'edit' ) ) {
134            return $this->userFactory->newUnsavedTempUser(
135                $this->tempUserCreator->getStashedName( $this->getRequest()->getSession() )
136            );
137        }
138        return $user;
139    }
140
141    /**
142     * @see VisualEditorParsoidClientFactory
143     * @return ParsoidClient
144     */
145    protected function getParsoidClient(): ParsoidClient {
146        return $this->parsoidClientFactory->createParsoidClient(
147            $this->getContext()->getRequest()->getHeader( 'Cookie' )
148        );
149    }
150
151    /**
152     * @warning (T323357) - Calling this method writes to stash, so it should be called
153     *   only when we are fetching page HTML for editing.
154     *
155     * @param RevisionRecord $revision
156     * @return array
157     */
158    abstract protected function requestRestbasePageHtml( RevisionRecord $revision ): array;
159
160    /**
161     * @param Title $title
162     * @param string $html
163     * @param int|null $oldid
164     * @param string|null $etag
165     * @return array
166     */
167    abstract protected function transformHTML(
168        Title $title, string $html, int $oldid = null, string $etag = null
169    ): array;
170
171    /**
172     * @return IContextSource
173     */
174    abstract public function getContext();
175
176    /**
177     * @return User
178     */
179    abstract public function getUser();
180
181    /**
182     * @return WebRequest
183     */
184    abstract public function getRequest();
185
186}