2013-02-20 22:00:42 +00:00
|
|
|
<?php
|
2021-10-08 10:18:29 +00:00
|
|
|
|
|
|
|
use MediaWiki\Content\Renderer\ContentParseParams;
|
2021-10-27 12:13:37 +00:00
|
|
|
use MediaWiki\Content\ValidationParams;
|
2021-10-08 10:18:29 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2021-10-27 12:13:37 +00:00
|
|
|
use MediaWiki\Page\PageIdentity;
|
2021-10-08 10:18:29 +00:00
|
|
|
|
2013-02-20 22:00:42 +00:00
|
|
|
/**
|
|
|
|
* Scribunto Content Handler
|
|
|
|
*
|
|
|
|
* @file
|
|
|
|
* @ingroup Extensions
|
|
|
|
* @ingroup Scribunto
|
|
|
|
*
|
|
|
|
* @author Brad Jorsch <bjorsch@wikimedia.org>
|
|
|
|
*/
|
|
|
|
|
2014-09-15 08:43:15 +00:00
|
|
|
class ScribuntoContentHandler extends CodeContentHandler {
|
2013-02-20 22:00:42 +00:00
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @param string $modelId
|
|
|
|
* @param string[] $formats
|
|
|
|
*/
|
2016-05-17 14:52:05 +00:00
|
|
|
public function __construct(
|
2017-06-15 17:19:00 +00:00
|
|
|
$modelId = CONTENT_MODEL_SCRIBUNTO, $formats = [ CONTENT_FORMAT_TEXT ]
|
2016-05-17 14:52:05 +00:00
|
|
|
) {
|
2013-02-20 22:00:42 +00:00
|
|
|
parent::__construct( $modelId, $formats );
|
|
|
|
}
|
|
|
|
|
2020-01-14 18:50:34 +00:00
|
|
|
/**
|
|
|
|
* @return string Class name
|
|
|
|
*/
|
2014-09-15 08:43:15 +00:00
|
|
|
protected function getContentClass() {
|
2020-01-14 18:50:34 +00:00
|
|
|
return ScribuntoContent::class;
|
2014-09-15 08:43:15 +00:00
|
|
|
}
|
|
|
|
|
2014-10-10 09:02:03 +00:00
|
|
|
/**
|
|
|
|
* @param string $format
|
|
|
|
* @return bool
|
|
|
|
*/
|
2013-04-29 20:51:39 +00:00
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2015-07-24 14:17:45 +00:00
|
|
|
/**
|
|
|
|
* 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 );
|
|
|
|
}
|
2021-10-08 10:18:29 +00:00
|
|
|
|
2021-10-27 12:13:37 +00:00
|
|
|
/**
|
|
|
|
* @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() );
|
|
|
|
}
|
|
|
|
|
2021-10-08 10:18:29 +00:00
|
|
|
/**
|
|
|
|
* @inheritDoc
|
|
|
|
*/
|
|
|
|
protected function fillParserOutput(
|
|
|
|
Content $content,
|
|
|
|
ContentParseParams $cpoParams,
|
|
|
|
ParserOutput &$output
|
|
|
|
) {
|
|
|
|
'@phan-var ScribuntoContent $content';
|
|
|
|
$page = $cpoParams->getPage();
|
|
|
|
$title = Title::castFromPageReference( $page );
|
|
|
|
$parserOptions = $cpoParams->getParserOptions();
|
|
|
|
$revId = $cpoParams->getRevId();
|
|
|
|
$generateHtml = $cpoParams->getGenerateHtml();
|
|
|
|
$parser = MediaWikiServices::getInstance()->getParser();
|
|
|
|
$text = $content->getText();
|
|
|
|
|
|
|
|
// Get documentation, if any
|
|
|
|
$output = 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() );
|
|
|
|
}
|
|
|
|
$output = $parser->parse( $docWikitext, $page, $parserOptions, true, true, $revId );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark the doc page as a transclusion, so we get purged when it
|
|
|
|
// changes.
|
|
|
|
$output->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
|
2021-10-27 12:13:37 +00:00
|
|
|
$status = $this->validate( $content, $title );
|
2021-10-08 10:18:29 +00:00
|
|
|
if ( !$status->isOK() ) {
|
|
|
|
$output->setText( $output->getRawText() .
|
|
|
|
Html::rawElement( 'div', [ 'class' => 'errorbox' ],
|
|
|
|
$status->getHTML( 'scribunto-error-short', 'scribunto-error-long' )
|
|
|
|
)
|
|
|
|
);
|
|
|
|
$trackingCategories = MediaWikiServices::getInstance()->getTrackingCategories();
|
|
|
|
$trackingCategories->addTrackingCategory( $output, 'scribunto-module-with-errors-category', $page );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !$generateHtml ) {
|
|
|
|
// We don't need the actual HTML
|
|
|
|
$output->setText( '' );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$engine = Scribunto::newDefaultEngine();
|
|
|
|
// @phan-suppress-next-line PhanTypeMismatchArgument
|
|
|
|
$engine->setTitle( $title );
|
|
|
|
if ( $this->highlight( $text, $output, $engine ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No GeSHi, or GeSHi can't parse it, use plain <pre>
|
|
|
|
$output->setText( $output->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 $output
|
|
|
|
* @param ScribuntoEngineBase $engine
|
|
|
|
* @return bool Success status
|
|
|
|
*/
|
|
|
|
protected function highlight( $text, ParserOutput $output, ScribuntoEngineBase $engine ) {
|
|
|
|
global $wgScribuntoUseGeSHi;
|
|
|
|
$language = $engine->getGeSHiLanguage();
|
|
|
|
if ( $wgScribuntoUseGeSHi && class_exists( SyntaxHighlight::class ) && $language ) {
|
|
|
|
$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
|
|
|
|
$output->addModuleStyles( 'ext.pygments' );
|
|
|
|
$output->addModules( 'ext.pygments.linenumbers' );
|
|
|
|
$output->setText( $output->getRawText() . $status->getValue() );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2013-02-20 22:00:42 +00:00
|
|
|
}
|