r110208 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r110207‎ | r110208 | r110209 >
Date:16:22, 28 January 2012
Author:vvv
Status:deferred (Comments)
Tags:
Comment:
Commiting the initial revision of the Scripting extension, a framework that allows programming languages to be embedded regardless of what are those languages actually are and how they are interpreted.

This is a very raw version, and it misses most messages (even in English!) and probably needs some more work.
Modified paths:
  • /trunk/extensions/Scripting (added) (history)
  • /trunk/extensions/Scripting/Scripting.i18n.php (added) (history)
  • /trunk/extensions/Scripting/Scripting.php (added) (history)
  • /trunk/extensions/Scripting/common (added) (history)
  • /trunk/extensions/Scripting/common/Base.php (added) (history)
  • /trunk/extensions/Scripting/common/Common.php (added) (history)
  • /trunk/extensions/Scripting/common/Hooks.php (added) (history)
  • /trunk/extensions/Scripting/common/LinkUpdates.php (added) (history)
  • /trunk/extensions/Scripting/engines (added) (history)
  • /trunk/extensions/Scripting/engines/LuaSandbox (added) (history)
  • /trunk/extensions/Scripting/engines/LuaSandbox/Engine.php (added) (history)
  • /trunk/extensions/Scripting/i18n (added) (history)
  • /trunk/extensions/Scripting/i18n/Magic.php (added) (history)
  • /trunk/extensions/Scripting/i18n/Messages.php (added) (history)
  • /trunk/extensions/Scripting/i18n/Namespaces.php (added) (history)
  • /trunk/extensions/Scripting/scripting.sql (added) (history)

Diff [purge]

Index: trunk/extensions/Scripting/i18n/Namespaces.php
@@ -0,0 +1,8 @@
 2+<?php
 3+
 4+$namespaceNames = array();
 5+
 6+$namespaceNames['en'] = array(
 7+ NS_MODULE => 'Module',
 8+ NS_MODULE_TALK => 'Module_talk',
 9+);
Property changes on: trunk/extensions/Scripting/i18n/Namespaces.php
___________________________________________________________________
Added: svn:eol-style
110 + native
Index: trunk/extensions/Scripting/i18n/Magic.php
@@ -0,0 +1,9 @@
 2+<?php
 3+
 4+$magicWords = array();
 5+
 6+$magicWords['en'] = array(
 7+ 'invoke' => array( 0, 'invoke' ),
 8+ 'script' => array( 0, 'script' ),
 9+);
 10+
Property changes on: trunk/extensions/Scripting/i18n/Magic.php
___________________________________________________________________
Added: svn:eol-style
111 + native
Index: trunk/extensions/Scripting/i18n/Messages.php
@@ -0,0 +1,18 @@
 2+<?php
 3+/**
 4+ * Internationalisation file for extension Scripting.
 5+ *
 6+ * @file
 7+ * @ingroup Extensions
 8+ */
 9+
 10+$messages = array();
 11+
 12+/** English
 13+ * @author Victor Vasiliev
 14+ */
 15+$messages['en'] = array(
 16+ 'scripting-desc' => 'Framework for embedding scripting languages into MediaWiki pages',
 17+
 18+ 'scripting-exception-luasandbox-error' => 'Lua error: $1',
 19+);
Property changes on: trunk/extensions/Scripting/i18n/Messages.php
___________________________________________________________________
Added: svn:eol-style
120 + native
Index: trunk/extensions/Scripting/scripting.sql
@@ -0,0 +1,16 @@
 2+--
 3+-- Track script inclusions.
 4+--
 5+CREATE TABLE /*_*/scriptlinks (
 6+ -- Key to the page_id of the page containing the link.
 7+ sl_from int unsigned NOT NULL default 0,
 8+
 9+ -- Key to page_title of the target page.
 10+ -- The target page may or may not exist, and due to renames
 11+ -- and deletions may refer to different page records as time
 12+ -- goes by.
 13+ sl_to varchar(255) binary NOT NULL default ''
 14+) /*$wgDBTableOptions*/;
 15+
 16+CREATE UNIQUE INDEX /*i*/sl_from ON /*_*/scriptlinks (sl_from,sl_to);
 17+CREATE UNIQUE INDEX /*i*/sl_title ON /*_*/scriptlinks (sl_to,sl_from);
Property changes on: trunk/extensions/Scripting/scripting.sql
___________________________________________________________________
Added: svn:eol-style
118 + native
Index: trunk/extensions/Scripting/Scripting.i18n.php
Property changes on: trunk/extensions/Scripting/Scripting.i18n.php
___________________________________________________________________
Added: svn:eol-style
219 + native
Index: trunk/extensions/Scripting/Scripting.php
@@ -0,0 +1,102 @@
 2+<?php
 3+/**
 4+ * Wikitext scripting infrastructure for MediaWiki.
 5+ * Copyright (C) 2009-2012 Victor Vasiliev <vasilvv@gmail.com>
 6+ * http://www.mediawiki.org/
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License along
 19+ * with this program; if not, write to the Free Software Foundation, Inc.,
 20+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 21+ * http://www.gnu.org/copyleft/gpl.html
 22+ */
 23+
 24+if( !defined( 'MEDIAWIKI' ) )
 25+ die();
 26+
 27+$wgExtensionCredits['parserhook']['Scripting'] = array(
 28+ 'path' => __FILE__,
 29+ 'name' => 'Scripting',
 30+ 'author' => 'Victor Vasiliev',
 31+ 'descriptionmsg' => 'scripting-desc',
 32+ 'url' => 'https://www.mediawiki.org/wiki/Extension:Scripting',
 33+);
 34+
 35+$dir = dirname(__FILE__) . '/';
 36+$wgExtensionMessagesFiles['Scripting'] = $dir . 'i18n/Messages.php';
 37+$wgExtensionMessagesFiles['ScriptingMagic'] = $dir . 'i18n/Magic.php';
 38+$wgExtensionMessagesFiles['ScriptingNamespaces'] = $dir . 'i18n/Namespaces.php';
 39+
 40+$wgAutoloadClasses['ScriptingEngineBase'] = $dir.'common/Base.php';
 41+$wgAutoloadClasses['ScriptingModuleBase'] = $dir.'common/Base.php';
 42+$wgAutoloadClasses['ScriptingFunctionBase'] = $dir.'common/Base.php';
 43+$wgAutoloadClasses['ScriptingHooks'] = $dir.'common/Hooks.php';
 44+$wgAutoloadClasses['ScriptLinksUpdateHooks'] = $dir.'common/LinkUpdates.php';
 45+$wgAutoloadClasses['ScriptingException'] = $dir.'common/Common.php';
 46+$wgAutoloadClasses['Scripting'] = $dir.'common/Common.php';
 47+
 48+$wgHooks['ParserFirstCallInit'][] = 'ScriptingHooks::setupParserHook';
 49+$wgHooks['ParserLimitReport'][] = 'ScriptingHooks::reportLimits';
 50+$wgHooks['ParserClearState'][] = 'ScriptingHooks::clearState';
 51+$wgHooks['ParserTestTables'][] = 'ScriptingHooks::addTestTables';
 52+
 53+$wgHooks['CanonicalNamespaces'][] = 'ScriptingHooks::addCanonicalNamespaces';
 54+$wgHooks['ArticleViewCustom'][] = 'ScriptingHooks::handleScriptView';
 55+$wgHooks['TitleIsWikitextPage'][] = 'ScriptingHooks::isWikitextPage';
 56+$wgHooks['EditFilter'][] = 'ScriptingHooks::validateScript';
 57+
 58+$wgHooks['LinksUpdate'][] = 'ScriptLinksUpdateHooks::updateLinks';
 59+$wgHooks['ArticleEditUpdates'][] = 'ScriptLinksUpdateHooks::purgeCache';
 60+$wgHooks['ParserAfterTidy'][] = 'ScriptLinksUpdateHooks::appendToOutput';
 61+$wgHooks['BacklinkCacheGetPrefix'][] = 'ScriptLinksUpdateHooks::getBacklinkCachePrefix';
 62+$wgHooks['BacklinkCacheGetConditions'][] = 'ScriptLinksUpdateHooks::getBacklinkCacheConditions';
 63+
 64+/***** Individual engines and their configurations *****/
 65+
 66+/**
 67+ * Available scripting engines.
 68+ */
 69+$wgScriptingEngines = array(
 70+ 'luasandbox' => 'LuaSandboxEngine',
 71+);
 72+
 73+$wgAutoloadClasses['LuaSandboxEngine'] = $dir.'engines/LuaSandbox/Engine.php';
 74+
 75+/***** Configuration *****/
 76+
 77+/**
 78+ * The name of the scripting engine.
 79+ */
 80+$wgScriptingEngine = false;
 81+
 82+
 83+$wgScriptingEngineConf = array();
 84+
 85+/**
 86+ * Script namespace numbers. Should be redefined before
 87+ * the inlcusion of the extension.
 88+ */
 89+if( !isset( $wgScriptingNamespaceNumbers ) ) {
 90+ $wgScriptingNamespaceNumbers = array(
 91+ 'Module' => 20,
 92+ 'Module_talk' => 21,
 93+ );
 94+}
 95+
 96+/**
 97+ * Turn on to true if you have linked or copied wikiscripts.php and
 98+ * SyntaxHighlight_GeSHi extension is enabled.
 99+ */
 100+$wgScriptingUseGeSHi = false;
 101+
 102+define( 'NS_MODULE', $wgScriptingNamespaceNumbers['Module'] );
 103+define( 'NS_MODULE_TALK', $wgScriptingNamespaceNumbers['Module_talk'] );
Property changes on: trunk/extensions/Scripting/Scripting.php
___________________________________________________________________
Added: svn:eol-style
1104 + native
Index: trunk/extensions/Scripting/common/Base.php
@@ -0,0 +1,251 @@
 2+<?php
 3+
 4+/**
 5+ * Wikitext scripting infrastructure for MediaWiki: base classes.
 6+ * Copyright (C) 2012 Victor Vasiliev <vasilvv@gmail.com> et al
 7+ * Based on MediaWiki file LinksUpdate.php
 8+ * http://www.mediawiki.org/
 9+ *
 10+ * This program is free software; you can redistribute it and/or modify
 11+ * it under the terms of the GNU General Public License as published by
 12+ * the Free Software Foundation; either version 2 of the License, or
 13+ * (at your option) any later version.
 14+ *
 15+ * This program is distributed in the hope that it will be useful,
 16+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 18+ * GNU General Public License for more details.
 19+ *
 20+ * You should have received a copy of the GNU General Public License along
 21+ * with this program; if not, write to the Free Software Foundation, Inc.,
 22+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 23+ * http://www.gnu.org/copyleft/gpl.html
 24+ */
 25+
 26+/**
 27+ * Base class for all scripting engines. Includes all code
 28+ * not related to particular modules, like tracking links between
 29+ * modules or loading module texts.
 30+ */
 31+abstract class ScriptingEngineBase {
 32+ protected
 33+ $mParser,
 34+ $mModules = array(),
 35+ $mModuleTitles = array(),
 36+ $mLoaded = false;
 37+
 38+ /**
 39+ * Required for the lazy-loading of the engine. Should have a sentinel
 40+ * inside checking whether it is already loaded.
 41+ */
 42+ abstract public function load();
 43+
 44+ /**
 45+ * Returns the name of your module class.
 46+ */
 47+ abstract protected function getModuleClassName();
 48+
 49+ /**
 50+ * Returns the default options of the engine.
 51+ */
 52+ abstract public function getDefaultOptions();
 53+
 54+ /**
 55+ * Is called by setOptions() in order to notify the engine
 56+ * that the options were changed.
 57+ */
 58+ protected function updateOptions() { /* No-op */ }
 59+
 60+ /**
 61+ * Constructor.
 62+ *
 63+ * @param $parser Parser Wikitext parser
 64+ */
 65+ public final function __construct( $parser ) {
 66+ $this->mParser = $parser;
 67+ }
 68+
 69+ /**
 70+ * Loads the module either from instance cache or from the actual revision.
 71+ * Does not initialize the module, i.e. do not expect it to complain if the module
 72+ * text is garbage or has syntax error. Returns a module or throws an exception.
 73+ *
 74+ * @param $title Title/string The title or the name of the module.
 75+ * @param $source string Source of the module
 76+ * @return ScriptingEngineModule
 77+ */
 78+ public function getModule( $title, $source = Scripting::Local ) {
 79+ // Convert string to title
 80+ if( !$title instanceof Title ) {
 81+ $titleobj = Title::newFromText( (string)$title, NS_MODULE );
 82+ if( !$titleobj || $titleobj->getNamespace() != NS_MODULE ) {
 83+ throw new ScriptingException( 'badtitle', 'common' ); // scripting-exceptions-common-badtitle
 84+ }
 85+ $title = $titleobj;
 86+ }
 87+
 88+ // Check if it is already loaded
 89+ $key = $title->getPrefixedText();
 90+ if( !isset( $this->mModules[$key] ) ) {
 91+ // Fetch the text
 92+ $rev = $this->getModuleRev( $title, $source );
 93+ if( !$rev ) {
 94+ throw new ScriptingException( 'nosuchmodule', 'common' ); // scripting-exceptions-common-nosuchmodule
 95+ }
 96+ if( $rev->getTitle()->getNamespace() != NS_MODULE ) {
 97+ throw new ScriptingException( 'badnamespace', 'common' ); // scripting-exceptions-common-badnamespace
 98+ }
 99+
 100+ // Create the class
 101+ $class = $this->getModuleClassName();
 102+ $this->mModules[$key] = new $class( $this, $title, $rev->getText(), $rev->getID(), $source );
 103+ $this->mModuleTitles[] = $title;
 104+ }
 105+ return $this->mModules[$key];
 106+ }
 107+
 108+ /**
 109+ * Fetches the revision for given module title.
 110+ */
 111+ private function getModuleRev( $title, $source ) {
 112+ if( $source != Scripting::Local ) {
 113+ throw new MWException( 'Non-local scripts are not supported at this point' );
 114+ }
 115+
 116+ $rev = Revision::newFromTitle( $title );
 117+ if( $rev && $real = Title::newFromRedirect( $rev->getText() ) ) {
 118+ $rev = Revision::newFromTitle( $real );
 119+ }
 120+ return $rev;
 121+ }
 122+
 123+ /**
 124+ * Sets the engine-specific options from $wgScriptingEngineConf.
 125+ */
 126+ function setOptions( $options ) {
 127+ $this->mOptions = array_merge( $this->getDefaultOptions(), $options );
 128+ $this->updateOptions();
 129+ }
 130+
 131+ /**
 132+ * Validates the script and returns an array of the syntax erros for the
 133+ * given code.
 134+ *
 135+ * @param $code Code to validate
 136+ * @param $title Title of the code page
 137+ * @return array
 138+ */
 139+ function validate( $code, $title ) {
 140+ $class = $this->getModuleClassName();
 141+ $module = new $class( $this, $title, $code, 0, Scripting::Local );
 142+
 143+ try {
 144+ $module->initialize();
 145+ } catch( ScriptingException $e ) {
 146+ return array( $e->getMessage() );
 147+ }
 148+
 149+ return array();
 150+ }
 151+
 152+ /**
 153+ * Allows the engine to append their information to the limits
 154+ * report.
 155+ */
 156+ public function getLimitsReport() {
 157+ /* No-op by default */
 158+ return '';
 159+ }
 160+
 161+ /**
 162+ * Returns the titles of all the modules used by this instance of the
 163+ * engine.
 164+ */
 165+ public function getUsedModules() {
 166+ return $this->mModuleTitles;
 167+ }
 168+
 169+ /**
 170+ * Invalidates the cache of the given module by its title. Should be
 171+ * redefined if the engine uses any form of bytecode or other cache.
 172+ */
 173+ function invalidateModuleCache( $title ) {
 174+ /* No-op by default */
 175+ }
 176+
 177+ /**
 178+ * Get the language code for GeSHi syntax highlighter.
 179+ */
 180+ function getGeSHiLangauge() {
 181+ return false;
 182+ }
 183+}
 184+
 185+/**
 186+ * Class that represents a module. Responsible for initial module parsing
 187+ * and maintaining the contents of the module.
 188+ */
 189+abstract class ScriptingModuleBase {
 190+ var $mEngine, $mTitle, $mCode, $mRevisionID, $mSource;
 191+
 192+ public final function __construct( $engine, $title, $code, $revisionID, $source ) {
 193+ $this->mEngine = $engine;
 194+ $this->mTitle = $title;
 195+ $this->mCode = $code;
 196+ $this->mRevisionID = $revisionID;
 197+ $this->mSource = $source;
 198+ }
 199+
 200+ /** Accessors **/
 201+ public function getEngine() { return $this->mEngine; }
 202+ public function getTitle() { return $this->mTitle; }
 203+ public function getCode() { return $this->mCode; }
 204+ public function getRevisionID() { return $this->mRevisionID; }
 205+ public function getSource() { return $this->mSource; }
 206+
 207+ /**
 208+ * Initialize the module. That means parse it and load the
 209+ * functions/constants/whatever into the object.
 210+ *
 211+ * Protection of double-initialization is the responsibility of this method.
 212+ */
 213+ abstract function initialize();
 214+
 215+ /**
 216+ * Returns the object for a given function. Should return null if it does not exist.
 217+ *
 218+ * @return ScriptingFunctionBase or null
 219+ */
 220+ abstract function getFunction( $name );
 221+
 222+ /**
 223+ * Returns the list of the functions in the module.
 224+ *
 225+ * @return array(string)
 226+ */
 227+ abstract function getFunctions();
 228+}
 229+
 230+abstract class ScriptingFunctionBase {
 231+ protected $mName, $mContents, $mModule, $mEngine;
 232+
 233+ public final function __construct( $name, $contents, $module, $engine ) {
 234+ $this->mName = $name;
 235+ $this->mContents = $contents;
 236+ $this->mModule = $module;
 237+ $this->mEngine = $engine;
 238+ }
 239+
 240+ /**
 241+ * Calls the function. Returns its first result or null if no result.
 242+ *
 243+ * @param $args array Arguments to the function
 244+ * @param $frame PPFrame
 245+ */
 246+ abstract public function call( $args, $frame );
 247+
 248+ /** Accessors **/
 249+ public function getName() { return $this->mName; }
 250+ public function getModule() { return $this->mModule; }
 251+ public function getEngine() { return $this->mEngine; }
 252+}
Property changes on: trunk/extensions/Scripting/common/Base.php
___________________________________________________________________
Added: svn:eol-style
1253 + native
Index: trunk/extensions/Scripting/common/Hooks.php
@@ -0,0 +1,224 @@
 2+<?php
 3+/**
 4+ * Wikitext scripting infrastructure for MediaWiki: hooks.
 5+ * Copyright (C) 2009-2012 Victor Vasiliev <vasilvv@gmail.com>
 6+ * http://www.mediawiki.org/
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License along
 19+ * with this program; if not, write to the Free Software Foundation, Inc.,
 20+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 21+ * http://www.gnu.org/copyleft/gpl.html
 22+ */
 23+
 24+/**
 25+ * Hooks for Scripting extension.
 26+ */
 27+class ScriptingHooks {
 28+ /**
 29+ * Register parser hooks.
 30+ * @param $parser Parser
 31+ */
 32+ public static function setupParserHook( &$parser ) {
 33+ $parser->setFunctionHook( 'invoke', 'ScriptingHooks::callHook', SFH_OBJECT_ARGS );
 34+ $parser->setFunctionHook( 'script', 'ScriptingHooks::transcludeHook', SFH_NO_HASH | SFH_OBJECT_ARGS );
 35+ return true;
 36+ }
 37+
 38+ /**
 39+ * Called when interpreter is to be reset.
 40+ *
 41+ * @static
 42+ * @param $parser Parser
 43+ * @return bool
 44+ */
 45+ public static function clearState( &$parser ) {
 46+ Scripting::resetEngine( $parser );
 47+ return true;
 48+ }
 49+
 50+ /**
 51+ * Adds scriptlinks table to parser tests.
 52+ */
 53+ public static function addTestTables( &$tables ) {
 54+ $tables[] = 'scriptlinks';
 55+ return true;
 56+ }
 57+
 58+ /**
 59+ * Handles the {{#invoke:module|func}} construction.
 60+ *
 61+ * @static
 62+ * @param $parser Parser
 63+ * @param $frame
 64+ * @param $args
 65+ * @return string
 66+ */
 67+ public static function callHook( &$parser, $frame, $args ) {
 68+ if( count( $args ) < 2 ) {
 69+ throw new ScriptingException( 'nofunction', 'common' ); // scripting-exceptions-common-nofunction
 70+ }
 71+
 72+ $module = $parser->mStripState->unstripBoth( array_shift( $args ) );
 73+ $function = $frame->expand( array_shift( $args ) );
 74+ return self::doRunHook( $parser, $frame, $module, $function, $args );
 75+ }
 76+
 77+ /**
 78+ * Handles the transclusion of the script ({{script:module}} hook).
 79+ *
 80+ * @static
 81+ * @param $parser Parser
 82+ * @param $frame
 83+ * @param $args
 84+ * @return string
 85+ */
 86+ public static function transcludeHook( &$parser, $frame, $args ) {
 87+ $module = $parser->mStripState->unstripBoth( array_shift( $args ) );
 88+ return self::doRunHook( $parser, $frame, $module, 'main', $args );
 89+ }
 90+
 91+ private static function doRunHook( &$parser, $frame, $module, $function, $args ) {
 92+ wfProfileIn( __METHOD__ );
 93+
 94+ try {
 95+ $engine = Scripting::getEngine( $parser );
 96+
 97+ foreach( $args as &$arg ) {
 98+ $arg = $frame->expand( $arg );
 99+ }
 100+
 101+ $module = $engine->getModule( $module, Scripting::Local );
 102+
 103+ $functionObj = $module->getFunction( $function );
 104+ if( !$functionObj ) {
 105+ throw new ScriptingException( 'nosuchfunction', 'common' ); // scripting-exceptions-common-nosuchfunction
 106+ }
 107+
 108+ $result = $functionObj->call( $args, $frame );
 109+
 110+ wfProfileOut( __METHOD__ );
 111+ return trim( $result );
 112+ } catch( ScriptingException $e ) {
 113+ $msg = $e->getMessage();
 114+ wfProfileOut( __METHOD__ );
 115+ return "<strong class=\"error\">{$msg}</strong>";
 116+ }
 117+ }
 118+
 119+ /**
 120+ * Overrides the standard view for modules. Enables syntax highlighting when
 121+ * possible.
 122+ *
 123+ * @static
 124+ * @param $text
 125+ * @param $title Title
 126+ * @param $output OutputPage
 127+ * @return bool
 128+ */
 129+ public static function handleScriptView( $text, $title, $output ) {
 130+ global $wgScriptingUseGeSHi, $wgParser;
 131+
 132+ if( $title->getNamespace() == NS_MODULE ) {
 133+ $engine = Scripting::getEngine( $wgParser );
 134+ $language = $engine->getGeSHiLangauge();
 135+
 136+ if( $wgScriptingUseGeSHi && $language ) {
 137+ $geshi = SyntaxHighlight_GeSHi::prepare( $text, $language );
 138+ $geshi->set_language( $language );
 139+ if( $geshi instanceof GeSHi && !$geshi->error() ) {
 140+ $code = $geshi->parse_code();
 141+ if( $code ) {
 142+ $output->addHeadItem( "source-{$language}", SyntaxHighlight_GeSHi::buildHeadItem( $geshi ) );
 143+ $output->addHTML( "<div dir=\"ltr\">{$code}</div>" );
 144+ return false;
 145+ }
 146+ }
 147+ }
 148+
 149+ // No GeSHi, or GeSHi can't parse it, use plain <pre>
 150+ $output->addHTML( "<pre class=\"mw-code mw-script\" dir=\"ltr\">\n" );
 151+ $output->addHTML( htmlspecialchars( $text ) );
 152+ $output->addHTML( "\n</pre>\n" );
 153+ return false;
 154+ } else {
 155+ return true;
 156+ }
 157+ }
 158+
 159+ /**
 160+ * Indicates that modules are not wikitext.
 161+ */
 162+ public static function isWikitextPage( $title, &$result ) {
 163+ if( $title->getNamespace() == NS_MODULE ) {
 164+ $result = false;
 165+ return false;
 166+ }
 167+ return true;
 168+ }
 169+
 170+ /**
 171+ * Adds report of number of evaluations by the single wikitext page.
 172+ *
 173+ * @static
 174+ * @param $parser Parser
 175+ * @param $report
 176+ * @return bool
 177+ */
 178+ public static function reportLimits( $parser, &$report ) {
 179+ # FIXME
 180+ global $wgScriptsLimits;
 181+ $engine = Scripting::getEngine( $parser );
 182+ $report .= $engine->getLimitsReport();
 183+ return true;
 184+ }
 185+
 186+ /**
 187+ * Adds the module namespaces.
 188+ */
 189+ public static function addCanonicalNamespaces( &$list ) {
 190+ $list[NS_MODULE] = 'Module';
 191+ $list[NS_MODULE_TALK] = 'Module_talk';
 192+ return true;
 193+ }
 194+
 195+ public static function validateScript( $editor, $text, $section, &$error ) {
 196+ global $wgUser, $wgParser;
 197+ $title = $editor->mTitle;
 198+
 199+ if( $title->getNamespace() == NS_MODULE ) {
 200+ $engine = Scripting::getEngine( $wgParser );
 201+ $errors = $engine->validate( $text, $title );
 202+ if( !$errors ) {
 203+ return true;
 204+ }
 205+
 206+ $errmsg = wfMsgExt( 'scripting-error', array( 'parsemag' ), array( count( $errors ) ) );
 207+ if( count( $errors ) == 1 ) {
 208+ $errlines = ': ' . wfEscapeWikiText( $errors[0] );
 209+ } else {
 210+ $errlines = '* ' . implode( "\n* ", array_map( 'wfEscapeWikiText', $errors ) );
 211+ }
 212+ $error = <<<HTML
 213+<div class="errorbox">
 214+{$errmsg}
 215+{$errlines}
 216+</div>
 217+<br clear="all" />
 218+HTML;
 219+
 220+ return true;
 221+ }
 222+
 223+ return true;
 224+ }
 225+}
Property changes on: trunk/extensions/Scripting/common/Hooks.php
___________________________________________________________________
Added: svn:eol-style
1226 + native
Index: trunk/extensions/Scripting/common/LinkUpdates.php
@@ -0,0 +1,201 @@
 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+ * Appends 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+ * Runs 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+ * Purges cache for all the pages where the script is used.
 62+ */
 63+ public static function purgeCache( &$article, &$editInfo, $changed ) {
 64+ global $wgDeferredUpdateList, $wgParser;
 65+
 66+ if( $article->mTitle->getNamespace() == NS_MODULE ) {
 67+ // Invalidate the script cache
 68+ $engine = Scripting::getEngine( $wgParser );
 69+ $engine->invalidateModuleCache( $article->mTitle );
 70+
 71+ // Invalidate caches of articles which include the script
 72+ $wgDeferredUpdateList[] = new HTMLCacheUpdate( $article->mTitle, 'scriptlinks' );
 73+ }
 74+
 75+ return true;
 76+ }
 77+
 78+ /**
 79+ * Adds scriptlinks to the list of tables supported by BacklinkCache.
 80+ */
 81+ public static function getBacklinkCachePrefix( $table, &$prefix ) {
 82+ if( $table == 'scriptlinks' ) {
 83+ $prefix = 'sl';
 84+ return false;
 85+ } else {
 86+ return true;
 87+ }
 88+ }
 89+
 90+ /**
 91+ * Adds scriptlinks to the list of tables supported by BacklinkCache.
 92+ */
 93+ public static function getBacklinkCacheConditions( $table, $title, &$conds ) {
 94+ if( $table == 'scriptlinks' ) {
 95+ $conds = array(
 96+ 'sl_to' => $title->getDBkey(),
 97+ 'page_id=sl_from'
 98+ );
 99+ return false;
 100+ } else {
 101+ return true;
 102+ }
 103+ }
 104+}
 105+
 106+/**
 107+ * A class that updates links on scripts like phase3/includes/LinksUpdate.php does that
 108+ * with templates.
 109+ */
 110+class ScriptLinksUpdate {
 111+ var $mUpdate, $mId, $mNew;
 112+
 113+ public function __construct( $update, $new ) {
 114+ $this->mUpdate = $update;
 115+ $this->mId = $update->mId;
 116+ $this->mNew = $new;
 117+ }
 118+
 119+ public function run() {
 120+ global $wgUseDumbLinkUpdate;
 121+
 122+ wfProfileIn( __METHOD__ );
 123+
 124+ if( $wgUseDumbLinkUpdate ) {
 125+ $this->mUpdate->dumbTableUpdate( 'scriptlinks', $this->getScriptInsertions(), 'sl_from' );
 126+ } else {
 127+ $existing = $this->getExistingScripts();
 128+ $this->mUpdate->incrTableUpdate( 'scriptlinks', 'sl', $this->getScriptDeletions( $existing ),
 129+ $this->getScriptInsertions( $existing ) );
 130+ }
 131+
 132+ if( $this->mUpdate->mRecursive && $this->mUpdate->mTitle->getNamespace() == NS_MODULE ) {
 133+ $this->queueRecursiveJobs();
 134+ }
 135+
 136+ wfProfileOut( __METHOD__ );
 137+ }
 138+
 139+ protected function getExistingScripts() {
 140+ $result = array();
 141+
 142+ $res = $this->mUpdate->mDb->select( 'scriptlinks', array( 'sl_to' ),
 143+ array( 'sl_from' => $this->mId ), __METHOD__, $this->mUpdate->mOptions );
 144+ foreach ( $res as $row ) {
 145+ $result[] = $row->sl_to;
 146+ }
 147+
 148+ return $result;
 149+ }
 150+
 151+ protected function getScriptInsertions( $existing = array() ) {
 152+ $result = array();
 153+
 154+ foreach( array_diff( $this->mNew, $existing ) as $module ) {
 155+ $result[] = array(
 156+ 'sl_from' => $this->mId,
 157+ 'sl_to' => $module,
 158+ );
 159+ }
 160+
 161+ return $result;
 162+ }
 163+
 164+ protected function getScriptDeletions( $existing = array() ) {
 165+ $result = array();
 166+
 167+ foreach( array_diff( $existing, $this->mNew ) as $module ) {
 168+ $result[] = array(
 169+ 'sl_from' => $this->mId,
 170+ 'sl_to' => $module,
 171+ );
 172+ }
 173+
 174+ return $result;
 175+ }
 176+
 177+ protected function queueRecursiveJobs() {
 178+ global $wgUpdateRowsPerJob;
 179+ wfProfileIn( __METHOD__ );
 180+
 181+ $cache = $this->mUpdate->mTitle->getBacklinkCache();
 182+ $batches = $cache->partition( 'scriptlinks', $wgUpdateRowsPerJob );
 183+ if ( !$batches ) {
 184+ wfProfileOut( __METHOD__ );
 185+ return;
 186+ }
 187+ $jobs = array();
 188+ foreach ( $batches as $batch ) {
 189+ list( $start, $end ) = $batch;
 190+ $params = array(
 191+ 'table' => 'scriptlinks',
 192+ 'start' => $start,
 193+ 'end' => $end,
 194+ );
 195+ $jobs[] = new RefreshLinksJob2( $this->mUpdate->mTitle, $params );
 196+ }
 197+ Job::batchInsert( $jobs );
 198+
 199+ wfProfileOut( __METHOD__ );
 200+ }
 201+}
 202+
Property changes on: trunk/extensions/Scripting/common/LinkUpdates.php
___________________________________________________________________
Added: svn:eol-style
1203 + native
Index: trunk/extensions/Scripting/common/Common.php
@@ -0,0 +1,63 @@
 2+<?php
 3+
 4+/**
 5+ * Generic scripting functions.
 6+ */
 7+class Scripting {
 8+ const Local = 'local';
 9+
 10+ protected static function getEngineClass() {
 11+ global $wgScriptingEngine, $wgScriptingEngines;
 12+
 13+ if( !$wgScriptingEngine ) {
 14+ throw new MWException( 'Scripting extension is enabled but $wgScriptingEngine is not set' );
 15+ }
 16+
 17+ if( !isset( $wgScriptingEngines[$wgScriptingEngine] ) ) {
 18+ throw new MWException( 'Invalid scripting engine is specified in $wgScriptingEngine' );
 19+ }
 20+
 21+ return $wgScriptingEngines[$wgScriptingEngine];
 22+ }
 23+
 24+ public static function getEngine( $parser ) {
 25+ global $wgScriptingEngineConf;
 26+
 27+ 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 );
 31+ }
 32+ return $parser->scripting_engine;
 33+ }
 34+
 35+ public static function resetEngine( $parser ) {
 36+ $parser->scripting_engine = null;
 37+ }
 38+}
 39+
 40+/**
 41+ * Exceptions which represents user-originating error in the script.
 42+ * Please do not use it for internal errors like "oh god, this should have never happened".
 43+ * Use casual MWException for that.
 44+ */
 45+class ScriptingException extends MWException {
 46+ function __construct( $exceptionID, $engine, $module = null, $line = null, $params = array() ) {
 47+ if( $module ) {
 48+ $codelocation = wfMsg( 'scripting-codelocation', $module, $line );
 49+ $msg = wfMsgExt( "scripting-exception-{$engine}-{$exceptionID}", array(), array_merge( array( $codelocation ), $params ) );
 50+ } else {
 51+ $msg = wfMsgExt( "scripting-exception-{$engine}-{$exceptionID}", array(), $params );
 52+ }
 53+ parent::__construct( $msg );
 54+
 55+ $this->mExceptionID = $exceptionID;
 56+ $this->mLine = $line;
 57+ $this->mModule = $module;
 58+ $this->mParams = $params;
 59+ }
 60+
 61+ public function getExceptionID() {
 62+ return $this->mExceptionID;
 63+ }
 64+}
Property changes on: trunk/extensions/Scripting/common/Common.php
___________________________________________________________________
Added: svn:eol-style
165 + native
Index: trunk/extensions/Scripting/engines/LuaSandbox/Engine.php
@@ -0,0 +1,140 @@
 2+<?php
 3+
 4+class LuaSandboxEngine extends ScriptingEngineBase {
 5+ public $mSandbox;
 6+
 7+ public function load() {
 8+ if( $this->mLoaded )
 9+ return;
 10+
 11+ if( !class_exists('luasandbox') ) {
 12+ throw new MWException( 'luasandbox PHP extension is not installed' );
 13+ }
 14+
 15+ $this->mSandbox = new LuaSandbox;
 16+ $this->mSandbox->setMemoryLimit( $this->mOptions['memoryLimit'] );
 17+ $this->mSandbox->setCPULimit( $this->mOptions['maxCPU'] );
 18+ $this->mSandbox->registerLibrary( 'mw', array( 'import' => array( $this, 'importModule' ) ) );
 19+
 20+ $this->mLoaded = true;
 21+ }
 22+
 23+ protected function updateOptions() {
 24+ if( $this->mLoaded ) {
 25+ $this->mSandbox->setMemoryLimit( $this->mOptions['memoryLimit'] );
 26+ $this->mSandbox->setCPULimit( $this->mOptions['maxCPU'] );
 27+ }
 28+ }
 29+
 30+ protected function getModuleClassName() {
 31+ return 'LuaSandboxEngineModule';
 32+ }
 33+
 34+ public function getDefaultOptions() {
 35+ return array(
 36+ 'memoryLimit' => 50 * 1024 * 1024,
 37+ 'maxCPU' => 7,
 38+ );
 39+ }
 40+
 41+ public function getGeSHiLangauge() {
 42+ return 'lua';
 43+ }
 44+
 45+ public function getLimitsReport() {
 46+ $this->load();
 47+
 48+ $usage = $this->mSandbox->getMemoryUsage();
 49+ if( $usage < 8 * 1024 ) {
 50+ $usageStr = $usage . " bytes";
 51+ } elseif( $usage < 8 * 1024 * 1024 ) {
 52+ $usageStr = round( $usage / 1024, 2 ) . " kilobytes";
 53+ } else {
 54+ $usageStr = round( $usage / 1024 / 1024, 2 ) . " megabytes";
 55+ }
 56+
 57+ return "Lua scripts memory usage: {$usageStr}\n";
 58+ }
 59+
 60+ function importModule() {
 61+ // FIXME: luasandbox segfaults on exceptions
 62+ try {
 63+ $args = func_get_args();
 64+ if( count( $args ) < 1 ) {
 65+ // FIXME: LuaSandbox PHP extension should provide proper context
 66+ throw new ScriptingException( 'toofewargs', 'common', null, null, array( 'mw.import' ) );
 67+ }
 68+
 69+ $module = $this->getModule( $args[0] );
 70+ $module->initialize();
 71+ return $module->mContents;
 72+ } catch( ScriptingException $e ) {
 73+ return null;
 74+ }
 75+ }
 76+}
 77+
 78+class LuaSandboxEngineModule extends ScriptingModuleBase {
 79+ protected $mInitialized;
 80+
 81+ function initialize() {
 82+ if( $this->mInitialized )
 83+ return;
 84+ $this->mEngine->load();
 85+
 86+ // FIXME: caching?
 87+
 88+ try {
 89+ $this->mBody = $this->mEngine->mSandbox->loadString( $this->mCode );
 90+ $output = $this->mBody->call();
 91+ } catch( LuaSandboxError $e ) {
 92+ throw new ScriptingException( 'error', 'luasandbox', null, null, array( $e->getMessage() ) );
 93+ }
 94+
 95+ if( !$output ) {
 96+ throw new ScriptingException( 'noreturn', 'luasandbox' );
 97+ }
 98+ if( count( $output ) > 2 ) {
 99+ throw new ScriptingException( 'toomanyreturns', 'luasandbox' );
 100+ }
 101+ if( !is_array( $output[0] ) ) {
 102+ throw new ScriptingException( 'notarrayreturn', 'luasandbox' );
 103+ }
 104+
 105+ $this->mContents = $output[0];
 106+ $this->mFunctions = array();
 107+ foreach( $this->mContents as $key => $content ) {
 108+ if( $content instanceof LuaSandboxFunction )
 109+ $this->mFunctions[] = $key;
 110+ }
 111+
 112+ $this->mInitialized = true;
 113+ }
 114+
 115+ function getFunction( $name ) {
 116+ $this->initialize();
 117+
 118+ if( isset( $this->mContents[$name] ) ) {
 119+ return new LuaSandboxEngineFunction( $name, $this->mContents[$name], $this, $this->mEngine );
 120+ } else {
 121+ return null;
 122+ }
 123+ }
 124+
 125+ function getFunctions() {
 126+ $this->initialize();
 127+ return $this->mFunctions;
 128+ }
 129+}
 130+
 131+class LuaSandboxEngineFunction extends ScriptingFunctionBase {
 132+ public function call( $args, $frame ) {
 133+ try {
 134+ $result = call_user_func_array( array( $this->mContents, 'call' ), $args );
 135+ } catch( LuaSandboxError $e ) {
 136+ throw new ScriptingException( 'error', 'luasandbox', null, null, array( $e->getMessage() ) );
 137+ }
 138+
 139+ return implode( '', $result );
 140+ }
 141+}
Property changes on: trunk/extensions/Scripting/engines/LuaSandbox/Engine.php
___________________________________________________________________
Added: svn:eol-style
1142 + native

Comments

#Comment by Platonides (talk | contribs)   17:07, 29 January 2012

You are creating a new namespace for modules.

Then, there's no need for a new table in scripting.sql


if( !isset( $wgScriptingNamespaceNumbers ) ) {

This is a register globals vulnerability.

#Comment by VasilievVV (talk | contribs)   17:13, 29 January 2012

> You are creating a new namespace for modules. > Then, there's no need for a new table in scripting.sql

I fail to see the connection.

> This is a register globals vulnerability. No, since there's a safeguard in the beginning of the file.

#Comment by Platonides (talk | contribs)   17:59, 29 January 2012

Sorry, I wasn't clear enough. scriptlinks is just like pagelinks with pl_namespace = NS_MODULE

That it can only be called from MediaWiki doesn't defer the havoc that could be created with a register_globals = on if the user didn't explicitely set $wgScriptingNamespaceNumbers in LocalSettings.

#Comment by VasilievVV (talk | contribs)   19:36, 29 January 2012

> Sorry, I wasn't clear enough. scriptlinks is just like pagelinks with pl_namespace = NS_MODULE.

No, there's a dramatic difference between linking to script ([[Module:aa]]) and calling a script ({{#invoke:aa|funcname}}).

> That it can only be called from MediaWiki doesn't defer the havoc that could be created with a register_globals = on if the user didn't explicitely set $wgScriptingNamespaceNumbers in LocalSettings.

Well, that's the approach which most extensions with custom namespaces use. LiquidThreads does the same. It does introduce a little register_globals problem, but it is not significant (after all, it is 2012 now and that feature is marked deprecated for ages and will be thankfully removed in 5.4).

#Comment by Krinkle (talk | contribs)   19:39, 29 January 2012

Not most extensions written in this decade afaik.

Wikis should always include extensions first and set settings later. So it's perfectly fine (and encouraged) to just set $wgScriptingNamespaceNumbers and let localsettings override it.

#Comment by VasilievVV (talk | contribs)   19:51, 29 January 2012

Fixed the second one in r110256.

#Comment by Pokeswap (talk | contribs)   19:41, 29 January 2012

Call to undefined method CheckUser::checkPermissions

Backtrace:

  1. 0 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/extensions/CheckUser/CheckUser_body.php(22): SpecialPage->__call('checkPermission...', Array)
  2. 1 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/extensions/CheckUser/CheckUser_body.php(22): CheckUser->checkPermissions()
  3. 2 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/includes/SpecialPageFactory.php(458): CheckUser->execute(NULL)
  4. 3 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/includes/Wiki.php(226): SpecialPageFactory::executePath(Object(Title), Object(RequestContext))
  5. 4 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/includes/Wiki.php(626): MediaWiki->performRequest()
  6. 5 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/includes/Wiki.php(533): MediaWiki->main()
  7. 6 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/index.php(57): MediaWiki->run()
  8. 7 {main}Call to undefined method CheckUser::checkPermissions

Backtrace:

  1. 0 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/extensions/CheckUser/CheckUser_body.php(22): SpecialPage->__call('checkPermission...', Array)
  2. 1 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/extensions/CheckUser/CheckUser_body.php(22): CheckUser->checkPermissions()
  3. 2 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/includes/SpecialPageFactory.php(458): CheckUser->execute(NULL)
  4. 3 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/includes/Wiki.php(226): SpecialPageFactory::executePath(Object(Title), Object(RequestContext))
  5. 4 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/includes/Wiki.php(626): MediaWiki->performRequest()
  6. 5 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/includes/Wiki.php(533): MediaWiki->main()
  7. 6 /hermes/bosweb/web023/b239/d5.pokeswap/public_html/spyinc/wiki/index.php(57): MediaWiki->run()
  8. 7 {main}


hoe do i fix (jdk370@gmail.com) or http://spyinginc.com/wiki

#Comment by Platonides (talk | contribs)   20:24, 29 January 2012

How is it related to this revision? I think you wrote it in the wrong place.

Status & tagging log