'use strict'; var async = require('async'); var winston = require('winston'); var plugins = require('../plugins'); var utils = require('../utils'); var db = require('../database'); module.exports = function (Groups) { Groups.update = function (groupName, values, callback) { callback = callback || function () {}; async.waterfall([ function (next) { db.exists('group:' + groupName, next); }, function (exists, next) { if (!exists) { return next(new Error('[[error:no-group]]')); } plugins.fireHook('filter:group.update', { groupName: groupName, values: values, }, next); }, function (result, next) { values = result.values; var payload = { description: values.description || '', icon: values.icon || '', labelColor: values.labelColor || '#000000', }; if (values.hasOwnProperty('userTitle')) { payload.userTitle = values.userTitle || ''; } if (values.hasOwnProperty('userTitleEnabled')) { payload.userTitleEnabled = values.userTitleEnabled ? '1' : '0'; } if (values.hasOwnProperty('hidden')) { payload.hidden = values.hidden ? '1' : '0'; } if (values.hasOwnProperty('private')) { payload.private = values.private ? '1' : '0'; } if (values.hasOwnProperty('disableJoinRequests')) { payload.disableJoinRequests = values.disableJoinRequests ? '1' : '0'; } async.series([ async.apply(checkNameChange, groupName, values.name), function (next) { if (values.hasOwnProperty('private')) { updatePrivacy(groupName, values.private, next); } else { next(); } }, function (next) { if (values.hasOwnProperty('hidden')) { updateVisibility(groupName, values.hidden, next); } else { next(); } }, async.apply(db.setObject, 'group:' + groupName, payload), async.apply(Groups.renameGroup, groupName, values.name), ], next); }, function (result, next) { plugins.fireHook('action:group.update', { name: groupName, values: values, }); next(); }, ], callback); }; function updateVisibility(groupName, hidden, callback) { if (hidden) { async.parallel([ async.apply(db.sortedSetRemove, 'groups:visible:createtime', groupName), async.apply(db.sortedSetRemove, 'groups:visible:memberCount', groupName), async.apply(db.sortedSetRemove, 'groups:visible:name', groupName.toLowerCase() + ':' + groupName), ], callback); } else { async.waterfall([ function (next) { db.getObjectFields('group:' + groupName, ['createtime', 'memberCount'], next); }, function (groupData, next) { async.parallel([ async.apply(db.sortedSetAdd, 'groups:visible:createtime', groupData.createtime, groupName), async.apply(db.sortedSetAdd, 'groups:visible:memberCount', groupData.memberCount, groupName), async.apply(db.sortedSetAdd, 'groups:visible:name', 0, groupName.toLowerCase() + ':' + groupName), ], next); }, ], callback); } } Groups.hide = function (groupName, callback) { showHide(groupName, 'hidden', callback); }; Groups.show = function (groupName, callback) { showHide(groupName, 'show', callback); }; function showHide(groupName, hidden, callback) { hidden = hidden === 'hidden'; callback = callback || function () {}; async.parallel([ async.apply(db.setObjectField, 'group:' + groupName, 'hidden', hidden ? 1 : 0), async.apply(updateVisibility, groupName, hidden), ], function (err) { callback(err); }); } function updatePrivacy(groupName, isPrivate, callback) { async.waterfall([ function (next) { Groups.getGroupFields(groupName, ['private'], next); }, function (currentValue, next) { var currentlyPrivate = parseInt(currentValue.private, 10) === 1; if (!currentlyPrivate || currentlyPrivate === isPrivate) { return callback(); } db.getSetMembers('group:' + groupName + ':pending', next); }, function (uids, next) { if (!uids.length) { return callback(); } var now = Date.now(); var scores = uids.map(function () { return now; }); winston.verbose('[groups.update] Group is now public, automatically adding ' + uids.length + ' new members, who were pending prior.'); async.series([ async.apply(db.sortedSetAdd, 'group:' + groupName + ':members', scores, uids), async.apply(db.delete, 'group:' + groupName + ':pending'), ], next); }, ], function (err) { callback(err); }); } function checkNameChange(currentName, newName, callback) { if (currentName === newName) { return setImmediate(callback); } var currentSlug = utils.slugify(currentName); var newSlug = utils.slugify(newName); if (currentSlug === newSlug) { return setImmediate(callback); } async.waterfall([ function (next) { async.parallel({ group: function (next) { db.getObject('group:' + currentName, next); }, exists: function (next) { Groups.existsBySlug(newSlug, next); }, }, next); }, function (results, next) { if (results.exists) { return next(new Error('[[error:group-already-exists]]')); } if (parseInt(results.group.system, 10) === 1) { return next(new Error('[[error:not-allowed-to-rename-system-group]]')); } next(); }, ], callback); } Groups.renameGroup = function (oldName, newName, callback) { if (oldName === newName || !newName || newName.length === 0) { return setImmediate(callback); } var group; async.waterfall([ function (next) { db.getObject('group:' + oldName, next); }, function (_group, next) { group = _group; if (!group) { return callback(); } Groups.exists(newName, next); }, function (exists, next) { if (exists) { return callback(new Error('[[error:group-already-exists]]')); } async.series([ async.apply(db.setObjectField, 'group:' + oldName, 'name', newName), async.apply(db.setObjectField, 'group:' + oldName, 'slug', utils.slugify(newName)), async.apply(db.deleteObjectField, 'groupslug:groupname', group.slug), async.apply(db.setObjectField, 'groupslug:groupname', utils.slugify(newName), newName), function (next) { db.getSortedSetRange('groups:createtime', 0, -1, function (err, groups) { if (err) { return next(err); } async.each(groups, function (group, next) { renameGroupMember('group:' + group + ':members', oldName, newName, next); }, next); }); }, async.apply(db.rename, 'group:' + oldName, 'group:' + newName), async.apply(db.rename, 'group:' + oldName + ':members', 'group:' + newName + ':members'), async.apply(db.rename, 'group:' + oldName + ':owners', 'group:' + newName + ':owners'), async.apply(db.rename, 'group:' + oldName + ':pending', 'group:' + newName + ':pending'), async.apply(db.rename, 'group:' + oldName + ':invited', 'group:' + newName + ':invited'), async.apply(renameGroupMember, 'groups:createtime', oldName, newName), async.apply(renameGroupMember, 'groups:visible:createtime', oldName, newName), async.apply(renameGroupMember, 'groups:visible:memberCount', oldName, newName), async.apply(renameGroupMember, 'groups:visible:name', oldName.toLowerCase() + ':' + oldName, newName.toLowerCase() + ':' + newName), function (next) { plugins.fireHook('action:group.rename', { old: oldName, new: newName, }); Groups.resetCache(); next(); }, ], next); }, ], function (err) { callback(err); }); }; function renameGroupMember(group, oldName, newName, callback) { var score; async.waterfall([ function (next) { db.isSortedSetMember(group, oldName, next); }, function (isMember, next) { if (!isMember) { return callback(); } db.sortedSetScore(group, oldName, next); }, function (_score, next) { score = _score; db.sortedSetRemove(group, oldName, next); }, function (next) { db.sortedSetAdd(group, score, newName, next); }, ], callback); } };