diff --git a/test/api/v3/integration/challenges/GET-challenges_group_groupid.test.js b/test/api/v3/integration/challenges/GET-challenges_group_groupid.test.js index 9dcbbdcd5d..3f116d833d 100644 --- a/test/api/v3/integration/challenges/GET-challenges_group_groupid.test.js +++ b/test/api/v3/integration/challenges/GET-challenges_group_groupid.test.js @@ -27,22 +27,38 @@ describe('GET challenges/group/:groupId', () => { challenge2 = await generateChallenge(user, group); }); - it('should return group challenges for non member', async () => { + it('should return group challenges for non member with populated leader', async () => { let challenges = await nonMember.get(`/challenges/groups/${publicGuild._id}`); let foundChallenge1 = _.find(challenges, { _id: challenge._id }); expect(foundChallenge1).to.exist; + expect(foundChallenge1.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); expect(foundChallenge2).to.exist; + expect(foundChallenge2.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); }); - it('should return group challenges for member', async () => { + it('should return group challenges for member with populated leader', async () => { let challenges = await user.get(`/challenges/groups/${publicGuild._id}`); let foundChallenge1 = _.find(challenges, { _id: challenge._id }); expect(foundChallenge1).to.exist; + expect(foundChallenge1.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); expect(foundChallenge2).to.exist; + expect(foundChallenge2.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); }); }); @@ -76,13 +92,21 @@ describe('GET challenges/group/:groupId', () => { }); }); - it('should return group challenges for member', async () => { + it('should return group challenges for member with populated leader', async () => { let challenges = await user.get(`/challenges/groups/${privateGuild._id}`); let foundChallenge1 = _.find(challenges, { _id: challenge._id }); expect(foundChallenge1).to.exist; + expect(foundChallenge1.leader).to.eql({ + _id: privateGuild.leader._id, + profile: {name: user.profile.name}, + }); let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); expect(foundChallenge2).to.exist; + expect(foundChallenge2.leader).to.eql({ + _id: privateGuild.leader._id, + profile: {name: user.profile.name}, + }); }); }); }); diff --git a/test/api/v3/integration/challenges/GET-challenges_user.test.js b/test/api/v3/integration/challenges/GET-challenges_user.test.js index 8b6a279045..21ae0aa96b 100644 --- a/test/api/v3/integration/challenges/GET-challenges_user.test.js +++ b/test/api/v3/integration/challenges/GET-challenges_user.test.js @@ -5,7 +5,7 @@ import { } from '../../../../helpers/api-v3-integration.helper'; describe('GET challenges/user', () => { - let user, member, nonMember, challenge, challenge2; + let user, member, nonMember, challenge, challenge2, publicGuild; before(async () => { let { group, groupLeader, members } = await createAndPopulateGroup({ @@ -18,7 +18,7 @@ describe('GET challenges/user', () => { }); user = groupLeader; - + publicGuild = group; member = members[0]; nonMember = await generateUser(); @@ -33,6 +33,16 @@ describe('GET challenges/user', () => { let foundChallenge = _.find(challenges, { _id: challenge._id }); expect(foundChallenge).to.exist; + expect(foundChallenge.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); + expect(foundChallenge.group).to.eql({ + _id: publicGuild._id, + type: publicGuild.type, + privacy: publicGuild.privacy, + name: publicGuild.name, + }); }); it('should return challenges user has created', async () => { @@ -40,8 +50,28 @@ describe('GET challenges/user', () => { let foundChallenge1 = _.find(challenges, { _id: challenge._id }); expect(foundChallenge1).to.exist; + expect(foundChallenge1.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); + expect(foundChallenge1.group).to.eql({ + _id: publicGuild._id, + type: publicGuild.type, + privacy: publicGuild.privacy, + name: publicGuild.name, + }); let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); expect(foundChallenge2).to.exist; + expect(foundChallenge2.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); + expect(foundChallenge2.group).to.eql({ + _id: publicGuild._id, + type: publicGuild.type, + privacy: publicGuild.privacy, + name: publicGuild.name, + }); }); it('should return challenges in user\'s group', async () => { @@ -49,8 +79,28 @@ describe('GET challenges/user', () => { let foundChallenge1 = _.find(challenges, { _id: challenge._id }); expect(foundChallenge1).to.exist; + expect(foundChallenge1.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); + expect(foundChallenge1.group).to.eql({ + _id: publicGuild._id, + type: publicGuild.type, + privacy: publicGuild.privacy, + name: publicGuild.name, + }); let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); expect(foundChallenge2).to.exist; + expect(foundChallenge2.leader).to.eql({ + _id: publicGuild.leader._id, + profile: {name: user.profile.name}, + }); + expect(foundChallenge2.group).to.eql({ + _id: publicGuild._id, + type: publicGuild.type, + privacy: publicGuild.privacy, + name: publicGuild.name, + }); }); it('should not return challenges user doesn\'t have access to', async () => { diff --git a/test/api/v3/integration/challenges/PUT-challenges_challengeId.test.js b/test/api/v3/integration/challenges/PUT-challenges_challengeId.test.js index e916ab5b7a..33cc2e5166 100644 --- a/test/api/v3/integration/challenges/PUT-challenges_challengeId.test.js +++ b/test/api/v3/integration/challenges/PUT-challenges_challengeId.test.js @@ -50,7 +50,7 @@ describe('PUT /challenges/:challengeId', () => { let res = await user.put(`/challenges/${challenge._id}`, { // ignored prize: 33, - groupId: 'blabla', + group: 'blabla', memberCount: 33, tasksOrder: 'new order', official: true, @@ -63,7 +63,7 @@ describe('PUT /challenges/:challengeId', () => { }); expect(res.prize).to.equal(0); - expect(res.groupId).to.equal(privateGuild._id); + expect(res.group).to.equal(privateGuild._id); expect(res.memberCount).to.equal(2); expect(res.tasksOrder).not.to.equal('new order'); expect(res.official).to.equal(false); diff --git a/test/api/v3/integration/groups/GET-groups_id.test.js b/test/api/v3/integration/groups/GET-groups_id.test.js index 9cc2fce766..2ecb53a33f 100644 --- a/test/api/v3/integration/groups/GET-groups_id.test.js +++ b/test/api/v3/integration/groups/GET-groups_id.test.js @@ -43,10 +43,6 @@ describe('GET /groups/:id', () => { expect(group.leader._id).to.eql(leader._id); expect(group.leader.profile.name).to.eql(leader.profile.name); - expect(group.leader.items).to.exist; - expect(group.leader.stats).to.exist; - expect(group.leader.achievements).to.exist; - expect(group.leader.contributor).to.exist; }); }); }); diff --git a/test/api/v3/integration/groups/POST-groups.test.js b/test/api/v3/integration/groups/POST-groups.test.js index 8a64ef4bb5..8036d6b0d8 100644 --- a/test/api/v3/integration/groups/POST-groups.test.js +++ b/test/api/v3/integration/groups/POST-groups.test.js @@ -32,12 +32,17 @@ describe('POST /group', () => { }); it('sets the group leader to the user who created the group', async () => { - await expect( - user.post('/groups', { - name: 'Test Public Guild', - type: 'guild', - }) - ).to.eventually.have.property('leader', user._id); + let group = await user.post('/groups', { + name: 'Test Public Guild', + type: 'guild', + }); + + expect(group.leader).to.eql({ + _id: user._id, + profile: { + name: user.profile.name, + }, + }); }); }); @@ -86,6 +91,12 @@ describe('POST /group', () => { expect(publicGuild.type).to.equal(groupType); expect(publicGuild.memberCount).to.equal(1); expect(publicGuild.privacy).to.equal(groupPrivacy); + expect(publicGuild.leader).to.eql({ + _id: user._id, + profile: { + name: user.profile.name, + }, + }); }); }); @@ -106,6 +117,12 @@ describe('POST /group', () => { expect(privateGuild.type).to.equal(groupType); expect(privateGuild.memberCount).to.equal(1); expect(privateGuild.privacy).to.equal(groupPrivacy); + expect(privateGuild.leader).to.eql({ + _id: user._id, + profile: { + name: user.profile.name, + }, + }); }); it('deducts gems from user and adds them to guild bank', async () => { @@ -138,6 +155,12 @@ describe('POST /group', () => { expect(party.name).to.equal(partyName); expect(party.type).to.equal(partyType); expect(party.memberCount).to.equal(1); + expect(party.leader).to.eql({ + _id: user._id, + profile: { + name: user.profile.name, + }, + }); }); it('does not require gems to create a party', async () => { diff --git a/test/helpers/api-integration/v3/object-generators.js b/test/helpers/api-integration/v3/object-generators.js index b7785e664f..57f6e2163b 100644 --- a/test/helpers/api-integration/v3/object-generators.js +++ b/test/helpers/api-integration/v3/object-generators.js @@ -113,7 +113,7 @@ export async function createAndPopulateGroup (settings = {}) { // optional details argument for the initial challenge creation and an // optional update argument which will update the challenge via the db export async function generateChallenge (challengeCreator, group, details = {}, update = {}) { - details.groupId = group._id; + details.group = group._id; details.name = details.name || 'a challenge'; details.shortName = details.shortName || 'aChallenge'; details.prize = details.prize || 0; diff --git a/website/src/controllers/api-v3/challenges.js b/website/src/controllers/api-v3/challenges.js index a75cc6cc58..7eccbcb9bf 100644 --- a/website/src/controllers/api-v3/challenges.js +++ b/website/src/controllers/api-v3/challenges.js @@ -2,7 +2,10 @@ import { authWithHeaders } from '../../middlewares/api-v3/auth'; import _ from 'lodash'; import cron from '../../middlewares/api-v3/cron'; import { model as Challenge } from '../../models/challenge'; -import { model as Group } from '../../models/group'; +import { + model as Group, + basicFields as basicGroupFields, +} from '../../models/group'; import { model as User, nameFields, @@ -35,12 +38,12 @@ api.createChallenge = { async handler (req, res) { let user = res.locals.user; - req.checkBody('groupId', res.t('groupIdRequired')).notEmpty(); + req.checkBody('group', res.t('groupIdRequired')).notEmpty(); let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let groupId = req.body.groupId; + let groupId = req.body.group; let prize = req.body.prize; let group = await Group.getGroup({user, groupId, fields: '-chat', mustBeMember: true}); @@ -92,6 +95,17 @@ api.createChallenge = { }), group.save()]); let savedChal = results[0]; + // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + // await Q.ninvoke(savedChal, 'populate', ['leader', nameFields]); // doc.populate doesn't return a promise + let response = savedChal.toJSON(); + response.leader = (await User.findById(response.leader).select(nameFields).exec()).toJSON({minimize: true}); + response.group = { + _id: group._id, + name: group.name, + type: group.type, + privacy: group.privacy, + }; + await savedChal.syncToUser(user); // (it also saves the user) res.respond(201, savedChal); }, @@ -122,7 +136,7 @@ api.joinChallenge = { if (!challenge) throw new NotFound(res.t('challengeNotFound')); if (challenge.isMember(user)) throw new NotAuthorized(res.t('userAlreadyInChallenge')); - let group = await Group.getGroup({user, groupId: challenge.groupId, fields: '_id type privacy', optionalMembership: true}); + let group = await Group.getGroup({user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true}); if (!group || !challenge.hasAccess(user, group)) throw new NotFound(res.t('challengeNotFound')); challenge.memberCount += 1; @@ -158,7 +172,7 @@ api.leaveChallenge = { let challenge = await Challenge.findOne({ _id: req.params.challengeId }); if (!challenge) throw new NotFound(res.t('challengeNotFound')); - let group = await Group.getGroup({user, groupId: challenge.groupId, fields: '_id type privacy'}); + let group = await Group.getGroup({user, groupId: challenge.group, fields: '_id type privacy'}); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); if (!challenge.isMember(user)) throw new NotAuthorized(res.t('challengeMemberNotFound')); @@ -186,25 +200,32 @@ api.getUserChallenges = { async handler (req, res) { let user = res.locals.user; - let groups = user.guilds.slice(0); // slice is used to clone the array so we don't modify it directly - if (user.party._id) groups.push(user.party._id); - groups.push('habitrpg'); // tavern challenges - let challenges = await Challenge.find({ $or: [ {_id: {$in: user.challenges}}, // Challenges where the user is participating - {groupId: {$in: groups}}, // Challenges in groups where I'm a member + {group: {$in: user.getGroups()}}, // Challenges in groups where I'm a member {leader: user._id}, // Challenges where I'm the leader ], _id: {$ne: '95533e05-1ff9-4e46-970b-d77219f199e9'}, // remove the Spread the Word Challenge for now, will revisit when we fix the closing-challenge bug TODO revisit }) .sort('-official -timestamp') - // TODO populate - // .populate('group', '_id name type') - // .populate('leader', 'profile.name') + // .populate('group', basicGroupFields) + // .populate('leader', nameFields) .exec(); - res.respond(200, challenges); + let resChals = challenges.map(challenge => challenge.toJSON()); + // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + await Q.all(resChals.map((chal, index) => { + return Q.all([ + User.findById(chal.leader).select(nameFields).exec(), + Group.findById(chal.group).select(basicGroupFields).exec(), + ]).then(populatedData => { + resChals[index].leader = populatedData[0].toJSON({minimize: true}); + resChals[index].group = populatedData[1].toJSON({minimize: true}); + }); + })); + + res.respond(200, resChals); }, }; @@ -234,14 +255,20 @@ api.getGroupChallenges = { let group = await Group.getGroup({user, groupId}); if (!group) throw new NotFound(res.t('groupNotFound')); - let challenges = await Challenge.find({groupId}) + let challenges = await Challenge.find({group: groupId}) .sort('-official -timestamp') - // TODO populate - // .populate('group', '_id name type') - // .populate('leader', 'profile.name') + // .populate('leader', nameFields) // Only populate the leader as the group is implicit .exec(); - res.respond(200, challenges); + let resChals = challenges.map(challenge => challenge.toJSON()); + // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + await Q.all(resChals.map((chal, index) => { + return User.findById(chal.leader).select(nameFields).exec().then(populatedLeader => { + resChals[index].leader = populatedLeader.toJSON({minimize: true}); + }); + })); + + res.respond(200, resChals); }, }; @@ -268,13 +295,21 @@ api.getChallenge = { let user = res.locals.user; let challengeId = req.params.challengeId; - let challenge = await Challenge.findById(challengeId).exec(); + let challenge = await Challenge.findById(challengeId) + // .populate('leader', nameFields) // don't populate the group as we'll fetch it manually later + .exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); - let group = await Group.getGroup({user, groupId: challenge.groupId, fields: '_id type privacy', optionalMembership: true}); + // Fetching basicGroupFields + let group = await Group.getGroup({user, groupId: challenge.group, fields: basicGroupFields, optionalMembership: true}); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); - res.respond(200, challenge); + let chalRes = challenge.toJSON(); + chalRes.group = group.toJSON({minimize: true}); + // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + chalRes.leader = (await User.findById(chalRes.leader).select(nameFields).exec()).toJSON({minimize: true}); + + res.respond(200, chalRes); }, }; @@ -301,9 +336,9 @@ api.exportChallengeCsv = { let user = res.locals.user; let challengeId = req.params.challengeId; - let challenge = await Challenge.findById(challengeId).select('_id groupId leader tasksOrder').exec(); + let challenge = await Challenge.findById(challengeId).select('_id group leader tasksOrder').exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); - let group = await Group.getGroup({user, groupId: challenge.groupId, fields: '_id type privacy', optionalMembership: true}); + let group = await Group.getGroup({user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true}); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); // In v2 this used the aggregation framework to run some computation on MongoDB but then iterated through all @@ -377,7 +412,7 @@ api.updateChallenge = { let challenge = await Challenge.findById(challengeId).exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); - let group = await Group.getGroup({user, groupId: challenge.groupId, fields: '_id name type privacy', optionalMembership: true}); + let group = await Group.getGroup({user, groupId: challenge.group, fields: '_id name type privacy', optionalMembership: true}); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); if (!challenge.canModify(user)) throw new NotAuthorized(res.t('onlyLeaderUpdateChal')); @@ -398,12 +433,12 @@ async function _closeChal (challenge, broken = {}) { await Challenge.remove({_id: challenge._id}).exec(); // Refund the leader if the challenge is closed and the group not the tavern - if (challenge.groupId !== 'habitrpg' && brokenReason === 'CHALLENGE_DELETED') { + if (challenge.group !== 'habitrpg' && brokenReason === 'CHALLENGE_DELETED') { await User.update({_id: challenge.leader}, {$inc: {balance: challenge.prize / 4}}).exec(); } // Update the challengeCount on the group - await Group.update({_id: challenge.groupId}, {$inc: {challengeCount: -1}}).exec(); + await Group.update({_id: challenge.group}, {$inc: {challengeCount: -1}}).exec(); // Award prize to winner and notify if (winner) { diff --git a/website/src/controllers/api-v3/groups.js b/website/src/controllers/api-v3/groups.js index b9b633c111..ff223bcef8 100644 --- a/website/src/controllers/api-v3/groups.js +++ b/website/src/controllers/api-v3/groups.js @@ -5,8 +5,12 @@ import cron from '../../middlewares/api-v3/cron'; import { INVITES_LIMIT, model as Group, + basicFields as basicGroupFields, } from '../../models/group'; -import { model as User } from '../../models/user'; +import { + model as User, + nameFields, +} from '../../models/user'; import { model as EmailUnsubscription } from '../../models/emailUnsubscription'; import { NotFound, @@ -55,11 +59,14 @@ api.createGroup = { let results = await Q.all([user.save(), group.save()]); let savedGroup = results[1]; + // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + // await Q.ninvoke(savedGroup, 'populate', ['leader', nameFields]); // doc.populate doesn't return a promise + let response = savedGroup.toJSON(); + response.leader = (await User.findById(response.leader).select(nameFields).exec()).toJSON({minimize: true}); + res.respond(201, response); firebase.updateGroupData(savedGroup); firebase.addUserToGroup(savedGroup._id, user._id); - - return res.respond(201, savedGroup); // TODO populate }, }; @@ -87,7 +94,7 @@ api.getGroups = { // TODO validate types are acceptable? probably not necessary let types = req.query.type.split(','); - let groupFields = 'name description memberCount balance'; + let groupFields = basicGroupFields.concat('description memberCount balance'); let sort = '-memberCount'; let queries = []; @@ -152,7 +159,7 @@ api.getGroup = { let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId: req.params.groupId, populateLeader: true}); + let group = await Group.getGroup({user, groupId: req.params.groupId, populateLeader: false}); if (!group) throw new NotFound(res.t('groupNotFound')); if (!user.contributor.admin) { @@ -163,6 +170,8 @@ api.getGroup = { }); } + // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + group.leader = (await User.findById(group.leader).select(nameFields).exec()).toJSON({minimize: true}); res.respond(200, group); }, }; diff --git a/website/src/controllers/api-v3/members.js b/website/src/controllers/api-v3/members.js index 142926ed64..de2213d3b6 100644 --- a/website/src/controllers/api-v3/members.js +++ b/website/src/controllers/api-v3/members.js @@ -74,12 +74,12 @@ function _getMembersForItem (type) { let group; if (type === 'challenge-members') { - challenge = await Challenge.findById(challengeId).select('_id type leader groupId').exec(); + challenge = await Challenge.findById(challengeId).select('_id type leader group').exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); // optionalMembership is set to true because even if you're not member of the group you may be able to access the challenge // for example if you've been booted from it, are the leader or a site admin - group = await Group.getGroup({user, groupId: challenge.groupId, fields: '_id type privacy', optionalMembership: true}); + group = await Group.getGroup({user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true}); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); } else { group = await Group.getGroup({user, groupId, fields: '_id type'}); @@ -212,7 +212,7 @@ api.getChallengeMemberProgress = { // optionalMembership is set to true because even if you're not member of the group you may be able to access the challenge // for example if you've been booted from it, are the leader or a site admin - let group = await Group.getGroup({user, groupId: challenge.groupId, fields: '_id type privacy', optionalMembership: true}); + let group = await Group.getGroup({user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true}); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); if (!challenge.isMember(member)) throw new NotFound(res.t('challengeMemberNotFound')); diff --git a/website/src/controllers/api-v3/tasks.js b/website/src/controllers/api-v3/tasks.js index df78ca3a4f..2b711a5a2a 100644 --- a/website/src/controllers/api-v3/tasks.js +++ b/website/src/controllers/api-v3/tasks.js @@ -216,9 +216,9 @@ api.getChallengeTasks = { let user = res.locals.user; let challengeId = req.params.challengeId; - let challenge = await Challenge.findOne({_id: challengeId}).select('groupId leader tasksOrder').exec(); + let challenge = await Challenge.findOne({_id: challengeId}).select('group leader tasksOrder').exec(); if (!challenge) throw new NotFound(res.t('challengeNotFound')); - let group = await Group.getGroup({user, groupId: challenge.groupId, fields: '_id type privacy', optionalMembership: true}); + let group = await Group.getGroup({user, groupId: challenge.group, fields: '_id type privacy', optionalMembership: true}); if (!group || !challenge.canView(user, group)) throw new NotFound(res.t('challengeNotFound')); await _getTasks(req, res, res.locals.user, challenge); diff --git a/website/src/models/challenge.js b/website/src/models/challenge.js index 62307f6d8e..b67e8b7832 100644 --- a/website/src/models/challenge.js +++ b/website/src/models/challenge.js @@ -20,7 +20,7 @@ let schema = new Schema({ rewards: [{type: String, ref: 'Task'}], }, leader: {type: String, ref: 'User', validate: [validator.isUUID, 'Invalid uuid.'], required: true}, - groupId: {type: String, ref: 'Group', validate: [validator.isUUID, 'Invalid uuid.'], required: true}, + group: {type: String, ref: 'Group', validate: [validator.isUUID, 'Invalid uuid.'], required: true}, memberCount: {type: Number, default: 1}, prize: {type: Number, default: 0, min: 0}, // TODO no update? }); @@ -31,7 +31,7 @@ schema.plugin(baseModel, { }); // A list of additional fields that cannot be updated (but can be set on creation) -let noUpdate = ['groupId', 'official', 'shortName', 'prize']; +let noUpdate = ['group', 'official', 'shortName', 'prize']; schema.statics.sanitizeUpdate = function sanitizeUpdate (updateObj) { return this.sanitize(updateObj, noUpdate); }; @@ -49,10 +49,7 @@ schema.methods.canModify = function canModifyChallenge (user) { // Returns true if user has access to the challenge (can join) schema.methods.hasAccess = function hasAccessToChallenge (user, group) { if (group.type === 'guild' && group.privacy === 'public') return true; - let userGroups = user.guilds.slice(0); // clone user.guilds so we don't modify the original - if (user.party._id) userGroups.push(user.party._id); - userGroups.push('habitrpg'); // tavern - return userGroups.indexOf(this.groupId) !== -1; + return user.getGroups().indexOf(this.group) !== -1; }; // Returns true if user can view the challenge diff --git a/website/src/models/group.js b/website/src/models/group.js index 1bbb0145a5..890662de4c 100644 --- a/website/src/models/group.js +++ b/website/src/models/group.js @@ -84,6 +84,9 @@ schema.statics.sanitizeUpdate = function sanitizeUpdate (updateObj) { return this.sanitize(updateObj, noUpdate); }; +// Basic fields to fetch for populating a group info +export let basicFields = 'name type privacy'; + // TODO migration /** * Derby duplicated stuff. This is a temporary solution, once we're completely off derby we'll run an mongo migration @@ -472,7 +475,7 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all') { let challenges = await Challenge.find({ _id: {$in: user.challenges}, - groupId: group._id, + group: group._id, }); let challengesToRemoveUserFrom = challenges.map(chal => { diff --git a/website/src/models/user.js b/website/src/models/user.js index cc1ebccf54..31272dc874 100644 --- a/website/src/models/user.js +++ b/website/src/models/user.js @@ -654,6 +654,14 @@ schema.methods.isSubscribed = function isSubscribed () { return !!this.purchased.plan.customerId; // eslint-disable-line no-implicit-coercion }; +// Get an array of groups ids the user is member of +schema.methods.getGroups = function getUserGroups () { + let userGroups = this.guilds.slice(0); // clone user.guilds so we don't modify the original + if (this.party._id) userGroups.push(this.party._id); + userGroups.push('habitrpg'); // tavern + return userGroups; +}; + // Unlink challenges tasks (and the challenge itself) from user schema.methods.unlinkChallengeTasks = async function unlinkChallengeTasks (challengeId, keep) { let user = this;