From 24680e64cedb9e3f161fbf7c3fd59ced6e16808f Mon Sep 17 00:00:00 2001 From: Egil Moeller Date: Fri, 12 Mar 2010 21:41:45 +0100 Subject: Got plugins and hooks to work --- trunk/etherpad/src/etherpad/admin/plugins.js | 182 +++++++++++++++++++++ .../src/etherpad/control/admin/pluginmanager.js | 65 ++++++++ trunk/etherpad/src/main.js | 8 + trunk/etherpad/src/plugins/kafoo/main.js | 8 + trunk/etherpad/src/plugins/testplugin/main.js | 27 +++ .../etherpad/src/templates/admin/pluginmanager.ejs | 126 ++++++++++++++ .../framework-src/modules/execution.js | 5 +- 7 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 trunk/etherpad/src/etherpad/admin/plugins.js create mode 100644 trunk/etherpad/src/etherpad/control/admin/pluginmanager.js create mode 100644 trunk/etherpad/src/plugins/kafoo/main.js create mode 100644 trunk/etherpad/src/plugins/testplugin/main.js create mode 100644 trunk/etherpad/src/templates/admin/pluginmanager.ejs 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 + * + * 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 + * + * 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\n'); + + function inArray(item, arr) { + for (var i = 0; i < arr.length; i++) + if (arr[i] == item) + return true; + return false; + } +%> + +
+
+
+
+
+ + <% if (isProAccountHolder) { %> +
<%= toHTML(account.email) %>(sign out)
+ <% } else if (isPro) { %> + + <% } %> +
+
+
+
+
Browse tags
+ + + + + + +
+
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+
+ +
+   +   +   +   +   +   +   +   +   +   +   +
+
+
+
+

Plugin manager

+ + + + + + + <% for (var plugin in pluginModules) { %> + + + + + + <% } %> +
Module nameStatus
<%= pluginModules[plugin].description %> + <% if (plugins[plugin] !== undefined) { %> + Installed + <% } else { %> + Not installed + <% } %> + + <% if (plugins[plugin] !== undefined) { %> + Uninstall + Reinstall + <% } else { %> + Install + <% } %> +
+
+
+
+ +
+
+
+
+
+ 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() { -- cgit v1.2.3