Index: trunk/extensions/Scripting/scripting.sql |
— | — | @@ -1,16 +0,0 @@ |
2 | | -CREATE TABLE /*_*/scriptlinks ( |
3 | | - -- Key to the page_id of the page containing the link. |
4 | | - sl_from int unsigned NOT NULL default 0, |
5 | | - |
6 | | - -- Key to page_title of the target page. |
7 | | - -- The target page may or may not exist, and due to renames |
8 | | - -- and deletions may refer to different page records as time |
9 | | - -- goes by. |
10 | | - sl_to varchar(255) binary NOT NULL default '' |
11 | | -) /*$wgDBTableOptions*/; |
12 | | - |
13 | | -CREATE UNIQUE INDEX /*i*/sl_from ON /*_*/scriptlinks (sl_from,sl_to); |
14 | | -CREATE UNIQUE INDEX /*i*/sl_title ON /*_*/scriptlinks (sl_to,sl_from); |
Index: trunk/extensions/Scripting/Scripting.i18n.php |
— | — | @@ -13,7 +13,13 @@ |
14 | 14 | */ |
15 | 15 | $messages['en'] = array( |
16 | 16 | 'scripting-desc' => 'Framework for embedding scripting languages into MediaWiki pages', |
17 | | - |
18 | | - 'scripting-exception-luasandbox-error' => 'Lua error: $1', |
19 | | - 'scripting-exception-common-toofewargs' => 'Lua error: Too few arguments to function $1', |
| 17 | + 'scripting-codelocation' => 'in $1 at line $2', |
| 18 | + 'scripting-luasandbox-error' => 'Lua error: $2', |
| 19 | + 'scripting-common-toofewargs' => 'Lua error: Too few arguments to function $2', |
| 20 | + 'scripting-common-nosuchmodule' => 'Script error: No such module', |
| 21 | + 'scripting-luasandbox-noreturn' => 'Script error: The module did not return a value, it should return an export table.', |
| 22 | + 'scripting-luasandbox-toomanyreturns' => 'Script error: The module returned multiple values, it should return an export table.', |
| 23 | + 'scripting-luasandbox-notarrayreturn' => 'Script error: The module returned something other than a table, it should return an export table.', |
| 24 | + 'scripting-common-nofunction' => 'Script error: You must specify a function to call.', |
| 25 | + 'scripting-common-nosuchfunction' => 'Script error: The function you specified did not exist.', |
20 | 26 | ); |
Index: trunk/extensions/Scripting/Scripting.php |
— | — | @@ -40,48 +40,41 @@ |
41 | 41 | $wgAutoloadClasses['ScriptingModuleBase'] = $dir.'common/Base.php'; |
42 | 42 | $wgAutoloadClasses['ScriptingFunctionBase'] = $dir.'common/Base.php'; |
43 | 43 | $wgAutoloadClasses['ScriptingHooks'] = $dir.'common/Hooks.php'; |
44 | | -$wgAutoloadClasses['ScriptLinksUpdateHooks'] = $dir.'common/LinkUpdates.php'; |
45 | 44 | $wgAutoloadClasses['ScriptingException'] = $dir.'common/Common.php'; |
46 | 45 | $wgAutoloadClasses['Scripting'] = $dir.'common/Common.php'; |
47 | 46 | |
48 | 47 | $wgHooks['ParserFirstCallInit'][] = 'ScriptingHooks::setupParserHook'; |
49 | 48 | $wgHooks['ParserLimitReport'][] = 'ScriptingHooks::reportLimits'; |
50 | 49 | $wgHooks['ParserClearState'][] = 'ScriptingHooks::clearState'; |
51 | | -$wgHooks['ParserTestTables'][] = 'ScriptingHooks::addTestTables'; |
52 | 50 | |
53 | 51 | $wgHooks['CanonicalNamespaces'][] = 'ScriptingHooks::addCanonicalNamespaces'; |
54 | 52 | $wgHooks['ArticleViewCustom'][] = 'ScriptingHooks::handleScriptView'; |
55 | 53 | $wgHooks['TitleIsWikitextPage'][] = 'ScriptingHooks::isWikitextPage'; |
56 | | -$wgHooks['CodeEditorGetPageLanguage'][] = 'ScriptingHooks::getCodeLangauge'; |
| 54 | +$wgHooks['CodeEditorGetPageLanguage'][] = 'ScriptingHooks::getCodeLanguage'; |
57 | 55 | $wgHooks['EditFilter'][] = 'ScriptingHooks::validateScript'; |
58 | 56 | |
59 | | -$wgHooks['LinksUpdate'][] = 'ScriptLinksUpdateHooks::updateLinks'; |
60 | | -$wgHooks['ArticleEditUpdates'][] = 'ScriptLinksUpdateHooks::purgeCache'; |
61 | | -$wgHooks['ParserAfterTidy'][] = 'ScriptLinksUpdateHooks::appendToOutput'; |
62 | | -$wgHooks['BacklinkCacheGetPrefix'][] = 'ScriptLinksUpdateHooks::getBacklinkCachePrefix'; |
63 | | -$wgHooks['BacklinkCacheGetConditions'][] = 'ScriptLinksUpdateHooks::getBacklinkCacheConditions'; |
64 | | - |
65 | 57 | /***** Individual engines and their configurations *****/ |
66 | 58 | |
67 | | -/** |
68 | | - * Available scripting engines. |
69 | | - */ |
70 | | -$wgScriptingEngines = array( |
71 | | - 'luasandbox' => 'LuaSandboxEngine', |
72 | | -); |
73 | | - |
74 | 59 | $wgAutoloadClasses['LuaSandboxEngine'] = $dir.'engines/LuaSandbox/Engine.php'; |
75 | 60 | |
76 | 61 | /***** Configuration *****/ |
77 | 62 | |
78 | 63 | /** |
79 | | - * The name of the scripting engine. |
| 64 | + * The name of the default scripting engine. |
80 | 65 | */ |
81 | | -$wgScriptingEngine = false; |
| 66 | +$wgScriptingDefaultEngine = 'luasandbox'; |
82 | 67 | |
| 68 | +/** |
| 69 | + * Configuration for each scripting engine |
| 70 | + */ |
| 71 | +$wgScriptingEngineConf = array( |
| 72 | + 'luasandbox' => array( |
| 73 | + 'class' => 'LuaSandboxEngine', |
| 74 | + 'memoryLimit' => 50 * 1024 * 1024, |
| 75 | + 'cpuLimit' => 7, |
| 76 | + ), |
| 77 | +); |
83 | 78 | |
84 | | -$wgScriptingEngineConf = array(); |
85 | | - |
86 | 79 | /** |
87 | 80 | * Script namespace numbers. |
88 | 81 | */ |
Index: trunk/extensions/Scripting/common/LinkUpdates.php |
— | — | @@ -1,209 +0,0 @@ |
2 | | -<?php |
3 | | -/** |
4 | | - * Wikitext scripting infrastructure for MediaWiki: link updating code |
5 | | - * Copyright (C) 2011-2012 Victor Vasiliev <vasilvv@gmail.com> et al |
6 | | - * Based on MediaWiki file LinksUpdate.php |
7 | | - * http://www.mediawiki.org/ |
8 | | - * |
9 | | - * This program is free software; you can redistribute it and/or modify |
10 | | - * it under the terms of the GNU General Public License as published by |
11 | | - * the Free Software Foundation; either version 2 of the License, or |
12 | | - * (at your option) any later version. |
13 | | - * |
14 | | - * This program is distributed in the hope that it will be useful, |
15 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | - * GNU General Public License for more details. |
18 | | - * |
19 | | - * You should have received a copy of the GNU General Public License along |
20 | | - * with this program; if not, write to the Free Software Foundation, Inc., |
21 | | - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
22 | | - * http://www.gnu.org/copyleft/gpl.html |
23 | | - */ |
24 | | - |
25 | | -if( !defined( 'MEDIAWIKI' ) ) |
26 | | - die(); |
27 | | - |
28 | | - |
29 | | -/** |
30 | | - * Class that contains hooks related to tracking links to scripts and invalidating |
31 | | - * pages on script change. |
32 | | - */ |
33 | | -class ScriptLinksUpdateHooks { |
34 | | - /** |
35 | | - * Append script links to the output. |
36 | | - */ |
37 | | - public static function appendToOutput( &$parser, &$text ) { |
38 | | - if( isset( $parser->scripting_engine ) ) { |
39 | | - $parser->mOutput->scripting_links = $parser->scripting_engine->getUsedModules(); |
40 | | - } |
41 | | - return true; |
42 | | - } |
43 | | - |
44 | | - /** |
45 | | - * Run the link updater. |
46 | | - */ |
47 | | - public static function updateLinks( &$update ) { |
48 | | - $output = $update->mParserOutput; |
49 | | - if( isset( $output->scripting_links ) ) { |
50 | | - $new = $output->scripting_links; |
51 | | - } else { |
52 | | - $new = array(); |
53 | | - } |
54 | | - |
55 | | - $isupdate = new ScriptLinksUpdate( $update, $new ); |
56 | | - $isupdate->run(); |
57 | | - return true; |
58 | | - } |
59 | | - |
60 | | - /** |
61 | | - * Purge cache for all the pages where the script is used. |
62 | | - * @param $article Article |
63 | | - * @param $editInfo |
64 | | - * @param $changed |
65 | | - * @return bool |
66 | | - */ |
67 | | - public static function purgeCache( &$article, &$editInfo, $changed ) { |
68 | | - global $wgDeferredUpdateList, $wgParser; |
69 | | - |
70 | | - if( $article->getTitle()->getNamespace() == NS_MODULE ) { |
71 | | - // Invalidate the script cache |
72 | | - $engine = Scripting::getEngine( $wgParser ); |
73 | | - $engine->invalidateModuleCache( $article->getTitle() ); |
74 | | - |
75 | | - // Invalidate the caches of articles which include the script |
76 | | - $wgDeferredUpdateList[] = new HTMLCacheUpdate( $article->getTitle(), 'scriptlinks' ); |
77 | | - } |
78 | | - |
79 | | - return true; |
80 | | - } |
81 | | - |
82 | | - /** |
83 | | - * Add scriptlinks to the list of tables supported by BacklinkCache. |
84 | | - */ |
85 | | - public static function getBacklinkCachePrefix( $table, &$prefix ) { |
86 | | - if( $table == 'scriptlinks' ) { |
87 | | - $prefix = 'sl'; |
88 | | - return false; |
89 | | - } else { |
90 | | - return true; |
91 | | - } |
92 | | - } |
93 | | - |
94 | | - /** |
95 | | - * Add scriptlinks to the list of tables supported by BacklinkCache. |
96 | | - * @param $table |
97 | | - * @param $title Title |
98 | | - * @param $conds |
99 | | - * @return bool |
100 | | - */ |
101 | | - public static function getBacklinkCacheConditions( $table, $title, &$conds ) { |
102 | | - if( $table == 'scriptlinks' ) { |
103 | | - $conds = array( |
104 | | - 'sl_to' => $title->getDBkey(), |
105 | | - 'page_id=sl_from' |
106 | | - ); |
107 | | - return false; |
108 | | - } else { |
109 | | - return true; |
110 | | - } |
111 | | - } |
112 | | -} |
113 | | - |
114 | | -/** |
115 | | - * A class that updates links on scripts like what phase3/includes/LinksUpdate.php does |
116 | | - * with templates. |
117 | | - */ |
118 | | -class ScriptLinksUpdate { |
119 | | - var $update, $id, $new; |
120 | | - |
121 | | - public function __construct( $update, $new ) { |
122 | | - $this->update = $update; |
123 | | - $this->id = $update->mId; |
124 | | - $this->new = $new; |
125 | | - } |
126 | | - |
127 | | - public function run() { |
128 | | - global $wgUseDumbLinkUpdate; |
129 | | - |
130 | | - wfProfileIn( __METHOD__ ); |
131 | | - |
132 | | - if( $wgUseDumbLinkUpdate ) { |
133 | | - $this->update->dumbTableUpdate( 'scriptlinks', $this->getScriptInsertions(), 'sl_from' ); |
134 | | - } else { |
135 | | - $existing = $this->getExistingScripts(); |
136 | | - $this->update->incrTableUpdate( 'scriptlinks', 'sl', $this->getScriptDeletions( $existing ), |
137 | | - $this->getScriptInsertions( $existing ) ); |
138 | | - } |
139 | | - |
140 | | - if( $this->update->mRecursive && $this->update->mTitle->getNamespace() == NS_MODULE ) { |
141 | | - $this->queueRecursiveJobs(); |
142 | | - } |
143 | | - |
144 | | - wfProfileOut( __METHOD__ ); |
145 | | - } |
146 | | - |
147 | | - protected function getExistingScripts() { |
148 | | - $result = array(); |
149 | | - |
150 | | - $res = $this->update->mDb->select( 'scriptlinks', array( 'sl_to' ), |
151 | | - array( 'sl_from' => $this->id ), __METHOD__, $this->update->mOptions ); |
152 | | - foreach ( $res as $row ) { |
153 | | - $result[] = $row->sl_to; |
154 | | - } |
155 | | - |
156 | | - return $result; |
157 | | - } |
158 | | - |
159 | | - protected function getScriptInsertions( $existing = array() ) { |
160 | | - $result = array(); |
161 | | - |
162 | | - foreach( array_diff( $this->new, $existing ) as $module ) { |
163 | | - $result[] = array( |
164 | | - 'sl_from' => $this->id, |
165 | | - 'sl_to' => $module, |
166 | | - ); |
167 | | - } |
168 | | - |
169 | | - return $result; |
170 | | - } |
171 | | - |
172 | | - protected function getScriptDeletions( $existing = array() ) { |
173 | | - $result = array(); |
174 | | - |
175 | | - foreach( array_diff( $existing, $this->new ) as $module ) { |
176 | | - $result[] = array( |
177 | | - 'sl_from' => $this->id, |
178 | | - 'sl_to' => $module, |
179 | | - ); |
180 | | - } |
181 | | - |
182 | | - return $result; |
183 | | - } |
184 | | - |
185 | | - protected function queueRecursiveJobs() { |
186 | | - global $wgUpdateRowsPerJob; |
187 | | - wfProfileIn( __METHOD__ ); |
188 | | - |
189 | | - $cache = $this->update->mTitle->getBacklinkCache(); |
190 | | - $batches = $cache->partition( 'scriptlinks', $wgUpdateRowsPerJob ); |
191 | | - if ( !$batches ) { |
192 | | - wfProfileOut( __METHOD__ ); |
193 | | - return; |
194 | | - } |
195 | | - $jobs = array(); |
196 | | - foreach ( $batches as $batch ) { |
197 | | - list( $start, $end ) = $batch; |
198 | | - $params = array( |
199 | | - 'table' => 'scriptlinks', |
200 | | - 'start' => $start, |
201 | | - 'end' => $end, |
202 | | - ); |
203 | | - $jobs[] = new RefreshLinksJob2( $this->update->mTitle, $params ); |
204 | | - } |
205 | | - Job::batchInsert( $jobs ); |
206 | | - |
207 | | - wfProfileOut( __METHOD__ ); |
208 | | - } |
209 | | -} |
210 | | - |
Index: trunk/extensions/Scripting/common/Common.php |
— | — | @@ -6,32 +6,49 @@ |
7 | 7 | class Scripting { |
8 | 8 | const LOCAL = 'local'; |
9 | 9 | |
10 | | - protected static function getEngineClass() { |
11 | | - global $wgScriptingEngine, $wgScriptingEngines; |
| 10 | + /** |
| 11 | + * Create a new engine object with specified parameters. |
| 12 | + */ |
| 13 | + public static function newEngine( $options ) { |
| 14 | + $class = $options['class']; |
| 15 | + return new $class( $options ); |
| 16 | + } |
12 | 17 | |
13 | | - if( !$wgScriptingEngine ) { |
14 | | - throw new MWException( 'Scripting extension is enabled but $wgScriptingEngine is not set' ); |
| 18 | + /** |
| 19 | + * Create a new engine object with default parameters |
| 20 | + * @param $extraOptions Extra options to pass to the constructor, in addition to the configured options |
| 21 | + */ |
| 22 | + public static function newDefaultEngine( $extraOptions = array() ) { |
| 23 | + global $wgScriptingDefaultEngine, $wgScriptingEngineConf; |
| 24 | + if( !$wgScriptingDefaultEngine ) { |
| 25 | + throw new MWException( 'Scripting extension is enabled but $wgScriptingDefaultEngine is not set' ); |
15 | 26 | } |
16 | 27 | |
17 | | - if( !isset( $wgScriptingEngines[$wgScriptingEngine] ) ) { |
18 | | - throw new MWException( 'Invalid scripting engine is specified in $wgScriptingEngine' ); |
| 28 | + if( !isset( $wgScriptingEngineConf[$wgScriptingDefaultEngine] ) ) { |
| 29 | + throw new MWException( 'Invalid scripting engine is specified in $wgScriptingDefaultEngine' ); |
19 | 30 | } |
20 | | - |
21 | | - return $wgScriptingEngines[$wgScriptingEngine]; |
| 31 | + $options = $extraOptions + $wgScriptingEngineConf[$wgScriptingDefaultEngine]; |
| 32 | + return self::newEngine( $options ); |
22 | 33 | } |
23 | 34 | |
24 | | - public static function getEngine( $parser ) { |
25 | | - global $wgScriptingEngineConf; |
26 | | - |
| 35 | + /** |
| 36 | + * Get an engine instance for the given parser, and cache it in the parser |
| 37 | + * so that subsequent calls to this function for the same parser will return |
| 38 | + * the same engine. |
| 39 | + * |
| 40 | + * @param Parser $parser |
| 41 | + */ |
| 42 | + public static function getParserEngine( $parser ) { |
27 | 43 | if( !isset( $parser->scripting_engine ) || !$parser->scripting_engine ) { |
28 | | - $class = self::getEngineClass(); |
29 | | - $parser->scripting_engine = new $class( $parser ); |
30 | | - $parser->scripting_engine->setOptions( $wgScriptingEngineConf ); |
| 44 | + $parser->scripting_engine = self::newDefaultEngine( array( 'parser' => $parser ) ); |
31 | 45 | } |
32 | 46 | return $parser->scripting_engine; |
33 | 47 | } |
34 | 48 | |
35 | | - public static function resetEngine( $parser ) { |
| 49 | + /** |
| 50 | + * Remove the current engine instance from the parser |
| 51 | + */ |
| 52 | + public static function resetParserEngine( $parser ) { |
36 | 53 | $parser->scripting_engine = null; |
37 | 54 | } |
38 | 55 | } |
— | — | @@ -41,22 +58,28 @@ |
42 | 59 | * normally abort the request, instead it is caught and shown to the user. |
43 | 60 | */ |
44 | 61 | class ScriptingException extends MWException { |
45 | | - function __construct( $exceptionID, $engine, $module = null, $line = null, $params = array() ) { |
46 | | - if( $module ) { |
47 | | - $codelocation = wfMsg( 'scripting-codelocation', $module, $line ); |
48 | | - $msg = wfMsgExt( "scripting-exception-{$engine}-{$exceptionID}", array(), array_merge( array( $codelocation ), $params ) ); |
| 62 | + var $messageName, $params; |
| 63 | + |
| 64 | + function __construct( $messageName, $params = array() ) { |
| 65 | + if ( isset( $params['args'] ) ) { |
| 66 | + $args = $params['args']; |
49 | 67 | } else { |
50 | | - $msg = wfMsgExt( "scripting-exception-{$engine}-{$exceptionID}", array(), $params ); |
| 68 | + $args = array(); |
51 | 69 | } |
| 70 | + if ( isset( $params['module'] ) && isset( $params['line'] ) ) { |
| 71 | + $codelocation = wfMsg( 'scripting-codelocation', $params['module'], $params['line'] ); |
| 72 | + } else { |
| 73 | + $codelocation = '[UNKNOWN]'; // should never happen |
| 74 | + } |
| 75 | + array_unshift( $args, $codelocation ); |
| 76 | + $msg = wfMsgExt( $messageName, array(), $args ); |
52 | 77 | parent::__construct( $msg ); |
53 | 78 | |
54 | | - $this->exceptionID = $exceptionID; |
55 | | - $this->line = $line; |
56 | | - $this->module = $module; |
| 79 | + $this->messageName = $messageName; |
57 | 80 | $this->params = $params; |
58 | 81 | } |
59 | 82 | |
60 | | - public function getExceptionID() { |
61 | | - return $this->exceptionID; |
| 83 | + public function getMessageName() { |
| 84 | + return $this->messageName; |
62 | 85 | } |
63 | 86 | } |
Index: trunk/extensions/Scripting/common/Base.php |
— | — | @@ -29,98 +29,51 @@ |
30 | 30 | abstract class ScriptingEngineBase { |
31 | 31 | protected |
32 | 32 | $parser, |
33 | | - $modules = array(), |
34 | | - $moduleTitles = array(); |
| 33 | + $options, |
| 34 | + $modules = array(); |
35 | 35 | |
36 | 36 | /** |
37 | 37 | * Creates a new module object within this engine |
38 | 38 | */ |
39 | | - abstract protected function newModule( $title, $code, $revisionID, $source ); |
| 39 | + abstract protected function newModule( $text, $chunkName ); |
40 | 40 | |
41 | 41 | /** |
42 | | - * Returns the default options of the engine. |
43 | | - */ |
44 | | - public function getDefaultOptions() { |
45 | | - return array(); |
46 | | - } |
47 | | - |
48 | | - /** |
49 | | - * Is called by setOptions() in order to notify the engine |
50 | | - * that the options were changed. |
51 | | - */ |
52 | | - protected function updateOptions() { /* No-op */ } |
53 | | - |
54 | | - /** |
55 | 42 | * Constructor. |
56 | 43 | * |
57 | | - * @param $parser Parser Wikitext parser |
| 44 | + * @param $options Associative array of options: |
| 45 | + * - parser: A Parser object |
58 | 46 | */ |
59 | | - public function __construct( $parser ) { |
60 | | - $this->parser = $parser; |
| 47 | + public function __construct( $options ) { |
| 48 | + $this->options = $options; |
| 49 | + if ( isset( $options['parser'] ) ) { |
| 50 | + $this->parser = $options['parser']; |
| 51 | + } |
61 | 52 | } |
62 | 53 | |
63 | 54 | /** |
64 | | - * Loads the module either from instance cache or from the actual revision. |
| 55 | + * Load a module from some parser-defined template loading mechanism and |
| 56 | + * register a parser output dependency. |
| 57 | + * |
65 | 58 | * Does not initialize the module, i.e. do not expect it to complain if the module |
66 | 59 | * text is garbage or has syntax error. Returns a module or throws an exception. |
67 | | - * |
68 | | - * @param $title Title/string The title or the name of the module. |
69 | | - * @param $source string Source of the module |
| 60 | + * |
| 61 | + * @param $title The title of the module |
70 | 62 | * @return ScriptingEngineModule |
71 | 63 | */ |
72 | | - public function getModule( $title, $source = Scripting::LOCAL ) { |
73 | | - // Convert string to title |
74 | | - if( !$title instanceof Title ) { |
75 | | - $titleobj = Title::newFromText( (string)$title, NS_MODULE ); |
76 | | - if( !$titleobj || $titleobj->getNamespace() != NS_MODULE ) { |
77 | | - throw new ScriptingException( 'badtitle', 'common' ); // scripting-exceptions-common-badtitle |
78 | | - } |
79 | | - $title = $titleobj; |
| 64 | + function fetchModuleFromParser( Title $title ) { |
| 65 | + list( $text, $finalTitle ) = $this->parser->fetchTemplateAndTitle( $title ); |
| 66 | + if ( $text === false ) { |
| 67 | + throw new ScriptingException( 'scripting-common-nosuchmodule' ); |
80 | 68 | } |
81 | 69 | |
82 | | - // Check if it is already loaded |
83 | | - $key = $title->getPrefixedText(); |
84 | | - if( !isset( $this->modules[$key] ) ) { |
85 | | - // Fetch the text |
86 | | - $rev = $this->getModuleRev( $title, $source ); |
87 | | - if( !$rev ) { |
88 | | - throw new ScriptingException( 'nosuchmodule', 'common' ); // scripting-exceptions-common-nosuchmodule |
89 | | - } |
90 | | - if( $rev->getTitle()->getNamespace() != NS_MODULE ) { |
91 | | - throw new ScriptingException( 'badnamespace', 'common' ); // scripting-exceptions-common-badnamespace |
92 | | - } |
93 | | - |
94 | | - // Create the class |
95 | | - $this->modules[$key] = $this->newModule( $title, $rev->getText(), $rev->getID(), $source ); |
96 | | - $this->moduleTitles[] = $title; |
| 70 | + $key = $finalTitle->getPrefixedDBkey(); |
| 71 | + if ( !isset( $this->modules[$key] ) ) { |
| 72 | + $this->modules[$key] = $this->newModule( $text, $key ); |
97 | 73 | } |
98 | 74 | return $this->modules[$key]; |
99 | 75 | } |
100 | 76 | |
101 | 77 | /** |
102 | | - * Fetches the revision for given module title. |
103 | | - */ |
104 | | - private function getModuleRev( $title, $source ) { |
105 | | - if( $source != Scripting::LOCAL ) { |
106 | | - throw new MWException( 'Non-local scripts are not supported at this point' ); |
107 | | - } |
108 | | - |
109 | | - $rev = Revision::newFromTitle( $title ); |
110 | | - if( $rev && $real = Title::newFromRedirect( $rev->getText() ) ) { |
111 | | - $rev = Revision::newFromTitle( $real ); |
112 | | - } |
113 | | - return $rev; |
114 | | - } |
115 | | - |
116 | | - /** |
117 | | - * Sets the engine-specific options from $wgScriptingEngineConf. |
118 | | - */ |
119 | | - function setOptions( $options ) { |
120 | | - $this->options = array_merge( $this->getDefaultOptions(), $options ); |
121 | | - $this->updateOptions(); |
122 | | - } |
123 | | - |
124 | | - /** |
125 | 78 | * Validates the script and returns an array of the syntax errors for the |
126 | 79 | * given code. |
127 | 80 | * |
— | — | @@ -128,8 +81,8 @@ |
129 | 82 | * @param $title Title of the code page |
130 | 83 | * @return array |
131 | 84 | */ |
132 | | - function validate( $code, $title ) { |
133 | | - $module = $this->newModule( $title, $code, 0, Scripting::LOCAL ); |
| 85 | + function validate( $text, $chunkName = false ) { |
| 86 | + $module = $this->newModule( $text, $chunkName ); |
134 | 87 | |
135 | 88 | try { |
136 | 89 | $module->initialize(); |
— | — | @@ -150,30 +103,14 @@ |
151 | 104 | } |
152 | 105 | |
153 | 106 | /** |
154 | | - * Returns the titles of all the modules used by this instance of the |
155 | | - * engine. |
156 | | - */ |
157 | | - public function getUsedModules() { |
158 | | - return $this->moduleTitles; |
159 | | - } |
160 | | - |
161 | | - /** |
162 | | - * Invalidates the cache of the given module by its title. Should be |
163 | | - * redefined if the engine uses any form of bytecode or other cache. |
164 | | - */ |
165 | | - function invalidateModuleCache( $title ) { |
166 | | - /* No-op by default */ |
167 | | - } |
168 | | - |
169 | | - /** |
170 | 107 | * Get the language for GeSHi syntax highlighter. |
171 | 108 | */ |
172 | | - function getGeSHiLangauge() { |
| 109 | + function getGeSHiLanguage() { |
173 | 110 | return false; |
174 | 111 | } |
175 | 112 | |
176 | 113 | /** |
177 | | - * Get the langauge for Ace code editor. |
| 114 | + * Get the language for Ace code editor. |
178 | 115 | */ |
179 | 116 | function getCodeEditorLanguage() { |
180 | 117 | return false; |
— | — | @@ -185,23 +122,19 @@ |
186 | 123 | * and maintaining the contents of the module. |
187 | 124 | */ |
188 | 125 | abstract class ScriptingModuleBase { |
189 | | - var $engine, $title, $code, $revisionID, $source; |
| 126 | + var $engine, $code, $chunkName; |
190 | 127 | |
191 | | - public function __construct( $engine, $title, $code, $revisionID, $source ) { |
| 128 | + public function __construct( $engine, $code, $chunkName ) { |
192 | 129 | $this->engine = $engine; |
193 | | - $this->title = $title; |
194 | 130 | $this->code = $code; |
195 | | - $this->revisionID = $revisionID; |
196 | | - $this->source = $source; |
| 131 | + $this->chunkName = $chunkName; |
197 | 132 | } |
198 | 133 | |
199 | 134 | /** Accessors **/ |
200 | 135 | public function getEngine() { return $this->engine; } |
201 | | - public function getTitle() { return $this->title; } |
202 | 136 | public function getCode() { return $this->code; } |
203 | | - public function getRevisionID() { return $this->revisionID; } |
204 | | - public function getSource() { return $this->source; } |
205 | | - |
| 137 | + public function getChunkName() { return $this->chunkName; } |
| 138 | + |
206 | 139 | /** |
207 | 140 | * Initialize the module. That means parse it and load the |
208 | 141 | * functions/constants/whatever into the object. |
Index: trunk/extensions/Scripting/common/Hooks.php |
— | — | @@ -42,19 +42,11 @@ |
43 | 43 | * @return bool |
44 | 44 | */ |
45 | 45 | public static function clearState( &$parser ) { |
46 | | - Scripting::resetEngine( $parser ); |
| 46 | + Scripting::resetParserEngine( $parser ); |
47 | 47 | return true; |
48 | 48 | } |
49 | 49 | |
50 | 50 | /** |
51 | | - * Add scriptlinks table to parser tests. |
52 | | - */ |
53 | | - public static function addTestTables( &$tables ) { |
54 | | - $tables[] = 'scriptlinks'; |
55 | | - return true; |
56 | | - } |
57 | | - |
58 | | - /** |
59 | 51 | * Hook function for {{#invoke:module|func}} |
60 | 52 | * |
61 | 53 | * @param $parser Parser |
— | — | @@ -64,7 +56,7 @@ |
65 | 57 | */ |
66 | 58 | public static function callHook( &$parser, $frame, $args ) { |
67 | 59 | if( count( $args ) < 2 ) { |
68 | | - throw new ScriptingException( 'nofunction', 'common' ); // scripting-exceptions-common-nofunction |
| 60 | + throw new ScriptingException( 'scripting-common-nofunction' ); |
69 | 61 | } |
70 | 62 | |
71 | 63 | $module = $parser->mStripState->unstripBoth( array_shift( $args ) ); |
— | — | @@ -88,29 +80,32 @@ |
89 | 81 | /** |
90 | 82 | * @param $parser Parser |
91 | 83 | * @param $frame PPFrame |
92 | | - * @param $module |
93 | | - * @param $function |
| 84 | + * @param $moduleName |
| 85 | + * @param $functionName |
94 | 86 | * @param $args |
95 | 87 | * @return string |
96 | 88 | * @throws ScriptingException |
97 | 89 | */ |
98 | | - private static function doRunHook( $parser, $frame, $module, $function, $args ) { |
| 90 | + private static function doRunHook( $parser, $frame, $moduleName, $functionName, $args ) { |
99 | 91 | wfProfileIn( __METHOD__ ); |
100 | | - |
| 92 | + |
101 | 93 | try { |
102 | | - $engine = Scripting::getEngine( $parser ); |
103 | | - |
104 | | - foreach( $args as &$arg ) { |
105 | | - $arg = $frame->expand( $arg ); |
| 94 | + $engine = Scripting::getParserEngine( $parser ); |
| 95 | + $title = Title::makeTitleSafe( NS_MODULE, $moduleName ); |
| 96 | + if ( !$title ) { |
| 97 | + throw new ScriptingException( 'scripting-common-nosuchmodule' ); |
106 | 98 | } |
| 99 | + $module = $engine->fetchModuleFromParser( $title ); |
107 | 100 | |
108 | | - $module = $engine->getModule( $module, Scripting::LOCAL ); |
109 | | - |
110 | | - $functionObj = $module->getFunction( $function ); |
| 101 | + $functionObj = $module->getFunction( $functionName ); |
111 | 102 | if( !$functionObj ) { |
112 | | - throw new ScriptingException( 'nosuchfunction', 'common' ); // scripting-exceptions-common-nosuchfunction |
| 103 | + throw new ScriptingException( 'scripting-common-nosuchfunction' ); |
113 | 104 | } |
114 | 105 | |
| 106 | + foreach( $args as &$arg ) { |
| 107 | + $arg = $frame->expand( $arg ); |
| 108 | + } |
| 109 | + |
115 | 110 | $result = $functionObj->call( $args, $frame ); |
116 | 111 | |
117 | 112 | wfProfileOut( __METHOD__ ); |
— | — | @@ -132,11 +127,11 @@ |
133 | 128 | * @return bool |
134 | 129 | */ |
135 | 130 | public static function handleScriptView( $text, $title, $output ) { |
136 | | - global $wgScriptingUseGeSHi, $wgParser; |
| 131 | + global $wgScriptingUseGeSHi; |
137 | 132 | |
138 | 133 | if( $title->getNamespace() == NS_MODULE ) { |
139 | | - $engine = Scripting::getEngine( $wgParser ); |
140 | | - $language = $engine->getGeSHiLangauge(); |
| 134 | + $engine = Scripting::newDefaultEngine(); |
| 135 | + $language = $engine->getGeSHiLanguage(); |
141 | 136 | |
142 | 137 | if( $wgScriptingUseGeSHi && $language ) { |
143 | 138 | $geshi = SyntaxHighlight_GeSHi::prepare( $text, $language ); |
— | — | @@ -161,10 +156,10 @@ |
162 | 157 | } |
163 | 158 | } |
164 | 159 | |
165 | | - public static function getCodeLangauge( $title, &$lang ) { |
166 | | - global $wgParser, $wgScriptingUseCodeEditor; |
| 160 | + public static function getCodeLanguage( $title, &$lang ) { |
| 161 | + global $wgScriptingUseCodeEditor; |
167 | 162 | if( $wgScriptingUseCodeEditor && $title->getNamespace() == NS_MODULE ) { |
168 | | - $engine = Scripting::getEngine( $wgParser ); |
| 163 | + $engine = Scripting::newDefaultEngine(); |
169 | 164 | if( $engine->getCodeEditorLanguage() ) { |
170 | 165 | $lang = $engine->getCodeEditorLanguage(); |
171 | 166 | return false; |
— | — | @@ -198,8 +193,8 @@ |
199 | 194 | public static function reportLimits( $parser, &$report ) { |
200 | 195 | # FIXME |
201 | 196 | global $wgScriptsLimits; |
202 | | - $engine = Scripting::getEngine( $parser ); |
203 | | - $report .= $engine->getLimitsReport(); |
| 197 | + $engine = Scripting::getParserEngine( $parser ); |
| 198 | + $report .= $engine->getLimitReport(); |
204 | 199 | return true; |
205 | 200 | } |
206 | 201 | |
— | — | @@ -213,12 +208,12 @@ |
214 | 209 | } |
215 | 210 | |
216 | 211 | public static function validateScript( $editor, $text, $section, &$error ) { |
217 | | - global $wgUser, $wgParser; |
| 212 | + global $wgUser; |
218 | 213 | $title = $editor->mTitle; |
219 | 214 | |
220 | 215 | if( $title->getNamespace() == NS_MODULE ) { |
221 | | - $engine = Scripting::getEngine( $wgParser ); |
222 | | - $errors = $engine->validate( $text, $title ); |
| 216 | + $engine = Scripting::newDefaultEngine(); |
| 217 | + $errors = $engine->validate( $text, $title->getPrefixedDBkey() ); |
223 | 218 | if( !$errors ) { |
224 | 219 | return true; |
225 | 220 | } |
Index: trunk/extensions/Scripting/engines/LuaSandbox/Engine.php |
— | — | @@ -1,10 +1,10 @@ |
2 | 2 | <?php |
3 | 3 | |
4 | 4 | class LuaSandboxEngine extends ScriptingEngineBase { |
5 | | - public $sandbox, $loaded = false; |
| 5 | + public $sandbox, $options, $loaded = false; |
6 | 6 | |
7 | | - public function newModule( $title, $code, $revisionID, $source ) { |
8 | | - return new LuaSandboxEngineModule( $this, $title, $code, $revisionID, $source ); |
| 7 | + public function newModule( $text, $chunkName ) { |
| 8 | + return new LuaSandboxEngineModule( $this, $text, $chunkName ); |
9 | 9 | } |
10 | 10 | |
11 | 11 | public function load() { |
— | — | @@ -18,31 +18,17 @@ |
19 | 19 | |
20 | 20 | $this->sandbox = new LuaSandbox; |
21 | 21 | $this->sandbox->setMemoryLimit( $this->options['memoryLimit'] ); |
22 | | - $this->sandbox->setCPULimit( $this->options['maxCPU'] ); |
| 22 | + $this->sandbox->setCPULimit( $this->options['cpuLimit'] ); |
23 | 23 | $this->sandbox->registerLibrary( 'mw', array( 'import' => array( $this, 'importModule' ) ) ); |
24 | 24 | |
25 | 25 | $this->loaded = true; |
26 | 26 | } |
27 | 27 | |
28 | | - protected function updateOptions() { |
29 | | - if( $this->loaded ) { |
30 | | - $this->sandbox->setMemoryLimit( $this->options['memoryLimit'] ); |
31 | | - $this->sandbox->setCPULimit( $this->options['maxCPU'] ); |
32 | | - } |
33 | | - } |
34 | | - |
35 | 28 | protected function getModuleClassName() { |
36 | 29 | return 'LuaSandboxEngineModule'; |
37 | 30 | } |
38 | 31 | |
39 | | - public function getDefaultOptions() { |
40 | | - return array( |
41 | | - 'memoryLimit' => 50 * 1024 * 1024, |
42 | | - 'maxCPU' => 7, |
43 | | - ); |
44 | | - } |
45 | | - |
46 | | - public function getGeSHiLangauge() { |
| 32 | + public function getGeSHiLanguage() { |
47 | 33 | return 'lua'; |
48 | 34 | } |
49 | 35 | |
— | — | @@ -50,7 +36,7 @@ |
51 | 37 | return 'lua'; |
52 | 38 | } |
53 | 39 | |
54 | | - public function getLimitsReport() { |
| 40 | + public function getLimitReport() { |
55 | 41 | $this->load(); |
56 | 42 | |
57 | 43 | $usage = $this->sandbox->getMemoryUsage(); |
— | — | @@ -69,12 +55,16 @@ |
70 | 56 | $args = func_get_args(); |
71 | 57 | if( count( $args ) < 1 ) { |
72 | 58 | // FIXME: LuaSandbox PHP extension should provide proper context |
73 | | - throw new ScriptingException( 'toofewargs', 'common', null, null, array( 'mw.import' ) ); |
| 59 | + throw new ScriptingException( 'scripting-common-toofewargs', |
| 60 | + array( 'args' => array( 'mw.import' ) ) ); |
74 | 61 | } |
75 | 62 | |
76 | | - $module = $this->getModule( $args[0] ); |
77 | | - $module->initialize(); |
78 | | - return $module->contents; |
| 63 | + $title = Title::makeTitleSafe( NS_MODULE, $args[0] ); |
| 64 | + if ( !$title ) { |
| 65 | + throw new ScriptingException( 'scripting-common-nosuchmodule' ); |
| 66 | + } |
| 67 | + $module = $this->fetchModuleFromParser( $title ); |
| 68 | + return $module->getContents(); |
79 | 69 | } |
80 | 70 | } |
81 | 71 | |
— | — | @@ -93,20 +83,21 @@ |
94 | 84 | $this->body = $this->engine->sandbox->loadString( |
95 | 85 | $this->code, |
96 | 86 | // Prepending an "@" to the chunk name makes Lua think it is a file name |
97 | | - '@' . $this->getTitle()->getPrefixedDBkey() ); |
| 87 | + '@' . $this->chunkName ); |
98 | 88 | $output = $this->body->call(); |
99 | 89 | } catch( LuaSandboxError $e ) { |
100 | | - throw new ScriptingException( 'error', 'luasandbox', null, null, array( $e->getMessage() ) ); |
| 90 | + throw new ScriptingException( 'scripting-luasandbox-error', |
| 91 | + array( 'args' => array( $e->getMessage() ) ) ); |
101 | 92 | } |
102 | 93 | |
103 | 94 | if( !$output ) { |
104 | | - throw new ScriptingException( 'noreturn', 'luasandbox' ); |
| 95 | + throw new ScriptingException( 'scripting-luasandbox-noreturn' ); |
105 | 96 | } |
106 | 97 | if( count( $output ) > 2 ) { |
107 | | - throw new ScriptingException( 'toomanyreturns', 'luasandbox' ); |
| 98 | + throw new ScriptingException( 'scripting-luasandbox-toomanyreturns' ); |
108 | 99 | } |
109 | 100 | if( !is_array( $output[0] ) ) { |
110 | | - throw new ScriptingException( 'notarrayreturn', 'luasandbox' ); |
| 101 | + throw new ScriptingException( 'scripting-luasandbox-notarrayreturn' ); |
111 | 102 | } |
112 | 103 | |
113 | 104 | $this->contents = $output[0]; |
— | — | @@ -133,6 +124,11 @@ |
134 | 125 | $this->initialize(); |
135 | 126 | return $this->functions; |
136 | 127 | } |
| 128 | + |
| 129 | + function getContents() { |
| 130 | + $this->initialize(); |
| 131 | + return $this->contents; |
| 132 | + } |
137 | 133 | } |
138 | 134 | |
139 | 135 | class LuaSandboxEngineFunction extends ScriptingFunctionBase { |
— | — | @@ -140,7 +136,8 @@ |
141 | 137 | try { |
142 | 138 | $result = call_user_func_array( array( $this->contents, 'call' ), $args ); |
143 | 139 | } catch( LuaSandboxError $e ) { |
144 | | - throw new ScriptingException( 'error', 'luasandbox', null, null, array( $e->getMessage() ) ); |
| 140 | + throw new ScriptingException( 'scripting-luasandbox-error', |
| 141 | + array( 'args' => array( $e->getMessage() ) ) ); |
145 | 142 | } |
146 | 143 | |
147 | 144 | if ( isset( $result[0] ) ) { |