performer = $performer; $this->helperFactory = $helperFactory; } /** * @param PageIdentity $page * @param RevisionRecord|null $revision * @param Bcp47Code|null $pageLanguage * @param bool $stash * @param string $flavor * * @return HtmlOutputRendererHelper */ private function getHtmlOutputRendererHelper( PageIdentity $page, ?RevisionRecord $revision = null, Bcp47Code $pageLanguage = null, bool $stash = false, string $flavor = self::FLAVOR_DEFAULT ): HtmlOutputRendererHelper { $helper = $this->helperFactory->newHtmlOutputRendererHelper(); // TODO: remove this once we no longer need a User object for rate limiting (T310476). if ( $this->performer instanceof User ) { $user = $this->performer; } else { $user = User::newFromIdentity( $this->performer->getUser() ); } $helper->init( $page, [], $user, $revision ); // Ensure we get a compatible version, not just the default $helper->setOutputProfileVersion( self::PARSOID_VERSION ); $helper->setStashingEnabled( $stash ); if ( !$stash ) { $helper->setFlavor( $flavor ); } if ( $revision ) { $helper->setRevision( $revision ); } if ( $pageLanguage ) { $helper->setPageLanguage( $pageLanguage ); } return $helper; } /** * @param PageIdentity $page * @param string $html * @param int|null $oldid * @param string|null $etag * @param Bcp47Code|null $pageLanguage * * @return HtmlInputTransformHelper */ private function getHtmlInputTransformHelper( PageIdentity $page, string $html, int $oldid = null, string $etag = null, Bcp47Code $pageLanguage = null ): HtmlInputTransformHelper { $helper = $this->helperFactory->newHtmlInputTransformHelper(); // Fake REST body $body = [ 'html' => [ 'body' => $html, ] ]; $renderId = $etag ? ParsoidRenderID::newFromETag( $etag ) : null; $metrics = MediaWikiServices::getInstance()->getParsoidSiteConfig()->metrics(); if ( $metrics ) { $helper->setMetrics( $metrics ); } $helper->init( $page, $body, [], null, $pageLanguage ); if ( $oldid || $renderId ) { $helper->setOriginal( $oldid, $renderId ); } return $helper; } /** * Request page HTML from Parsoid. * * @param RevisionRecord $revision Page revision * @param ?Bcp47Code $targetLanguage Page language (default: `null`) * * @return array An array mimicking a RESTbase server's response, with keys: 'headers' and 'body' * @phan-return array{body:string,headers:array} */ public function getPageHtml( RevisionRecord $revision, ?Bcp47Code $targetLanguage = null ): array { // In the VE client, we always want to stash. $page = $revision->getPage(); $helper = $this->getHtmlOutputRendererHelper( $page, $revision, $targetLanguage, true ); $parserOutput = $helper->getHtml(); return $this->fakeRESTbaseHTMLResponse( $parserOutput->getRawText(), $helper ); } /** * @param PageIdentity $page * @param string $wikitext * * @return RevisionRecord */ private function makeFakeRevision( PageIdentity $page, string $wikitext ): RevisionRecord { $rev = new MutableRevisionRecord( $page ); $rev->setId( 0 ); $rev->setPageId( $page->getId() ); $rev->setContent( SlotRecord::MAIN, new WikitextContent( $wikitext ) ); return $rev; } /** * Transform wikitext to HTML with Parsoid. * * @param PageIdentity $page The page the content belongs to use as the parsing context * @param Bcp47Code $targetLanguage Page language * @param string $wikitext The wikitext fragment to parse * @param bool $bodyOnly Whether to provide only the contents of the `` tag * @param int|null $oldid What oldid revision, if any, to base the request from (default: `null`) * @param bool $stash Whether to stash the result in the server-side cache (default: `false`) * * @return array An array mimicking a RESTbase server's response, with keys: 'headers' and 'body' * @phan-return array{body:string,headers:array} */ public function transformWikitext( PageIdentity $page, Bcp47Code $targetLanguage, string $wikitext, bool $bodyOnly, ?int $oldid, bool $stash ): array { $revision = $this->makeFakeRevision( $page, $wikitext ); $helper = $this->getHtmlOutputRendererHelper( $page, $revision, $targetLanguage, $stash ); if ( $bodyOnly ) { $helper->setFlavor( 'fragment' ); } $parserOutput = $helper->getHtml(); $html = $parserOutput->getRawText(); return $this->fakeRESTbaseHTMLResponse( $html, $helper ); } /** * Transform HTML to wikitext with Parsoid * * @param PageIdentity $page The page the content belongs to * @param Bcp47Code $targetLanguage The desired output language * @param string $html The HTML of the page to be transformed * @param ?int $oldid What oldid revision, if any, to base the request from (default: `null`) * @param ?string $etag The ETag to set in the HTTP request header * * @return array An array mimicking a RESTbase server's response, with keys: 'headers' and 'body' * @phan-return array{body:string,headers:array} */ public function transformHTML( PageIdentity $page, Bcp47Code $targetLanguage, string $html, ?int $oldid, ?string $etag ): array { $helper = $this->getHtmlInputTransformHelper( $page, $html, $oldid, $etag, $targetLanguage ); $content = $helper->getContent(); $format = $content->getDefaultFormat(); return [ 'headers' => [ 'Content-Type' => $format, ], 'body' => $content->serialize( $format ), ]; } /** * @param mixed $data * @param HtmlOutputRendererHelper $helper * * @return array */ private function fakeRESTbaseHTMLResponse( $data, HtmlOutputRendererHelper $helper ): array { $contentLanguage = $helper->getHtmlOutputContentLanguage(); return [ 'headers' => [ 'content-language' => $contentLanguage->toBcp47Code(), 'etag' => $helper->getETag() ], 'body' => $data, ]; } }