mediawiki-extensions-Scribunto/includes/ScribuntoContentHandler.php
Thiemo Kreuz 8654dfc466 Use new ContentHandler::supportsPreloadContent() feature
Bug: T300644
Depends-On: Ia5c491cd856ca395fb431bcefd63026084b01a99
Change-Id: I301cbb6b2d547e622ee695fd20a910f8553788c5
2022-07-06 22:39:44 +00:00

231 lines
6.5 KiB
PHP

<?php
namespace MediaWiki\Extension\Scribunto;
use CodeContentHandler;
use Content;
use ExtensionRegistry;
use Html;
use MediaWiki\Content\Renderer\ContentParseParams;
use MediaWiki\Content\ValidationParams;
use MediaWiki\MediaWikiServices;
use MediaWiki\Page\PageIdentity;
use MediaWiki\SyntaxHighlight\SyntaxHighlight;
use ParserOutput;
use Status;
use TextContent;
use Title;
/**
* Scribunto Content Handler
*
* @file
* @ingroup Extensions
* @ingroup Scribunto
*
* @author Brad Jorsch <bjorsch@wikimedia.org>
*/
class ScribuntoContentHandler extends CodeContentHandler {
/**
* @param string $modelId
* @param string[] $formats
*/
public function __construct(
$modelId = CONTENT_MODEL_SCRIBUNTO, $formats = [ CONTENT_FORMAT_TEXT ]
) {
parent::__construct( $modelId, $formats );
}
/**
* @return string Class name
*/
protected function getContentClass() {
return ScribuntoContent::class;
}
/**
* @param string $format
* @return bool
*/
public function isSupportedFormat( $format ) {
// An error in an earlier version of Scribunto means we might see this.
if ( $format === 'CONTENT_FORMAT_TEXT' ) {
$format = CONTENT_FORMAT_TEXT;
}
return parent::isSupportedFormat( $format );
}
/**
* Only allow this content handler to be used in the Module namespace
* @param Title $title
* @return bool
*/
public function canBeUsedOn( Title $title ) {
if ( $title->getNamespace() !== NS_MODULE ) {
return false;
}
return parent::canBeUsedOn( $title );
}
/** @inheritDoc */
public function supportsPreloadContent(): bool {
return true;
}
/**
* @inheritDoc
*/
public function validateSave(
Content $content,
ValidationParams $validationParams
) {
'@phan-var ScribuntoContent $content';
return $this->validate( $content, $validationParams->getPageIdentity() );
}
/**
* Checks whether the script is valid
*
* @param TextContent $content
* @param PageIdentity $page
* @return Status
*/
public function validate( TextContent $content, PageIdentity $page ) {
if ( !( $page instanceof Title ) ) {
$titleFactory = MediaWikiServices::getInstance()->getTitleFactory();
$page = $titleFactory->castFromPageIdentity( $page );
}
$engine = Scribunto::newDefaultEngine();
// @phan-suppress-next-line PhanTypeMismatchArgument
$engine->setTitle( $page );
return $engine->validate( $content->getText(), $page->getPrefixedDBkey() );
}
/**
* @inheritDoc
*/
protected function fillParserOutput(
Content $content,
ContentParseParams $cpoParams,
ParserOutput &$parserOutput
) {
'@phan-var ScribuntoContent $content';
$page = $cpoParams->getPage();
$title = Title::castFromPageReference( $page );
$parserOptions = $cpoParams->getParserOptions();
$revId = $cpoParams->getRevId();
$generateHtml = $cpoParams->getGenerateHtml();
$parser = MediaWikiServices::getInstance()->getParserFactory()->getInstance();
$text = $content->getText();
// Get documentation, if any
$parserOutput = new ParserOutput();
// @phan-suppress-next-line PhanTypeMismatchArgument
$doc = Scribunto::getDocPage( $title );
if ( $doc ) {
$msg = wfMessage(
$doc->exists() ? 'scribunto-doc-page-show' : 'scribunto-doc-page-does-not-exist',
$doc->getPrefixedText()
)->inContentLanguage();
if ( !$msg->isDisabled() ) {
// We need the ParserOutput for categories and such, so we
// can't use $msg->parse().
$docViewLang = $doc->getPageViewLanguage();
$dir = $docViewLang->getDir();
// Code is forced to be ltr, but the documentation can be rtl.
// Correct direction class is needed for correct formatting.
// The possible classes are
// mw-content-ltr or mw-content-rtl
$dirClass = "mw-content-$dir";
$docWikitext = Html::rawElement(
'div',
[
'lang' => $docViewLang->getHtmlCode(),
'dir' => $dir,
'class' => $dirClass,
],
// Line breaks are needed so that wikitext would be
// appropriately isolated for correct parsing. See Bug 60664.
"\n" . $msg->plain() . "\n"
);
if ( $parserOptions->getTargetLanguage() === null ) {
$parserOptions->setTargetLanguage( $doc->getPageLanguage() );
}
$parserOutput = $parser->parse( $docWikitext, $page, $parserOptions, true, true, $revId );
}
// Mark the doc page as a transclusion, so we get purged when it
// changes.
$parserOutput->addTemplate( $doc, $doc->getArticleID(), $doc->getLatestRevID() );
}
// Validate the script, and include an error message and tracking
// category if it's invalid
// @phan-suppress-next-line PhanTypeMismatchArgument
$status = $this->validate( $content, $title );
if ( !$status->isOK() ) {
$parserOutput->setText( $parserOutput->getRawText() .
Html::rawElement( 'div', [ 'class' => 'errorbox' ],
$status->getHTML( 'scribunto-error-short', 'scribunto-error-long' )
)
);
$trackingCategories = MediaWikiServices::getInstance()->getTrackingCategories();
$trackingCategories->addTrackingCategory( $parserOutput, 'scribunto-module-with-errors-category', $page );
}
if ( !$generateHtml ) {
// We don't need the actual HTML
$parserOutput->setText( '' );
return;
}
$engine = Scribunto::newDefaultEngine();
// @phan-suppress-next-line PhanTypeMismatchArgument
$engine->setTitle( $title );
if ( $this->highlight( $text, $parserOutput, $engine ) ) {
return;
}
// No GeSHi, or GeSHi can't parse it, use plain <pre>
$parserOutput->setText( $parserOutput->getRawText() .
"<pre class='mw-code mw-script' dir='ltr'>\n" .
htmlspecialchars( $text ) .
"\n</pre>\n"
);
}
/**
* Adds syntax highlighting to the output (or do not touch it and return false).
* @param string $text
* @param ParserOutput $parserOutput
* @param ScribuntoEngineBase $engine
* @return bool Success status
*/
protected function highlight( $text, ParserOutput $parserOutput, ScribuntoEngineBase $engine ) {
global $wgScribuntoUseGeSHi;
$language = $engine->getGeSHiLanguage();
if (
$wgScribuntoUseGeSHi && $language && ExtensionRegistry::getInstance()->isLoaded( 'SyntaxHighlight' )
) {
$status = SyntaxHighlight::highlight( $text, $language, [ 'line' => true, 'linelinks' => 'L' ] );
if ( $status->isGood() ) {
// @todo replace addModuleStyles line with the appropriate call on
// SyntaxHighlight once one is created
$parserOutput->addModuleStyles( [ 'ext.pygments' ] );
$parserOutput->addModules( [ 'ext.pygments.linenumbers' ] );
$parserOutput->setText( $parserOutput->getRawText() . $status->getValue() );
return true;
}
}
return false;
}
}