diff --git a/RELEASE-NOTES-1.38 b/RELEASE-NOTES-1.38 index dbf40fcb749..c3d997123b2 100644 --- a/RELEASE-NOTES-1.38 +++ b/RELEASE-NOTES-1.38 @@ -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 === diff --git a/includes/MediaWikiServices.php b/includes/MediaWikiServices.php index d45c51566d2..90f329be419 100644 --- a/includes/MediaWikiServices.php +++ b/includes/MediaWikiServices.php @@ -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 diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php index 140e88f7f54..92de100de36 100644 --- a/includes/ServiceWiring.php +++ b/includes/ServiceWiring.php @@ -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() ); }, diff --git a/includes/content/AbstractContent.php b/includes/content/AbstractContent.php index 23212b1d3ce..4f1e436a31d 100644 --- a/includes/content/AbstractContent.php +++ b/includes/content/AbstractContent.php @@ -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 ); } } diff --git a/includes/content/Content.php b/includes/content/Content.php index 37e85b33cef..9dfb01746f0 100644 --- a/includes/content/Content.php +++ b/includes/content/Content.php @@ -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) diff --git a/includes/content/ContentHandler.php b/includes/content/ContentHandler.php index dcd5f950063..e5ebc3cc3f9 100644 --- a/includes/content/ContentHandler.php +++ b/includes/content/ContentHandler.php @@ -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() + ); + } } diff --git a/includes/content/CssContent.php b/includes/content/CssContent.php index 1ad294a088f..d62edf87ca7 100644 --- a/includes/content/CssContent.php +++ b/includes/content/CssContent.php @@ -47,16 +47,6 @@ class CssContent extends TextContent { parent::__construct( $text, $modelId ); } - /** - * @return string CSS wrapped in a
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
diff --git a/includes/content/CssContentHandler.php b/includes/content/CssContentHandler.php
index 06bc41c609f..49f3bf4cced 100644
--- a/includes/content/CssContentHandler.php
+++ b/includes/content/CssContentHandler.php
@@ -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 tag.
+ $html = Html::element(
+ 'pre',
+ [ 'class' => 'mw-code mw-css', 'dir' => 'ltr' ],
+ "\n" . $content->getText() . "\n"
+ ) . "\n";
+ } else {
+ $html = '';
+ }
+
+ $output->clearWrapperDivClass();
+ $output->setText( $html );
+ }
}
diff --git a/includes/content/FallbackContent.php b/includes/content/FallbackContent.php
index 7cf3694aaff..78ddbc6088e 100644
--- a/includes/content/FallbackContent.php
+++ b/includes/content/FallbackContent.php
@@ -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
diff --git a/includes/content/FallbackContentHandler.php b/includes/content/FallbackContentHandler.php
index 4ba9e5113bf..11d3cd75c36 100644
--- a/includes/content/FallbackContentHandler.php
+++ b/includes/content/FallbackContentHandler.php
@@ -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
*
diff --git a/includes/content/JavaScriptContent.php b/includes/content/JavaScriptContent.php
index e053b82b2f7..893764970db 100644
--- a/includes/content/JavaScriptContent.php
+++ b/includes/content/JavaScriptContent.php
@@ -47,16 +47,6 @@ class JavaScriptContent extends TextContent {
parent::__construct( $text, $modelId );
}
- /**
- * @return string JavaScript wrapped in a 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
diff --git a/includes/content/JavaScriptContentHandler.php b/includes/content/JavaScriptContentHandler.php
index c236d5b5450..3482439f55d 100644
--- a/includes/content/JavaScriptContentHandler.php
+++ b/includes/content/JavaScriptContentHandler.php
@@ -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 tag.
+ $html = Html::element(
+ 'pre',
+ [ 'class' => 'mw-code mw-js', 'dir' => 'ltr' ],
+ "\n" . $content->getText() . "\n"
+ ) . "\n";
+ } else {
+ $html = '';
+ }
+
+ $output->clearWrapperDivClass();
+ $output->setText( $html );
+ }
}
diff --git a/includes/content/JsonContent.php b/includes/content/JsonContent.php
index 1e1a6e88174..115abd0b23c 100644
--- a/includes/content/JsonContent.php
+++ b/includes/content/JsonContent.php
@@ -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 );
}
diff --git a/includes/content/JsonContentHandler.php b/includes/content/JsonContentHandler.php
index 962c838a313..566888034b6 100644
--- a/includes/content/JsonContentHandler.php
+++ b/includes/content/JsonContentHandler.php
@@ -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( '' );
+ }
+ }
}
diff --git a/includes/content/MessageContent.php b/includes/content/MessageContent.php
index 24c7987f22d..b0baaa919bb 100644
--- a/includes/content/MessageContent.php
+++ b/includes/content/MessageContent.php
@@ -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;
- }
-
}
diff --git a/includes/content/Renderer/ContentParseParams.php b/includes/content/Renderer/ContentParseParams.php
new file mode 100644
index 00000000000..718206e4ad3
--- /dev/null
+++ b/includes/content/Renderer/ContentParseParams.php
@@ -0,0 +1,67 @@
+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;
+ }
+}
diff --git a/includes/content/Renderer/ContentRenderer.php b/includes/content/Renderer/ContentRenderer.php
new file mode 100644
index 00000000000..1f3c50251f3
--- /dev/null
+++ b/includes/content/Renderer/ContentRenderer.php
@@ -0,0 +1,49 @@
+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 );
+ }
+}
diff --git a/includes/content/TextContent.php b/includes/content/TextContent.php
index ee532c4d694..9c3f61aff8f 100644
--- a/includes/content/TextContent.php
+++ b/includes/content/TextContent.php
@@ -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.
diff --git a/includes/content/TextContentHandler.php b/includes/content/TextContentHandler.php
index 9c6c7f8a187..b9f99e6dd71 100644
--- a/includes/content/TextContentHandler.php
+++ b/includes/content/TextContentHandler.php
@@ -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 );
+ }
}
diff --git a/includes/content/WikitextContent.php b/includes/content/WikitextContent.php
index e7c2b1ecb04..99d54573b2c 100644
--- a/includes/content/WikitextContent.php
+++ b/includes/content/WikitextContent.php
@@ -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;
+ }
}
diff --git a/includes/content/WikitextContentHandler.php b/includes/content/WikitextContentHandler.php
index 66d94a80b78..8455487752f 100644
--- a/includes/content/WikitextContentHandler.php
+++ b/includes/content/WikitextContentHandler.php
@@ -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' );
+ }
+ }
}
diff --git a/includes/language/Message.php b/includes/language/Message.php
index a42edc1b410..80e757e6d1b 100644
--- a/includes/language/Message.php
+++ b/includes/language/Message.php
@@ -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 );
}
diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php
index cc15da290cb..6c9968130b4 100644
--- a/includes/parser/Parser.php
+++ b/includes/parser/Parser.php
@@ -3697,8 +3697,8 @@ class Parser {
$text = false;
break;
}
- $content = $message->content();
$text = $message->plain();
+ break;
} else {
break;
}
diff --git a/tests/phpunit/MediaWikiTestCaseTrait.php b/tests/phpunit/MediaWikiTestCaseTrait.php
index 337f38c03f5..4b6bc4e245b 100644
--- a/tests/phpunit/MediaWikiTestCaseTrait.php
+++ b/tests/phpunit/MediaWikiTestCaseTrait.php
@@ -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;
}
}
diff --git a/tests/phpunit/includes/content/MessageContentTest.php b/tests/phpunit/includes/content/MessageContentTest.php
index 5df7cca4448..ab915f602ad 100644
--- a/tests/phpunit/includes/content/MessageContentTest.php
+++ b/tests/phpunit/includes/content/MessageContentTest.php
@@ -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 );
diff --git a/tests/phpunit/integration/includes/parser/ParserObserverIntegrationTest.php b/tests/phpunit/integration/includes/parser/ParserObserverIntegrationTest.php
index ee4dd3ec2e5..72dfcd4b925 100644
--- a/tests/phpunit/integration/includes/parser/ParserObserverIntegrationTest.php
+++ b/tests/phpunit/integration/includes/parser/ParserObserverIntegrationTest.php
@@ -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() );
diff --git a/tests/phpunit/mocks/content/DummyContentForTesting.php b/tests/phpunit/mocks/content/DummyContentForTesting.php
index 27e68726e90..8f24c8e4fb6 100644
--- a/tests/phpunit/mocks/content/DummyContentForTesting.php
+++ b/tests/phpunit/mocks/content/DummyContentForTesting.php
@@ -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 );
- }
}
diff --git a/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php b/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php
index 11183627adc..a7aa2c01bb8 100644
--- a/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php
+++ b/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php
@@ -1,5 +1,7 @@
getNativeData() );
+ }
}
diff --git a/tests/phpunit/mocks/content/DummyNonTextContent.php b/tests/phpunit/mocks/content/DummyNonTextContent.php
index bdfa8d072b9..5e44796bedc 100644
--- a/tests/phpunit/mocks/content/DummyNonTextContent.php
+++ b/tests/phpunit/mocks/content/DummyNonTextContent.php
@@ -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() );
- }
}
diff --git a/tests/phpunit/mocks/content/DummyNonTextContentHandler.php b/tests/phpunit/mocks/content/DummyNonTextContentHandler.php
index 3294953643d..4038803cb84 100644
--- a/tests/phpunit/mocks/content/DummyNonTextContentHandler.php
+++ b/tests/phpunit/mocks/content/DummyNonTextContentHandler.php
@@ -1,5 +1,7 @@
serialize() );
+ }
}
diff --git a/tests/phpunit/structure/ContentHandlerSanityTest.php b/tests/phpunit/structure/ContentHandlerSanityTest.php
index 0423e28fd47..73d0b0a3c59 100644
--- a/tests/phpunit/structure/ContentHandlerSanityTest.php
+++ b/tests/phpunit/structure/ContentHandlerSanityTest.php
@@ -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 )
+ );
}
/**