From 1bee91fa2286987743dea737bee2741f6ae1ac0a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 1 Mar 2017 22:42:10 +0300 Subject: [PATCH] groups refactor --- src/groups.js | 666 ++++++++++++++--------------------- src/groups/data.js | 93 +++++ src/groups/membership.js | 125 +++---- src/groups/posts.js | 32 ++ src/groups/search.js | 2 +- src/groups/user.js | 50 +++ src/privileges/categories.js | 2 +- 7 files changed, 499 insertions(+), 471 deletions(-) create mode 100644 src/groups/data.js create mode 100644 src/groups/posts.js create mode 100644 src/groups/user.js diff --git a/src/groups.js b/src/groups.js index 863a61016c..7e2e216caa 100644 --- a/src/groups.js +++ b/src/groups.js @@ -6,215 +6,197 @@ var validator = require('validator'); var user = require('./user'); var db = require('./database'); var plugins = require('./plugins'); -var posts = require('./posts'); -var privileges = require('./privileges'); var utils = require('../public/src/utils'); -(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); - require('./groups/cover')(Groups); - - var ephemeralGroups = ['guests']; +var Groups = module.exports; + +require('./groups/data')(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); +require('./groups/cover')(Groups); +require('./groups/posts')(Groups); +require('./groups/user')(Groups); + + +Groups.ephemeralGroups = ['guests']; + +Groups.getEphemeralGroup = function (groupName) { + return { + name: groupName, + slug: utils.slugify(groupName), + description: '', + deleted: '0', + hidden: '0', + system: '1', + }; +}; - var internals = { - getEphemeralGroup: function (groupName) { - return { - name: groupName, - slug: utils.slugify(groupName), - description: '', - deleted: '0', - hidden: '0', - system: '1', - }; - }, - removeEphemeralGroups: function (groups) { - for (var x = groups.length; x >= 0; x -= 1) { - if (ephemeralGroups.indexOf(groups[x]) !== -1) { - groups.splice(x, 1); - } +Groups.removeEphemeralGroups = function (groups) { + for (var x = groups.length; x >= 0; x -= 1) { + if (Groups.ephemeralGroups.indexOf(groups[x]) !== -1) { + groups.splice(x, 1); + } + } + + return groups; +}; + +var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/; +Groups.isPrivilegeGroup = function (groupName) { + return isPrivilegeGroupRegex.test(groupName); +}; + +Groups.getGroupsFromSet = function (set, uid, start, stop, callback) { + async.waterfall([ + function (next) { + if (set === 'groups:visible:name') { + db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1, next); + } else { + db.getSortedSetRevRange(set, start, stop, next); } - - return groups; }, - }; - - Groups.internals = internals; - - var isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w:]+$/; - Groups.isPrivilegeGroup = function (groupName) { - return isPrivilegeGroupRegex.test(groupName); - }; - - Groups.getEphemeralGroups = function () { - return ephemeralGroups; - }; - - Groups.getGroupsFromSet = function (set, uid, start, stop, callback) { - async.waterfall([ - function (next) { - if (set === 'groups:visible:name') { - db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1, next); - } else { - db.getSortedSetRevRange(set, start, stop, next); - } - }, - function (groupNames, next) { - if (set === 'groups:visible:name') { - groupNames = groupNames.map(function (name) { - return name.split(':')[1]; - }); - } - - Groups.getGroupsAndMembers(groupNames, next); - }, - ], callback); - }; - - Groups.getGroups = function (set, start, stop, callback) { - db.getSortedSetRevRange(set, start, stop, callback); - }; - - Groups.getGroupsAndMembers = function (groupNames, callback) { - async.parallel({ - groups: function (next) { - Groups.getGroupsData(groupNames, next); - }, - members: function (next) { - Groups.getMemberUsers(groupNames, 0, 3, next); - }, - }, function (err, data) { - if (err) { - return callback(err); + function (groupNames, next) { + if (set === 'groups:visible:name') { + groupNames = groupNames.map(function (name) { + return name.split(':')[1]; + }); } + Groups.getGroupsAndMembers(groupNames, next); + }, + ], callback); +}; + +Groups.getGroups = function (set, start, stop, callback) { + db.getSortedSetRevRange(set, start, stop, callback); +}; + +Groups.getGroupsAndMembers = function (groupNames, callback) { + async.waterfall([ + function (next) { + async.parallel({ + groups: function (next) { + Groups.getGroupsData(groupNames, next); + }, + members: function (next) { + Groups.getMemberUsers(groupNames, 0, 3, next); + }, + }, next); + }, + function (data, next) { data.groups.forEach(function (group, index) { - if (!group) { - return; + if (group) { + group.members = data.members[index] || []; + group.truncated = group.memberCount > data.members.length; } - - group.members = data.members[index] || []; - group.truncated = group.memberCount > data.members.length; }); + next(null, data.groups); + }, + ], callback); +}; + +Groups.get = function (groupName, options, callback) { + if (!groupName) { + return callback(new Error('[[error:invalid-group]]')); + } + + var stop = -1; + + var results; + async.waterfall([ + function (next) { + async.parallel({ + base: function (next) { + db.getObject('group:' + groupName, next); + }, + members: function (next) { + if (options.truncateUserList) { + stop = (parseInt(options.userListCount, 10) || 4) - 1; + } - callback(null, data.groups); - }); - }; - - Groups.get = function (groupName, options, callback) { - if (!groupName) { - return callback(new Error('[[error:invalid-group]]')); - } - - var stop = -1; - - async.parallel({ - base: function (next) { - db.getObject('group:' + groupName, next); - }, - members: function (next) { - if (options.truncateUserList) { - stop = (parseInt(options.userListCount, 10) || 4) - 1; - } - - Groups.getOwnersAndMembers(groupName, options.uid, 0, stop, next); - }, - pending: function (next) { - async.waterfall([ - function (next) { - db.getSetMembers('group:' + groupName + ':pending', next); - }, - function (uids, next) { - user.getUsersData(uids, next); - }, - ], next); - }, - invited: function (next) { - async.waterfall([ - function (next) { - db.getSetMembers('group:' + groupName + ':invited', next); - }, - function (uids, next) { - user.getUsersData(uids, next); - }, - ], next); - }, - isMember: async.apply(Groups.isMember, options.uid, groupName), - isPending: async.apply(Groups.isPending, options.uid, groupName), - isInvited: async.apply(Groups.isInvited, options.uid, groupName), - isOwner: async.apply(Groups.ownership.isOwner, options.uid, groupName), - }, function (err, results) { - if (err) { - return callback(err); - } else if (!results.base) { + Groups.getOwnersAndMembers(groupName, options.uid, 0, stop, next); + }, + pending: function (next) { + Groups.getUsersFromSet('group:' + groupName + ':pending', next); + }, + invited: function (next) { + Groups.getUsersFromSet('group:' + groupName + ':invited', next); + }, + isMember: async.apply(Groups.isMember, options.uid, groupName), + isPending: async.apply(Groups.isPending, options.uid, groupName), + isInvited: async.apply(Groups.isInvited, options.uid, groupName), + isOwner: async.apply(Groups.ownership.isOwner, options.uid, groupName), + }, next); + }, + function (_results, next) { + results = _results; + if (!results.base) { return callback(new Error('[[error:no-group]]')); } - - results.base['cover:url'] = results.base['cover:url'] || require('./coverPhoto').getDefaultGroupCover(groupName); - results.base['cover:position'] = validator.escape(String(results.base['cover:position'] || '50% 50%')); - results.base.labelColor = validator.escape(String(results.base.labelColor || '#000000')); - results.base.icon = validator.escape(String(results.base.icon || '')); - - plugins.fireHook('filter:parse.raw', results.base.description, function (err, descriptionParsed) { - if (err) { - return callback(err); - } - - Groups.escapeGroupData(results.base); - - results.base.descriptionParsed = descriptionParsed; - results.base.userTitleEnabled = results.base.userTitleEnabled ? !!parseInt(results.base.userTitleEnabled, 10) : true; - results.base.createtimeISO = utils.toISOString(results.base.createtime); - results.base.members = results.members; - results.base.membersNextStart = stop + 1; - results.base.pending = results.pending.filter(Boolean); - results.base.invited = results.invited.filter(Boolean); - results.base.deleted = !!parseInt(results.base.deleted, 10); - results.base.hidden = !!parseInt(results.base.hidden, 10); - results.base.system = !!parseInt(results.base.system, 10); - results.base.memberCount = parseInt(results.base.memberCount, 10); - results.base.private = (results.base.private === null || results.base.private === undefined) ? true : !!parseInt(results.base.private, 10); - results.base.disableJoinRequests = parseInt(results.base.disableJoinRequests, 10) === 1; - results.base.isMember = results.isMember; - results.base.isPending = results.isPending; - results.base.isInvited = results.isInvited; - results.base.isOwner = results.isOwner; - - plugins.fireHook('filter:group.get', { group: results.base }, function (err, data) { - callback(err, data ? data.group : null); - }); - }); - }); - }; - - Groups.getOwners = function (groupName, callback) { - db.getSetMembers('group:' + groupName + ':owners', callback); - }; - - Groups.getOwnersAndMembers = function (groupName, uid, start, stop, callback) { - async.parallel({ - owners: function (next) { - async.waterfall([ - function (next) { - db.getSetMembers('group:' + groupName + ':owners', next); - }, - function (uids, next) { - user.getUsers(uids, uid, next); - }, - ], next); - }, - members: function (next) { - user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop, next); - }, - }, function (err, results) { - if (err) { - return callback(err); - } - + plugins.fireHook('filter:parse.raw', results.base.description, next); + }, + function (descriptionParsed, next) { + var groupData = results.base; + Groups.escapeGroupData(groupData); + + groupData.descriptionParsed = descriptionParsed; + groupData.userTitleEnabled = groupData.userTitleEnabled ? !!parseInt(groupData.userTitleEnabled, 10) : true; + groupData.createtimeISO = utils.toISOString(groupData.createtime); + groupData.members = results.members; + groupData.membersNextStart = stop + 1; + groupData.pending = results.pending.filter(Boolean); + groupData.invited = results.invited.filter(Boolean); + groupData.deleted = !!parseInt(groupData.deleted, 10); + groupData.hidden = !!parseInt(groupData.hidden, 10); + groupData.system = !!parseInt(groupData.system, 10); + groupData.memberCount = parseInt(groupData.memberCount, 10); + groupData.private = (groupData.private === null || groupData.private === undefined) ? true : !!parseInt(groupData.private, 10); + groupData.disableJoinRequests = parseInt(groupData.disableJoinRequests, 10) === 1; + groupData.isMember = results.isMember; + groupData.isPending = results.isPending; + groupData.isInvited = results.isInvited; + groupData.isOwner = results.isOwner; + groupData['cover:url'] = groupData['cover:url'] || require('./coverPhoto').getDefaultGroupCover(groupName); + groupData['cover:position'] = validator.escape(String(groupData['cover:position'] || '50% 50%')); + groupData.labelColor = validator.escape(String(groupData.labelColor || '#000000')); + groupData.icon = validator.escape(String(groupData.icon || '')); + + plugins.fireHook('filter:group.get', { group: groupData }, next); + }, + function (results, next) { + next(null, results.group); + }, + ], callback); +}; + +Groups.getOwners = function (groupName, callback) { + db.getSetMembers('group:' + groupName + ':owners', callback); +}; + +Groups.getOwnersAndMembers = function (groupName, uid, start, stop, callback) { + async.waterfall([ + function (next) { + async.parallel({ + owners: function (next) { + async.waterfall([ + function (next) { + db.getSetMembers('group:' + groupName + ':owners', next); + }, + function (uids, next) { + user.getUsers(uids, uid, next); + }, + ], next); + }, + members: function (next) { + user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop, next); + }, + }, next); + }, + function (results, next) { var ownerUids = []; results.owners.forEach(function (user) { if (user) { @@ -228,229 +210,97 @@ var utils = require('../public/src/utils'); }); results.members = results.owners.concat(results.members); - callback(null, results.members); - }); - }; - - Groups.escapeGroupData = function (group) { - if (group) { - group.nameEncoded = encodeURIComponent(group.name); - group.displayName = validator.escape(String(group.name)); - group.description = validator.escape(String(group.description || '')); - group.userTitle = validator.escape(String(group.userTitle || '')) || group.displayName; - } - }; - - Groups.getByGroupslug = function (slug, options, callback) { - db.getObjectField('groupslug:groupname', slug, function (err, groupName) { - if (err) { - return callback(err); - } else if (!groupName) { - return callback(new Error('[[error:no-group]]')); - } - - Groups.get(groupName, options, callback); - }); - }; - - Groups.getGroupNameByGroupSlug = function (slug, callback) { - db.getObjectField('groupslug:groupname', slug, callback); - }; - - Groups.getGroupFields = function (groupName, fields, callback) { - Groups.getMultipleGroupFields([groupName], fields, function (err, groups) { - callback(err, groups ? groups[0] : null); - }); - }; - - Groups.getMultipleGroupFields = function (groups, fields, callback) { - db.getObjectsFields(groups.map(function (group) { - return 'group:' + group; - }), fields, callback); - }; - - Groups.setGroupField = function (groupName, field, value, callback) { - db.setObjectField('group:' + groupName, field, value, function (err) { - if (err) { - return callback(err); + next(null, results.members); + }, + ], callback); +}; + +Groups.escapeGroupData = function (group) { + if (group) { + group.nameEncoded = encodeURIComponent(group.name); + group.displayName = validator.escape(String(group.name)); + group.description = validator.escape(String(group.description || '')); + group.userTitle = validator.escape(String(group.userTitle || '')) || group.displayName; + } +}; + +Groups.getByGroupslug = function (slug, options, callback) { + async.waterfall([ + function (next) { + db.getObjectField('groupslug:groupname', slug, next); + }, + function (groupName, next) { + if (!groupName) { + return next(new Error('[[error:no-group]]')); } - plugins.fireHook('action:group.set', { field: field, value: value, type: 'set' }); - callback(); - }); - }; + Groups.get(groupName, options, next); + }, + ], callback); +}; - Groups.isPrivate = function (groupName, callback) { - db.getObjectField('group:' + groupName, 'private', function (err, isPrivate) { - if (err) { - return callback(err); - } +Groups.getGroupNameByGroupSlug = function (slug, callback) { + db.getObjectField('groupslug:groupname', slug, callback); +}; - callback(null, parseInt(isPrivate, 10) !== 0); - }); - }; +Groups.isPrivate = function (groupName, callback) { + async.waterfall([ + function (next) { + db.getObjectField('group:' + groupName, 'private', next); + }, + function (isPrivate, next) { + next(null, parseInt(isPrivate, 10) !== 0); + }, + ], callback); +}; - Groups.isHidden = function (groupName, callback) { - db.getObjectField('group:' + groupName, 'hidden', function (err, isHidden) { - if (err) { - return callback(err); - } +Groups.isHidden = function (groupName, callback) { + async.waterfall([ + function (next) { + db.getObjectField('group:' + groupName, 'hidden', next); + }, + function (isHidden, next) { + next(null, parseInt(isHidden, 10) === 1); + }, + ], callback); +}; - callback(null, parseInt(isHidden, 10) === 1); +Groups.exists = function (name, callback) { + if (Array.isArray(name)) { + var slugs = name.map(function (groupName) { + return utils.slugify(groupName); }); - }; - - Groups.exists = function (name, callback) { - if (Array.isArray(name)) { - var slugs = name.map(function (groupName) { - return utils.slugify(groupName); - }); - async.parallel([ - function (next) { - next(null, slugs.map(function (slug) { - return ephemeralGroups.indexOf(slug) !== -1; - })); - }, - async.apply(db.isSortedSetMembers, 'groups:createtime', name), - ], function (err, results) { - if (err) { - return callback(err); - } - callback(null, name.map(function (n, index) { - return results[0][index] || results[1][index]; - })); - }); - } else { - var slug = utils.slugify(name); - async.parallel([ - function (next) { - next(null, ephemeralGroups.indexOf(slug) !== -1); - }, - async.apply(db.isSortedSetMember, 'groups:createtime', name), - ], function (err, results) { - callback(err, !err ? (results[0] || results[1]) : null); - }); - } - }; - - Groups.existsBySlug = function (slug, callback) { - if (Array.isArray(slug)) { - db.isObjectFields('groupslug:groupname', slug, callback); - } else { - db.isObjectField('groupslug:groupname', slug, callback); - } - }; - - Groups.getLatestMemberPosts = function (groupName, max, uid, callback) { - async.waterfall([ + async.parallel([ function (next) { - Groups.getMembers(groupName, 0, -1, next); - }, - function (uids, next) { - if (!Array.isArray(uids) || !uids.length) { - return callback(null, []); - } - var keys = uids.map(function (uid) { - return 'uid:' + uid + ':posts'; - }); - db.getSortedSetRevRange(keys, 0, max - 1, next); - }, - function (pids, next) { - privileges.posts.filter('read', pids, uid, next); - }, - function (pids, next) { - posts.getPostSummaryByPids(pids, uid, { stripTags: false }, next); + next(null, slugs.map(function (slug) { + return Groups.ephemeralGroups.indexOf(slug) !== -1; + })); }, - ], callback); - }; - - Groups.getGroupData = function (groupName, callback) { - Groups.getGroupsData([groupName], function (err, groupsData) { - callback(err, Array.isArray(groupsData) && groupsData[0] ? groupsData[0] : null); - }); - }; - - Groups.getGroupsData = function (groupNames, callback) { - if (!Array.isArray(groupNames) || !groupNames.length) { - return callback(null, []); - } - - var keys = groupNames.map(function (groupName) { - return 'group:' + groupName; - }); - - var ephemeralIdx = groupNames.reduce(function (memo, cur, idx) { - if (ephemeralGroups.indexOf(cur) !== -1) { - memo.push(idx); - } - return memo; - }, []); - - db.getObjects(keys, function (err, groupData) { + async.apply(db.isSortedSetMembers, 'groups:createtime', name), + ], function (err, results) { if (err) { return callback(err); } - - if (ephemeralIdx.length) { - ephemeralIdx.forEach(function (idx) { - groupData[idx] = internals.getEphemeralGroup(groupNames[idx]); - }); - } - - groupData.forEach(function (group) { - if (group) { - Groups.escapeGroupData(group); - group.userTitleEnabled = group.userTitleEnabled ? parseInt(group.userTitleEnabled, 10) === 1 : true; - group.labelColor = validator.escape(String(group.labelColor || '#000000')); - group.icon = validator.escape(String(group.icon || '')); - group.createtimeISO = utils.toISOString(group.createtime); - group.hidden = parseInt(group.hidden, 10) === 1; - group.system = parseInt(group.system, 10) === 1; - group.private = (group.private === null || group.private === undefined) ? true : !!parseInt(group.private, 10); - group.disableJoinRequests = parseInt(group.disableJoinRequests, 10) === 1; - - group['cover:url'] = group['cover:url'] || require('./coverPhoto').getDefaultGroupCover(group.name); - group['cover:thumb:url'] = group['cover:thumb:url'] || group['cover:url']; - group['cover:position'] = validator.escape(String(group['cover:position'] || '50% 50%')); - } - }); - - plugins.fireHook('filter:groups.get', { groups: groupData }, function (err, data) { - callback(err, data ? data.groups : null); - }); + callback(null, name.map(function (n, index) { + return results[0][index] || results[1][index]; + })); }); - }; - - Groups.getUserGroups = function (uids, callback) { - Groups.getUserGroupsFromSet('groups:visible:createtime', uids, callback); - }; - - Groups.getUserGroupsFromSet = function (set, uids, callback) { - async.waterfall([ + } else { + var slug = utils.slugify(name); + async.parallel([ function (next) { - db.getSortedSetRevRange(set, 0, -1, next); - }, - function (groupNames, next) { - var groupSets = groupNames.map(function (name) { - return 'group:' + name + ':members'; - }); - - async.map(uids, function (uid, next) { - db.isMemberOfSortedSets(groupSets, uid, function (err, isMembers) { - if (err) { - return next(err); - } - - var memberOf = []; - isMembers.forEach(function (isMember, index) { - if (isMember) { - memberOf.push(groupNames[index]); - } - }); - - Groups.getGroupsData(memberOf, next); - }); - }, next); + next(null, Groups.ephemeralGroups.indexOf(slug) !== -1); }, - ], callback); - }; -}(module.exports)); + async.apply(db.isSortedSetMember, 'groups:createtime', name), + ], function (err, results) { + callback(err, !err ? (results[0] || results[1]) : null); + }); + } +}; + +Groups.existsBySlug = function (slug, callback) { + if (Array.isArray(slug)) { + db.isObjectFields('groupslug:groupname', slug, callback); + } else { + db.isObjectField('groupslug:groupname', slug, callback); + } +}; diff --git a/src/groups/data.js b/src/groups/data.js new file mode 100644 index 0000000000..24e7f99a02 --- /dev/null +++ b/src/groups/data.js @@ -0,0 +1,93 @@ +'use strict'; + +var async = require('async'); +var validator = require('validator'); + +var db = require('../database'); +var plugins = require('../plugins'); +var utils = require('../../public/src/utils'); + +module.exports = function (Groups) { + Groups.getGroupsData = function (groupNames, callback) { + if (!Array.isArray(groupNames) || !groupNames.length) { + return callback(null, []); + } + + var keys = groupNames.map(function (groupName) { + return 'group:' + groupName; + }); + + var ephemeralIdx = groupNames.reduce(function (memo, cur, idx) { + if (Groups.ephemeralGroups.indexOf(cur) !== -1) { + memo.push(idx); + } + return memo; + }, []); + + async.waterfall([ + function (next) { + db.getObjects(keys, next); + }, + function (groupData, next) { + if (ephemeralIdx.length) { + ephemeralIdx.forEach(function (idx) { + groupData[idx] = Groups.getEphemeralGroup(groupNames[idx]); + }); + } + + groupData.forEach(function (group) { + if (group) { + Groups.escapeGroupData(group); + group.userTitleEnabled = group.userTitleEnabled ? parseInt(group.userTitleEnabled, 10) === 1 : true; + group.labelColor = validator.escape(String(group.labelColor || '#000000')); + group.icon = validator.escape(String(group.icon || '')); + group.createtimeISO = utils.toISOString(group.createtime); + group.hidden = parseInt(group.hidden, 10) === 1; + group.system = parseInt(group.system, 10) === 1; + group.private = (group.private === null || group.private === undefined) ? true : !!parseInt(group.private, 10); + group.disableJoinRequests = parseInt(group.disableJoinRequests, 10) === 1; + + group['cover:url'] = group['cover:url'] || require('../coverPhoto').getDefaultGroupCover(group.name); + group['cover:thumb:url'] = group['cover:thumb:url'] || group['cover:url']; + group['cover:position'] = validator.escape(String(group['cover:position'] || '50% 50%')); + } + }); + + plugins.fireHook('filter:groups.get', { groups: groupData }, next); + }, + function (results, next) { + next(null, results.groups); + }, + ], callback); + }; + + Groups.getGroupData = function (groupName, callback) { + Groups.getGroupsData([groupName], function (err, groupsData) { + callback(err, Array.isArray(groupsData) && groupsData[0] ? groupsData[0] : null); + }); + }; + + Groups.getGroupFields = function (groupName, fields, callback) { + Groups.getMultipleGroupFields([groupName], fields, function (err, groups) { + callback(err, groups ? groups[0] : null); + }); + }; + + Groups.getMultipleGroupFields = function (groups, fields, callback) { + db.getObjectsFields(groups.map(function (group) { + return 'group:' + group; + }), fields, callback); + }; + + Groups.setGroupField = function (groupName, field, value, callback) { + async.waterfall([ + function (next) { + db.setObjectField('group:' + groupName, field, value, next); + }, + function (next) { + plugins.fireHook('action:group.set', { field: field, value: value, type: 'set' }); + next(); + }, + ], callback); + }; +}; diff --git a/src/groups/membership.js b/src/groups/membership.js index 31a32da17c..6bcafd2343 100644 --- a/src/groups/membership.js +++ b/src/groups/membership.js @@ -413,32 +413,33 @@ module.exports = function (Groups) { }; Groups.getMemberCount = function (groupName, callback) { - db.getObjectField('group:' + groupName, 'memberCount', function (err, count) { - if (err) { - return callback(err); - } - callback(null, parseInt(count, 10)); - }); + async.waterfall([ + function (next) { + db.getObjectField('group:' + groupName, 'memberCount', next); + }, + function (count, next) { + next(null, parseInt(count, 10)); + }, + ], callback); }; 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); + async.waterfall([ + function (next) { + db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, next); + }, + function (groupNames, next) { + groupNames = Groups.removeEphemeralGroups(groupNames); + if (groupNames.length === 0) { + return callback(null, false); } - callback(null, isMembers.indexOf(true) !== -1); - }); - }); + Groups.isMemberOfGroups(uid, groupNames, next); + }, + function (isMembers, next) { + next(null, isMembers.indexOf(true) !== -1); + }, + ], callback); }; Groups.isMemberOfGroupsList = function (uid, groupListKeys, callback) { @@ -446,19 +447,20 @@ module.exports = function (Groups) { 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 uniqueGroups; + var members; + async.waterfall([ + function (next) { + db.getSortedSetsMembers(sets, next); + }, + function (_members, next) { + members = _members; + uniqueGroups = _.unique(_.flatten(members)); + uniqueGroups = Groups.removeEphemeralGroups(uniqueGroups); + Groups.isMemberOfGroups(uid, uniqueGroups, next); + }, + function (isMembers, next) { var map = {}; uniqueGroups.forEach(function (groupName, index) { @@ -474,62 +476,63 @@ module.exports = function (Groups) { return false; }); - callback(null, result); - }); - }); + next(null, result); + }, + ], callback); }; Groups.isMembersOfGroupList = function (uids, groupListKey, callback) { - db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, function (err, groupNames) { - if (err) { - return callback(err); - } + var groupNames; + var results = []; + uids.forEach(function () { + results.push(false); + }); - var results = []; - uids.forEach(function () { - results.push(false); - }); + async.waterfall([ + function (next) { + db.getSortedSetRange('group:' + groupListKey + ':members', 0, -1, next); + }, + function (_groupNames, next) { + groupNames = Groups.removeEphemeralGroups(_groupNames); - groupNames = Groups.internals.removeEphemeralGroups(groupNames); - if (groupNames.length === 0) { - return callback(null, results); - } + 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); - } + async.map(groupNames, function (groupName, next) { + Groups.isMembers(uids, groupName, next); + }, next); + }, + function (isGroupMembers, next) { + isGroupMembers.forEach(function (isMembers) { results.forEach(function (isMember, index) { if (!isMember && isMembers[index]) { results[index] = true; } }); - next(); }); - }, function (err) { - callback(err, results); - }); - }); + next(null, results); + }, + ], callback); }; Groups.isInvited = function (uid, groupName, callback) { if (!uid) { - return callback(null, false); + return setImmediate(callback, null, false); } db.isSetMember('group:' + groupName + ':invited', uid, callback); }; Groups.isPending = function (uid, groupName, callback) { if (!uid) { - return callback(null, false); + return setImmediate(callback, null, false); } db.isSetMember('group:' + groupName + ':pending', uid, callback); }; Groups.getPending = function (groupName, callback) { if (!groupName) { - return callback(null, []); + return setImmediate(callback, null, []); } db.getSetMembers('group:' + groupName + ':pending', callback); }; diff --git a/src/groups/posts.js b/src/groups/posts.js new file mode 100644 index 0000000000..6f4d65520d --- /dev/null +++ b/src/groups/posts.js @@ -0,0 +1,32 @@ +'use strict'; + +var async = require('async'); + +var db = require('../database'); +var privileges = require('../privileges'); +var posts = require('../posts'); + +module.exports = function (Groups) { + Groups.getLatestMemberPosts = function (groupName, max, uid, callback) { + async.waterfall([ + function (next) { + Groups.getMembers(groupName, 0, -1, next); + }, + function (uids, next) { + if (!Array.isArray(uids) || !uids.length) { + return callback(null, []); + } + var keys = uids.map(function (uid) { + return 'uid:' + uid + ':posts'; + }); + db.getSortedSetRevRange(keys, 0, max - 1, next); + }, + function (pids, next) { + privileges.posts.filter('read', pids, uid, next); + }, + function (pids, next) { + posts.getPostSummaryByPids(pids, uid, { stripTags: false }, next); + }, + ], callback); + }; +}; diff --git a/src/groups/search.js b/src/groups/search.js index 3c580b5e8d..62df79696c 100644 --- a/src/groups/search.js +++ b/src/groups/search.js @@ -16,7 +16,7 @@ module.exports = function (Groups) { async.apply(db.getObjectValues, 'groupslug:groupname'), function (groupNames, next) { // Ephemeral groups and the registered-users groups are searchable - groupNames = Groups.getEphemeralGroups().concat(groupNames).concat('registered-users'); + groupNames = Groups.ephemeralGroups.concat(groupNames).concat('registered-users'); groupNames = groupNames.filter(function (name) { return name.toLowerCase().indexOf(query) !== -1 && name !== 'administrators' && !Groups.isPrivilegeGroup(name); }); diff --git a/src/groups/user.js b/src/groups/user.js new file mode 100644 index 0000000000..9a68478ade --- /dev/null +++ b/src/groups/user.js @@ -0,0 +1,50 @@ +'use strict'; + +var async = require('async'); + +var db = require('../database'); +var user = require('../user'); + +module.exports = function (Groups) { + Groups.getUsersFromSet = function (set, callback) { + async.waterfall([ + function (next) { + db.getSetMembers(set, next); + }, + function (uids, next) { + user.getUsersData(uids, next); + }, + ], callback); + }; + + Groups.getUserGroups = function (uids, callback) { + Groups.getUserGroupsFromSet('groups:visible:createtime', uids, callback); + }; + + Groups.getUserGroupsFromSet = function (set, uids, callback) { + async.waterfall([ + function (next) { + db.getSortedSetRevRange(set, 0, -1, next); + }, + function (groupNames, next) { + async.map(uids, function (uid, next) { + async.waterfall([ + function (next) { + Groups.isMemberOfGroups(uid, groupNames, next); + }, + function (isMembers, next) { + var memberOf = []; + isMembers.forEach(function (isMember, index) { + if (isMember) { + memberOf.push(groupNames[index]); + } + }); + + Groups.getGroupsData(memberOf, next); + }, + ], next); + }, next); + }, + ], callback); + }; +}; diff --git a/src/privileges/categories.js b/src/privileges/categories.js index ff3d16148f..d0bde8ced3 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -96,7 +96,7 @@ module.exports = function (privileges) { return groupName.indexOf(':privileges:') === -1 && uniqueGroups.indexOf(groupName) !== -1; }); - groupNames = groups.getEphemeralGroups().concat(groupNames); + groupNames = groups.ephemeralGroups.concat(groupNames); var registeredUsersIndex = groupNames.indexOf('registered-users'); if (registeredUsersIndex !== -1) { groupNames.splice(0, 0, groupNames.splice(registeredUsersIndex, 1)[0]);