Move Content::getParserOutput & AbstractContent::fillParserOutput to ContentHandler

Update/Create override classes of ContentHandler.
Soft-deprecate and remove method from Content and classes that override them.

Bug: T287158
Change-Id: Idfcfbfe1a196cd69a04ca357281d08bb3d097ce2
This commit is contained in:
Roman Stolar 2021-08-24 15:17:12 +03:00
parent 3801581dae
commit a68e641f9d
31 changed files with 625 additions and 319 deletions

View file

@ -149,7 +149,11 @@ because of Phabricator reports.
* The following methods from the User class were hard deprecated:
- ::blockedBy
- ::getBlockId
* Content::getParserOutput() was deprecated.
Use ContentRenderer::getParserOutput and override
ContentHandler::fillParserOutput instead.
* MessageContent class was hard-deprecated.
* Message::content() was hard-deprecated.
* …
=== Other changes in 1.38 ===

View file

@ -43,6 +43,7 @@ use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\Collation\CollationFactory;
use MediaWiki\Config\ConfigRepository;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Content\Renderer\ContentRenderer;
use MediaWiki\Content\Transform\ContentTransformer;
use MediaWiki\EditPage\SpamChecker;
use MediaWiki\Export\WikiExporterFactory;
@ -820,6 +821,14 @@ class MediaWikiServices extends ServiceContainer {
return $this->getService( 'ContentModelStore' );
}
/**
* @since 1.38
* @return ContentRenderer
*/
public function getContentRenderer(): ContentRenderer {
return $this->getService( 'ContentRenderer' );
}
/**
* @since 1.37
* @return ContentTransformer

View file

@ -65,6 +65,7 @@ use MediaWiki\Config\ConfigRepository;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Content\ContentHandlerFactory;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Content\Renderer\ContentRenderer;
use MediaWiki\Content\Transform\ContentTransformer;
use MediaWiki\EditPage\Constraint\EditConstraintFactory;
use MediaWiki\EditPage\SpamChecker;
@ -392,6 +393,10 @@ return [
return $services->getNameTableStoreFactory()->getContentModels();
},
'ContentRenderer' => static function ( MediaWikiServices $services ): ContentRenderer {
return new ContentRenderer( $services->getContentHandlerFactory() );
},
'ContentTransformer' => static function ( MediaWikiServices $services ): ContentTransformer {
return new ContentTransformer( $services->getContentHandlerFactory() );
},

View file

@ -27,6 +27,7 @@
*/
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\Transform\PreloadTransformParamsValue;
use MediaWiki\Content\Transform\PreSaveTransformParamsValue;
use MediaWiki\MediaWikiServices;
@ -517,10 +518,9 @@ abstract class AbstractContent implements Content {
* Subclasses that override getParserOutput() itself should take care to call the
* ContentGetParserOutput hook.
*
* @stable to override
*
* @since 1.24
*
* @deprecated since 1.38. Use ContentRenderer::getParserOutput instead.
* Extensions defining a content model should override ContentHandler::fillParserOutput.
* @param Title $title Context title for parsing
* @param int|null $revId Revision ID being rendered
* @param ParserOptions|null $options
@ -531,34 +531,47 @@ abstract class AbstractContent implements Content {
public function getParserOutput( Title $title, $revId = null,
ParserOptions $options = null, $generateHtml = true
) {
if ( $options === null ) {
$options = ParserOptions::newCanonical( 'canonical' );
$detectGPODeprecatedOverride = MWDebug::detectDeprecatedOverride(
$this,
self::class,
'getParserOutput'
);
$detectFPODeprecatedOverride = MWDebug::detectDeprecatedOverride(
$this,
self::class,
'fillParserOutput'
);
if ( $detectGPODeprecatedOverride || $detectFPODeprecatedOverride ) {
if ( $options === null ) {
$options = ParserOptions::newCanonical( 'canonical' );
}
$po = new ParserOutput();
$options->registerWatcher( [ $po, 'recordOption' ] );
if ( Hooks::runner()->onContentGetParserOutput(
$this, $title, $revId, $options, $generateHtml, $po )
) {
// Save and restore the old value, just in case something is reusing
// the ParserOptions object in some weird way.
$oldRedir = $options->getRedirectTarget();
$options->setRedirectTarget( $this->getRedirectTarget() );
$this->fillParserOutput( $title, $revId, $options, $generateHtml, $po );
$options->setRedirectTarget( $oldRedir );
}
Hooks::runner()->onContentAlterParserOutput( $this, $title, $po );
$options->registerWatcher( null );
return $po;
}
$output = new ParserOutput();
$options->registerWatcher( [ $output, 'recordOption' ] );
if ( Hooks::runner()->onContentGetParserOutput(
$this, $title, $revId, $options, $generateHtml, $output )
) {
// Save and restore the old value, just in case something is reusing
// the ParserOptions object in some weird way.
$oldRedir = $options->getRedirectTarget();
$options->setRedirectTarget( $this->getRedirectTarget() );
$this->fillParserOutput( $title, $revId, $options, $generateHtml, $output );
MediaWikiServices::getInstance()->get( '_ParserObserver' )->notifyParse(
$title,
$revId,
$options,
$output
);
$options->setRedirectTarget( $oldRedir );
}
Hooks::runner()->onContentAlterParserOutput( $this, $title, $output );
$options->registerWatcher( null );
return $output;
$cpoParams = new ContentParseParams( $title, $revId, $options, $generateHtml );
return $this->getContentHandler()->getParserOutput(
$this,
$cpoParams
);
}
/**
@ -571,10 +584,8 @@ abstract class AbstractContent implements Content {
*
* This placeholder implementation always throws an exception.
*
* @stable to override
*
* @since 1.24
*
* @deprecated since 1.37. Use ContentHandler::fillParserOutput instead.
* @param Title $title Context title for parsing
* @param int|null $revId ID of the revision being rendered.
* See Parser::parse() for the ramifications.
@ -587,7 +598,7 @@ abstract class AbstractContent implements Content {
protected function fillParserOutput( Title $title, $revId,
ParserOptions $options, $generateHtml, ParserOutput &$output
) {
// Don't make abstract, so subclasses that override getParserOutput() directly don't fail.
throw new MWException( 'Subclasses of AbstractContent must override fillParserOutput!' );
$cpoParams = new ContentParseParams( $title, $revId, $options, $generateHtml );
return $this->getContentHandler()->fillParserOutputInternal( $this, $cpoParams, $output );
}
}

View file

@ -268,7 +268,8 @@ interface Content {
* @note To control which options are used in the cache key for the
* generated parser output, implementations of this method
* may call ParserOutput::recordOption() on the output object.
*
* @deprecated since 1.38. Use ContentRenderer::getParserOutput
* and override ContentHandler::fillParserOutput.
* @param Title $title The page title to use as a context for rendering.
* @param int|null $revId ID of the revision being rendered.
* See Parser::parse() for the ramifications. (default: null)

View file

@ -26,6 +26,7 @@
* @author Daniel Kinzler
*/
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\Transform\PreloadTransformParams;
use MediaWiki\Content\Transform\PreSaveTransformParams;
use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
@ -1606,6 +1607,116 @@ abstract class ContentHandler {
);
}
/**
* Returns a ParserOutput object containing information derived from this content.
* Most importantly, unless $cpoParams->getGenerateHtml was false, the return value contains an
* HTML representation of the content.
*
* Subclasses that want to control the parser output may override
* fillParserOutput() instead.
*
*
*
* @since 1.38
*
* @param Content $content
* @param ContentParseParams $cpoParams
* @return ParserOutput Containing information derived from this content.
*/
public function getParserOutput(
Content $content,
ContentParseParams $cpoParams
) {
$detectGPODeprecatedOverride = MWDebug::detectDeprecatedOverride(
$content,
AbstractContent::class,
'getParserOutput'
);
$detectFPODeprecatedOverride = MWDebug::detectDeprecatedOverride(
$content,
AbstractContent::class,
'fillParserOutput'
);
if ( $detectGPODeprecatedOverride || $detectFPODeprecatedOverride ) {
return $this->callDeprecatedContentGPO( $content, $cpoParams );
}
$services = MediaWikiServices::getInstance();
$title = $services->getTitleFactory()->castFromPageReference( $cpoParams->getPage() );
$parserOptions = $cpoParams->getParserOptions();
$po = new ParserOutput();
$parserOptions->registerWatcher( [ $po, 'recordOption' ] );
if ( Hooks::runner()->onContentGetParserOutput(
$content, $title, $cpoParams->getRevId(), $parserOptions, $cpoParams->getGenerateHtml(), $po )
) {
// Save and restore the old value, just in case something is reusing
// the ParserOptions object in some weird way.
$oldRedir = $parserOptions->getRedirectTarget();
$parserOptions->setRedirectTarget( $content->getRedirectTarget() );
$this->fillParserOutput(
$content,
$cpoParams,
$po
);
MediaWikiServices::getInstance()->get( '_ParserObserver' )->notifyParse(
$title,
$cpoParams->getRevId(),
$parserOptions,
$po
);
$parserOptions->setRedirectTarget( $oldRedir );
}
Hooks::runner()->onContentAlterParserOutput( $content, $title, $po );
$parserOptions->registerWatcher( null );
return $po;
}
/**
* A temporary layer to move AbstractContent::fillParserOutput to ContentHandler::fillParserOutput
*
* @internal only core AbstractContent::fillParserOutput implementations need to call this.
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*/
public function fillParserOutputInternal(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
$this->fillParserOutput( $content, $cpoParams, $output );
}
/**
* Fills the provided ParserOutput with information derived from the content.
* Unless $generateHtml was false, this includes an HTML representation of the content.
*
* Subclasses are expected to override this method.
*
* This placeholder implementation always throws an exception.
*
* @stable to override
*
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*
* @throws MWException
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
// Subclasses must override fillParserOutput() to directly don't fail.
throw new MWException( 'Subclasses of ContentHandler must override fillParserOutput!' );
}
/**
* Check if we need to provide content overrides deprecated Content method.
*
@ -1672,4 +1783,26 @@ abstract class ContentHandler {
$params->getParams()
);
}
/**
* If provided content overrides deprecated Content::getParserOutput,
* call it and return.
* @internal only core ContentHandler implementations need to call this.
* @param Content $content
* @param ContentParseParams $cpoParams
* @return ParserOutput
*/
protected function callDeprecatedContentGPO(
Content $content,
ContentParseParams $cpoParams
) {
$services = MediaWikiServices::getInstance();
$legacyTitle = $services->getTitleFactory()->castFromPageReference( $cpoParams->getPage() );
return $content->getParserOutput(
$legacyTitle,
$cpoParams->getRevId(),
$cpoParams->getParserOptions(),
$cpoParams->getGenerateHtml()
);
}
}

View file

@ -47,16 +47,6 @@ class CssContent extends TextContent {
parent::__construct( $text, $modelId );
}
/**
* @return string CSS wrapped in a <pre> tag.
*/
protected function getHtml() {
return Html::element( 'pre',
[ 'class' => 'mw-code mw-css', 'dir' => 'ltr' ],
"\n" . $this->getText() . "\n"
) . "\n";
}
/**
* @param Title $target
* @return CssContent

View file

@ -21,6 +21,7 @@
* @ingroup Content
*/
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\Transform\PreSaveTransformParams;
use MediaWiki\MediaWikiServices;
use Wikimedia\Minify\CSSMin;
@ -99,4 +100,42 @@ class CssContentHandler extends CodeContentHandler {
$class = $this->getContentClass();
return new $class( $pst );
}
/**
* @inheritDoc
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
global $wgTextModelsToParse;
'@phan-var CssContent $content';
if ( in_array( $content->getModel(), $wgTextModelsToParse ) ) {
// parse just to get links etc into the database, HTML is replaced below.
$output = MediaWikiServices::getInstance()->getParser()
->parse(
$content->getText(),
$cpoParams->getPage(),
$cpoParams->getParserOptions(),
true,
true,
$cpoParams->getRevId()
);
}
if ( $cpoParams->getGenerateHtml() ) {
// Return CSS wrapped in a <pre> tag.
$html = Html::element(
'pre',
[ 'class' => 'mw-code mw-css', 'dir' => 'ltr' ],
"\n" . $content->getText() . "\n"
) . "\n";
} else {
$html = '';
}
$output->clearWrapperDivClass();
$output->setText( $html );
}
}

View file

@ -129,22 +129,6 @@ class FallbackContent extends AbstractContent {
return false;
}
/**
* Fills the ParserOutput with an error message.
* @param Title $title
* @param int $revId
* @param ParserOptions $options
* @param bool $generateHtml
* @param ParserOutput &$output
*/
protected function fillParserOutput( Title $title, $revId,
ParserOptions $options, $generateHtml, ParserOutput &$output
) {
$msg = wfMessage( 'unsupported-content-model', [ $this->getModel() ] );
$html = Html::rawElement( 'div', [ 'class' => 'error' ], $msg->inContentLanguage()->parse() );
$output->setText( $html );
}
/**
* @param string $toModel
* @param string $lossy

View file

@ -23,6 +23,8 @@
* @ingroup Content
*/
use MediaWiki\Content\Renderer\ContentParseParams;
/**
* Content handler implementation for unknown content.
*
@ -104,6 +106,25 @@ class FallbackContentHandler extends ContentHandler {
return false;
}
/**
* Fills the ParserOutput with an error message.
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
'@phan-var FallbackContent $content';
$msg = wfMessage( 'unsupported-content-model', [ $content->getModel() ] );
$html = Html::rawElement( 'div', [ 'class' => 'error' ], $msg->inContentLanguage()->parse() );
$output->setText( $html );
}
/**
* @param IContextSource $context
*

View file

@ -47,16 +47,6 @@ class JavaScriptContent extends TextContent {
parent::__construct( $text, $modelId );
}
/**
* @return string JavaScript wrapped in a <pre> tag.
*/
protected function getHtml() {
return Html::element( 'pre',
[ 'class' => 'mw-code mw-js', 'dir' => 'ltr' ],
"\n" . $this->getText() . "\n"
) . "\n";
}
/**
* If this page is a redirect, return the content
* if it should redirect to $target instead

View file

@ -18,6 +18,7 @@
* @file
*/
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\Transform\PreSaveTransformParams;
use MediaWiki\MediaWikiServices;
@ -101,4 +102,55 @@ class JavaScriptContentHandler extends CodeContentHandler {
$contentClass = $this->getContentClass();
return new $contentClass( $pst );
}
/**
* Fills the provided ParserOutput object with information derived from the content.
* Unless $cpo->getGenerateHtml was false, this includes an HTML representation of the content.
*
* For content models listed in $wgTextModelsToParse, this method will call the MediaWiki
* wikitext parser on the text to extract any (wikitext) links, magic words, etc.
*
* Subclasses may override this to provide custom content processing..
*
* @stable to override
*
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
global $wgTextModelsToParse;
'@phan-var TextContent $content';
if ( in_array( $content->getModel(), $wgTextModelsToParse ) ) {
// parse just to get links etc into the database, HTML is replaced below.
$output = MediaWikiServices::getInstance()->getParser()
->parse(
$content->getText(),
$cpoParams->getPage(),
$cpoParams->getParserOptions(),
true,
true,
$cpoParams->getRevId()
);
}
if ( $cpoParams->getGenerateHtml() ) {
// Return JavaScript wrapped in a <pre> tag.
$html = Html::element(
'pre',
[ 'class' => 'mw-code mw-js', 'dir' => 'ltr' ],
"\n" . $content->getText() . "\n"
) . "\n";
} else {
$html = '';
}
$output->clearWrapperDivClass();
$output->setText( $html );
}
}

View file

@ -61,28 +61,6 @@ class JsonContent extends TextContent {
return FormatJson::encode( $this->getData()->getValue(), true, FormatJson::UTF8_OK );
}
/**
* Set the HTML and add the appropriate styles.
*
* @param Title $title
* @param int $revId
* @param ParserOptions $options
* @param bool $generateHtml
* @param ParserOutput &$output
*/
protected function fillParserOutput( Title $title, $revId,
ParserOptions $options, $generateHtml, ParserOutput &$output
) {
// FIXME: WikiPage::doEditContent generates parser output before validation.
// As such, native data may be invalid (though output is discarded later in that case).
if ( $generateHtml && $this->isValid() ) {
$output->setText( $this->rootValueTable( $this->getData()->getValue() ) );
$output->addModuleStyles( 'mediawiki.content.json' );
} else {
$output->setText( '' );
}
}
/**
* Construct HTML table representation of any JSON value.
*
@ -91,7 +69,7 @@ class JsonContent extends TextContent {
* @param mixed $val
* @return string HTML.
*/
protected function rootValueTable( $val ) {
public function rootValueTable( $val ) {
if ( is_object( $val ) ) {
return $this->objectTable( $val );
}

View file

@ -18,6 +18,7 @@
* @file
*/
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\Transform\PreSaveTransformParams;
/**
@ -74,4 +75,28 @@ class JsonContentHandler extends CodeContentHandler {
$contentClass = $this->getContentClass();
return new $contentClass( JsonContent::normalizeLineEndings( $content->beautifyJSON() ) );
}
/**
* Set the HTML and add the appropriate styles.
*
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
'@phan-var JsonContent $content';
// FIXME: WikiPage::doEditContent generates parser output before validation.
// As such, native data may be invalid (though output is discarded later in that case).
if ( $cpoParams->getGenerateHtml() && $content->isValid() ) {
$output->setText( $content->rootValueTable( $content->getData()->getValue() ) );
$output->addModuleStyles( 'mediawiki.content.json' );
} else {
$output->setText( '' );
}
}
}

View file

@ -18,6 +18,7 @@
* http://www.gnu.org/copyleft/gpl.html
*
* @since 1.21
* @deprecated since 1.38.
*
* @file
* @ingroup Content
@ -28,7 +29,7 @@
/**
* Wrapper allowing us to handle a system message as a Content object.
* Note that this is generally *not* used to represent content from the
* MediaWiki namespace, and that there is no MessageContentHandler.
* MediaWiki namespace.
* MessageContent is just intended as glue for wrapping a message programmatically.
*
* @ingroup Content
@ -45,6 +46,7 @@ class MessageContent extends AbstractContent {
* @param string[]|null $params An optional array of message parameters.
*/
public function __construct( $msg, $params = null ) {
wfDeprecated( __CLASS__, '1.38' );
# XXX: messages may be wikitext, html or plain text! and maybe even something else entirely.
parent::__construct( CONTENT_MODEL_WIKITEXT );
@ -59,15 +61,6 @@ class MessageContent extends AbstractContent {
}
}
/**
* Fully parse the text from wikitext to HTML.
*
* @return string Parsed HTML.
*/
public function getHtml() {
return $this->mMessage->parse();
}
/**
* Returns the message text. {{-transformation is done.
*
@ -159,30 +152,4 @@ class MessageContent extends AbstractContent {
public function isCountable( $hasLinks = null ) {
return false;
}
/**
* @param Title $title Unused.
* @param int|null $revId Unused.
* @param ParserOptions|null $options Unused.
* @param bool $generateHtml Whether to generate HTML (default: true).
*
* @return ParserOutput
*
* @see Content::getParserOutput
*/
public function getParserOutput( Title $title, $revId = null,
ParserOptions $options = null, $generateHtml = true ) {
if ( $generateHtml ) {
$html = $this->getHtml();
} else {
$html = '';
}
$po = new ParserOutput( $html );
// Message objects are in the user language.
$po->recordOption( 'userlang' );
return $po;
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace MediaWiki\Content\Renderer;
use MediaWiki\Page\PageReference;
use ParserOptions;
/**
* @internal
* An object to hold parser params.
*/
class ContentParseParams {
/** @var PageReference */
private $page;
/** @var int|null */
private $revId;
/** @var ParserOptions */
private $parserOptions;
/** @var bool */
private $generateHtml;
public function __construct(
PageReference $page,
int $revId = null,
ParserOptions $parserOptions = null,
bool $generateHtml = true
) {
$this->page = $page;
$this->parserOptions = $parserOptions ?? ParserOptions::newCanonical( 'canonical' );
$this->revId = $revId;
$this->generateHtml = $generateHtml;
}
/**
*
* @return PageReference
*/
public function getPage(): PageReference {
return $this->page;
}
/**
*
* @return int|null
*/
public function getRevId(): ?int {
return $this->revId;
}
/**
*
* @return ParserOptions
*/
public function getParserOptions(): ParserOptions {
return $this->parserOptions;
}
/**
*
* @return bool
*/
public function getGenerateHtml(): bool {
return $this->generateHtml;
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace MediaWiki\Content\Renderer;
use Content;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Page\PageReference;
use ParserOptions;
use ParserOutput;
/**
* A service to render content.
*
* @since 1.38
*/
class ContentRenderer {
/** @var IContentHandlerFactory */
private $contentHandlerFactory;
/**
* @param IContentHandlerFactory $contentHandlerFactory
*/
public function __construct( IContentHandlerFactory $contentHandlerFactory ) {
$this->contentHandlerFactory = $contentHandlerFactory;
}
/**
* Returns a ParserOutput object containing information derived from this content.
*
* @param Content $content
* @param PageReference $page
* @param int|null $revId
* @param ParserOptions|null $parserOptions
* @param bool $generateHtml
*
* @return ParserOutput
*/
public function getParserOutput(
Content $content,
PageReference $page,
?int $revId = null,
?ParserOptions $parserOptions = null,
bool $generateHtml = true
): ParserOutput {
$contentHandler = $this->contentHandlerFactory->getContentHandler( $content->getModel() );
$cpoParams = new ContentParseParams( $page, $revId, $parserOptions, $generateHtml );
return $contentHandler->getParserOutput( $content, $cpoParams );
}
}

View file

@ -239,62 +239,6 @@ class TextContent extends AbstractContent {
return $diff;
}
/**
* Fills the provided ParserOutput object with information derived from the content.
* Unless $generateHtml was false, this includes an HTML representation of the content
* provided by getHtml().
*
* For content models listed in $wgTextModelsToParse, this method will call the MediaWiki
* wikitext parser on the text to extract any (wikitext) links, magic words, etc.
*
* Subclasses may override this to provide custom content processing.
* For custom HTML generation alone, it is sufficient to override getHtml().
*
* @stable to override
*
* @param Title $title Context title for parsing
* @param int $revId Revision ID (for {{REVISIONID}})
* @param ParserOptions $options
* @param bool $generateHtml Whether or not to generate HTML
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput( Title $title, $revId,
ParserOptions $options, $generateHtml, ParserOutput &$output
) {
global $wgTextModelsToParse;
if ( in_array( $this->getModel(), $wgTextModelsToParse ) ) {
// parse just to get links etc into the database, HTML is replaced below.
$output = MediaWikiServices::getInstance()->getParser()
->parse( $this->getText(), $title, $options, true, true, $revId );
}
if ( $generateHtml ) {
$html = $this->getHtml();
} else {
$html = '';
}
$output->clearWrapperDivClass();
$output->setText( $html );
}
/**
* Generates an HTML version of the content, for display. Used by
* fillParserOutput() to provide HTML for the ParserOutput object.
*
* Subclasses may override this to provide a custom HTML rendering.
* If further information is to be derived from the content (such as
* categories), the fillParserOutput() method can be overridden instead.
*
* @stable to override
*
* @return string An HTML representation of the content
*/
protected function getHtml() {
return htmlspecialchars( $this->getText() );
}
/**
* This implementation provides lossless conversion between content models based
* on TextContent.

View file

@ -23,7 +23,9 @@
* @ingroup Content
*/
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\Transform\PreSaveTransformParams;
use MediaWiki\MediaWikiServices;
/**
* Base content handler implementation for flat text contents.
@ -187,4 +189,60 @@ class TextContentHandler extends ContentHandler {
$contentClass = $this->getContentClass();
return ( $text === $pst ) ? $content : new $contentClass( $pst, $content->getModel() );
}
/**
* Fills the provided ParserOutput object with information derived from the content.
* Unless $generateHtml was false, this includes an HTML representation of the content
* provided by getHtml().
*
* For content models listed in $wgTextModelsToParse, this method will call the MediaWiki
* wikitext parser on the text to extract any (wikitext) links, magic words, etc.
*
* Subclasses may override this to provide custom content processing.
* For custom HTML generation alone, it is sufficient to override getHtml().
*
* @stable to override
*
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
global $wgTextModelsToParse;
'@phan-var TextContent $content';
if ( in_array( $content->getModel(), $wgTextModelsToParse ) ) {
// parse just to get links etc into the database, HTML is replaced below.
$output = MediaWikiServices::getInstance()->getParser()
->parse(
$content->getText(),
$cpoParams->getPage(),
$cpoParams->getParserOptions(),
true,
true,
$cpoParams->getRevId()
);
}
if ( $cpoParams->getGenerateHtml() ) {
// Temporary changes as getHtml() is deprecated, we are working on removing usage of it.
if ( method_exists( $content, 'getHtml' ) ) {
$method = new ReflectionMethod( 'TextContent', 'getHtml' );
$method->setAccessible( true );
$html = $method->invoke( $content );
} else {
// Return an HTML representation of the content
$html = htmlspecialchars( $content->getText() );
}
} else {
$html = '';
}
$output->clearWrapperDivClass();
$output->setText( $html );
}
}

View file

@ -144,7 +144,7 @@ class WikitextContent extends TextContent {
*
* @return array List of two elements: Title|null and string.
*/
protected function getRedirectTargetAndText() {
public function getRedirectTargetAndText() {
global $wgMaxRedirects;
if ( $this->redirectTargetAndText !== null ) {
@ -279,56 +279,6 @@ class WikitextContent extends TextContent {
return $truncatedtext;
}
/**
* Returns a ParserOutput object resulting from parsing the content's text
* using the global Parser service.
*
* @param Title $title
* @param int|null $revId ID of the revision being rendered.
* See Parser::parse() for the ramifications. (default: null)
* @param ParserOptions $options (default: null)
* @param bool $generateHtml (default: true)
* @param ParserOutput &$output ParserOutput representing the HTML form of the text,
* may be manipulated or replaced.
*/
protected function fillParserOutput( Title $title, $revId,
ParserOptions $options, $generateHtml, ParserOutput &$output
) {
list( $redir, $text ) = $this->getRedirectTargetAndText();
$output = MediaWikiServices::getInstance()->getParser()
->parse( $text, $title, $options, true, true, $revId );
// Add redirect indicator at the top
if ( $redir ) {
// Make sure to include the redirect link in pagelinks
$output->addLink( $redir );
if ( $generateHtml ) {
$chain = $this->getRedirectChain();
$output->setText(
Article::getRedirectHeaderHtml( $title->getPageLanguage(), $chain, false ) .
$output->getRawText()
);
$output->addModuleStyles( 'mediawiki.action.view.redirectPage' );
}
}
// Pass along user-signature flag
if ( in_array( 'user-signature', $this->preSaveTransformFlags ) ) {
$output->setFlag( 'user-signature' );
}
}
/**
* @throws MWException
*/
protected function getHtml() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException(
"getHtml() not implemented for wikitext. "
. "Use getParserOutput()->getText()."
);
}
/**
* This implementation calls $word->match() on the this TextContent object's text.
*
@ -350,4 +300,13 @@ class WikitextContent extends TextContent {
public function setPreSaveTransformFlags( array $flags ) {
$this->preSaveTransformFlags = $flags;
}
/**
* Records flags set by preSaveTransform
* @internal for use by WikitextContentHandler
* @return string[]
*/
public function getPreSaveTransformFlags() {
return $this->preSaveTransformFlags;
}
}

View file

@ -23,6 +23,7 @@
* @ingroup Content
*/
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\Transform\PreloadTransformParams;
use MediaWiki\Content\Transform\PreSaveTransformParams;
use MediaWiki\Languages\LanguageNameUtils;
@ -266,4 +267,48 @@ class WikitextContentHandler extends TextContentHandler {
$contentClass = $this->getContentClass();
return new $contentClass( $plt );
}
/**
* Returns a ParserOutput object resulting from parsing the content's text
* using the global Parser service.
*
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
'@phan-var WikitextContent $content';
$services = MediaWikiServices::getInstance();
$title = $services->getTitleFactory()->castFromPageReference( $cpoParams->getPage() );
$parserOptions = $cpoParams->getParserOptions();
$revId = $cpoParams->getRevId();
list( $redir, $text ) = $content->getRedirectTargetAndText();
$output = $services->getParser()
->parse( $text, $title, $parserOptions, true, true, $revId );
// Add redirect indicator at the top
if ( $redir ) {
// Make sure to include the redirect link in pagelinks
$output->addLink( $redir );
if ( $cpoParams->getGenerateHtml() ) {
$chain = $content->getRedirectChain();
$output->setText(
Article::getRedirectHeaderHtml( $title->getPageLanguage(), $chain, false ) .
$output->getRawText()
);
$output->addModuleStyles( 'mediawiki.action.view.redirectPage' );
}
}
// Pass along user-signature flag
if ( in_array( 'user-signature', $content->getPreSaveTransformFlags() ) ) {
$output->setFlag( 'user-signature' );
}
}
}

View file

@ -870,10 +870,11 @@ class Message implements MessageSpecifier, Serializable {
/**
* Returns the message as a Content object.
*
* @deprecated since 1.38, MessageContent class is hard-deprecated.
* @return Content
*/
public function content() {
wfDeprecated( __METHOD__, '1.38' );
if ( !$this->content ) {
$this->content = new MessageContent( $this );
}

View file

@ -3697,8 +3697,8 @@ class Parser {
$text = false;
break;
}
$content = $message->content();
$text = $message->plain();
break;
} else {
break;
}

View file

@ -296,7 +296,6 @@ trait MediaWikiTestCaseTrait {
$msg->method( 'useDatabase' )->willReturn( $msg );
$msg->method( 'setContext' )->willReturn( $msg );
$msg->method( 'exists' )->willReturn( true );
$msg->method( 'content' )->willReturn( new MessageContent( $msg ) );
return $msg;
}
}

View file

@ -6,15 +6,9 @@
*/
class MessageContentTest extends MediaWikiLangTestCase {
public function testGetHtml() {
$msg = new Message( 'about' );
$cnt = new MessageContent( $msg );
$this->assertSame( $msg->parse(), $cnt->getHtml() );
}
public function testGetWikitext() {
$msg = new Message( 'about' );
$this->hideDeprecated( 'MessageContent' );
$cnt = new MessageContent( $msg );
$this->assertSame( $msg->text(), $cnt->getWikitext() );
@ -22,28 +16,22 @@ class MessageContentTest extends MediaWikiLangTestCase {
public function testGetMessage() {
$msg = new Message( 'about' );
$this->hideDeprecated( 'MessageContent' );
$cnt = new MessageContent( $msg );
$this->assertEquals( $msg, $cnt->getMessage() );
}
public function testGetParserOutput() {
$msg = new Message( 'about' );
$cnt = new MessageContent( $msg );
$title = Title::makeTitle( NS_MEDIAWIKI, 'about' );
$this->assertSame( $msg->parse(), $cnt->getParserOutput( $title )->getText() );
}
public function testSerialize() {
$msg = new Message( 'about' );
$this->hideDeprecated( 'MessageContent' );
$cnt = new MessageContent( $msg );
$this->assertSame( $msg->plain(), $cnt->serialize() );
}
public function testEquals() {
$this->hideDeprecated( 'MessageContent' );
$msg1 = new Message( 'about' );
$cnt1 = new MessageContent( $msg1 );

View file

@ -18,12 +18,12 @@ class ParserObserverIntegrationTest extends MediaWikiIntegrationTestCase {
$logger = new TestLogger( true );
$observer = new ParserObserver( $logger );
$this->setService( '_ParserObserver', $observer );
$contentRenderer = $this->getServiceContainer()->getContentRenderer();
// Create a test page. Parse it twice if a duplicate is desired, or once otherwise.
$page = $this->getExistingTestPage();
$page->getContent()->getParserOutput( $page->getTitle() );
$contentRenderer->getParserOutput( $page->getContent(), $page->getTitle() );
if ( $duplicate ) {
$page->getContent()->getParserOutput( $page->getTitle() );
$contentRenderer->getParserOutput( $page->getContent(), $page->getTitle() );
}
$this->assertCount( $count, $logger->getBuffer() );

View file

@ -91,33 +91,4 @@ class DummyContentForTesting extends AbstractContent {
public function isCountable( $hasLinks = null ) {
return false;
}
/**
* @param Title $title
* @param int|null $revId Unused.
* @param null|ParserOptions $options
* @param bool $generateHtml Whether to generate Html (default: true). If false, the result
* of calling getText() on the ParserOutput object returned by this method is undefined.
*
* @return ParserOutput
*/
public function getParserOutput( Title $title, $revId = null,
ParserOptions $options = null, $generateHtml = true
) {
return new ParserOutput( $this->data );
}
/**
* @see AbstractContent::fillParserOutput()
*
* @param Title $title Context title for parsing
* @param int|null $revId Revision ID (for {{REVISIONID}})
* @param ParserOptions $options
* @param bool $generateHtml Whether or not to generate HTML
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput( Title $title, $revId,
ParserOptions $options, $generateHtml, ParserOutput &$output ) {
$output = new ParserOutput( $this->data );
}
}

View file

@ -1,5 +1,7 @@
<?php
use MediaWiki\Content\Renderer\ContentParseParams;
class DummyContentHandlerForTesting extends ContentHandler {
public function __construct( $dataModel, $formats = [ DummyContentForTesting::MODEL_ID ] ) {
@ -47,4 +49,21 @@ class DummyContentHandlerForTesting extends ContentHandler {
public function generateHTMLOnEdit(): bool {
return false;
}
/**
* @see ContentHandler::fillParserOutput()
*
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
'@phan-var DummyContentForTesting $content';
$output = new ParserOutput( $content->getNativeData() );
}
}

View file

@ -89,33 +89,4 @@ class DummyNonTextContent extends AbstractContent {
public function isCountable( $hasLinks = null ) {
return false;
}
/**
* @param Title $title
* @param int|null $revId Unused.
* @param null|ParserOptions $options
* @param bool $generateHtml Whether to generate Html (default: true). If false, the result
* of calling getText() on the ParserOutput object returned by this method is undefined.
*
* @return ParserOutput
*/
public function getParserOutput( Title $title, $revId = null,
ParserOptions $options = null, $generateHtml = true
) {
return new ParserOutput( $this->serialize() );
}
/**
* @see AbstractContent::fillParserOutput()
*
* @param Title $title Context title for parsing
* @param int|null $revId Revision ID (for {{REVISIONID}})
* @param ParserOptions $options
* @param bool $generateHtml Whether or not to generate HTML
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput( Title $title, $revId,
ParserOptions $options, $generateHtml, ParserOutput &$output ) {
$output = new ParserOutput( $this->serialize() );
}
}

View file

@ -1,5 +1,7 @@
<?php
use MediaWiki\Content\Renderer\ContentParseParams;
class DummyNonTextContentHandler extends DummyContentHandlerForTesting {
public function __construct( $dataModel ) {
@ -44,4 +46,20 @@ class DummyNonTextContentHandler extends DummyContentHandlerForTesting {
return true;
}
/**
* @see ContentHandler::fillParserOutput()
*
* @since 1.38
* @param Content $content
* @param ContentParseParams $cpoParams
* @param ParserOutput &$output The output object to fill (reference).
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$output
) {
'@phan-var DummyNonTextContent $content';
$output = new ParserOutput( $content->serialize() );
}
}

View file

@ -18,6 +18,7 @@
* @file
*/
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\Transform\PreloadTransformParamsValue;
use MediaWiki\Content\Transform\PreSaveTransformParamsValue;
use MediaWiki\MediaWikiServices;
@ -68,13 +69,20 @@ class ContentHandlerSanityTest extends MediaWikiIntegrationTestCase {
* @dataProvider provideModels
*/
public function testGetParserOutput( string $model ) {
$this->filterDeprecated( '/Use of AbstractContent::getParserOutput was deprecated/' );
$handler = $this->getServiceContainer()->getContentHandlerFactory()
->getContentHandler( $model );
$title = $this->getExistingTestPage()->getTitle();
$content = $handler->makeEmptyContent();
$this->assertInstanceOf( ParserOutput::class, $content->getParserOutput( $title ) );
$gpoParams = new ContentParseParams( $title );
$this->assertInstanceOf(
ParserOutput::class,
$handler->getParserOutput( $content, $gpoParams )
);
}
/**