summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/node/hooks/express/adminplugins.js30
-rw-r--r--src/package.json8
-rw-r--r--src/static/js/pluginfw/hooks.js8
-rw-r--r--src/static/js/pluginfw/installer.js95
-rw-r--r--src/static/js/pluginfw/plugins.js14
-rw-r--r--src/templates/admin/plugins.html72
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>