Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.15% covered (success)
96.15%
50 / 52
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
RevisionHTMLHandler
96.15% covered (success)
96.15%
50 / 52
77.78% covered (warning)
77.78%
7 / 9
18
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 postValidationSetup
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
5
 run
96.00% covered (success)
96.00%
24 / 25
0.00% covered (danger)
0.00%
0 / 1
4
 getETag
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getLastModified
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getOutputMode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 needsWriteAccess
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getParamSettings
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 hasRepresentation
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\Rest\Handler;
4
5use LogicException;
6use MediaWiki\Rest\Handler\Helper\HtmlOutputRendererHelper;
7use MediaWiki\Rest\Handler\Helper\PageRestHelperFactory;
8use MediaWiki\Rest\Handler\Helper\RevisionContentHelper;
9use MediaWiki\Rest\LocalizedHttpException;
10use MediaWiki\Rest\Response;
11use MediaWiki\Rest\SimpleHandler;
12use MediaWiki\Rest\StringStream;
13use Wikimedia\Assert\Assert;
14
15/**
16 * A handler that returns Parsoid HTML for the following routes:
17 * - /revision/{revision}/html,
18 * - /revision/{revision}/with_html
19 *
20 * Class RevisionHTMLHandler
21 * @package MediaWiki\Rest\Handler
22 */
23class RevisionHTMLHandler extends SimpleHandler {
24
25    /** @var HtmlOutputRendererHelper */
26    private $htmlHelper;
27
28    /** @var RevisionContentHelper */
29    private $contentHelper;
30
31    public function __construct( PageRestHelperFactory $helperFactory ) {
32        $this->contentHelper = $helperFactory->newRevisionContentHelper();
33        $this->htmlHelper = $helperFactory->newHtmlOutputRendererHelper();
34    }
35
36    protected function postValidationSetup() {
37        $authority = $this->getAuthority();
38        $this->contentHelper->init( $authority, $this->getValidatedParams() );
39
40        $page = $this->contentHelper->getPage();
41        $revision = $this->contentHelper->getTargetRevision();
42
43        if ( $page && $revision ) {
44            $this->htmlHelper->init( $page, $this->getValidatedParams(), $authority, $revision );
45
46            $request = $this->getRequest();
47            $acceptLanguage = $request->getHeaderLine( 'Accept-Language' ) ?: null;
48            if ( $acceptLanguage ) {
49                $this->htmlHelper->setVariantConversionLanguage(
50                    $acceptLanguage
51                );
52            }
53        }
54    }
55
56    /**
57     * @return Response
58     * @throws LocalizedHttpException
59     */
60    public function run(): Response {
61        $this->contentHelper->checkAccess();
62
63        $page = $this->contentHelper->getPage();
64        $revisionRecord = $this->contentHelper->getTargetRevision();
65
66        // The call to $this->contentHelper->getPage() should not return null if
67        // $this->contentHelper->checkAccess() did not throw.
68        Assert::invariant( $page !== null, 'Page should be known' );
69
70        // The call to $this->contentHelper->getTargetRevision() should not return null if
71        // $this->contentHelper->checkAccess() did not throw.
72        Assert::invariant( $revisionRecord !== null, 'Revision should be known' );
73
74        $outputMode = $this->getOutputMode();
75        $setContentLanguageHeader = true;
76        switch ( $outputMode ) {
77            case 'html':
78                $parserOutput = $this->htmlHelper->getHtml();
79                $response = $this->getResponseFactory()->create();
80                // TODO: need to respect content-type returned by Parsoid.
81                $response->setHeader( 'Content-Type', 'text/html' );
82                $this->htmlHelper->putHeaders( $response, $setContentLanguageHeader );
83                $this->contentHelper->setCacheControl( $response, $parserOutput->getCacheExpiry() );
84                $response->setBody( new StringStream( $parserOutput->getRawText() ) );
85                break;
86            case 'with_html':
87                $parserOutput = $this->htmlHelper->getHtml();
88                $body = $this->contentHelper->constructMetadata();
89                $body['html'] = $parserOutput->getRawText();
90                $response = $this->getResponseFactory()->createJson( $body );
91                // For JSON content, it doesn't make sense to set content language header
92                $this->htmlHelper->putHeaders( $response, !$setContentLanguageHeader );
93                $this->contentHelper->setCacheControl( $response, $parserOutput->getCacheExpiry() );
94                break;
95            default:
96                throw new LogicException( "Unknown HTML type $outputMode" );
97        }
98
99        return $response;
100    }
101
102    /**
103     * Returns an ETag representing a page's source. The ETag assumes a page's source has changed
104     * if the latest revision of a page has been made private, un-readable for another reason,
105     * or a newer revision exists.
106     * @return string|null
107     */
108    protected function getETag(): ?string {
109        if ( !$this->contentHelper->isAccessible() ) {
110            return null;
111        }
112
113        // Vary eTag based on output mode
114        return $this->htmlHelper->getETag( $this->getOutputMode() );
115    }
116
117    /**
118     * @return string|null
119     */
120    protected function getLastModified(): ?string {
121        if ( !$this->contentHelper->isAccessible() ) {
122            return null;
123        }
124
125        return $this->htmlHelper->getLastModified();
126    }
127
128    private function getOutputMode(): string {
129        return $this->getConfig()['format'];
130    }
131
132    public function needsWriteAccess(): bool {
133        return false;
134    }
135
136    public function getParamSettings(): array {
137        return array_merge(
138            $this->contentHelper->getParamSettings(),
139            $this->htmlHelper->getParamSettings()
140        );
141    }
142
143    /**
144     * @return bool
145     */
146    protected function hasRepresentation() {
147        return $this->contentHelper->hasContent();
148    }
149}