diff --git a/habitica-images b/habitica-images index b9f9a9b6e3..a6f741c997 160000 --- a/habitica-images +++ b/habitica-images @@ -1 +1 @@ -Subproject commit b9f9a9b6e313787b773a2db6f5205a10481c3189 +Subproject commit a6f741c997717d0a11ee7fe14e0b7fd0d8c0f6cb diff --git a/website/client/package-lock.json b/website/client/package-lock.json index cb5a114000..78e44d9bc2 100644 --- a/website/client/package-lock.json +++ b/website/client/package-lock.json @@ -16940,6 +16940,11 @@ "domelementtype": "1" } }, + "dompurify": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.8.tgz", + "integrity": "sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==" + }, "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", diff --git a/website/client/package.json b/website/client/package.json index cc15ca8b92..a116711b39 100644 --- a/website/client/package.json +++ b/website/client/package.json @@ -33,6 +33,7 @@ "bootstrap-vue": "^2.22.0", "chai": "^4.3.6", "core-js": "^3.22.7", + "dompurify": "^2.3.8", "eslint": "^6.8.0", "eslint-config-habitrpg": "^6.2.0", "eslint-plugin-mocha": "^5.3.0", diff --git a/website/client/src/assets/css/sprites/spritesmith-main.css b/website/client/src/assets/css/sprites/spritesmith-main.css index b4ee575052..f153612926 100644 --- a/website/client/src/assets/css/sprites/spritesmith-main.css +++ b/website/client/src/assets/css/sprites/spritesmith-main.css @@ -605,6 +605,11 @@ width: 141px; height: 147px; } +.background_beach_with_dunes { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_beach_with_dunes.png'); + width: 141px; + height: 147px; +} .background_beehive { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_beehive.png'); width: 141px; @@ -1389,6 +1394,11 @@ width: 141px; height: 147px; } +.background_mountain_waterfall { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_mountain_waterfall.png'); + width: 141px; + height: 147px; +} .background_mystical_observatory { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_mystical_observatory.png'); width: 141px; @@ -1559,6 +1569,11 @@ width: 141px; height: 147px; } +.background_sailboat_at_sunset { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_sailboat_at_sunset.png'); + width: 141px; + height: 147px; +} .background_salt_lake { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/background_salt_lake.png'); width: 141px; @@ -2116,6 +2131,11 @@ width: 68px; height: 68px; } +.icon_background_beach_with_dunes { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_beach_with_dunes.png'); + width: 67px; + height: 69px; +} .icon_background_beehive { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_beehive.png'); width: 68px; @@ -2905,6 +2925,11 @@ width: 68px; height: 68px; } +.icon_background_mountain_waterfall { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_mountain_waterfall.png'); + width: 68px; + height: 68px; +} .icon_background_mystical_observatory { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_mystical_observatory.png'); width: 68px; @@ -3075,6 +3100,11 @@ width: 68px; height: 68px; } +.icon_background_sailboat_at_sunset { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_sailboat_at_sunset.png'); + width: 68px; + height: 68px; +} .icon_background_salt_lake { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/icon_background_salt_lake.png'); width: 68px; @@ -20415,6 +20445,11 @@ width: 68px; height: 68px; } +.shop_weapon_armoire_blueKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_blueKite.png'); + width: 68px; + height: 68px; +} .shop_weapon_armoire_blueLongbow { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_blueLongbow.png'); width: 68px; @@ -20515,6 +20550,11 @@ width: 68px; height: 68px; } +.shop_weapon_armoire_greenKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_greenKite.png'); + width: 68px; + height: 68px; +} .shop_weapon_armoire_guardiansCrook { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_guardiansCrook.png'); width: 68px; @@ -20630,11 +20670,21 @@ width: 68px; height: 68px; } +.shop_weapon_armoire_orangeKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_orangeKite.png'); + width: 68px; + height: 68px; +} .shop_weapon_armoire_paperCutter { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_paperCutter.png'); width: 68px; height: 68px; } +.shop_weapon_armoire_pinkKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_pinkKite.png'); + width: 68px; + height: 68px; +} .shop_weapon_armoire_pinkLongbow { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_pinkLongbow.png'); width: 68px; @@ -20775,6 +20825,11 @@ width: 68px; height: 68px; } +.shop_weapon_armoire_yellowKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_weapon_armoire_yellowKite.png'); + width: 68px; + height: 68px; +} .slim_armor_armoire_alchemistsRobe { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/slim_armor_armoire_alchemistsRobe.png'); width: 114px; @@ -21250,6 +21305,11 @@ width: 114px; height: 90px; } +.weapon_armoire_blueKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_blueKite.png'); + width: 114px; + height: 90px; +} .weapon_armoire_blueLongbow { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_blueLongbow.png'); width: 90px; @@ -21350,6 +21410,11 @@ width: 90px; height: 90px; } +.weapon_armoire_greenKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_greenKite.png'); + width: 114px; + height: 90px; +} .weapon_armoire_guardiansCrook { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_guardiansCrook.png'); width: 114px; @@ -21465,11 +21530,21 @@ width: 90px; height: 90px; } +.weapon_armoire_orangeKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_orangeKite.png'); + width: 114px; + height: 90px; +} .weapon_armoire_paperCutter { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_paperCutter.png'); width: 114px; height: 90px; } +.weapon_armoire_pinkKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_pinkKite.png'); + width: 114px; + height: 90px; +} .weapon_armoire_pinkLongbow { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_pinkLongbow.png'); width: 114px; @@ -21610,6 +21685,11 @@ width: 90px; height: 90px; } +.weapon_armoire_yellowKite { + background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/weapon_armoire_yellowKite.png'); + width: 114px; + height: 90px; +} .armor_special_bardRobes { background-image: url('https://habitica-assets.s3.amazonaws.com/mobileApp/images/armor_special_bardRobes.png'); width: 90px; diff --git a/website/client/src/assets/scss/typography.scss b/website/client/src/assets/scss/typography.scss index f4b3306baf..a15f2b7eae 100644 --- a/website/client/src/assets/scss/typography.scss +++ b/website/client/src/assets/scss/typography.scss @@ -18,8 +18,10 @@ body { // Restore the default styling for a elements without an href attribute // that was changed in bootstrap 4.5.1 a:not([href]), a:not([href]):hover { - color: inherit; - text-decoration: none; + &:not([role=button]) { + color: inherit; + text-decoration: none; + } } a, a:not([href]):not([tabindex]) { diff --git a/website/client/src/components/auth/registerLoginReset.vue b/website/client/src/components/auth/registerLoginReset.vue index eeecca89b1..60a08f3b70 100644 --- a/website/client/src/components/auth/registerLoginReset.vue +++ b/website/client/src/components/auth/registerLoginReset.vue @@ -614,6 +614,7 @@ import axios from 'axios'; import hello from 'hellojs'; import debounce from 'lodash/debounce'; import isEmail from 'validator/lib/isEmail'; +import DOMPurify from 'dompurify'; import { buildAppleAuthUrl } from '../../libs/auth'; import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants'; @@ -754,6 +755,12 @@ export default { } }); }, 500), + sanitizeRedirect (redirect) { + if (!redirect) return '/'; + let sanitizedString = DOMPurify.sanitize(redirect).replace(/\\|\/\/|\./g, ''); + sanitizedString = `/${sanitizedString}`; + return sanitizedString; + }, async register () { // @TODO do not use alert if (!this.email) { @@ -785,19 +792,14 @@ export default { passwordConfirm: this.passwordConfirm, }); - let redirectTo; - - if (this.$route.query.redirectTo) { - redirectTo = this.$route.query.redirectTo; - } else { - redirectTo = '/'; - } + const redirectTo = this.sanitizeRedirect(this.$route.query.redirectTo); // @TODO do not reload entire page // problem is that app.vue created hook should be called again // after user is logged in / just signed up // ALSO it's the only way to make sure language data // is reloaded and correct for the logged in user + // Same situation in login and socialAuth functions window.location.href = redirectTo; }, async login () { @@ -807,19 +809,8 @@ export default { password: this.password, }); - let redirectTo; + const redirectTo = this.sanitizeRedirect(this.$route.query.redirectTo); - if (this.$route.query.redirectTo) { - redirectTo = this.$route.query.redirectTo; - } else { - redirectTo = '/'; - } - - // @TODO do not reload entire page - // problem is that app.vue created hook should be called again - // after user is logged in / just signed up - // ALSO it's the only way to make sure language data - // is reloaded and correct for the logged in user window.location.href = redirectTo; }, // @TODO: Abstract hello in to action or lib @@ -842,19 +833,8 @@ export default { auth, }); - let redirectTo; + const redirectTo = this.sanitizeRedirect(this.$route.query.redirectTo); - if (this.$route.query.redirectTo) { - redirectTo = this.$route.query.redirectTo; - } else { - redirectTo = '/'; - } - - // @TODO do not reload entire page - // problem is that app.vue created hook should be called again - // after user is logged in / just signed up - // ALSO it's the only way to make sure language data - // is reloaded and correct for the logged in user window.location.href = redirectTo; } }, diff --git a/website/common/locales/en/achievements.json b/website/common/locales/en/achievements.json index 7e8bb8f59a..a88747b02c 100644 --- a/website/common/locales/en/achievements.json +++ b/website/common/locales/en/achievements.json @@ -132,5 +132,8 @@ "achievementBirdsOfAFeatherModalText": "You collected all the flying pets!", "achievementReptacularRumble": "Reptacular Rumble", "achievementReptacularRumbleText": "Has hatched all the standard colors of reptile pets: Alligator, Pterodactyl, Snake, Triceratops, Turtle, Tyrannosaurus Rex, and Velociraptor!", - "achievementReptacularRumbleModalText": "You collected all the reptile pets!" + "achievementReptacularRumbleModalText": "You collected all the reptile pets!", + "achievementGroupsBeta2022":"Interactive Beta Tester", + "achievementGroupsBeta2022Text":"You and your group provided invaluable feedback to help Habitica test.", + "achievementGroupsBeta2022ModalText":"You and your groups helped Habitica by testing and providing feedback!" } diff --git a/website/common/locales/en/backgrounds.json b/website/common/locales/en/backgrounds.json index 8218926222..d1d6106dd7 100644 --- a/website/common/locales/en/backgrounds.json +++ b/website/common/locales/en/backgrounds.json @@ -787,6 +787,14 @@ "backgroundEnchantedMusicRoomText": "Enchanted Music Room", "backgroundEnchantedMusicRoomNotes": "Play in an Enchanted Music Room.", + "backgrounds062022": "SET 97: Released June 2022", + "backgroundBeachWithDunesText": "Beach With Dunes", + "backgroundBeachWithDunesNotes": "Explore a beach with dunes.", + "backgroundMountainWaterfallText": "Mountain Waterfall", + "backgroundMountainWaterfallNotes": "Admire a mountain waterfall.", + "backgroundSailboatAtSunsetText": "Sailboat At Sunset", + "backgroundSailboatAtSunsetNotes": "Enjoy the beauty of a sailboat at sunset.", + "timeTravelBackgrounds": "Steampunk Backgrounds", "backgroundAirshipText": "Airship", "backgroundAirshipNotes": "Become a sky sailor on board your very own Airship.", diff --git a/website/common/locales/en/gear.json b/website/common/locales/en/gear.json index 4d49fa003b..52624b7cf1 100644 --- a/website/common/locales/en/gear.json +++ b/website/common/locales/en/gear.json @@ -630,6 +630,16 @@ "weaponArmoireGardenersWateringCanNotes": "You can’t get far without water! Have an infinite supply on hand with this magic, refilling watering can. Increases Intelligence by <%= int %>. Enchanted Armoire: Gardener Set (Item 4 of 4).", "weaponArmoireHuntingHornText": "Hunting Horn", "weaponArmoireHuntingHornNotes": "Twooooo! Twoo! Twoo! Gather your party for an adventure or quest by playing this horn. Increases Strength by <%= str %> and Intelligence by <%= int %>. Enchanted Armoire: Musical Instrument Set 1 (Item 1 of 3)", + "weaponArmoireBlueKiteText":"Blue Kite", + "weaponArmoireBlueKiteNotes":"Sailing high up in the blue, what tricks can you make your kite do? Increases all stats by <%= attrs %> each. Enchanted Armoire: Kite Set (Item 1 of 5)", + "weaponArmoireGreenKiteText":"Green Kite", + "weaponArmoireGreenKiteNotes":"A more stunning kite you’ve never seen, with its shades of yellow and green. Increases all stats by <%= attrs %> each. Enchanted Armoire: Kite Set (Item 2 of 5)", + "weaponArmoireOrangeKiteText":"Orange Kite", + "weaponArmoireOrangeKiteNotes":"With colors like sunrise and sunset, let’s see how high your kite can get! Increases all stats by <%= attrs %> each. Enchanted Armoire: Kite Set (Item 3 of 5)", + "weaponArmoirePinkKiteText":"Pink Kite", + "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)", "armor": "armor", "armorCapitalized": "Armor", diff --git a/website/common/script/content/achievements.js b/website/common/script/content/achievements.js index 705dec9f8a..cc7734b8a0 100644 --- a/website/common/script/content/achievements.js +++ b/website/common/script/content/achievements.js @@ -372,6 +372,11 @@ const specialAchievs = { titleKey: 'achievementKickstarter2019', textKey: 'achievementKickstarter2019Text', }, + groupsBeta2022: { + icon: 'achievement-groupsBeta2022', + titleKey: 'achievementGroupsBeta2022', + textKey: 'achievementGroupsBeta2022Text', + }, }; Object.assign(achievementsData, specialAchievs); diff --git a/website/common/script/content/appearance/backgrounds.js b/website/common/script/content/appearance/backgrounds.js index 20ac40b4b6..54d3a7b570 100644 --- a/website/common/script/content/appearance/backgrounds.js +++ b/website/common/script/content/appearance/backgrounds.js @@ -500,6 +500,11 @@ const backgrounds = { enchanted_music_room: { }, castle_gate: { }, }, + backgrounds062022: { + beach_with_dunes: { }, + mountain_waterfall: { }, + sailboat_at_sunset: { }, + }, timeTravelBackgrounds: { airship: { price: 1, diff --git a/website/common/script/content/gear/sets/armoire.js b/website/common/script/content/gear/sets/armoire.js index 08a92cf612..ff543ad7dd 100644 --- a/website/common/script/content/gear/sets/armoire.js +++ b/website/common/script/content/gear/sets/armoire.js @@ -1475,6 +1475,41 @@ const weapon = { int: 6, set: 'musicalInstrumentOne', }, + blueKite: { + str: 3, + con: 3, + int: 3, + per: 3, + set: 'kite', + }, + greenKite: { + str: 3, + con: 3, + int: 3, + per: 3, + set: 'kite', + }, + orangeKite: { + str: 3, + con: 3, + int: 3, + per: 3, + set: 'kite', + }, + pinkKite: { + str: 3, + con: 3, + int: 3, + per: 3, + set: 'kite', + }, + yellowKite: { + str: 3, + con: 3, + int: 3, + per: 3, + set: 'kite', + }, }; forEach({ diff --git a/website/common/script/libs/achievements.js b/website/common/script/libs/achievements.js index 882feddf50..3f7cf1e93f 100644 --- a/website/common/script/libs/achievements.js +++ b/website/common/script/libs/achievements.js @@ -344,6 +344,10 @@ function _getSpecialAchievements (user, language) { _addSimple(result, user, { path: 'kickstarter2019', language }); } + if (user.achievements.groupsBeta2022) { + _addSimple(result, user, { path: 'groupsBeta2022', language }); + } + return result; } diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index b2765a4051..9e9fd4fd5d 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -156,6 +156,7 @@ export default new Schema({ hatchedPet: Boolean, fedPet: Boolean, purchasedEquipment: Boolean, + groupsBeta2022: Boolean, }, backer: {