diff --git a/public/src/client/groups/details.js b/public/src/client/groups/details.js index 12ca74884a..e8d4ed957f 100644 --- a/public/src/client/groups/details.js +++ b/public/src/client/groups/details.js @@ -1,17 +1,35 @@ "use strict"; +/* globals socket, ajaxify */ define('forum/groups/details', function() { var Details = {}; Details.init = function() { - var memberListEl = $('.groups.details .members'); - - memberListEl.on('click', '[data-slug]', function() { - var slug = this.getAttribute('data-slug'); - ajaxify.go('user/' + slug); - }); + var memberList = $('.groups .members'); $('.latest-posts .content img').addClass('img-responsive'); + + memberList.on('click', '[data-action]', function() { + var btnEl = $(this), + userRow = btnEl.parents('tr'), + ownerFlagEl = userRow.find('.member-name i'), + isOwner = !ownerFlagEl.hasClass('hidden') ? true : false, + uid = userRow.attr('data-uid'), + action = btnEl.attr('data-action'); + + switch(action) { + case 'toggleOwnership': + socket.emit('groups.' + (isOwner ? 'rescind' : 'grant'), { + toUid: uid, + groupName: ajaxify.variables.get('group_name') + }, function(err) { + if (!err) { + ownerFlagEl.toggleClass('hidden'); + } + }); + break; + } + }); }; return Details; diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 72d83d4fc2..2d2ce8f050 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -23,10 +23,12 @@ groupsController.list = function(req, res, next) { groupsController.details = function(req, res, next) { var uid = req.user ? parseInt(req.user.uid, 10) : 0; + async.parallel({ group: function(next) { groups.get(req.params.name, { - expand: true + expand: true, + uid: uid }, next); }, posts: function(next) { diff --git a/src/groups.js b/src/groups.js index 2dcb708f66..60019a47b9 100644 --- a/src/groups.js +++ b/src/groups.js @@ -71,25 +71,7 @@ var async = require('async'), groupNames = groupNames.concat(ephemeralGroups); async.map(groupNames, function (groupName, next) { - async.waterfall([ - async.apply(Groups.get, groupName, options), - function(groupObj, next) { - // Retrieve group membership state, if uid is passed in - if (!options.uid) { - return next(null, groupObj); - } - - Groups.isMember(options.uid, groupName, function(err, isMember) { - if (err) { - winston.warn('[groups.list] Could not determine membership in group `' + groupName + '` for uid `' + options.uid + '`: ' + err.message); - return next(null, groupObj); - } - - groupObj.isMember = isMember; - next(null, groupObj); - }); - } - ], next); + Groups.get(groupName, options, next); }, function (err, groups) { callback(err, internals.filterGroups(groups, options)); }); @@ -126,6 +108,9 @@ var async = require('async'), async.waterfall([ async.apply(async.map, uids, user.getUserData), function(users, next) { + // Filter out non-matches + users = users.filter(Boolean); + async.mapLimit(users, 10, function(userObj, next) { Groups.ownership.isOwner(userObj.uid, groupName, function(err, isOwner) { if (err) { @@ -143,6 +128,36 @@ var async = require('async'), next(err, uids); } }); + }, + isMember: function(next) { + // Retrieve group membership state, if uid is passed in + if (!options.uid) { + return next(); + } + + Groups.isMember(options.uid, groupName, function(err, isMember) { + if (err) { + winston.warn('[groups.get] Could not determine membership in group `' + groupName + '` for uid `' + options.uid + '`: ' + err.message); + return next(); + } + + next(null, isMember); + }); + }, + isOwner: function(next) { + // Retrieve group ownership state, if uid is passed in + if (!options.uid) { + return next(); + } + + Groups.ownership.isOwner(options.uid, groupName, function(err, isOwner) { + if (err) { + winston.warn('[groups.get] Could not determine ownership in group `' + groupName + '` for uid `' + options.uid + '`: ' + err.message); + return next(); + } + + next(null, isOwner); + }); } }, function (err, results) { if (err || !results.base) { @@ -157,6 +172,8 @@ var async = require('async'), results.base.system = !!parseInt(results.base.system, 10); results.base.deletable = !results.base.system; results.base.truncated = truncated; + results.base.isMember = results.isMember; + results.base.isOwner = results.isOwner; callback(err, results.base); }); diff --git a/src/socket.io/groups.js b/src/socket.io/groups.js index 9fd29bc2d4..943b9dc10c 100644 --- a/src/socket.io/groups.js +++ b/src/socket.io/groups.js @@ -20,4 +20,32 @@ SocketGroups.leave = function(socket, data, callback) { groups.leave(data.groupName, socket.uid, callback); }; +SocketGroups.grant = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-data]]')); + } + + groups.ownership.isOwner(socket.uid, data.groupName, function(err, isOwner) { + if (!isOwner) { + return callback(new Error('[[error:no-privileges]]')); + } + + groups.ownership.grant(data.toUid, data.groupName, callback); + }); +}; + +SocketGroups.rescind = function(socket, data, callback) { + if (!data) { + return callback(new Error('[[error:invalid-data]]')); + } + + groups.ownership.isOwner(socket.uid, data.groupName, function(err, isOwner) { + if (!isOwner) { + return callback(new Error('[[error:no-privileges]]')); + } + + groups.ownership.rescind(data.toUid, data.groupName, callback); + }); +}; + module.exports = SocketGroups;