From aca5d24a7d83916fcf4ebf22560a7e05c34a30c2 Mon Sep 17 00:00:00 2001 From: Julian Lam <julian@designcreateplay.com> Date: Tue, 26 May 2015 14:45:17 -0400 Subject: [PATCH] split groups.js into more subsidiary files --- src/groups.js | 482 +-------------------------------------- src/groups/membership.js | 317 +++++++++++++++++++++++++ src/groups/ownership.js | 31 +++ src/groups/search.js | 86 +++++++ src/groups/update.js | 73 +++++- 5 files changed, 510 insertions(+), 479 deletions(-) create mode 100644 src/groups/membership.js create mode 100644 src/groups/ownership.js create mode 100644 src/groups/search.js diff --git a/src/groups.js b/src/groups.js index 0c667c9871..17d39c413b 100644 --- a/src/groups.js +++ b/src/groups.js @@ -2,8 +2,6 @@ var async = require('async'), winston = require('winston'), - _ = require('underscore'), - crypto = require('crypto'), path = require('path'), nconf = require('nconf'), fs = require('fs'), @@ -16,15 +14,16 @@ var async = require('async'), posts = require('./posts'), privileges = require('./privileges'), utils = require('../public/src/utils'), - util = require('util'), - - uploadsController = require('./controllers/uploads'); + util = require('util'); (function(Groups) { require('./groups/create')(Groups); require('./groups/delete')(Groups); require('./groups/update')(Groups); + require('./groups/membership')(Groups); + require('./groups/ownership')(Groups); + require('./groups/search')(Groups); var ephemeralGroups = ['guests'], @@ -70,6 +69,8 @@ var async = require('async'), } }; + Groups.internals = internals; + var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/; Groups.isPrivilegeGroup = function(groupName) { return isPrivilegeGroupRegex.test(groupName); @@ -330,144 +331,6 @@ var async = require('async'), }); }; - Groups.getMembers = function(groupName, start, stop, callback) { - db.getSortedSetRevRange('group:' + groupName + ':members', start, stop, callback); - }; - - Groups.getMembersOfGroups = function(groupNames, callback) { - db.getSortedSetsMembers(groupNames.map(function(name) { - return 'group:' + name + ':members'; - }), callback); - }; - - Groups.isMember = function(uid, groupName, callback) { - if (!uid || parseInt(uid, 10) <= 0) { - return callback(null, false); - } - db.isSortedSetMember('group:' + groupName + ':members', uid, callback); - }; - - Groups.isMembers = function(uids, groupName, callback) { - db.isSortedSetMembers('group:' + groupName + ':members', uids, callback); - }; - - Groups.isMemberOfGroups = function(uid, groups, callback) { - if (!uid || parseInt(uid, 10) <= 0) { - return callback(null, groups.map(function() {return false;})); - } - groups = groups.map(function(groupName) { - return 'group:' + groupName + ':members'; - }); - - db.isMemberOfSortedSets(groups, uid, callback); - }; - - Groups.getMemberCount = function(groupName, callback) { - db.getObjectField('group:' + groupName, 'memberCount', function(err, count) { - if (err) { - return callback(err); - } - callback(null, parseInt(count, 10)); - }); - }; - - Groups.isMemberOfGroupList = function(uid, groupListKey, callback) { - db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function(err, groupNames) { - if (err) { - return callback(err); - } - groupNames = internals.removeEphemeralGroups(groupNames); - if (groupNames.length === 0) { - return callback(null, false); - } - - Groups.isMemberOfGroups(uid, groupNames, function(err, isMembers) { - if (err) { - return callback(err); - } - - callback(null, isMembers.indexOf(true) !== -1); - }); - }); - }; - - Groups.isMemberOfGroupsList = function(uid, groupListKeys, callback) { - var sets = groupListKeys.map(function(groupName) { - return 'group:' + groupName + ':members'; - }); - - db.getSortedSetsMembers(sets, function(err, members) { - if (err) { - return callback(err); - } - - var uniqueGroups = _.unique(_.flatten(members)); - uniqueGroups = internals.removeEphemeralGroups(uniqueGroups); - - Groups.isMemberOfGroups(uid, uniqueGroups, function(err, isMembers) { - if (err) { - return callback(err); - } - - var map = {}; - - uniqueGroups.forEach(function(groupName, index) { - map[groupName] = isMembers[index]; - }); - - var result = members.map(function(groupNames) { - for (var i=0; i<groupNames.length; ++i) { - if (map[groupNames[i]]) { - return true; - } - } - return false; - }); - - callback(null, result); - }); - }); - }; - - Groups.isMembersOfGroupList = function(uids, groupListKey, callback) { - db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function(err, groupNames) { - if (err) { - return callback(err); - } - - var results = []; - uids.forEach(function() { - results.push(false); - }); - - groupNames = internals.removeEphemeralGroups(groupNames); - if (groupNames.length === 0) { - return callback(null, results); - } - - async.each(groupNames, function(groupName, next) { - Groups.isMembers(uids, groupName, function(err, isMembers) { - if (err) { - return next(err); - } - results.forEach(function(isMember, index) { - if (!isMember && isMembers[index]) { - results[index] = true; - } - }); - next(); - }); - }, function(err) { - callback(err, results); - }); - }); - }; - - Groups.isInvited = function(uid, groupName, callback) { - if (!uid) { return callback(null, false); } - db.isSetMember('group:' + groupName + ':invited', uid, callback); - }; - Groups.exists = function(name, callback) { if (Array.isArray(name)) { var slugs = name.map(function(groupName) { @@ -510,179 +373,6 @@ var async = require('async'), } }; - Groups.hide = function(groupName, callback) { - callback = callback || function() {}; - db.setObjectField('group:' + groupName, 'hidden', 1, callback); - }; - - Groups.join = function(groupName, uid, callback) { - function join() { - var tasks = [ - async.apply(db.sortedSetAdd, 'group:' + groupName + ':members', Date.now(), uid), - async.apply(db.incrObjectField, 'group:' + groupName, 'memberCount') - ]; - - async.waterfall([ - function(next) { - user.isAdministrator(uid, next); - }, - function(isAdmin, next) { - if (isAdmin) { - tasks.push(async.apply(db.setAdd, 'group:' + groupName + ':owners', uid)); - } - async.parallel(tasks, next); - }, - function(results, next) { - user.setGroupTitle(groupName, uid, next); - }, - function(next) { - plugins.fireHook('action:group.join', { - groupName: groupName, - uid: uid - }); - next(); - } - ], callback); - } - - callback = callback || function() {}; - - Groups.exists(groupName, function(err, exists) { - if (err) { - return callback(err); - } - - if (exists) { - return join(); - } - - Groups.create({ - name: groupName, - description: '', - hidden: 1 - }, function(err) { - if (err && err.message !== '[[error:group-already-exists]]') { - winston.error('[groups.join] Could not create new hidden group: ' + err.message); - return callback(err); - } - join(); - }); - }); - }; - - Groups.requestMembership = function(groupName, uid, callback) { - async.parallel({ - exists: async.apply(Groups.exists, groupName), - isMember: async.apply(Groups.isMember, uid, groupName) - }, function(err, checks) { - if (!checks.exists) { - return callback(new Error('[[error:no-group]]')); - } else if (checks.isMember) { - return callback(new Error('[[error:group-already-member]]')); - } - - if (parseInt(uid, 10) > 0) { - db.setAdd('group:' + groupName + ':pending', uid, callback); - plugins.fireHook('action:group.requestMembership', { - groupName: groupName, - uid: uid - }); - } else { - callback(new Error('[[error:not-logged-in]]')); - } - }); - }; - - Groups.acceptMembership = function(groupName, uid, callback) { - // Note: For simplicity, this method intentially doesn't check the caller uid for ownership! - async.waterfall([ - async.apply(db.setRemove, 'group:' + groupName + ':pending', uid), - async.apply(db.setRemove, 'group:' + groupName + ':invited', uid), - async.apply(Groups.join, groupName, uid) - ], callback); - }; - - Groups.rejectMembership = function(groupName, uid, callback) { - // Note: For simplicity, this method intentially doesn't check the caller uid for ownership! - async.parallel([ - async.apply(db.setRemove, 'group:' + groupName + ':pending', uid), - async.apply(db.setRemove, 'group:' + groupName + ':invited', uid) - ], callback); - }; - - Groups.invite = function(groupName, uid, callback) { - async.parallel({ - exists: async.apply(Groups.exists, groupName), - isMember: async.apply(Groups.isMember, uid, groupName) - }, function(err, checks) { - if (!checks.exists) { - return callback(new Error('[[error:no-group]]')); - } else if (checks.isMember) { - return callback(new Error('[[error:group-already-member]]')); - } - - if (parseInt(uid, 10) > 0) { - db.setAdd('group:' + groupName + ':invited', uid, callback); - plugins.fireHook('action:group.inviteMember', { - groupName: groupName, - uid: uid - }); - } else { - callback(new Error('[[error:not-logged-in]]')); - } - }); - }; - - Groups.leave = function(groupName, uid, callback) { - callback = callback || function() {}; - - var tasks = [ - async.apply(db.sortedSetRemove, 'group:' + groupName + ':members', uid), - async.apply(db.setRemove, 'group:' + groupName + ':owners', uid), - async.apply(db.decrObjectField, 'group:' + groupName, 'memberCount') - ]; - - async.parallel(tasks, function(err) { - if (err) { - return callback(err); - } - - plugins.fireHook('action:group.leave', { - groupName: groupName, - uid: uid - }); - - Groups.getGroupFields(groupName, ['hidden', 'memberCount'], function(err, groupData) { - if (err || !groupData) { - return callback(err); - } - - if (parseInt(groupData.hidden, 10) === 1 && parseInt(groupData.memberCount, 10) === 0) { - Groups.destroy(groupName, callback); - } else { - callback(); - } - }); - }); - }; - - Groups.leaveAllGroups = function(uid, callback) { - db.getSortedSetRange('groups:createtime', 0, -1, function(err, groups) { - if (err) { - return callback(err); - } - async.each(groups, function(groupName, next) { - Groups.isMember(uid, groupName, function(err, isMember) { - if (!err && isMember) { - Groups.leave(groupName, uid, next); - } else { - next(); - } - }); - }, callback); - }); - }; - Groups.getLatestMemberPosts = function(groupName, max, uid, callback) { async.waterfall([ async.apply(Groups.getMembers, groupName, 0, -1), @@ -780,164 +470,4 @@ var async = require('async'), }); }); }; - - Groups.updateCoverPosition = function(groupName, position, callback) { - Groups.setGroupField(groupName, 'cover:position', position, callback); - }; - - Groups.updateCover = function(data, callback) { - var tempPath, md5sum, url; - - // Position only? That's fine - if (!data.imageData && data.position) { - return Groups.updateCoverPosition(data.groupName, data.position, callback); - } - - async.series([ - function(next) { - // Calculate md5sum of image - // This is required because user data can be private - md5sum = crypto.createHash('md5'); - md5sum.update(data.imageData); - md5sum = md5sum.digest('hex'); - next(); - }, - function(next) { - // Save image - tempPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), md5sum); - var buffer = new Buffer(data.imageData.slice(data.imageData.indexOf('base64') + 7), 'base64'); - - fs.writeFile(tempPath, buffer, { - encoding: 'base64' - }, next); - }, - function(next) { - uploadsController.uploadGroupCover({ - path: tempPath - }, function(err, uploadData) { - if (err) { - return next(err); - } - - url = uploadData.url; - next(); - }); - }, - function(next) { - Groups.setGroupField(data.groupName, 'cover:url', url, next); - }, - function(next) { - fs.unlink(tempPath, next); // Delete temporary file - } - ], function(err) { - if (err) { - return callback(err); - } - - Groups.updateCoverPosition(data.groupName, data.position, callback); - }); - }; - - Groups.ownership = {}; - - Groups.ownership.isOwner = function(uid, groupName, callback) { - // Note: All admins automatically become owners upon joining - db.isSetMember('group:' + groupName + ':owners', uid, callback); - }; - - Groups.ownership.grant = function(toUid, groupName, callback) { - // Note: No ownership checking is done here on purpose! - db.setAdd('group:' + groupName + ':owners', toUid, callback); - }; - - Groups.ownership.rescind = function(toUid, groupName, callback) { - // Note: No ownership checking is done here on purpose! - - // If the owners set only contains one member, error out! - db.setCount('group:' + groupName + ':owners', function(err, numOwners) { - if (numOwners <= 1) { - return callback(new Error('[[error:group-needs-owner]]')); - } - - db.setRemove('group:' + groupName + ':owners', toUid, callback); - }); - }; - - Groups.search = function(query, options, callback) { - if (!query) { - return callback(null, []); - } - query = query.toLowerCase(); - async.waterfall([ - async.apply(db.getObjectValues, 'groupslug:groupname'), - function(groupNames, next) { - groupNames = groupNames.filter(function(name) { - return name.toLowerCase().indexOf(query) !== -1 && name !== 'administrators'; - }); - groupNames = groupNames.slice(0, 100); - Groups.getGroupsData(groupNames, next); - }, - async.apply(Groups.sort, options.sort) - ], callback); - }; - - Groups.sort = function(strategy, groups, next) { - switch(strategy) { - case 'count': - groups = groups.sort(function(a, b) { - return a.slug > b.slug; - }).sort(function(a, b) { - return a.memberCount < b.memberCount; - }); - break; - - case 'date': - groups = groups.sort(function(a, b) { - return a.createtime < b.createtime; - }); - break; - - case 'alpha': // intentional fall-through - default: - groups = groups.sort(function(a, b) { - return a.slug > b.slug ? 1 : -1; - }); - } - - next(null, groups); - }; - - Groups.searchMembers = function(data, callback) { - - function findUids(query, searchBy, callback) { - if (!query) { - return Groups.getMembers(data.groupName, 0, -1, callback); - } - - query = query.toLowerCase(); - - async.waterfall([ - function(next) { - Groups.getMembers(data.groupName, 0, -1, next); - }, - function(members, next) { - user.getMultipleUserFields(members, ['uid'].concat([searchBy]), next); - }, - function(users, next) { - var uids = []; - for(var i=0; i<users.length; ++i) { - var field = users[i][searchBy]; - if (field.toLowerCase().startsWith(query)) { - uids.push(users[i].uid); - } - } - next(null, uids); - } - ], callback); - } - - data.findUids = findUids; - user.search(data, callback); - }; - }(module.exports)); diff --git a/src/groups/membership.js b/src/groups/membership.js new file mode 100644 index 0000000000..152029d6b1 --- /dev/null +++ b/src/groups/membership.js @@ -0,0 +1,317 @@ +'use strict'; + +var async = require('async'), + winston = require('winston'), + _ = require('underscore'), + + user = require('../user'), + plugins = require('../plugins'), + db = require('./../database'); + +module.exports = function(Groups) { + Groups.join = function(groupName, uid, callback) { + function join() { + var tasks = [ + async.apply(db.sortedSetAdd, 'group:' + groupName + ':members', Date.now(), uid), + async.apply(db.incrObjectField, 'group:' + groupName, 'memberCount') + ]; + + async.waterfall([ + function(next) { + user.isAdministrator(uid, next); + }, + function(isAdmin, next) { + if (isAdmin) { + tasks.push(async.apply(db.setAdd, 'group:' + groupName + ':owners', uid)); + } + async.parallel(tasks, next); + }, + function(results, next) { + user.setGroupTitle(groupName, uid, next); + }, + function(next) { + plugins.fireHook('action:group.join', { + groupName: groupName, + uid: uid + }); + next(); + } + ], callback); + } + + callback = callback || function() {}; + + Groups.exists(groupName, function(err, exists) { + if (err) { + return callback(err); + } + + if (exists) { + return join(); + } + + Groups.create({ + name: groupName, + description: '', + hidden: 1 + }, function(err) { + if (err && err.message !== '[[error:group-already-exists]]') { + winston.error('[groups.join] Could not create new hidden group: ' + err.message); + return callback(err); + } + join(); + }); + }); + }; + + Groups.requestMembership = function(groupName, uid, callback) { + async.parallel({ + exists: async.apply(Groups.exists, groupName), + isMember: async.apply(Groups.isMember, uid, groupName) + }, function(err, checks) { + if (!checks.exists) { + return callback(new Error('[[error:no-group]]')); + } else if (checks.isMember) { + return callback(new Error('[[error:group-already-member]]')); + } + + if (parseInt(uid, 10) > 0) { + db.setAdd('group:' + groupName + ':pending', uid, callback); + plugins.fireHook('action:group.requestMembership', { + groupName: groupName, + uid: uid + }); + } else { + callback(new Error('[[error:not-logged-in]]')); + } + }); + }; + + Groups.acceptMembership = function(groupName, uid, callback) { + // Note: For simplicity, this method intentially doesn't check the caller uid for ownership! + async.waterfall([ + async.apply(db.setRemove, 'group:' + groupName + ':pending', uid), + async.apply(db.setRemove, 'group:' + groupName + ':invited', uid), + async.apply(Groups.join, groupName, uid) + ], callback); + }; + + Groups.rejectMembership = function(groupName, uid, callback) { + // Note: For simplicity, this method intentially doesn't check the caller uid for ownership! + async.parallel([ + async.apply(db.setRemove, 'group:' + groupName + ':pending', uid), + async.apply(db.setRemove, 'group:' + groupName + ':invited', uid) + ], callback); + }; + + Groups.invite = function(groupName, uid, callback) { + async.parallel({ + exists: async.apply(Groups.exists, groupName), + isMember: async.apply(Groups.isMember, uid, groupName) + }, function(err, checks) { + if (!checks.exists) { + return callback(new Error('[[error:no-group]]')); + } else if (checks.isMember) { + return callback(new Error('[[error:group-already-member]]')); + } + + if (parseInt(uid, 10) > 0) { + db.setAdd('group:' + groupName + ':invited', uid, callback); + plugins.fireHook('action:group.inviteMember', { + groupName: groupName, + uid: uid + }); + } else { + callback(new Error('[[error:not-logged-in]]')); + } + }); + }; + + Groups.leave = function(groupName, uid, callback) { + callback = callback || function() {}; + + var tasks = [ + async.apply(db.sortedSetRemove, 'group:' + groupName + ':members', uid), + async.apply(db.setRemove, 'group:' + groupName + ':owners', uid), + async.apply(db.decrObjectField, 'group:' + groupName, 'memberCount') + ]; + + async.parallel(tasks, function(err) { + if (err) { + return callback(err); + } + + plugins.fireHook('action:group.leave', { + groupName: groupName, + uid: uid + }); + + Groups.getGroupFields(groupName, ['hidden', 'memberCount'], function(err, groupData) { + if (err || !groupData) { + return callback(err); + } + + if (parseInt(groupData.hidden, 10) === 1 && parseInt(groupData.memberCount, 10) === 0) { + Groups.destroy(groupName, callback); + } else { + callback(); + } + }); + }); + }; + + Groups.leaveAllGroups = function(uid, callback) { + db.getSortedSetRange('groups:createtime', 0, -1, function(err, groups) { + if (err) { + return callback(err); + } + async.each(groups, function(groupName, next) { + Groups.isMember(uid, groupName, function(err, isMember) { + if (!err && isMember) { + Groups.leave(groupName, uid, next); + } else { + next(); + } + }); + }, callback); + }); + }; + + Groups.getMembers = function(groupName, start, stop, callback) { + db.getSortedSetRevRange('group:' + groupName + ':members', start, stop, callback); + }; + + Groups.getMembersOfGroups = function(groupNames, callback) { + db.getSortedSetsMembers(groupNames.map(function(name) { + return 'group:' + name + ':members'; + }), callback); + }; + + Groups.isMember = function(uid, groupName, callback) { + if (!uid || parseInt(uid, 10) <= 0) { + return callback(null, false); + } + db.isSortedSetMember('group:' + groupName + ':members', uid, callback); + }; + + Groups.isMembers = function(uids, groupName, callback) { + db.isSortedSetMembers('group:' + groupName + ':members', uids, callback); + }; + + Groups.isMemberOfGroups = function(uid, groups, callback) { + if (!uid || parseInt(uid, 10) <= 0) { + return callback(null, groups.map(function() {return false;})); + } + groups = groups.map(function(groupName) { + return 'group:' + groupName + ':members'; + }); + + db.isMemberOfSortedSets(groups, uid, callback); + }; + + Groups.getMemberCount = function(groupName, callback) { + db.getObjectField('group:' + groupName, 'memberCount', function(err, count) { + if (err) { + return callback(err); + } + callback(null, parseInt(count, 10)); + }); + }; + + Groups.isMemberOfGroupList = function(uid, groupListKey, callback) { + db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function(err, groupNames) { + if (err) { + return callback(err); + } + groupNames = Groups.internals.removeEphemeralGroups(groupNames); + if (groupNames.length === 0) { + return callback(null, false); + } + + Groups.isMemberOfGroups(uid, groupNames, function(err, isMembers) { + if (err) { + return callback(err); + } + + callback(null, isMembers.indexOf(true) !== -1); + }); + }); + }; + + Groups.isMemberOfGroupsList = function(uid, groupListKeys, callback) { + var sets = groupListKeys.map(function(groupName) { + return 'group:' + groupName + ':members'; + }); + + db.getSortedSetsMembers(sets, function(err, members) { + if (err) { + return callback(err); + } + + var uniqueGroups = _.unique(_.flatten(members)); + uniqueGroups = Groups.internals.removeEphemeralGroups(uniqueGroups); + + Groups.isMemberOfGroups(uid, uniqueGroups, function(err, isMembers) { + if (err) { + return callback(err); + } + + var map = {}; + + uniqueGroups.forEach(function(groupName, index) { + map[groupName] = isMembers[index]; + }); + + var result = members.map(function(groupNames) { + for (var i=0; i<groupNames.length; ++i) { + if (map[groupNames[i]]) { + return true; + } + } + return false; + }); + + callback(null, result); + }); + }); + }; + + Groups.isMembersOfGroupList = function(uids, groupListKey, callback) { + db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function(err, groupNames) { + if (err) { + return callback(err); + } + + var results = []; + uids.forEach(function() { + results.push(false); + }); + + groupNames = Groups.internals.removeEphemeralGroups(groupNames); + if (groupNames.length === 0) { + return callback(null, results); + } + + async.each(groupNames, function(groupName, next) { + Groups.isMembers(uids, groupName, function(err, isMembers) { + if (err) { + return next(err); + } + results.forEach(function(isMember, index) { + if (!isMember && isMembers[index]) { + results[index] = true; + } + }); + next(); + }); + }, function(err) { + callback(err, results); + }); + }); + }; + + Groups.isInvited = function(uid, groupName, callback) { + if (!uid) { return callback(null, false); } + db.isSetMember('group:' + groupName + ':invited', uid, callback); + }; +}; diff --git a/src/groups/ownership.js b/src/groups/ownership.js new file mode 100644 index 0000000000..38d5a699d8 --- /dev/null +++ b/src/groups/ownership.js @@ -0,0 +1,31 @@ +'use strict'; + +var db = require('./../database'); + +module.exports = function(Groups) { + + Groups.ownership = {}; + + Groups.ownership.isOwner = function(uid, groupName, callback) { + // Note: All admins automatically become owners upon joining + db.isSetMember('group:' + groupName + ':owners', uid, callback); + }; + + Groups.ownership.grant = function(toUid, groupName, callback) { + // Note: No ownership checking is done here on purpose! + db.setAdd('group:' + groupName + ':owners', toUid, callback); + }; + + Groups.ownership.rescind = function(toUid, groupName, callback) { + // Note: No ownership checking is done here on purpose! + + // If the owners set only contains one member, error out! + db.setCount('group:' + groupName + ':owners', function(err, numOwners) { + if (numOwners <= 1) { + return callback(new Error('[[error:group-needs-owner]]')); + } + + db.setRemove('group:' + groupName + ':owners', toUid, callback); + }); + }; +}; diff --git a/src/groups/search.js b/src/groups/search.js new file mode 100644 index 0000000000..4629d17af2 --- /dev/null +++ b/src/groups/search.js @@ -0,0 +1,86 @@ +'use strict'; + +var async = require('async'), + + user = require('../user'), + db = require('./../database'); + +module.exports = function(Groups) { + + Groups.search = function(query, options, callback) { + if (!query) { + return callback(null, []); + } + query = query.toLowerCase(); + async.waterfall([ + async.apply(db.getObjectValues, 'groupslug:groupname'), + function(groupNames, next) { + groupNames = groupNames.filter(function(name) { + return name.toLowerCase().indexOf(query) !== -1 && name !== 'administrators'; + }); + groupNames = groupNames.slice(0, 100); + Groups.getGroupsData(groupNames, next); + }, + async.apply(Groups.sort, options.sort) + ], callback); + }; + + Groups.sort = function(strategy, groups, next) { + switch(strategy) { + case 'count': + groups = groups.sort(function(a, b) { + return a.slug > b.slug; + }).sort(function(a, b) { + return a.memberCount < b.memberCount; + }); + break; + + case 'date': + groups = groups.sort(function(a, b) { + return a.createtime < b.createtime; + }); + break; + + case 'alpha': // intentional fall-through + default: + groups = groups.sort(function(a, b) { + return a.slug > b.slug ? 1 : -1; + }); + } + + next(null, groups); + }; + + Groups.searchMembers = function(data, callback) { + + function findUids(query, searchBy, callback) { + if (!query) { + return Groups.getMembers(data.groupName, 0, -1, callback); + } + + query = query.toLowerCase(); + + async.waterfall([ + function(next) { + Groups.getMembers(data.groupName, 0, -1, next); + }, + function(members, next) { + user.getMultipleUserFields(members, ['uid'].concat([searchBy]), next); + }, + function(users, next) { + var uids = []; + for(var i=0; i<users.length; ++i) { + var field = users[i][searchBy]; + if (field.toLowerCase().startsWith(query)) { + uids.push(users[i].uid); + } + } + next(null, uids); + } + ], callback); + } + + data.findUids = findUids; + user.search(data, callback); + }; +}; diff --git a/src/groups/update.js b/src/groups/update.js index 4b2974b013..b533f63c4e 100644 --- a/src/groups/update.js +++ b/src/groups/update.js @@ -2,9 +2,16 @@ var async = require('async'), winston = require('winston'), + crypto = require('crypto'), + path = require('path'), + nconf = require('nconf'), + fs = require('fs'), + plugins = require('../plugins'), utils = require('../../public/src/utils'), - db = require('./../database'); + db = require('./../database'), + + uploadsController = require('../controllers/uploads'); module.exports = function(Groups) { @@ -55,6 +62,68 @@ module.exports = function(Groups) { }); }; + Groups.hide = function(groupName, callback) { + callback = callback || function() {}; + db.setObjectField('group:' + groupName, 'hidden', 1, callback); + }; + + Groups.updateCoverPosition = function(groupName, position, callback) { + Groups.setGroupField(groupName, 'cover:position', position, callback); + }; + + Groups.updateCover = function(data, callback) { + var tempPath, md5sum, url; + + // Position only? That's fine + if (!data.imageData && data.position) { + return Groups.updateCoverPosition(data.groupName, data.position, callback); + } + + async.series([ + function(next) { + // Calculate md5sum of image + // This is required because user data can be private + md5sum = crypto.createHash('md5'); + md5sum.update(data.imageData); + md5sum = md5sum.digest('hex'); + next(); + }, + function(next) { + // Save image + tempPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), md5sum); + var buffer = new Buffer(data.imageData.slice(data.imageData.indexOf('base64') + 7), 'base64'); + + fs.writeFile(tempPath, buffer, { + encoding: 'base64' + }, next); + }, + function(next) { + uploadsController.uploadGroupCover({ + path: tempPath + }, function(err, uploadData) { + if (err) { + return next(err); + } + + url = uploadData.url; + next(); + }); + }, + function(next) { + Groups.setGroupField(data.groupName, 'cover:url', url, next); + }, + function(next) { + fs.unlink(tempPath, next); // Delete temporary file + } + ], function(err) { + if (err) { + return callback(err); + } + + Groups.updateCoverPosition(data.groupName, data.position, callback); + }); + }; + function updatePrivacy(groupName, newValue, callback) { if (!newValue) { return callback(); @@ -160,6 +229,4 @@ module.exports = function(Groups) { ], callback); }); } - - };