mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 15:48:04 +01:00
wip on groups controller
This commit is contained in:
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
5
website/src/middlewares/api-v3/setupBody.js
Normal file
5
website/src/middlewares/api-v3/setupBody.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// TODO tests?
|
||||
export default function setupBodyMiddleware (req, res, next) {
|
||||
req.body = req.body || {};
|
||||
next();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user