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

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

Loading…
Cancel
Save