mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 15:48:04 +01:00
Merge branch 'api-v3-groups-invites' of https://github.com/TheHollidayInn/habitrpg into TheHollidayInn-api-v3-groups-invites
This commit is contained in:
@@ -37,7 +37,7 @@
|
|||||||
"memberCannotRemoveYourself": "You cannot remove yourself!",
|
"memberCannotRemoveYourself": "You cannot remove yourself!",
|
||||||
"groupMemberNotFound": "User not found among group's members",
|
"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.",
|
"canOnlyInviteEmailUuid": "Can only invite using uuids or emails.",
|
||||||
"inviteMissingEmail": "Missing email address in invite.",
|
"inviteMissingEmail": "Missing email address in invite.",
|
||||||
"onlyGroupLeaderChal": "Only the group leader can create challenges",
|
"onlyGroupLeaderChal": "Only the group leader can create challenges",
|
||||||
"pubChalsMinPrize": "Prize must be at least 1 Gem for public challenges.",
|
"pubChalsMinPrize": "Prize must be at least 1 Gem for public challenges.",
|
||||||
@@ -47,5 +47,13 @@
|
|||||||
"challengeNotFound": "Challenge not found.",
|
"challengeNotFound": "Challenge not found.",
|
||||||
"onlyLeaderDeleteChal": "Only the challenge leader can delete it.",
|
"onlyLeaderDeleteChal": "Only the challenge leader can delete it.",
|
||||||
"winnerNotFound": "Winner with id \"<%= userId %>\" not found or not part of the challenge.",
|
"winnerNotFound": "Winner with id \"<%= userId %>\" not found or not part of the challenge.",
|
||||||
"partyMustbePrivate": "Parties must be private"
|
"partyMustbePrivate": "Parties must be private",
|
||||||
|
"userAlreadyInGroup": "User already in that group.",
|
||||||
|
"userAlreadyInvitedToGroup": "User already invited to that group.",
|
||||||
|
"userAlreadyPendingInvitation": "User already pending invitation.",
|
||||||
|
"userAlreadyInAParty": "User already in a party.",
|
||||||
|
"userWithIDNotFound": "User with id \"<%= userId %>\" not found.",
|
||||||
|
"uuidsMustBeAnArray": "UUIDs invites must be a an Array.",
|
||||||
|
"emailsMustBeAnArray": "Email invites must be a an Array.",
|
||||||
|
"canOnlyInviteMaxInvites": "You can only invite \"<%= maxInvites %>\" at a time"
|
||||||
}
|
}
|
||||||
|
|||||||
310
test/api/v3/integration/groups/POST-groups_invite.test.js
Normal file
310
test/api/v3/integration/groups/POST-groups_invite.test.js
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../../helpers/api-integration.helper';
|
||||||
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
|
||||||
|
const INVITES_LIMIT = 100;
|
||||||
|
|
||||||
|
describe('Post /groups/:groupId/invite', () => {
|
||||||
|
let inviter;
|
||||||
|
let group;
|
||||||
|
let groupName = 'Test Public Guild';
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
inviter = await generateUser({balance: 1});
|
||||||
|
group = await inviter.post('/groups', {
|
||||||
|
name: groupName,
|
||||||
|
type: 'guild',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('user id invites', () => {
|
||||||
|
it('returns an error when invited user is not found', async () => {
|
||||||
|
let fakeID = generateUUID();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [fakeID],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('userWithIDNotFound', {userId: fakeID}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when uuids is not an array', async () => {
|
||||||
|
let fakeID = generateUUID();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: {fakeID},
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('uuidsMustBeAnArray'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty when uuids is empty', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when there are more than INVITES_LIMIT uuids', async () => {
|
||||||
|
let uuids = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 101; i += 1) {
|
||||||
|
uuids.push(generateUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites a user to a group by uuid', async () => {
|
||||||
|
let userToInvite = await generateUser();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id],
|
||||||
|
})).to.eventually.deep.equal([{
|
||||||
|
id: group._id,
|
||||||
|
name: groupName,
|
||||||
|
inviter: inviter._id,
|
||||||
|
}]);
|
||||||
|
await expect(userToInvite.get('/user'))
|
||||||
|
.to.eventually.have.deep.property('invitations.guilds[0].id', group._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites multiple users to a group by uuid', async () => {
|
||||||
|
let userToInvite = await generateUser();
|
||||||
|
let userToInvite2 = await generateUser();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id, userToInvite2._id],
|
||||||
|
})).to.eventually.deep.equal([
|
||||||
|
{
|
||||||
|
id: group._id,
|
||||||
|
name: groupName,
|
||||||
|
inviter: inviter._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: group._id,
|
||||||
|
name: groupName,
|
||||||
|
inviter: inviter._id,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
await expect(userToInvite.get('/user')).to.eventually.have.deep.property('invitations.guilds[0].id', group._id);
|
||||||
|
await expect(userToInvite2.get('/user')).to.eventually.have.deep.property('invitations.guilds[0].id', group._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when inviting multiple users and a user is not found', async () => {
|
||||||
|
let userToInvite = await generateUser();
|
||||||
|
let fakeID = generateUUID();
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id, fakeID],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 404,
|
||||||
|
error: 'NotFound',
|
||||||
|
message: t('userWithIDNotFound', {userId: fakeID}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('email invites', () => {
|
||||||
|
let testInvite = {name: 'test', email: 'test@habitica.com'};
|
||||||
|
|
||||||
|
it('returns an error when invite is missing an email', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
emails: [{name: 'test'}],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('inviteMissingEmail'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when emails is not an array', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
emails: {testInvite},
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('emailsMustBeAnArray'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty when emails is an empty array', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
emails: [],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.empty;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when there are more than INVITES_LIMIT emails', async () => {
|
||||||
|
let emails = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 101; i += 1) {
|
||||||
|
emails.push(`${generateUUID()}@habitica.com`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
emails,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites a user to a group by email', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
emails: [testInvite],
|
||||||
|
})).to.exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites multiple users to a group by email', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
emails: [testInvite, {name: 'test2', email: 'test2@habitica.com'}],
|
||||||
|
})).to.exist;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('user and email invites', () => {
|
||||||
|
it('returns an error when emails and uuids are not provided', async () => {
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('canOnlyInviteEmailUuid'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when there are more than INVITES_LIMIT uuids and emails', async () => {
|
||||||
|
let emails = [];
|
||||||
|
let uuids = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 50; i += 1) {
|
||||||
|
emails.push(`${generateUUID()}@habitica.com`);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 51; i += 1) {
|
||||||
|
uuids.push(generateUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
emails,
|
||||||
|
uuids,
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invites users to a group by uuid and email', async () => {
|
||||||
|
let newUser = await generateUser();
|
||||||
|
let invite = await inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [newUser._id],
|
||||||
|
emails: [{name: 'test', email: 'test@habitica.com'}],
|
||||||
|
});
|
||||||
|
let invitedUser = await newUser.get('/user');
|
||||||
|
|
||||||
|
expect(invite).to.exist;
|
||||||
|
expect(invitedUser.invitations.guilds[0].id).to.equal(group._id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('guild invites', () => {
|
||||||
|
it('returns an error when invited user is already invited to the group', async () => {
|
||||||
|
let userToInivite = await generateUser();
|
||||||
|
await inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [userToInivite._id],
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [userToInivite._id],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('userAlreadyInvitedToGroup'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when invited user is already in the group', async () => {
|
||||||
|
let userToInvite = await generateUser();
|
||||||
|
await inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id],
|
||||||
|
});
|
||||||
|
await userToInvite.post(`/groups/${group._id}/join`);
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${group._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('userAlreadyInGroup'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('party invites', () => {
|
||||||
|
let party;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
party = await inviter.post('/groups', {
|
||||||
|
name: 'Test Party',
|
||||||
|
type: 'party',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when invited user has a pending invitation to the party', async () => {
|
||||||
|
let userToInvite = await generateUser();
|
||||||
|
await inviter.post(`/groups/${party._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id],
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${party._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('userAlreadyPendingInvitation'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an error when invited user is already in the party', async () => {
|
||||||
|
let userToInvite = await generateUser();
|
||||||
|
await inviter.post(`/groups/${party._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id],
|
||||||
|
});
|
||||||
|
await userToInvite.post(`/groups/${party._id}/join`);
|
||||||
|
|
||||||
|
await expect(inviter.post(`/groups/${party._id}/invite`, {
|
||||||
|
uuids: [userToInvite._id],
|
||||||
|
}))
|
||||||
|
.to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('userAlreadyInAParty'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,16 +2,20 @@ import { authWithHeaders } from '../../middlewares/api-v3/auth';
|
|||||||
import Q from 'q';
|
import Q from 'q';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import cron from '../../middlewares/api-v3/cron';
|
import cron from '../../middlewares/api-v3/cron';
|
||||||
import { model as Group } from '../../models/group';
|
import {
|
||||||
|
INVITES_LIMIT,
|
||||||
|
model as Group,
|
||||||
|
} from '../../models/group';
|
||||||
import { model as User } from '../../models/user';
|
import { model as User } from '../../models/user';
|
||||||
|
import { model as EmailUnsubscription } from '../../models/emailUnsubscription';
|
||||||
import {
|
import {
|
||||||
NotFound,
|
NotFound,
|
||||||
BadRequest,
|
BadRequest,
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
} from '../../libs/api-v3/errors';
|
} from '../../libs/api-v3/errors';
|
||||||
import * as firebase from '../../libs/api-v3/firebase';
|
import * as firebase from '../../libs/api-v3/firebase';
|
||||||
import { txnEmail } from '../../libs/api-v3/email';
|
import { sendTxn as sendTxnEmail } from '../../libs/api-v3/email';
|
||||||
// import { encrypt } from '../../libs/api-v3/encryption';
|
import { encrypt } from '../../libs/api-v3/encryption';
|
||||||
|
|
||||||
let api = {};
|
let api = {};
|
||||||
|
|
||||||
@@ -305,7 +309,7 @@ api.leaveGroup = {
|
|||||||
// Send an email to the removed user with an optional message from the leader
|
// Send an email to the removed user with an optional message from the leader
|
||||||
function _sendMessageToRemoved (group, removedUser, message) {
|
function _sendMessageToRemoved (group, removedUser, message) {
|
||||||
if (removedUser.preferences.emailNotifications.kickedGroup !== false) {
|
if (removedUser.preferences.emailNotifications.kickedGroup !== false) {
|
||||||
txnEmail(removedUser, `kicked-from-${group.type}`, [
|
sendTxnEmail(removedUser, `kicked-from-${group.type}`, [
|
||||||
{name: 'GROUP_NAME', content: group.name},
|
{name: 'GROUP_NAME', content: group.name},
|
||||||
{name: 'MESSAGE', content: message},
|
{name: 'MESSAGE', content: message},
|
||||||
{name: 'GUILDS_LINK', content: '/#/options/groups/guilds/public'},
|
{name: 'GUILDS_LINK', content: '/#/options/groups/guilds/public'},
|
||||||
@@ -392,145 +396,105 @@ api.removeGroupMember = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* function _inviteByUUIDs (uuids, group, inviter, req, res, next) {
|
async function _inviteByUUID (uuid, group, inviter, req, res) {
|
||||||
async.each(uuids, function(uuid, cb){
|
// @TODO: Add Push Notifications
|
||||||
User.findById(uuid, function(err,invite){
|
let userToInvite = await User.findById(uuid).exec();
|
||||||
if (err) return cb(err);
|
|
||||||
if (!invite)
|
if (!userToInvite) {
|
||||||
return cb({code:400,err:'User with id "' + uuid + '" not found'});
|
throw new NotFound(res.t('userWithIDNotFound', {userId: uuid}));
|
||||||
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') {
|
if (group.type === 'guild') {
|
||||||
invite.invitations.guilds.push({id: group._id, name: group.name, inviter:res.locals.user._id});
|
if (_.contains(userToInvite.guilds, group._id)) {
|
||||||
|
throw new NotAuthorized(res.t('userAlreadyInGroup'));
|
||||||
pushNotify.sendNotify(invite, shared.i18n.t('invitedGuild'), group.name);
|
}
|
||||||
}else{
|
if (_.find(userToInvite.invitations.guilds, {id: group._id})) {
|
||||||
|
throw new NotAuthorized(res.t('userAlreadyInvitedToGroup'));
|
||||||
|
}
|
||||||
|
userToInvite.invitations.guilds.push({id: group._id, name: group.name, inviter: inviter._id});
|
||||||
|
} else if (group.type === 'party') {
|
||||||
|
if (!_.isEmpty(userToInvite.invitations.party)) {
|
||||||
|
throw new NotAuthorized(res.t('userAlreadyPendingInvitation'));
|
||||||
|
}
|
||||||
|
if (userToInvite.party._id) {
|
||||||
|
throw new NotAuthorized(res.t('userAlreadyInAParty'));
|
||||||
|
}
|
||||||
|
// @TODO: Why was this here?
|
||||||
// req.body.type in 'guild', 'party'
|
// req.body.type in 'guild', 'party'
|
||||||
invite.invitations.party = {id: group._id, name: group.name, inviter:res.locals.user._id};
|
userToInvite.invitations.party = {id: group._id, name: group.name, inviter: inviter._id};
|
||||||
|
|
||||||
pushNotify.sendNotify(invite, shared.i18n.t('invitedParty'), group.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group.invites.push(invite._id);
|
let groupLabel = group.type === 'guild' ? 'Guild' : 'Party';
|
||||||
|
if (userToInvite.preferences.emailNotifications[`invited${groupLabel}`] !== false) {
|
||||||
async.series([
|
let emailVars = [
|
||||||
function(cb){
|
{name: 'INVITER', content: inviter.profile.name},
|
||||||
invite.save(cb);
|
{name: 'REPLY_TO_ADDRESS', content: inviter.email},
|
||||||
}
|
|
||||||
], 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'){
|
if (group.type === 'guild') {
|
||||||
emailVars.push(
|
emailVars.push(
|
||||||
{name: 'GUILD_NAME', content: group.name},
|
{name: 'GUILD_NAME', content: group.name},
|
||||||
{name: 'GUILD_URL', content: '/#/options/groups/guilds/public'}
|
{name: 'GUILD_URL', content: '/#/options/groups/guilds/public'},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
emailVars.push(
|
emailVars.push(
|
||||||
{name: 'PARTY_NAME', content: group.name},
|
{name: 'PARTY_NAME', content: group.name},
|
||||||
{name: 'PARTY_URL', content: '/#/options/groups/party'}
|
{name: 'PARTY_URL', content: '/#/options/groups/party'},
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.txnEmail(invite, ('invited-' + (group.type == 'guild' ? 'guild' : 'party')), emailVars);
|
sendTxnEmail(userToInvite, `invited-${groupLabel}`, emailVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
cb();
|
let userInvited = await userToInvite.save();
|
||||||
});
|
if (group.type === 'guild') {
|
||||||
|
return userInvited.invitations.guilds[userToInvite.invitations.guilds.length - 1];
|
||||||
|
} else if (group.type === 'party') {
|
||||||
|
return userInvited.invitations.party;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}, 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) {
|
async function _inviteByEmail (invite, group, inviter, req, res) {
|
||||||
let usersAlreadyRegistered = [];
|
let userReturnInfo;
|
||||||
let invitesToSend = [];
|
|
||||||
|
|
||||||
return Q.all(emails.forEach(invite => {
|
|
||||||
if (!invite.email) throw new BadRequest(res.t('inviteMissingEmail'));
|
if (!invite.email) throw new BadRequest(res.t('inviteMissingEmail'));
|
||||||
|
|
||||||
return User.findOne({$or: [
|
let userToContact = await User.findOne({$or: [
|
||||||
{'auth.local.email': invite.email},
|
{'auth.local.email': invite.email},
|
||||||
{'auth.facebook.emails.value': invite.email}
|
{'auth.facebook.emails.value': invite.email},
|
||||||
]})
|
]})
|
||||||
.select({_id: true, 'preferences.emailNotifications': true})
|
.select({_id: true, 'preferences.emailNotifications': true})
|
||||||
.exec()
|
.exec();
|
||||||
.then(userToContact => {
|
|
||||||
if (userToContact) {
|
if (userToContact) {
|
||||||
usersAlreadyRegistered.push(userToContact._id); // TODO does it work not returning
|
userReturnInfo = await _inviteByUUID(userToContact._id, group, inviter, req, res);
|
||||||
} else {
|
} else {
|
||||||
|
userReturnInfo = invite.email;
|
||||||
// yeah, it supports guild too but for backward compatibility we'll use partyInvite as query
|
// yeah, it supports guild too but for backward compatibility we'll use partyInvite as query
|
||||||
// TODO absolutely refactor this horrible code
|
// TODO absolutely refactor this horrible code
|
||||||
let link = `?partyInvite=${utils.encrypt(JSON.stringify({id: group._id, inviter: inviter, name: group.name}))}`;
|
const partyQueryString = JSON.stringify({id: group._id, inviter, name: group.name});
|
||||||
|
const encryptedPartyqueryString = encrypt(partyQueryString);
|
||||||
|
let link = `?partyInvite=${encryptedPartyqueryString}`;
|
||||||
|
|
||||||
let inviterVars = getUserInfo(inviter, ['name', 'email']);
|
|
||||||
let variables = [
|
let variables = [
|
||||||
{name: 'LINK', content: link},
|
{name: 'LINK', content: link},
|
||||||
{name: 'INVITER', content: req.body.inviter || inviterVars.name},
|
{name: 'INVITER', content: inviter || inviter.profile.name},
|
||||||
{name: 'REPLY_TO_ADDRESS', content: inviterVars.email}
|
{name: 'REPLY_TO_ADDRESS', content: inviter.email},
|
||||||
];
|
];
|
||||||
|
|
||||||
if(group.type == 'guild'){
|
if (group.type === 'guild') {
|
||||||
variables.push({name: 'GUILD_NAME', content: group.name});
|
variables.push({name: 'GUILD_NAME', content: group.name});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO implement "users can only be invited once"
|
// TODO implement "users can only be invited once"
|
||||||
// Check for the email address not to be unsubscribed
|
// Check for the email address not to be unsubscribed
|
||||||
return EmailUnsubscription.findOne({email: invite.email}).exec()
|
let userIsUnsubscribed = await EmailUnsubscription.findOne({email: invite.email}).exec();
|
||||||
.then(unsubscribed => {
|
let groupLabel = group.type === 'guild' ? '-guild' : '';
|
||||||
if (!unsubscribed) utils.txnEmail(invite, ('invite-friend' + (group.type == 'guild' ? '-guild' : '')), variables);
|
if (!userIsUnsubscribed) sendTxnEmail(invite, `invite-friend${groupLabel}`, variables);
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
.then(() => {
|
|
||||||
if (usersAlreadyRegistered.length > 0){
|
|
||||||
return _inviteByUUIDs(usersAlreadyRegistered, group, inviter, req, res, next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.respond(200, {}); // TODO what to return?
|
return userReturnInfo;
|
||||||
});
|
}
|
||||||
}; */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /groups/:groupId/invite Invite users to a group using their UUIDs or email addresses
|
* @api {post} /groups/:groupId/invite Invite users to a group using their UUIDs or email addresses
|
||||||
@@ -564,15 +528,49 @@ api.inviteToGroup = {
|
|||||||
let uuids = req.body.uuids;
|
let uuids = req.body.uuids;
|
||||||
let emails = req.body.emails;
|
let emails = req.body.emails;
|
||||||
|
|
||||||
if (uuids && emails) { // TODO fix this, low priority, allow for inviting by both at the same time
|
let uuidsIsArray = Array.isArray(uuids);
|
||||||
throw new BadRequest(res.t('canOnlyInviteEmailUuid'));
|
let emailsIsArray = Array.isArray(emails);
|
||||||
} else if (Array.isArray(uuids)) {
|
|
||||||
// return _inviteByUUIDs(uuids, group, user, req, res, next);
|
if (!uuids && !emails) {
|
||||||
} else if (Array.isArray(emails)) {
|
|
||||||
// return _inviteByEmails(emails, group, user, req, res, next);
|
|
||||||
} else {
|
|
||||||
throw new BadRequest(res.t('canOnlyInviteEmailUuid'));
|
throw new BadRequest(res.t('canOnlyInviteEmailUuid'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let results = [];
|
||||||
|
let totalInvites = 0;
|
||||||
|
|
||||||
|
if (uuids) {
|
||||||
|
if (!uuidsIsArray) {
|
||||||
|
throw new BadRequest(res.t('uuidsMustBeAnArray'));
|
||||||
|
} else {
|
||||||
|
totalInvites += uuids.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emails) {
|
||||||
|
if (!emailsIsArray) {
|
||||||
|
throw new BadRequest(res.t('emailsMustBeAnArray'));
|
||||||
|
} else {
|
||||||
|
totalInvites += emails.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalInvites > INVITES_LIMIT) {
|
||||||
|
throw new BadRequest(res.t('canOnlyInviteMaxInvites', {maxInvites: INVITES_LIMIT}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuids) {
|
||||||
|
let uuidInvites = uuids.map((uuid) => _inviteByUUID(uuid, group, user, req, res));
|
||||||
|
let uuidResults = await Q.all(uuidInvites);
|
||||||
|
results.push(...uuidResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emails) {
|
||||||
|
let emailInvites = emails.map((invite) => _inviteByEmail(invite, group, user, req, res));
|
||||||
|
let emailResults = await Q.all(emailInvites);
|
||||||
|
results.push(...emailResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.respond(200, results);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -520,3 +520,5 @@ model.count({_id: 'habitrpg'}, (err, ct) => {
|
|||||||
privacy: 'public',
|
privacy: 'public',
|
||||||
}).save();
|
}).save();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const INVITES_LIMIT = 100;
|
||||||
|
|||||||
Reference in New Issue
Block a user