From ef7fb5c7f0e233268530eb8cd6eb02224751dae5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 25 Mar 2013 12:18:06 +0100 Subject: [PATCH 01/24] Update npm --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 822676a3..ff662575 100644 --- a/src/package.json +++ b/src/package.json @@ -27,7 +27,7 @@ "nodemailer" : "0.3.x", "jsdom-nocontextifiy" : "0.2.10", "async-stacktrace" : "0.0.2", - "npm" : "1.1.x", + "npm" : "1.2.x", "npm-registry-client" : "0.2.10", "ejs" : "0.6.1", "graceful-fs" : "1.1.5", From 0070eab4164ef473c706fe7f1f5c8064e7c819b9 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 25 Mar 2013 12:45:23 +0100 Subject: [PATCH 02/24] Fix caching of npm search results and only make one registry request on /admin/plugins fixes #1488 --- src/node/hooks/express/adminplugins.js | 4 ++-- src/static/js/admin/plugins.js | 6 ++++-- src/static/js/pluginfw/installer.js | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 7e221cf1..85bf2108 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -36,7 +36,7 @@ exports.socketio = function (hook_name, args, cb) { socket.on("checkUpdates", function() { socket.emit("progress", {progress:0, message:'Checking for plugin updates...'}); // Check plugins for updates - installer.search({offset: 0, pattern: '', limit: 500}, /*useCache:*/true, function(data) { // hacky + installer.search({offset: 0, pattern: '', limit: 500}, /*maxCacheAge:*/60*10, function(data) { // hacky if (!data.results) return; var updatable = _(plugins.plugins).keys().filter(function(plugin) { if(!data.results[plugin]) return false; @@ -51,7 +51,7 @@ exports.socketio = function (hook_name, args, cb) { socket.on("search", function (query) { socket.emit("progress", {progress:0, message:'Fetching results...'}); - installer.search(query, true, function (progress) { + installer.search(query, /*maxCacheAge:*/60*10, function (progress) { if (progress.results) socket.emit("search-result", progress); socket.emit("progress", progress); diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index a973875c..d7f94d2c 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -163,8 +163,10 @@ $(document).ready(function () { } updateHandlers(); - socket.emit('checkUpdates'); - tasks++; + setTimeout(function() { + socket.emit('checkUpdates'); + tasks++; + }, 5000) }); socket.on('updatable', function(data) { diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 15d87940..14a3b7be 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -71,12 +71,13 @@ exports.install = function(plugin_name, cb) { }; exports.searchCache = null; +var cacheTimestamp = 0; -exports.search = function(query, cache, cb) { +exports.search = function(query, maxCacheAge, cb) { withNpm( function (cb) { var getData = function (cb) { - if (cache && exports.searchCache) { + if (maxCacheAge && exports.searchCache && Math.round(+new Date/1000)-cacheTimestamp < maxCacheAge) { cb(null, exports.searchCache); } else { registry.get( @@ -84,6 +85,7 @@ exports.search = function(query, cache, cb) { function (er, data) { if (er) return cb(er); exports.searchCache = data; + cacheTimestamp = Math.round(+new Date/1000) cb(er, data); } ); From b297784288a5c93e329509deca45a78fc81057d3 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 25 Mar 2013 16:51:12 +0100 Subject: [PATCH 03/24] Make npm registry access code more sane --- src/package.json | 1 - src/static/js/pluginfw/installer.js | 139 +++++++++------------------- 2 files changed, 46 insertions(+), 94 deletions(-) diff --git a/src/package.json b/src/package.json index ff662575..24f15ca9 100644 --- a/src/package.json +++ b/src/package.json @@ -28,7 +28,6 @@ "jsdom-nocontextifiy" : "0.2.10", "async-stacktrace" : "0.0.2", "npm" : "1.2.x", - "npm-registry-client" : "0.2.10", "ejs" : "0.6.1", "graceful-fs" : "1.1.5", "slide" : "1.1.3", diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 14a3b7be..949fdae8 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -1,120 +1,73 @@ var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks"); var npm = require("npm"); -var RegClient = require("npm-registry-client") -var registry = new RegClient( -{ registry: "http://registry.npmjs.org" -, cache: npm.cache } -); - -var withNpm = function (npmfn, final, cb) { +var withNpm = function (npmfn, cb) { npm.load({}, function (er) { - if (er) return cb({progress:1, error:er}); + if (er) return cb(er); npm.on("log", function (message) { - cb({progress: 0.5, message:message.msg + ": " + message.pref}); - }); - npmfn(function (er, data) { - if (er) { - console.error(er); - return cb({progress:1, error: er.message}); - } - if (!data) data = {}; - data.progress = 1; - data.message = "Done."; - cb(data); - final(); + console.log('npm: ',message) }); + npmfn(); }); } -// 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) { + withNpm(function () { + npm.commands.uninstall([plugin_name], function (er) { + if (er) return cb && cb(er); + hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function (er, data) { if (er) return cb(er); - hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function (er, data) { - if (er) return cb(er); - plugins.update(cb); - }); + plugins.update(cb); + cb && cb(); + hooks.aCallAll("restartServer", {}, function () {}); }); - }, - function () { - hooks.aCallAll("restartServer", {}, function () {}); - }, - cb - ); + }); + }); }; exports.install = function(plugin_name, cb) { - withNpm( - function (cb) { + withNpm(function () { npm.commands.install([plugin_name], function (er) { - if (er) return cb(er); + if (er) return cb && cb(er); hooks.aCallAll("pluginInstall", {plugin_name: plugin_name}, function (er, data) { if (er) return cb(er); plugins.update(cb); + cb && cb(); + hooks.aCallAll("restartServer", {}, function () {}); }); }); - }, - function () { - hooks.aCallAll("restartServer", {}, function () {}); - }, - cb - ); + }); }; -exports.searchCache = null; +exports.availablePlugins = null; var cacheTimestamp = 0; -exports.search = function(query, maxCacheAge, cb) { - withNpm( - function (cb) { - var getData = function (cb) { - if (maxCacheAge && exports.searchCache && Math.round(+new Date/1000)-cacheTimestamp < maxCacheAge) { - cb(null, exports.searchCache); - } else { - registry.get( - "/-/all", 600, false, true, - function (er, data) { - if (er) return cb(er); - exports.searchCache = data; - cacheTimestamp = Math.round(+new Date/1000) - cb(er, data); - } - ); - } - } - getData( - function (er, data) { - if (er) return cb(er); - var res = {}; - var i = 0; - var pattern = query.pattern.toLowerCase(); - for (key in data) { // for every plugin in the data from npm - if ( key.indexOf(plugins.prefix) == 0 - && key.indexOf(pattern) != -1 - || key.indexOf(plugins.prefix) == 0 - && data[key].description.indexOf(pattern) != -1 - ) { // If the name contains ep_ and the search string is in the name or description - i++; - if (i > query.offset - && i <= query.offset + query.limit) { - res[key] = data[key]; - } - } - } - cb(null, {results:res, query: query, total:i}); - } - ); - }, - function () { }, - cb - ); +exports.getAvailablePlugins = function(maxCacheAge, cb) { + withNpm(function () { + if(exports.availablePlugins && maxCacheAge && Math.round(+new Date/1000)-cacheTimestamp <= maxCacheAge) { + return cb && cb(null, exports.availablePlugins) + } + npm.commands.search(['ep_'], function(er, results) { + if(er) return cb && cb(er); + exports.availablePlugins = results; + cacheTimestamp = Math.round(+new Date/1000); + cb && cb(null, results) + }) + }); +}; + + +exports.search = function(searchTerm, maxCacheAge, cb) { + exports.getAvailablePlugins(maxCacheAge, function(er, results) { + if(er) return cb && cb(er); + var res = {}; + searchTerm = searchTerm.toLowerCase(); + for (var pluginName in results) { // for every available plugin + if (pluginName.indexOf(plugins.prefix) != 0) continue; // TODO: Also search in keywords here! + if(pluginName.indexOf(searchTerm) < 0 && results[pluginName].description.indexOf(searchTerm) < 0) continue; + res[pluginName] = results[pluginName]; + } + cb && cb(null, res) + }) }; From 079fdf0f38160481836b6fc881c60741f90a414b Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 25 Mar 2013 17:20:10 +0100 Subject: [PATCH 04/24] Revamp /admin/plugins - dry up the client-side code - use the new saner API of pluginfw/installer.js on the server - Improve UX: allow user to infinitely scroll to display their results --- src/node/hooks/express/adminplugins.js | 62 +++++--- src/static/js/admin/plugins.js | 207 ++++++++----------------- src/templates/admin/plugins.html | 5 +- 3 files changed, 105 insertions(+), 169 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 85bf2108..6b7c01e0 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -27,48 +27,66 @@ exports.socketio = function (hook_name, args, cb) { io.on('connection', function (socket) { if (!socket.handshake.session.user || !socket.handshake.session.user.is_admin) return; - socket.on("load", function (query) { + socket.on("getInstalled", function (query) { // send currently installed plugins - socket.emit("installed-results", {results: plugins.plugins}); - socket.emit("progress", {progress:1}); + var installed = Object.keys(plugins.plugins).map(function(plugin) { + return plugins.plugins[plugin].package + }) + socket.emit("results:installed", {installed: installed}); }); - socket.on("checkUpdates", function() { - socket.emit("progress", {progress:0, message:'Checking for plugin updates...'}); + socket.on("getUpdatable", function() { // Check plugins for updates - installer.search({offset: 0, pattern: '', limit: 500}, /*maxCacheAge:*/60*10, function(data) { // hacky - if (!data.results) return; + installer.getAvailable(/*maxCacheAge:*/60*10, function(er, results) { + if(er) { + console.warn(er); + socket.emit("results:updatable", {updatable: {}}); + return; + } var updatable = _(plugins.plugins).keys().filter(function(plugin) { - if(!data.results[plugin]) return false; - var latestVersion = data.results[plugin]['dist-tags'].latest + if(!results[plugin]) return false; + var latestVersion = results[plugin].version var currentVersion = plugins.plugins[plugin].package.version return semver.gt(latestVersion, currentVersion) }); - socket.emit("updatable", {updatable: updatable}); - socket.emit("progress", {progress:1}); + socket.emit("results:updatable", {updatable: updatable}); }); }) + + socket.on("getAvailable", function (query) { + installer.getAvailablePlugins(/*maxCacheAge:*/false, function (er, results) { + if(er) { + console.error(er) + results = {} + } + socket.emit("results:available", results); + }); + }); socket.on("search", function (query) { - socket.emit("progress", {progress:0, message:'Fetching results...'}); - installer.search(query, /*maxCacheAge:*/60*10, function (progress) { - if (progress.results) - socket.emit("search-result", progress); - socket.emit("progress", progress); + installer.search(query.searchTerm, /*maxCacheAge:*/60*10, function (er, results) { + if(er) { + console.error(er) + results = {} + } + var res = Object.keys(results) + .slice(query.offset, query.offset+query.length) + .map(function(pluginName) { + return results[pluginName] + }); + socket.emit("results:search", {results: res, query: query}); }); }); socket.on("install", function (plugin_name) { - socket.emit("progress", {progress:0, message:'Downloading and installing ' + plugin_name + "..."}); - installer.install(plugin_name, function (progress) { - socket.emit("progress", progress); + installer.install(plugin_name, function (er) { + socket.emit("finished:install", {error: er}); }); }); socket.on("uninstall", function (plugin_name) { - socket.emit("progress", {progress:0, message:'Uninstalling ' + plugin_name + "..."}); - installer.uninstall(plugin_name, function (progress) { - socket.emit("progress", progress); + installer.uninstall(plugin_name, function (er) { + socket.emit("finished:uninstall", {error: er}); }); }); }); diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index d7f94d2c..8aff0f68 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -12,165 +12,86 @@ $(document).ready(function () { //connect socket = io.connect(url, {resource : resource}).of("/pluginfw/installer"); - $('.search-results').data('query', { - pattern: '', - offset: 0, - limit: 12, - }); - - var doUpdate = false; - - var search = function () { - socket.emit("search", $('.search-results').data('query')); - tasks++; + function search(searchTerm) { + if(search.searchTerm != searchTerm) { + search.offset = 0 + search.results = [] + search.end = false + } + search.searchTerm = searchTerm; + socket.emit("search", {searchTerm: searchTerm, offset:search.offset, length: search.limit}); + search.offset += search.limit; } + search.offset = 0 + search.limit = 12 + search.results = [] + search.end = true// have we received all results already? - function updateHandlers() { - $("form").submit(function(){ - var query = $('.search-results').data('query'); - query.pattern = $("#search-query").val(); - query.offset = 0; - search(); - return false; - }); - - $("#search-query").unbind('keyup').keyup(function () { - var query = $('.search-results').data('query'); - query.pattern = $("#search-query").val(); - query.offset = 0; - search(); - }); - - $(".do-install, .do-update").unbind('click').click(function (e) { - var row = $(e.target).closest("tr"); - doUpdate = true; - socket.emit("install", row.find(".name").text()); - tasks++; - }); - - $(".do-uninstall").unbind('click').click(function (e) { - var row = $(e.target).closest("tr"); - doUpdate = true; - socket.emit("uninstall", row.find(".name").text()); - tasks++; - }); - - $(".do-prev-page").unbind('click').click(function (e) { - var query = $('.search-results').data('query'); - query.offset -= query.limit; - if (query.offset < 0) { - query.offset = 0; - } - search(); - }); - $(".do-next-page").unbind('click').click(function (e) { - var query = $('.search-results').data('query'); - var total = $('.search-results').data('total'); - if (query.offset + query.limit < total) { - query.offset += query.limit; - } - search(); - }); - } - - updateHandlers(); - - var tasks = 0; - socket.on('progress', function (data) { - $("#progress").show(); - $('#progress').data('progress', data.progress); - - var message = "Unknown status"; - if (data.message) { - message = data.message.toString(); - } - if (data.error) { - data.progress = 1; - } - - $("#progress .message").html(message); - - if (data.progress >= 1) { - tasks--; - if (tasks <= 0) { - // Hide the activity indicator once all tasks are done - $("#progress").hide(); - tasks = 0; - } - - if (data.error) { - alert('An error occurred: '+data.error+' -- the server log might know more...'); - }else { - if (doUpdate) { - doUpdate = false; - socket.emit("load"); - tasks++; - } - } - } - }); - - socket.on('search-result', function (data) { - var widget=$(".search-results"); - - widget.data('query', data.query); - widget.data('total', data.total); - - widget.find('.offset').html(data.query.offset); - if (data.query.offset + data.query.limit > data.total){ - widget.find('.limit').html(data.total); - }else{ - widget.find('.limit').html(data.query.offset + data.query.limit); - } - widget.find('.total').html(data.total); - - widget.find(".results *").remove(); - for (plugin_name in data.results) { - var plugin = data.results[plugin_name]; - var row = widget.find(".template tr").clone(); + function displayPluginList(plugins, container, template) { + plugins.forEach(function(plugin) { + var row = template.clone(); for (attr in plugin) { if(attr == "name"){ // Hack to rewrite URLS into name - row.find(".name").html(""+plugin[attr]+""); + row.find(".name").html(""+plugin['name']+""); }else{ row.find("." + attr).html(plugin[attr]); } } - row.find(".version").html( data.results[plugin_name]['dist-tags'].latest ); + row.find(".version").html( plugin.version ); - widget.find(".results").append(row); - } - + container.append(row); + }) updateHandlers(); + } + + function updateHandlers() { + // Search + $("#search-query").unbind('keyup').keyup(function () { + search($("#search-query").val()); + }); + + // update & install + $(".do-install, .do-update").unbind('click').click(function (e) { + var row = $(e.target).closest("tr"); + socket.emit("install", row.find(".name").text()); + }); + + // uninstall + $(".do-uninstall").unbind('click').click(function (e) { + var row = $(e.target).closest("tr"); + socket.emit("uninstall", row.find(".name").text()); + }); + + // Infinite scroll + $(window).unbind('scroll').scroll(function() { + if(search.end) return;// don't keep requesting if there are no more results + var top = $('.search-results .results > tr').last().offset().top + if($(window).scrollTop()+$(window).height() > top) search(search.searchTerm) + }) + } + + socket.on('results:search', function (data) { + console.log('got search results', data) + search.results = search.results.concat(data.results); + if(!data.results.length) search.end = true; + + var searchWidget = $(".search-results"); + searchWidget.find(".results *").remove(); + displayPluginList(search.results, searchWidget.find(".results"), searchWidget.find(".template tr")) }); - socket.on('installed-results', function (data) { + socket.on('results:installed', function (data) { $("#installed-plugins *").remove(); - - for (plugin_name in data.results) { - if (plugin_name == "ep_etherpad-lite") continue; // Hack... - var plugin = data.results[plugin_name]; - var row = $("#installed-plugin-template").clone(); - - for (attr in plugin.package) { - if(attr == "name"){ // Hack to rewrite URLS into name - row.find(".name").html(""+plugin.package[attr]+""); - }else{ - row.find("." + attr).html(plugin.package[attr]); - } - } - $("#installed-plugins").append(row); - } - updateHandlers(); + displayPluginList(data.installed, $("#installed-plugins"), $("#installed-plugin-template")) setTimeout(function() { socket.emit('checkUpdates'); - tasks++; }, 5000) }); - socket.on('updatable', function(data) { - $('#installed-plugins>tr').each(function(i,tr) { + socket.on('results:updatable', function(data) { + $('#installed-plugins > tr').each(function(i, tr) { var pluginName = $(tr).find('.name').text() if (data.updatable.indexOf(pluginName) >= 0) { @@ -182,8 +103,8 @@ $(document).ready(function () { updateHandlers(); }) - socket.emit("load"); - tasks++; - - search(); + // init + updateHandlers(); + socket.emit("getInstalled"); + search(''); }); diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 7c2a7abf..d6fff9f0 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -86,11 +86,8 @@ - - .. of . - - + From 1ebbcd2f30895e4e0ec54a49c2e49905ab52f626 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 25 Mar 2013 17:22:51 +0100 Subject: [PATCH 05/24] Don't leak event listeners in pluginfw/installer.js fixes #921 --- src/static/js/pluginfw/installer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 949fdae8..1ef34733 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -2,9 +2,12 @@ var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks"); var npm = require("npm"); -var withNpm = function (npmfn, cb) { +var npmIsLoaded = false; +var withNpm = function (npmfn) { + if(npmIsLoaded) return npmfn(); npm.load({}, function (er) { if (er) return cb(er); + npmIsLoaded = true; npm.on("log", function (message) { console.log('npm: ',message) }); From 773293991b2ad4877d2c360cef59ccc4cd7562f5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 25 Mar 2013 23:09:03 +0100 Subject: [PATCH 06/24] admin/plugins: Allow people to sort search results --- src/node/hooks/express/adminplugins.js | 14 +++++- src/static/css/admin.css | 13 +++++ src/static/js/admin/plugins.js | 66 +++++++++++++++++++++----- src/templates/admin/plugins.html | 6 +-- 4 files changed, 84 insertions(+), 15 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 6b7c01e0..70ace317 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -70,10 +70,11 @@ exports.socketio = function (hook_name, args, cb) { results = {} } var res = Object.keys(results) - .slice(query.offset, query.offset+query.length) .map(function(pluginName) { return results[pluginName] }); + res = sortPluginList(res, query.sortBy, query.sortDir) + .slice(query.offset, query.offset+query.limit); socket.emit("results:search", {results: res, query: query}); }); }); @@ -91,3 +92,14 @@ exports.socketio = function (hook_name, args, cb) { }); }); } + +function sortPluginList(plugins, property, /*ASC?*/dir) { + return plugins.sort(function(a, b) { + if (a[property] < b[property]) + return dir? -1 : 1; + if (a[property] > b[property]) + return dir? 1 : -1; + // a must be equal to b + return 0; + }) +} \ No newline at end of file diff --git a/src/static/css/admin.css b/src/static/css/admin.css index b6823842..8160ee98 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -102,6 +102,19 @@ input[type="text"] { max-width: 500px; } +.sort { + cursor: pointer; +} +.sort:after { + content: '▲▼' +} +.sort.up:after { + content:'▲' +} +.sort.down:after { + content:'▼' +} + table { border: 1px solid #ddd; border-radius: 3px; diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 8aff0f68..308413bb 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -12,20 +12,23 @@ $(document).ready(function () { //connect socket = io.connect(url, {resource : resource}).of("/pluginfw/installer"); - function search(searchTerm) { + function search(searchTerm, limit) { if(search.searchTerm != searchTerm) { search.offset = 0 search.results = [] search.end = false } + limit = limit? limit : search.limit search.searchTerm = searchTerm; - socket.emit("search", {searchTerm: searchTerm, offset:search.offset, length: search.limit}); - search.offset += search.limit; + socket.emit("search", {searchTerm: searchTerm, offset:search.offset, limit: limit, sortBy: search.sortBy, sortDir: search.sortDir}); + search.offset += limit; } - search.offset = 0 - search.limit = 12 - search.results = [] - search.end = true// have we received all results already? + search.offset = 0; + search.limit = 12; + search.results = []; + search.sortBy = 'name'; + search.sortDir = /*DESC?*/true; + search.end = true;// have we received all results already? function displayPluginList(plugins, container, template) { plugins.forEach(function(plugin) { @@ -44,6 +47,17 @@ $(document).ready(function () { }) updateHandlers(); } + + function sortPluginList(plugins, property, /*ASC?*/dir) { + return plugins.sort(function(a, b) { + if (a[property] < b[property]) + return dir? -1 : 1; + if (a[property] > b[property]) + return dir? 1 : -1; + // a must be equal to b + return 0; + }) + } function updateHandlers() { // Search @@ -66,24 +80,54 @@ $(document).ready(function () { // Infinite scroll $(window).unbind('scroll').scroll(function() { if(search.end) return;// don't keep requesting if there are no more results - var top = $('.search-results .results > tr').last().offset().top + var top = $('.search-results .results > tr:last').offset().top if($(window).scrollTop()+$(window).height() > top) search(search.searchTerm) }) + + // Sort + $('.sort.up').unbind('click').click(function() { + search.sortBy = $(this).text().toLowerCase(); + search.sortDir = false; + search.offset = 0; + search.results = []; + search(search.searchTerm, search.results.length); + }) + $('.sort.down, .sort.none').unbind('click').click(function() { + search.sortBy = $(this).text().toLowerCase(); + search.sortDir = true; + search.offset = 0; + search.results = []; + search(search.searchTerm); + }) } socket.on('results:search', function (data) { - console.log('got search results', data) - search.results = search.results.concat(data.results); if(!data.results.length) search.end = true; + console.log('got search results', data) + + // add to results + search.results = search.results.concat(data.results); + + // Update sorting head + $('.sort') + .removeClass('up down') + .addClass('none'); + $('.search-results thead th[data-label='+data.query.sortBy+']') + .removeClass('none') + .addClass(data.query.sortDir? 'up' : 'down'); + + // re-render search results var searchWidget = $(".search-results"); searchWidget.find(".results *").remove(); displayPluginList(search.results, searchWidget.find(".results"), searchWidget.find(".template tr")) }); socket.on('results:installed', function (data) { + sortPluginList(data.installed, 'name', /*ASC?*/true); + $("#installed-plugins *").remove(); - displayPluginList(data.installed, $("#installed-plugins"), $("#installed-plugin-template")) + displayPluginList(data.installed, $("#installed-plugins"), $("#installed-plugin-template")); setTimeout(function() { socket.emit('checkUpdates'); diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index d6fff9f0..5b90aeab 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -67,9 +67,9 @@ - - - + + + From b35d9c14fda2904d4a3dd1e55a6fdb6ea52f1416 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 25 Mar 2013 23:52:10 +0100 Subject: [PATCH 07/24] /admin/plugins:Hide ep_etherpad-lite in the list of installed plugins --- src/static/js/admin/plugins.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 308413bb..1c00bbfa 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -127,6 +127,9 @@ $(document).ready(function () { sortPluginList(data.installed, 'name', /*ASC?*/true); $("#installed-plugins *").remove(); + data.installed = data.installed.filter(function(plugin) { + return plugin.name != 'ep_etherpad-lite' + }) displayPluginList(data.installed, $("#installed-plugins"), $("#installed-plugin-template")); setTimeout(function() { From e8bae61cf5cfb568a237be4932460626409a056f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 11:19:36 +0100 Subject: [PATCH 08/24] /admin/plugins: Add progress indicators and report errors --- src/node/hooks/express/adminplugins.js | 6 +- src/static/css/admin.css | 22 +++++-- src/static/js/admin/plugins.js | 39 ++++++++++-- src/templates/admin/plugins.html | 87 ++++++++++++++------------ 4 files changed, 101 insertions(+), 53 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 70ace317..bf796ee7 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -81,13 +81,15 @@ exports.socketio = function (hook_name, args, cb) { socket.on("install", function (plugin_name) { installer.install(plugin_name, function (er) { - socket.emit("finished:install", {error: er}); + if(er) console.warn(er) + socket.emit("finished:install", {error: er? er.message : null}); }); }); socket.on("uninstall", function (plugin_name) { installer.uninstall(plugin_name, function (er) { - socket.emit("finished:uninstall", {error: er}); + if(er) console.warn(er) + socket.emit("finished:uninstall", {error: er? er.message : null}); }); }); }); diff --git a/src/static/css/admin.css b/src/static/css/admin.css index 8160ee98..d1c86ac5 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -35,7 +35,8 @@ div.menu li:last-child { div.innerwrapper { padding: 15px; - padding-left: 265px; + margin-left: 265px; + position:relative; /* Allows us to position the loading indicator relative to this div */ } #wrapper { @@ -121,6 +122,7 @@ table { border-spacing: 0; width: 100%; margin: 20px 0; + position:relative; /* Allows us to position the loading indicator relative to the table */ } table thead tr { @@ -135,13 +137,23 @@ td, th { display: none; } -#progress { +.progress { position: absolute; - bottom: 50px; + top: 0; left: 0; bottom:0; right:0; + padding: auto; + padding-top: 20%; + + background: rgba(255,255,255,0.85); + display: none; + /*border-radius: 7px; + border: 1px solid #ddd;*/ } -#progress img { - vertical-align: top; +.progress * { + display: block; + margin: 0 auto; + text-align: center; + color: #999; } .settings { diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 1c00bbfa..b43f26bb 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -22,6 +22,7 @@ $(document).ready(function () { search.searchTerm = searchTerm; socket.emit("search", {searchTerm: searchTerm, offset:search.offset, limit: limit, sortBy: search.sortBy, sortDir: search.sortDir}); search.offset += limit; + $('#search-progress').show() } search.offset = 0; search.limit = 12; @@ -59,6 +60,18 @@ $(document).ready(function () { }) } + var progress = { + show: function(msg) { + $('#progress').show() + $('#progress .message').text(msg) + $(window).scrollTop(0) + }, + hide: function() { + $('#progress').hide() + $('#progress .message').text('') + } + } + function updateHandlers() { // Search $("#search-query").unbind('keyup').keyup(function () { @@ -67,14 +80,18 @@ $(document).ready(function () { // update & install $(".do-install, .do-update").unbind('click').click(function (e) { - var row = $(e.target).closest("tr"); - socket.emit("install", row.find(".name").text()); + var row = $(e.target).closest("tr") + , plugin = row.find(".name").text(); + socket.emit("install", plugin); + progress.show('Installing plugin '+plugin+'...') }); // uninstall $(".do-uninstall").unbind('click').click(function (e) { - var row = $(e.target).closest("tr"); - socket.emit("uninstall", row.find(".name").text()); + var row = $(e.target).closest("tr") + , plugin = row.find(".name").text(); + socket.emit("uninstall", plugin); + progress.show('Uninstalling plugin '+plugin+'...') }); // Infinite scroll @@ -121,16 +138,18 @@ $(document).ready(function () { var searchWidget = $(".search-results"); searchWidget.find(".results *").remove(); displayPluginList(search.results, searchWidget.find(".results"), searchWidget.find(".template tr")) + $('#search-progress').hide() }); socket.on('results:installed', function (data) { sortPluginList(data.installed, 'name', /*ASC?*/true); - $("#installed-plugins *").remove(); data.installed = data.installed.filter(function(plugin) { return plugin.name != 'ep_etherpad-lite' }) + $("#installed-plugins *").remove(); displayPluginList(data.installed, $("#installed-plugins"), $("#installed-plugin-template")); + progress.hide() setTimeout(function() { socket.emit('checkUpdates'); @@ -150,6 +169,16 @@ $(document).ready(function () { updateHandlers(); }) + socket.on('finished:install', function(data) { + if(data.error) alert('An error occured while installing the plugin. \n'+data.error) + socket.emit("getInstalled"); + }) + + socket.on('finished:uninstall', function(data) { + if(data.error) alert('An error occured while uninstalling the plugin. \n'+data.error) + socket.emit("getInstalled"); + }) + // init updateHandlers(); socket.emit("getInstalled"); diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 5b90aeab..1e798432 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -28,65 +28,70 @@
  • Troubleshooting information
  • <% e.end_block(); %> -
      
    -

    Installed plugins

    -
    NameDescriptionVersionNameDescriptionVersion
    - - - - - - - - - - - - - - - - - - -
    NameDescriptionVersion
    - -
    - -
    -
    - -

    Available plugins

    -
    - -
    - +

    Installed plugins

    - - - + + + - + + + - +
    NameDescriptionVersionNameDescriptionVersion
    - -
    -
    + +
    +
    + +

    Available plugins

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + +
    NameDescriptionVersion
    + +
    +
    +
    +
    +

    From 9109bd206ee44afd390db85ec0e7e8839ff8579a Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 11:20:12 +0100 Subject: [PATCH 09/24] Catch all errors in pluginfw/installer.js --- src/static/js/pluginfw/installer.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 1ef34733..56f18e10 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -6,7 +6,7 @@ var npmIsLoaded = false; var withNpm = function (npmfn) { if(npmIsLoaded) return npmfn(); npm.load({}, function (er) { - if (er) return cb(er); + if (er) return npmfn(er); npmIsLoaded = true; npm.on("log", function (message) { console.log('npm: ',message) @@ -16,7 +16,8 @@ var withNpm = function (npmfn) { } exports.uninstall = function(plugin_name, cb) { - withNpm(function () { + withNpm(function (er) { + if (er) return cb && cb(er); npm.commands.uninstall([plugin_name], function (er) { if (er) return cb && cb(er); hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function (er, data) { @@ -30,16 +31,17 @@ exports.uninstall = function(plugin_name, cb) { }; exports.install = function(plugin_name, cb) { - withNpm(function () { - npm.commands.install([plugin_name], function (er) { - if (er) return cb && cb(er); - hooks.aCallAll("pluginInstall", {plugin_name: plugin_name}, function (er, data) { - if (er) return cb(er); - plugins.update(cb); - cb && cb(); - hooks.aCallAll("restartServer", {}, function () {}); - }); + withNpm(function (er) { + if (er) return cb && cb(er); + npm.commands.install([plugin_name], function (er) { + if (er) return cb && cb(er); + hooks.aCallAll("pluginInstall", {plugin_name: plugin_name}, function (er, data) { + if (er) return cb(er); + plugins.update(cb); + cb && cb(); + hooks.aCallAll("restartServer", {}, function () {}); }); + }); }); }; @@ -47,7 +49,8 @@ exports.availablePlugins = null; var cacheTimestamp = 0; exports.getAvailablePlugins = function(maxCacheAge, cb) { - withNpm(function () { + withNpm(function (er) { + if (er) return cb && cb(er); if(exports.availablePlugins && maxCacheAge && Math.round(+new Date/1000)-cacheTimestamp <= maxCacheAge) { return cb && cb(null, exports.availablePlugins) } From 5d7a8adcb77ea53dea1109f74cf601c465909bfe Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 11:33:04 +0100 Subject: [PATCH 10/24] Silence npm when using npm.commands.search --- src/static/js/pluginfw/installer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 56f18e10..9723ec67 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -54,7 +54,7 @@ exports.getAvailablePlugins = function(maxCacheAge, cb) { if(exports.availablePlugins && maxCacheAge && Math.round(+new Date/1000)-cacheTimestamp <= maxCacheAge) { return cb && cb(null, exports.availablePlugins) } - npm.commands.search(['ep_'], function(er, results) { + npm.commands.search(['ep_'], /*silent?*/true, function(er, results) { if(er) return cb && cb(er); exports.availablePlugins = results; cacheTimestamp = Math.round(+new Date/1000); From 511407241a920a32746c5c1a97d1629ec99ffb01 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 11:38:51 +0100 Subject: [PATCH 11/24] /admin/plugins: Make it display the same amount of plugins after sorting --- src/static/js/admin/plugins.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index b43f26bb..8f03f3b9 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -106,15 +106,15 @@ $(document).ready(function () { search.sortBy = $(this).text().toLowerCase(); search.sortDir = false; search.offset = 0; - search.results = []; search(search.searchTerm, search.results.length); + search.results = []; }) $('.sort.down, .sort.none').unbind('click').click(function() { search.sortBy = $(this).text().toLowerCase(); search.sortDir = true; search.offset = 0; + search(search.searchTerm, search.results.length); search.results = []; - search(search.searchTerm); }) } From aca5d150e48810ccc98fb0c284c35b1cca1f2070 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 11:58:31 +0100 Subject: [PATCH 12/24] /admin/plugins: Don't list installed plugins as available --- src/node/hooks/express/adminplugins.js | 3 +++ src/static/js/admin/plugins.js | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index bf796ee7..3ef34115 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -72,6 +72,9 @@ exports.socketio = function (hook_name, args, cb) { var res = Object.keys(results) .map(function(pluginName) { return results[pluginName] + }) + .filter(function(plugin) { + return !plugins.plugins[plugin.name] }); res = sortPluginList(res, query.sortBy, query.sortDir) .slice(query.offset, query.offset+query.limit); diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 8f03f3b9..49477678 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -80,16 +80,17 @@ $(document).ready(function () { // update & install $(".do-install, .do-update").unbind('click').click(function (e) { - var row = $(e.target).closest("tr") - , plugin = row.find(".name").text(); + var $row = $(e.target).closest("tr") + , plugin = $row.find(".name").text(); socket.emit("install", plugin); progress.show('Installing plugin '+plugin+'...') }); // uninstall $(".do-uninstall").unbind('click').click(function (e) { - var row = $(e.target).closest("tr") - , plugin = row.find(".name").text(); + var $row = $(e.target).closest("tr") + , plugin = $row.find(".name").text(); + $row.remove() socket.emit("uninstall", plugin); progress.show('Uninstalling plugin '+plugin+'...') }); @@ -172,11 +173,21 @@ $(document).ready(function () { socket.on('finished:install', function(data) { if(data.error) alert('An error occured while installing the plugin. \n'+data.error) socket.emit("getInstalled"); + + // update search results + search.offset = 0; + search(search.searchTerm, search.results.length); + search.results = []; }) socket.on('finished:uninstall', function(data) { if(data.error) alert('An error occured while uninstalling the plugin. \n'+data.error) socket.emit("getInstalled"); + + // update search results + search.offset = 0; + search(search.searchTerm, search.results.length); + search.results = []; }) // init From 981a33f01ef1c1b66a03abdc971d9bd35669b412 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 14:40:19 +0100 Subject: [PATCH 13/24] pluginfw/installer.js fire callbacks only once --- src/static/js/pluginfw/installer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 9723ec67..377be35e 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -23,7 +23,6 @@ exports.uninstall = function(plugin_name, cb) { hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function (er, data) { if (er) return cb(er); plugins.update(cb); - cb && cb(); hooks.aCallAll("restartServer", {}, function () {}); }); }); @@ -38,7 +37,6 @@ exports.install = function(plugin_name, cb) { hooks.aCallAll("pluginInstall", {plugin_name: plugin_name}, function (er, data) { if (er) return cb(er); plugins.update(cb); - cb && cb(); hooks.aCallAll("restartServer", {}, function () {}); }); }); From 638cea5fd65cb0845cbb290bcaf6630ffea76a24 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 15:11:30 +0100 Subject: [PATCH 14/24] Install and uninstall plugins with style - Don't block the whole page when installing a plugin - allow people to search and install other plugins meanwhile Why? http://i.imgur.com/XoX6uYS.jpg --- src/node/hooks/express/adminplugins.js | 4 +- src/static/css/admin.css | 18 ++++-- src/static/js/admin/plugins.js | 77 ++++++++++++++++---------- src/templates/admin/plugins.html | 11 +++- 4 files changed, 70 insertions(+), 40 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 3ef34115..611a6b14 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -85,14 +85,14 @@ exports.socketio = function (hook_name, args, cb) { socket.on("install", function (plugin_name) { installer.install(plugin_name, function (er) { if(er) console.warn(er) - socket.emit("finished:install", {error: er? er.message : null}); + socket.emit("finished:install", {plugin: plugin_name, error: er? er.message : null}); }); }); socket.on("uninstall", function (plugin_name) { installer.uninstall(plugin_name, function (er) { if(er) console.warn(er) - socket.emit("finished:uninstall", {error: er? er.message : null}); + socket.emit("finished:uninstall", {plugin: plugin_name, error: er? er.message : null}); }); }); }); diff --git a/src/static/css/admin.css b/src/static/css/admin.css index d1c86ac5..c65aafd0 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -36,7 +36,6 @@ div.menu li:last-child { div.innerwrapper { padding: 15px; margin-left: 265px; - position:relative; /* Allows us to position the loading indicator relative to this div */ } #wrapper { @@ -137,23 +136,30 @@ td, th { display: none; } +#installed-plugins td>div { + position: relative;/* Allows us to position the loading indicator relative to this row */ + display: inline-block; /*make this fill the whole cell*/ +} + .progress { position: absolute; top: 0; left: 0; bottom:0; right:0; padding: auto; - padding-top: 20%; - background: rgba(255,255,255,0.85); + background: rgba(255,255,255,0.95); display: none; - /*border-radius: 7px; - border: 1px solid #ddd;*/ +} + +#search-progress.progress { + padding-top: 20%; + background: rgba(255,255,255,0.7); } .progress * { display: block; margin: 0 auto; text-align: center; - color: #999; + color: #666; } .settings { diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 49477678..ed854b7d 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -31,6 +31,20 @@ $(document).ready(function () { search.sortDir = /*DESC?*/true; search.end = true;// have we received all results already? + var installed = { + progress: { + show: function(plugin, msg) { + $('#installed-plugins .'+plugin+' .progress').show() + $('#installed-plugins .'+plugin+' .progress .message').text(msg) + }, + hide: function(plugin) { + $('#installed-plugins .'+plugin+' .progress').hide() + $('#installed-plugins .'+plugin+' .progress .message').text('') + } + }, + list: [] + } + function displayPluginList(plugins, container, template) { plugins.forEach(function(plugin) { var row = template.clone(); @@ -43,7 +57,7 @@ $(document).ready(function () { } } row.find(".version").html( plugin.version ); - + row.addClass(plugin.name) container.append(row); }) updateHandlers(); @@ -60,18 +74,6 @@ $(document).ready(function () { }) } - var progress = { - show: function(msg) { - $('#progress').show() - $('#progress .message').text(msg) - $(window).scrollTop(0) - }, - hide: function() { - $('#progress').hide() - $('#progress .message').text('') - } - } - function updateHandlers() { // Search $("#search-query").unbind('keyup').keyup(function () { @@ -82,17 +84,20 @@ $(document).ready(function () { $(".do-install, .do-update").unbind('click').click(function (e) { var $row = $(e.target).closest("tr") , plugin = $row.find(".name").text(); + $row.remove().appendTo('#installed-plugins') socket.emit("install", plugin); - progress.show('Installing plugin '+plugin+'...') + installed.progress.show(plugin, 'Installing') }); // uninstall $(".do-uninstall").unbind('click').click(function (e) { var $row = $(e.target).closest("tr") - , plugin = $row.find(".name").text(); - $row.remove() - socket.emit("uninstall", plugin); - progress.show('Uninstalling plugin '+plugin+'...') + , pluginName = $row.find(".name").text(); + socket.emit("uninstall", pluginName); + installed.progress.show(pluginName, 'Uninstalling') + installed.list = installed.list.filter(function(plugin) { + return plugin.name != pluginName + }) }); // Infinite scroll @@ -143,18 +148,19 @@ $(document).ready(function () { }); socket.on('results:installed', function (data) { - sortPluginList(data.installed, 'name', /*ASC?*/true); + installed.list = data.installed + sortPluginList(installed.list, 'name', /*ASC?*/true); - data.installed = data.installed.filter(function(plugin) { + // filter out epl + installed.list = installed.list.filter(function(plugin) { return plugin.name != 'ep_etherpad-lite' }) - $("#installed-plugins *").remove(); - displayPluginList(data.installed, $("#installed-plugins"), $("#installed-plugin-template")); - progress.hide() - setTimeout(function() { - socket.emit('checkUpdates'); - }, 5000) + // remove all installed plugins (leave all plugins that are still being installed) + installed.list.forEach(function(plugin) { + $('#installed-plugins .'+plugin.name).remove() + }) + displayPluginList(installed.list, $("#installed-plugins"), $("#installed-plugin-template")); }); socket.on('results:updatable', function(data) { @@ -171,7 +177,11 @@ $(document).ready(function () { }) socket.on('finished:install', function(data) { - if(data.error) alert('An error occured while installing the plugin. \n'+data.error) + if(data.error) { + alert('An error occured while installing '+data.plugin+' \n'+data.error) + $('#installed-plugins .'+data.plugin).remove() + } + socket.emit("getInstalled"); // update search results @@ -181,9 +191,13 @@ $(document).ready(function () { }) socket.on('finished:uninstall', function(data) { - if(data.error) alert('An error occured while uninstalling the plugin. \n'+data.error) - socket.emit("getInstalled"); + if(data.error) alert('An error occured while uninstalling the '+data.plugin+' \n'+data.error) + + // remove plugin from installed list + $('#installed-plugins .'+data.plugin).remove() + socket.emit("getInstalled"); + // update search results search.offset = 0; search(search.searchTerm, search.results.length); @@ -194,4 +208,9 @@ $(document).ready(function () { updateHandlers(); socket.emit("getInstalled"); search(''); + + // check for updates every 15mins + setInterval(function() { + socket.emit('checkUpdates'); + }, 1000*60*15) }); diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 1e798432..e9c34917 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -47,7 +47,10 @@ - +
    + +

    +
    @@ -78,7 +81,10 @@ - +
    + +

    +
    @@ -91,7 +97,6 @@ -

    From 7edfff75743a876d94411e9a1313a8f19777502c Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 16:23:47 +0100 Subject: [PATCH 15/24] /admin/plugins: Show some text if nothing is display otherwise --- src/static/css/admin.css | 9 +++++++++ src/static/js/admin/plugins.js | 21 ++++++++++++++++++--- src/templates/admin/plugins.html | 16 ++++++++++++---- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/static/css/admin.css b/src/static/css/admin.css index c65aafd0..7a2e00f4 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -141,6 +141,15 @@ td, th { display: inline-block; /*make this fill the whole cell*/ } +.messages * { + text-align: center; + display: none; +} + +.messages .fetching { + display: block; +} + .progress { position: absolute; top: 0; left: 0; bottom:0; right:0; diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index ed854b7d..f640ed83 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -87,6 +87,7 @@ $(document).ready(function () { $row.remove().appendTo('#installed-plugins') socket.emit("install", plugin); installed.progress.show(plugin, 'Installing') + $(".installed-results .nothing-installed").hide() }); // uninstall @@ -126,6 +127,8 @@ $(document).ready(function () { socket.on('results:search', function (data) { if(!data.results.length) search.end = true; + $(".search-results .nothing-found").hide() + $(".search-results .fetching").hide() console.log('got search results', data) @@ -143,11 +146,18 @@ $(document).ready(function () { // re-render search results var searchWidget = $(".search-results"); searchWidget.find(".results *").remove(); - displayPluginList(search.results, searchWidget.find(".results"), searchWidget.find(".template tr")) + if(search.results.length > 0) { + displayPluginList(search.results, searchWidget.find(".results"), searchWidget.find(".template tr")) + }else { + $(".search-results .nothing-found").show() + } $('#search-progress').hide() }); socket.on('results:installed', function (data) { + $(".installed-results .nothing-installed").hide() + $(".installed-results .fetching").hide() + installed.list = data.installed sortPluginList(installed.list, 'name', /*ASC?*/true); @@ -156,11 +166,16 @@ $(document).ready(function () { return plugin.name != 'ep_etherpad-lite' }) - // remove all installed plugins (leave all plugins that are still being installed) + // remove all installed plugins (leave plugins that are still being installed) installed.list.forEach(function(plugin) { $('#installed-plugins .'+plugin.name).remove() }) - displayPluginList(installed.list, $("#installed-plugins"), $("#installed-plugin-template")); + + if(installed.list.length > 0) { + displayPluginList(installed.list, $("#installed-plugins"), $("#installed-plugin-template")); + }else { + $(".installed-results .nothing-installed").show() + } }); socket.on('results:updatable', function(data) { diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index e9c34917..8ca24644 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -32,7 +32,7 @@

    Installed plugins

    - +
    @@ -56,6 +56,12 @@ + + +
    Name
    +

    You haven't installed any plugins yet.

    +

    Fetching installed plugins...

    +
    @@ -91,9 +97,11 @@ - -
    - + +
    +

    No plugins found.

    +

    Fetching catalogue...

    +
    From f75a839cd093480684f2a0790b67dd9bd7324197 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 18:39:46 +0100 Subject: [PATCH 16/24] Remove plugin prefix in pluin lists and make links to plugins more clear --- src/static/css/admin.css | 22 ++++++++++++++++++++-- src/static/js/admin/plugins.js | 7 ++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/static/css/admin.css b/src/static/css/admin.css index 7a2e00f4..56019129 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -155,7 +155,7 @@ td, th { top: 0; left: 0; bottom:0; right:0; padding: auto; - background: rgba(255,255,255,0.95); + background: rgb(255,255,255); display: none; } @@ -187,7 +187,25 @@ a:link, a:visited, a:hover, a:focus { } a:focus, a:hover { - border-bottom: #333333 1px solid; + text-decoration: underline; +} + +.installed-results a:link, +.search-results a:link, +.installed-results a:visited, +.search-results a:visited, +.installed-results a:hover, +.search-results a:hover, +.installed-results a:focus, +.search-results a:focus { + text-decoration: underline; +} + +.installed-results a:focus, +.search-results a:focus, +.installed-results a:hover, +.search-results a:hover { + text-decoration: none; } pre { diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index f640ed83..21edee6b 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -51,13 +51,14 @@ $(document).ready(function () { for (attr in plugin) { if(attr == "name"){ // Hack to rewrite URLS into name - row.find(".name").html(""+plugin['name']+""); + row.find(".name").html(""+plugin['name'].substr(3)+""); // remove 'ep_' }else{ row.find("." + attr).html(plugin[attr]); } } row.find(".version").html( plugin.version ); row.addClass(plugin.name) + row.data('plugin', plugin.name) container.append(row); }) updateHandlers(); @@ -83,7 +84,7 @@ $(document).ready(function () { // update & install $(".do-install, .do-update").unbind('click').click(function (e) { var $row = $(e.target).closest("tr") - , plugin = $row.find(".name").text(); + , plugin = $row.data('plugin'); $row.remove().appendTo('#installed-plugins') socket.emit("install", plugin); installed.progress.show(plugin, 'Installing') @@ -93,7 +94,7 @@ $(document).ready(function () { // uninstall $(".do-uninstall").unbind('click').click(function (e) { var $row = $(e.target).closest("tr") - , pluginName = $row.find(".name").text(); + , pluginName = $row.data('plugin'); socket.emit("uninstall", pluginName); installed.progress.show(pluginName, 'Uninstalling') installed.list = installed.list.filter(function(plugin) { From 2393dcd65259e232006ad5c956f19eeee60370e7 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 19:22:04 +0100 Subject: [PATCH 17/24] Disable search until registry is loaded and fix sorting by version ... and always display a scrollbar. --- src/static/css/admin.css | 2 +- src/static/js/admin/plugins.js | 1 + src/templates/admin/plugins.html | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/static/css/admin.css b/src/static/css/admin.css index 56019129..3108655e 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -43,7 +43,7 @@ div.innerwrapper { box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.2); margin: auto; max-width: 1150px; - min-height: 100%; + min-height: 101%;/*always display a scrollbar*/ } h1 { diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 21edee6b..53e16ff3 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -130,6 +130,7 @@ $(document).ready(function () { if(!data.results.length) search.end = true; $(".search-results .nothing-found").hide() $(".search-results .fetching").hide() + $("#search-query").removeAttr('disabled') console.log('got search results', data) diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 8ca24644..fe1f607a 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -69,7 +69,7 @@

    Available plugins

    - +
    @@ -77,7 +77,7 @@ - + From 4edb3b7ab3a7f2aeb24d510dd103b079d67c31a1 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 19:32:15 +0100 Subject: [PATCH 18/24] /admin/plugins: Fix infinite scroll for larger screens --- src/static/js/admin/plugins.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 53e16ff3..440b5879 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -75,6 +75,16 @@ $(document).ready(function () { }) } + // Infinite scroll + $(window).scroll(checkInfiniteScroll) + function checkInfiniteScroll() { + if(search.end) return;// don't keep requesting if there are no more results + try{ + var top = $('.search-results .results > tr:last').offset().top + if($(window).scrollTop()+$(window).height() > top) search(search.searchTerm) + }catch(e){} + } + function updateHandlers() { // Search $("#search-query").unbind('keyup').keyup(function () { @@ -102,13 +112,6 @@ $(document).ready(function () { }) }); - // Infinite scroll - $(window).unbind('scroll').scroll(function() { - if(search.end) return;// don't keep requesting if there are no more results - var top = $('.search-results .results > tr:last').offset().top - if($(window).scrollTop()+$(window).height() > top) search(search.searchTerm) - }) - // Sort $('.sort.up').unbind('click').click(function() { search.sortBy = $(this).text().toLowerCase(); @@ -154,6 +157,7 @@ $(document).ready(function () { $(".search-results .nothing-found").show() } $('#search-progress').hide() + checkInfiniteScroll() }); socket.on('results:installed', function (data) { From 806926d0f6013369096577e7ad9389e0579a8b98 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 19:54:23 +0100 Subject: [PATCH 19/24] /admin/plugins: If a user installs sth scroll up to the loading indicator --- src/static/js/admin/plugins.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 440b5879..2ffb55b0 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -36,6 +36,7 @@ $(document).ready(function () { show: function(plugin, msg) { $('#installed-plugins .'+plugin+' .progress').show() $('#installed-plugins .'+plugin+' .progress .message').text(msg) + $(window).scrollTop(0) }, hide: function(plugin) { $('#installed-plugins .'+plugin+' .progress').hide() From 76c879bb4705212747b9d2e0d546a5db6d97bc68 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 20:41:24 +0100 Subject: [PATCH 20/24] /admin/plugins: Fix for smaller screens --- src/static/css/admin.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/css/admin.css b/src/static/css/admin.css index 3108655e..322fe41c 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -35,7 +35,7 @@ div.menu li:last-child { div.innerwrapper { padding: 15px; - margin-left: 265px; + padding-left: 265px; } #wrapper { From d01a209cbf3377bc9c03719b00f2a3adbbb5b386 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 21:04:21 +0100 Subject: [PATCH 21/24] /admin/plugins: Dry up displaying of info messages --- src/static/css/admin.css | 4 +-- src/static/js/admin/plugins.js | 42 +++++++++++++++++++++++--------- src/templates/admin/plugins.html | 8 +++--- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/static/css/admin.css b/src/static/css/admin.css index 322fe41c..31b16b9c 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -141,9 +141,9 @@ td, th { display: inline-block; /*make this fill the whole cell*/ } -.messages * { - text-align: center; +.messages td>* { display: none; + text-align: center; } .messages .fetching { diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 2ffb55b0..3937e666 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -30,17 +30,37 @@ $(document).ready(function () { search.sortBy = 'name'; search.sortDir = /*DESC?*/true; search.end = true;// have we received all results already? + search.messages = { + show: function(msg) { + $('.search-results .messages').show() + $('.search-results .messages .'+msg+'').show() + }, + hide: function(msg) { + $('.search-results .messages').hide() + $('.search-results .messages .'+msg+'').hide() + } + } var installed = { progress: { show: function(plugin, msg) { - $('#installed-plugins .'+plugin+' .progress').show() - $('#installed-plugins .'+plugin+' .progress .message').text(msg) + $('.installed-results .'+plugin+' .progress').show() + $('.installed-results .'+plugin+' .progress .message').text(msg) $(window).scrollTop(0) }, hide: function(plugin) { - $('#installed-plugins .'+plugin+' .progress').hide() - $('#installed-plugins .'+plugin+' .progress .message').text('') + $('.installed-results .'+plugin+' .progress').hide() + $('.installed-results .'+plugin+' .progress .message').text('') + } + }, + messages: { + show: function(msg) { + $('.installed-results .messages').show() + $('.installed-results .messages .'+msg+'').show() + }, + hide: function(msg) { + $('.installed-results .messages').hide() + $('.installed-results .messages .'+msg+'').hide() } }, list: [] @@ -99,7 +119,7 @@ $(document).ready(function () { $row.remove().appendTo('#installed-plugins') socket.emit("install", plugin); installed.progress.show(plugin, 'Installing') - $(".installed-results .nothing-installed").hide() + installed.messages.hide("nothing-installed") }); // uninstall @@ -132,8 +152,8 @@ $(document).ready(function () { socket.on('results:search', function (data) { if(!data.results.length) search.end = true; - $(".search-results .nothing-found").hide() - $(".search-results .fetching").hide() + search.messages.hide('nothing-found') + search.messages.hide('fetching') $("#search-query").removeAttr('disabled') console.log('got search results', data) @@ -155,15 +175,15 @@ $(document).ready(function () { if(search.results.length > 0) { displayPluginList(search.results, searchWidget.find(".results"), searchWidget.find(".template tr")) }else { - $(".search-results .nothing-found").show() + search.messages.show('nothing-found') } $('#search-progress').hide() checkInfiniteScroll() }); socket.on('results:installed', function (data) { - $(".installed-results .nothing-installed").hide() - $(".installed-results .fetching").hide() + installed.messages.hide("fetching") + installed.messages.hide("nothing-installed") installed.list = data.installed sortPluginList(installed.list, 'name', /*ASC?*/true); @@ -181,7 +201,7 @@ $(document).ready(function () { if(installed.list.length > 0) { displayPluginList(installed.list, $("#installed-plugins"), $("#installed-plugin-template")); }else { - $(".installed-results .nothing-installed").show() + installed.messages.show("nothing-installed") } }); diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index fe1f607a..270fbfd7 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -56,8 +56,8 @@ - - + @@ -96,8 +96,8 @@ - - + - -
    Name DescriptionVersionVersion
    +

    You haven't installed any plugins yet.

    Fetching installed plugins...

    +

    No plugins found.

    Fetching catalogue...

    From c4d9a71156c39d84350230e88ef9ebab148f1a2a Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 27 Mar 2013 12:02:19 +0100 Subject: [PATCH 22/24] /admin/plugins: Fix update check --- src/node/hooks/express/adminplugins.js | 4 ++-- src/static/css/admin.css | 1 + src/static/js/admin/plugins.js | 27 +++++++++++++------------- src/templates/admin/plugins.html | 8 ++++---- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 611a6b14..d8f19bba 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -35,9 +35,9 @@ exports.socketio = function (hook_name, args, cb) { socket.emit("results:installed", {installed: installed}); }); - socket.on("getUpdatable", function() { + socket.on("checkUpdates", function() { // Check plugins for updates - installer.getAvailable(/*maxCacheAge:*/60*10, function(er, results) { + installer.getAvailablePlugins(/*maxCacheAge:*/60*10, function(er, results) { if(er) { console.warn(er); socket.emit("results:updatable", {updatable: {}}); diff --git a/src/static/css/admin.css b/src/static/css/admin.css index 31b16b9c..2260e27d 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -139,6 +139,7 @@ td, th { #installed-plugins td>div { position: relative;/* Allows us to position the loading indicator relative to this row */ display: inline-block; /*make this fill the whole cell*/ + width:100%; } .messages td>* { diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index 3937e666..bc7a0208 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -46,7 +46,7 @@ $(document).ready(function () { show: function(plugin, msg) { $('.installed-results .'+plugin+' .progress').show() $('.installed-results .'+plugin+' .progress .message').text(msg) - $(window).scrollTop(0) + $(window).scrollTop($('.'+plugin).offset().top-100) }, hide: function(plugin) { $('.installed-results .'+plugin+' .progress').hide() @@ -116,9 +116,13 @@ $(document).ready(function () { $(".do-install, .do-update").unbind('click').click(function (e) { var $row = $(e.target).closest("tr") , plugin = $row.data('plugin'); - $row.remove().appendTo('#installed-plugins') + if($(this).hasClass('do-install')) { + $row.remove().appendTo('#installed-plugins') + installed.progress.show(plugin, 'Installing') + }else{ + installed.progress.show(plugin, 'Updating') + } socket.emit("install", plugin); - installed.progress.show(plugin, 'Installing') installed.messages.hide("nothing-installed") }); @@ -200,20 +204,17 @@ $(document).ready(function () { if(installed.list.length > 0) { displayPluginList(installed.list, $("#installed-plugins"), $("#installed-plugin-template")); + socket.emit('checkUpdates'); }else { installed.messages.show("nothing-installed") } }); socket.on('results:updatable', function(data) { - $('#installed-plugins > tr').each(function(i, tr) { - var pluginName = $(tr).find('.name').text() - - if (data.updatable.indexOf(pluginName) >= 0) { - var actions = $(tr).find('.actions') - actions.append('') - actions.css('width', 200) - } + data.updatable.forEach(function(pluginName) { + var $row = $('#installed-plugins > tr.'+pluginName) + , actions = $row.find('.actions') + actions.append('') }) updateHandlers(); }) @@ -251,8 +252,8 @@ $(document).ready(function () { socket.emit("getInstalled"); search(''); - // check for updates every 15mins + // check for updates every 5mins setInterval(function() { socket.emit('checkUpdates'); - }, 1000*60*15) + }, 1000*60*5) }); diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 270fbfd7..1b69549c 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -46,8 +46,8 @@
    -
    +
    +

    @@ -86,8 +86,8 @@
    -
    +
    +

    From bc8d6d4c45f443716d3334e1606cf7664b9a97ac Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 27 Mar 2013 12:20:50 +0100 Subject: [PATCH 23/24] /admin/plugins: Add a loading indicator to some messages --- src/static/js/admin/plugins.js | 2 +- src/templates/admin/plugins.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index bc7a0208..e2e07e96 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -46,7 +46,7 @@ $(document).ready(function () { show: function(plugin, msg) { $('.installed-results .'+plugin+' .progress').show() $('.installed-results .'+plugin+' .progress .message').text(msg) - $(window).scrollTop($('.'+plugin).offset().top-100) + if($(window).scrollTop() > $('.'+plugin).offset().top)$(window).scrollTop($('.'+plugin).offset().top-100) }, hide: function(plugin) { $('.installed-results .'+plugin+' .progress').hide() diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 1b69549c..44e6f7a5 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -59,7 +59,7 @@

    You haven't installed any plugins yet.

    -

    Fetching installed plugins...

    +


    Fetching installed plugins...

    @@ -100,7 +100,7 @@

    No plugins found.

    -

    Fetching catalogue...

    +


    Fetching catalogue...

    From cbee50d42d8a528c524ad5a76a54bf59e8978689 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 27 Mar 2013 12:28:54 +0100 Subject: [PATCH 24/24] /admin/plugins: Display a tooltip when hovering the plugin details link --- src/static/js/admin/plugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/admin/plugins.js b/src/static/js/admin/plugins.js index e2e07e96..41affa74 100644 --- a/src/static/js/admin/plugins.js +++ b/src/static/js/admin/plugins.js @@ -72,7 +72,7 @@ $(document).ready(function () { for (attr in plugin) { if(attr == "name"){ // Hack to rewrite URLS into name - row.find(".name").html(""+plugin['name'].substr(3)+""); // remove 'ep_' + row.find(".name").html(""+plugin['name'].substr(3)+""); // remove 'ep_' }else{ row.find("." + attr).html(plugin[attr]); }