diff --git a/public/src/forum/admin/plugins.js b/public/src/forum/admin/plugins.js index 01b4e4589f..68b03c4b50 100644 --- a/public/src/forum/admin/plugins.js +++ b/public/src/forum/admin/plugins.js @@ -6,30 +6,65 @@ define(function() { init: function() { var pluginsList = $('.plugins'), numPlugins = pluginsList[0].querySelectorAll('li').length, - pluginID, pluginTgl; + pluginID; if (numPlugins > 0) { + pluginsList.on('click', 'button[data-action="toggleActive"]', function() { pluginID = $(this).parents('li').attr('data-plugin-id'); - socket.emit('admin.plugins.toggle', pluginID); + var btn = $(this); + socket.emit('admin.plugins.toggleActive', pluginID, function(err, status) { + + btn.html(' ' + (status.active ? 'Dea' : 'A') + 'ctivate'); + btn.toggleClass('btn-warning', status.active).toggleClass('btn-success', !status.active); + + app.alert({ + alert_id: 'plugin_toggled', + title: 'Plugin ' + (status.active ? 'Enabled' : 'Disabled'), + message: 'Please restart your NodeBB to fully ' + (status.active ? 'activate' : 'deactivate') + ' this plugin', + type: 'info', + timeout: 5000, + clickfn: function() { + socket.emit('admin.restart'); + } + }); + }); }); - socket.on('admin.plugins.toggle', function(status) { - pluginTgl = $('.plugins li[data-plugin-id="' + status.id + '"] button'); - pluginTgl.html(' ' + (status.active ? 'Dea' : 'A') + 'ctivate'); - pluginTgl.toggleClass('btn-warning', status.active).toggleClass('btn-success', !status.active); - - app.alert({ - alert_id: 'plugin_toggled', - title: 'Plugin ' + (status.active ? 'Enabled' : 'Disabled'), - message: 'Please restart your NodeBB to fully ' + (status.active ? 'activate' : 'deactivate') + ' this plugin', - type: 'info', - timeout: 5000, - clickfn: function() { - socket.emit('admin.restart'); + pluginsList.on('click', 'button[data-action="toggleInstall"]', function() { + pluginID = $(this).parents('li').attr('data-plugin-id'); + + var btn = $(this); + btn.html(btn.html() + 'ing') + .attr('disabled', true) + .find('i').attr('class', 'fa fa-refresh fa-spin'); + + socket.emit('admin.plugins.toggleInstall', pluginID, function(err, status) { + var activateBtn = $('.plugins li[data-plugin-id="' + pluginID + '"] button[data-action="toggleActive"]'); + + if (status.installed) { + btn.html(' Uninstall'); + } else { + btn.html(' Install'); } + + btn.toggleClass('btn-warning', status.installed).toggleClass('btn-success', !status.installed) + .attr('disabled', false); + + activateBtn.toggleClass('hide', !status.installed); + activateBtn.html(' Activate'); + activateBtn.toggleClass('btn-success', true).toggleClass('btn-warning', false); + + app.alert({ + alert_id: 'plugin_toggled', + title: 'Plugin ' + (status.installed ? 'Installed' : 'Uninstalled'), + message: status.installed ? 'You still have to activate this plugin to use it!' : 'The plugin is also deactivated!', + type: 'info', + timeout: 5000 + }); }); }); + } else { pluginsList.append('
  • No plugins found.

  • '); } diff --git a/src/controllers/admin.js b/src/controllers/admin.js index e978c85a78..92ac358c62 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -80,15 +80,15 @@ adminController.events.get = function(req, res, next) { }; adminController.plugins.get = function(req, res, next) { - plugins.showInstalled(function (err, plugins) { + plugins.getAll(function(err, plugins) { if (err || !Array.isArray(plugins)) { plugins = []; } - res.render('admin/plugins', { + res.render('admin/plugins' , { plugins: plugins }); - }); + }) }; adminController.languages.get = function(req, res, next) { diff --git a/src/plugins.js b/src/plugins.js index 351629d85c..d660d90eae 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -373,20 +373,23 @@ var fs = require('fs'), Plugins.toggleActive = function(id, callback) { Plugins.isActive(id, function(err, active) { if (err) { - if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\''); - return; + if (global.env === 'development') { + winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\''); + } + return callback(err); } db[(active ? 'setRemove' : 'setAdd')]('plugins:active', id, function(err, success) { if (err) { - if (global.env === 'development') winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\''); - return; + if (global.env === 'development') { + winston.info('[plugins] Could not toggle active state on plugin \'' + id + '\''); + } + return callback(err); } - // Restart Required flag meta.restartRequired = true; - if(active) { + if (active) { Plugins.fireHook('action:plugin.deactivate', id); } @@ -397,8 +400,8 @@ var fs = require('fs'), Plugins.fireHook('action:plugin.activate', id); } - if (callback) { - callback({ + if (typeof callback === 'function') { + callback(null, { id: id, active: !active }); @@ -408,6 +411,42 @@ var fs = require('fs'), }); }; + Plugins.toggleInstall = function(id, callback) { + Plugins.isInstalled(id, function(err, installed) { + if (err) { + return callback(err); + } + + var npm = require('npm'); + + async.waterfall([ + function(next) { + Plugins.isActive(id, next); + }, + function(active, next) { + if (active) { + Plugins.toggleActive(id, function(err, status) { + next(err); + }); + return; + } + next(); + }, + function(next) { + npm.load({}, next); + }, + function(res, next) { + npm.commands[installed ? 'uninstall' : 'install'](installed ? id : [id], next); + } + ], function(err) { + callback(err, { + id: id, + installed: !installed + }); + }); + }); + }; + Plugins.getTemplates = function(callback) { var templates = {}; @@ -431,6 +470,52 @@ var fs = require('fs'), }); }; + Plugins.getAll = function(callback) { + var request = require('request'); + request('http://npm.aws.af.cm/api/v1/plugins', function(err, res, body) { + if (err) { + return callback(err); + } + var plugins = []; + try { + plugins = JSON.parse(body); + } catch(err) { + winston.error('Error parsing plugins : ' + err.message); + return callback(null, []); + } + + async.map(plugins, function(plugin, next) { + + plugin.id = plugin.name; + + async.parallel({ + active: function(next) { + Plugins.isActive(plugin.id, next); + }, + installed: function(next) { + Plugins.isInstalled(plugin.id, next); + } + }, function(err, results) { + if (err) { + return next(err); + } + plugin.active = results.active; + plugin.installed = results.installed; + next(null, plugin); + }); + + }, callback); + }); + }; + + Plugins.isInstalled = function(id, callback) { + var pluginDir = path.join(__dirname, '../node_modules', id); + + fs.stat(pluginDir, function(err, stats) { + callback(null, err ? false : stats.isDirectory()); + }); + }; + Plugins.showInstalled = function(callback) { var npmPluginPath = path.join(__dirname, '../node_modules'); @@ -483,7 +568,8 @@ var fs = require('fs'), delete config.library; delete config.hooks; config.active = active; - config.activeText = ' ' + (active ? 'Dea' : 'A') + 'ctivate'; + config.installed = true; + next(null, config); }); } diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 05286f616b..94afaa2356 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -83,10 +83,12 @@ SocketAdmin.themes.set = function(socket, data, callback) { }); }; -SocketAdmin.plugins.toggle = function(socket, plugin_id) { - plugins.toggleActive(plugin_id, function(status) { - socket.emit('admin.plugins.toggle', status); - }); +SocketAdmin.plugins.toggleActive = function(socket, plugin_id, callback) { + plugins.toggleActive(plugin_id, callback); +}; + +SocketAdmin.plugins.toggleInstall = function(socket, plugin_id, callback) { + plugins.toggleInstall(plugin_id, callback); }; SocketAdmin.widgets.set = function(socket, data, callback) {