From ab2e805aa0143ffc04d2875672ba8ac3193c9460 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 23 Mar 2013 14:50:00 +0000 Subject: [PATCH 01/32] changelog --- CHANGELOG.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 642846a6e..152af7f79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 1.2.91 + * NEW: Authors can now send custom object messages to other Authors making 3 way conversations possible. This introduces WebRTC plugin support. + * NEW: Hook for Chat Messages Allows for Desktop Notification support + * NEW: FreeBSD installation docs + * Fix: Cookies inside of plugins + * Fix: Long lines in Firefox now wrap properly + * Fix: Log HTTP on DEBUG log level + * Fix: Server wont crash on import fails on 0 file import. + * Fix: Import no longer fails consistantly + * Fix: Language support for non existing languages + * Fix: Mobile support for chat notifications are now usable + * Fix: Re-Enable Editbar buttons on reconnect + * Fix: Clearing authorship colors no longer disconnects all clients + # 1.2.9 * Fix: MAJOR Security issue, where a hacker could submit content as another user * Fix: security issue due to unescaped user input @@ -6,7 +20,7 @@ * Fix: PadUsers API endpoint * NEW: A script to import data to all dbms * NEW: Add authorId to chat and userlist as a data attribute - * NEW Refactor and fix our frontend tests + * NEW: Refactor and fix our frontend tests * NEW: Localisation updates From af80e37ac752e1004752cff275db730daf6c2292 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 23 Mar 2013 15:03:56 +0000 Subject: [PATCH 02/32] missed this one.. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 152af7f79..ca5b078fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * NEW: Hook for Chat Messages Allows for Desktop Notification support * NEW: FreeBSD installation docs * Fix: Cookies inside of plugins + * Fix: Refactor Caret navigation with Arrow and Pageup/down keys stops cursor being lost * Fix: Long lines in Firefox now wrap properly * Fix: Log HTTP on DEBUG log level * Fix: Server wont crash on import fails on 0 file import. From c78aad16eaf888c47844e3f36db9a9cde4539844 Mon Sep 17 00:00:00 2001 From: disy-mk Date: Sun, 24 Mar 2013 01:18:44 +0100 Subject: [PATCH 03/32] adds missing semicolons in src/node/utils folder --- src/node/utils/Abiword.js | 6 +++--- src/node/utils/ExportDokuWiki.js | 2 +- src/node/utils/ExportHelper.js | 8 ++++---- src/node/utils/ExportHtml.js | 5 ++--- src/node/utils/ExportTxt.js | 3 +-- src/node/utils/Minify.js | 10 +++++----- src/node/utils/Settings.js | 8 ++++---- src/node/utils/caching_middleware.js | 4 ++-- src/node/utils/padDiff.js | 22 +++++++++++----------- 9 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/node/utils/Abiword.js b/src/node/utils/Abiword.js index 27138e648..925203433 100644 --- a/src/node/utils/Abiword.js +++ b/src/node/utils/Abiword.js @@ -63,7 +63,7 @@ if(os.type().indexOf("Windows") > -1) callback(); }); - } + }; exports.convertFile = function(srcFile, destFile, type, callback) { @@ -121,7 +121,7 @@ else firstPrompt = false; } }); - } + }; spawnAbiword(); doConvertTask = function(task, callback) @@ -135,7 +135,7 @@ else console.log("queue continue"); task.callback(err); }; - } + }; //Queue with the converts we have to do var queue = async.queue(doConvertTask, 1); diff --git a/src/node/utils/ExportDokuWiki.js b/src/node/utils/ExportDokuWiki.js index d2f71236b..f5d2d177f 100644 --- a/src/node/utils/ExportDokuWiki.js +++ b/src/node/utils/ExportDokuWiki.js @@ -316,7 +316,7 @@ exports.getPadDokuWikiDocument = function (padId, revNum, callback) getPadDokuWiki(pad, revNum, callback); }); -} +}; function _escapeDokuWiki(s) { diff --git a/src/node/utils/ExportHelper.js b/src/node/utils/ExportHelper.js index a939a8b6e..136896f06 100644 --- a/src/node/utils/ExportHelper.js +++ b/src/node/utils/ExportHelper.js @@ -45,7 +45,7 @@ exports.getPadPlainText = function(pad, revNum){ } return pieces.join(''); -} +}; exports._analyzeLine = function(text, aline, apool){ @@ -77,11 +77,11 @@ exports._analyzeLine = function(text, aline, apool){ line.aline = aline; } return line; -} +}; exports._encodeWhitespace = function(s){ return s.replace(/[^\x21-\x7E\s\t\n\r]/g, function(c){ - return "&#" +c.charCodeAt(0) + ";" + return "&#" +c.charCodeAt(0) + ";"; }); -} +}; diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 585694d4b..7b94310a3 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -21,7 +21,7 @@ var padManager = require("../db/PadManager"); var ERR = require("async-stacktrace"); var Security = require('ep_etherpad-lite/static/js/security'); var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); -var getPadPlainText = require('./ExportHelper').getPadPlainText +var getPadPlainText = require('./ExportHelper').getPadPlainText; var _analyzeLine = require('./ExportHelper')._analyzeLine; var _encodeWhitespace = require('./ExportHelper')._encodeWhitespace; @@ -515,7 +515,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback) callback(null, head + html + foot); }); }); -} +}; // copied from ACE @@ -595,4 +595,3 @@ function _processSpaces(s){ } return parts.join(''); } - diff --git a/src/node/utils/ExportTxt.js b/src/node/utils/ExportTxt.js index c57424f1d..f0b62743a 100644 --- a/src/node/utils/ExportTxt.js +++ b/src/node/utils/ExportTxt.js @@ -289,5 +289,4 @@ exports.getPadTXTDocument = function (padId, revNum, noDocType, callback) callback(null, html); }); }); -} - +}; diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 5fc8accbb..58d08b30e 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -125,11 +125,11 @@ function requestURIs(locations, method, headers, callback) { } function completed() { - var statuss = responses.map(function (x) {return x[0]}); - var headerss = responses.map(function (x) {return x[1]}); - var contentss = responses.map(function (x) {return x[2]}); + var statuss = responses.map(function (x) {return x[0];}); + var headerss = responses.map(function (x) {return x[1];}); + var contentss = responses.map(function (x) {return x[2];}); callback(statuss, headerss, contentss); - }; + } } /** @@ -263,7 +263,7 @@ function getAceFile(callback) { var filename = item.match(/"([^"]*)"/)[1]; var request = require('request'); - var baseURI = 'http://localhost:' + settings.port + var baseURI = 'http://localhost:' + settings.port; var resourceURI = baseURI + path.normalize(path.join('/static/', filename)); resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?) diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 45f81aa5f..a6c71a85e 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -137,7 +137,7 @@ exports.abiwordAvailable = function() { return "no"; } -} +}; exports.reloadSettings = function reloadSettings() { // Discover where the settings file lives @@ -157,7 +157,7 @@ exports.reloadSettings = function reloadSettings() { try { if(settingsStr) { settings = vm.runInContext('exports = '+settingsStr, vm.createContext(), "settings.json"); - settings = JSON.parse(JSON.stringify(settings)) // fix objects having constructors of other vm.context + settings = JSON.parse(JSON.stringify(settings)); // fix objects having constructors of other vm.context } }catch(e){ console.error('There was an error processing your settings.json file: '+e.message); @@ -196,9 +196,9 @@ exports.reloadSettings = function reloadSettings() { } if(exports.dbType === "dirty"){ - console.warn("DirtyDB is used. This is fine for testing but not recommended for production.") + console.warn("DirtyDB is used. This is fine for testing but not recommended for production."); } -} +}; // initially load settings exports.reloadSettings(); diff --git a/src/node/utils/caching_middleware.js b/src/node/utils/caching_middleware.js index c6b237139..1d103ffd6 100644 --- a/src/node/utils/caching_middleware.js +++ b/src/node/utils/caching_middleware.js @@ -23,7 +23,7 @@ var util = require('util'); var settings = require('./Settings'); var semver = require('semver'); -var existsSync = (semver.satisfies(process.version, '>=0.8.0')) ? fs.existsSync : path.existsSync +var existsSync = (semver.satisfies(process.version, '>=0.8.0')) ? fs.existsSync : path.existsSync; var CACHE_DIR = path.normalize(path.join(settings.root, 'var/')); CACHE_DIR = existsSync(CACHE_DIR) ? CACHE_DIR : undefined; @@ -133,7 +133,7 @@ CachingMiddleware.prototype = new function () { old_res.write = res.write; old_res.end = res.end; res.write = function(data, encoding) {}; - res.end = function(data, encoding) { respond() }; + res.end = function(data, encoding) { respond(); }; } else { res.writeHead(status, headers); } diff --git a/src/node/utils/padDiff.js b/src/node/utils/padDiff.js index 1b3cf58f5..c53540417 100644 --- a/src/node/utils/padDiff.js +++ b/src/node/utils/padDiff.js @@ -68,7 +68,7 @@ PadDiff.prototype._isClearAuthorship = function(changeset){ return false; return true; -} +}; PadDiff.prototype._createClearAuthorship = function(rev, callback){ var self = this; @@ -84,7 +84,7 @@ PadDiff.prototype._createClearAuthorship = function(rev, callback){ callback(null, changeset); }); -} +}; PadDiff.prototype._createClearStartAtext = function(rev, callback){ var self = this; @@ -107,7 +107,7 @@ PadDiff.prototype._createClearStartAtext = function(rev, callback){ callback(null, newAText); }); }); -} +}; PadDiff.prototype._getChangesetsInBulk = function(startRev, count, callback) { var self = this; @@ -124,7 +124,7 @@ PadDiff.prototype._getChangesetsInBulk = function(startRev, count, callback) { async.forEach(revisions, function(rev, callback){ self._pad.getRevision(rev, function(err, revision){ if(err){ - return callback(err) + return callback(err); } var arrayNum = rev-startRev; @@ -137,7 +137,7 @@ PadDiff.prototype._getChangesetsInBulk = function(startRev, count, callback) { }, function(err){ callback(err, changesets, authors); }); -} +}; PadDiff.prototype._addAuthors = function(authors) { var self = this; @@ -147,7 +147,7 @@ PadDiff.prototype._addAuthors = function(authors) { self._authors.push(author); } }); -} +}; PadDiff.prototype._createDiffAtext = function(callback) { var self = this; @@ -219,7 +219,7 @@ PadDiff.prototype._createDiffAtext = function(callback) { } ); }); -} +}; PadDiff.prototype.getHtml = function(callback){ //cache the html @@ -279,7 +279,7 @@ PadDiff.prototype.getAuthors = function(callback){ } else { callback(null, self._authors); } -} +}; PadDiff.prototype._extendChangesetWithAuthor = function(changeset, author, apool) { //unpack @@ -312,7 +312,7 @@ PadDiff.prototype._extendChangesetWithAuthor = function(changeset, author, apool //return the modified changeset return Changeset.pack(unpacked.oldLen, unpacked.newLen, assem.toString(), unpacked.charBank); -} +}; //this method is 80% like Changeset.inverse. I just changed so instead of reverting, it adds deletions and attribute changes to to the atext. PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) { @@ -463,7 +463,7 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) { // If the text this operator applies to is only a star, than this is a false positive and should be ignored if (csOp.attribs && textBank != "*") { var deletedAttrib = apool.putAttrib(["removed", true]); - var authorAttrib = apool.putAttrib(["author", ""]);; + var authorAttrib = apool.putAttrib(["author", ""]); attribKeys.length = 0; attribValues.length = 0; @@ -473,7 +473,7 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) { if(apool.getAttribKey(n) === "author"){ authorAttrib = n; - }; + } }); var undoBackToAttribs = cachedStrFunc(function (attribs) { From 2e7a9796ded7c66a14639e261d54b7bf6c4496ec Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 24 Mar 2013 01:12:01 +0000 Subject: [PATCH 04/32] option to show sticky chat on screen, note i use a literal string, how am i supposed to add a l10n title? --- src/static/css/pad.css | 9 +++++++++ src/templates/pad.html | 7 +++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/static/css/pad.css b/src/static/css/pad.css index 320a47202..dafb77ef4 100644 --- a/src/static/css/pad.css +++ b/src/static/css/pad.css @@ -559,6 +559,15 @@ table#otheruserstable { margin: 4px 0 0 4px; position: absolute; } +#titlesticky{ + font-size: 10px; + padding-top:2px; + float: right; + text-align: right; + text-decoration: none; + cursor: pointer; + color: #555; +} #titlecross { font-size: 25px; float: right; diff --git a/src/templates/pad.html b/src/templates/pad.html index 1d8c069a5..16599e175 100644 --- a/src/templates/pad.html +++ b/src/templates/pad.html @@ -369,14 +369,17 @@ <% e.end_block(); %> -
+
0
- +
+ + █   +
loading.. From ef7fb5c7f0e233268530eb8cd6eb02224751dae5 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Mon, 25 Mar 2013 12:18:06 +0100 Subject: [PATCH 05/32] Update npm --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index 822676a38..ff6625753 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 06/32] 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 7e221cf1e..85bf2108b 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 a973875ce..d7f94d2cf 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 15d879409..14a3b7be5 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 07/32] 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 ff6625753..24f15ca9b 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 14a3b7be5..949fdae82 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 08/32] 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 85bf2108b..6b7c01e04 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 d7f94d2cf..8aff0f68f 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 7c2a7abf2..d6fff9f00 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 09/32] 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 949fdae82..1ef34733f 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 10/32] 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 6b7c01e04..70ace317f 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 b68238425..8160ee98a 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 8aff0f68f..308413bb7 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 d6fff9f00..5b90aeabe 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 11/32] /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 308413bb7..1c00bbfa4 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 6b55d13370f1057e117178c46807a59bd679de29 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 26 Mar 2013 01:54:01 +0000 Subject: [PATCH 12/32] expose ace document, reqjired for various plugins --- src/static/js/ace2_inner.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 2dc6408b1..fc69d5921 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1013,6 +1013,11 @@ function Ace2Inner(){ return caughtErrors.slice(); }; + editorInfo.ace_getDocument = function() + { + return doc; + }; + editorInfo.ace_getDebugProperty = function(prop) { if (prop == "debugger") From e8bae61cf5cfb568a237be4932460626409a056f Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 11:19:36 +0100 Subject: [PATCH 13/32] /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 70ace317f..bf796ee7b 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 8160ee98a..d1c86ac59 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 1c00bbfa4..b43f26bb1 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 5b90aeabe..1e7984321 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 14/32] 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 1ef34733f..56f18e10f 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 15/32] 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 56f18e10f..9723ec671 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 16/32] /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 b43f26bb1..8f03f3b95 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 17/32] /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 bf796ee7b..3ef341154 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 8f03f3b95..494776782 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 18/32] 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 9723ec671..377be35e9 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 19/32] 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 3ef341154..611a6b149 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 d1c86ac59..c65aafd0b 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 494776782..ed854b7d6 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 1e7984321..e9c34917e 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 20/32] /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 c65aafd0b..7a2e00f4a 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 ed854b7d6..f640ed83a 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 e9c34917e..8ca24644f 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 e96baf6ef1fde0e47e0a4afaf32078dc39e441ff Mon Sep 17 00:00:00 2001 From: neurolit Date: Tue, 26 Mar 2013 16:37:57 +0100 Subject: [PATCH 21/32] API documentation: listAllPads() returns {padIDs: [...]} instead of [...] --- doc/api/http_api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/http_api.md b/doc/api/http_api.md index 7e05ff86d..f71e0a5d0 100644 --- a/doc/api/http_api.md +++ b/doc/api/http_api.md @@ -458,4 +458,4 @@ returns ok when the current api token is valid lists all pads on this epl instance *Example returns:* - * `{code: 0, message:"ok", data: ["testPad", "thePadsOfTheOthers"]}` + * `{code: 0, message:"ok", data: {padIDs: ["testPad", "thePadsOfTheOthers"]}}` From f75a839cd093480684f2a0790b67dd9bd7324197 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 18:39:46 +0100 Subject: [PATCH 22/32] 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 7a2e00f4a..560191291 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 f640ed83a..21edee6ba 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 23/32] 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 560191291..3108655e4 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 21edee6ba..53e16ff30 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 8ca24644f..fe1f607aa 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 24/32] /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 53e16ff30..440b5879f 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 25/32] /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 440b5879f..2ffb55b0d 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 26/32] /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 3108655e4..322fe41c3 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 27/32] /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 322fe41c3..31b16b9c6 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 2ffb55b0d..3937e6665 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 fe1f607aa..270fbfd7a 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 ac0018cdfafa8b8b5de594ff9127c190d20f3f63 Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Tue, 26 Mar 2013 21:19:09 +0100 Subject: [PATCH 28/32] Don't leak event listeners for process:uncaughtException --- src/node/hooks/express/errorhandling.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/node/hooks/express/errorhandling.js b/src/node/hooks/express/errorhandling.js index 3c5956835..825c5f3b4 100644 --- a/src/node/hooks/express/errorhandling.js +++ b/src/node/hooks/express/errorhandling.js @@ -28,6 +28,7 @@ exports.gracefulShutdown = function(err) { }, 3000); } +process.on('uncaughtException', exports.gracefulShutdown); exports.expressCreateServer = function (hook_name, args, cb) { exports.app = args.app; @@ -47,6 +48,4 @@ exports.expressCreateServer = function (hook_name, args, cb) { //https://github.com/joyent/node/issues/1553 process.on('SIGINT', exports.gracefulShutdown); } - - process.on('uncaughtException', exports.gracefulShutdown); -} +} \ No newline at end of file From c4d9a71156c39d84350230e88ef9ebab148f1a2a Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 27 Mar 2013 12:02:19 +0100 Subject: [PATCH 29/32] /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 611a6b149..d8f19bba9 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 31b16b9c6..2260e27df 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 3937e6665..bc7a02082 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 270fbfd7a..1b69549c4 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 30/32] /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 bc7a02082..e2e07e967 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 1b69549c4..44e6f7a51 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 31/32] /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 e2e07e967..41affa745 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]); } From 40cbe55507fdf9eb45e092d3f9321de4f72dce22 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 27 Mar 2013 14:11:20 +0000 Subject: [PATCH 32/32] Update en.json --- src/locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 920a2b003..d08ebe657 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -16,7 +16,7 @@ "pad.toolbar.timeslider.title": "Timeslider", "pad.toolbar.savedRevision.title": "Save Revision", "pad.toolbar.settings.title": "Settings", - "pad.toolbar.embed.title": "Embed this pad", + "pad.toolbar.embed.title": "Share and Embed this pad", "pad.toolbar.showusers.title": "Show the users on this pad", "pad.colorpicker.save": "Save", "pad.colorpicker.cancel": "Cancel", @@ -113,4 +113,4 @@ "pad.impexp.importfailed": "Import failed", "pad.impexp.copypaste": "Please copy paste", "pad.impexp.exportdisabled": "Exporting as {{type}} format is disabled. Please contact your system administrator for details." -} \ No newline at end of file +}