Plugin list can now be reloaded 'live'
This commit is contained in:
parent
6fe7f2c2b2
commit
c591efb352
|
@ -1,6 +1,7 @@
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var eejs = require('ep_etherpad-lite/node/eejs');
|
var eejs = require('ep_etherpad-lite/node/eejs');
|
||||||
var installer = require('ep_etherpad-lite/static/js/pluginfw/installer');
|
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) {
|
exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
args.app.get('/admin/plugins', function(req, res) {
|
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) {
|
exports.socketio = function (hook_name, args, cb) {
|
||||||
var io = args.io.of("/pluginfw/installer");
|
var io = args.io.of("/pluginfw/installer");
|
||||||
io.on('connection', function (socket) {
|
io.on('connection', function (socket) {
|
||||||
|
socket.on("load", function (query) {
|
||||||
|
socket.emit("installed-results", {results: plugins.plugins});
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("search", function (query) {
|
socket.on("search", function (query) {
|
||||||
socket.emit("progress", {progress:0, message:'Fetching results...'});
|
socket.emit("progress", {progress:0, message:'Fetching results...'});
|
||||||
installer.search(query, function (er, data) {
|
installer.search(query, function (progress) {
|
||||||
if (er) {
|
if (progress.results)
|
||||||
socket.emit("progress", {progress:1, error:er});
|
socket.emit("search-result", progress);
|
||||||
} else {
|
socket.emit("progress", progress);
|
||||||
socket.emit("search-result", {results: data});
|
|
||||||
socket.emit("progress", {progress:1, message:'Done.'});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("install", function (plugin_name) {
|
socket.on("install", function (plugin_name) {
|
||||||
socket.emit("progress", {progress:0, message:'Downloading and installing ' + plugin_name + "..."});
|
socket.emit("progress", {progress:0, message:'Downloading and installing ' + plugin_name + "..."});
|
||||||
installer.install(plugin_name, function (er) {
|
installer.install(plugin_name, function (progress) {
|
||||||
if (er)
|
socket.emit("progress", progress);
|
||||||
socket.emit("progress", {progress:1, error:er});
|
|
||||||
else
|
|
||||||
socket.emit("progress", {progress:1, message:'Done.'});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("uninstall", function (plugin_name) {
|
socket.on("uninstall", function (plugin_name) {
|
||||||
socket.emit("progress", {progress:0, message:'Uninstalling ' + plugin_name + "..."});
|
socket.emit("progress", {progress:0, message:'Uninstalling ' + plugin_name + "..."});
|
||||||
installer.uninstall(plugin_name, function (er) {
|
installer.uninstall(plugin_name, function (progress) {
|
||||||
if (er)
|
socket.emit("progress", progress);
|
||||||
socket.emit("progress", {progress:1, error:er});
|
|
||||||
else
|
|
||||||
socket.emit("progress", {progress:1, message:'Done.'});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,8 +23,14 @@
|
||||||
"log4js" : "0.4.1",
|
"log4js" : "0.4.1",
|
||||||
"jsdom-nocontextifiy" : "0.2.10",
|
"jsdom-nocontextifiy" : "0.2.10",
|
||||||
"async-stacktrace" : "0.0.2",
|
"async-stacktrace" : "0.0.2",
|
||||||
|
|
||||||
"npm" : "1.1",
|
"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": {
|
"devDependencies": {
|
||||||
"jshint" : "*"
|
"jshint" : "*"
|
||||||
|
|
|
@ -40,14 +40,14 @@ exports.callAll = function (hook_name, args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.aCallAll = function (hook_name, args, cb) {
|
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(
|
async.map(
|
||||||
plugins.hooks[hook_name],
|
plugins.hooks[hook_name],
|
||||||
function (hook, cb) {
|
function (hook, cb) {
|
||||||
hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); });
|
hookCallWrapper(hook, hook_name, args, function (res) { cb(null, res); });
|
||||||
},
|
},
|
||||||
function (err, 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) {
|
exports.aCallFirst = function (hook_name, args, cb) {
|
||||||
if (plugins.hooks[hook_name][0] === undefined) cb([]);
|
if (plugins.hooks[hook_name][0] === undefined) return cb(null, []);
|
||||||
hookCallWrapper(plugins.hooks[hook_name][0], hook_name, args, function (res) { cb(exports.flatten(res)); });
|
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) {
|
exports.callAllStr = function(hook_name, args, sep, pre, post) {
|
||||||
|
|
|
@ -3,43 +3,74 @@ var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks");
|
||||||
var npm = require("npm");
|
var npm = require("npm");
|
||||||
var registry = require("npm/lib/utils/npm-registry-client/index.js");
|
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) {
|
npm.load({}, function (er) {
|
||||||
if (er) return cb(er)
|
if (er) return cb({progress:1, error:er});
|
||||||
npm.commands.uninstall([plugin_name], function (er) {
|
npm.on("log", function (message) {
|
||||||
if (er) return cb(er);
|
cb({progress: 0.5, message:message.msg + ": " + message.pref});
|
||||||
hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function (er) {
|
});
|
||||||
cb(er);
|
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);
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
cb(null, res);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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) {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ exports.isClient = typeof global != "object";
|
||||||
|
|
||||||
if (!exports.isClient) {
|
if (!exports.isClient) {
|
||||||
var npm = require("npm/lib/npm.js");
|
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 relativize = require("npm/lib/utils/relativize.js");
|
||||||
var readJson = require("npm/lib/utils/read-json.js");
|
var readJson = require("npm/lib/utils/read-json.js");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
|
@ -10,6 +10,7 @@ if (!exports.isClient) {
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var tsort = require("./tsort");
|
var tsort = require("./tsort");
|
||||||
var util = require("util");
|
var util = require("util");
|
||||||
|
var extend = require("node.extend");
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.prefix = 'ep_';
|
exports.prefix = 'ep_';
|
||||||
|
@ -112,14 +113,19 @@ exports.getPackages = function (cb) {
|
||||||
function flatten(deps) {
|
function flatten(deps) {
|
||||||
Object.keys(deps).forEach(function (name) {
|
Object.keys(deps).forEach(function (name) {
|
||||||
if (name.indexOf(exports.prefix) == 0) {
|
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)
|
if (deps[name].dependencies !== undefined)
|
||||||
flatten(deps[name].dependencies);
|
flatten(deps[name].dependencies);
|
||||||
delete deps[name].dependencies;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
flatten([data]);
|
var tmp = {};
|
||||||
|
tmp[data.name] = data;
|
||||||
|
flatten(tmp);
|
||||||
cb(null, packages);
|
cb(null, packages);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
width: 500px;
|
width: 700px;
|
||||||
height: 400px;
|
height: 500px;
|
||||||
margin-left: -250px;
|
margin-left: -350px;
|
||||||
margin-top: -200px;
|
margin-top: -250px;
|
||||||
border: 3px solid #999999;
|
border: 3px solid #999999;
|
||||||
background: #eeeeee;
|
background: #eeeeee;
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,8 @@
|
||||||
border-bottom: 3px solid #999999;
|
border-bottom: 3px solid #999999;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
|
height: 24px;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.dialog .title .close {
|
.dialog .title .close {
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -46,6 +48,7 @@
|
||||||
left: 10px;
|
left: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="../../static/js/jquery.js"></script>
|
<script src="../../static/js/jquery.js"></script>
|
||||||
|
@ -54,6 +57,8 @@
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
var socket = io.connect().of("/pluginfw/installer");
|
var socket = io.connect().of("/pluginfw/installer");
|
||||||
|
|
||||||
|
var doUpdate = false;
|
||||||
|
|
||||||
function updateHandlers() {
|
function updateHandlers() {
|
||||||
$("#progress.dialog .close").click(function () {
|
$("#progress.dialog .close").click(function () {
|
||||||
$("#progress.dialog").hide();
|
$("#progress.dialog").hide();
|
||||||
|
@ -64,14 +69,16 @@
|
||||||
socket.emit("search", $("#search-query")[0].value);
|
socket.emit("search", $("#search-query")[0].value);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#do-install").click(function (e) {
|
$(".do-install").click(function (e) {
|
||||||
var row = $(e.target).closest("tr");
|
var row = $(e.target).closest("tr");
|
||||||
|
doUpdate = true;
|
||||||
socket.emit("install", row.find(".name").html());
|
socket.emit("install", row.find(".name").html());
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#do-uninstall").click(function (e) {
|
$(".do-uninstall").click(function (e) {
|
||||||
var row = $(e.target).closest("tr");
|
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) {
|
socket.on('progress', function (data) {
|
||||||
$("#progress.dialog .close").hide();
|
$("#progress.dialog .close").hide();
|
||||||
$("#progress.dialog").show();
|
$("#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) {
|
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 .message").html(message);
|
||||||
$("#progress.dialog .history").append(message);
|
$("#progress.dialog .history").append("<div>" + message + "</div>");
|
||||||
|
|
||||||
if (data.progress >= 1) {
|
if (data.progress >= 1) {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
$("#progress.dialog .close").show();
|
$("#progress.dialog .close").show();
|
||||||
} else {
|
} else {
|
||||||
|
if (doUpdate) {
|
||||||
|
doUpdate = false;
|
||||||
|
socket.emit("load");
|
||||||
|
}
|
||||||
$("#progress.dialog").hide();
|
$("#progress.dialog").hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +123,23 @@
|
||||||
}
|
}
|
||||||
updateHandlers();
|
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>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -131,17 +162,16 @@
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody class="template">
|
||||||
<% for (var plugin_name in plugins) { %>
|
<tr id="installed-plugin-template">
|
||||||
<% var plugin = plugins[plugin_name]; %>
|
<td class="name"></td>
|
||||||
<tr>
|
<td class="description"></td>
|
||||||
<td class="name"><%= plugin.package.name %></td>
|
<td class="actions">
|
||||||
<td><%= plugin.package.description %></td>
|
<input type="button" value="I" class="do-uninstall">
|
||||||
<td>
|
</td>
|
||||||
<input type="submit" value="U" class="do-uninstall">
|
</tr>
|
||||||
</td>
|
</tbody>
|
||||||
</tr>
|
<tbody id="installed-plugins">
|
||||||
<% } %>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue