diff options
author | Egil Moeller <egil.moller@freecode.no> | 2012-03-19 17:16:49 +0100 |
---|---|---|
committer | Egil Moeller <egil.moller@freecode.no> | 2012-03-19 17:16:49 +0100 |
commit | c591efb352030d2e348b2fa63fba78e722c75a62 (patch) | |
tree | 6f1dff2b72df9e941bc56ab4b50dce6ea160b876 | |
parent | 6fe7f2c2b201428f645f5af36a0f75801025ce8d (diff) | |
download | etherpad-lite-c591efb352030d2e348b2fa63fba78e722c75a62.zip |
Plugin list can now be reloaded 'live'
-rw-r--r-- | src/node/hooks/express/adminplugins.js | 30 | ||||
-rw-r--r-- | src/package.json | 8 | ||||
-rw-r--r-- | src/static/js/pluginfw/hooks.js | 8 | ||||
-rw-r--r-- | src/static/js/pluginfw/installer.js | 95 | ||||
-rw-r--r-- | src/static/js/pluginfw/plugins.js | 14 | ||||
-rw-r--r-- | src/templates/admin/plugins.html | 72 |
6 files changed, 148 insertions, 79 deletions
diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 4dbd788f..fa7e7077 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -1,6 +1,7 @@ var path = require('path'); var eejs = require('ep_etherpad-lite/node/eejs'); var installer = require('ep_etherpad-lite/static/js/pluginfw/installer'); +var plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins'); exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/admin/plugins', function(req, res) { @@ -20,35 +21,30 @@ exports.expressCreateServer = function (hook_name, args, cb) { exports.socketio = function (hook_name, args, cb) { var io = args.io.of("/pluginfw/installer"); io.on('connection', function (socket) { + socket.on("load", function (query) { + socket.emit("installed-results", {results: plugins.plugins}); + }); + socket.on("search", function (query) { socket.emit("progress", {progress:0, message:'Fetching results...'}); - installer.search(query, function (er, data) { - if (er) { - socket.emit("progress", {progress:1, error:er}); - } else { - socket.emit("search-result", {results: data}); - socket.emit("progress", {progress:1, message:'Done.'}); - } + installer.search(query, function (progress) { + if (progress.results) + socket.emit("search-result", progress); + socket.emit("progress", progress); }); }); socket.on("install", function (plugin_name) { socket.emit("progress", {progress:0, message:'Downloading and installing ' + plugin_name + "..."}); - installer.install(plugin_name, function (er) { - if (er) - socket.emit("progress", {progress:1, error:er}); - else - socket.emit("progress", {progress:1, message:'Done.'}); + installer.install(plugin_name, function (progress) { + socket.emit("progress", progress); }); }); socket.on("uninstall", function (plugin_name) { socket.emit("progress", {progress:0, message:'Uninstalling ' + plugin_name + "..."}); - installer.uninstall(plugin_name, function (er) { - if (er) - socket.emit("progress", {progress:1, error:er}); - else - socket.emit("progress", {progress:1, message:'Done.'}); + installer.uninstall(plugin_name, function (progress) { + socket.emit("progress", progress); }); }); }); diff --git a/src/package.json b/src/package.json index 80378a0a..34a9c01c 100644 --- a/src/package.json +++ b/src/package.json @@ -23,8 +23,14 @@ "log4js" : "0.4.1", "jsdom-nocontextifiy" : "0.2.10", "async-stacktrace" : "0.0.2", + "npm" : "1.1", - "ejs" : "0.6.1" + "ejs" : "0.6.1", + "node.extend" : "1.0.0", + "graceful-fs" : "1.1.5", + "slide" : "1.1.3", + "semver" : "1.0.13" + }, "devDependencies": { "jshint" : "*" diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 1b09a6e5..0b96c882 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -40,14 +40,14 @@ exports.callAll = function (hook_name, args) { } exports.aCallAll = function (hook_name, args, cb) { - if (plugins.hooks[hook_name] === undefined) cb([]); + if (plugins.hooks[hook_name] === undefined) return cb(null, []); async.map( plugins.hooks[hook_name], function (hook, cb) { hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); }); }, function (err, res) { - cb(exports.flatten(res)); + cb(null, exports.flatten(res)); } ); } @@ -58,8 +58,8 @@ exports.callFirst = function (hook_name, args) { } exports.aCallFirst = function (hook_name, args, cb) { - if (plugins.hooks[hook_name][0] === undefined) cb([]); - hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args, function (res) { cb(exports.flatten(res)); }); + if (plugins.hooks[hook_name][0] === undefined) return cb(null, []); + hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args, function (res) { cb(null, exports.flatten(res)); }); } exports.callAllStr = function(hook_name, args, sep, pre, post) { diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 3ba7f458..6cc043b7 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -3,43 +3,74 @@ var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks"); var npm = require("npm"); var registry = require("npm/lib/utils/npm-registry-client/index.js"); -exports.uninstall = function(plugin_name, cb) { +var withNpm = function (npmfn, cb) { npm.load({}, function (er) { - if (er) return cb(er) - npm.commands.uninstall([plugin_name], function (er) { - if (er) return cb(er); - hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function (er) { - cb(er); - }); - }) - }) + if (er) return cb({progress:1, error:er}); + npm.on("log", function (message) { + cb({progress: 0.5, message:message.msg + ": " + message.pref}); + }); + npmfn(function (er, data) { + if (er) return cb({progress:1, error:er.code + ": " + er.path}); + if (!data) data = {}; + data.progress = 1; + data.message = "Done."; + cb(data); + }); + }); } +// All these functions call their callback multiple times with +// {progress:[0,1], message:STRING, error:object}. They will call it +// with progress = 1 at least once, and at all times will either +// message or error be present, not both. It can be called multiple +// times for all values of propgress except for 1. + +exports.uninstall = function(plugin_name, cb) { + withNpm( + function (cb) { + npm.commands.uninstall([plugin_name], function (er) { + if (er) return cb(er); + hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function (er, data) { + if (er) return cb(er); + plugins.update(cb); + }); + }); + }, + cb + ); +}; + exports.install = function(plugin_name, cb) { - npm.load({}, function (er) { - if (er) return cb(er) - npm.commands.install([plugin_name], function (er) { - if (er) return cb(er); - hooks.aCallAll("pluginInstall", {plugin_name: plugin_name}, function (er) { - cb(er); + withNpm( + function (cb) { + npm.commands.install([plugin_name], function (er) { + if (er) return cb(er); + hooks.aCallAll("pluginInstall", {plugin_name: plugin_name}, function (er, data) { + if (er) return cb(er); + plugins.update(cb); + }); }); - }); - }) -} + }, + cb + ); +}; exports.search = function(pattern, cb) { - npm.load({}, function (er) { - registry.get( - "/-/all", null, 600, false, true, - function (er, data) { - if (er) return cb(er); - var res = {}; - for (key in data) { - if (/*key.indexOf(plugins.prefix) == 0 &&*/ key.indexOf(pattern) != -1) - res[key] = data[key]; + withNpm( + function (cb) { + registry.get( + "/-/all", null, 600, false, true, + function (er, data) { + if (er) return cb(er); + var res = {}; + for (key in data) { + if (/*key.indexOf(plugins.prefix) == 0 &&*/ key.indexOf(pattern) != -1) + res[key] = data[key]; + } + cb(null, {results:res}); } - cb(null, res); - } - ); - }); -} + ); + }, + cb + ); +}; diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js index c5c21903..5017962b 100644 --- a/src/static/js/pluginfw/plugins.js +++ b/src/static/js/pluginfw/plugins.js @@ -2,7 +2,7 @@ exports.isClient = typeof global != "object"; if (!exports.isClient) { var npm = require("npm/lib/npm.js"); - var readInstalled = require("npm/lib/utils/read-installed.js"); + var readInstalled = require("./read-installed.js"); var relativize = require("npm/lib/utils/relativize.js"); var readJson = require("npm/lib/utils/read-json.js"); var path = require("path"); @@ -10,6 +10,7 @@ if (!exports.isClient) { var fs = require("fs"); var tsort = require("./tsort"); var util = require("util"); + var extend = require("node.extend"); } exports.prefix = 'ep_'; @@ -112,14 +113,19 @@ exports.getPackages = function (cb) { function flatten(deps) { Object.keys(deps).forEach(function (name) { if (name.indexOf(exports.prefix) == 0) { - packages[name] = deps[name]; + packages[name] = extend({}, deps[name]); + // Delete anything that creates loops so that the plugin + // list can be sent as JSON to the web client + delete packages[name].dependencies; + delete packages[name].parent; } if (deps[name].dependencies !== undefined) flatten(deps[name].dependencies); - delete deps[name].dependencies; }); } - flatten([data]); + var tmp = {}; + tmp[data.name] = data; + flatten(tmp); cb(null, packages); }); } diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 61e277d2..a0822e87 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -20,10 +20,10 @@ position: absolute; left: 50%; top: 50%; - width: 500px; - height: 400px; - margin-left: -250px; - margin-top: -200px; + width: 700px; + height: 500px; + margin-left: -350px; + margin-top: -250px; border: 3px solid #999999; background: #eeeeee; } @@ -33,6 +33,8 @@ border-bottom: 3px solid #999999; font-size: 24px; line-height: 24px; + height: 24px; + overflow: hidden; } .dialog .title .close { float: right; @@ -46,6 +48,7 @@ left: 10px; right: 10px; padding: 2px; + overflow: auto; } </style> <script src="../../static/js/jquery.js"></script> @@ -54,6 +57,8 @@ $(document).ready(function () { var socket = io.connect().of("/pluginfw/installer"); + var doUpdate = false; + function updateHandlers() { $("#progress.dialog .close").click(function () { $("#progress.dialog").hide(); @@ -64,14 +69,16 @@ socket.emit("search", $("#search-query")[0].value); }); - $("#do-install").click(function (e) { + $(".do-install").click(function (e) { var row = $(e.target).closest("tr"); + doUpdate = true; socket.emit("install", row.find(".name").html()); }); - $("#do-uninstall").click(function (e) { + $(".do-uninstall").click(function (e) { var row = $(e.target).closest("tr"); - socket.emit("install", row.find(".name").html()); + doUpdate = true; + socket.emit("uninstall", row.find(".name").html()); }); } @@ -80,17 +87,24 @@ socket.on('progress', function (data) { $("#progress.dialog .close").hide(); $("#progress.dialog").show(); - var message = data.message; + var message = "Unknown status"; + if (data.message) { + message = "<span class='status'>" + data.message.toString() + "</span>"; + } if (data.error) { - message = "<div class='error'>" + data.error.toString() + "<div>"; + message = "<span class='error'>" + data.error.toString() + "<span>"; } $("#progress.dialog .message").html(message); - $("#progress.dialog .history").append(message); + $("#progress.dialog .history").append("<div>" + message + "</div>"); if (data.progress >= 1) { if (data.error) { $("#progress.dialog .close").show(); } else { + if (doUpdate) { + doUpdate = false; + socket.emit("load"); + } $("#progress.dialog").hide(); } } @@ -109,6 +123,23 @@ } updateHandlers(); }); + + socket.on('installed-results', function (data) { + $("#installed-plugins *").remove(); + for (plugin_name in data.results) { + var plugin = data.results[plugin_name]; + var row = $("#installed-plugin-template").clone(); + + for (attr in plugin.package) { + row.find("." + attr).html(plugin.package[attr]); + } + $("#installed-plugins").append(row); + } + updateHandlers(); + }); + + socket.emit("load"); + }); </script> </head> @@ -131,17 +162,16 @@ <td></td> </tr> </thead> - <tbody> - <% for (var plugin_name in plugins) { %> - <% var plugin = plugins[plugin_name]; %> - <tr> - <td class="name"><%= plugin.package.name %></td> - <td><%= plugin.package.description %></td> - <td> - <input type="submit" value="U" class="do-uninstall"> - </td> - </tr> - <% } %> + <tbody class="template"> + <tr id="installed-plugin-template"> + <td class="name"></td> + <td class="description"></td> + <td class="actions"> + <input type="button" value="I" class="do-uninstall"> + </td> + </tr> + </tbody> + <tbody id="installed-plugins"> </tbody> </table> |