wip on groups controller

This commit is contained in:
Matteo Pagliazzi
2015-12-27 17:24:55 +01:00
parent 99be74b6f7
commit 6ae2c5fa89
5 changed files with 212 additions and 3 deletions

View File

@@ -36,5 +36,7 @@
"onlyLeaderCanRemoveMember": "Only group leader can remove a member!",
"memberCannotRemoveYourself": "You cannot remove yourself!",
"groupMemberNotFound": "User not found among group's members",
"keepOrRemoveAll": "req.query.keep must be either \"keep-all\" or \"remove-all\""
"keepOrRemoveAll": "req.query.keep must be either \"keep-all\" or \"remove-all\"",
"canOnlyInviteEmailUuid": "Can only invite using uuids or emails but not both at the same time.",
"inviteMissingEmail": "Missing email address in invite."
}

View File

@@ -10,10 +10,13 @@ import {
NotAuthorized,
} from '../../libs/api-v3/errors';
import * as firebase from '../../libs/api-v3/firebase';
import txnEmail from '../../libs/api-v3/email';
import { txnEmail } from '../../libs/api-v3/email';
// import { encrypt } from '../../libs/api-v3/encryption';
let api = {};
// TODO shall we accept party as groupId in all routes?
/**
* @api {post} /groups Create group
* @apiVersion 3.0.0
@@ -250,6 +253,9 @@ api.leaveGroup = {
// When removing the user from challenges, should we keep the tasks?
req.checkQuery('keep', res.t('keepOrRemoveAll')).optional().isIn(['keep-all', 'remove-all']);
let validationErrors = req.validationErrors();
if (validationErrors) return next(validationErrors);
Group.getGroup(user, req.params.groupId, '-chat') // Do not fetch chat
.then(group => {
if (!group) throw new NotFound(res.t('groupNotFound'));
@@ -305,11 +311,16 @@ api.removeGroupMember = {
let group;
req.checkParams('groupId', res.t('groupIdRequired')).notEmpty();
req.checkParams('groupId', res.t('userIdRequired')).notEmpty().isUUID();
req.checkParams('memberId', res.t('userIdRequired')).notEmpty().isUUID();
let validationErrors = req.validationErrors();
if (validationErrors) return next(validationErrors);
Group.getGroup(user, req.params.groupId, '-chat') // Do not fetch chat
.then(foundGroup => {
group = foundGroup;
if (!group) throw new NotFound(res.t('groupNotFound'));
let uuid = req.query.memberId;
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyLeaderCanRemoveMember'));
@@ -363,4 +374,191 @@ api.removeGroupMember = {
},
};
/* function _inviteByUUIDs (uuids, group, inviter, req, res, next) {
async.each(uuids, function(uuid, cb){
User.findById(uuid, function(err,invite){
if (err) return cb(err);
if (!invite)
return cb({code:400,err:'User with id "' + uuid + '" not found'});
if (group.type == 'guild') {
if (_.contains(group.members,uuid))
return cb({code:400, err: "User already in that group"});
if (invite.invitations && invite.invitations.guilds && _.find(invite.invitations.guilds, {id:group._id}))
return cb({code:400, err:"User already invited to that group"});
sendInvite();
} else if (group.type == 'party') {
if (invite.invitations && !_.isEmpty(invite.invitations.party))
return cb({code: 400,err:"User already pending invitation."});
Group.find({type: 'party', members: {$in: [uuid]}}, function(err, groups){
if (err) return cb(err);
if (!_.isEmpty(groups) && groups[0].members.length > 1) {
return cb({code: 400, err: "User already in a party."})
}
sendInvite();
});
}
function sendInvite (){
if(group.type === 'guild'){
invite.invitations.guilds.push({id: group._id, name: group.name, inviter:res.locals.user._id});
pushNotify.sendNotify(invite, shared.i18n.t('invitedGuild'), group.name);
}else{
//req.body.type in 'guild', 'party'
invite.invitations.party = {id: group._id, name: group.name, inviter:res.locals.user._id};
pushNotify.sendNotify(invite, shared.i18n.t('invitedParty'), group.name);
}
group.invites.push(invite._id);
async.series([
function(cb){
invite.save(cb);
}
], function(err, results){
if (err) return cb(err);
if(invite.preferences.emailNotifications['invited' + (group.type == 'guild' ? 'Guild' : 'Party')] !== false){
var inviterVars = utils.getUserInfo(res.locals.user, ['name', 'email']);
var emailVars = [
{name: 'INVITER', content: inviterVars.name},
{name: 'REPLY_TO_ADDRESS', content: inviterVars.email}
];
if(group.type == 'guild'){
emailVars.push(
{name: 'GUILD_NAME', content: group.name},
{name: 'GUILD_URL', content: '/#/options/groups/guilds/public'}
);
}else{
emailVars.push(
{name: 'PARTY_NAME', content: group.name},
{name: 'PARTY_URL', content: '/#/options/groups/party'}
)
}
utils.txnEmail(invite, ('invited-' + (group.type == 'guild' ? 'guild' : 'party')), emailVars);
}
cb();
});
}
});
}, function(err){
if(err) return err.code ? res.json(err.code, {err: err.err}) : next(err);
async.series([
function(cb) {
group.save(cb);
},
function(cb) {
// TODO pass group from save above don't find it again, or you have to find it again in order to run populate?
populateQuery(group.type, Group.findById(group._id)).exec(function(err, populatedGroup){
if(err) return next(err);
res.json(populatedGroup);
});
}
]);
});
};
function _inviteByEmails (emails, group, inviter, req, res, next) {
let usersAlreadyRegistered = [];
let invitesToSend = [];
return Q.all(emails.forEach(invite => {
if (!invite.email) throw new BadRequest(res.t('inviteMissingEmail'));
return User.findOne({$or: [
{'auth.local.email': invite.email},
{'auth.facebook.emails.value': invite.email}
]})
.select({_id: true, 'preferences.emailNotifications': true})
.exec()
.then(userToContact => {
if(userToContact){
usersAlreadyRegistered.push(userToContact._id); // TODO does it work not returning
} else {
// yeah, it supports guild too but for backward compatibility we'll use partyInvite as query
// TODO absolutely refactor this horrible code
let link = `?partyInvite=${utils.encrypt(JSON.stringify({id: group._id, inviter: inviter, name: group.name}))}`;
let inviterVars = getUserInfo(inviter, ['name', 'email']);
let variables = [
{name: 'LINK', content: link},
{name: 'INVITER', content: req.body.inviter || inviterVars.name},
{name: 'REPLY_TO_ADDRESS', content: inviterVars.email}
];
if(group.type == 'guild'){
variables.push({name: 'GUILD_NAME', content: group.name});
}
// TODO implement "users can only be invited once"
// Check for the email address not to be unsubscribed
return EmailUnsubscription.findOne({email: invite.email}).exec()
.then(unsubscribed => {
if (!unsubscribed) utils.txnEmail(invite, ('invite-friend' + (group.type == 'guild' ? '-guild' : '')), variables);
});
}
});
}))
.then(() => {
if (usersAlreadyRegistered.length > 0){
return _inviteByUUIDs(usersAlreadyRegistered, group, inviter, req, res, next);
}
res.respond(200, {}); // TODO what to return?
});
}; */
/**
* @api {post} /groups/:groupId/invite Invite users to a group using their UUIDs or email addresses
* @apiVersion 3.0.0
* @apiName InviteToGroup
* @apiGroup Group
*
* @apiParam {string} groupId The group _id (or 'party')
*
* @apiParam {array} emails An array of emails addresses to invite (optional) (inside body)
* @apiParam {array} uuids An array of uuids to invite (optional) (inside body)
* @apiParam {string} inviter The inviters' name (optional) (inside body)
*
* @apiSuccess {Object} empty An empty object
*/
/* api.inviteToGroup = {
method: 'POST',
url: '/groups/:groupId/invite',
middlewares: [authWithHeaders(), cron],
handler (req, res, next) {
let user = res.locals.user;
req.checkParams('groupId', res.t('groupIdRequired')).notEmpty();
let validationErrors = req.validationErrors();
if (validationErrors) return next(validationErrors);
Group.getGroup(user, req.params.groupId, '-chat') // Do not fetch chat TODO other fields too?
.then(group => {
if (!group) throw new NotFound(res.t('groupNotFound'));
let uuids = req.body.uuids;
let emails = req.body.emails;
if (uuids && emails) { // TODO fix this, low priority, allow for inviting by both at the same time
throw new BadRequest(res.t('canOnlyInviteEmailUuid'));
} else if (Array.isArray(uuids)) {
return _inviteByUUIDs(uuids, group, user, req, res, next);
} else if (Array.isArray(emails)) {
return _inviteByEmails(emails, group, user, req, res, next)
} else {
throw new BadRequest(res.t('canOnlyInviteEmailUuid'));
}
})
.catch(next);
},
};*/
export default api;

View File

@@ -9,6 +9,7 @@ import notFoundHandler from './notFound';
import nconf from 'nconf';
import morgan from 'morgan';
import responseHandler from './response';
import setupBody from './setupBody';
const IS_PROD = nconf.get('IS_PROD');
const DISABLE_LOGGING = nconf.get('DISABLE_REQUEST_LOGGING');
@@ -23,6 +24,7 @@ export default function attachMiddlewares (app) {
app.use(bodyParser.json());
app.use(expressValidator());
app.use(analytics);
app.use(setupBody);
app.use(responseHandler);
app.use(getUserLanguage);

View File

@@ -0,0 +1,5 @@
// TODO tests?
export default function setupBodyMiddleware (req, res, next) {
req.body = req.body || {};
next();
}

View File

@@ -162,6 +162,8 @@ schema.statics.getGroup = function getGroup (user, groupId, fields) {
.findOne(query)
.select(fields)
.exec(); // TODO catch errors here?
// TODO purge chat flags info?
};
// TODO move to its own model