mediawiki-extensions-Scribunto/common/Hooks.php
Brad Jorsch 04a0a580e3 Add mw.title library
Note that fetching any title besides the one for the current page is
considered "expensive". It also records the title fetched in the
ParserOutput so it will be listed in pagelinks, just like #ifexists.

This also moves the ToString test formatter into TestFramework.lua, so
TitleLibraryTests.lua can use it too.

Change-Id: I799f3289a37fe1349b6bca5758829acf82cb718f
2013-02-28 11:57:11 -08:00

344 lines
9.6 KiB
PHP

<?php
/**
* Wikitext scripting infrastructure for MediaWiki: hooks.
* Copyright (C) 2009-2012 Victor Vasiliev <vasilvv@gmail.com>
* http://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*/
/**
* Hooks for the Scribunto extension.
*/
class ScribuntoHooks {
/**
* Register parser hooks.
* @param $parser Parser
* @return bool
*/
public static function setupParserHook( &$parser ) {
$parser->setFunctionHook( 'invoke', 'ScribuntoHooks::invokeHook', SFH_OBJECT_ARGS );
return true;
}
/**
* Called when the interpreter is to be reset.
*
* @param $parser Parser
* @return bool
*/
public static function clearState( &$parser ) {
Scribunto::resetParserEngine( $parser );
return true;
}
/**
* Called when the parser is cloned
*
* @param $parser Parser
* @return bool
*/
public static function parserCloned( $parser ) {
$parser->scribunto_engine = null;
return true;
}
/**
* Hook function for {{#invoke:module|func}}
*
* @param $parser Parser
* @param $frame PPFrame
* @param $args array
* @throws MWException
* @throws ScribuntoException
* @return string
*/
public static function invokeHook( &$parser, $frame, $args ) {
if ( !@constant( get_class( $frame ) . '::SUPPORTS_INDEX_OFFSET' ) ) {
throw new MWException(
'Scribunto needs MediaWiki 1.20 or later (Preprocessor::SUPPORTS_INDEX_OFFSET)' );
}
wfProfileIn( __METHOD__ );
try {
if ( count( $args ) < 2 ) {
throw new ScribuntoException( 'scribunto-common-nofunction' );
}
$moduleName = trim( $frame->expand( $args[0] ) );
$engine = Scribunto::getParserEngine( $parser );
$title = Title::makeTitleSafe( NS_MODULE, $moduleName );
if ( !$title || Scribunto::isDocSubpage( $title ) ) {
throw new ScribuntoException( 'scribunto-common-nosuchmodule' );
}
$module = $engine->fetchModuleFromParser( $title );
if ( !$module ) {
throw new ScribuntoException( 'scribunto-common-nosuchmodule' );
}
$functionName = trim( $frame->expand( $args[1] ) );
unset( $args[0] );
unset( $args[1] );
$childFrame = $frame->newChild( $args, $title, 1 );
$result = $module->invoke( $functionName, $childFrame );
$result = UtfNormal::cleanUp( strval( $result ) );
wfProfileOut( __METHOD__ );
return $result;
} catch( ScribuntoException $e ) {
$trace = $e->getScriptTraceHtml( array( 'msgOptions' => array( 'content' ) ) );
$html = Html::element( 'p', array(), $e->getMessage() );
if ( $trace !== false ) {
$html .= Html::element( 'p',
array(),
wfMessage( 'scribunto-common-backtrace' )->inContentLanguage()->text()
) . $trace;
}
$out = $parser->getOutput();
if ( !isset( $out->scribunto_errors ) ) {
$out->addOutputHook( 'ScribuntoError' );
$out->scribunto_errors = array();
$parser->addTrackingCategory( 'scribunto-common-error-category' );
}
$out->scribunto_errors[] = $html;
$id = 'mw-scribunto-error-' . ( count( $out->scribunto_errors ) - 1 );
$parserError = wfMessage( 'scribunto-parser-error' )->inContentLanguage()->text() .
$parser->insertStripItem( '<!--' . htmlspecialchars( $e->getMessage() ) . '-->' );
wfProfileOut( __METHOD__ );
// #iferror-compatible error element
return "<strong class=\"error\"><span class=\"scribunto-error\" id=\"$id\">" .
$parserError. "</span></strong>";
}
}
/**
* @param $title Title
* @param $lang string
* @return bool
*/
public static function getCodeLanguage( $title, &$lang ) {
global $wgScribuntoUseCodeEditor;
if( $wgScribuntoUseCodeEditor && $title->getNamespace() == NS_MODULE &&
!Scribunto::isDocSubpage( $title )
) {
$engine = Scribunto::newDefaultEngine();
if( $engine->getCodeEditorLanguage() ) {
$lang = $engine->getCodeEditorLanguage();
return false;
}
}
return true;
}
/**
* Set the Scribunto content handler for modules
* @param $title Title
* @param &$model string
* @return bool
*/
public static function contentHandlerDefaultModelFor( $title, &$model ) {
if( $title->getNamespace() == NS_MODULE && !Scribunto::isDocSubpage( $title ) ) {
$model = 'Scribunto';
return false;
}
return true;
}
/**
* Adds report of number of evaluations by the single wikitext page.
*
* @param $parser Parser
* @param $report
* @return bool
*/
public static function reportLimits( $parser, &$report ) {
if ( Scribunto::isParserEnginePresent( $parser ) ) {
$engine = Scribunto::getParserEngine( $parser );
$report .= $engine->getLimitReport();
}
return true;
}
/**
* Adds the module namespaces.
*/
public static function addCanonicalNamespaces( &$list ) {
$list[NS_MODULE] = 'Module';
$list[NS_MODULE_TALK] = 'Module_talk';
return true;
}
/**
* EditPageBeforeEditChecks hook
* @param $editor EditPage
* @param $checkboxes Checkbox array
* @param $tabindex Current tabindex
*/
public static function beforeEditChecks( &$editor, &$checkboxes, &$tabindex ) {
if ( $editor->getTitle()->getNamespace() !== NS_MODULE ) {
return true;
}
if ( Scribunto::isDocSubpage( $editor->getTitle() ) ) {
return true;
}
$req = RequestContext::getMain()->getRequest();
$name = 'scribunto_ignore_errors';
$attribs = array(
'tabindex' => ++$tabindex,
'id' => "mw-$name",
);
$checkboxes['scribunto'] =
Xml::check( $name, $req->getCheck( $name ), $attribs ) .
'&#160;' .
Xml::label( wfMessage( 'scribunto-ignore-errors' )->text(), "mw-$name" );
// While we're here, lets set up the edit module
global $wgOut;
$wgOut->addModules( 'ext.scribunto.edit' );
$editor->editFormTextAfterTools = '<div id="mw-scribunto-console"></div>';
return true;
}
/**
* EditPageBeforeEditButtons hook
* @param $editor EditPage
* @param $buttons Button array
* @param $tabindex Current tabindex
*/
public static function beforeEditButtons( &$editor, &$buttons, &$tabindex ) {
if ( $editor->getTitle()->getNamespace() !== NS_MODULE ) {
return true;
}
if ( Scribunto::isDocSubpage( $editor->getTitle() ) ) {
return true;
}
unset( $buttons['preview'] );
return true;
}
/**
* @param $editor EditPage
* @param $text string
* @param $error
* @param $summary
* @return bool
*/
public static function validateScript( $editor, $text, &$error, $summary ) {
global $wgOut;
$title = $editor->getTitle();
if( $title->getNamespace() != NS_MODULE ) {
return true;
}
if ( Scribunto::isDocSubpage( $title ) ) {
return true;
}
$req = RequestContext::getMain()->getRequest();
if ( $req->getBool( 'scribunto_ignore_errors' ) ) {
return true;
}
$engine = Scribunto::newDefaultEngine();
$engine->setTitle( $title );
$status = $engine->validate( $text, $title->getPrefixedDBkey() );
if( $status->isOK() ) {
return true;
}
$errmsg = $status->getWikiText( 'scribunto-error-short', 'scribunto-error-long' );
$error = <<<WIKI
<div class="errorbox">
{$errmsg}
</div>
<br clear="all" />
WIKI;
if ( isset( $status->scribunto_error->params['module'] ) ) {
$module = $status->scribunto_error->params['module'];
$line = $status->scribunto_error->params['line'];
if ( $module === $title->getPrefixedDBkey() && preg_match( '/^\d+$/', $line ) ) {
$wgOut->addInlineScript( 'window.location.hash = ' . Xml::encodeJsVar( "#mw-ce-l$line" ) );
}
}
return true;
}
/**
* @param $files array
* @return bool
*/
public static function unitTestsList( &$files ) {
$tests = array(
'engines/LuaStandalone/LuaStandaloneInterpreterTest.php',
'engines/LuaStandalone/StandaloneTest.php',
'engines/LuaSandbox/LuaSandboxInterpreterTest.php',
'engines/LuaSandbox/SandboxTest.php',
'engines/LuaCommon/LuaEnvironmentComparisonTest.php',
'engines/LuaCommon/CommonTest.php',
'engines/LuaCommon/SiteLibraryTest.php',
'engines/LuaCommon/UriLibraryTest.php',
'engines/LuaCommon/UstringLibraryTest.php',
'engines/LuaCommon/MessageLibraryTest.php',
'engines/LuaCommon/TitleLibraryTest.php',
);
foreach ( $tests as $test ) {
$files[] = dirname( __FILE__ ) .'/../tests/' . $test;
}
return true;
}
/**
* @param $outputPage OutputPage
* @param $parserOutput ParserOutput
*/
public static function parserOutputHook( $outputPage, $parserOutput ) {
$outputPage->addModules( 'ext.scribunto' );
$outputPage->addInlineScript( 'mw.loader.using("ext.scribunto", function() {' .
Xml::encodeJsCall( 'mw.scribunto.setErrors', array( $parserOutput->scribunto_errors ) )
. '});' );
}
/**
* @param &$article Article
* @param &$outputDone boolean
* @param &$pcache boolean
* @return boolean
*/
public static function showDocSubpageHeader( &$article, &$outputDone, &$pcache ) {
global $wgOut;
$title = $article->getTitle();
if( $title->getNamespace() === NS_MODULE && Scribunto::isDocSubpage( $title ) ) {
$docSubpage = wfMessage( 'scribunto-doc-subpage-name' )->plain();
$title = substr( $title, 0, -strlen( $docSubpage ) - 1 );
$wgOut->addHTML(
wfMessage( 'scribunto-doc-subpage-header', $title )->parseAsBlock()
);
}
return true;
}
}