feat: #7743,groups/index,join

v1.18.x
Barış Soner Uşaklı 6 years ago
parent 87b1148fa8
commit d5342a40ba

@ -1,13 +1,11 @@
'use strict'; 'use strict';
var async = require('async'); const user = require('../user');
const db = require('../database');
const plugins = require('../plugins');
const utils = require('../utils');
var user = require('../user'); const Groups = module.exports;
var db = require('../database');
var plugins = require('../plugins');
var utils = require('../utils');
var Groups = module.exports;
require('./data')(Groups); require('./data')(Groups);
require('./create')(Groups); require('./create')(Groups);
@ -52,237 +50,146 @@ Groups.isPrivilegeGroup = function (groupName) {
return isPrivilegeGroupRegex.test(groupName); return isPrivilegeGroupRegex.test(groupName);
}; };
Groups.getGroupsFromSet = function (set, uid, start, stop, callback) { Groups.getGroupsFromSet = async function (set, uid, start, stop) {
async.waterfall([ let groupNames;
function (next) { if (set === 'groups:visible:name') {
if (set === 'groups:visible:name') { groupNames = await db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1);
db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1, next); } else {
} else { groupNames = await db.getSortedSetRevRange(set, start, stop);
db.getSortedSetRevRange(set, start, stop, next); }
} if (set === 'groups:visible:name') {
}, groupNames = groupNames.map(name => name.split(':')[1]);
function (groupNames, next) { }
if (set === 'groups:visible:name') {
groupNames = groupNames.map(function (name) {
return name.split(':')[1];
});
}
Groups.getGroupsAndMembers(groupNames, next); return await Groups.getGroupsAndMembers(groupNames);
},
], callback);
}; };
Groups.getNonPrivilegeGroups = function (set, start, stop, callback) { Groups.getNonPrivilegeGroups = async function (set, start, stop) {
async.waterfall([ let groupNames = await db.getSortedSetRevRange(set, start, stop);
function (next) { groupNames = groupNames.concat(Groups.ephemeralGroups).filter(groupName => !Groups.isPrivilegeGroup(groupName));
db.getSortedSetRevRange(set, start, stop, next); return await Groups.getGroupsData(groupNames);
},
function (groupNames, next) {
groupNames = groupNames.concat(Groups.ephemeralGroups).filter(groupName => !Groups.isPrivilegeGroup(groupName));
Groups.getGroupsData(groupNames, next);
},
], callback);
}; };
Groups.getGroups = function (set, start, stop, callback) { Groups.getGroups = async function (set, start, stop) {
db.getSortedSetRevRange(set, start, stop, callback); return await db.getSortedSetRevRange(set, start, stop);
}; };
Groups.getGroupsAndMembers = function (groupNames, callback) { Groups.getGroupsAndMembers = async function (groupNames) {
async.waterfall([ const [groups, members] = await Promise.all([
function (next) { Groups.getGroupsData(groupNames),
async.parallel({ Groups.getMemberUsers(groupNames, 0, 3),
groups: function (next) { ]);
Groups.getGroupsData(groupNames, next); groups.forEach(function (group, index) {
}, if (group) {
members: function (next) { group.members = members[index] || [];
Groups.getMemberUsers(groupNames, 0, 3, next); group.truncated = group.memberCount > group.members.length;
}, }
}, next); });
}, return groups;
function (data, next) {
data.groups.forEach(function (group, index) {
if (group) {
group.members = data.members[index] || [];
group.truncated = group.memberCount > group.members.length;
}
});
next(null, data.groups);
},
], callback);
}; };
Groups.get = function (groupName, options, callback) { Groups.get = async function (groupName, options) {
if (!groupName) { if (!groupName) {
return callback(new Error('[[error:invalid-group]]')); throw new Error('[[error:invalid-group]]');
} }
var stop = -1; let stop = -1;
var results; if (options.truncateUserList) {
async.waterfall([ stop = (parseInt(options.userListCount, 10) || 4) - 1;
function (next) { }
async.parallel({
base: function (next) {
Groups.getGroupData(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) {
Groups.getUsersFromSet('group:' + groupName + ':pending', ['username', 'userslug', 'picture'], next);
},
invited: function (next) {
Groups.getUsersFromSet('group:' + groupName + ':invited', ['username', 'userslug', 'picture'], 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(null, null);
}
plugins.fireHook('filter:parse.raw', results.base.description, next);
},
function (descriptionParsed, next) {
var groupData = results.base;
groupData.descriptionParsed = descriptionParsed;
groupData.members = results.members;
groupData.membersNextStart = stop + 1;
groupData.pending = results.pending.filter(Boolean);
groupData.invited = results.invited.filter(Boolean);
groupData.isMember = results.isMember;
groupData.isPending = results.isPending;
groupData.isInvited = results.isInvited;
groupData.isOwner = results.isOwner;
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) {
user.isOwner = true;
ownerUids.push(user.uid.toString());
}
});
results.members = results.members.filter(function (user) { const [groupData, members, pending, invited, isMember, isPending, isInvited, isOwner] = await Promise.all([
return user && user.uid && !ownerUids.includes(user.uid.toString()); Groups.getGroupData(groupName),
}); Groups.getOwnersAndMembers(groupName, options.uid, 0, stop),
results.members = results.owners.concat(results.members); Groups.getUsersFromSet('group:' + groupName + ':pending', ['username', 'userslug', 'picture']),
Groups.getUsersFromSet('group:' + groupName + ':invited', ['username', 'userslug', 'picture']),
Groups.isMember(options.uid, groupName),
Groups.isPending(options.uid, groupName),
Groups.isInvited(options.uid, groupName),
Groups.ownership.isOwner(options.uid, groupName),
]);
if (!groupData) {
return null;
}
const descriptionParsed = await plugins.fireHook('filter:parse.raw', groupData.description);
groupData.descriptionParsed = descriptionParsed;
groupData.members = members;
groupData.membersNextStart = stop + 1;
groupData.pending = pending.filter(Boolean);
groupData.invited = invited.filter(Boolean);
groupData.isMember = isMember;
groupData.isPending = isPending;
groupData.isInvited = isInvited;
groupData.isOwner = isOwner;
const results = await plugins.fireHook('filter:group.get', { group: groupData });
return results.group;
};
Groups.getOwners = async function (groupName) {
return await db.getSetMembers('group:' + groupName + ':owners');
};
Groups.getOwnersAndMembers = async function (groupName, uid, start, stop) {
const ownerUids = await db.getSetMembers('group:' + groupName + ':owners');
const [owners, members] = await Promise.all([
user.getUsers(ownerUids, uid),
user.getUsersFromSet('group:' + groupName + ':members', uid, start, stop),
]);
owners.forEach(function (user) {
if (user) {
user.isOwner = true;
}
});
next(null, results.members); const nonOwners = members.filter(user => user && user.uid && !ownerUids.includes(user.uid.toString()));
}, return owners.concat(nonOwners);
], callback);
}; };
Groups.getByGroupslug = function (slug, options, callback) { Groups.getByGroupslug = async function (slug, options) {
async.waterfall([ const groupName = await db.getObjectField('groupslug:groupname', slug);
function (next) { if (!groupName) {
db.getObjectField('groupslug:groupname', slug, next); throw new Error('[[error:no-group]]');
}, }
function (groupName, next) { return await Groups.get(groupName, options);
if (!groupName) {
return next(new Error('[[error:no-group]]'));
}
Groups.get(groupName, options, next);
},
], callback);
}; };
Groups.getGroupNameByGroupSlug = function (slug, callback) { Groups.getGroupNameByGroupSlug = async function (slug) {
db.getObjectField('groupslug:groupname', slug, callback); return await db.getObjectField('groupslug:groupname', slug);
}; };
Groups.isPrivate = function (groupName, callback) { Groups.isPrivate = async function (groupName) {
isFieldOn(groupName, 'private', callback); return await isFieldOn(groupName, 'private');
}; };
Groups.isHidden = function (groupName, callback) { Groups.isHidden = async function (groupName) {
isFieldOn(groupName, 'hidden', callback); return await isFieldOn(groupName, 'hidden');
}; };
function isFieldOn(groupName, field, callback) { async function isFieldOn(groupName, field) {
async.waterfall([ const value = await db.getObjectField('group:' + groupName, field);
function (next) { return parseInt(value, 10) === 1;
db.getObjectField('group:' + groupName, field, next);
},
function (value, next) {
next(null, parseInt(value, 10) === 1);
},
], callback);
} }
Groups.exists = function (name, callback) { Groups.exists = async function (name) {
if (Array.isArray(name)) { if (Array.isArray(name)) {
var slugs = name.map(groupName => utils.slugify(groupName)); const slugs = name.map(groupName => utils.slugify(groupName));
async.waterfall([ const isMembersOfRealGroups = await db.isSortedSetMembers('groups:createtime', name);
async.apply(db.isSortedSetMembers, 'groups:createtime', name), const isMembersOfEphemeralGroups = slugs.map(slug => Groups.ephemeralGroups.includes(slug));
function (isMembersOfRealGroups, next) { return name.map((n, index) => isMembersOfRealGroups[index] || isMembersOfEphemeralGroups[index]);
const isMembersOfEphemeralGroups = slugs.map(slug => Groups.ephemeralGroups.includes(slug));
const exists = name.map((n, index) => isMembersOfRealGroups[index] || isMembersOfEphemeralGroups[index]);
next(null, exists);
},
], callback);
} else {
var slug = utils.slugify(name);
async.waterfall([
async.apply(db.isSortedSetMember, 'groups:createtime', name),
function (isMemberOfRealGroups, next) {
const isMemberOfEphemeralGroups = Groups.ephemeralGroups.includes(slug);
next(null, isMemberOfRealGroups || isMemberOfEphemeralGroups);
},
], callback);
} }
const slug = utils.slugify(name);
const isMemberOfRealGroups = await db.isSortedSetMember('groups:createtime', name);
const isMemberOfEphemeralGroups = Groups.ephemeralGroups.includes(slug);
return isMemberOfRealGroups || isMemberOfEphemeralGroups;
}; };
Groups.existsBySlug = function (slug, callback) { Groups.existsBySlug = async function (slug) {
if (Array.isArray(slug)) { if (Array.isArray(slug)) {
db.isObjectFields('groupslug:groupname', slug, callback); return await db.isObjectFields('groupslug:groupname', slug);
} else {
db.isObjectField('groupslug:groupname', slug, callback);
} }
return await db.isObjectField('groupslug:groupname', slug);
}; };
Groups.async = require('../promisify')(Groups); Groups.async = require('../promisify')(Groups);

@ -8,117 +8,96 @@ const user = require('../user');
const plugins = require('../plugins'); const plugins = require('../plugins');
module.exports = function (Groups) { module.exports = function (Groups) {
Groups.join = function (groupNames, uid, callback) { Groups.join = async function (groupNames, uid) {
callback = callback || function () {};
if (!groupNames) { if (!groupNames) {
return callback(new Error('[[error:invalid-data]]')); throw new Error('[[error:invalid-data]]');
} }
if (Array.isArray(groupNames) && !groupNames.length) { if (Array.isArray(groupNames) && !groupNames.length) {
return setImmediate(callback); return;
} }
if (!Array.isArray(groupNames)) { if (!Array.isArray(groupNames)) {
groupNames = [groupNames]; groupNames = [groupNames];
} }
if (!uid) { if (!uid) {
return callback(new Error('[[error:invalid-uid]]')); throw new Error('[[error:invalid-uid]]');
} }
var isAdmin;
async.waterfall([
function (next) {
async.parallel({
isMembers: async.apply(Groups.isMemberOfGroups, uid, groupNames),
exists: async.apply(Groups.exists, groupNames),
isAdmin: async.apply(user.isAdministrator, uid),
}, next);
},
function (results, next) {
isAdmin = results.isAdmin;
var groupsToCreate = groupNames.filter(function (groupName, index) {
return groupName && !results.exists[index];
});
groupNames = groupNames.filter(function (groupName, index) { const [isMembers, exists, isAdmin] = await Promise.all([
return !results.isMembers[index]; Groups.isMemberOfGroups(uid, groupNames),
}); Groups.exists(groupNames),
user.isAdministrator(uid),
]);
if (!groupNames.length) { const groupsToCreate = groupNames.filter((groupName, index) => groupName && !exists[index]);
return callback(); const groupsToJoin = groupNames.filter((groupName, index) => !isMembers[index]);
}
createNonExistingGroups(groupsToCreate, next); if (!groupsToJoin.length) {
}, return;
function (next) { }
var tasks = [ await createNonExistingGroups(groupsToCreate);
async.apply(db.sortedSetsAdd, groupNames.map(groupName => 'group:' + groupName + ':members'), Date.now(), uid),
async.apply(db.incrObjectField, groupNames.map(groupName => 'group:' + groupName), 'memberCount'),
];
if (isAdmin) {
tasks.push(async.apply(db.setsAdd, groupNames.map(groupName => 'group:' + groupName + ':owners'), uid));
}
async.parallel(tasks, next); const promises = [
}, db.sortedSetsAdd(groupsToJoin.map(groupName => 'group:' + groupName + ':members'), Date.now(), uid),
function (results, next) { db.incrObjectField(groupsToJoin.map(groupName => 'group:' + groupName), 'memberCount'),
Groups.clearCache(uid, groupNames); ];
Groups.getGroupsFields(groupNames, ['name', 'hidden', 'memberCount'], next); if (isAdmin) {
}, promises.push(db.setsAdd(groupsToJoin.map(groupName => 'group:' + groupName + ':owners'), uid));
function (groupData, next) { }
var visibleGroups = groupData.filter(groupData => groupData && !groupData.hidden);
await Promise.all(promises);
if (visibleGroups.length) {
db.sortedSetAdd('groups:visible:memberCount', visibleGroups.map(groupData => groupData.memberCount), visibleGroups.map(groupData => groupData.name), next); Groups.clearCache(uid, groupsToJoin);
} else {
next(); const groupData = await Groups.getGroupsFields(groupsToJoin, ['name', 'hidden', 'memberCount']);
} const visibleGroups = groupData.filter(groupData => groupData && !groupData.hidden);
},
function (next) { if (visibleGroups.length) {
setGroupTitleIfNotSet(groupNames, uid, next); await db.sortedSetAdd('groups:visible:memberCount',
}, visibleGroups.map(groupData => groupData.memberCount),
function (next) { visibleGroups.map(groupData => groupData.name)
plugins.fireHook('action:group.join', { );
groupNames: groupNames, }
uid: uid,
}); await setGroupTitleIfNotSet(groupsToJoin, uid);
next();
}, plugins.fireHook('action:group.join', {
], callback); groupNames: groupsToJoin,
uid: uid,
});
}; };
function createNonExistingGroups(groupsToCreate, callback) { async function createNonExistingGroups(groupsToCreate) {
if (!groupsToCreate.length) { if (!groupsToCreate.length) {
return setImmediate(callback); return;
} }
async.eachSeries(groupsToCreate, function (groupName, next) {
Groups.create({ await async.eachSeries(groupsToCreate, async function (groupName) {
name: groupName, try {
hidden: 1, await Groups.create({
}, function (err) { name: groupName,
hidden: 1,
});
} catch (err) {
if (err && err.message !== '[[error:group-already-exists]]') { if (err && err.message !== '[[error:group-already-exists]]') {
winston.error('[groups.join] Could not create new hidden group', err); winston.error('[groups.join] Could not create new hidden group', err);
return next(err); throw err;
} }
next(); }
}); });
}, callback);
} }
function setGroupTitleIfNotSet(groupNames, uid, callback) { async function setGroupTitleIfNotSet(groupNames, uid) {
groupNames = groupNames.filter(function (groupName) { groupNames = groupNames.filter(groupName => groupName !== 'registered-users' && !Groups.isPrivilegeGroup(groupName));
return groupName !== 'registered-users' && !Groups.isPrivilegeGroup(groupName);
});
if (!groupNames.length) { if (!groupNames.length) {
return callback(); return;
} }
db.getObjectField('user:' + uid, 'groupTitle', function (err, currentTitle) { const currentTitle = await db.getObjectField('user:' + uid, 'groupTitle');
if (err || currentTitle || currentTitle === '') { if (currentTitle || currentTitle === '') {
return callback(err); return;
} }
user.setUserField(uid, 'groupTitle', JSON.stringify(groupNames), callback); await user.setUserField(uid, 'groupTitle', JSON.stringify(groupNames));
});
} }
}; };

Loading…
Cancel
Save