diff options
Diffstat (limited to 'trunk')
-rw-r--r-- | trunk/README.hooks | 24 | ||||
-rw-r--r-- | trunk/etherpad/src/etherpad/admin/plugins.js | 247 | ||||
-rw-r--r-- | trunk/etherpad/src/etherpad/control/admin/pluginmanager.js | 65 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/kafoo/main.js | 16 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/testplugin/controllers/testplugin.js | 57 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/testplugin/hooks.js | 15 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/testplugin/main.js | 23 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/testplugin/static/js/main.js | 11 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/testplugin/static/js/test.js | 1 | ||||
-rw-r--r-- | trunk/etherpad/src/plugins/testplugin/templates/testplugin.ejs | 29 | ||||
-rw-r--r-- | trunk/etherpad/src/static/js/plugins.js | 19 | ||||
-rw-r--r-- | trunk/etherpad/src/templates/admin/pluginmanager.ejs | 126 |
12 files changed, 633 insertions, 0 deletions
diff --git a/trunk/README.hooks b/trunk/README.hooks new file mode 100644 index 0000000..d15949c --- /dev/null +++ b/trunk/README.hooks @@ -0,0 +1,24 @@ +Hooks that plugins can provide + +All hooks must return either undefined/null or a list of return values. This might be an empty list or a list of just one value. + +handlePath + Registers new urls to serve + Parameters: None + Returns: Parameter suitable for Dispatcher +renderPageBodyPre + Adds extra html before the body of a page + Parameters: bodyFileName, data, plugin + Returns: String(s) of html +renderPageBodyPost + Adds extra html after the body of a page + Parameters: bodyFileName, data, plugin + Returns: String(s) of html +serverStartup + Run right after server startup + Parameters: None + Returns: None +serverShutdown + Run before server shutdown + Parameters: None + Returns: None diff --git a/trunk/etherpad/src/etherpad/admin/plugins.js b/trunk/etherpad/src/etherpad/admin/plugins.js new file mode 100644 index 0000000..41482fc --- /dev/null +++ b/trunk/etherpad/src/etherpad/admin/plugins.js @@ -0,0 +1,247 @@ +/** + * 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 = {}; +clientHooks = {}; + +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 { + var res = execution.fancyAssEval(importStmt, "main;"); + res = new res.init(); + return res; + } 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 loadPluginHooks(pluginName) { + function registerHookNames(hookSet, type) { + return function (hook) { + var row = {hook:hook, type:type, plugin:pluginName}; + if (hookSet[hook] == undefined) hookSet[hook] = []; + hookSet[hook].push(row); + return row; + } + } + plugins[pluginName] = pluginModules[pluginName].hooks.map(registerHookNames(hooks, 'server')); + if (pluginModules[pluginName].client != undefined && pluginModules[pluginName].client.hooks != undefined) + plugins[pluginName] = plugins[pluginName].concat(pluginModules[pluginName].client.hooks.map(registerHookNames(clientHooks, 'client'))); +} + +function unloadPluginHooks(pluginName) { + for (var hookSet in [hooks, clientHooks]) + for (var hookName in hookSet) { + var hook = hookSet[hookName]; + for (i = hook.length - 1; i >= 0; i--) + if (hook[i].plugin == pluginName) + hook.splice(i, 1); + } + delete plugins[pluginName]; +} + +function loadInstalledHooks() { + var sql = '' + + 'select ' + + ' hook.name as hook, ' + + ' hook_type.name as type, ' + + ' plugin.name as plugin, ' + + ' plugin_hook.original_name as original ' + + '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 ' + + ' left outer join hook_type on ' + + ' hook.type_id = hook_type.id ' + + 'order by hook.name, plugin.name'; + + var rows = sqlobj.executeRaw(sql, {}); + for (var i = 0; i < rows.length; i++) { + var row = rows[i]; + + if (plugins[row.plugin] == undefined) + plugins[row.plugin] = []; + plugins[row.plugin].push(row); + + var hookSet; + + if (row.type == 'server') + hookSet = hooks; + else if (row.type == 'client') + hookSet = clientHooks; + + if (hookSet[row.hook] == undefined) + hookSet[row.hook] = []; + if (row.hook != 'null') + hookSet[row.hook].push(row); + } +} + +function selectOrInsert(table, columns) { + var res = sqlobj.selectSingle(table, columns); + if (res !== null) + return res; + sqlobj.insert(table, columns); + return sqlobj.selectSingle(table, columns); +} + +function saveInstalledHooks(pluginName) { + var plugin = sqlobj.selectSingle('plugin', {name:pluginName}); + + if (plugin !== null) { + sqlobj.deleteRows('plugin_hook', {plugin_id:plugin.id}); + if (plugins[pluginName] === undefined) + sqlobj.deleteRows('plugin', {name:pluginName}); + } + + if (plugins[pluginName] !== undefined) { + if (plugin === null) + plugin = selectOrInsert('plugin', {name:pluginName}); + + for (var i = 0; i < plugins[pluginName].length; i++) { + var row = plugins[pluginName][i]; + + var hook_type = selectOrInsert('hook_type', {name:row.type}); + var hook = selectOrInsert('hook', {name:row.hook, type_id:hook_type.id}); + + sqlobj.insert("plugin_hook", {plugin_id:plugin.id, hook_id:hook.id}); + } + } +} + + +function loadPlugins() { + if (pluginsLoaded) return; + pluginsLoaded = true; + loadAvailablePlugins(); + loadInstalledHooks(); +} + + +/* User API */ +function enablePlugin(pluginName) { + loadPlugins(); + loadPluginHooks(pluginName); + saveInstalledHooks(pluginName); + try { + pluginModules[pluginName].install(); + } catch (e) { + unloadPluginHooks(pluginName); + saveInstalledHooks(pluginName); + throw e; + } + log.info({PLUGINS:plugins, HOOKS:hooks}); +} + +function disablePlugin(pluginName) { + loadPlugins(); + try { + pluginModules[pluginName].uninstall(); + } catch (e) { + log.info({errorUninstallingPlugin:exceptionutils.getStackTracePlain(e)}); + } + unloadPluginHooks(pluginName); + saveInstalledHooks(pluginName); + log.info({PLUGINS:plugins, HOOKS:hooks}); +} + +function registerClientHandlerJS() { + loadPlugins(); + for (pluginName in plugins) { + var plugin = pluginModules[pluginName]; + if (plugin.client !== undefined) { + helpers.includeJs("plugins/" + pluginName + "/main.js"); + if (plugin.client.modules != undefined) + for (j = 0; j < client.modules.length; j++) + helpers.includeJs("plugins/" + pluginName + "/" + plugin.client.modules[j] + ".js"); + } + } + helpers.addClientVars({hooks:clientHooks}); + helpers.includeJs("plugins.js"); +} + +function callHook(hookName, args) { + loadPlugins(); + if (hooks[hookName] === undefined) + return []; + var res = []; + for (i = 0; i < hooks[hookName].length; i++) { + var plugin = hooks[hookName][i]; + var pluginRes = pluginModules[plugin.plugin][plugin.original || hookName](args); + if (pluginRes != undefined && pluginRes != null) + res = res.concat(pluginRes); + } + return res; +} + +function callHookStr(hookName, args, sep, pre, post) { + if (sep == undefined) sep = ''; + if (pre == undefined) pre = ''; + if (post == undefined) 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/plugins/kafoo/main.js b/trunk/etherpad/src/plugins/kafoo/main.js new file mode 100644 index 0000000..f645576 --- /dev/null +++ b/trunk/etherpad/src/plugins/kafoo/main.js @@ -0,0 +1,16 @@ +import("etherpad.log"); + +function init() { + this.hooks = []; + this.description = 'KaBar plugin'; + this.install = install; + this.uninstall = uninstall; +} + +function install() { + log.info("Installing testplugin"); +} + +function uninstall() { + log.info("Uninstalling testplugin"); +} diff --git a/trunk/etherpad/src/plugins/testplugin/controllers/testplugin.js b/trunk/etherpad/src/plugins/testplugin/controllers/testplugin.js new file mode 100644 index 0000000..0c79e06 --- /dev/null +++ b/trunk/etherpad/src/plugins/testplugin/controllers/testplugin.js @@ -0,0 +1,57 @@ +/** + * 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"); + + +function onRequest() { + var isPro = pro_utils.isProDomainRequest(); + var userId = padusers.getUserId(); + + helpers.addClientVars({ + userAgent: request.headers["User-Agent"], + debugEnabled: request.params.djs, + clientIp: request.clientAddr, + colorPalette: COLOR_PALETTE, + serverTimestamp: +(new Date), + isProPad: isPro, + userIsGuest: padusers.isGuest(userId), + userId: userId, + }); + + var isProUser = (isPro && ! padusers.isGuest(userId)); + + renderHtml("testplugin.ejs", + { + isPro: isPro, + isProAccountHolder: isProUser, + account: getSessionProAccount(), // may be falsy + }, 'testplugin'); + return true; +} diff --git a/trunk/etherpad/src/plugins/testplugin/hooks.js b/trunk/etherpad/src/plugins/testplugin/hooks.js new file mode 100644 index 0000000..493a2c2 --- /dev/null +++ b/trunk/etherpad/src/plugins/testplugin/hooks.js @@ -0,0 +1,15 @@ +import("etherpad.log"); +import("dispatch.{Dispatcher,PrefixMatcher,forward}"); +import("plugins.testplugin.controllers.testplugin"); + +function serverStartup() { + log.info("Server startup for testplugin"); +} + +function serverShutdown() { + log.info("Server shutdown for testplugin"); +} + +function handlePath() { + return [[PrefixMatcher('/ep/testplugin/'), forward(testplugin)]]; +} diff --git a/trunk/etherpad/src/plugins/testplugin/main.js b/trunk/etherpad/src/plugins/testplugin/main.js new file mode 100644 index 0000000..49b447c --- /dev/null +++ b/trunk/etherpad/src/plugins/testplugin/main.js @@ -0,0 +1,23 @@ +import("etherpad.log"); +import("plugins.testplugin.hooks"); +import("plugins.testplugin.static.js.main"); + +function init() { + this.hooks = ['serverStartup', 'serverShutdown', 'handlePath']; + this.client = new main.init(); + this.description = 'Test Plugin'; + this.serverStartup = hooks.serverStartup; + this.serverShutdown = hooks.serverShutdown; + this.handlePath = hooks.handlePath; + this.install = install; + this.uninstall = uninstall; +} + +function install() { + log.info("Installing testplugin"); +} + +function uninstall() { + log.info("Uninstalling testplugin"); +} + diff --git a/trunk/etherpad/src/plugins/testplugin/static/js/main.js b/trunk/etherpad/src/plugins/testplugin/static/js/main.js new file mode 100644 index 0000000..f08b8f7 --- /dev/null +++ b/trunk/etherpad/src/plugins/testplugin/static/js/main.js @@ -0,0 +1,11 @@ +function init() { + this.hooks = ['kafoo']; + this.kafoo = kafoo; +} + +function kafoo() { + alert('hej'); +} + +/* used on the client side only */ +testplugin = new init(); diff --git a/trunk/etherpad/src/plugins/testplugin/static/js/test.js b/trunk/etherpad/src/plugins/testplugin/static/js/test.js new file mode 100644 index 0000000..0f30cd9 --- /dev/null +++ b/trunk/etherpad/src/plugins/testplugin/static/js/test.js @@ -0,0 +1 @@ +callHook("kafoo"); diff --git a/trunk/etherpad/src/plugins/testplugin/templates/testplugin.ejs b/trunk/etherpad/src/plugins/testplugin/templates/testplugin.ejs new file mode 100644 index 0000000..f70ca8d --- /dev/null +++ b/trunk/etherpad/src/plugins/testplugin/templates/testplugin.ejs @@ -0,0 +1,29 @@ +<% /* 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("Test plugin"); + 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.includeJs("plugins/testplugin/test.js"); + helpers.addToHead('\n<style type="text/css" title="dynamicsyntax"></style>\n'); +%> + +<div id="padpage"> + Welcome to the test plugin +</div> diff --git a/trunk/etherpad/src/static/js/plugins.js b/trunk/etherpad/src/static/js/plugins.js new file mode 100644 index 0000000..6d8804e --- /dev/null +++ b/trunk/etherpad/src/static/js/plugins.js @@ -0,0 +1,19 @@ +function callHook(hookName, args) { + if (clientVars.hooks[hookName] === undefined) + return []; + var res = []; + for (i = 0; i < clientVars.hooks[hookName].length; i++) { + var plugin = clientVars.hooks[hookName][i]; + var pluginRes = eval(plugin.plugin)[plugin.original || hookName](args); + if (pluginRes != undefined && pluginRes != null) + res = res.concat(pluginRes); + } + return res; +} + +function callHookStr(hookName, args, sep, pre, post) { + if (sep == undefined) sep = ''; + if (pre == undefined) pre = ''; + if (post == undefined) post = ''; + return callHook(hookName, args).map(function (x) { return pre + x + post}).join(sep || ""); +} 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> |