diff options
author | Egil Moeller <egil.moller@freecode.no> | 2010-03-12 21:41:45 +0100 |
---|---|---|
committer | Egil Moeller <egil.moller@freecode.no> | 2010-03-12 21:41:45 +0100 |
commit | 24680e64cedb9e3f161fbf7c3fd59ced6e16808f (patch) | |
tree | ca83cfefda70b72e79d2a74c153f7e45ee30e3a0 /trunk | |
parent | c1894c8e0a52f4e3d2f89fa92f0066bbf0fcf1b1 (diff) | |
download | etherpad-24680e64cedb9e3f161fbf7c3fd59ced6e16808f.tar.gz etherpad-24680e64cedb9e3f161fbf7c3fd59ced6e16808f.tar.xz etherpad-24680e64cedb9e3f161fbf7c3fd59ced6e16808f.zip |
Got plugins and hooks to work
Diffstat (limited to 'trunk')
-rw-r--r-- | trunk/etherpad/src/etherpad/admin/plugins.js | 182 | ||||
-rw-r--r-- | trunk/etherpad/src/etherpad/control/admin/pluginmanager.js | 65 | ||||
-rw-r--r-- | trunk/etherpad/src/main.js | 8 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/kafoo/main.js | 8 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/testplugin/main.js | 27 | ||||
-rw-r--r-- | trunk/etherpad/src/templates/admin/pluginmanager.ejs | 126 | ||||
-rw-r--r-- | trunk/infrastructure/framework-src/modules/execution.js | 5 |
7 files changed, 420 insertions, 1 deletions
diff --git a/trunk/etherpad/src/etherpad/admin/plugins.js b/trunk/etherpad/src/etherpad/admin/plugins.js new file mode 100644 index 0000000..f48dbb5 --- /dev/null +++ b/trunk/etherpad/src/etherpad/admin/plugins.js @@ -0,0 +1,182 @@ +/** + * Copyright 2009 RedHog, Egil Möller <egil.moller@piratpartiet.se> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("faststatic"); +import("dispatch.{Dispatcher,PrefixMatcher,forward}"); + +import("etherpad.utils.*"); +import("etherpad.collab.server_utils"); +import("etherpad.globals.*"); +import("etherpad.log"); +import("etherpad.pad.padusers"); +import("etherpad.pro.pro_utils"); +import("etherpad.helpers"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("sqlbase.sqlbase"); +import("sqlbase.sqlcommon"); +import("sqlbase.sqlobj"); +import("exceptionutils"); +import("execution"); + +jimport("java.io.File", + "java.io.DataInputStream", + "java.io.FileInputStream", + "java.lang.Byte", + "java.io.FileReader", + "java.io.BufferedReader", + "net.appjet.oui.JarVirtualFile"); + +pluginsLoaded = false; +pluginModules = {}; +plugins = {}; +hooks = {}; + +function loadAvailablePlugin(pluginName) { + if (plugins[pluginName] != undefined) + return plugins[pluginName]; + + var pluginsDir = new Packages.java.io.File("src/plugins"); + + var pluginFile = new Packages.java.io.File(pluginsDir, pluginName + '/main.js'); + if (pluginFile.exists()) { + var pluginModulePath = pluginFile.getPath().replace(new RegExp("src/\(.*\)\.js"), "$1").replace("/", ".", "g"); + var importStmt = "import('" + pluginModulePath + "')"; + try { + return execution.fancyAssEval(importStmt, "main;"); + } catch (e) { + log.info({errorLoadingPlugin:exceptionutils.getStackTracePlain(e)}); + } + } + return null; +} + +function loadAvailablePlugins() { + var pluginsDir = new Packages.java.io.File("src/plugins"); + + var pluginNames = pluginsDir.list(); + + for (i = 0; i < pluginNames.length; i++) { + var plugin = loadAvailablePlugin(pluginNames[i]); + if (plugin != null) + pluginModules[pluginNames[i]] = plugin + } +} + +function loadInstalledHooks() { + var sql = '' + + 'select ' + + ' hook.name as hook, ' + + ' plugin.name as plugin, ' + + ' plugin_hook.original_name as original_hook ' + + 'from ' + + ' plugin ' + + ' left outer join plugin_hook on ' + + ' plugin.id = plugin_hook.plugin_id ' + + ' left outer join hook on ' + + ' plugin_hook.hook_id = hook.id ' + + 'order by hook.name, plugin.name'; + + var rows = sqlobj.executeRaw(sql, {}); + for (var i = 0; i < rows.length; i++) { + if (hooks[rows[i].hook] == undefined) + hooks[rows[i].hook] = [];0 + if (plugins[rows[i].plugin] == undefined) + plugins[rows[i].plugin] = []; + plugins[rows[i].plugin].push({hookName:rows[i].hook, originalHook:rows[i].originalName}); + if (rows[i].hook != 'null') + hooks[rows[i].hook].push({pluginName:rows[i].plugin, originalHook:rows[i].originalName}); + } +} + +function loadPlugins() { + if (pluginsLoaded) return; + pluginsLoaded = true; + loadAvailablePlugins(); + loadInstalledHooks(); +} + +function registerHook(pluginName, hookName, originalHook) { + if (originalHook == undefined) originalHook = null; + plugins[pluginName].push({hookName:hookName, originalHook:originalHook}); + if (hooks[hookName] === undefined) hooks[hookName] = []; + hooks[hookName].push({pluginName:pluginName, originalHook:originalHook}); + + var plugin = sqlobj.selectSingle('plugin', {name:pluginName}); + var hook = sqlobj.selectSingle('hook', {name:hookName}); + if (hook == null) { + sqlobj.insert('hook', {name:hookName}); + hook = sqlobj.selectSingle('hook', {name:hookName}); + } + sqlobj.insert("plugin_hook", {plugin_id:plugin.id, hook_id:hook.id, original_name:originalHook}); +} + +function unregisterHook(pluginName, hookName) { + plugins[pluginName] = plugins[pluginName].filter(function (hook) { return hook.hookName != hookName; }); + hooks[hookName] = hooks[hookName].filter(function (plugin) { return plugin.pluginName != pluginName; }); + if (hooks[hookName].length == 0) + delete hooks[hookName]; + + var conditions = {}; + if (pluginName != undefined) { + var plugin = sqlobj.selectSingle('plugin', {name:pluginName}); + conditions['plugin_id'] = plugin.id; + } + if (hookName != undefined) { + var hook = sqlobj.selectSingle('hook', {name:hookName}); + conditions['hook_id'] = hook.id; + } + sqlobj.deleteRows('plugin_hook', conditions); +} + +/* User API */ +function enablePlugin(pluginName) { + loadPlugins(); + if (pluginModules[pluginName] === undefined) + throw new Error ("Unable to find a plugin named " + pluginName); + if (plugins[pluginName] !== undefined) + throw new Error ("Atempting to reenable the already enabled plugin " + pluginName); + sqlobj.insert("plugin", {name:pluginName}); + plugins[pluginName] = []; + for (var i = 0; i < pluginModules[pluginName].hooks.length; i++) + registerHook(pluginName, pluginModules[pluginName].hooks[i]); + pluginModules[pluginName].install(); +} + +function disablePlugin(pluginName) { + loadPlugins(); + pluginModules[pluginName].uninstall(); + var pluginHooks = plugins[pluginName].map(function (x) { return x; }); // copy array + + for (pluginHook in pluginHooks) + unregisterHook(pluginName, pluginHooks[pluginHook].hookName); + delete plugins[pluginName]; + sqlobj.deleteRows("plugin", {name:pluginName}); +} + +function callHook(hookName, args) { + loadPlugins(); +log.info({XYZZZZ:hooks, NANANA:hookName}); + if (hooks[hookName] === undefined) + return []; + return hooks[hookName].map( + function (plugin) { + return pluginModules[plugin.pluginName][plugin.originalHook || hookName](args); + }); +} + +function callHookStr(hookName, args, sep, pre, post) { + return callHook(hookName, args).map(function (x) { return pre + x + post}).join(sep || ""); +} diff --git a/trunk/etherpad/src/etherpad/control/admin/pluginmanager.js b/trunk/etherpad/src/etherpad/control/admin/pluginmanager.js new file mode 100644 index 0000000..3fb017c --- /dev/null +++ b/trunk/etherpad/src/etherpad/control/admin/pluginmanager.js @@ -0,0 +1,65 @@ +/** + * Copyright 2009 RedHog, Egil Möller <egil.moller@piratpartiet.se> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import("faststatic"); +import("dispatch.{Dispatcher,PrefixMatcher,forward}"); + +import("etherpad.utils.*"); +import("etherpad.collab.server_utils"); +import("etherpad.globals.*"); +import("etherpad.log"); +import("etherpad.pad.padusers"); +import("etherpad.pro.pro_utils"); +import("etherpad.helpers"); +import("etherpad.pro.pro_accounts.getSessionProAccount"); +import("etherpad.admin.plugins"); + + +function onRequest() { + plugins.loadPlugins(); + + if (request.params.action == 'install') { + plugins.enablePlugin(request.params.plugin); + } else if (request.params.action == 'uninstall') { + plugins.disablePlugin(request.params.plugin); + } else if (request.params.action == 'reinstall') { + plugins.disablePlugin(request.params.plugin); + plugins.enablePlugin(request.params.plugin); + } + + helpers.addClientVars({ + userAgent: request.headers["User-Agent"], + debugEnabled: request.params.djs, + clientIp: request.clientAddr, + colorPalette: COLOR_PALETTE, + serverTimestamp: +(new Date), + isProPad: pro_utils.isProDomainRequest(), + userIsGuest: padusers.isGuest(padusers.getUserId()), + userId: padusers.getUserId(), + }); + + renderHtml("admin/pluginmanager.ejs", + { + pluginModules: plugins.pluginModules, + plugins: plugins.plugins, + config: appjet.config, + bodyClass: 'nonpropad', + isPro: pro_utils.isProDomainRequest(), + isProAccountHolder: pro_utils.isProDomainRequest() && ! padusers.isGuest(padusers.getUserId()), + account: getSessionProAccount(), // may be falsy + }); + return true; +} diff --git a/trunk/etherpad/src/main.js b/trunk/etherpad/src/main.js index 9cc1db2..cf07829 100644 --- a/trunk/etherpad/src/main.js +++ b/trunk/etherpad/src/main.js @@ -34,6 +34,7 @@ import("etherpad.importexport.importexport"); import("etherpad.legacy_urls"); import("etherpad.control.aboutcontrol"); +import("etherpad.control.admin.pluginmanager"); import("etherpad.control.admincontrol"); import("etherpad.control.blogcontrol"); import("etherpad.control.connection_diagnostics_control"); @@ -68,6 +69,8 @@ import("etherpad.pad.dbwriter"); import("etherpad.pad.pad_migrations"); import("etherpad.pad.noprowatcher"); +import("etherpad.admin.plugins"); + jimport("java.lang.System.out.println"); serverhandlers.startupHandler = function() { @@ -92,6 +95,8 @@ serverhandlers.startupHandler = function() { team_billing.onStartup(); collabroom_server.onStartup(); readLatestSessionsFromDisk(); + + plugins.callHook('serverStartup'); }; serverhandlers.resetHandler = function() { @@ -99,6 +104,8 @@ serverhandlers.resetHandler = function() { } serverhandlers.shutdownHandler = function() { + plugins.callHook('serverShutdown'); + appjet.cache.shutdownHandlerIsRunning = true; log.callCatchingExceptions(writeSessionsToDisk); @@ -375,6 +382,7 @@ function handlePath() { [DirMatcher('/ep/beta-account/'), forward(pro_beta_control)], [DirMatcher('/ep/pro-signup/'), forward(pro_signup_control)], [DirMatcher('/ep/about/'), forward(aboutcontrol)], + [DirMatcher('/ep/admin/pluginmanager'), forward(pluginmanager)], [DirMatcher('/ep/admin/'), forward(admincontrol)], [DirMatcher('/ep/blog/posts/'), blogcontrol.render_post], [DirMatcher('/ep/blog/'), forward(blogcontrol)], diff --git a/trunk/etherpad/src/plugins/kafoo/main.js b/trunk/etherpad/src/plugins/kafoo/main.js new file mode 100644 index 0000000..d0ef442 --- /dev/null +++ b/trunk/etherpad/src/plugins/kafoo/main.js @@ -0,0 +1,8 @@ +hooks = ['nahook', 'fiehook']; +description = 'Kabar'; + +function install() { +} + +function uninstall() { +} diff --git a/trunk/etherpad/src/plugins/testplugin/main.js b/trunk/etherpad/src/plugins/testplugin/main.js new file mode 100644 index 0000000..589c60c --- /dev/null +++ b/trunk/etherpad/src/plugins/testplugin/main.js @@ -0,0 +1,27 @@ +import("etherpad.log"); +import("etherpad.admin.plugins"); + +hooks = ['testhook', 'nahook', 'serverStartup', 'serverShutdown']; +description = 'Test Plugin'; + +function install() { + log.info("Installing testplugin"); +} + +function uninstall() { + log.info("Uninstalling testplugin"); +} + +function testhook () { +} + +function nahook() { +} + +function serverStartup() { + log.info("Server startup for testplugin"); +} + +function serverShutdown() { + log.info("Server shutdown for testplugin"); +} diff --git a/trunk/etherpad/src/templates/admin/pluginmanager.ejs b/trunk/etherpad/src/templates/admin/pluginmanager.ejs new file mode 100644 index 0000000..4e08fc9 --- /dev/null +++ b/trunk/etherpad/src/templates/admin/pluginmanager.ejs @@ -0,0 +1,126 @@ +<% /* Copyright 2009 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS-IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ %> +<% + helpers.setHtmlTitle("Browse tags"); + helpers.setBodyId("padbody"); + helpers.addBodyClass("limwidth nonpropad nonprouser"); + helpers.includeCss("pad2_ejs.css"); + helpers.setRobotsPolicy({index: false, follow: false}) + helpers.includeJQuery(); + helpers.includeCometJs(); + helpers.includeJs("json2.js"); + helpers.addToHead('\n<style type="text/css" title="dynamicsyntax"></style>\n'); + + function inArray(item, arr) { + for (var i = 0; i < arr.length; i++) + if (arr[i] == item) + return true; + return false; + } +%> + +<div id="padpage"> + <div id="padtop"> + <div id="topbar" style="margin: 7px; margin-top: 0px;"> + <div id="topbarleft"><!-- --></div> + <div id="topbarright"><!-- --></div> + <div id="topbarcenter"><a href="/" id="topbaretherpad">EtherPad</a></div> + <% if (isProAccountHolder) { %> + <div id="accountnav"><%= toHTML(account.email) %><a href="/ep/account/sign-out">(sign out)</a></div> + <% } else if (isPro) { %> + <div id="accountnav"><a href="<%= signinUrl %>">sign in</a></div> + <% } %> + </div> + </div> + <div id="docbar" class="docbar-public"> + <div id="docbarleft"><!-- --></div> + <div title="Browse pads by tag" id="docbarpadtitle"><span>Browse tags</span></div> + + <div id="docbaroptions-outer"><a href="javascript:void(0)" id="docbaroptions">Pad Options</a></div> + <div id="docbarsavedrevs-outer"><a href="javascript:void(0)" id="docbarsavedrevs">Saved revisions</a></div> + <div id="docbarimpexp-outer"><a href="javascript:void(0)" id="docbarimpexp">Import/Export</a></div> + <div id="docbarslider-outer"><a target="_blank" href="/ep/pad/view/xx/latest" id="docbarslider">Time Slider</a></div> + + </div> + </div> + <div id="padmain"> + + <div id="padsidebar"> + <div id="padusers"> + </div> + + <div id="hdraggie"><!-- --></div> + + <div id="padchat"></div> + </div> <!-- /padsidebar --> + + <div id="padeditor"> + <div id="editbar" class="enabledtoolbar"> + <div id="editbarleft"><!-- --></div> + <div id="editbarright"><!-- --></div> + + <div id="editbarinner"> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('bold'));" class="editbarbutton bold" title="Bold (ctrl-B)"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('italic'));" class="editbarbutton italic" title="Italics (ctrl-I)"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('underline'));" class="editbarbutton underline" title="Underline (ctrl-U)"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('strikethrough'));" class="editbarbutton strikethrough" title="Strikethrough"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('clearauthorship'));" class="editbarbutton clearauthorship" title="Clear Authorship Colors"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('undo'));" class="editbarbutton undo" title="Undo (ctrl-Z)"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('redo'));" class="editbarbutton redo" title="Redo (ctrl-Y)"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('insertunorderedlist'));" class="editbarbutton insertunorderedlist" title="Toggle Bullet List"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('indent'));" class="editbarbutton indent" title="Indent List"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('outdent'));" class="editbarbutton outdent" title="Unindent List"> </a> + <a unselectable="on" href="javascript:void (window.pad&&pad.editbarClick('save'));" class="editbarbutton save" title="Save Revision"> </a> + </div> + </div> + <div style="height: 268px;" id="editorcontainerbox"> + <div id="editorcontainer" style="padding:5pt; height: 600pt;"> + <h1>Plugin manager</h1> + <table> + <tr> + <th>Module name</th> + <th>Status</th> + <th></th> + </tr> + <% for (var plugin in pluginModules) { %> + <tr> + <td><%= pluginModules[plugin].description %></td> + <td> + <% if (plugins[plugin] !== undefined) { %> + Installed + <% } else { %> + Not installed + <% } %> + </td> + <td> + <% if (plugins[plugin] !== undefined) { %> + <a href="/ep/admin/pluginmanager/?plugin=<%= plugin %>&action=uninstall">Uninstall</a> + <a href="/ep/admin/pluginmanager/?plugin=<%= plugin %>&action=reinstall">Reinstall</a> + <% } else { %> + <a href="/ep/admin/pluginmanager/?plugin=<%= plugin %>&action=install">Install</a> + <% } %> + </td> + </tr> + <% } %> + </table> + </div> + </div> + </div><!-- /padeditor --> + + <div id="bottomarea"> + <div id="widthprefcheck" class="widthprefunchecked"><!-- --></div> + <div id="sidebarcheck" class="sidebarchecked"><!-- --></div> + </div> + </div> +</div> diff --git a/trunk/infrastructure/framework-src/modules/execution.js b/trunk/infrastructure/framework-src/modules/execution.js index 1cec418..2f9d933 100644 --- a/trunk/infrastructure/framework-src/modules/execution.js +++ b/trunk/infrastructure/framework-src/modules/execution.js @@ -44,8 +44,11 @@ function fancyAssEval(initCode, mainCode) { 1); } var runner = Packages.net.appjet.oui.ScopeReuseManager.getEmpty(scalaF1(init)); + var requestWrapper = null; + if (request.underlying !== undefined) + requestWrapper = new Packages.net.appjet.oui.RequestWrapper(request.underlying); var ec = new Packages.net.appjet.oui.ExecutionContext( - new Packages.net.appjet.oui.RequestWrapper(request.underlying), + requestWrapper, null, runner); return Packages.net.appjet.oui.ExecutionContextUtils.withContext(ec, scalaF0(function() { |