mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
Merge branch 'develop' into sabrecat/teams-rebase
This commit is contained in:
Submodule habitica-images updated: 4f18c662a7...58b8905b08
@@ -4,6 +4,7 @@ import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { MAX_SUMMARY_SIZE_FOR_CHALLENGES } from '../../../../../website/common/script/constants';
|
||||
|
||||
describe('POST /challenges', () => {
|
||||
it('returns error when group is empty', async () => {
|
||||
@@ -60,6 +61,22 @@ describe('POST /challenges', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('return error when creating a challenge with summary with greater than MAX_SUMMARY_SIZE_FOR_CHALLENGES characters', async () => {
|
||||
const user = await generateUser();
|
||||
const summary = 'A'.repeat(MAX_SUMMARY_SIZE_FOR_CHALLENGES + 1);
|
||||
const group = createAndPopulateGroup({
|
||||
members: 1,
|
||||
});
|
||||
await expect(user.post('/challenges', {
|
||||
group: group._id,
|
||||
summary,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
|
||||
context('Creating a challenge for a valid group', () => {
|
||||
let groupLeader;
|
||||
let group;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
createAndPopulateGroup,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { MAX_SUMMARY_SIZE_FOR_CHALLENGES } from '../../../../../website/common/script/constants';
|
||||
|
||||
describe('PUT /challenges/:challengeId', () => {
|
||||
let privateGuild; let user; let nonMember; let challenge; let
|
||||
@@ -91,4 +92,15 @@ describe('PUT /challenges/:challengeId', () => {
|
||||
expect(res.name).to.equal('New Challenge Name');
|
||||
expect(res.description).to.equal('New challenge description.');
|
||||
});
|
||||
|
||||
it('return error when challenge summary is greater than MAX_SUMMARY_SIZE_FOR_CHALLENGES characters', async () => {
|
||||
const summary = 'A'.repeat(MAX_SUMMARY_SIZE_FOR_CHALLENGES + 1);
|
||||
await expect(user.put(`/challenges/${challenge._id}`, {
|
||||
summary,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { model as Group } from '../../../../../website/server/models/group';
|
||||
import { MAX_SUMMARY_SIZE_FOR_GUILDS } from '../../../../../website/common/script/constants';
|
||||
|
||||
describe('POST /group', () => {
|
||||
let user;
|
||||
@@ -71,6 +72,20 @@ describe('POST /group', () => {
|
||||
|
||||
expect(updatedGroup.summary).to.eql(summary);
|
||||
});
|
||||
|
||||
it('returns error when summary is longer than MAX_SUMMARY_SIZE_FOR_GUILDS characters', async () => {
|
||||
const name = 'Test Group';
|
||||
const summary = 'A'.repeat(MAX_SUMMARY_SIZE_FOR_GUILDS + 1);
|
||||
await expect(user.post('/groups', {
|
||||
name,
|
||||
type: 'guild',
|
||||
summary,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('Guilds', () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
import { MAX_SUMMARY_SIZE_FOR_GUILDS } from '../../../../../website/common/script/constants';
|
||||
|
||||
describe('PUT /group', () => {
|
||||
let leader; let nonLeader; let groupToUpdate; let
|
||||
@@ -130,4 +131,15 @@ describe('PUT /group', () => {
|
||||
|
||||
expect(response.bannedWordsAllowed).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('returns error when summary is longer than MAX_SUMMARY_SIZE_FOR_GUILDS characters', async () => {
|
||||
const summary = 'A'.repeat(MAX_SUMMARY_SIZE_FOR_GUILDS + 1);
|
||||
await expect(leader.put(`/groups/${groupToUpdate._id}`, {
|
||||
summary,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('invalidReqParams'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -690,6 +690,11 @@
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_by_a_campfire {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_by_a_campfire.png');
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_camping_out {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_camping_out.png');
|
||||
width: 141px;
|
||||
@@ -1354,6 +1359,11 @@
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_messy_room {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_messy_room.png');
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_meteor_shower {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_meteor_shower.png');
|
||||
width: 141px;
|
||||
@@ -1509,6 +1519,11 @@
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_rainbow_eucalyptus {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_rainbow_eucalyptus.png');
|
||||
width: 141px;
|
||||
height: 147px;
|
||||
}
|
||||
.background_rainbow_meadow {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_rainbow_meadow.png');
|
||||
width: 141px;
|
||||
@@ -2231,6 +2246,11 @@
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.icon_background_by_a_campfire {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_by_a_campfire.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.icon_background_camping_out {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_camping_out.png');
|
||||
width: 68px;
|
||||
@@ -2900,6 +2920,11 @@
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.icon_background_messy_room {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_messy_room.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.icon_background_meteor_shower {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_meteor_shower.png');
|
||||
width: 68px;
|
||||
@@ -3055,6 +3080,11 @@
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.icon_background_rainbow_eucalyptus {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_rainbow_eucalyptus.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.icon_background_rainbow_meadow {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_rainbow_meadow.png');
|
||||
width: 68px;
|
||||
@@ -18970,6 +19000,11 @@
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
.shield_armoire_dustpan {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_dustpan.png');
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.shield_armoire_fancyBlownGlassVase {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shield_armoire_fancyBlownGlassVase.png');
|
||||
width: 114px;
|
||||
@@ -20180,6 +20215,11 @@
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_shield_armoire_dustpan {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_shield_armoire_dustpan.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_shield_armoire_fancyBlownGlassVase {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_shield_armoire_fancyBlownGlassVase.png');
|
||||
width: 68px;
|
||||
@@ -20565,6 +20605,11 @@
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_weapon_armoire_featherDuster {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_featherDuster.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_weapon_armoire_festivalFirecracker {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_festivalFirecracker.png');
|
||||
width: 68px;
|
||||
@@ -20805,6 +20850,11 @@
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_weapon_armoire_pushBroom {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_pushBroom.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_weapon_armoire_rancherLasso {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_rancherLasso.png');
|
||||
width: 68px;
|
||||
@@ -21430,6 +21480,11 @@
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_armoire_featherDuster {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_featherDuster.png');
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_armoire_festivalFirecracker {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_festivalFirecracker.png');
|
||||
width: 90px;
|
||||
@@ -21670,6 +21725,11 @@
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_armoire_pushBroom {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_pushBroom.png');
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.weapon_armoire_rancherLasso {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_rancherLasso.png');
|
||||
width: 90px;
|
||||
@@ -26925,6 +26985,31 @@
|
||||
width: 117px;
|
||||
height: 120px;
|
||||
}
|
||||
.eyewear_mystery_202208 {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/eyewear_mystery_202208.png');
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.head_mystery_202208 {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/head_mystery_202208.png');
|
||||
width: 114px;
|
||||
height: 90px;
|
||||
}
|
||||
.shop_eyewear_mystery_202208 {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_eyewear_mystery_202208.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_head_mystery_202208 {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_head_mystery_202208.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.shop_set_mystery_202208 {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_set_mystery_202208.png');
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
.broad_armor_mystery_301404 {
|
||||
background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/broad_armor_mystery_301404.png');
|
||||
width: 90px;
|
||||
|
||||
@@ -803,6 +803,14 @@
|
||||
"backgroundUnderwaterStatuesText": "Underwater Statue Garden",
|
||||
"backgroundUnderwaterStatuesNotes": "Try not to blink in an Underwater Statue Garden.",
|
||||
|
||||
"backgrounds082022": "SET 99: Released August 2022",
|
||||
"backgroundRainbowEucalyptusText": "Rainbow Eucalyptus",
|
||||
"backgroundRainbowEucalyptusNotes": "Admire a Rainbow Eucalyptus grove.",
|
||||
"backgroundMessyRoomText": "Messy Room",
|
||||
"backgroundMessyRoomNotes": "Tidy up a Messy Room.",
|
||||
"backgroundByACampfireText": "By A Campfire",
|
||||
"backgroundByACampfireNotes": "Bask in the glow By a Campfire.",
|
||||
|
||||
"timeTravelBackgrounds": "Steampunk Backgrounds",
|
||||
"backgroundAirshipText": "Airship",
|
||||
"backgroundAirshipNotes": "Become a sky sailor on board your very own Airship.",
|
||||
|
||||
@@ -650,6 +650,10 @@
|
||||
"weaponArmoirePinkKiteNotes": "Diving, twirling, soaring high, your kite stands out against the sky. Increases all stats by <%= attrs %> each. Enchanted Armoire: Kite Set (Item 4 of 5)",
|
||||
"weaponArmoireYellowKiteText": "Yellow Kite",
|
||||
"weaponArmoireYellowKiteNotes": "Swooping and swerving to and fro, watch your cheerful kite go. Increases all stats by <%= attrs %> each. Enchanted Armoire: Kite Set (Item 5 of 5)",
|
||||
"weaponArmoirePushBroomText": "Push Broom",
|
||||
"weaponArmoirePushBroomNotes": "Take this tidying tool on your adventures and always be able to sweep a sooty stoop or clear cobwebs from corners. Increases Strength and Intelligence by <%= attrs %> each. Enchanted Armoire: Cleaning Supplies Set (Item 1 of 3)",
|
||||
"weaponArmoireFeatherDusterText": "Feather Duster",
|
||||
"weaponArmoireFeatherDusterNotes": "Let these fancy feathers fly over all your old objects to make them shine like new. Just beware of the disturbed dust so you don’t sneeze! Increases Constitution and Perception by <%= attrs %> each. Enchanted Armoire: Cleaning Supplies Set (Item 2 of 3)",
|
||||
|
||||
"armor": "armor",
|
||||
"armorCapitalized": "Armor",
|
||||
@@ -2487,6 +2491,8 @@
|
||||
"shieldArmoireSnareDrumNotes": "Rat-a-tat-tat! Gather your party for a parade or march into battle by playing this drum. Increases Constitution by <%= con %> and Intelligence by <%= int %>. Enchanted Armoire: Musical Instrument Set 1 (Item 3 of 3)",
|
||||
"shieldArmoireTreasureMapText": "Treasure Map",
|
||||
"shieldArmoireTreasureMapNotes": "X marks the spot! You never know what you’ll find when you follow this handy map to fabled treasures: gold, jewels, relics, or perhaps a petrified orange? Increases Strength and Intelligence by <%= attrs %> each. Enchanted Armoire: Fancy Pirate Set (Item 3 of 3).",
|
||||
"shieldArmoireDustpanText": "Dustpan",
|
||||
"shieldArmoireDustpanNotes": "Have this handy handheld dustpan ready every time you clean. A vanishing spell cast on it means you never have to search for a trash can to empty it into. Increases Intelligence and Constitution by <%= attrs %> each. Enchanted Armoire: Cleaning Supplies Set (Item 3 of 3).",
|
||||
|
||||
"back": "Back Accessory",
|
||||
"backBase0Text": "No Back Accessory",
|
||||
|
||||
@@ -510,6 +510,11 @@ const backgrounds = {
|
||||
underwater_cave: { },
|
||||
underwater_statues: { },
|
||||
},
|
||||
backgrounds082022: {
|
||||
rainbow_eucalyptus: { },
|
||||
messy_room: { },
|
||||
by_a_campfire: { },
|
||||
},
|
||||
timeTravelBackgrounds: {
|
||||
airship: {
|
||||
price: 1,
|
||||
|
||||
@@ -1100,6 +1100,11 @@ const shield = {
|
||||
str: 4,
|
||||
set: 'fancyPirate',
|
||||
},
|
||||
dustpan: {
|
||||
int: 4,
|
||||
con: 4,
|
||||
set: 'cleaningSupplies',
|
||||
},
|
||||
};
|
||||
|
||||
const headAccessory = {
|
||||
@@ -1524,6 +1529,16 @@ const weapon = {
|
||||
per: 3,
|
||||
set: 'kite',
|
||||
},
|
||||
pushBroom: {
|
||||
str: 4,
|
||||
int: 4,
|
||||
set: 'cleaningSupplies',
|
||||
},
|
||||
featherDuster: {
|
||||
con: 4,
|
||||
per: 4,
|
||||
set: 'cleaningSupplies',
|
||||
},
|
||||
};
|
||||
|
||||
forEach({
|
||||
|
||||
@@ -39,4 +39,5 @@ export default {
|
||||
directionUpDown: '"direction" is required and must be "up" or "down".',
|
||||
invalidTaskIdentifier: 'A task is identified by its UUID or alias.',
|
||||
invalidTaskScorings: 'This API route expects a body in the form of [{id, direction}].',
|
||||
summaryLengthExceedsMax: 'Summary length is too high.',
|
||||
};
|
||||
|
||||
@@ -30,6 +30,10 @@ import {
|
||||
} from '../../libs/challenges';
|
||||
import apiError from '../../libs/apiError';
|
||||
|
||||
import common from '../../../common';
|
||||
|
||||
const { MAX_SUMMARY_SIZE_FOR_CHALLENGES } = common.constants;
|
||||
|
||||
const api = {};
|
||||
|
||||
/**
|
||||
@@ -200,6 +204,7 @@ api.createChallenge = {
|
||||
const { user } = res.locals;
|
||||
|
||||
req.checkBody('group', apiError('groupIdRequired')).notEmpty();
|
||||
req.checkBody('summary', apiError('summaryLengthExceedsMax')).isLength({ max: MAX_SUMMARY_SIZE_FOR_CHALLENGES });
|
||||
|
||||
const validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
@@ -707,6 +712,7 @@ api.updateChallenge = {
|
||||
middlewares: [authWithHeaders()],
|
||||
async handler (req, res) {
|
||||
req.checkParams('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID();
|
||||
req.checkBody('summary', apiError('summaryLengthExceedsMax')).isLength({ max: MAX_SUMMARY_SIZE_FOR_CHALLENGES });
|
||||
|
||||
const validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
@@ -28,6 +28,7 @@ import amzLib from '../../libs/payments/amazon';
|
||||
import apiError from '../../libs/apiError';
|
||||
import { model as UserNotification } from '../../models/userNotification';
|
||||
|
||||
const { MAX_SUMMARY_SIZE_FOR_GUILDS } = common.constants;
|
||||
const MAX_EMAIL_INVITES_BY_USER = 200;
|
||||
const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL');
|
||||
|
||||
@@ -118,6 +119,11 @@ api.createGroup = {
|
||||
const group = new Group(Group.sanitize(req.body));
|
||||
group.leader = user._id;
|
||||
|
||||
req.checkBody('summary', apiError('summaryLengthExceedsMax')).isLength({ max: MAX_SUMMARY_SIZE_FOR_GUILDS });
|
||||
|
||||
const validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
if (group.type === 'guild') {
|
||||
if (group.privacy === 'public' && user.flags.chatRevoked) throw new NotAuthorized(res.t('chatPrivilegesRevoked'));
|
||||
if (user.balance < 1) throw new NotAuthorized(res.t('messageInsufficientGems'));
|
||||
@@ -191,7 +197,7 @@ api.createGroupPlan = {
|
||||
const group = new Group(Group.sanitize(req.body.groupToCreate));
|
||||
|
||||
req.checkBody('paymentType', res.t('paymentTypeRequired')).notEmpty();
|
||||
|
||||
req.checkBody('summary', apiError('summaryLengthExceedsMax')).isLength({ max: MAX_SUMMARY_SIZE_FOR_GUILDS });
|
||||
const validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
@@ -464,6 +470,7 @@ api.updateGroup = {
|
||||
const { user } = res.locals;
|
||||
|
||||
req.checkParams('groupId', apiError('groupIdRequired')).notEmpty();
|
||||
req.checkBody('summary', apiError('summaryLengthExceedsMax')).isLength({ max: MAX_SUMMARY_SIZE_FOR_GUILDS });
|
||||
|
||||
const validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
@@ -714,8 +714,11 @@ api.transferGems = {
|
||||
throw new NotAuthorized(res.t('badAmountOfGemsToSend'));
|
||||
}
|
||||
|
||||
// Received from {sender}
|
||||
await receiver.updateBalance(amount, 'gift_receive', sender._id, sender.auth.local.username);
|
||||
await sender.updateBalance(-amount, 'gift_send', sender._id, receiver.auth.local.username);
|
||||
|
||||
// Gifted to {receiver}
|
||||
await sender.updateBalance(-amount, 'gift_send', receiver._id, receiver.auth.local.username);
|
||||
// @TODO necessary? Also saved when sending the inbox message
|
||||
const promises = [receiver.save(), sender.save()];
|
||||
await Promise.all(promises);
|
||||
|
||||
Reference in New Issue
Block a user