Compare commits

...

790 Commits

Author SHA1 Message Date
Sabe Jones
a636e15d11 4.23.0 2018-01-30 21:38:34 +00:00
Sabe Jones
3cc15e869e chore(i18n): update locales 2018-01-30 21:38:00 +00:00
SabreCat
88c8b92a68 chore(sprites): compile 2018-01-30 21:26:15 +00:00
SabreCat
cee4d7e87b feat(event): Habit Birthday 2018 2018-01-30 21:25:19 +00:00
Keith Holliday
1f1459b0d8 4.22.1 2018-01-27 11:31:59 -06:00
Keith Holliday
6489e74b6b Added tests for new username restrictions (#9900) 2018-01-27 10:30:17 -07:00
Alys
c1e264955f describe Login Name limitations on Registration form and Add Local Auth form (#9896)
* describe Login Name limitations on registration form and Add Local Auth form

* adjust text in response to this change: c69687f935

* update max length
2018-01-27 09:38:23 -07:00
Alys
f302d15bc4 add length and character limitations for login name (username) (#9895)
* update API comments to for `username` restrictions and to use Login Name terminology

We use "login name" rather than "username" in user-visible text
on the website and (usually) when communicating with users because
"username" could be confused with "profile name".
Using it in the docs allows you to search for that term.

* add alphanumeric and length validation for creating new login name (username)

The 'en-US' locale is specified explicitly to ensure we never use
another locale. The point of this change is to limit the character
set to prevent login names being used to send spam in the Welcome
emails, such as Chinese language spam we've had trouble with.

* add error messages for bad login names

* allow login name to also contain hyphens

This is because our automated tests generate user accounts using:
  let username = generateUUID();

* allow login names to be up to 36 characters long because we use UUIDs as login names in our tests

* revert back to using max 20 characters and only a-z, 0-9 for login name.

It's been decided to change the username generation in the tests instead.

* disable test that is failing because it's redundant

Spaces are now prohibited by other code.

We can probably delete this test later. I don't want to delete it
now, but instead give us time to think about that.

* fix typos

* revert to login name restrictions that allow us to keep using our existing test code

I'm really not comfortable changing our test suite in ways that
aren't essential, especially since we're working in a hurry with
a larger chance than normal of breaking things.
The 36 character length is larger than we initially decided but
not so much larger that it's a huge problem.
We can reduce it to 20 when we have more time.

* limit username length to 20 chars

* fix tests
2018-01-27 09:33:56 -07:00
Keith Holliday
8c70c8839b 4.22.0 2018-01-25 17:35:45 -06:00
Keith Holliday
3fcd04fd8a Updated encryption 2018-01-25 17:33:50 -06:00
Sabe Jones
d85f18751c chore(i18n): update locales 2018-01-25 23:26:24 +00:00
SabreCat
1390c4eae5 Merge branch 'develop' into release 2018-01-25 23:20:01 +00:00
Sabe Jones
18ade8ca65 Analytics: track challenge and task events (#9885)
* feat(analytics): track challenge and task events

* feat(analytics): add more challenge events
Also tweaks data for better troubleshooting utility

* refactor(analytics): include IDs for challenges/groups

* refactor(analytics): term for award challenge is "close"
2018-01-25 17:14:41 -06:00
Keith Holliday
7b026fa32c Added existence check for lastMessageText (#9886) 2018-01-25 13:46:49 -07:00
Keith Holliday
33698c219f Added existence check for lastMessageText (#9881) 2018-01-25 11:33:37 -07:00
Matteo Pagliazzi
b76d731cee fix gold icon alignment 2018-01-25 16:53:53 +01:00
Matteo Pagliazzi
4d1ac51543 upgrade datepicker 2018-01-25 15:13:21 +01:00
Matteo Pagliazzi
3818fbdd3e update boostrap and bootstrap-vue (no breaking changes) 2018-01-24 19:08:07 +01:00
Sabe Jones
af245b63d9 Merge branch 'release' into develop 2018-01-24 01:31:27 +00:00
Sabe Jones
028da1d6a9 4.21.0 2018-01-23 23:07:20 +00:00
Sabe Jones
49397244c4 chore(i18n): update locales 2018-01-23 23:07:05 +00:00
SabreCat
2b04ed3246 feat(content): Mystery Items 2018/01 2018-01-23 22:38:23 +00:00
Keith Holliday
51aebb540c Removed display of users personal checklist on challenge tasks (#9837) 2018-01-23 10:55:55 -07:00
Keith Holliday
f5d7777b2c Reloaded user data after cancelling subscription (#9836) 2018-01-23 10:28:44 -07:00
Keith Holliday
be1ffbd671 Removed promot to leader from challenge member modal (#9831) 2018-01-23 09:58:12 -07:00
Keith Holliday
5640139ef1 Awaited unlink so the UI refreshes removed tasks (#9815) 2018-01-23 09:36:46 -07:00
Keith Holliday
0959499450 Changed width to maxwidth (#9830) 2018-01-22 09:15:55 -07:00
Keith Holliday
90ffe587dd Added analytics to drop (#9792)
* Added analytics to drop

* Updated tracking category
2018-01-22 09:15:23 -07:00
Keith Holliday
38aafb6c7b Added clear completed todos (#9782) 2018-01-22 08:28:42 -07:00
Keith Holliday
ecfcf09184 Added more responsive styles to filters (#9820) 2018-01-22 08:20:34 -07:00
Keith Holliday
7083dc7e05 Added resync for completed todos (#9821) 2018-01-22 08:19:47 -07:00
Keith Holliday
d4e0417c48 Added challenge tag when challenge task is added to existing challenge (#9833) 2018-01-22 08:19:27 -07:00
Matteo Pagliazzi
ec7c25de9f add new notifications types 2018-01-22 11:46:51 +01:00
Matteo Pagliazzi
6f9db87843 Upgrade travis (#9850)
* upgrade travis

* upgrade travis
2018-01-21 22:47:58 +01:00
Matteo Pagliazzi
46c9038f54 Remove unused packages (#9849)
* remove unused packages

* update lock package

* try with paypal-rest-sdk 1.7.1

* setup nconf
2018-01-21 21:23:25 +01:00
Keith Holliday
1ce09aeb34 Added chatscroll check (#9814) 2018-01-20 17:00:54 -07:00
Keith Holliday
2ba327ef14 Fixed gem purchasing and error catching 2018-01-20 15:09:08 -06:00
Matteo Pagliazzi
de93b47493 add new notifications types 2018-01-20 17:28:50 +01:00
Sabe Jones
b0a21e116a 4.20.3 2018-01-19 03:59:29 +00:00
Sabe Jones
53d1a5f9dc chore(i18n): update locales 2018-01-19 03:58:59 +00:00
SabreCat
274f942b1e chore(news): Add note re audio themes 2018-01-18 21:45:15 +00:00
SabreCat
4aad52242c chore(news): iOS + Blog Bailey 2018-01-18 21:25:59 +00:00
Keith Holliday
166a48e139 Temporarily removed task allocation settings (#9822) 2018-01-18 14:16:30 -06:00
SabreCat
13de97dde6 fix(market): hide nav buttons when too few items
Fixes https://github.com/HabitRPG/habitica/issues/9802#issuecomment-358687806
2018-01-18 20:05:16 +00:00
Matteo Pagliazzi
6d8407ff94 fix tags selection in groups 2018-01-18 17:39:50 +01:00
Alys
663b794435 adjust Ram Barbarian equipment set to say "(Item .. of 3)"
"Item" had been missing.
2018-01-18 21:25:26 +10:00
Keith Holliday
c0276e3663 More staging fixes (#9816)
* Added ability to adjust challenge task copy's streak

* Disabled stat allocation if method is incorrect
2018-01-18 11:51:56 +01:00
Matteo Pagliazzi
6d57ce3050 fix typo 2018-01-18 11:41:19 +01:00
Matteo Pagliazzi
2159df785f Staging fixes (#9819)
* categories can be selected

* quick inventory fixes
2018-01-18 11:30:39 +01:00
Matteo Pagliazzi
9762258975 Merge branch 'develop' of github.com:HabitRPG/habitrpg into develop 2018-01-17 19:33:30 +01:00
Matteo Pagliazzi
deea64e839 more fixes for the task modal 2018-01-17 19:33:21 +01:00
Matteo Pagliazzi
9e615ba862 move delete task btn outside of advanced settings 2018-01-17 19:27:53 +01:00
SabreCat
d34beca3cc Merge branch 'release' into develop 2018-01-17 18:24:48 +00:00
Sabe Jones
07ed989862 4.20.2 2018-01-16 22:46:08 +00:00
Sabe Jones
049844ea7d chore(i18n): update locales 2018-01-16 22:35:00 +00:00
SabreCat
ff4c76165a fix(event): end New Year's cards 2018-01-16 22:09:24 +00:00
Keith Holliday
c3220e7c03 Pushed zindex of progress bar above modals (#9781)
* Pushed zindex of progress bar above modals

* Moved notifications above modals
2018-01-15 13:30:03 -07:00
Keith Holliday
cb4c6b3ca6 Added redirects for old url styles (#9780) 2018-01-15 12:43:02 -07:00
Keith Holliday
ba36ba0157 Added max width to profile image (#9783) 2018-01-15 12:34:00 -07:00
Keith Holliday
dd95acf436 Added coupon purchasing back to stripe (#9794) 2018-01-15 12:12:19 -07:00
Keith Holliday
a73b03452a Fixed overflow for member lists larger than 10 (#9777) 2018-01-15 10:32:32 -07:00
Keith Holliday
935fa1baae Fixed update query to revert challenge tags when broken (#9791) 2018-01-15 10:31:33 -07:00
Keith Holliday
745f930749 Added docs for mongo indexes (#9790) 2018-01-15 10:30:49 -07:00
Keith Holliday
d87db40c52 Staging fixes (#9804)
* Fixed party member loading

* Fixed quest details

* Fixed party creating

* Fixed challenge habit restore streak permissions

* Fixed fetch recent messages for party

* Adjusted category box placement for challenges

* Fixed zindex for input on group

* Changed reset streak restriction and allowed for adjust streak
2018-01-15 10:21:08 -07:00
Keith Holliday
0ea91016f8 Trimmed username spaces (#9793) 2018-01-15 10:20:36 -07:00
Keith Holliday
d4f634c3d8 Fixed disabling button and dismissing modals (#9805) 2018-01-15 09:16:15 -07:00
Matteo Pagliazzi
286566fc0c misc fixes for task modal 2018-01-13 11:27:02 +01:00
Cassidy Pignatello
2ed4df0b7c unhides scroll bar and adjusts overflow to remove whitespace at bottom (#9767) 2018-01-12 16:38:54 -06:00
Alys
9bb7c6ece0 allow user to restore streak on their copy of a challenge Daily (#9757) 2018-01-12 16:37:36 -06:00
Lula Villalobos
db0a6f6bb8 Fix Server Errors Appear As Blank Red Bar (#9747)
* fix path to error message

* changed error message path to check user auth error
2018-01-12 16:33:00 -06:00
Brad Lugo
b6305826be Update Vagrantfile.example to forward port 8080 (#9732)
There was a change in how the code is run locally and part
of this change included using port 8080. This change includes
the port forwarding for said port in the example file for Vagrant
2018-01-12 16:32:09 -06:00
Mel
f00ab86eff Update tier icons to correct size and color (#9727)
* update tier icons to correct size and color

* make tier text bold
2018-01-12 16:29:37 -06:00
Cassidy Pignatello
a44f29dad8 replaces btn-default with btn-secondary (#9704) 2018-01-12 16:25:02 -06:00
Pizilden
67b396bf16 Added Lunasol Theme (#9635) 2018-01-12 16:22:57 -06:00
Grayson Gilmore
ce14a9dadb Challenge modal optimization - remove unnecessary API call - partial fix for #9371 (#9546)
* Attempt to use party data from the store rather than always fetching it from the API

* Move init code to shown() to prevent unnecessary network requests

* Use store party data in getGroup action if possible to save an API call

* Use store data rather than API call for party in challengeModal; remove unnecessary code in guilds:getGroup action

* Create party:getParty action and employ it in Group and ChallengeModal

* Use store instead of action return for party data

* Change how party data is stored
2018-01-12 16:18:56 -06:00
MathWhiz
183c90ac3a Fix presskit (#9418)
* Move presskit to static folder

* Fix image location

* Add press kit FAQ strings

* Add faq section, fix images

* Update Images

* Add images to presskit page

* Remove unecessary parts
2018-01-12 16:18:20 -06:00
borisabramovich86
9e1a262f96 Fixes Purchase contributor equipment (fixes #9179) (#9306)
* Able to see all non class related items in market

* Fix lint errors

* Able to see all non class related items in market

* Fix lint errors

* add test for showing contributor gear

* Added previously owned items to test with eslint exception
2018-01-12 16:16:28 -06:00
MathWhiz
06dd9fe859 Add SpacePenguin's Theme (#9237)
* Add SpacePenguin's Theme

* Fix files
2018-01-12 16:15:55 -06:00
zags
2a2c525c2d Add support for multiDaysCountAsOneDay == false to evasion (#9077)
* Add support for `multiDaysCountAsOneDay == false` to evasion

`if (dailiesDaysMissed > 1) dailiesDaysMissed = 1;` causes the evasion for-loop to only evaluate once even if `multiDaysCountAsOneDay == false`.  This statement isn't necessary because `if (multiDaysCountAsOneDay) break;` will cause the for-loop to evaluate only once if `multiDaysCountAsOneDay == true`.  Removing this statement makes the `dailiesDaysMissed` variable unnecessary.

* Moves break statement out of conditional
2018-01-12 16:12:19 -06:00
Sabe Jones
b2c1c9d9dc Merge branch 'release' into develop 2018-01-12 21:54:20 +00:00
Sabe Jones
c33eba6736 4.20.1 2018-01-12 21:47:21 +00:00
Sabe Jones
56434cce71 chore(i18n): update locales 2018-01-12 21:46:32 +00:00
SabreCat
c41123c36c chore(news): Blog Bailey 2018-01-12 21:38:16 +00:00
SabreCat
043a6cd4ba chore(shops): update Featured Items 2018-01-12 21:25:09 +00:00
SabreCat
0ca2f9034f Revert "WIP: Buy-1-Get-1 Gift Subs (#9719)"
This reverts commit dc3d694d0e, with the exception of locale strings that need not be purged.
2018-01-12 21:15:42 +00:00
Keith Holliday
4c7157807b Synced isdue/next due when user joins challenge (#9779) 2018-01-12 10:16:51 -06:00
Keith Holliday
0afe797bae Fixed display when quest has no completion function (#9778) 2018-01-12 10:16:21 -06:00
Alys
1c8797e473 change Attribute to Stat and upper-case Stat Points and a couple of other terms 2018-01-12 08:09:31 +10:00
Keith Holliday
e0bf6d2e55 Reverted group flag code (#9784)
* Reverted group flag code

* Reverted all flagging code

* Added hyphens back
2018-01-11 12:04:07 -06:00
Matteo Pagliazzi
e96d0659cb fix typos in migration to convert field for Apple subscribers 2018-01-10 18:19:32 +01:00
Matteo Pagliazzi
72d70236ea fix typos in migration to convert field for Apple subscribers 2018-01-10 18:10:01 +01:00
Matteo Pagliazzi
ee2fc8c763 add migration to convert field for Apple subscribers 2018-01-10 18:08:32 +01:00
Sabe Jones
b53c03bca8 Merge branch 'release' into develop 2018-01-10 16:42:13 +00:00
Sabe Jones
9545f692ef 4.20.0 2018-01-10 16:41:44 +00:00
Sabe Jones
0112bd9b5a chore(i18n): update locales 2018-01-10 16:40:59 +00:00
SabreCat
d235576e18 chore(sprites): compile 2018-01-10 16:32:34 +00:00
SabreCat
3d5d5da933 feat(content): Pept quest 2018-01-10 16:31:56 +00:00
Alys
9b19477e2f adjust wording for Keys to Kennels
Also moves the new Keys text strings to the same place as the old ones in the locales file.
Also fixes minor typos in code that was preventing the new text from being displayed.
2018-01-10 21:21:58 +10:00
Sabe Jones
5a9c95f07e fix(guilds): include exact breakpoints 2018-01-09 18:29:59 -06:00
Keith Holliday
3000e2b72c Removed keys and inbox flagging (#9776) 2018-01-09 12:58:01 -06:00
Matteo Pagliazzi
c1f6f0398e fix positioning of checkmarks for checklist items 2018-01-09 11:31:02 +01:00
Matteo Pagliazzi
cb6488fa05 fix path for KeysToKennel module 2018-01-09 11:29:12 +01:00
Keith Holliday
126d90f471 Added keys to the kennel (#9772)
* Added keys to the kennel

* Added titles and descriptions
2018-01-08 13:15:19 -06:00
Keith Holliday
98d4fb0f34 Chat flag inbox (#9761)
* Refactored chat reporting

* Added inbox flag chat

* Added flag chat to inbox

* Added you have flagged message
2018-01-08 13:13:25 -06:00
Keith Holliday
d3ee3ca53d Updated plan updated date if user has cancelled (#9773)
* Updated plan updated date if user has cancelled

* Added test for plan with only date updated
2018-01-08 12:50:15 -06:00
Matteo Pagliazzi
7eac5cebf5 fix typo 2018-01-08 18:47:59 +01:00
Matteo Pagliazzi
6a109adbc5 Task Modal Improvements (#9560)
* start adding advanced options

* new imput

* partial colors

* update deps

* misc adds

* fix text color

* add advanced options

* initial reordering of task modal labels

* start to fix colors in the modal

* wip colors

* update package-lock.json

* fix merge

* finish modal

* refactor colors

* fix quick add

* fix colors

* new icon colors

* add markdown formatting help

* fix habits colors

* fix rewards colors

* fixed remaining colors

* start to inline inputs

* fix typ

* fixes

* habits hover state

* wip new task modal inputs

* bootstrap: upgrade to v4-beta3

* finish reward edit modal

* fix attributes allocation, checklists and add help tooltips for attributes and difficulty

* lots of fixes

* update datepicker

* misc fixes
2018-01-08 18:43:57 +01:00
Sabe Jones
587847f5e9 4.19.1 2018-01-08 17:23:28 +00:00
Sabe Jones
7842cd8a41 chore(i18n): update locales 2018-01-08 17:18:12 +00:00
SabreCat
2f9cf02932 fix(sprites): rename 2018/01 background icons 2018-01-08 17:11:20 +00:00
Pizilden
daa796454c Added Farvoid Theme (new, clean branch) (#9634)
* Added Farvoid Theme

* Updated generic.json and index.js

* fix(merge): address conflicts
2018-01-05 14:34:19 -06:00
Cassidy Pignatello
c531239618 Fixes equipment sorting by name in inventory (#9692)
* returns items sorted in ascending order when sorted by name

* returns items sorted in ascending order when sorted by name
2018-01-05 13:51:47 -06:00
Matti Petrelius
f6ac7b890a Both eggs and potions should hatch pets from Items (#9645)
* Show click on hatching potion when egg is clicked

* Fix translation parameter name

* Show egg as active when clicked

* Remove itemDragStart event handler from egg items

* Change isHatchable to take in potion and egg objects

* Add margin to item popover content
2018-01-05 13:31:34 -06:00
kartik adur
229e39facf Other user checkins: user profile modal checkin count (#9646)
* add loginIncentives to public fields to show non-loggedin user checkins

* update integration tests for update in api response
2018-01-05 13:29:38 -06:00
Allison Virgil
75b00ce2df Fixed Challenge Winner Text (partial fix for #9629) (#9664)
* Fixed Challenge Winner Text (#9629)

* Fixing typo
2018-01-05 13:29:00 -06:00
Julius Jung
4576353f26 inital commit to add confirmation to animalEars (#9666) 2018-01-05 13:26:45 -06:00
Allister
acf4b4da63 Change focused state of introjs button (#9677) 2018-01-05 13:24:32 -06:00
josteins1
8b5933177a Given back header the priority over snackbar with z-index value and a padding to avoid collision. (#9687)
* increased top padding to match main header

referring to issue 9678
https://github.com/HabitRPG/habitica/issues/9678

* adjusted the z value to appropriate levels

z-index adjusted from 99999 to 999
2018-01-05 13:23:03 -06:00
Julius Jung
a6ddd6d233 fix drawer-slider carousel function && convert to pointer logic (fixes #9366 AND #9638) (#9711)
* fix drawer-slider carousel function && convert to pointer logic

* refactor conditional logic

* get rid of absolute value
2018-01-05 13:20:21 -06:00
Sabe Jones
5ca5adc774 4.19.0 2018-01-04 21:49:53 +00:00
Sabe Jones
005ffe850a chore(i18n): update locales 2018-01-04 21:44:47 +00:00
SabreCat
71cb4e8510 feat(content): enable Wintery Skins and Hair 2018-01-04 21:35:26 +00:00
SabreCat
40244ab81b Merge branch 'develop' into release 2018-01-04 21:11:44 +00:00
Keith Holliday
15b65b342a Removed extra achievement sound (#9763) 2018-01-04 10:17:33 -06:00
Matteo Pagliazzi
7df3aba71b make search case insensitive in equipment and inventory pages (#9762) 2018-01-04 13:06:15 +01:00
Keith Holliday
6bb535c129 Reset chat options when change guild routes (#9743) 2018-01-02 22:56:31 -07:00
Sabe Jones
e3bf3d29f7 4.18.1 2018-01-03 00:54:23 +00:00
SabreCat
df9c42c1b5 fix(backgrounds): add 2018 group 2018-01-03 00:52:54 +00:00
Sabe Jones
7e241bb76f Merge branch 'release' into develop 2018-01-03 00:07:42 +00:00
Sabe Jones
17fb681671 4.18.0 2018-01-03 00:05:22 +00:00
Sabe Jones
0069aee5b0 chore(i18n): update locales 2018-01-03 00:00:07 +00:00
SabreCat
240dd1b965 chore(sprites): compile 2018-01-02 23:52:35 +00:00
SabreCat
88e6b2da7c feat(content): Armoire and BGs 2018-01
Also ends New Year's hat fanciness
2018-01-02 23:49:50 +00:00
Sabe Jones
6e7f4a231d chore(sprites): compile 2018-01-02 21:41:37 +00:00
Sabe Jones
822a0e56af feat(notifications): recanvas sprites for notifs 2018-01-02 21:38:34 +00:00
Sabe Jones
da73c5c418 Merge branch 'release' into develop 2017-12-31 02:45:04 +00:00
Sabe Jones
2cf8439bd1 4.17.0 2017-12-31 02:44:43 +00:00
Sabe Jones
0e404ad6ba chore(i18n): update locales 2017-12-31 02:43:40 +00:00
SabreCat
b9f709ab30 chore(sprites): compile 2017-12-31 02:35:50 +00:00
SabreCat
d57c525fab feat(event): New Year's 2017-18 2017-12-31 02:34:52 +00:00
Alys
9a3a104ba4 fix typo in apidocs comment block 2017-12-30 08:43:11 +10:00
Keith Holliday
63bba13b5f Changed paypal redirect to subscription page (#9742) 2017-12-26 17:31:23 -06:00
Keith Holliday
d90d781740 Added mobile style fixes (#9741) 2017-12-26 17:31:00 -06:00
Alys
a3bf329c44 make reset password apidocs comment more accurate 2017-12-24 09:23:32 +00:00
Sabe Jones
22a12e37fa 4.16.2 2017-12-22 21:58:16 +00:00
SabreCat
446e0422c7 Merge branch 'release' into develop 2017-12-22 21:57:43 +00:00
SabreCat
5220cc1bf3 fix(market): add broken Armoire items 2017-12-22 21:56:39 +00:00
Sabe Jones
e8976b40f4 Merge branch 'release' into develop 2017-12-22 21:42:02 +00:00
Sabe Jones
3b4b459e68 4.16.1 2017-12-22 21:41:37 +00:00
Sabe Jones
bbbdd89ade chore(i18n): update locales 2017-12-22 21:41:18 +00:00
Sabe Jones
a20c1ba751 Include seasonal gear in Market class lists (#9739)
* fix(shops): include seasonal gear in Market class lists

* fix(market): display non-seasonal broken special items
Also fixes a bug where if a current seasonal item was broken, it would show up twice.
2017-12-22 15:29:51 -06:00
Alys
d725b5be19 fix typo in loading screen tip 8 2017-12-22 20:45:35 +00:00
Keith Holliday
545b052c10 Added fix for quantity confirmation (#9735) 2017-12-22 10:23:55 -06:00
SabreCat
028b9d569d Merge branch 'release' into develop 2017-12-21 23:20:51 +00:00
Sabe Jones
85b861c4a9 4.16.0 2017-12-21 23:20:00 +00:00
Sabe Jones
762e87a82a chore(i18n): update locales 2017-12-21 23:19:35 +00:00
SabreCat
b68e69e1a1 chore(sprites): compile 2017-12-21 23:12:06 +00:00
SabreCat
4764f115b1 feat(content): Subscriber Items Dec 2017 2017-12-21 23:10:54 +00:00
Keith Holliday
95c99295c1 Removed gold locally when user buys a card (#9736) 2017-12-21 10:27:09 -06:00
Keith Holliday
a7617fa947 Added broken megaphone icon (#9737) 2017-12-21 10:25:54 -06:00
Sabe Jones
2da2a47f32 4.15.3 2017-12-20 19:28:52 +00:00
Sabe Jones
8f744565e2 chore(i18n): update locales 2017-12-20 19:28:22 +00:00
SabreCat
714512b0a3 fix(promo): send payment method with promo 2017-12-20 18:41:19 +00:00
SabreCat
9538c86d02 fix(seasonal): include current season gear in Shop 2017-12-20 18:31:10 +00:00
Keith Holliday
afc1ffd90b Refactored duplicate card section (#9730)
* Refactored duplicate card section

* Updated to new computed methods
2017-12-20 12:27:21 -06:00
Matteo Pagliazzi
6988875e8a fix promo gifting 2017-12-20 19:09:15 +01:00
Keith Holliday
6e0b6171c6 Many ie style fixes (#9728) 2017-12-20 10:33:21 -06:00
Sabe Jones
53bbd93d80 4.15.2 2017-12-20 04:44:07 +00:00
Sabe Jones
75092336c4 fix(news): Holly, not Peppermint, potions 2017-12-20 04:43:43 +00:00
Sabe Jones
310bdf8cb5 4.15.1 2017-12-20 02:43:46 +00:00
Sabe Jones
9435a3089a chore(i18n): update locales 2017-12-20 02:43:00 +00:00
SabreCat
bb6dac2e84 4.15.0 2017-12-20 02:27:56 +00:00
SabreCat
acf34e2344 chore(sprites): compile 2017-12-20 02:27:43 +00:00
SabreCat
1aac4c713d feat(event): Winter Wonderland sprites (2/2) 2017-12-20 02:27:13 +00:00
SabreCat
bb527caa06 feat(content): Winter Wonderland sprites (1/2) 2017-12-20 02:26:21 +00:00
SabreCat
98bb6fd7ce feat(content): Winter Wonderland 2017-18
and Starry Night Hatching Potions
2017-12-20 02:23:11 +00:00
Keith Holliday
b8c716ff82 Fixed pending damage display and nav size (#9723) 2017-12-18 12:31:16 -06:00
Sabe Jones
9830fce760 4.14.2 2017-12-15 19:26:15 +00:00
Sabe Jones
7fccf59f50 Merge branch 'release' into develop 2017-12-15 15:54:09 +00:00
Sabe Jones
dd79f2be60 4.14.1 2017-12-15 15:02:22 +00:00
Sabe Jones
fbdcd4b0a3 chore(i18n): update locales 2017-12-15 15:01:52 +00:00
SabreCat
e229bc5042 Merge branch 'release' into develop 2017-12-15 05:37:04 +00:00
SabreCat
44c7e8c9dc 4.14.0 2017-12-15 05:36:01 +00:00
SabreCat
c4ffe39ec9 chore(news): Bailey 2017-12-15 05:34:30 +00:00
Sabe Jones
dc3d694d0e WIP: Buy-1-Get-1 Gift Subs (#9719)
* feat(promo): Buy-1-Get-1 Gift Subs

* feat(promo): add explanatory text to subscription screens
Also adds some add'l test coverage and creates a test context for this event
2017-12-14 23:09:02 -06:00
Sabe Jones
4f0ce77205 Winter Quest Bundle (#9718)
* feat(content): Winter Quest Bundle

* fix(sprites): revert spritesheet changes
2017-12-14 22:40:42 -06:00
Keith Holliday
c28ec24c33 Added notification for when leader is updated (#9674)
* Added notification for when leader is updated

* Abstracted challenge member search component

* Added challenge member search modal to challenge detail

* Added group search
2017-12-14 12:12:43 -06:00
Keith Holliday
54db84fddc Payment tests refactor (#9695)
* Reorganized files, reduced function size, reduced duplication

* Refactored amazon tests organization

* Reduced duplication

* Reorganized paypal tests

* Reorganized stripe tests

* Fixed lint issues

* Fixed gem purchase expectations

* Added cloning so we don't modify the common block
2017-12-14 09:59:45 -06:00
Keith Holliday
e7fd2b4c79 [WIP] Added initial inapp loading screen with tips (#9710)
* Added initial inapp loading screen with tips

* Added new tips and styles

* Removed unrelated readme
2017-12-14 09:35:10 -06:00
Keith Holliday
05640f513e Added test to recreate early cron issue (#9668)
* Added test to recreate early cron issue

* Gave user extra time based on reverse timezone change
2017-12-14 09:09:11 -06:00
SabreCat
b0ebdfeb65 Merge branch 'release' into develop 2017-12-13 22:16:30 +00:00
SabreCat
6c01db8d81 4.13.4 2017-12-13 22:15:11 +00:00
SabreCat
5a3751cbac chore(news): Blog Bailey 2017-12-13 22:14:19 +00:00
Keith Holliday
7802e30e80 Added static/front and meta tags (#9712)
* Added static/front and meta tags

* Moved meta to index
2017-12-13 15:11:22 -06:00
Matteo Pagliazzi
899452279b fix ie 11 rendering (#9713) 2017-12-13 13:51:04 -06:00
Keith Holliday
566716e2fe Added display logic for npcs (#9709)
* Added display logic for npcs

* Fixed lint
2017-12-12 19:05:18 -06:00
SabreCat
2a42bc9450 Merge branch 'release' into develop 2017-12-12 21:27:04 +00:00
SabreCat
1ef62d1b66 4.13.3 2017-12-12 21:25:39 +00:00
SabreCat
355773ecf3 chore(news): FCC CTA
Also fixes an issue with sprite alignment of the Lamplighter Set.
2017-12-12 21:24:18 +00:00
Keith Holliday
2bb5751f33 Added float rounding (#9657)
* Added float rounding

* Changed to isNaN
2017-12-11 11:48:50 -06:00
Keith Holliday
2570c59130 Ensured admin can always PM user (#9653)
* Ensured admin can always PM user

* Fixed lint issues

* Updated admin check and removed async

* Removed console log
2017-12-11 11:48:17 -06:00
Keith Holliday
2dfcda068b Added streak to export of challenge tasks (#9625)
* Added streak to export of challenge tasks

* Fixed tests
2017-12-11 11:39:43 -06:00
Keith Holliday
507133c76e Added client side logging (#9643) 2017-12-11 11:07:16 -06:00
Keith Holliday
a7c115877f Added query option to limit query fields (#9642)
* Added query option to limit query fields

* Removed only
2017-12-11 10:24:19 -06:00
Keith Holliday
1750a0c2e6 Updated responsive styles (#9696)
* Updated responsive styles

* Font adjustments

* Changed to max height
2017-12-09 23:23:16 -06:00
Keith Holliday
759ce61492 Added support for party invites by email (#9665)
* Added support for party invites by email

* Changed to groupInvite
2017-12-07 12:57:01 -05:00
Keith Holliday
57193bd5f3 Ensured quest drops are only from incomplete progress (#9671)
* Ensured quest drops are only from incomplete progress

* Fixed spelling error
2017-12-07 12:33:40 -05:00
Keith Holliday
e1a1b4eab6 Added body param for remove message (#9669)
* Added body param for remove message

* Removed console.log
2017-12-07 11:52:28 -05:00
Keith Holliday
350894f985 Added achievement restore migration (#9641)
* Added achievement restore migration

* Updated checks
2017-12-07 10:18:16 -05:00
Keith Holliday
0184d774c2 Disabled challenge button when loading (#9686) 2017-12-06 20:16:40 -05:00
Sabe Jones
d136162d48 4.13.2 2017-12-06 19:16:35 +00:00
SabreCat
2be8ddb60d fix(news): winner typo 2017-12-06 19:15:34 +00:00
Keith Holliday
3c67f91525 Quest refactor (#9681)
* Separated out quest sidebar component

* Added accepted count
2017-12-06 11:13:44 -05:00
Keith Holliday
c02aadfac4 Made user pay for amoire locally (#9673) 2017-12-06 10:50:15 -05:00
Julius Jung
2f956252ab Don't show shield when previewing two-handed weapon (fixes #9495) (#9676)
* initial commit to not show shield when previewing two-hand weapon

* revert error made

* update to fix all combinations of equipping / trying two-handed weapons

* clarify conditional logic

* refactor to let avatar check for twoHanded display/hide logic

* add case when avatar doesn't have weapon equipped
2017-12-05 17:05:59 -06:00
Andrew Bustos
341f16cc82 Fixed block icon not showing in profile modal. (#9667)
* Fixed block and add icon not showing and the ordering of the buttons. Also fixed and added tooltips for buttons.

* Changed unnecessary change of color in svg icon files and changed tooltip attribute to not be empty
2017-12-05 15:01:18 -06:00
Julius Jung
ec179182e7 submit initial working solution to disallow selling of Saddle (#9663) 2017-12-05 14:22:59 -06:00
Pizilden
b886d7bb33 Added MAFL Theme (#9633) 2017-12-05 14:20:58 -06:00
Pizilden
a8f8f4f544 Added Pizilden's Theme (#9632) 2017-12-05 14:14:45 -06:00
Feywood
4047bf6943 Rewards permit decimal value. Fixes https://github.com/HabitRPG/habitica/issues/9513 (#9617)
* testing additional event trigger for sendMessage

* moved keyup event to newmessage

* added keyup event to tavern vue too

* removed number.toFixed, changed to placeholder and step
2017-12-05 14:13:54 -06:00
Julius Jung
a5a985fd00 Don't show shield when previewing two-handed weapon (fixes #9495) (#9607)
* initial commit to not show shield when previewing two-hand weapon

* revert error made

* update to fix all combinations of equipping / trying two-handed weapons

* clarify conditional logic

* refactor to let avatar check for twoHanded display/hide logic
2017-12-05 14:11:19 -06:00
Alys
444d6889de add subscription cancellation instructions for free Group Plans subscriptions (#9606) 2017-12-05 14:10:15 -06:00
negue
c56c69d464 market fixes 28th nov (#9593)
* list special gear by the `specialClass` - fixes #9485

* only disable the currencly label + value not the amount input - fixes #9492

* disable transformations on equipment previews - fixes #9497

* show boss strength - fixes #9522

* pin time travelers animals - closes #9382

* clean up + package-lock ?

* fix quest info
2017-12-05 14:09:34 -06:00
Kip Raske
4b610ba3f1 Fixing the pig flying too high in the stable square bug (#9592)
The `.FlyingPig` css class necessary to re-center the pig in its square
is no longer applied when the square is greyed out. So I am adding that
to the greyed out square. It seems to not have any affect on the other
pets.
2017-12-05 14:06:35 -06:00
Feywood
65e3b599e6 Fix for sending chat with ctrl enter for windows chrome/firefox. Fixes https://github.com/HabitRPG/habitica/issues/9380 (#9588)
* testing additional event trigger for sendMessage

* moved keyup event to newmessage

* added keyup event to tavern vue too

* removed obsolete check from _updateCarretPosition

* fixed lint issue
2017-12-05 14:05:01 -06:00
aalsehly86
7caf211bec Fix issue #9534 - changed the character-name (#9547)
* Fix issue #9534 - changed the character-name from $white to $header-dark-background

* changes to #9534 - changed character-name color from -dark-background to -color.

* character-name color is back to  #9534

* changed character-name color to -200

* Changed colors for character name and level details #9534
2017-12-05 14:02:29 -06:00
Julius Jung
d4bc7c77a9 Check previous gear owned before purchasing next level gear (fixes #9071) (#9466)
* add another check if previous gear is owned

* respect gear purchase order

* catch error with miscalculation of equipment number floor

* add integration test for proper equipment purchasing order

* fix syntax

* add 'previousGearNotOwned' string

* rewrite logic for different starting levels for wep vs others

* separate and add tests for armor and weapon

* rename variable for clarification

* skip check if itemIndex is NaN

* change obscure NaN check for readability

* change conditional from checking NaN to Int
2017-12-05 13:58:12 -06:00
Tyler Nychka
bfaa7c0fea Validate that everyX values in dailies are integers bounded by 0 and 9999 fixes #8782 (#9268)
* Validate that everyX values are integers bounded by 0 and 9999

* Added client side check

* Updated tests

* Added migration for bad dailies

Near idential to the other task migration.

* fix(typo): camelCase function call
2017-12-05 13:55:32 -06:00
Sabe Jones
8367de34bf Merge branch 'release' into develop 2017-12-05 19:47:52 +00:00
Sabe Jones
ea6b78b7ca 4.13.1 2017-12-05 19:47:26 +00:00
Sabe Jones
401067bfed chore(i18n): update locales 2017-12-05 19:46:40 +00:00
Sabe Jones
b457daa616 Merge branch 'release' into develop 2017-12-05 19:29:34 +00:00
Sabe Jones
54c2441934 4.13.0 2017-12-05 19:29:06 +00:00
Sabe Jones
9e8807c40d Armoire and Backgrounds December 2017 (#9659)
* feat(content): Armoire and Backgrounds 2017/12

* chore(sprites): compile

* chore(news): Bailey
2017-12-05 19:29:00 +00:00
Sabe Jones
9bfbeaf93e Armoire and Backgrounds December 2017 (#9659)
* feat(content): Armoire and Backgrounds 2017/12

* chore(sprites): compile

* chore(news): Bailey
2017-12-05 13:25:52 -06:00
Keith Holliday
860efefdb2 Removed client side armoire call (#9660) 2017-12-05 12:23:31 -06:00
Keith Holliday
6310482b9d Added popovers to quests (#9655) 2017-12-05 10:57:28 -05:00
Keith Holliday
2d4928cd2b Removed challenge access restriction (#9652) 2017-12-04 15:57:38 -05:00
SabreCat
f3c2c0f901 Merge branch 'release' into develop 2017-12-04 19:23:55 +00:00
Keith Holliday
1fc84c2357 Added calculated property (#9637) 2017-12-04 11:24:04 -06:00
Keith Holliday
13cdcedcba Added support for scoring group tasks down after approval (#9623)
* Added support for scoring group tasks down after approval

* Fixed lint issues
2017-12-04 11:23:47 -06:00
Matteo Pagliazzi
72f0b8ed7c send correct email when user is removed from group plan 2017-12-04 12:04:07 +01:00
thehollidayinn
95f9479d7a 4.12.6 2017-12-02 08:37:36 -06:00
Keith Holliday
af095d8450 Revert query optimization (#9636) 2017-12-02 08:35:31 -06:00
Sabe Jones
470495387c 4.12.5 2017-12-02 03:28:44 +00:00
Keith Holliday
bdef1ca23c Fixed max width none (#9631) 2017-12-01 21:26:52 -06:00
SabreCat
1835804e86 4.12.4 2017-12-01 21:33:06 +00:00
SabreCat
cb58994bdf Merge branch 'release' into develop 2017-12-01 21:32:28 +00:00
SabreCat
44f3b73183 fix(avatar): layer base/bangs correctly 2017-12-01 21:10:33 +00:00
Luan Muniz
7e23fdc22a Fix install node permissions (#9621)
Signed-off-by: Luan <luan@luanmuniz.com.br>
2017-12-01 16:05:17 +00:00
Keith Holliday
e138d2b67b Added needs cron check to achievements (#9624) 2017-12-01 09:54:43 -06:00
Keith Holliday
3e3248fecb Changed row adding when user blurs/focuses (#9610) 2017-12-01 08:37:37 -06:00
Keith Holliday
78ee60611a Remove cancel button when clicked (#9616) 2017-12-01 08:03:54 -06:00
Keith Holliday
3c7aaa605b Fixed text being cut off (#9612) 2017-12-01 08:03:36 -06:00
Keith Holliday
00343da266 Added max width seetings to screens larger than 1300 (#9609) 2017-12-01 07:47:45 -06:00
Sabe Jones
56d09411d9 Merge branch 'release' into develop 2017-12-01 00:31:23 +00:00
Sabe Jones
ae0df2242a 4.12.3 2017-12-01 00:30:49 +00:00
Sabe Jones
5b06b28c97 chore(event): end Thunderstorm Potions 2017-12-01 00:30:06 +00:00
Keith Holliday
c6a3bfb291 Added exp reset when changing level (#9611) 2017-11-30 15:45:26 -06:00
Keith Holliday
7797794cd5 Deselect a tag if it is selected when removing (#9614) 2017-11-30 15:45:14 -06:00
Keith Holliday
d9e09a5f3d Fixed streak bonus style (#9608) 2017-11-30 12:37:53 -06:00
Keith Holliday
4e73c8513e Hide progress if user is not on quest (#9597) 2017-11-30 12:37:31 -06:00
Keith Holliday
9421fd7ced Added analytics to backgrounds (#9615) 2017-11-30 12:10:49 -06:00
Keith Holliday
699de64328 Added more fields to scoring (#9613) 2017-11-30 10:09:04 -06:00
Keith Holliday
6f9cbf9ca1 Only update the user when editing profile (#9601) 2017-11-30 08:19:03 -06:00
Keith Holliday
a097819b72 User auth performance improvements (#9589)
* Added initial user projecting in auth and fixed projection for get user tasks

* Added fields to score route

* Added another field to get tasks

* Added group fields to user
2017-11-30 08:17:28 -06:00
Keith Holliday
77f71b5415 Fixed saving in progress tag when clicking save (#9598) 2017-11-30 08:16:54 -06:00
Keith Holliday
ced3621dea Fixed leaving from guild list item (#9599) 2017-11-30 08:16:00 -06:00
Keith Holliday
e321d85b3c Donate buy modal fix (#9604)
* Added donate back and buy modal

* Fixed login check

* Added ability to remove mustache
2017-11-30 08:15:28 -06:00
SabreCat
d72b40d5b0 Merge branch 'release' into develop 2017-11-29 05:07:42 +00:00
SabreCat
54443a2980 4.12.2 2017-11-29 05:04:46 +00:00
SabreCat
00dc990974 chore(event): end Thanksgiving, add Bailey 2017-11-29 05:03:01 +00:00
Keith Holliday
3737aa045d Fixed text when cloning (#9594) 2017-11-28 19:18:04 -06:00
Keith Holliday
b03ddf6f7d Added fix for task order using / for arrays (#9590) 2017-11-28 14:57:08 -06:00
tim1234ltp
4ab89fd3e0 Bug fixes on Subscription termination date format [Fixes Issues #9186] (#9583)
* Fixed date.

* Got rid of the filter and returned moment.

* fix the return value

* Stupid typo.
2017-11-28 09:19:29 +01:00
negue
f1e200c0f5 autofix pinned seasonal gear - fixes #9448 (#9570)
* auto-remove officialPinned item from userPinned-array on pinning

* hide event limited message if an item was already owned by the user
2017-11-28 09:11:40 +01:00
Ryan Holinshead
218664dfcc (ISSUE-9353) Fix pinned item alignment (#9358)
* (ISSUE-9353) Fix pinned item alignment
- Get rid of justify-content: space-between to allow flex-start default to be used
- Add margin to direct children (item-wrappers)

* Issue-9353: Change selector to & > div instead of just > for more explicit selection of direct child divs

* Fix rewards item spacing to match Zeplin mockups
- Make sure horizontal/vertical spacing between items is 16px
- Add use of grid if supported, else use flex
2017-11-27 20:54:03 -06:00
Trevor Ford
a0f29e970d fix Stable sidebar width and center inventory drawer (fixes #9263) (#9419)
* fix Stable sidebar width and center inventory drawer (fixes #9263)

* hide all .standard-sidebars on small/mobile devices
2017-11-27 20:48:52 -06:00
MathWhiz
200cd66d66 Use config when starting development server (#9410)
* Use config when starting development server

* import nconf setup from website

* Add comment explaining choice

* Fix lint issues
2017-11-27 20:38:27 -06:00
MathWhiz
dd05a8d608 Contributor Title tooltip (#9413)
* Remove usage of cachedProfileData when determining contributor level

* Add tooltip

* Remove directive import

* /s/msg.contributor.title/msg.contributor.text

* move tooltip placement

* update tooltip position
2017-11-27 20:37:36 -06:00
Asif Mallik
299e88233c Fixes multiple complete and uncomplete for todos and daily (Fixes #8669) (#8971)
* Fixed bug that allows users to complete todo and daily multiple times

* Added tests

* Fix syntax

* Fix existing tests that rely on multiple complete or uncomplete

* Undoes removal of website/client/README.md

* Change sessionOutdated string to reflect separate client needs

* Fix should update history test by changing lastCron
2017-11-27 20:13:18 -06:00
Ryan Holinshead
26bde1f766 Character Create Modal - Prevent Options From Jumping When Selected (#9252)
* Prevent options from jumping when selected due to border being added/removed based on active option. Instead, always have a border on the option but set its color when active

* Use gray instead of white border in order to match background so that it isn't visible while unselected. Add margin-bottom back

* Make sure the locked option style remains unchanged
- Nest .locked in .customize-options .option to get specificity
- Override border, border-radius, and margin-top for .locked
- Set the override values to what would be applied without other style changes
2017-11-27 20:11:15 -06:00
Garrett Scott
d95836b881 Translator minor changes fixes #8917 (#9297)
* Updated userItemsNotEnough string

* Added a variable to be passed to the deleteSocialAccountText string. This variable name is `magic_word` and is set as DELETE where used

* modified incorrectDeletePhrase to use a variable rather than translatable string for the word DELETE. Updated the DELETE-user test and the user api

* Changed noSudoAccess from translatable string to static

* Changed enterprisePlansEmailSubject from a translatable string to a static string within groupPlans.vue

* Fixed test problems with translation fixes

* Added no sudo access string to api messages

* changed plain string to apiMessage for no sudo access messages
2017-11-27 20:08:39 -06:00
Tyler Nychka
fac81bb9ee Long names overflow task box fixes #9403 (#9404)
* Issue 9403 Long names overflow task box

* Added padding

* Enabled overflow-wrap: break-word;

Added min width to allow overflow-wrap to actually break content
2017-11-27 20:07:13 -06:00
Sarvesh Kakodkar
b323abd225 Added confirmation step at begin button for quest (#9199)
* Added confirmation step at begin button for quest

* Fixed the 3 errors caused by questConfirm method in travis-ci
2017-11-27 20:05:05 -06:00
negue
b3870e5f34 multiple market fixes (#9468)
* show `selectMemberModal` to send a card, even if the user doesn't have a party yet

* market - prevent filter reset on pinning items

* hide buy amount for gear, backgrounds, mystery_set, card, rebirth_orb, fortify, armoire - fix mystery set preview in timetravelers

* purchase confirmation on gem / hourglass purchases

* fix lint
2017-11-27 19:54:55 -06:00
kartik adur
29dc56c12f Party roster sorter: Member Modal Component (#9472)
* modify sort options for party members

* add unittest for membersModalComponent sort

* updates as requested in PR

* removed duplicates for `class` and `background` from flavour text

* fix linting error thrown by travis ci
2017-11-27 19:54:13 -06:00
Esben Sparre Andreasen
b62f08d500 Misc. bug fixes from lgtm.com (2) (#9474)
* Remove dead branch of ternary: `gift` is always truthy here

Problem found here:

- https://lgtm.com/projects/g/HabitRPG/habitrpg/snapshot/dist-98076885-1510577633582/files/website/server/libs/amazonPayments.js?sort=name&dir=ASC&mode=heatmap&excluded=false#x5a22f31110a55091:1

* Remove superfluous argument, preenUserHistory only takes two args

Problem found here:

- https://lgtm.com/projects/g/HabitRPG/habitrpg/snapshot/dist-98076885-1510577633582/files/website/server/libs/cron.js?sort=name&dir=ASC&mode=heatmap&excluded=false#xf16a045ecabb07f6:1

* Cleanup: remove useless assignments

Problems found here:

- https://lgtm.com/projects/g/HabitRPG/habitrpg/snapshot/dist-98076885-1510577633582/files/website/client/store/actions/shops.js?sort=name&dir=ASC&mode=heatmap&excluded=false#xf782ed2cf920441%3A1
- https://lgtm.com/projects/g/HabitRPG/habitrpg/snapshot/dist-98076885-1510577633582/files/website/client/app.vue?sort=name&dir=ASC&mode=heatmap&excluded=false#x172c1dda85e84dc8%3A1
- https://lgtm.com/projects/g/HabitRPG/habitrpg/snapshot/dist-98076885-1510577633582/files/website/client/components/settings/site.vue#x9b3afee802a3a8f8%3A1
- https://lgtm.com/projects/g/HabitRPG/habitrpg/snapshot/dist-98076885-1510577633582/files/website/client/components/selectMembersModal.vue?sort=name&dir=ASC&mode=heatmap&excluded=false#x1fbc2a3d62facd70:1
- https://lgtm.com/projects/g/HabitRPG/habitrpg/snapshot/dist-98076885-1510577633582/files/website/common/script/libs/taskClasses.js?sort=name&dir=ASC&mode=heatmap&excluded=false#x41ce0e121a4defee:1

* Fix online editor whitespace change.
2017-11-27 19:51:25 -06:00
Esben Sparre Andreasen
f62177fb1a Misc. bug fixes from lgtm.com (#9325)
* Bugfix: declare variable locally

* Bugfix: fix syntax error

* Bugfix: regex char-class with alternatives

The old implementation used character classes instead of
alternatives. As a consequence, the regex would match:

- a_warrior_0
- r_warrior_0
- m_warrior_0
- o_warrior_0
- r_warrior_0
- |_warrior_0
- h_warrior_0
- ...

The regex will now match:

- armor_warrior_0
- head_warrior_0
- shield_warrior_0
2017-11-27 19:51:02 -06:00
Paul
885f2998ae System messages flaggable (#9408)
* Remove flag from system messages, throw an error if system messages are flagged

* Modify unflag system message test to check if flagging a system message throws an error

* Move email from nconf to top
2017-11-27 19:45:04 -06:00
Paul
2afd96e11c Remove spaces from filters (#9524) 2017-11-27 19:44:33 -06:00
Paul
cd92f44365 Fix difficult to edit checklists in Firefox (#9525)
* Change checklist item hover from move to text

* Add vue draggable

* Add vue draggable

* Replace sortablejs directive with Vue Draggable component

* Indent draggable properties
2017-11-27 19:43:24 -06:00
Paul
863177902a Remove extra 'to' paramater from router link (#9529) 2017-11-27 19:42:53 -06:00
Cassidy Pignatello
96974461e5 Change badge tooltips fixes https://github.com/HabitRPG/habitica/issues/9520 (#9539)
* updates text for costume contestant

* updates text for contributor badge

* removes unnecessary "Badge" text from contribName
2017-11-27 19:37:18 -06:00
Paul
8895b70ffa Adjust left positioning of the left-panel to unobscure sroll-bar, change left panel overflow to overflow-y (#9544) 2017-11-27 19:35:30 -06:00
Kip Raske
03480ebfc7 Passing quantity to the API call when buying quests from the shop (#9565)
You can buy multiple quests from the buy modal, but the quanitity is
never passed to the server. So the client thinks that you are buying the
number you said you did, but the server only buys one regardless. This
can lead to syncing problems down the road.
2017-11-27 19:33:08 -06:00
Joseti
9b8676f02e Changed "Donate" button to "Contribute" button (#9581)
* Changed functionality of "donate"-button on static pages

* Changed strings to reflect change from "donate" to "contribute"
2017-11-27 19:29:45 -06:00
Keith Holliday
3e7738b5b1 Added keys to for loops (#9584) 2017-11-27 12:46:25 -06:00
Keith Holliday
33a235b46c Fixed equiping glasses and ears (#9585) 2017-11-27 12:19:36 -06:00
Keith Holliday
137d6c1f9d Set default empty array when party members haven't loaded (#9579)
* Set default empty array when party members haven't loaded

* Corrected variable usage
2017-11-27 11:59:42 -06:00
Keith Holliday
1a5e820d88 Fixed when user unclaims a task assigned to them (#9578)
* Fixed when user unclaims a task assigned to them

* Removed test

* Fixed lint
2017-11-27 11:38:59 -06:00
Keith Holliday
0c7f9ca6bb Changed search to regex (#9575)
* Changed search to regex

* Changed  to array
2017-11-27 11:10:26 -06:00
Keith Holliday
3e6b3ce3ff Added habitica event for profile display (#9576) 2017-11-27 10:29:20 -06:00
Keith Holliday
ea5ba965e7 Fixed promoting group leader (#9574) 2017-11-27 10:27:30 -06:00
Keith Holliday
7215a550b5 Added page increment when page is loaded to prevent scroll stopping (#9573) 2017-11-27 10:23:04 -06:00
Keith Holliday
3235dfa236 Added equipment filter (#9572) 2017-11-27 10:22:34 -06:00
Keith Holliday
9baf7a7c67 Do not reset item when buying cards (#9571) 2017-11-27 10:22:00 -06:00
Alys
cd629ef7fa add missing @ before a contributor's name in quest content 2017-11-27 09:16:13 +00:00
Keith Holliday
9ef7c45241 Ensured user is saved after validation checks (#9569) 2017-11-23 20:46:02 -06:00
Alys
fef3d09f2d remove words related to alcohol
This is because they're causing problems in housework guilds (alcohol
is used for cleaning) and since it's coming up to Christmas a lot of
guilds are starting to have permissible conversations about drinks,
rum balls, etc.

We might put these back after Christmas but make specific exceptions
for the housework guilds.

When a more advanced word blocker is introduced, we'll be able to
ban alcohol words only in the Tavern. For irony.
2017-11-23 17:52:22 +00:00
Matteo Pagliazzi
53c83c585a fix navbar not showing up in homepage 2017-11-23 16:07:58 +01:00
Keith Holliday
e628c5dc3b Fixed task best color (#9563) 2017-11-21 14:02:53 -06:00
Keith Holliday
9eaa531f66 Amazon payment fixes (#9562)
* Added custom amazon event, removed redundency, fixed variable names

* Fixed more variables and group plan data
2017-11-21 14:02:40 -06:00
Keith Holliday
3ffea4332e Added extra confirmation incase the class modal shows multiple times (#9557) 2017-11-20 15:57:33 -06:00
Sabe Jones
4618fd8954 Merge branch 'release' into develop 2017-11-20 19:21:17 +00:00
Sabe Jones
791c19b5f1 4.12.1 2017-11-20 19:20:54 +00:00
Sabe Jones
7193cc6bae chore(i18n): update locales 2017-11-20 19:20:03 +00:00
Keith Holliday
1845bd1e35 Fixed display of RYA behind bailey (#9555) 2017-11-20 12:38:26 -06:00
Keith Holliday
5f468d16b7 Cancel users free group plan when they leave a group (#9543)
* Cancel users free group plan when they leave a group

* Fixed lint
2017-11-20 12:34:41 -06:00
Keith Holliday
20a99e526d Should do diff week fix (#9551)
* Fixed week difference when changing year

* Added year switchover test

* Fixed start date setting
2017-11-20 12:22:01 -06:00
Keith Holliday
1e69f42d0f Added account transfer migration (#9548)
* Added account transfer migration

* Removed bad comment
2017-11-19 16:53:08 -06:00
Keith Holliday
9c2f5213cb Challenge fixes (#9528)
* Added challenge member search to progress dropdown

* Added leave challenge modal

* Allowed editing for challenge leader only

* Pevented users from editing challenge task info

* Set default progress default to daily

* Removed reward filters from user challenge progress
2017-11-17 17:13:07 -06:00
Sabe Jones
c06d5107ac Merge branch 'release' into develop 2017-11-17 22:50:27 +00:00
Sabe Jones
1eb0f5baa5 4.12.0 2017-11-17 22:50:05 +00:00
Sabe Jones
b28189fff5 chore(i18n): update locales 2017-11-17 22:30:34 +00:00
SabreCat
82497e4041 chore(sprites): compile 2017-11-17 22:22:27 +00:00
SabreCat
2a5e9c0780 feat(event): Turkey Day 2017
and (content) November Subscriber Items
2017-11-17 22:21:05 +00:00
SabreCat
c8ca67aa64 fix(seasonal-shop): short circuit if categories empty 2017-11-17 18:32:55 +00:00
MathWhiz
89e4cbcffe Disable immutable inputs when editing a challenge (#9412)
* Disable uneditable inputs when editing a challenge

* Revert prize display when creating
2017-11-17 23:45:38 +11:00
Keith Holliday
67564317fb Group plan fixes (#9518)
* Fixed group plan editing

* Added translations

* Abstracted query for group or challenge tasks
2017-11-17 20:31:39 +11:00
Keith Holliday
dc2269a307 Hot fixes nov 15 (#9519)
* Added fortify sync. Removed multiple buy

* Added yesterdaily check

* Fixed checks for running yesterdailies
2017-11-17 20:31:29 +11:00
Sabe Jones
0c713ab368 4.11.1 2017-11-16 22:08:24 +00:00
Sabe Jones
be5d776fb4 chore(i18n): update locales 2017-11-16 22:07:54 +00:00
SabreCat
b0051c45b4 fix(group-plans): more claim/unclaim alignment 2017-11-16 21:21:39 +00:00
Matteo Pagliazzi
b82044e07b fix Remove Claim positioning 2017-11-16 22:05:35 +01:00
Matteo Pagliazzi
b4c4769208 fix: correctly handle bootstrap variables 2017-11-16 21:59:55 +01:00
SabreCat
42a05446c0 fix(notifications): extend snackbar duration 2017-11-16 20:32:57 +00:00
MathWhiz
9f7c0b4861 Fix regex for message highlighting (#9516) 2017-11-16 14:12:26 -06:00
Alys
4814b0c52b allow banned word / swearword blocker to apply to most public guilds (#9253)
* allow banned word / swearword blocker to apply to all public guilds, with specified exceptions

* add another guild

* add more guilds to those that do not have the bannedWords blocker applied

* fix lint errors
2017-11-16 19:52:17 +01:00
SabreCat
67b16d91a3 fix(drawer): remove overflow CSS 2017-11-16 16:38:38 +00:00
Alys
8d444980de remove unneeded space from in front of a full stop 2017-11-16 19:29:38 +10:00
MathWhiz
08ccd595f2 Add quantity information when purchasing items (#9481) 2017-11-16 10:08:11 +01:00
Alys
6b625a60ab remove client-side purchase of Armoire - fixes #9432 (#9463) 2017-11-16 10:06:45 +01:00
Matteo Pagliazzi
6bdb695616 Merge branch 'CSE2410-TeamZero-issue9334' into develop 2017-11-15 17:47:27 +01:00
Matteo Pagliazzi
b999d46142 Merge branch 'issue9334' of https://github.com/CSE2410-TeamZero/habitica into CSE2410-TeamZero-issue9334 2017-11-15 17:47:17 +01:00
Matteo Pagliazzi
ce0f5af08d update package-lock.json 2017-11-15 17:44:55 +01:00
Allister
c5296d4cb0 Add variable placeholder. (#9465)
Changes the character translation string `charactersRemaining`  in
groups.json to include a variable placeholder `<%= characters %>`
2017-11-15 17:41:18 +01:00
MathWhiz
22b683b1d9 Fix regex for chat highlighting (#9411)
* Fix regex for chat highlighting

* Fix lint errors
2017-11-15 17:39:29 +01:00
Joseti
229fd06ee3 Removed "Tag list in tasks starts collapsed" settings and strings (#9406) 2017-11-15 17:37:15 +01:00
Stephanie Wu
eb8f84aae0 #9354 Indicate that Orb of Rebirth is instant (#9357)
* #9354 Indicate that Orb of Rebirth is instant

Clarified the text

* #9354 Indicate that Orb of Rebirth is instant

Reverted changes in all but locales/en
2017-11-15 17:35:51 +01:00
Andrew
69b69e9d27 Update appFooter.vue in attempt to fix #9336 (#9348)
* Update appFooter.vue in attempt to fix #9336

Make whole social circle clickable.
Replace div with `a` tag allowing whole circle to become a link.

* Indent and reformat "Not ready yet" Instagram button.
2017-11-15 17:35:16 +01:00
dnlup
8033e7c0a0 Fix Incorrect Heading margins (#9311)
* fix(style): Fix margin of headers in inventory page

Set the `mb-0` class in h1 header to `mb-4`.
Set the class if `h2` header to `mb-3`.

* fix(style): Fix margin of headers in shops section

Set the `mb-0` class in h1 header to `mb-4`.
Set the class of `h2` header to `mb-3`.

* fix(style): Fix margin of headers in shops adn time traveler section

Set the `mb-0` class in h1 header to `mb-4`.
Set the class of `h2` header to `mb-3`.
2017-11-15 17:34:57 +01:00
Emily Ong
e39b80bb9a changed to 'createChallengeCloneTasks' (#9300)
* changed to 'createChallengeCloneTasks'

* createChallengeCloneTasks

* Added new i18n strings
2017-11-15 17:34:33 +01:00
Ryan Holinshead
2038d5e7c8 Choose class modal remove tooltip (#9298)
* Change chooseClass modal opt out button and tooltip
- Remove hover tooltip from opt out
- Fix centering of choose class button
- Add some margin between opt out and choose class buttons
- Make cursor on opt out text the pointer to make it obvious it's clickable

* Alphabetical order class selector
2017-11-15 17:32:49 +01:00
Aquib Master
46a8ee52d4 Modify relative dueIn time on tasks to be in days (#9251)
* Modify relative dueIn time on tasks to be in days

- Normalizes the current time and task due time to the ends of their respective days.
- Returns 'today' if the dates are the same day else uses moment's humanize function to allow for weeks, months, years and so on.

* Modify task due date to appear grey when due the next day
2017-11-15 17:32:30 +01:00
William Perry
52064f6b2a Changes animal attribute that A-Z sort is applied to. (#9241) 2017-11-15 17:31:10 +01:00
Łukasz Dobrogowski
f15a27a7f1 added favicons to the new client (#9235) 2017-11-15 17:29:58 +01:00
Blade Barringer
fcf0dd87f9 Rename constant for restricted email domains (#9459) 2017-11-15 17:14:13 +01:00
Matteo Pagliazzi
ab974675b9 More Tasks page fixes (#9475)
* fix datepicker not closing when clicking outside of it, fixes #9346

* fixes #9441
2017-11-15 17:06:32 +01:00
Sabe Jones
cb612d99d7 chore(sprites): recompile 2017-11-15 01:31:01 +00:00
Unknown
cb5a47ec7b Deleted unnessecary import. 2017-11-14 20:18:36 -05:00
Sabe Jones
838b9a5822 Merge branch 'release' into develop 2017-11-15 01:03:07 +00:00
Sabe Jones
6c65056e2b 4.11.0 2017-11-15 01:01:18 +00:00
Sabe Jones
2bf0fdf4a2 chore(i18n): update locales 2017-11-15 01:00:56 +00:00
SabreCat
cd5ff04ee4 chore(sprites): compile 2017-11-15 00:53:27 +00:00
SabreCat
dbc5b9f850 feat(content): Yarn Pet Quest 2017-11-15 00:53:10 +00:00
Unknown
ca3437d676 Added pin.scss to index.scss. 2017-11-14 19:26:11 -05:00
Keith Holliday
c43ca62bc4 Added check for balance with respect to quantity (#9469) 2017-11-14 16:55:08 -07:00
Keith Holliday
eaa91b2a09 Group plan fixes (#9437)
* Prevented title editing on personal page

* Fixed claim/unlclaim from user task page

* Removed task from local on delete

* Immediately show unassigned bar

* Add move to group tasks

* Fixed group member count increase

* Added upgrade when group plan is canceled
2017-11-14 16:54:11 -07:00
Unknown
6259b68b4f Does not reuse same css classes, imports them from pin.scss. For the togglePin function the notification. 2017-11-14 18:10:33 -05:00
Unknown
08073acf11 Solved Issue #9334. Added unpin feature for rewards list in tasks. 2017-11-14 12:24:11 -05:00
Matteo Pagliazzi
bddafd4392 fix bailey css 2017-11-13 12:12:41 +01:00
Matteo Pagliazzi
3ab14e4e5a fix checklist deleting 2017-11-13 12:08:42 +01:00
Matteo Pagliazzi
d9830950aa fix modal handling 2017-11-10 12:43:50 +01:00
Matteo Pagliazzi
45696a6273 remove outline from focused inputs 2017-11-10 12:02:20 +01:00
Sabe Jones
3f92317b9e Revert "Implements repeat every X days since last completion (Fixes #6941) (#8962)"
This reverts commit 9d69d4b863.
2017-11-09 22:13:34 +00:00
Sabe Jones
848883736d 4.10.1 2017-11-09 21:23:55 +00:00
Alys
69e0ab11c0 swap achievement badge icons for Challenges Won and Quests 2017-11-10 07:10:03 +10:00
Matteo Pagliazzi
d8d7a81edf Tasks: fixes and new edit task design (#9442)
* add missing tooltips

* makes sure due date is update correctly, fixes #9436

* do not collapse checklist when casting spells, fixes #9345

* start to fix spells drawer

* fix drawer requiring two clicks to open
2017-11-09 19:38:48 +01:00
Matteo Pagliazzi
03a09b7546 Fix issues with Bootstrap Vue and Boostrap upgrades (#9452)
* start to fix modals

* fixed cards paddings

* fix notifications not being marked as read

* add tests for reading a notification

* fixed indentation and added tests for reading multiple notifications

* register from home page using enter key
2017-11-09 19:37:47 +01:00
Sabe Jones
0d2737572d Merge branch 'release' into develop 2017-11-09 17:57:48 +00:00
Sabe Jones
28149202db chore(i18n): update locales 2017-11-09 17:53:06 +00:00
Alys
4b23cd9f23 add missing string for dateEndNovember 2017-11-09 17:46:12 +00:00
Alys
8aaabdc086 add missing removeInvite string 2017-11-09 17:45:59 +00:00
SabreCat
6e6ca05352 fix(sprites): missing Guild image 2017-11-09 17:44:48 +00:00
SabreCat
74d8ecc732 Merge branch 'release' into develop 2017-11-09 17:24:48 +00:00
Alys
63c8a09e22 add missing string for dateEndNovember 2017-11-09 21:58:16 +10:00
Alys
865f623c99 add missing removeInvite string 2017-11-09 18:16:58 +10:00
Matteo Pagliazzi
061968dd1a intro tour: fix justin position 2017-11-08 19:55:28 +01:00
Matteo Pagliazzi
21bc91c3ae chore(npm): upgrade bootstrap-vue 2017-11-08 18:56:28 +01:00
Matteo Pagliazzi
0bfc6608c1 update package-lock.json 2017-11-08 18:44:18 +01:00
negue
4108a22d78 [WIP] bootstrap-vue upgrade (#9178)
* update bootstrap-vue to 1.0.0-beta.9 - remove all individual bootstrap components and use BootstrapVue into Vue

* change modal action names from show::modal to bv::show::modal

* check if drops are undefined

* fix modal widths - sellModal now using input instead of dropbox

* upgrade to bootstrap 4.0beta

* include package-lock changes

* fix app menu dropdown position

* upgrade bootstrap to beta2 (was missing grid offset and other fixes) - refix header menu position

* fix tags popup (auto width to max not working) - fix filter panel width (adding width: 100% works until max-width)

* show hide logo on different screensize (new css breakpoints - http://getbootstrap.com/docs/4.0/utilities/display/ )

* fix package-lock?

* fix active button style / app header toggle button

* fix package-lock !

* update package lock after merge - new mixin "openedItemRows" to save the "show more/show less" in stable

* mixin naming style

* fix buyQuestModal marginTop

* fix customMenuDropdown position

* fix userDropdown items
2017-11-08 18:40:37 +01:00
Matteo Pagliazzi
34f6b63968 remove unused loggin for ios purchases 2017-11-08 16:12:11 +01:00
Sabe Jones
8d1ebff7e9 4.10.0 2017-11-07 23:55:16 +00:00
Sabe Jones
993df72708 chore(i18n): update locales 2017-11-07 23:53:47 +00:00
SabreCat
50d3226a86 feat(content): change to Thunderstorm Hatching Potions 2017-11-07 22:02:46 +00:00
Keith Holliday
17ce2febf9 [WIP] Add initial fixes for concurrency (#9321)
* Add initial fixes for concurrency

* Added memory edit for notifications

* Fixed tag delete

* Fixed adding and moving task order

* Updated delete task

* Fixed lint

* Fixed task adding

* Switch to mongoose push and pull
2017-11-07 13:19:39 -07:00
Matteo Pagliazzi
0caa195c6f fix confetti image on home page 2017-11-07 20:34:52 +01:00
Keith Holliday
f964e3c0a5 Updated avatar menu icons and style (#9409) 2017-11-07 11:57:42 -07:00
Asif Mallik
9d69d4b863 Implements repeat every X days since last completion (Fixes #6941) (#8962)
* Implemented repeat after completion

* Added tests for repeat after completion in shouldDo.test.js

* Remove lastTicked

* Undoes removal of website/client/README.md
2017-11-07 12:56:46 -06:00
Matteo Pagliazzi
19500600bc cache sprites and fix images caching (#9422) 2017-11-07 11:48:23 +01:00
Matteo Pagliazzi
f25fe9e263 remove forked vue version 2017-11-06 23:46:18 +00:00
Matteo Pagliazzi
5f37487c23 Fix guilds fetching (#9416)
* fix guilds fetching

* fix missing categories
2017-11-06 17:16:41 +01:00
Alys
12fd79059b adjust login messages to indicate case-sensitivity and hint about google sign-in 2017-11-04 20:34:17 +10:00
Alys
232061a629 improve wording for Brutal Smash skill 2017-11-04 20:24:35 +10:00
Alys
3fcc1c522d allow "visit the stable" text to be translated 2017-11-04 20:16:47 +10:00
Matteo Pagliazzi
8302c50302 remove forked vue version 2017-11-04 10:24:45 +01:00
Sabe Jones
6eb06fb054 4.9.1 2017-11-03 22:58:12 +00:00
Keith Holliday
286c8c7530 Ensured sort value is true for checklists (#9386) 2017-11-03 15:50:11 -05:00
Keith Holliday
47ab8f2073 Added exitence checks (#9383) 2017-11-03 15:38:05 -05:00
Sabe Jones
83353f6481 Merge branch 'paglias/fix-sprites' into release 2017-11-03 20:35:20 +00:00
Matteo Pagliazzi
9cbd7ad62d fix customize-options sprites 2017-11-03 19:06:06 +01:00
thehollidayinn
3485a1d0bc Reloaded completed todos if we are unlinking a todo 2017-11-03 10:33:20 -06:00
thehollidayinn
2e5106fda1 Removed old state item 2017-11-03 10:24:46 -06:00
thehollidayinn
2e5f5714e4 Added broken task event 2017-11-03 10:24:00 -06:00
Sabe Jones
3cf7b2c96c 4.9.0 2017-11-03 03:04:52 +00:00
Sabe Jones
286db39478 chore(i18n): update locales 2017-11-03 03:04:15 +00:00
SabreCat
4d4c1cfaf3 chore(sprites): compile 2017-11-03 02:54:26 +00:00
SabreCat
d7ad3efabf fix(news): proper Take This announcement 2017-11-03 02:49:41 +00:00
SabreCat
f8876fe055 feat(content): Backgrounds and Armoire 2017-11
End Habitoween and Fall Festival
2017-11-03 01:56:15 +00:00
Keith Holliday
b973335d69 Added extra months to account for months with larger amount of days (#9379) 2017-11-02 15:44:24 -06:00
Phillip Thelen
3b6fce0708 Add "stats" as a tutorial step (#9377)
This is used mostly for the mobile apps to identify if the tutorial step about stats has be shown yet or not.
2017-11-02 15:57:16 -05:00
Keith Holliday
ff6bd6de71 Ensured user selects plan (#9378) 2017-11-02 14:26:00 -06:00
Keith Holliday
042afe1df3 Add close to tags popup (#9376) 2017-11-02 14:25:37 -06:00
Matteo Pagliazzi
a208ba4aba Tasks v2 Part 2 (#9236)
* start updating colors for tasks controls

* finish updating controls colors

* change hoevr behavior

* change transition duration

* update color with transition

* refactor menu wip

* wip

* upgrade vue deps

* fix warnings

* fix menu

* misc fixes

* more fixes

* fix badge

* fix margins in menu

* wip tooltips

* tooltips

* fix checklist colors

* add badges

* fix quick add input

* add transition to task controls too

* add batch add

* fix task filtering

* finish tasks badges

* fix menu

* upgrade deps

* fix shop items using all the same image

* fix animation

* disable client tests until we remove phantomjs

* revert changes to tasks colors

* fix opacity in task modal inputs

* remove client unit tests from travis

* wip task dropdown

* fix z-index for task footer/header

* fixes and add files

* fixes

* bigger clickable area

* more space to open task dropdown

* droddown position

* fix menu position

* make sure other dropdowns get closed correctly

* fixes

* start to fix z-index

* draggable = false for task dropdown

* fix for dropdown position

* implement move to top / bottom

* fix push to bottom

* typo

* fix drag and drop

* use standard code

* wider click area for dropdown

* unify badge look

* fix padding

* misc fixes

* more fixes

* make dropdown scrollable

* misc fixes

* fix padding for notes

* use existing code instead of new props
2017-11-02 21:07:38 +01:00
Keith Holliday
0e958fd306 Prevented challenge edit during RYA (#9373) 2017-11-02 11:37:29 -06:00
Keith Holliday
d98fe79e9c Fixed member modal search (#9375) 2017-11-02 11:36:08 -06:00
Keith Holliday
0e5a811b98 Prevented challenge prize edit. Fixed edit to create modal (#9374) 2017-11-02 11:30:01 -06:00
Keith Holliday
a28aea65f8 Added reward text (#9332) 2017-11-01 10:03:23 -06:00
Keith Holliday
0f92349902 Added fix for multiple level up (#9330) 2017-11-01 10:01:58 -06:00
Sabe Jones
d4881cb73a Merge branch 'release' into develop 2017-10-31 19:22:58 +00:00
Sabe Jones
b3216fdb85 4.8.0 2017-10-31 19:22:27 +00:00
Keith Holliday
3e37941e0a Bulk stats (#9260)
* Reorganized stats

* Organized allocation common code

* Added bulk allocate to common

* Added allocate bulk route

* Fixed structure and lint

* Fixed import and apidoc
2017-10-31 12:57:44 -06:00
Alys
32088767ac update github templates for new website and wiki page locations 2017-10-31 21:25:01 +10:00
Alys
f4d021ab8c fix an inaccurate comment about guilds needing summaries 2017-10-31 20:54:23 +10:00
SabreCat
8532203717 Merge branch 'release' into develop 2017-10-31 01:03:07 +00:00
SabreCat
365daba6fc 4.7.1 2017-10-31 00:49:58 +00:00
SabreCat
70692752c7 fix(sprites): bad shirt centering 2017-10-31 00:48:37 +00:00
Sabe Jones
95d4016678 Merge branch 'release' into develop 2017-10-30 20:50:32 +00:00
Sabe Jones
061457b268 4.7.0 2017-10-30 20:50:11 +00:00
Sabe Jones
e7ec9a6d65 chore(i18n): update locales 2017-10-30 20:49:09 +00:00
SabreCat
d1396e7bc6 chore(sprites): compile 2017-10-30 20:41:32 +00:00
SabreCat
d5cedaa925 feat(event): Habitoween 2017-10-30 20:40:29 +00:00
Sabe Jones
bea813b318 Merge branch 'release' into develop 2017-10-26 22:47:18 +00:00
Sabe Jones
b31268fbc2 4.6.3 2017-10-26 22:46:51 +00:00
thehollidayinn
35727228f0 Removed autorefresh 2017-10-26 17:20:52 -05:00
SabreCat
feb7ab8345 Merge branch 'release' into develop 2017-10-26 21:16:09 +00:00
SabreCat
f4422b8d6c 4.6.2 2017-10-26 21:14:13 +00:00
SabreCat
2d4dc9e23c chore(news): blog Bailey 2017-10-26 21:13:04 +00:00
Tyler Nychka
bba2e71af3 Task order fix (#8928) 2017-10-26 10:37:51 -05:00
Mel
26123ac6ae API - Get challenges for a group does not allow party or habitrpg (#8882)
* test get party challenges by party ID

* tavern challenge tests; failing tests with ID 'party' or 'habitrpg'

* allow finding challenges by groupid 'party' or 'habitrpg'

* use single quotes in strings
2017-10-25 15:35:23 -05:00
Zobdek
addee73e4d Set _cronSignature to current time instead of uuid (#8565)
* Changes made to satisfy #8163. _cronSignature is set to current time when cron starts so that if cron fails to set _cronSignature to 'NOT_RUNNING' for any reason a new cron can be started after a set amount of time (1 hour for now)

* fix lint errors

* changed cronTimeout to CRON_TIMEOUT

* Changed variable names and comments to be more clear

* Fixed stub for failing test so that it matches new mongo db update call signature

* First pass at unit tests, error messages and some other things need to be determined

* Fixed a tab that snuck in :/

* Fixed lint issues (issues with spaces)

* Fix infix operator spacing

* Created constant. Make sure cron failure test verifies that it is failing for the right reason

* Fixed lint errors

* Removed no longer used uuid import
2017-10-25 15:29:16 -05:00
Keith Holliday
c39505d41c Hotfix/add autoreload (#9269)
* Added auto refresh on server update back

* Fixed lint

* Updated copy
2017-10-25 15:22:26 -05:00
Lachlan Heywood
9736ef0d25 Add ESLint to gulp scripts (#9259)
* Remove gulp/* and gulpfile from ESLint ignores

* Update .eslintrc in local gulp folder

* Start work on refactoring gulp files

* add radix

* Add line-specific eslint exceptions

* removed redundant eslint file for gulp

* add more exceptions

* Add exceptions to main gulpfile.js
2017-10-25 10:45:03 +02:00
Sabe Jones
638259b885 Merge branch 'release' into develop 2017-10-24 22:42:20 +00:00
Sabe Jones
c24545cae5 4.6.1 2017-10-24 22:41:58 +00:00
Keith Holliday
6bc70ca471 Hotfix/oct 24 fixes (#9261)
* Removed max height from text section

* Added items to mystery sets

* Group manager fixes

* Fixed promo code apply
2017-10-24 17:41:36 -05:00
Sabe Jones
d2f0d7b20b Merge branch 'release' into develop 2017-10-24 20:36:24 +00:00
Sabe Jones
b445bc8261 4.6.0 2017-10-24 20:35:17 +00:00
Sabe Jones
31281b43d3 chore(i18n): update locales 2017-10-24 20:34:47 +00:00
SabreCat
a8a915ea8e chore(sprites): compile 2017-10-24 20:23:42 +00:00
SabreCat
dc3ee25e65 feat(content): subscriber items
Also fix(sprites): resize and recanvas gift boxes
2017-10-24 20:21:30 +00:00
Lula Villalobos
3c9f7ff9d8 Bug Fix on ScoreTask: Gp added instead of append [fixes #9180] (#9207)
* added parseInt to stats.gp so it can add the new value

* added radix parameter to fix lint issue

* revert changes in scoreTask.js

* convert restoreValues into numbers before setting to users.stats
2017-10-24 16:23:12 +02:00
Sabe Jones
54f57445da 4.5.0 2017-10-23 22:40:41 +00:00
Sabe Jones
95ef2b1789 Merge branch 'develop' into release 2017-10-23 22:40:30 +00:00
Keith Holliday
4d32977e5c Quick fix (#9257)
* Removed hard refresh

* Changed sorting to happen during compute
2017-10-23 17:27:26 -05:00
Keith Holliday
7fe2504906 Bulk purchasing (#9196)
* Moved buy tests

* Added mystery buy to buy.js

* Added quest purchasing to buy

* Added buy special

* Moved integration tests to buy folder

* Removed buyGear dependency

* Removed buyArmoire dependency

* Removed buyHealthPotion dependency

* Removed myster, quest and special dependency

* Replaced functions with factory

* Added bulk purchasing to common

* Added bulk purchasing to the api

* Added bulk purchasing to client

* Refactored purchasing function to reduce long method

* Added bulk purchase to gem purchases

* Added bulk purchasing to api

* Added bulk purchasing to gem items on client

* Removed bulk from equipment

* Removed recentlyPurchased

* Fixed style issues and prevented puchasing more gems than are left

* Fixed lint

* Fixed missing keys

* Fixed gem amount notice

* Added quest modal to pinned item

* Added bulk purchase to gem modal

* Fixed styles

* Fixed bulk purchase for spells

* Fixed modal size

* Hid autofill
2017-10-23 16:05:35 -05:00
Matteo Pagliazzi
b74cee3d21 API Token Changes (#9202)
* hide API token by default

* wip

* add route to reset the api token

* remove dead code
2017-10-23 22:58:33 +02:00
Keith Holliday
5af7733150 Task order fixes (#9255)
* Reset todo task order

* Disabled sorting on todo due

* Revert to task order

* Fixed task sorting sync with server
2017-10-23 12:59:52 -05:00
Keith Holliday
824bf62e0a Force refresh is server version is updated (#9239)
* Force refresh is server version is updated

* Added reload true

* Added confirmation of update

* Forced refresh on cron

* Updated response tests
2017-10-23 12:58:11 -05:00
Keith Holliday
ac24a5dddd Moved ponytails to styles (#9254) 2017-10-22 15:48:50 -05:00
Alys
9111f59da4 change price for backgrounds set from 5 to 15 2017-10-22 09:24:32 +10:00
Sabe Jones
6a550b34df 4.4.5 2017-10-21 03:35:51 +00:00
SabreCat
0c973b1cf0 chore(news): Bailey 2017-10-21 03:33:42 +00:00
SabreCat
4170ef5e79 Merge branch 'release' into develop 2017-10-21 03:23:13 +00:00
Keith Holliday
506275c30e Oct 19 fixes (#9234)
* Add more checks for user achievements

* Began to add ajax request for bailey

* Prevented purchase of locked item

* Refactored notifications to have unique id and use actions

* Added feature banner when gear is bought

* Removed debug code

* Mark group notifications as read

* Fixed lint issues

* Added gem icon to purchase all
2017-10-20 15:22:13 +02:00
Alys
6838b7d0a6 remove duplicated word "caused" from Masterclassers quest text 2017-10-20 23:12:26 +10:00
Sabe Jones
e987cd52a6 4.4.4 2017-10-18 21:06:05 +00:00
Sabe Jones
1df8668d38 chore(i18n): update locales 2017-10-18 20:57:03 +00:00
Stephy
1af42aa7fe Set a max-width for Markdown images (#9230)
Prevents images from overflowing in tavern chat, guild sidebars, tasks,
and anywhere else using Markdown

Closes https://github.com/HabitRPG/habitica/issues/9171
2017-10-18 15:47:16 -05:00
maurnoc
9dc9faa70d fixes bug #9173 notification menu having unneeded scroll bars (#9229) 2017-10-18 15:44:50 -05:00
Cezary Piątek
7fbcc0a263 Replace raw HTML internal hyperlinks with router links #9209 (#9212)
* Replace raw HTML internal hyperlinks with router links #9209

* Fix improper route-link
2017-10-18 15:40:53 -05:00
borisabramovich86
0450e9c3ae Added empty state values for photo and description in user profile (#9201) 2017-10-18 15:36:26 -05:00
borisabramovich86
15626b8ae1 Prevent user from blocking himself - fixes #9097 (#9194)
* Added check - user can't block himself

* removed exclusive test
2017-10-18 15:26:01 -05:00
borisabramovich86
444f393f3a stops Orb of Rebirth being visible after rebirth - fixes #8593 (#9172)
* remove link between rebirth and beastmaster, and fix tests

* add test checking rebirth is reset when beastmaster is enabled

* remove exclusive test
2017-10-18 15:23:27 -05:00
q--
d711bf4085 Enable browser spell-checking for task titles (#9100)
This is disabled by default in Firefox for <input> elements.

It can also be enabled document-wide by adding spellcheck=true to the <html> tag, but I haven't done so because I don't know if there might be elements on the page where spell checking is not warranted.

See also: https://developer.mozilla.org/docs/Web/HTML/Global_attributes/spellcheck
2017-10-18 15:13:57 -05:00
John Montgomery
7b93e326fc Re-add the relevant attribute to the Stealth tooltip (#9198) 2017-10-18 23:39:39 +10:00
maurnoc
c62ea522c0 duplicate readme (#9219) 2017-10-18 09:41:45 +10:00
Alys
810362a404 fix typo in apidocs comment and my screwup in the previous commit - so sorry :( 2017-10-18 08:13:16 +10:00
Alys
2e429513da fix typo in apidocs comment 2017-10-18 08:04:53 +10:00
Matteo Pagliazzi
c8625cb23f Misc fixes (#9215)
* quick add assign tags

* fix columns descriptions

* fix changing language not taking effect immediately
2017-10-17 23:38:42 +02:00
tim1234ltp
ad50f90ba0 Bug fixes on Merchandise Page [Fixes Issues #9144] (#9195)
* Moved pictures of merch items from assets/images/merch/ to /website/static/merch
Image src in merch.vue is linked to the new path.

* Rebased and fixed.

* Reverted changes of package-lock.json
2017-10-17 10:48:23 -05:00
Sabe Jones
d5305f74e3 4.4.3 2017-10-17 00:01:54 +00:00
Sabe Jones
5ea20e4c8c fix(news): add links 2017-10-17 00:01:39 +00:00
Sabe Jones
9e4e2d0b34 4.4.2 2017-10-16 23:12:41 +00:00
Sabe Jones
6456e6b8e3 Merge branch 'develop' into release 2017-10-16 23:12:30 +00:00
Sabe Jones
af961ff16c chore(i18n): update locales 2017-10-16 23:11:52 +00:00
Sabe Jones
eb2a6095c2 Fixes 2017/10/16 (#9203)
* fix(various): small issues
* Rename "Gold-Purchasable Quests" to "Masterclasser Quest Lines"
* Use "getting_organized" instead of "organization" as task category
* Use pixel gem instead of SVG gem for Market
* Recanvas shop sprites for Fall 2017 seasonal items

* chore(news): update Bailey
2017-10-16 18:04:49 -05:00
Matteo Pagliazzi
ee82f5a774 Client: Tasks and Header v2 (#9168)
* wip: footer

* pin bootstrap-vue

* compact footer and fixes

* compact header

* fix member details in other views

* fix first party member in header

* fix unread notifications badge color

* sync + menu fixes

* loading bar styles

* new filter button

* create button

* create fixes

* quick add

* fix quick add inputs

* tooltips for create buttons

* fix create task moda

* fix class badge

* fixed footer links

* revert change that caused problems with tests and old browsers

* misc
2017-10-16 22:04:16 +02:00
Alys
f1c8c4c54b add 'declined' string, needed for quest participants list 2017-10-16 21:41:37 +10:00
Sabe Jones
79b15d7ddf 4.4.1 2017-10-15 17:31:13 +00:00
Trevor Ford
f2fed7ea39 fix Stable component to correctly count pets (#9182) 2017-10-15 12:20:40 -05:00
Aivan Monceller
ca81ff5af6 #9184 case insensitive search (#9190) 2017-10-15 12:12:30 -05:00
Sabe Jones
e28c214696 chore(i18n): update locales 2017-10-15 17:07:09 +00:00
SabreCat
792f45e9bd fix(tasks): incorrect string for text field 2017-10-15 02:37:11 +00:00
Sabe Jones
a69c0999d3 Fixes 2017/10/14 (#9191)
* fix(shops): event date range for mobile

* fix(profile): clean up equipment grids

* fix(strings): remove unused string

* fix(sprites): recanvas Lost Masterclasser scrolls
2017-10-14 19:59:28 -05:00
Sabe Jones
37ff35306c 4.4.0 2017-10-14 01:45:57 +00:00
Sabe Jones
fd430e95b2 chore(i18n): update locales 2017-10-14 01:45:08 +00:00
Sabe Jones
0ea472b3af Merge branch 'develop' into release 2017-10-14 01:34:12 +00:00
Sabe Jones
6d0496fbd0 Lost Masterclasser Quest Line (#9183)
* fix(sprites): serve animated GIFs
Also add new GIF for upcoming quest line

* feat(content): Masterclasser capstone quest
Also fixes Quests page for clearer lock/unlock display

* chore(sprites): compile

* feat(achievements): award quest completionist cheevo

* feat(quests): Siphoning Void
fix Achievements page styling and text spacing
fix display of Rage bar on party page

* test(quests): Lost Masterclasser

* chore(news): Bailey

* fix(test): only use Masterclasser in relevant scenario
2017-10-13 20:32:55 -05:00
Keith Holliday
fac1889776 Oct 11 fxes (#9181)
* Added unqequip for hair styles

* Added quest rewards to quest completed modal

* Fixed display of task approvals

* Ensured the user is welcomed before showing login notification

* Added new message modal

* fixed manager functions

* Fixed group edit on group plan
2017-10-13 06:40:53 +02:00
SabreCat
9ca6da0f75 fix(sprites): include sprite CSS in new client build 2017-10-12 18:57:23 +00:00
Matteo Pagliazzi
99f50f825a Old Client Cleanup (#9141)
* move emails images to website/static/emails and remove old files

* remove old client tests

* remove more files

* add sprites back

* cleanup gulp

* cleanup gulp

* remove old files

* more fixes

* pin bootstrap-vue

* disable old test

* remove old tasks

* fix apidoc
2017-10-12 16:44:28 +02:00
Sabe Jones
2308f2397e 4.3.0 2017-10-10 22:55:47 +00:00
Sabe Jones
f10d5110e5 Merge branch 'develop' into release 2017-10-10 22:55:39 +00:00
Sabe Jones
5ced47f590 Content 2017/10/10 (#9177)
* feat(content): bundle, customizations

* chore(sprites): update
2017-10-10 17:30:44 -05:00
Sabe Jones
24b2bc9aa1 4.2.3 2017-10-10 17:12:43 +00:00
Sabe Jones
aea08a971a Merge branch 'develop' into release 2017-10-10 17:12:35 +00:00
Sabe Jones
e3452b3ba7 chore(i18n): update locales 2017-10-10 17:11:48 +00:00
Michael Hibbs
3db666eb9e Show error message in empty market inventory drawer (#9041)
* client/tasks/user: (create dropdown) change icon color on menuitem hover

Previously the color was only changing when you hovered over the
text or icon.

This change changes the icon color when you hover anywhere over
the `menuitem`

* client/shops/market: fix search field in market

* Hide drawer sliders if number of items for type is empty
* Move drawer-help-text icon down to line up vertically with text
* Add TODO for error thrown when trying to sell special item types

* client/tasks/taskModal: display all selected tags for task

* i18n/groups: religous to religious

* client/shops/market: Add error message to empty drawer slider

* Create new locale file to hold inventory locales

* client/tasks/user: Revert change to create dropdown

* Revert tag changes

This will be picked up in a separate PR.

* Fix missing space before function paren
2017-10-10 11:18:50 -05:00
negue
cf5985e38c oct 9 fixes (#9175)
* hide hatching modal before opening the hatched modal

* fix Options not sticking: Costume, Auto Equip

* Hall of Heroes - Collapse Items & Auth

* prevent wrapping currency icon+text

* fix spacing of remaining gems (if two digits)

* fix logo in safari (removed <mask />)

* update seasonal shop items on purchase
2017-10-09 22:40:39 -05:00
Sabe Jones
d88a8247d1 Fixes 2017/10/09 (#9176)
* fix(quests): correctly divide owner-only/all

* fix(equipment): clarify locked items
2017-10-09 22:15:52 -05:00
Keith Holliday
7e1ae6a571 Oct 9 fixes (#9174)
* Prevented purchasing of locked quest

* Fixed category filters for challenges

* Added armoire equipment directly to user

* Halfed the time notifications display

* Added quest accept/reject to party page
2017-10-09 14:40:45 -05:00
Sabe Jones
d117117885 4.2.2 2017-10-09 19:17:49 +00:00
Sabe Jones
5dd168eee4 chore(i18n): update locales 2017-10-09 19:14:21 +00:00
Keith Holliday
a7c9355dd5 Fixed breaking api change (#9170) 2017-10-06 16:06:48 -05:00
Matteo Pagliazzi
81d3e8a68f add extra logging for ios/verify 2017-10-06 18:07:26 +02:00
Sabe Jones
e0fbfffbf2 4.2.1 2017-10-06 00:49:02 +00:00
Sabe Jones
36b968a74a fix(news): remove Last Chance blurb 2017-10-06 00:48:34 +00:00
Sabe Jones
fff249fb00 4.2.0 2017-10-06 00:39:47 +00:00
Sabe Jones
ce5c6b9517 Merge branch 'develop' into release 2017-10-06 00:39:40 +00:00
Sabe Jones
702013f9ed chore(i18n): update locales 2017-10-06 00:38:10 +00:00
Sabe Jones
3503b307b2 Content and fixes 2017-10-05 (#9165)
* fix(market): display classless equipment

* fix(challenges): more missing Markdown

* feat(content): backgrounds and Enchanted Armoire 2017-10

* chore(sprites): compile
2017-10-05 19:00:39 -05:00
Keith Holliday
f1ec8bbf2c Added styles to selecting challenge winner (#9164) 2017-10-05 18:02:26 -05:00
Keith Holliday
58b033db9e Oct 5 fixes (#9163)
* Added removing invites

* Addeed messages for empty gems to icon

* Added member for challenge members

* Fixed task cloning ending

* Fixed group task assignment

* Added small hack to prevent scrolling issues

* Fixed lint
2017-10-05 14:41:03 -05:00
thehollidayinn
b0dcc2f6ef 4.1.4 2017-10-04 18:35:50 -05:00
SabreCat
3377f8a916 Merge branch 'develop' into release 2017-10-04 23:30:39 +00:00
Keith Holliday
22f83d09c4 Release with develop (#9162)
* Client: fix Apidoc and move email files (#9139)

* fix apidoc

* move emails files

* quest leader can start/end quest; admins can edit challenges/guilds; reverse chat works; remove static/videos link; etc (#9140)

* enable link to markdown info on group and challenge edit screen

* allow admin (moderators and staff) to edit challenges

* allow admin (moderators and staff) to edit guilds

Also add some unrelated TODO comments.

* allow any party member (not just leader) to start quest from party page

* allow quest owner to cancel, begin, abort quest

Previously only the party leader could see those buttons. The leader still can.

This also hides those buttons from all other party members.

* enable reverse chat in guilds and party

* remove outdated videos from press kit

* adjust various wordings

* Be consistent with capitalization of Check-In. (#9118)

* limit for inlined svg images and make home leaner by not bundling it with the rest of static pages

* sep 27 fixes (#9088)

* fix item paddings / drawer width

* expand the width of item-rows by the margin of an item

* fix hatchedPet-dialog

* fix hatching-modal

* remove min-height

* Oct 3 fixes (#9148)

* Only show level after yesterdailies modal

* Fixed zindex

* Added spcial spells to rewards column

* Added single click buy for health and armoire

* Prevented task scoring when casting a spell

* Renamed generic purchase method

* Updated nav for small screen

* Hide checklist while casting

* fix some text describing menu items (#9145)

* 4.1.3

* use `selectGearToPin` instead of updateStore in migration-script (#9102)

* Tags redesign in edit-task modal (#9122)

* Truncate tags list to maximum number of tasks

This commit truncates the list of tags in the edit task modal and
displays the remaining selected tasks as a number.

* Align tags-select dropdown with "Tags" label

* Add tags popup component

* Use solid purple for tags-select dropdown

* Remove shadow when tags-select is active
* Add border-radius to tags-select
* Re-add previously disabled transitions
* Remove unused template element

* Add Clear Tags button to footer of tags popup

* Decrease column size for tags to better match design
* Truncate tag name to avoid overflows
* Add tag name as title to show full name on hover

* Grow inline tags select from left to right

* Style none button
* Add spacing to streak reset button to line up with tags select

* Add top offset to tags dropdown toggle to line up with label

* Ability to collapse checklists (#9158)

* ability to collapse checklists

* typo

* show warning to use the mobile apps (#9152)

* Oct 4 fixes (#9159)

* Added gem purchase note

* Added notification count

* Added party reload

* Added description when user can no longer purchase gems this month

* Prevent modal from showing when pruchasing recently purchased items

* Added progress bar

* Prevented non leader from loading approvals

* Added group billing

* Release fix (#9161)

* Merge Develop onto Release (#9123)

* Some random quick (#9111)

* Switch group button directions

* Allowed admins to export challenges

* Added scoping to some stable styles

* Fixed challenge cloning

* Tasks tags (#9112)

* Added auto apply and exit

* Add challenge tag editing

* Fixed lint

* Skill fixes (#9113)

* Added local storage setting for spell drawer

* Added new spell styles

* Fixed typo

* Reset local creds if access is denied (#9114)

* various fixes: group leader's name at top of edit drop-down; Members List; etc (#9117)

* fix text describing location of subscription/gem gift box

* disable Copy As To-Do in Tavern, guilds, party because it's not working

* change members label on group pages to Member List

* remove outdated info about seeing number of Gems available to buy

* allow Danger Zone to be seen by players without local authentication

Also add an hr because the Danger Zone heading was crammed up against the button above it.

* put current group leader's name at top of Leader change drop-down

* Client Fixes (#9120)

* unduplicate logout code

* re-enable debug menu

* fix pets badge and equipping mounts

* close gift modal after sending gems

* armoire notifications

* Oct 1 fixes (#9121)

* Added default tags to task

* Added seasonal gear check and show spooky

* Disabled spooky sparkles

* Fixed challenge remove tasks modal

* Hid checklist

* Added group gems modal

* Purchase with amazon

* Added check for user health

* Added missing notification file

* 4.1.1

* 4.1.2

* Merge develop into release (#9154)

* Client: fix Apidoc and move email files (#9139)

* fix apidoc

* move emails files

* quest leader can start/end quest; admins can edit challenges/guilds; reverse chat works; remove static/videos link; etc (#9140)

* enable link to markdown info on group and challenge edit screen

* allow admin (moderators and staff) to edit challenges

* allow admin (moderators and staff) to edit guilds

Also add some unrelated TODO comments.

* allow any party member (not just leader) to start quest from party page

* allow quest owner to cancel, begin, abort quest

Previously only the party leader could see those buttons. The leader still can.

This also hides those buttons from all other party members.

* enable reverse chat in guilds and party

* remove outdated videos from press kit

* adjust various wordings

* Be consistent with capitalization of Check-In. (#9118)

* limit for inlined svg images and make home leaner by not bundling it with the rest of static pages

* sep 27 fixes (#9088)

* fix item paddings / drawer width

* expand the width of item-rows by the margin of an item

* fix hatchedPet-dialog

* fix hatching-modal

* remove min-height

* Oct 3 fixes (#9148)

* Only show level after yesterdailies modal

* Fixed zindex

* Added spcial spells to rewards column

* Added single click buy for health and armoire

* Prevented task scoring when casting a spell

* Renamed generic purchase method

* Updated nav for small screen

* Hide checklist while casting

* fix some text describing menu items (#9145)
2017-10-04 18:26:05 -05:00
Keith Holliday
496534ab4b Release fix (#9161)
* Merge Develop onto Release (#9123)

* Some random quick (#9111)

* Switch group button directions

* Allowed admins to export challenges

* Added scoping to some stable styles

* Fixed challenge cloning

* Tasks tags (#9112)

* Added auto apply and exit

* Add challenge tag editing

* Fixed lint

* Skill fixes (#9113)

* Added local storage setting for spell drawer

* Added new spell styles

* Fixed typo

* Reset local creds if access is denied (#9114)

* various fixes: group leader's name at top of edit drop-down; Members List; etc (#9117)

* fix text describing location of subscription/gem gift box

* disable Copy As To-Do in Tavern, guilds, party because it's not working

* change members label on group pages to Member List

* remove outdated info about seeing number of Gems available to buy

* allow Danger Zone to be seen by players without local authentication

Also add an hr because the Danger Zone heading was crammed up against the button above it.

* put current group leader's name at top of Leader change drop-down

* Client Fixes (#9120)

* unduplicate logout code

* re-enable debug menu

* fix pets badge and equipping mounts

* close gift modal after sending gems

* armoire notifications

* Oct 1 fixes (#9121)

* Added default tags to task

* Added seasonal gear check and show spooky

* Disabled spooky sparkles

* Fixed challenge remove tasks modal

* Hid checklist

* Added group gems modal

* Purchase with amazon

* Added check for user health

* Added missing notification file

* 4.1.1

* 4.1.2

* Merge develop into release (#9154)

* Client: fix Apidoc and move email files (#9139)

* fix apidoc

* move emails files

* quest leader can start/end quest; admins can edit challenges/guilds; reverse chat works; remove static/videos link; etc (#9140)

* enable link to markdown info on group and challenge edit screen

* allow admin (moderators and staff) to edit challenges

* allow admin (moderators and staff) to edit guilds

Also add some unrelated TODO comments.

* allow any party member (not just leader) to start quest from party page

* allow quest owner to cancel, begin, abort quest

Previously only the party leader could see those buttons. The leader still can.

This also hides those buttons from all other party members.

* enable reverse chat in guilds and party

* remove outdated videos from press kit

* adjust various wordings

* Be consistent with capitalization of Check-In. (#9118)

* limit for inlined svg images and make home leaner by not bundling it with the rest of static pages

* sep 27 fixes (#9088)

* fix item paddings / drawer width

* expand the width of item-rows by the margin of an item

* fix hatchedPet-dialog

* fix hatching-modal

* remove min-height

* Oct 3 fixes (#9148)

* Only show level after yesterdailies modal

* Fixed zindex

* Added spcial spells to rewards column

* Added single click buy for health and armoire

* Prevented task scoring when casting a spell

* Renamed generic purchase method

* Updated nav for small screen

* Hide checklist while casting

* fix some text describing menu items (#9145)
2017-10-04 17:39:34 -05:00
Keith Holliday
7325d2020e Oct 4 fixes (#9159)
* Added gem purchase note

* Added notification count

* Added party reload

* Added description when user can no longer purchase gems this month

* Prevent modal from showing when pruchasing recently purchased items

* Added progress bar

* Prevented non leader from loading approvals

* Added group billing
2017-10-04 16:47:19 -05:00
negue
1a0d39e566 show warning to use the mobile apps (#9152) 2017-10-04 21:19:10 +02:00
Matteo Pagliazzi
dc62ab7577 Ability to collapse checklists (#9158)
* ability to collapse checklists

* typo
2017-10-04 20:35:59 +02:00
Michael Hibbs
c4e5633e48 Tags redesign in edit-task modal (#9122)
* Truncate tags list to maximum number of tasks

This commit truncates the list of tags in the edit task modal and
displays the remaining selected tasks as a number.

* Align tags-select dropdown with "Tags" label

* Add tags popup component

* Use solid purple for tags-select dropdown

* Remove shadow when tags-select is active
* Add border-radius to tags-select
* Re-add previously disabled transitions
* Remove unused template element

* Add Clear Tags button to footer of tags popup

* Decrease column size for tags to better match design
* Truncate tag name to avoid overflows
* Add tag name as title to show full name on hover

* Grow inline tags select from left to right

* Style none button
* Add spacing to streak reset button to line up with tags select

* Add top offset to tags dropdown toggle to line up with label
2017-10-04 12:33:35 -05:00
negue
d977656e96 use selectGearToPin instead of updateStore in migration-script (#9102) 2017-10-04 10:59:44 -05:00
Keith Holliday
b0a980d56e Merge develop into release (#9154)
* Client: fix Apidoc and move email files (#9139)

* fix apidoc

* move emails files

* quest leader can start/end quest; admins can edit challenges/guilds; reverse chat works; remove static/videos link; etc (#9140)

* enable link to markdown info on group and challenge edit screen

* allow admin (moderators and staff) to edit challenges

* allow admin (moderators and staff) to edit guilds

Also add some unrelated TODO comments.

* allow any party member (not just leader) to start quest from party page

* allow quest owner to cancel, begin, abort quest

Previously only the party leader could see those buttons. The leader still can.

This also hides those buttons from all other party members.

* enable reverse chat in guilds and party

* remove outdated videos from press kit

* adjust various wordings

* Be consistent with capitalization of Check-In. (#9118)

* limit for inlined svg images and make home leaner by not bundling it with the rest of static pages

* sep 27 fixes (#9088)

* fix item paddings / drawer width

* expand the width of item-rows by the margin of an item

* fix hatchedPet-dialog

* fix hatching-modal

* remove min-height

* Oct 3 fixes (#9148)

* Only show level after yesterdailies modal

* Fixed zindex

* Added spcial spells to rewards column

* Added single click buy for health and armoire

* Prevented task scoring when casting a spell

* Renamed generic purchase method

* Updated nav for small screen

* Hide checklist while casting

* fix some text describing menu items (#9145)
2017-10-03 21:15:00 -05:00
Alys
3d75c99f8d fix some text describing menu items (#9145) 2017-10-04 09:24:44 +10:00
Keith Holliday
0aa16d7021 Oct 3 fixes (#9148)
* Only show level after yesterdailies modal

* Fixed zindex

* Added spcial spells to rewards column

* Added single click buy for health and armoire

* Prevented task scoring when casting a spell

* Renamed generic purchase method

* Updated nav for small screen

* Hide checklist while casting
2017-10-03 16:33:01 -05:00
negue
4fa3046104 sep 27 fixes (#9088)
* fix item paddings / drawer width

* expand the width of item-rows by the margin of an item

* fix hatchedPet-dialog

* fix hatching-modal

* remove min-height
2017-10-03 21:44:45 +02:00
Matteo Pagliazzi
fda4be01b8 limit for inlined svg images and make home leaner by not bundling it with the rest of static pages 2017-10-03 17:31:53 +02:00
Brian Murray
67436fbef1 Be consistent with capitalization of Check-In. (#9118) 2017-10-04 00:25:58 +10:00
Alys
ffb92a5faa quest leader can start/end quest; admins can edit challenges/guilds; reverse chat works; remove static/videos link; etc (#9140)
* enable link to markdown info on group and challenge edit screen

* allow admin (moderators and staff) to edit challenges

* allow admin (moderators and staff) to edit guilds

Also add some unrelated TODO comments.

* allow any party member (not just leader) to start quest from party page

* allow quest owner to cancel, begin, abort quest

Previously only the party leader could see those buttons. The leader still can.

This also hides those buttons from all other party members.

* enable reverse chat in guilds and party

* remove outdated videos from press kit

* adjust various wordings
2017-10-04 00:19:49 +10:00
Matteo Pagliazzi
4a44d60fac Client: fix Apidoc and move email files (#9139)
* fix apidoc

* move emails files
2017-10-03 15:34:12 +02:00
Sabe Jones
122cc510d3 4.1.2 2017-10-03 01:44:30 +00:00
Sabe Jones
29a7a07d14 Merge branch 'develop' into release 2017-10-03 01:39:53 +00:00
Sabe Jones
18783aefe3 chore(i18n): update locales 2017-10-03 01:38:52 +00:00
Sabe Jones
a5e242759c chore(news): Bailey 20171002 (#9131)
Extend subscriber item and Farm Friends duration
Parse Markdown in achievement challenges list
2017-10-02 20:32:13 -05:00
Keith Holliday
e01c6cc9a6 Added buy special support to buy route (#9130) 2017-10-02 20:31:06 -05:00
Keith Holliday
8a75383c43 Added spooky sparkles to features (#9129) 2017-10-02 19:46:21 -05:00
Matteo Pagliazzi
47ebee9ae8 Client Fixes Oct 1 (#9126)
* fix exp notifications for armoire

* open the profile modal only if data is available
2017-10-02 23:01:27 +02:00
Keith Holliday
4e97355110 Oct 2 fixes (#9124)
* Prevented filters from closing when editing tags

* Fixed group plan upgrade

* Add check for class change

* Fixed scrolling stopping on bailey

* Added server side results for armoire

* Added persistend equipment drawer toggle

* Updated reactivity in amazon payments
2017-10-02 12:51:17 -05:00
Matteo Pagliazzi
7045b5c214 update notifications lib 2017-10-02 09:26:41 +02:00
Sabe Jones
e41dccf6d5 4.1.1 2017-10-02 02:01:31 +00:00
Sabe Jones
d0815ea9ee Merge branch 'develop' into release 2017-10-02 01:51:08 +00:00
Sabe Jones
d0bd62bf02 chore(i18n): update locales 2017-10-02 01:49:12 +00:00
Keith Holliday
39d7581c6c Merge Develop onto Release (#9123)
* Some random quick (#9111)

* Switch group button directions

* Allowed admins to export challenges

* Added scoping to some stable styles

* Fixed challenge cloning

* Tasks tags (#9112)

* Added auto apply and exit

* Add challenge tag editing

* Fixed lint

* Skill fixes (#9113)

* Added local storage setting for spell drawer

* Added new spell styles

* Fixed typo

* Reset local creds if access is denied (#9114)

* various fixes: group leader's name at top of edit drop-down; Members List; etc (#9117)

* fix text describing location of subscription/gem gift box

* disable Copy As To-Do in Tavern, guilds, party because it's not working

* change members label on group pages to Member List

* remove outdated info about seeing number of Gems available to buy

* allow Danger Zone to be seen by players without local authentication

Also add an hr because the Danger Zone heading was crammed up against the button above it.

* put current group leader's name at top of Leader change drop-down

* Client Fixes (#9120)

* unduplicate logout code

* re-enable debug menu

* fix pets badge and equipping mounts

* close gift modal after sending gems

* armoire notifications

* Oct 1 fixes (#9121)

* Added default tags to task

* Added seasonal gear check and show spooky

* Disabled spooky sparkles

* Fixed challenge remove tasks modal

* Hid checklist

* Added group gems modal

* Purchase with amazon

* Added check for user health

* Added missing notification file
2017-10-01 20:42:02 -05:00
Keith Holliday
fdf146dd01 Merge branch 'release' into develop 2017-10-01 19:46:57 -05:00
thehollidayinn
023b3ffd88 Added missing notification file 2017-10-01 19:45:21 -05:00
Keith Holliday
f01e552637 Oct 1 fixes (#9121)
* Added default tags to task

* Added seasonal gear check and show spooky

* Disabled spooky sparkles

* Fixed challenge remove tasks modal

* Hid checklist

* Added group gems modal

* Purchase with amazon

* Added check for user health
2017-10-01 19:17:02 -05:00
Matteo Pagliazzi
9f11820a02 Client Fixes (#9120)
* unduplicate logout code

* re-enable debug menu

* fix pets badge and equipping mounts

* close gift modal after sending gems

* armoire notifications
2017-10-01 23:25:32 +02:00
Alys
ca6c7b8e5f various fixes: group leader's name at top of edit drop-down; Members List; etc (#9117)
* fix text describing location of subscription/gem gift box

* disable Copy As To-Do in Tavern, guilds, party because it's not working

* change members label on group pages to Member List

* remove outdated info about seeing number of Gems available to buy

* allow Danger Zone to be seen by players without local authentication

Also add an hr because the Danger Zone heading was crammed up against the button above it.

* put current group leader's name at top of Leader change drop-down
2017-10-01 22:15:44 +10:00
Keith Holliday
02f8ba1638 4.1.0 (#9116) 2017-09-30 23:59:52 -05:00
Keith Holliday
8eb7c67f12 Merge develop to release (#9115)
* Some random quick (#9111)

* Switch group button directions

* Allowed admins to export challenges

* Added scoping to some stable styles

* Fixed challenge cloning

* Tasks tags (#9112)

* Added auto apply and exit

* Add challenge tag editing

* Fixed lint

* Skill fixes (#9113)

* Added local storage setting for spell drawer

* Added new spell styles

* Fixed typo

* Reset local creds if access is denied (#9114)
2017-09-30 23:54:12 -05:00
Keith Holliday
1f895fda44 Reset local creds if access is denied (#9114) 2017-09-30 23:15:24 -05:00
Keith Holliday
e87c180e9b Skill fixes (#9113)
* Added local storage setting for spell drawer

* Added new spell styles

* Fixed typo
2017-09-30 23:04:18 -05:00
Keith Holliday
dbf9fd54be Tasks tags (#9112)
* Added auto apply and exit

* Add challenge tag editing

* Fixed lint
2017-09-30 21:49:00 -05:00
Keith Holliday
c9b99d1ecf Some random quick (#9111)
* Switch group button directions

* Allowed admins to export challenges

* Added scoping to some stable styles

* Fixed challenge cloning
2017-09-30 17:54:27 -05:00
Sabe Jones
fd8120c80d 4.0.9 2017-09-30 17:38:49 +00:00
Sabe Jones
053e75562f Merge branch 'develop' into release 2017-09-30 17:38:34 +00:00
Sabe Jones
1d50027f51 fix(challenges): Markdown titles 2017-09-30 17:10:57 +00:00
Sabe Jones
7fe74fd06a Serve static news page for mobile (#9109)
* fix(news): serve static page for mobile

* fix(news): commit new component

* refactor(news): computed prop DRY
2017-09-30 10:48:24 -05:00
Sabe Jones
173a8561b6 4.0.8 2017-09-30 13:37:29 +00:00
Sabe Jones
f21bef707b Merge branch 'develop' into release 2017-09-30 13:36:40 +00:00
Keith Holliday
9cf2ccf7c4 Style fixes (#9108)
* fixed some columns on smaller screens

* changed mystic hourglass check

* Checked for bad data

* Groupped pets and mounts

* Added show more

* Fixed lint

* Fixed lint
2017-09-30 08:34:25 -05:00
Keith Holliday
77d75c4669 Pending quest styles (#9106)
* Added pending quest modal

* Added quest completed modal

* Removed duplicate string
2017-09-30 08:28:39 -05:00
thehollidayinn
1c17b95146 4.0.7 2017-09-29 20:18:00 -05:00
Trevor Ford
d89fc209d1 fix allocate() function in (#9105)
website/client/components/userMenu/profile.vue
2017-09-29 18:48:24 -05:00
Sabe Jones
844c8bb3e6 4.0.6 2017-09-29 22:08:23 +00:00
Sabe Jones
569fb11db8 Merge branch 'develop' into release 2017-09-29 22:08:11 +00:00
Keith Holliday
7671347d3a Chat avatar fixes (#9103)
* Ensured rejection doesn't hurt performance

* Added debounce and scroll removal

* Added debounce for keydown

* Fixed linting
2017-09-29 16:03:43 -05:00
Alys
dc3a02bc2e clarify change class code and remove incorrect confirmation step (#9099)
- prevent the user seeing a confirmation about paying 3 gems when enabling the class system (which is free)
- rename changeClass function to changeClassForUser to avoid confusion with changeClass function
- make change class confirmation translatable
- changed "gems" to "Gems" in locales string
2017-09-29 15:57:05 -05:00
Alys
1d8c126687 hide the Block button in the user's own profile screen (#9098)
This prevents a user from accidentally blocking themself from sending PMs.
2017-09-29 15:56:54 -05:00
Alys
7ee49a43f2 Various fixes: mods can delete any message; no Report / CopyAsTodo button in inbox; etc (#9091)
* fix link to Data Display Tool

* prevent the Copy As To-Do button appearing in the inbox because it isn't working

* allow mods/staff to delete any chat message

* prevent the Flag/Report button appearing in the inbox because it isn't working
2017-09-29 15:56:44 -05:00
Sabe Jones
900bc8dfc1 Merge branch 'develop' into release 2017-09-29 20:34:52 +00:00
Keith Holliday
ec260016d3 Task sort payment fixes (#9104)
* Added sorting to dated filter

* Validated payment data type
2017-09-29 15:30:28 -05:00
Sabe Jones
ec770fb29e 4.0.5 2017-09-29 16:19:02 +00:00
Matteo Pagliazzi
c1079e4eae Client Tasks Length (#9101)
* change tasks column height

* stack columns on smaller screens
2017-09-29 18:17:58 +02:00
Sabe Jones
3cb5637fd5 Merge branch 'release' into develop 2017-09-29 15:21:10 +00:00
Matteo Pagliazzi
cf3a0118c9 fix typo 2017-09-29 12:39:18 +02:00
Matteo Pagliazzi
895a383089 cache what is cacheable (because hashed) in the new client, should fix the broken navigation while using the site before and after a deploy 2017-09-29 12:37:03 +02:00
Matteo Pagliazzi
f730e7b345 fix password reset (setting a new one) 2017-09-29 12:15:31 +02:00
Alys
d8f3d99d59 fix footer link to Report a Bug guild 2017-09-29 18:57:33 +10:00
Sabe Jones
99fb1f6116 4.0.4 2017-09-29 01:56:18 +00:00
Alys
7c6dce2124 change link in Bailey from G docs to wiki 2017-09-29 11:51:50 +10:00
Sabe Jones
c757a3f52d 4.0.3 2017-09-28 23:50:44 +00:00
Sabe Jones
ffb318fe8d fix(news): sprite path 2017-09-28 23:50:29 +00:00
Sabe Jones
19cd15ed62 4.0.2 2017-09-28 23:37:16 +00:00
Sabe Jones
3495662196 fix(redesign): Bailey and debug 2017-09-28 23:37:00 +00:00
SabreCat
908a1340a4 4.0.1 2017-09-28 22:59:07 +00:00
Keith Holliday
31f4610b20 Sabrecat/redesign launch bailey (#9094)
* chore(news): Bailey

* chore(news): Bailey on old client

* Fixed seasonal images

* Fixed footer and images for bailey
2017-09-28 15:36:48 -05:00
Sabe Jones
5eac2a6697 4.0.0 2017-09-28 17:50:11 +00:00
Sabe Jones
4495539e8c Merge branch 'develop' into release 2017-09-28 17:39:35 +00:00
Alys
42e6f10b08 remove non-working difficulty tooltip (#9092) 2017-09-28 12:19:29 -05:00
Matteo Pagliazzi
66750e77d1 Client 404 Page (#9093)
* fix z-index of footer

* 404 page

* fixes
2017-09-28 12:19:15 -05:00
SabreCat
1eb31a4fec Merge branch 'develop' into release 2017-09-28 16:02:47 +00:00
SabreCat
fdf2dd1f1a chore(guilds): migration to rearrange fields 2017-09-28 15:24:59 +00:00
SabreCat
f098fbcc80 chore(sprites): compile 2017-09-28 14:40:53 +00:00
Sabe Jones
5c429d0328 chore(i18n): update locales 2017-09-28 01:07:43 +00:00
Sabe Jones
08a84ce13d Redesign: Veteran Pet migration (#9090)
* feat(migration): add Veteran pets
Also refactor pinned items query to fetch less data

* refactor(migration): rename/move, not duplicate
2017-09-27 19:47:04 -05:00
negue
f4bf2df4a9 fortify potion + fix pinning check (#9089) 2017-09-27 18:06:07 -05:00
negue
ee6ceecc35 check pinning for unknow item type and path (#9087) 2017-09-27 23:25:34 +02:00
negue
76c3e51660 sep 25 fixes (#9075)
* on class change: remove owned gear from pinned items

* move dragging item-info closer to the cursor (food / hatching potion)

* remove v-once at attributesPopover (if the parent component is recycled the old data stays active)

* prevent popover to show data of an item that was removed - but the popover stays opened
2017-09-27 21:07:42 +02:00
Matteo Pagliazzi
d40543f4ca Paglias Client Fixes (#9086)
* fix login background on bigger screens

* redirect to correct page after login / signup

* fix mountains in background
2017-09-27 20:59:49 +02:00
Matteo Pagliazzi
e1ad19c216 Client: Port Reset Password Page (#9057)
* start work on porting the reset password page

* add new api route for setting a new password after a reset

* wip client page

* port tests

* wip

* fix linting

* skip tests
2017-09-27 18:57:52 +02:00
Alys
a03c6184b3 fix link to Data Display Tool (#9085) 2017-09-27 22:24:57 +10:00
Keith Holliday
526d0b1a23 Cloned correctly (#9084)
Fixed cloning values during fix custom values
2017-09-26 23:02:58 -05:00
Sabe Jones
f523bd424f 3.116.1 2017-09-27 00:18:34 +00:00
Sabe Jones
eda76efd28 fix(hooks): remove default tasks from content instead 2017-09-26 23:29:39 +00:00
Sabe Jones
08323f307c fix(new-client): display tweaks (#9080) 2017-09-26 18:24:57 -05:00
Sabe Jones
1c301f8328 fix(test): update expectations on mobile tasks 2017-09-26 22:45:20 +00:00
Sabe Jones
a515168766 fix(mobile): let mobile reg handle default tasks and tags 2017-09-26 21:40:06 +00:00
Keith Holliday
927a08defd Small payment fixes (#9082)
* Fixed some parameter passing and computed placements

* reset edit when user change

* Lint fix
2017-09-26 16:05:54 -05:00
Sabe Jones
c8d5eb9689 3.116.0 2017-09-26 20:58:49 +00:00
SabreCat
ec298291d4 feat(event): Fall Hatching Potions
and important announcements
2017-09-26 20:55:12 +00:00
Keith Holliday
356f2c7b7f Sept 26 fixes (#9081)
* Fixed layering of avatar

* Fixed navbar padding on small screens

* Dropdowns go left

* Adjusted member details styles

* Fixed task tag display

* Added toggle to buy gems

* Began moving presskit

* Fixed easing and validating immediately

* Added hover state to login and fixed transitions for social buttons

* Fixed more style issues

* Fixed overflow

* Added email warning

* Show login button on mobile

* Some column adjustments for mobile

* Fixed email/username confusion
2017-09-26 15:04:29 -05:00
Matteo Pagliazzi
b12bf773f1 Payments Fixes (#9079)
* fix stripe checkout

* fix amazon pay on firefox

* add comment
2017-09-26 19:30:04 +02:00
Alys
d70ff4e5a3 new client various fixes: all users now see challenge progress, changes to create strings, etc (#9078)
* adjust how User > Profile is referred to

* change "Create" to "Add Task" in a Challenge's page

* remove commented-out code for making a Group Plan cost Gems (there should be no Gem cost)

* split 'create' locales string into multiple strings for different purposes

- Allows text to be changed individually for each purpose.
- Assists translators in determining the best translation.
- Makes it easier to find locations in code for each string.

* remove "public" option for Group Plan creation modal

* allow all users to see challenge participant progress
2017-09-26 22:44:49 +10:00
Alys
a68b02d403 new client various fixes: flagging; non-Public Challenges can have 0 gem prize; etc (#9072)
* allow a user (admin or non-admin) to flag their own message

Initially implemented in https://github.com/HabitRPG/habitica/issues/8005

Use case for a non-admin needing to flag their own message:
- a new user posts a question
- a socialite answers
- the user posts again and reveals that they're under 13
- the socialite flags all of the user's messages and all replies to the user including the socialite's own reply (it's a requirement that all messages to and from under-aged users be hidden and some socialites know this)

* change NPC message for Tavern: "Rest in the Inn" > "Pause Damage"

* prevent a chat message being hidden until it's been flagged more than once

* allow non-Tavern (non-"Public") challenges to have zero gem prize

Also changes message about gem prize to say "Public Challenges" instead of "Tavern Challenges"

* finish committing merge conflict
2017-09-26 09:33:46 +10:00
Keith Holliday
2db5ab2352 Sept 25 fixes (#9076)
* Fixed amazon config

* Added breakpoint for fixed footer

* Fixed some minor sorting bugs
2017-09-25 17:26:23 -05:00
negue
2a43df34c0 multiple fixes sep 24 (#9073)
* pin purchase-gems + orb of rebirth

* show questInfo in items inventory and reward column

* un-equip gear using the star / clicking on the drawer
2017-09-25 20:12:49 +02:00
Keith Holliday
a317b351be Sept 23 fixes (#9074)
* Discover challenges

* Fixed hero loading

* Moved add task button

* Fixed bailey showing

* Added logs for bad sub data

* Fixed blurb editing

* Added confirmation for deleteing message

* Reset invite modals on invite

* fixed group member sorting

* Fixed chat time styles

* Fixed hover on liked

* Fixed like count

* Added reverse

* Fixed editing party

* Added leader conditions

* Added search

* Added loading

* Reset members when leaving party

* Rounded pending

* Fixed overflow on collecting quests

* Added to invite friends

* Hid summary from party

* Fixed button styles

* Fixed button class

* Removed okay button

* Fixed renav for profile modal

* Added subscription back to menu

* Fixed static link

* Added daily due setting

* Added local auth adding

* Fixed centering of text

* Removed message locally

* Added count for new message

* Added style fix for profile pet

* Fixed achievement popovers

* Fixed white boxes

* Added plain color backgrounds

* fixed challenge mutability

* Fixed challenge editing

* Added notation for large numbers

* Add color text to guild sizes

* Removed membership filters from discover challenges

* Added invites to group

* Cmd + enter send message

* Made leader clickable

* Updated group validation

* Added cancelling autocomplete

* Added mention icon

* Added removing member

* Removed extra string
2017-09-25 13:02:12 -05:00
negue
4759764e61 NPC Flavors (#9066)
* use scss variables to chose the seasonal images instead of replacing them + added fall festival npc images

* hide spooky sparkles until needed
2017-09-24 22:10:54 +02:00
Alys
5aea8def3b new client various fixes: Hall of Heroes is visible, Sort gear by Perception, etc (#9070)
* adjust wording and time format for Custom Day Start time confirmation

- explains that this is the EARLIEST possible time for cron
- allows user to see the exact earliest time

* remove unnecessary hyphen from login date in Profile; added whitespace after colons

* allow Hall of Heroes to be seen by non-admin users (except for admin interface)

Also removes a level of code indentation that seemed to be incorrect.

* clarify that Orb of Rebirth costs 0 gems after level 100

Previously, the modal showed just a gem icon with no number, which was misleading.
Now it shows explicitly 0 gems.

* fix spelling mistake (Challeges > Challenges)

* allow equipment to be sorted by Perception

Also change Con > CON, Per > PER, etc for consistency with abbreviations used in other locations.
2017-09-24 20:06:17 +10:00
Sabe Jones
dde63b619f Redesign: Fixes 09/21 (#9062)
* fix(notifications): click to clear

* fix(chat): no italic/gray typing in Tavern

* fix(tutorial): tighter Justin modal

* fix(modal): revert changes to avatar editing

* fix(avatar): let edit modal be big
2017-09-22 22:27:00 -05:00
Keith Holliday
cbdb0bc3e3 Sept 22 2 (#9067)
* Added show member modal when hero is clicked

* Added drag for checklist and fixed display system messages

* Fixed party modal showing from group page

* Delete new message key

* Adjusted column sizes for chat

* Fixed key
2017-09-22 19:30:37 -05:00
Keith Holliday
6edd1a1fa5 Sept 22 fixes (#9065)
* Removed lingering checklist

* Added another party data check

* Added move cursor on hover

* Removed task locally

* Prevented user from being able to delete an active challenge task

* Reset tasks when viewing member progress

* Prevented challenge owners from adding checklists

* Hide challenges columns with no tasks

* Add error translations

* Added markdown to challenge description

* Allowed leader to rejoin challenge

* Replaced description with summary

* Fixed delete logic

* Added author

* Added loading message

* Added load more

* Added default sub

* Fixed remove all

* Added lint
2017-09-22 16:47:16 -05:00
negue
6fcf739c89 sep 21 fixes (#9060)
* remove `classSelected` on adding new pinned gear

* use show::modal on buyModal / selectMembersModal instead of v-if=item

* show::modal for buyQuestModal - fix bundles class

* fix buy potion

* fix open selectMembersModal (after changing to shop::modal)
2017-09-22 13:34:00 +02:00
Matteo Pagliazzi
f128f3d3cd Client Fixes (#9064)
* fix loading of payments scripts

* improve loading times
2017-09-22 13:29:08 +02:00
Keith Holliday
744090e652 Sept 21 fixes (#9061)
* Fixed analytics when party is not defined:

* Waited to set Stripe

* Added popovers to danger zone

* Added date format to next cron

* Redirect user back to home for static

* Removed old settings

* Fixed show bailey

* Fixed close button on inbox

* Fixed sending blank message

* Changed input to text area in inbox

* Fixed deleting messages in inbox

* Allowed user to delete other user message in inbox

* Fixed joined date

* Fixed count styling

* Fixed display of pet and mount

* Removed white box when using base_0 items

* Fixed your profile to show when clicking from menu

* Prevented user from pinning already purchased backgroud

* Added user last log in

* Added date refresh for yesterdailies modal

* Updatd eyewear

* Fixed start a quest button

* Fixed styles on home page footer

* Added checklist sorting

* Added support for if card type is missing computed field

* Fixed linting
2017-09-21 18:53:45 -05:00
Sabe Jones
9c3b367b29 3.115.0 2017-09-21 18:44:14 +00:00
Sabe Jones
90451fbec8 chore(i18n): update locales 2017-09-21 18:42:09 +00:00
SabreCat
64addcf847 fix(news): add'l art credit 2017-09-21 18:34:17 +00:00
Sabe Jones
5263b5e42f Fall Festival 2017 (#9058)
* feat(event): Fall Festival 2017

* chore(sprites): compile

* fix(sanity): remove duplicate locale file
2017-09-21 13:33:43 -05:00
Matteo Pagliazzi
81fc727d41 Client Fixes (#9056)
* remove console.log calls used for debugging

* open member modal when clicking on user name or avatar in chat

* misc fixes to strings, payments, ...

* simplify code

* secure statsComputed
2017-09-21 19:49:27 +02:00
Matteo Pagliazzi
7a74d4c296 Client: update urls (#9025)
* start to update urls

* ability to unsubscribe from emails
2017-09-21 16:00:52 +02:00
Matteo Pagliazzi
b6a5efc524 client: add logging around bug 2017-09-21 15:43:35 +02:00
Keith Holliday
1e1220d0f9 Sept 20 fixes 2 (#9055)
* Added cards reading

* Updated nav items

* Replaced logos with svgs

* Updated mobile preview image
2017-09-20 19:35:03 -05:00
negue
0a691ffb4f [WIP] new client - seasonal-shop (#9018)
* extract seasonal-shop config - use summer season items (to work on)

* add suggested border to shopItems

* refactor getOfficialPinnedItems (now includes the seasonal gear)

* refactor shops.getSeasonalShop - add featured items to result - add the set to special equipment items

* feat(content): Fall 2017 seasonal gear
Also adds set keys for all prior seasonal gear.

* show item limited time (buyModal & shopItem)

* select seasonal fall sets

* WIP(seasonal-shop): placeholder Fall 2017 items

* fix lint

* sprites

* styling + fix purchase of seasonal spells

* compile sprites

* fixes: check isPinned with officialItems

* enable purchase of seasonal items for testing

* fix shop apis

* add featuredItems to market

* quest shop: add featuredItems to api

* tiem travelers shop: add featuredItems to api

* fix gear types filter

* feat(content): Fall 2017 compleat

* chore(sprites): compile

* show opened shop state (npc+background)

* add opened seasonal npc

* current seasonal users class set = purchase by gold - lock other sets of the current season

* hide event badge in seasonal shop - dot only for suggested items - cursor: pointer on shopItems

* refresh rewards column list (seasonal gear won't refresh it on purchase)

* fix duplicate seasonal gear -> remove special items from the old reward gear (which is used to reset the pinned gears)

* every current season gear is purchased by gold - prevent buyModal on locked items

* use the current event date range

* list seasonal sets by event date

* use custom method instead of updateStore to list the pinnable gear

* change daterange to 10-31

* fix start quest modal from items - disable invite quest button if a quest is already active

* toggle pin in buy-dialogs

* check if the item is not undefined/null - renamed the watch function
2017-09-20 19:28:11 -05:00
Matteo Pagliazzi
cd0222e208 fix: client: allow tags selection for user tasks 2017-09-20 21:47:19 +02:00
Keith Holliday
7812e14898 Sept 20 fixes (#9054)
* Fixed start quest from items

* More style fixes

* Changed gem modal to 20

* Removed unused image

* Added initial header styles for home page
2017-09-20 09:56:26 -05:00
Matteo Pagliazzi
c0f159a8a5 Client Fixes (#9050)
* reset yesterdailies array after cron has run

* start to fix gifting

* remove unused code, move encodegift

* wip

* fix styles and bugs

* fix error if amazon is slow to load its script

* fix error notifications not showing up
2017-09-20 15:04:35 +02:00
Keith Holliday
dd0c95f051 Fixed selecting and sending messages (#9052) 2017-09-19 18:05:30 -05:00
SabreCat
136947169b chore(sprites): redesign updates 2017-09-19 21:06:58 +00:00
SabreCat
1379140cf6 Merge branch 'release' into develop 2017-09-19 21:00:19 +00:00
negue
2cb9228a7c misc fixes - sep 17 (#9045)
* not locking pre-owned gear - pin removed gear on revive

* fix createAnimal (check for function) - hide pet-popover while dragging food

* fix dragging popover text color

* fix drop toasts

* selectMembersModal: load members on item change
2017-09-19 22:45:28 +02:00
Keith Holliday
b1652ddd97 Sept 18 fixes (#9051)
* Added hover state to buy buttons

* Translated profile

* Fixed sending private message from member modal

* Added payment functions

* Added translation to home page

* Fixed translation

* Some front page styles

* Fixed inbox sorting and searching

* Added seasonals

* Fixed buy gem modal conflict

* Fixed paypal link

* Fixed footer style crossover

* Fixed quest update

* Fixed sanity
2017-09-19 15:35:32 -05:00
Alys
0a69c7a08d allow customised text and translations for notifications for gaining/losing gold/xp/mana/health (#9049) 2017-09-19 21:44:45 +10:00
Alys
e784ae21ea new client 2017 09 19 various fixes: subscriptions, streaks, etc (#9047)
* remove excess brace on settings > subscription screen

* prevent User icon > Settings > Subscription page from crashing when subscription has termination date

* stop subscription modal from pitching subscription to a subscriber

* change placeholder text from Username to Login Name for consistency with other login/register forms

* fix test for POST-tasks_user -- streak and dateCompleted weren't being tested correctly

* prevent tag selector from appearing in edit screen for a challenge's own tasks

* restore Restore Streak when editing a user's Dailies (including their own copies of Challenge and Group Plan Dailies)

* remove failing streak test
2017-09-19 16:39:39 +10:00
Sabe Jones
32fa49191e fix(sprites): put back pixel paw (#9046) 2017-09-18 18:05:13 -05:00
Keith Holliday
ccc862f82a Sept 15 fixes (#9044)
* Adjusted styles and added loading for private messages

* Allowed for assigning during group task creation

* Fixed white logos and start overflow

* Added state styles to home

* Fixed position

* Updated avatar purchasing
2017-09-18 11:54:25 -05:00
Matteo Pagliazzi
ff92f14a5b remove old console.logs 2017-09-17 22:08:30 +02:00
negue
0b0baf2195 new client misc fixes (#9033)
* show quests and open quest dialog on click

* extract questDialogContent/Drops to separate components & use those in startQuestModal & buyQuestModal

* fix market search

* remove & readd pinned gear on revive

* remove listener once destroyed
2017-09-16 23:09:31 +02:00
Alys
8ccec0ed9d New client 2017 09 16 - Fox_town, adjust URLs in links, etc (#9042)
New client 2017 09 16 - Fox_town, adjust URLs in links, etc
2017-09-16 22:19:07 +10:00
Sabe Jones
46b42c8441 Redesign: More Sprites (#9040)
* fix(sprites): various cleanup

* chore(sprites): compile

* fix(sprites): more resizing
2017-09-15 18:04:50 -05:00
Sabe Jones
381f652c08 Redesign: Static Pages Wrapper (#9027)
* refactor(static-pages): wrapper

* refactor(statics): use router-view
2017-09-15 13:59:15 -05:00
Sabe Jones
5c5e117da0 Redesign: Subscribe/Buy Gems modal style (#9038)
* feat(gems): User menu Gems prompt

* feat(purchases): Gems modal

* feat(purchasing): Subscription side of modal

* fix(modals): analytics and dropdown styling
2017-09-15 13:11:58 -05:00
Michael Hibbs
79b3b26ab2 [client fix] Level-100 stats message (#9039)
* Fix formatting for level 100 cap message

* Fix position for attribute points counter
* Fix text overflow in points box by increasing column width from 8 to 12

* Fix tests: Add userV to expected response in response middleware
2017-09-15 15:33:05 +02:00
Matteo Pagliazzi
89f8f047ae fix api unit tests 2017-09-15 12:33:36 +02:00
Keith Holliday
a5dfb499b3 Sept 14 2017 (#9037)
* Added new message count

* Added route change for group plan

* Added fix for quest value of 0

* Fixed guild summary limit, guild and challenge height

* Added task sorting

* Added api buy armoire

* Fixed linting
2017-09-14 14:53:27 -05:00
Matteo Pagliazzi
5b1530b216 remove console .log 2017-09-14 18:57:31 +02:00
Matteo Pagliazzi
c6881c5e30 More Client Fixes (#9036)
* add automatic user syncing when user._v does not match with server

* fix google signup

* fixes to user sync

* check for next cron on activity

* add comment
2017-09-14 18:55:17 +02:00
Keith Holliday
2f913666cd Sept 13 fixes (#9035)
* Fixed tavern message sending

* Added markdown to sumary

* Added color to inputs

* Added new breakpoint

* Added canown

* Added styles for tutorial popups

* Added background image to home page header
2017-09-13 13:31:23 -05:00
Sabe Jones
5ba3e3ce5b Merge branch 'release' into develop 2017-09-12 20:34:03 +00:00
Keith Holliday
e29c393629 Client sept 12 (#9032)
* Removed load all members from challengs

* Fixed chalenge member modal loading

* Fixed party setting

* Fixed chat showing

* Fixed armoire drops

* Ignored lint issue

* Fixed buying old armoir items

* Fixed hatching

* Fixed pending damage
2017-09-12 12:56:06 -05:00
SabreCat
0c6540e6d8 Merge branch 'release' into develop 2017-09-12 16:57:30 +00:00
Matteo Pagliazzi
3c2cad43d0 fix linting 2017-09-12 18:53:49 +02:00
Matteo Pagliazzi
de67c130fa Client Fixes Sept 11th (#9030)
* fix issues with loading screen on static pages

* another try at timezone issue

* interval -> timeout

* remove references to user.fns
2017-09-12 18:39:33 +02:00
Keith Holliday
55e62cdc79 Sept 8 fixes (#9028)
* Added task sync after joining challenge

* Added gem purchasing

* Updated member modal style

* Added community guidelines to all groups

* Added group plans redirect

* Began add new front page design

* Fixed challenge loading on mount

* Fixed upgrade

* Added default summary

* Fixed small nav bar styles

* Added more unlock options to avatar editor

* Added more home page finishes
2017-09-11 23:00:34 -05:00
Matteo Pagliazzi
eee41142b1 Client Fixes (#9026)
* loading screen

* debug yesterdailies modal
2017-09-12 01:06:14 +02:00
Matteo Pagliazzi
d2832b7169 fix typo in chat component 2017-09-10 23:55:35 +02:00
Matteo Pagliazzi
daca2c7fff fix store tests (analytics created store before it existed) 2017-09-09 15:05:26 +02:00
Matteo Pagliazzi
861b78ce8a remove old code 2017-09-08 21:26:01 +02:00
Matteo Pagliazzi
1dc1923d7b Client Analytics (#9023)
* start to refactor analytics and some mixins

* wip

* wip

* wip

* more analytics

* more analytics

* more anlytics

* fix analytics module

* finish analytics

* fix env

* vue casing
2017-09-08 21:23:58 +02:00
Keith Holliday
6e70f27bc6 Removed guidelines testing (#9024) 2017-09-07 15:51:02 -06:00
Matteo Pagliazzi
ad5decc285 fix unit tests 2017-09-07 21:35:09 +02:00
Keith Holliday
3e1c128600 Sept 7 fixes (#9022)
* Fixed login incentives header

* Added achievement hover

* Removed grassy background from editing modal

* Fixed loading of other user equipment

* Prevented non admins from using habitica official

* Fixed challenge loading and leader changing on group reload

* Added community guidlines link

* Added challenge cloning

* Fixed heroes editing
2017-09-07 13:26:53 -06:00
Matteo Pagliazzi
d9817be8f3 remove unused param 2017-09-07 14:35:47 +02:00
Matteo Pagliazzi
e54dcd2859 sync before showing yesterdailies modal 2017-09-07 14:21:05 +02:00
Matteo Pagliazzi
5ec7815cfe Client Fixes (#9021)
* pass the timezoneoffset to the server

* implement yesterdaily modal timer

* fix typos

* todo

* task editing: checklist should not overlay the tags box

* make tags selector use two columns

* show all tags

* do not allow users to go to /login and /register if logged in
2017-09-07 14:16:39 +02:00
Keith Holliday
adeee244e3 New client sept 6 (#9020)
* Added flagging to show to admins

* Fixed filters in my guilds

* Fixed login incentives style

* Added inital rage bar

* Added categoires to get group

* Updated profile stats page

* Added stat allocation

* Updated tests to include categoires

* Fixed lint issue

* Added another lint fix
2017-09-06 17:28:52 -06:00
Sabe Jones
fe8a44b8c4 Merge branch 'release' into develop 2017-09-05 19:50:08 +00:00
Matteo Pagliazzi
2b912b354d disable adding local auth until code is fixed 2017-09-05 21:42:28 +02:00
Matteo Pagliazzi
5f65be98df remove metatag 2017-09-05 21:27:27 +02:00
Matteo Pagliazzi
56c32d9691 client: add meta tag for webmaster tools 2017-09-05 21:23:14 +02:00
Keith Holliday
36a933d0c4 Client fixed sept 4 (#9019)
* Fixed background purchasing

* Added challenge export

* Fixed is leader filter

* Fixed staff icon

* Add block to profile modal

* Added initial send gems modal

* Added modal stack

* Fixed lint issues

* Updated notification styles

* Updated level up styles

* Fixed many achievement styles

* Fixed notification navigate to same route with different param

* Added mark chat seen and remove new messages

* Added scroll to notifications

* Updated hall loading
2017-09-05 12:34:00 -06:00
negue
d051bdf2c9 [WIP] Play Sounds in the new Client (#9012)
* move audioThemes to content - add audio settings

* copy audioFiles to website/static/audio - playSounds on $emit()

* Play Sounds for Rewards, Purchasing Items / Quests

* remove shop http-api calls

* play reward sound when buying gem

* fix lint / always show mysterybox
2017-09-02 20:24:53 +02:00
Matteo Pagliazzi
0424d214c5 Client Fixed (#9017)
* fix spacing between rewards and items

* fix rewards description

* rewards cost in bold

* fix gp notifications

* fix dailies gray text

* fix cancel in task edit modal

* tags: use AND not OR for filtering

* fix tasksDefaults so that monthlies can be created correctly

* tags: usable if no task selected, saving checklist and tags saved the one being added without requiting to press enter

* remove tags from tasks when they are deleted

* fix tags removal when multiple tags are deleted and fix tags editing
2017-09-02 18:08:32 +02:00
Keith Holliday
c2aaa9b592 Fixes sept 1 (#9016)
* Added avatars to inbox

* Added ordering of inbox messages

* Fixed blurb not converting to string

* Added message to member modal

* Added quest invites

* Moved filters to server
2017-09-01 15:26:10 -06:00
SabreCat
e450e52836 chore(news): Bailey announcement 2017-09-01 20:31:34 +00:00
SabreCat
e2ecec03e8 Merge branch 'release' into develop 2017-09-01 20:25:05 +00:00
Matteo Pagliazzi
318482d3ff docker: fix npm5 installation 2017-09-01 20:22:55 +02:00
Matteo Pagliazzi
2e4f665fa5 NPM@5 (2nd try) (#9013)
* second try at using npm5

* use npm 5 in travis

* build client only in production
2017-09-01 20:21:40 +02:00
Matteo Pagliazzi
3d9738ac2f client: remove extra code 2017-09-01 15:27:53 +02:00
Matteo Pagliazzi
666cc855c1 Client: fix popovers and tooltips (#9014)
* rebuild shrinkwrap

* upgrade tooltips and popovers
2017-09-01 15:15:54 +02:00
Sabe Jones
3a059f6aca Redesign: Sabe fixes 08/31 (#9011)
* fix(modules): datepicker dist

* fix(strings): Tumblr typo

* fix(default-tasks): remove "ADD Notes"

* fix(homepage): working header
2017-08-31 18:37:09 -05:00
Keith Holliday
cdd3bc3cd6 Client fixes aug 31 (#9010)
* Separated private message model

* Added markdown to profile

* Add color backgrounds

* Added broken challenge flow

* Added summary field to getgroups

* Fixed group form information loading

* Updated autocomplete to use chat

* Fixed npc styles

* Fixed onload mentions
2017-08-31 16:14:09 -06:00
negue
87d57dab13 Backgrounds / BuyModal / misc (#9009)
* remove buyModal from all views / move to app.vue - show party member selection if card was bought

* add .btn style to 'show more' / 'show less'

* prevent adding duplicate pinned items - reset pinned gear on rebirth

* remove hourglass in items

* fix unequip gear from drawer

* fix bought item notification

* fix background in avatar - avatar position in buyModal
2017-08-31 22:56:53 +02:00
Matteo Pagliazzi
19789eb7ab Client Fixes (#9003)
* fix strings on login and register pages

* back to older styles for placeholder

* scope->scoped

* remove old css
2017-08-31 15:15:17 +02:00
Sabe Jones
395385f3e2 Redesign: Static and Modal Fixes (#9007)
* fix(static-page): Community Manager links

* feat(modals): Bailey news

* feat(footer): link to Hall of Heroes
Also makes a number of strings translatable instead of hardcoded.

* fix(notifications): simplify drop messages
2017-08-30 17:59:31 -05:00
Keith Holliday
65aabc8333 More fixes (#9006)
* Reset user when clicking profile nav

* Added editprofile

* Add scroll to conversations and scroll to bottom on messages

* Fixed profile click

* Added notification mixin

* Fixed challenge edit assignment

* Guild list item now returns summary

* Fixed tier icons

* Fixed quest invitaion check

* Fixed extra loading of party members in modals

* Removed background from avatar in tutorial

* Changed port back

* Fixed task adding
2017-08-30 15:19:17 -06:00
Sabe Jones
5c16600b25 Merge branch 'release' into develop 2017-08-30 17:44:19 +00:00
Sabe Jones
32fd4e33c8 feat(dates): Clear and Today buttons (#9005) 2017-08-30 12:15:24 -05:00
Keith Holliday
560d247c9b Miscing groups (#9004)
* Fixed party invite accepting

* Fixed message sending and scrolling

* Fixed profile clicking

* Hids needs work

* Added group plan page images

* Added category to editing groups

* Added quest box fix

* Added tier styles

* Changed port back to 8080

* Fixed npc style
2017-08-29 14:23:09 -06:00
Matteo Pagliazzi
40567fc8d0 More Client Fixes (#9002)
* logout should entirely clear localstorage

* fix logout, fix social login, remove duplicate home page
2017-08-29 16:18:26 +02:00
Keith Holliday
9fc7bae13e Quick group fixes (#9001)
* Added tavern background and fixed notification key

* Fixed notification on error response

* Fixed group plans header

* Updated images/icons in tavern
2017-08-28 18:39:32 -06:00
negue
515fd62dd8 Client/negue multiplefixes6 (#9000)
* use store action to purchase gem by gold

* only show party members in header if enough width available / use overflow hidden

* fix featuredItems buyModals

* fix special items listing - open modal on special spells

* don't open the buymodal on locked items

* fix lint
2017-08-28 18:17:45 -06:00
Matteo Pagliazzi
170f901d86 fix avatar positions in the header 2017-08-28 20:58:00 +02:00
Matteo Pagliazzi
d1fbe98379 client: fix rewards top margin 2017-08-28 20:00:06 +02:00
Matteo Pagliazzi
fb1808d85a misc client fixes (#8998)
* color selected difficulty icon

* misc fixes
2017-08-28 19:37:22 +02:00
Sabe Jones
4094ca99dd Merge branch 'release' into develop 2017-08-28 17:22:03 +00:00
Alys
fd05286e1a add header, footer, etc to various static pages for the new client (#8996) 2017-08-28 08:05:37 +10:00
Alys
9436c83919 prevent skills/spells being cast on rewards and group plan tasks (#8995) 2017-08-28 07:39:18 +10:00
Alys
4f636d3d2c new client static Overview and FAQ: wording and formatting changes (#8993)
* improve formatting for Help > Overview page

* change wording on Help > Overview page

* change FAQ text for new client
2017-08-27 14:45:30 +10:00
Sabe Jones
1a056be637 Merge branch 'release' into develop 2017-08-26 23:23:16 +00:00
Alys
f38e184434 new client: move translatable strings to appropriate locales files (#8992)
* move translatable strings from newClient.json to appropriate locales files

* whitespace changes

* whitespace changes
2017-08-26 18:53:10 +02:00
Sabe Jones
d24eb67fa2 Redesign: Static Pages (#8989)
* feat(pages): static Features

* feat(static-pages): add header

* feat(static-pages): Community Guidelines

* fix(strings): tweaks
2017-08-26 16:03:43 +02:00
negue
c35e4f5750 Items/Market/Quests/misc fixes (#8987)
* use countBadge instead of class

* generic purchase action from buyModal to handle all kind of purchases - able to purchase backgrounds - return backgrounds as flat array

* List MysteryItem & Hourglass in Inventory/Items

* add Subscribers Gem Item (purchase by gold)

* fix mysterybox

* sort unlocked gear first

* add orb of rebirth to market

* remove old quest scroll + class of the quest items

* more padding on countBadge

* use the generic purchase on quests
2017-08-26 12:18:55 +02:00
Keith Holliday
0233f7b486 More fixes (#8988)
* Links stay white on hover

* Fixed task icon color

* Disabled plus button when needed

* Fixed difficulty color

* Fixed task reward color

* Updated create styles

* Fixed group plan link

* Fixed second group test modal

* Added login incentives

* Fixed group notification clear

* Show baily correctly

* Styled armoire notification

* Fixed contributor achievement styles

* Fixed death

* Fixed drop styles

* Fixed invited friend modal

* Fixed joined challenge achievement style

* Fixed joined guild style

* Fixed level up styles

* Updated low health styles

* Fixed bailey styles

* Updated quest completed

* Added soem conditionals to hide modals

* Added rebirth styles

* Fixed rebirth enable styles

* Fixed streak styles

* Fixed testing modals

* Fixed ultimate gear achievement

* Fixed won challenge

* Set user to welcomed if created on mobile

* Removed old default tasks

* Began adding more options to avatar

* Added change class

* Inbox to messages

* Moved profile to menu

* Added user modal for viewing a user and send message

* Fixed conversations

* Fixed lint

* Fixed challenges sending to server

* Added challenge progress view

* Fixed group sync after pay

* Fixed some group accepting features

* Fixed initial chat loading

* Fixed some exitence errors

* Added user names to assigned

* Added upgrade link

* Began adding new payment flow

* Added default tasks

* Updated avatar styles

* Updated tutorial styles

* Rebuilt notifications and styles

* Updated upload script

* Fixed lint

* Added default tasks back to mobile and added updated tests

* More test fixes
2017-08-25 20:56:21 -06:00
Matteo Pagliazzi
c129c38631 Paglias/client fixes (#8990)
* correct buttons for reset modal

* correct buttons for delete account modal

* fix first party member not showing up
2017-08-25 23:54:50 +02:00
negue
748ce8a23f fix isPinned : check if pinnedItems-array exists 2017-08-24 18:49:14 +02:00
Matteo Pagliazzi
a86166742f docs(client): add a note to specify how the new client is served 2017-08-24 18:24:47 +02:00
Matteo Pagliazzi
82c912237b fix hard links for new client (#8986) 2017-08-24 18:19:31 +02:00
Matteo Pagliazzi
5c89451985 Client Fixes (#8985)
* fix typos

* tasks created immediately

* fix page scroll when cancelling task edit

* fix cheklist editing + misc fixes to tasks modal

* remove console.log
2017-08-24 16:48:52 +02:00
negue
2624b06729 [WIP] shops + market gear api (#8980)
* lock other classes gear

* fix avatar in equip-gear-modal

* fix seasonal shop

* seasonal : new gear type order

* fix pin gear (and get new gear on buying)

* API: /shops/market-gear  - refactoring pinnedGearUtils - move _isPinned to common/libs

* use shops.getMarketGearCategories to list the marketGear

* use shops.getMarketCategories instead of API-call

* mark gear reward items as locked

* purchase time-travelers stuff + update view + use method instead of http-api + add missing mammoth shop image

* Time Travelers Shop: open/closed state

* time travelers: show gear preview + hide sidebar if closed

* update resized images

* fix lint
2017-08-24 07:16:18 +02:00
Sabe Jones
7b7f5c09fd Merge branch 'release' into develop 2017-08-23 22:16:47 +00:00
Sabe Jones
ad621e7208 Redesign: Misc fixes (#8984)
* fix(checklists): parse Markdown

* fix(Guilds): Markdown in descriptions
2017-08-23 14:36:43 -05:00
Matteo Pagliazzi
034f18c419 Client Misc Fixes (#8983)
* fixes party header button

* fix some svg icons
2017-08-23 17:32:20 +02:00
Alys
7a6bf8b870 new client - various fixes: Earnable Quests removed, equipment type names and sort order changed (#8977)
* remove Earnable Quests from quests shop

Earnable Quests could be put back later but with an explanation
for how you can earn them. The Quest Shop isn't well set up for
that at the moment.

* change names of some equipment items

NOTE: The `...Capitalized` strings all have lower-case versions
(e.g., "weapon" and "weaponCapitalized"). I suspect we should
change or (perhaps better) get rid of the uncapitalized version
but I'm not sure what other effects that might have (e.g., haven't
searched for exactly where it's used; don't know what it would do
to the mobile apps). Suggest looking into that after launch. If
the old, uncapitalized strings appear elsewhere during our site
testing, we should probably change them to the Capitalized version
then, or if lowercase is essential, create new strings (e.g.,
"weaponUncapitalized" - that will be much easier later than just
"weapon" for searching for where it's used!)

* change order of Equipment types on the inventory page

http://i.imgur.com/64D5jEk.png
2017-08-23 15:17:11 +10:00
Keith Holliday
f529a5c64c New client many fixes (#8981)
* Footer style fixes

* Limited string display

* Fixed background reload

* Began adding more avatar items

* Fixed challenge updated cats and official to be seen by admins

* Fixed min prize

* Fixed required fields

* Added my challenges and find challenges to menu

* Removed nav to party page when have no party

* Updated user and notifications icon

* Added accept, reject and messages

* Added selfcare

* Underline links

* Added forgot password

* Fixed task adding

* Disabled habit options that should be

* Added markdown to tags

* Added confirm to delete

* Fixed cancel/delete style

* Fixed rounding

* Added gold icon

* Fixed task icon styles

* Fixed margin botton

* Fixed some repeat styles

* Fixed custom reward style

* Hide like count 0

* Added new tavern images

* Redirect to party page after create

* Hid leader options from non leaders

* Removed manager options from non group plan

* Fixed some nav styles

* Fixed overlay color

* Prevented edit data from being transfered to create

* Added hover state for spells

* Add performance fixes for chat avatars

* Fixed merge conflicts

* Updated fron navigation

* Fixed reg gryphon logo

* Began adding gem modal functionality

* Added purchase gems with gold

* Fixed restore

* Replaced description with summary

* Spells that target tasks fix

* Added initial challenge task load

* Fixed lint issue
2017-08-22 21:58:13 -06:00
Sabe Jones
69662f84df Choose Class modal (#8976)
* feat(modals): near-complete Choose Class modal
Also fixes avatar alignment in the Equip modal.

* feat(classes): send API requests from choose modal

* feat(classes): full functionality on choose modal

* WIP(classes): refactor class functions

* refactor(avatar): more intelligible sprites margin

* fix(imports): correct import syntax
2017-08-22 17:59:18 -05:00
Alys
7d0ab1ba25 add summary field to challenges and guilds (#8960)
* create new summary field for challenges

* finish implementating summary for challenges, add some support for guilds

* make small improvements to challenges code

* fix lint errors

* add more code to support summaries for guilds (still more needed)

* fix existing tests by adding summary field

* make existing tests pass

* WIP make "Public Challenges" text translatable

* change "leader" locale key to "guildOrPartyLeader" to make searches for it easier

* remove v-once from h2 headings

* remove failed attempt to localise text in <script>

* add quick-and-dirty error checking for guild not having categories

* make "Public Challenges" text translatable

* rename final ...PlaceHolder strings to ...Placeholder (lower-case "h") for consistency with existing Placeholder strings
2017-08-23 06:39:45 +10:00
Matteo Pagliazzi
bd46e3e195 Client: i18n (#8972)
* wip: client: i18n

* remove maxAge from cookies to get same expiration ad localStorage

* set cookies expiration to 10 years

* moment: load translations in browser, moment: only load necessary data, remove jquery, remove bluebird

* ability to change language

* fix logout

* add some requiresLogin: false to static pages

* fix tests
2017-08-22 18:26:53 +02:00
Alys
e5a92f64c0 adjust wording on new client's register and login screens (#8958) 2017-08-22 23:56:56 +10:00
Keith Holliday
798d0ab82b New client spell fixes (#8974)
* Prevented spells menu from apearing below lvl 11

* Expanding clickable casting area

* Persisted spell drawer close

* Added edit form modal to party

* Added spell toggle

* Added markdown notification

* Prevented casting on challenge and group tasks locally

* Quick fix for self cast

* Fixed lint issue
2017-08-21 23:04:56 -06:00
SabreCat
acaed1ef0e chore(sprites): compile 2017-08-21 22:59:12 +00:00
Sabe Jones
78f94e365c Redesign: Bulk Image Edits (#8975)
* refactor(sprites): recanvas quest scrolls

* refactor(sprites): recanvas gear icons

* refactor(sprites): resize reward items to 2/3 and recanvas

* feat(sprites): add icons for backgrounds
2017-08-21 17:54:07 -05:00
Alys
11379f150c new and old client: change text for skills, avatar transformation buffs, and antidotes (#8969)
* change text for skills, avatar transformation buffs, and antidotes

* minor fixes
2017-08-22 08:26:50 +10:00
Alys
41da55921c new client various fixes: Equipment shop empty; Skills not Spells; no Saddles message (#8959)
* shows message in Equipment shop when you've bought all class gear

* change Spells to Skills in visible text

* show correct message in stable when you don't have any Saddles
2017-08-21 23:45:25 +02:00
Keith Holliday
e9141ff5c9 New client flurry of fixes (#8973)
* Fixed login/reg styles and on submit

* Fixed duedate display in yesterdailiy modal

* Prevented tutorial from showing after logged in has past created

* Hide premium shirts from creating

* Fixed level up notification

* Updated categoires

* Updated justin message

* Fixed notification changes

* Fixed guild nav in join guild modal

* Added buygems modal

* Removed avatar page click

* Fixed group plan nav

* Fixed death issue

* Fixed party header styles
2017-08-21 13:28:27 -06:00
Matteo Pagliazzi
2760de7951 try to disable possible cause of too many redirects 2017-08-21 19:48:08 +02:00
Keith Holliday
203d6d3ac2 New client party tutorial (#8968)
* Adjusted intro tutorial

* Added images to create party modal

* Fixed shared button styles

* Added method to start a party

* various style fixes
2017-08-21 09:36:31 -06:00
Alys
2a2192e196 new client router changes: rename guild.vue to group.vue and GuildPage to GroupPage (#8957)
* change challenge creation button to "Add Challenge Tasks"

* router changes: rename guild.vue to group.vue and GuildPage to GroupPage
2017-08-21 08:49:47 +10:00
negue
876552b922 multiplefixes quest (#8964)
* add hourglass to header

* multiple quest fixes - show quest info in popover/modal

* pin backgrounds

* unpin gem-purchable items / change pinType of quest to quests

* check if hatching potion is allowed on egg - wide hatching info

* fix (perf): items - request text()/notes() only once

* change items margin to 23px

* list cards + open modal to choose a target + add space between market items

* buy card from task-list

* fix tests - unpin items on purchasing eggs / hatchingPotions
2017-08-21 00:32:32 +02:00
Keith Holliday
2b922508c5 New client group fixes (#8965)
* Fixed gem styles

* Fixed party challenge logic

* Fixed category sending

* Filtered habitrpg

* Fixed updating group

* Added new empty state badge

* Added information to create group

* Fixed large icons

* Fixed name collision
2017-08-18 14:29:49 -06:00
Keith Holliday
cbee0542ad New client tavern fixes (#8967)
* Load chat on sent

* Removed report own message

* Fixed some minor styles

* Removed extra mods

* Hide class badge

* Fixed chat alignment

* Fixed taven autocomplete
2017-08-18 12:30:39 -06:00
Sabe Jones
bc499bcfbf Merge branch 'release' into develop 2017-08-18 17:26:42 +00:00
Sabe Jones
861c85f099 Merge branch 'release' into develop 2017-08-17 20:40:36 +00:00
Keith Holliday
de63622cdd Challenges plus misc fixes (#8961)
* Forced full refresh after deletE

* Fixed styles on firefox

* Removed instagram link

* Added information to modal

* Fixed deleteing and task keeping

* Added redirect to challenge detail after created

* Updated challenge item styles

* Added new limit option to challenges
2017-08-17 11:03:32 -06:00
negue
f5cf27a79e [WIP] Client/multiple fixes shops (#8956)
* reposition FlyingPigs and Hydra

* hide count-badge if 0

* fix sortBy hatchable (ignore already hatched pets)

* always show animal name

* featuredItems, use shopItem objects

* fix egg / potion names in market

* buyModals: check for price, mark it if not enough available / change buy-button opacity / show purchaseGems button

* save itemRows open/collapsed state during session, refactor itemRows for some more performance

* pin featured items

* show bordered items in market buyModal

* fix popover margins / paddings

* position pinned items popovers to the left
2017-08-16 16:34:25 -06:00
SabreCat
88e6cf7d8a feat(RYA): style Yesterdailies MODAL 2017-08-16 21:56:10 +00:00
Keith Holliday
0d28e663e4 New client edit avatar (#8955)
* Fixed some purchasing issues with backgrounds

* Added more background styles

* Fixed some menu styles

* Initial old client removal

* Added cross-env

* removed bower and fixed lint

* Made interceptor errors use notify

* Removed old client tests and fixed lint
2017-08-16 15:51:48 -06:00
Alys
716695e11e Various fixes for new client (WIP) (#8953)
* sort equipment by stats: highest first

* spelling fix: excercise > exercise

*     change wording for login link on register screen

* remove incorrect Items heading from Quests Shop

*     fix missing "Recovery + Support Groups" guild category text and remove whitespace from keys

* fix text for guildInformationPlaceHolder
2017-08-16 22:03:07 +02:00
Alys
2770650340 add an Earnable Quests group for Basi-List and Dust Bunnnies (new client fix) (#8952)
* add an Earnable Quests group for Basi-List and Dust Bunnnies

This allows Basi-List and Dust Bunnies to be automatically placed
in their own group on the Quests Shop page in the new client.

* adjust name of Vice and Recidivate quest groups
2017-08-16 22:02:20 +02:00
Keith Holliday
0bff37b600 New client footer (#8954)
* Minor footer style fixes

* Added initial gem modal

* Fixed some heroe stuff.

* Preventing system member loading

* Added social delete
2017-08-16 10:05:33 -06:00
Pavel Pletenev
8614f11a31 Refactor api description with @apiParam groups to make it more explicit (#8919)
* Refactor api description for auth.js with @apiParam groups

* Refactor apiDoc toward better consistency

* Fix missing groups for get requests

* Fix missing groups for other request methods
2017-08-15 18:06:19 -05:00
Brian Murray
b27319313d Fix spelling of received. (#8937) 2017-08-15 18:02:49 -05:00
John Montgomery
1f1c7826a4 Add the relevant attribute to the Stealth tooltip (#8941) 2017-08-15 17:59:30 -05:00
Sabe Jones
d8736c17e6 Merge branch 'release' into develop 2017-08-15 22:42:35 +00:00
SabreCat
00d72fe555 fix(dropdowns): fewer bottom borders 2017-08-15 22:25:22 +00:00
SabreCat
9943a94108 fix(Stable): weird Matt wording 2017-08-15 21:18:41 +00:00
SabreCat
7a1b7b3291 fix(tags): only show trashcan on hover 2017-08-15 21:09:03 +00:00
SabreCat
832106dc86 fix(stats): show decimals between 0-1 2017-08-15 18:42:29 +00:00
Keith Holliday
24b9bd6ccc New client spells (#8950)
* Added initial spell casting

* added casting

* Added spells style

* Fixed linting issues

* Added escape

* Added casting

* Added task drag style
2017-08-15 12:28:30 -06:00
Keith Holliday
bd19b83db4 Added avatars to chat (#8949) 2017-08-14 15:44:09 -06:00
Keith Holliday
eb43f83c71 New client group plan (#8948)
* Added stripe payment for group plan

* Began adding amazon

* Added amazon payments for group

* Added get group plans route

* Added group plan nav

* Added initial task page

* Added create and edit group plans

* Added initial approval header and footer

* Added assignment and approved requirement

* Added minor text fixes

* Added inital approval flow

* Added approval modal

* Removed always true

* Added more styles for filters

* Added search

* Added env vars

* Fixed router issues

* Added env to social login

* Fixed merge conflict
2017-08-14 13:19:41 -06:00
Sabe Jones
6e89197b3f fix(stats): floor HP/MP/XP values (#8947) 2017-08-14 12:20:21 -05:00
Sabe Jones
51739a4dfe Redesign: Display weekdays in correct order (#8945)
* fix(dailies): display weekdays in correct order

* fix(style): display weekday checkboxes inline
2017-08-14 12:20:03 -05:00
negue
87f39b4273 item pinning (#8918)
* toggle pinned state of items server + client

* pin quests / add pin src

* add officially pinned items and api to get in app rewards

* update schema and get items deatils

* update pin actions to the new logic

* show countBadge only with a number

* extract getPinKey - pin seasonal items

* togglePinned in buy-dialogs

* add pinKey to shop items

* wip

* wip

* fix path

* togglePinnedItem as common.op / use in client

* fix linting

* pinning: getItemInfo and save in db path and type

* make api more consistent, fix bugs

* updates

* fix bugs

* update actions to current api

* marketGear

* change to pinType

* add mystery_set to getItemInfo

* fix isPinned

* ignore animals

* list shopItems (initial)

* shopItem now has default popoverconent, itemclass and price / currency - list pinned items as rewards - attributes to gear

* show buyModal for the rewards

* show mystery_set icon

* add info whether item is suggested

* write migration, fix style issues

* pin potion and armoire

* make potion, armoire not unpinnable

* show notes for armoire and potion, add default items for new users

* show unpin notification

* add/remove pinned gear on class-change

* remove pinned & add new gear on purchase - refactoring pinning methods - fixes

* always allow to purchase armoire

* highlight item if suggested
2017-08-14 19:15:32 +02:00
negue
fcea1ecbc2 [WIP] multiplefixes for items / equipment / stable (#8938)
* highlight egg on dragging a potion - hide popover while dragging - flat show more button

* Show EquipGearModal on Item-Click - misc fixes

* disable dragging

* hide egg notes while dragging

* rename headgear to helm

* sort equipment by name & attributes

* set welcome-dialog flag

* show feeding notification

* select animals on click

* add hatchable popover back

* fix stable sortby A-Z

* remove property brackets
2017-08-13 18:18:46 +02:00
SabreCat
7b9ebc3465 fix(avatars): line up correctly 2017-08-12 16:14:46 +00:00
SabreCat
40ebaefac9 fix(lists): remove borky gradient
Left the SCSS for it in, for ease of playing with to get it to work in v2.
2017-08-12 15:29:20 +00:00
10187 changed files with 122552 additions and 175182 deletions

View File

@@ -1,3 +0,0 @@
{
"directory": "website/client-old/bower_components"
}

View File

@@ -14,17 +14,13 @@ files:
owner: root
group: users
content: |
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@4
$(ls -td /opt/elasticbeanstalk/node-install/node-* | head -1)/bin/npm install -g npm@5
container_commands:
01_makeBabel:
command: "touch /tmp/.babel.json"
02_ownBabel:
command: "chmod a+rw /tmp/.babel.json"
03_installBower:
command: "$NODE_HOME/bin/npm install -g bower"
04_installGulp:
03_installGulp:
command: "$NODE_HOME/bin/npm install -g gulp"
05_runBower:
command: "$NODE_HOME/lib/node_modules/bower/bin/bower --config.interactive=false --allow-root install -f"
06_runGulp:
04_runGulp:
command: "$NODE_HOME/lib/node_modules/gulp/bin/gulp.js build"

View File

@@ -6,6 +6,9 @@ website/transpiled-babel/
website/common/transpiled-babel/
dist/
dist-client/
apidoc_build/
content_cache/
node_modules/
# Not linted
website/client-old/
@@ -16,5 +19,3 @@ migrations/*
scripts/*
website/common/browserify.js
Gruntfile.js
gulpfile.js
gulp

View File

@@ -4,7 +4,7 @@
# Pull Request
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request)
[Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API)
# Requesting a feature

View File

@@ -6,7 +6,7 @@
[//]: # (For more guidelines see https://github.com/HabitRPG/habitica/issues/2760)
[//]: # (Fill out relevant information - UUID is found in Settings -> API)
[//]: # (Fill out relevant information - UUID is found from the Habitia website at User Icon > Settings > API)
### General Info
* UUID:
* Browser:

View File

@@ -1,4 +1,4 @@
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request for more info)
[//]: # (Note: See http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info)
[//]: # (Put Issue # or URL here, if applicable. This will automatically close the issue if your PR is merged in)
Fixes put_issue_url_here
@@ -8,7 +8,7 @@ Fixes put_issue_url_here
[//]: # (Put User ID in here - found in Settings -> API)
[//]: # (Put User ID in here - found on the Habitica website at User Icon > Settings > API)
----
UUID:

3
.gitignore vendored
View File

@@ -2,11 +2,14 @@
website/client-old/gen
website/client-old/common
website/client-old/apidoc
website/build
website/client-old/js/habitrpg-shared.js*
website/client-old/css/habitrpg-shared.css
website/transpiled-babel/
website/common/transpiled-babel/
node_modules
content_cache
apidoc_build
*.swp
.idea*
config.json

View File

@@ -1,32 +1,22 @@
language: node_js
node_js:
- '6'
sudo: required
dist: precise
services:
- mongodb
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
cache:
directories:
- 'node_modules'
before_install:
- $CXX --version
- npm install -g npm@4
- if [ $REQUIRES_SERVER ]; then sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10; echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list; sudo apt-get update; sudo apt-get install mongodb-org-server; fi
install:
- npm install &> npm.install.log || (cat npm.install.log; false)
- npm install -g npm@5
before_script:
- npm run test:build
- cp config.json.example config.json
- sleep 15
- sleep 5
script:
- npm run $TEST
- if [ $COVERAGE ]; then ./node_modules/.bin/lcov-result-merger 'coverage/**/*.info' | ./node_modules/coveralls/bin/coveralls.js; fi
env:
global:
- CXX=g++-4.8
- DISABLE_REQUEST_LOGGING=true
matrix:
- TEST="lint"
@@ -34,6 +24,4 @@ env:
- TEST="test:sanity"
- TEST="test:content" COVERAGE=true
- TEST="test:common" COVERAGE=true
- TEST="test:karma" COVERAGE=true
- TEST="client:unit" COVERAGE=true
- TEST="apidoc"

View File

@@ -1,7 +1,10 @@
FROM node:boron
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
RUN yarn global add npm@5
# Install global packages
RUN npm install -g gulp grunt-cli bower mocha
RUN npm install -g gulp mocha
# Clone Habitica repo and install dependencies
RUN mkdir -p /usr/src/habitrpg
@@ -9,7 +12,6 @@ WORKDIR /usr/src/habitrpg
RUN git clone https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN cp config.json.example config.json
RUN npm install
RUN bower install --allow-root
# Create Build dir
RUN mkdir -p ./website/build

View File

@@ -1,14 +1,27 @@
FROM node:boron
ENV ADMIN_EMAIL admin@habitica.com
ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e
ENV AMAZON_PAYMENTS_SELLER_ID AMQ3SB4SG5E91
ENV AMPLITUDE_KEY e8d4c24b3d6ef3ee73eeba715023dd43
ENV BASE_URL https://habitica.com
ENV FACEBOOK_KEY 128307497299777
ENV GA_ID UA-33510635-1
ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com
ENV NODE_ENV production
ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA
# Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807)
# The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975
RUN yarn global add npm@5
# Install global packages
RUN npm install -g gulp grunt-cli bower mocha
RUN npm install -g gulp mocha
# Clone Habitica repo and install dependencies
RUN mkdir -p /usr/src/habitrpg
WORKDIR /usr/src/habitrpg
RUN git clone --branch v3.114.1 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN git clone --branch v4.20.3 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg
RUN npm install
RUN bower install --allow-root
RUN gulp build:prod --force
# Create Build dir

View File

@@ -1,142 +0,0 @@
/*global module:false*/
require('babel-register');
var _ = require('lodash');
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
karma: {
unit: {
configFile: 'test/client-old/spec/karma.conf.js'
},
continuous: {
configFile: 'test/client-old/spec/karma.conf.js',
singleRun: true,
autoWatch: false
}
},
clean: {
build: ['website/build']
},
cssmin: {
dist: {
options: {
report: 'gzip'
},
files:{
"website/client-old/css/habitrpg-shared.css": [
"website/assets/sprites/dist/spritesmith*.css",
"website/assets/sprites/css/backer.css",
"website/assets/sprites/css/Mounts.css",
"website/assets/sprites/css/index.css"
]
}
}
},
stylus: {
build: {
options: {
compress: false, // AFTER
'include css': true,
paths: ['website/client-old']
},
files: {
'website/build/app.css': ['website/client-old/css/index.styl'],
'website/build/static.css': ['website/client-old/css/static.styl']
}
}
},
copy: {
build: {
files: [
{expand: true, cwd: 'website/client-old/', src: 'favicon.ico', dest: 'website/build/'},
{expand: true, cwd: 'website/client-old/', src: 'favicon_192x192.png', dest: 'website/build/'},
{expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/static/sprites'},
{expand: true, cwd: 'website/assets/sprites/', src: 'backer-only/*.gif', dest: 'website/build/'},
{expand: true, cwd: 'website/assets/sprites/', src: 'npc_ian.gif', dest: 'website/build/'},
{expand: true, cwd: 'website/assets/sprites/', src: 'quest_*.gif', dest: 'website/build/'},
{expand: true, cwd: 'website/client-old/', src: 'bower_components/bootstrap/dist/fonts/*', dest: 'website/build/'}
]
}
},
// UPDATE IT WHEN YOU ADD SOME FILES NOT ALREADY MATCHED!
hashres: {
build: {
options: {
fileNameFormat: '${name}-${hash}.${ext}'
},
src: [
'website/build/*.js',
'website/build/*.css',
'website/build/favicon.ico',
'website/build/favicon_192x192.png',
'website/build/*.png',
'website/build/static/sprites/*.png',
'website/build/*.gif',
'website/build/bower_components/bootstrap/dist/fonts/*'
],
dest: 'website/build/*.css'
}
}
});
//Load build files from client-old/manifest.json
grunt.registerTask('loadManifestFiles', 'Load all build files from client-old/manifest.json', function(){
var files = grunt.file.readJSON('./website/client-old/manifest.json');
var uglify = {};
var cssmin = {};
_.each(files, function(val, key){
var js = uglify['website/build/' + key + '.js'] = [];
_.each(files[key].js, function(val){
var path = "./";
if( val.indexOf('common/') == -1)
path = './website/client-old/';
js.push(path + val);
});
var css = cssmin['website/build/' + key + '.css'] = [];
_.each(files[key].css, function(val){
var path = "./";
if( val.indexOf('common/') == -1) {
path = (val == 'app.css' || val == 'static.css') ? './website/build/' : './website/client-old/';
}
css.push(path + val)
});
});
grunt.config.set('uglify.build.files', uglify);
grunt.config.set('uglify.build.options', {compress: false});
grunt.config.set('cssmin.build.files', cssmin);
// Rewrite urls to relative path
grunt.config.set('cssmin.build.options', {'target': 'website/client-old/css/whatever-css.css'});
});
// Register tasks.
grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']);
grunt.registerTask('build:dev', ['cssmin', 'stylus']);
grunt.registerTask('build:test', ['build:dev']);
// Load tasks
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-hashres');
if (process.env.NODE_ENV !== 'production') grunt.loadNpmTasks('grunt-karma');
};

View File

@@ -16,5 +16,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.hostname = "habitrpg"
config.vm.network "forwarded_port", guest: 3000, host: 3000, auto_correct: true
config.vm.usable_port_range = (3000..3050)
config.vm.network "forwarded_port", guest: 8080, host: 8080, auto_correct: true
config.vm.usable_port_range = (8080..8130)
config.vm.provision :shell, :path => "vagrant_scripts/vagrant.sh"
end

View File

@@ -1,56 +0,0 @@
{
"name": "HabitRPG",
"version": "0.1.1",
"homepage": "https://github.com/lefnire/habitrpg",
"authors": [
"Tyler Renelle <tylerrenelle@gmail.com>"
],
"private": true,
"ignore": [
"**/.*",
"node_modules",
"website/client-old/bower_components",
"test",
"tests"
],
"dependencies": {
"Angular-At-Directive": "snicker/Angular-At-Directive#c27bae207aa06d1e",
"angular": "1.3.9",
"angular-bootstrap": "0.13.0",
"angular-filter": "0.5.1",
"angular-loading-bar": "0.6.0",
"angular-resource": "1.3.9",
"angular-sanitize": "1.3.9",
"angular-ui": "0.4.0",
"angular-ui-router": "0.2.13",
"angular-ui-select2": "angular-ui/ui-select2#afa6589a54cb72815f",
"angular-ui-utils": "0.1.0",
"bootstrap": "3.1.0",
"bootstrap-growl": "ifightcrime/bootstrap-growl#162daa41cd1155f",
"bootstrap-tour": "0.10.1",
"css-social-buttons": "samcollins/css-social-buttons#v1.1.1 ",
"github-buttons": "mdo/github-buttons#v3.0.0",
"hello": "1.14.1",
"jquery": "2.1.0",
"jquery-colorbox": "1.4.36",
"jquery-ui": "1.10.3",
"jquery.cookie": "1.4.0",
"js-emoji": "snicker/js-emoji#f25d8a303f",
"ngInfiniteScroll": "1.1.0",
"pnotify": "1.3.1",
"sticky": "1.0.3",
"swagger-ui": "wordnik/swagger-ui#v2.0.24",
"smart-app-banner": "78ef9c0679723b25be1a0ae04f7b4aef7cbced4f",
"habitica-markdown": "1.2.2",
"pusher-js-auth": "^2.0.0",
"pusher-websocket-iso": "pusher#^3.2.0",
"taggle": "^1.11.1"
},
"devDependencies": {
"angular-mocks": "1.3.9"
},
"resolutions": {
"angular": "1.3.9",
"jquery": ">=1.9.0"
}
}

View File

@@ -22,6 +22,8 @@
"CRON_SEMI_SAFE_MODE":"false",
"MAINTENANCE_MODE": "false",
"SESSION_SECRET":"YOUR SECRET HERE",
"SESSION_SECRET_KEY": "1234567891234567891234567891234567891234567891234567891234567891",
"SESSION_SECRET_IV": "12345678912345678912345678912345",
"ADMIN_EMAIL": "you@example.com",
"SMTP_USER":"user@example.com",
"SMTP_PASS":"password",
@@ -71,6 +73,7 @@
},
"IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/",
"LOGGLY_TOKEN": "token",
"LOGGLY_CLIENT_TOKEN": "token",
"LOGGLY_ACCOUNT": "account",
"PUSH_CONFIGS": {
"GCM_SERVER_API_KEY": "",

View File

@@ -1,10 +0,0 @@
{
"root": true,
"env": {
"node": true,
},
"extends": [
"habitrpg/server",
"habitrpg/babel"
],
}

View File

@@ -2,7 +2,7 @@ import gulp from 'gulp';
import clean from 'rimraf';
import apidoc from 'apidoc';
const APIDOC_DEST_PATH = './website/build/apidoc';
const APIDOC_DEST_PATH = './apidoc_build';
const APIDOC_SRC_PATH = './website/server';
gulp.task('apidoc:clean', (done) => {
clean(APIDOC_DEST_PATH, done);
@@ -22,5 +22,5 @@ gulp.task('apidoc', ['apidoc:clean'], (done) => {
});
gulp.task('apidoc:watch', ['apidoc'], () => {
return gulp.watch(APIDOC_SRC_PATH + '/**/*.js', ['apidoc']);
return gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, ['apidoc']);
});

View File

@@ -1,31 +0,0 @@
import gulp from 'gulp';
import browserify from 'browserify';
import source from 'vinyl-source-stream';
import buffer from 'vinyl-buffer';
import uglify from 'gulp-uglify';
import sourcemaps from 'gulp-sourcemaps';
import babel from 'babelify';
gulp.task('browserify', function () {
let bundler = browserify({
entries: './website/common/browserify.js',
debug: true,
transform: [[babel, { compact: false }]],
});
return bundler.bundle()
.pipe(source('habitrpg-shared.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(uglify())
.on('error', function (err) {
console.error(err);
this.emit('end');
})
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./website/client-old/js/'));
});
gulp.task('browserify:watch', () => {
gulp.watch('./website/common/script/**/*.js', ['browserify']);
});

View File

@@ -1,36 +0,0 @@
import gulp from 'gulp';
import fs from 'fs';
// Copy Bootstrap 4 config variables from /website /node_modules so we can check
// them into Git
const BOOSTRAP_NEW_CONFIG_PATH = 'website/client/assets/scss/bootstrap_config.scss';
const BOOTSTRAP_ORIGINAL_CONFIG_PATH = 'node_modules/bootstrap/scss/_custom.scss';
// https://stackoverflow.com/a/14387791/969528
function copyFile(source, target, cb) {
let cbCalled = false;
function done(err) {
if (!cbCalled) {
cb(err);
cbCalled = true;
}
}
let rd = fs.createReadStream(source);
rd.on('error', done);
let wr = fs.createWriteStream(target);
wr.on('error', done);
wr.on('close', () => done());
rd.pipe(wr);
}
gulp.task('bootstrap', (done) => {
// use new config
copyFile(
BOOSTRAP_NEW_CONFIG_PATH,
BOOTSTRAP_ORIGINAL_CONFIG_PATH,
done,
);
});

View File

@@ -1,13 +1,10 @@
import gulp from 'gulp';
import runSequence from 'run-sequence';
import babel from 'gulp-babel';
require('gulp-grunt')(gulp);
import webpackProductionBuild from '../webpack/build';
gulp.task('build', () => {
if (process.env.NODE_ENV === 'production') {
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
gulp.start('build:prod');
} else {
gulp.start('build:dev');
}
});
@@ -25,18 +22,16 @@ gulp.task('build:common', () => {
gulp.task('build:server', ['build:src', 'build:common']);
gulp.task('build:dev', ['browserify', 'prepare:staticNewStuff'], (done) => {
gulp.start('grunt-build:dev', done);
// Client Production Build
gulp.task('build:client', (done) => {
webpackProductionBuild((err, output) => {
if (err) return done(err);
console.log(output); // eslint-disable-line no-console
});
});
gulp.task('build:dev:watch', ['build:dev'], () => {
gulp.watch(['website/client-old/**/*.styl', 'website/common/script/*']);
});
gulp.task('build:prod', ['browserify', 'build:server', 'prepare:staticNewStuff'], (done) => {
runSequence(
'grunt-build:prod',
'apidoc',
done
);
});
gulp.task('build:prod', [
'build:server',
'build:client',
'apidoc',
]);

View File

@@ -7,10 +7,11 @@ import gulp from 'gulp';
// Add additional properties to the repl's context
let improveRepl = (context) => {
// Let "exit" and "quit" terminate the console
['exit', 'quit'].forEach((term) => {
Object.defineProperty(context, term, { get () { process.exit(); }});
Object.defineProperty(context, term, { get () {
process.exit();
}});
});
// "clear" clears the screen
@@ -18,12 +19,12 @@ let improveRepl = (context) => {
process.stdout.write('\u001B[2J\u001B[0;0f');
}});
context.Challenge = require('../website/server/models/challenge').model;
context.Group = require('../website/server/models/group').model;
context.User = require('../website/server/models/user').model;
context.Challenge = require('../website/server/models/challenge').model; // eslint-disable-line global-require
context.Group = require('../website/server/models/group').model; // eslint-disable-line global-require
context.User = require('../website/server/models/user').model; // eslint-disable-line global-require
var isProd = nconf.get('NODE_ENV') === 'production';
var mongooseOptions = !isProd ? {} : {
const isProd = nconf.get('NODE_ENV') === 'production';
const mongooseOptions = !isProd ? {} : {
replset: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
};
@@ -31,16 +32,15 @@ let improveRepl = (context) => {
mongoose.connect(
nconf.get('NODE_DB_URI'),
mongooseOptions,
function (err) {
(err) => {
if (err) throw err;
logger.info('Connected with Mongoose');
}
)
);
};
gulp.task('console', (cb) => {
gulp.task('console', () => {
improveRepl(repl.start({
prompt: 'Habitica > ',
}).context);

View File

@@ -1,10 +0,0 @@
import gulp from 'gulp';
import jade from 'jade';
import {writeFileSync} from 'fs';
gulp.task('prepare:staticNewStuff', () => {
writeFileSync(
'./website/client-old/new-stuff.html',
jade.compileFile('./website/views/shared/new-stuff.jade')()
);
});

View File

@@ -10,82 +10,39 @@ import {each} from 'lodash';
// https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248
const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3;
const DIST_PATH = 'website/assets/sprites/dist/';
const IMG_DIST_PATH_NEW_CLIENT = 'website/static/sprites/';
const CSS_DIST_PATH_NEW_CLIENT = 'website/client/assets/css/sprites/';
const IMG_DIST_PATH = 'website/client/assets/images/sprites/';
const CSS_DIST_PATH = 'website/client/assets/css/sprites/';
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
function checkForSpecialTreatment (name) {
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
return name.match(regex) || name === 'head_0';
}
gulp.task('sprites:main', () => {
let mainSrc = sync('website/assets/sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
function calculateImgDimensions (img, addPadding) {
let dims = sizeOf(img);
gulp.task('sprites:largeSprites', () => {
let largeSrc = sync('website/assets/sprites/spritesmith_large/**/*.png');
return createSpritesStream('largeSprites', largeSrc);
});
gulp.task('sprites:clean', (done) => {
clean(`{${DIST_PATH}spritesmith*,${IMG_DIST_PATH_NEW_CLIENT}spritesmith*,${CSS_DIST_PATH_NEW_CLIENT}spritesmith*}`, done);
});
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
console.log('Verifiying that images do not exceed max dimensions');
let numberOfSheetsThatAreTooBig = 0;
let distSpritesheets = sync(`${DIST_PATH}*.png`);
each(distSpritesheets, (img, index) => {
let spriteSize = calculateImgDimensions(img);
if (spriteSize > MAX_SPRITESHEET_SIZE) {
numberOfSheetsThatAreTooBig++;
let name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`);
}
});
if (numberOfSheetsThatAreTooBig > 0) {
console.error(`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle them, but there is a margin of error in these calculations so it is probably okay. Mention this to an admin so they can test a staging site on mobile Safari after your PR is merged.`); // https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
} else {
console.log('All images are within the correct dimensions');
let requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
let newWidth = dims.width < 90 ? 90 : dims.width;
let newHeight = dims.height < 90 ? 90 : dims.height;
dims = {
width: newWidth,
height: newHeight,
};
}
});
function createSpritesStream (name, src) {
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
let stream = mergeStream();
let padding = 0;
each(spritesheetSliceIndicies, (start, index) => {
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
if (addPadding) {
padding = dims.width * 8 + dims.height * 8;
}
let spriteData = gulp.src(slicedSrc)
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/assets/sprites/css/css.template.handlebars',
cssVarMap: cssVarMap,
}));
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console
let imgStream = spriteData.img
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH_NEW_CLIENT))
.pipe(gulp.dest(DIST_PATH));
let totalPixelSize = dims.width * dims.height + padding;
let cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH_NEW_CLIENT))
.pipe(gulp.dest(DIST_PATH));
stream.add(imgStream);
stream.add(cssStream);
});
return stream;
return totalPixelSize;
}
function calculateSpritesheetsSrcIndicies (src) {
@@ -105,37 +62,6 @@ function calculateSpritesheetsSrcIndicies (src) {
return slices;
}
function calculateImgDimensions (img, addPadding) {
let dims = sizeOf(img);
let requiresSpecialTreatment = checkForSpecialTreatment(img);
if (requiresSpecialTreatment) {
let newWidth = dims.width < 90 ? 90 : dims.width;
let newHeight = dims.height < 90 ? 90 : dims.height;
dims = {
width: newWidth,
height: newHeight,
};
}
let padding = 0;
if (addPadding) {
padding = (dims.width * 8) + (dims.height * 8);
}
if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims);
let totalPixelSize = (dims.width * dims.height) + padding;
return totalPixelSize;
}
function checkForSpecialTreatment (name) {
let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/;
return name.match(regex) || name === 'head_0';
}
function cssVarMap (sprite) {
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a
// 60x60 image pointing at the proper part of the 90x90 sprite.
@@ -144,18 +70,93 @@ function cssVarMap (sprite) {
if (requiresSpecialTreatment) {
sprite.custom = {
px: {
offset_x: `-${ sprite.x + 25 }px`,
offset_y: `-${ sprite.y + 15 }px`,
offsetX: `-${ sprite.x + 25 }px`,
offsetY: `-${ sprite.y + 15 }px`,
width: '60px',
height: '60px',
},
};
}
if (~sprite.name.indexOf('shirt'))
sprite.custom.px.offset_y = `-${ sprite.y + 30 }px`; // even more for shirts
if (~sprite.name.indexOf('hair_base')) {
let styleArray = sprite.name.split('_').slice(2,3);
if (sprite.name.indexOf('shirt') !== -1)
sprite.custom.px.offsetY = `-${ sprite.y + 35 }px`; // even more for shirts
if (sprite.name.indexOf('hair_base') !== -1) {
let styleArray = sprite.name.split('_').slice(2, 3);
if (Number(styleArray[0]) > 14)
sprite.custom.px.offset_y = `-${ sprite.y }px`; // don't crop updos
sprite.custom.px.offsetY = `-${ sprite.y }px`; // don't crop updos
}
}
function createSpritesStream (name, src) {
let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src);
let stream = mergeStream();
each(spritesheetSliceIndicies, (start, index) => {
let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]);
let spriteData = gulp.src(slicedSrc)
.pipe(spritesmith({
imgName: `spritesmith-${name}-${index}.png`,
cssName: `spritesmith-${name}-${index}.css`,
algorithm: 'binary-tree',
padding: 1,
cssTemplate: 'website/raw_sprites/css/css.template.handlebars',
cssVarMap,
}));
let imgStream = spriteData.img
.pipe(imagemin())
.pipe(gulp.dest(IMG_DIST_PATH));
let cssStream = spriteData.css
.pipe(gulp.dest(CSS_DIST_PATH));
stream.add(imgStream);
stream.add(cssStream);
});
return stream;
}
gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']);
gulp.task('sprites:main', () => {
let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png');
return createSpritesStream('main', mainSrc);
});
gulp.task('sprites:largeSprites', () => {
let largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png');
return createSpritesStream('largeSprites', largeSrc);
});
gulp.task('sprites:clean', (done) => {
clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done);
});
gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => {
console.log('Verifiying that images do not exceed max dimensions'); // eslint-disable-line no-console
let numberOfSheetsThatAreTooBig = 0;
let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`);
each(distSpritesheets, (img) => {
let spriteSize = calculateImgDimensions(img);
if (spriteSize > MAX_SPRITESHEET_SIZE) {
numberOfSheetsThatAreTooBig++;
let name = basename(img, '.png');
console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console
}
});
if (numberOfSheetsThatAreTooBig > 0) {
// https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180
console.error( // eslint-disable-line no-console
`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle
them, but there is a margin of error in these calculations so it is probably okay. Mention
this to an admin so they can test a staging site on mobile Safari after your PR is merged.`);
} else {
console.log('All images are within the correct dimensions'); // eslint-disable-line no-console
}
});

View File

@@ -3,8 +3,6 @@ import nodemon from 'gulp-nodemon';
let pkg = require('../package.json');
gulp.task('run:dev', ['nodemon', 'build:dev:watch']);
gulp.task('nodemon', () => {
nodemon({
script: pkg.main,

View File

@@ -1,21 +1,12 @@
import {
pipe,
awaitPort,
kill,
runMochaTests,
} from './taskHelper';
import { server as karma } from 'karma';
import mongoose from 'mongoose';
import { exec } from 'child_process';
import psTree from 'ps-tree';
import gulp from 'gulp';
import Bluebird from 'bluebird';
import runSequence from 'run-sequence';
import os from 'os';
import nconf from 'nconf';
import fs from 'fs';
const i18n = require('../website/server/libs/i18n');
// TODO rewrite
@@ -24,25 +15,23 @@ let server;
const TEST_DB_URI = nconf.get('TEST_DB_URI');
const API_V3_TEST_COMMAND = 'npm run test:api-v3';
const SANITY_TEST_COMMAND = 'npm run test:sanity';
const COMMON_TEST_COMMAND = 'npm run test:common';
const CONTENT_TEST_COMMAND = 'npm run test:content';
const CONTENT_OPTIONS = {maxBuffer: 1024 * 500};
const KARMA_TEST_COMMAND = 'npm run test:karma';
/* Helper methods for reporting test summary */
let testResults = [];
let testCount = (stdout, regexp) => {
let match = stdout.match(regexp);
return parseInt(match && match[1] || 0);
return parseInt(match && match[1] || 0, 10);
};
let testBin = (string, additionalEnvVariables = '') => {
if (os.platform() === 'win32') {
if (additionalEnvVariables != '') {
if (additionalEnvVariables !== '') {
additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set ');
additionalEnvVariables = 'set ' + additionalEnvVariables + '&&';
additionalEnvVariables = `set ${additionalEnvVariables}&&`;
}
return `set NODE_ENV=test&&${additionalEnvVariables}${string}`;
} else {
@@ -50,9 +39,9 @@ let testBin = (string, additionalEnvVariables = '') => {
}
};
gulp.task('test:nodemon', (done) => {
process.env.PORT = TEST_SERVER_PORT;
process.env.NODE_DB_URI = TEST_DB_URI;
gulp.task('test:nodemon', () => {
process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env
process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env
runSequence('nodemon');
});
@@ -69,37 +58,27 @@ gulp.task('test:prepare:mongo', (cb) => {
gulp.task('test:prepare:server', ['test:prepare:mongo'], () => {
if (!server) {
server = exec(testBin('node ./website/server/index.js', `NODE_DB_URI=${TEST_DB_URI} PORT=${TEST_SERVER_PORT}`), (error, stdout, stderr) => {
if (error) { throw `Problem with the server: ${error}`; }
if (stderr) { console.error(stderr); }
if (error) {
throw new Error(`Problem with the server: ${error}`);
}
if (stderr) {
console.error(stderr); // eslint-disable-line no-console
}
});
}
});
gulp.task('test:prepare:translations', (cb) => {
fs.writeFile(
'test/client-old/spec/mocks/translations.js',
`if(!window.env) window.env = {};
window.env.translations = ${JSON.stringify(i18n.translations['en'])};`, cb);
});
gulp.task('test:prepare:build', ['build', 'test:prepare:translations']);
// exec(testBin('grunt build:test'), cb);
gulp.task('test:prepare:webdriver', (cb) => {
exec('npm run test:prepare:webdriver', cb);
});
gulp.task('test:prepare:build', ['build']);
gulp.task('test:prepare', [
'test:prepare:build',
'test:prepare:mongo',
'test:prepare:webdriver',
]);
gulp.task('test:sanity', (cb) => {
let runner = exec(
testBin(SANITY_TEST_COMMAND),
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -112,7 +91,7 @@ gulp.task('test:sanity', (cb) => {
gulp.task('test:common', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(COMMON_TEST_COMMAND),
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -133,7 +112,7 @@ gulp.task('test:common:watch', ['test:common:clean'], () => {
gulp.task('test:common:safe', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(COMMON_TEST_COMMAND),
(err, stdout, stderr) => {
(err, stdout) => { // eslint-disable-line handle-callback-err
testResults.push({
suite: 'Common Specs\t',
pass: testCount(stdout, /(\d+) passing/),
@@ -150,7 +129,7 @@ gulp.task('test:content', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(CONTENT_TEST_COMMAND),
CONTENT_OPTIONS,
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -172,7 +151,7 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(CONTENT_TEST_COMMAND),
CONTENT_OPTIONS,
(err, stdout, stderr) => {
(err, stdout) => { // eslint-disable-line handle-callback-err
testResults.push({
suite: 'Content Specs\t',
pass: testCount(stdout, /(\d+) passing/),
@@ -185,103 +164,10 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => {
pipe(runner);
});
gulp.task('test:karma', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(KARMA_TEST_COMMAND),
(err, stdout) => {
if (err) {
process.exit(1);
}
cb();
}
);
pipe(runner);
});
gulp.task('test:karma:watch', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(`${KARMA_TEST_COMMAND}:watch`),
(err, stdout) => {
cb(err);
}
);
pipe(runner);
});
gulp.task('test:karma:safe', ['test:prepare:build'], (cb) => {
let runner = exec(
testBin(KARMA_TEST_COMMAND),
(err, stdout) => {
testResults.push({
suite: 'Karma Specs\t',
pass: testCount(stdout, /(\d+) tests? completed/),
fail: testCount(stdout, /(\d+) tests? failed/),
pend: testCount(stdout, /(\d+) tests? skipped/),
});
cb();
}
);
pipe(runner);
});
gulp.task('test:e2e', ['test:prepare', 'test:prepare:server'], (cb) => {
let support = [
'Xvfb :99 -screen 0 1024x768x24 -extension RANDR',
testBin('npm run test:e2e:webdriver', 'DISPLAY=:99'),
].map(exec);
support.push(server);
Bluebird.all([
awaitPort(TEST_SERVER_PORT),
awaitPort(4444),
]).then(() => {
let runner = exec(
'npm run test:e2e',
(err, stdout, stderr) => {
support.forEach(kill);
if (err) {
process.exit(1);
}
cb();
}
);
pipe(runner);
});
});
gulp.task('test:e2e:safe', ['test:prepare', 'test:prepare:server'], (cb) => {
let support = [
'Xvfb :99 -screen 0 1024x768x24 -extension RANDR',
'npm run test:e2e:webdriver',
].map(exec);
Bluebird.all([
awaitPort(TEST_SERVER_PORT),
awaitPort(4444),
]).then(() => {
let runner = exec(
'npm run test:e2e',
(err, stdout, stderr) => {
let match = stdout.match(/(\d+) tests?.*(\d) failures?/);
testResults.push({
suite: 'End-to-End Specs\t',
pass: testCount(stdout, /(\d+) passing/),
fail: testCount(stdout, /(\d+) failing/),
pend: testCount(stdout, /(\d+) pending/),
});
support.forEach(kill);
cb();
}
);
pipe(runner);
});
});
gulp.task('test:api-v3:unit', (done) => {
let runner = exec(
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-unit --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/unit --recursive --require ./test/helpers/start-server'),
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -300,7 +186,7 @@ gulp.task('test:api-v3:integration', (done) => {
let runner = exec(
testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'),
{maxBuffer: 500 * 1024},
(err, stdout, stderr) => {
(err) => {
if (err) {
process.exit(1);
}
@@ -320,7 +206,7 @@ gulp.task('test:api-v3:integration:separate-server', (done) => {
let runner = exec(
testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'),
{maxBuffer: 500 * 1024},
(err, stdout, stderr) => done(err)
(err) => done(err)
);
pipe(runner);
@@ -331,7 +217,6 @@ gulp.task('test', (done) => {
'test:sanity',
'test:content',
'test:common',
'test:karma',
'test:api-v3:unit',
'test:api-v3:integration',
done

View File

@@ -1,6 +1,5 @@
import fs from 'fs';
import _ from 'lodash';
import nconf from 'nconf';
import gulp from 'gulp';
import { postToSlack, conf } from './taskHelper';
@@ -12,8 +11,82 @@ const SLACK_CONFIG = {
const LOCALES = './website/common/locales/';
const ENGLISH_LOCALE = `${LOCALES}en/`;
function getArrayOfLanguages () {
let languages = fs.readdirSync(LOCALES);
languages.shift(); // Remove README.md from array of languages
return languages;
}
const ALL_LANGUAGES = getArrayOfLanguages();
function stripOutNonJsonFiles (collection) {
let onlyJson = _.filter(collection, (file) => {
return file.match(/[a-zA-Z]*\.json/);
});
return onlyJson;
}
function eachTranslationFile (languages, cb) {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
_.each(languages, (lang) => {
_.each(jsonFiles, (filename) => {
let parsedTranslationFile;
try {
const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
parsedTranslationFile = JSON.parse(translationFile);
} catch (err) {
return cb(err);
}
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
let parsedEnglishFile = JSON.parse(englishFile);
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
});
});
}
function eachTranslationString (languages, cb) {
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
if (error) return;
_.each(englishJSON, (string, key) => {
const translationString = translationJSON[key];
cb(language, filename, key, string, translationString);
});
});
}
function formatMessageForPosting (msg, items) {
let body = `*Warning:* ${msg}`;
body += '\n\n```\n';
body += items.join('\n');
body += '\n```';
return body;
}
function getStringsWith (json, interpolationRegex) {
let strings = {};
_.each(json, (fileName) => {
const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName);
const parsedJson = JSON.parse(rawFile);
strings[fileName] = {};
_.each(parsedJson, (value, key) => {
const match = value.match(interpolationRegex);
if (match) strings[fileName][key] = match;
});
});
return strings;
}
const malformedStringExceptions = {
messageDropFood: true,
armoireFood: true,
@@ -23,7 +96,6 @@ const malformedStringExceptions = {
gulp.task('transifex', ['transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings']);
gulp.task('transifex:missingFiles', () => {
let missingStrings = [];
eachTranslationFile(ALL_LANGUAGES, (error) => {
@@ -40,7 +112,6 @@ gulp.task('transifex:missingFiles', () => {
});
gulp.task('transifex:missingStrings', () => {
let missingStrings = [];
eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => {
@@ -58,7 +129,6 @@ gulp.task('transifex:missingStrings', () => {
});
gulp.task('transifex:malformedStrings', () => {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
let interpolationRegex = /<%= [a-zA-Z]* %>/g;
let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex);
@@ -66,25 +136,23 @@ gulp.task('transifex:malformedStrings', () => {
let stringsWithMalformedInterpolations = [];
let stringsWithIncorrectNumberOfInterpolations = [];
let count = 0;
_.each(ALL_LANGUAGES, function (lang) {
_.each(stringsToLookFor, function (strings, file) {
let translationFile = fs.readFileSync(LOCALES + lang + '/' + file);
_.each(ALL_LANGUAGES, (lang) => {
_.each(stringsToLookFor, (strings, filename) => {
let translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`);
let parsedTranslationFile = JSON.parse(translationFile);
_.each(strings, function (value, key) {
_.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks
let translationString = parsedTranslationFile[key];
if (!translationString) return;
let englishOccurences = stringsToLookFor[file][key];
let englishOccurences = stringsToLookFor[filename][key];
let translationOccurences = translationString.match(interpolationRegex);
if (!translationOccurences) {
let malformedString = `${lang} - ${file} - ${key} - ${translationString}`;
let malformedString = `${lang} - ${filename} - ${key} - ${translationString}`;
stringsWithMalformedInterpolations.push(malformedString);
} else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) {
let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`;
let missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`;
stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString);
}
});
@@ -103,74 +171,3 @@ gulp.task('transifex:malformedStrings', () => {
postToSlack(formattedMessage, SLACK_CONFIG);
}
});
function getArrayOfLanguages () {
let languages = fs.readdirSync(LOCALES);
languages.shift(); // Remove README.md from array of languages
return languages;
}
function eachTranslationFile (languages, cb) {
let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE));
_.each(languages, (lang) => {
_.each(jsonFiles, (filename) => {
try {
var translationFile = fs.readFileSync(LOCALES + lang + '/' + filename);
var parsedTranslationFile = JSON.parse(translationFile);
} catch (err) {
return cb(err);
}
let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename);
let parsedEnglishFile = JSON.parse(englishFile);
cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile);
});
});
}
function eachTranslationString (languages, cb) {
eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => {
if (error) return;
_.each(englishJSON, (string, key) => {
var translationString = translationJSON[key];
cb(language, filename, key, string, translationString);
});
});
}
function formatMessageForPosting (msg, items) {
let body = `*Warning:* ${msg}`;
body += '\n\n```\n';
body += items.join('\n');
body += '\n```';
return body;
}
function getStringsWith (json, interpolationRegex) {
var strings = {};
_.each(json, function (file_name) {
var raw_file = fs.readFileSync(ENGLISH_LOCALE + file_name);
var parsed_json = JSON.parse(raw_file);
strings[file_name] = {};
_.each(parsed_json, function (value, key) {
var match = value.match(interpolationRegex);
if (match) strings[file_name][key] = match;
});
});
return strings;
}
function stripOutNonJsonFiles (collection) {
let onlyJson = _.filter(collection, (file) => {
return file.match(/[a-zA-Z]*\.json/);
});
return onlyJson;
}

View File

@@ -12,7 +12,7 @@ import { resolve } from 'path';
* Get access to configruable values
*/
nconf.argv().env().file({ file: 'config.json' });
export var conf = nconf;
export const conf = nconf;
/*
* Kill a child process and any sub-children that process may have spawned.
@@ -26,11 +26,12 @@ export function kill (proc) {
pids.forEach(kill); return;
}
try {
exec(/^win/.test(process.platform)
? `taskkill /PID ${pid} /T /F`
: `kill -9 ${pid}`);
exec(/^win/.test(process.platform) ?
`taskkill /PID ${pid} /T /F` :
`kill -9 ${pid}`);
} catch (e) {
console.log(e); // eslint-disable-line no-console
}
catch (e) { console.log(e); }
});
};
@@ -44,21 +45,25 @@ export function kill (proc) {
* before failing.
*/
export function awaitPort (port, max = 60) {
return new Bluebird((reject, resolve) => {
let socket, timeout, interval;
return new Bluebird((rej, res) => {
let socket;
let timeout;
let interval;
timeout = setTimeout(() => {
clearInterval(interval);
reject(`Timed out after ${max} seconds`);
rej(`Timed out after ${max} seconds`);
}, max * 1000);
interval = setInterval(() => {
socket = net.connect({port: port}, () => {
socket = net.connect({port}, () => {
clearInterval(interval);
clearTimeout(timeout);
socket.destroy();
resolve();
}).on('error', () => { socket.destroy; });
res();
}).on('error', () => {
socket.destroy();
});
}, 1000);
});
}
@@ -67,8 +72,12 @@ export function awaitPort (port, max = 60) {
* Pipe the child's stdin and stderr to the parent process.
*/
export function pipe (child) {
child.stdout.on('data', (data) => { process.stdout.write(data); });
child.stderr.on('data', (data) => { process.stderr.write(data); });
child.stdout.on('data', (data) => {
process.stdout.write(data);
});
child.stderr.on('data', (data) => {
process.stderr.write(data);
});
}
/*
@@ -78,8 +87,8 @@ export function postToSlack (msg, config = {}) {
let slackUrl = nconf.get('SLACK_URL');
if (!slackUrl) {
console.error('No slack post url specified. Your message was:');
console.log(msg);
console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console
console.log(msg); // eslint-disable-line no-console
return;
}
@@ -89,15 +98,15 @@ export function postToSlack (msg, config = {}) {
channel: `#${config.channel || '#general'}`,
username: config.username || 'gulp task',
text: msg,
icon_emoji: `:${config.emoji || 'gulp'}:`,
icon_emoji: `:${config.emoji || 'gulp'}:`, // eslint-disable-line camelcase
})
.end((err, res) => {
if (err) console.error('Unable to post to slack', err);
.end((err) => {
if (err) console.error('Unable to post to slack', err); // eslint-disable-line no-console
});
}
export function runMochaTests (files, server, cb) {
require('../test/helpers/globals.helper');
require('../test/helpers/globals.helper'); // eslint-disable-line global-require
let mocha = new Mocha({reporter: 'spec'});
let tests = glob(files);
@@ -108,7 +117,7 @@ export function runMochaTests (files, server, cb) {
});
mocha.run((numberOfFailures) => {
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) {
if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env
if (server) kill(server);
process.exit(numberOfFailures);
}

View File

@@ -8,13 +8,10 @@
require('babel-register');
if (process.env.NODE_ENV === 'production') {
require('./gulp/gulp-apidoc');
require('./gulp/gulp-newstuff');
require('./gulp/gulp-build');
require('./gulp/gulp-babelify');
require('./gulp/gulp-bootstrap');
if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env
require('./gulp/gulp-apidoc'); // eslint-disable-line global-require
require('./gulp/gulp-build'); // eslint-disable-line global-require
} else {
require('glob').sync('./gulp/gulp-*').forEach(require);
require('gulp').task('default', ['test']);
require('glob').sync('./gulp/gulp-*').forEach(require); // eslint-disable-line global-require
require('gulp').task('default', ['test']); // eslint-disable-line global-require
}

View File

@@ -16,7 +16,7 @@ var migrationName = '20140831_increase_gems_for_previous_contributions';
* https://github.com/HabitRPG/habitrpg/issues/3933
* Increase Number of Gems for Contributors
* author: Alys (d904bd62-da08-416b-a816-ba797c9ee265)
*
*
* Increase everyone's gems per their contribution level.
* Originally they were given 2 gems per tier.
* Now they are given 3 gems per tier for tiers 1,2,3
@@ -70,7 +70,7 @@ dbUsers.findEach(query, fields, function(err, user) {
var extraGems = tier; // tiers 1,2,3
if (tier > 3) { extraGems = 3 + (tier - 3) * 2; }
if (tier == 8) { extraGems = 11; }
extraBalance = extraGems / 4;
var extraBalance = extraGems / 4;
set['balance'] = user.balance + extraBalance;
// Capture current state of user:

View File

@@ -39,7 +39,7 @@ function findUsers(gt){
console.log('User: ', countUsers, user._id);
var update = {
$set: {};
$set: {}
};
if(user.auth && user.auth.local) {
@@ -60,4 +60,4 @@ function findUsers(gt){
});
};
findUsers();
findUsers();

View File

@@ -0,0 +1,97 @@
var migrationName = '20170928_redesign_guilds.js';
/*
* Copy Guild Leader messages to end of Guild descriptions
* Copy Guild logos to beginning of Guild descriptions
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var dbGroups = monk(connectionString).get('groups', { castIds: false });
function processGroups(lastId) {
// specify a query to limit the affected groups (empty for all groups):
var query = {
};
var fields = {
'description': 1,
'logo': 1,
'leaderMessage': 1,
}
if (lastId) {
query._id = {
$gt: lastId
}
}
return dbGroups.find(query, {
fields: fields,
sort: {_id: 1},
limit: 250,
})
.then(updateGroups)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 1000;
var count = 0;
function updateGroups (groups) {
if (!groups || groups.length === 0) {
console.warn('All appropriate groups found and modified.');
displayData();
return;
}
var groupPromises = groups.map(updateGroup);
var lastGroup = groups[groups.length - 1];
return Promise.all(groupPromises)
.then(function () {
processGroups(lastGroup._id);
});
}
function updateGroup (group) {
count++;
var description = group.description;
if (group.logo) {
description = '![Guild Logo](' + group.logo + ')\n\n&nbsp;\n\n' + description;
}
if (group.leaderMessage) {
description = description + '\n\n&nbsp;\n\n' + group.leaderMessage;
}
var set = {
description: description,
};
if (count % progressCount == 0) console.warn(count + ' ' + group._id);
return dbGroups.update({_id: group._id}, {$set:set});
}
function displayData() {
console.warn('\n' + count + ' groups processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processGroups;

View File

@@ -0,0 +1,128 @@
import { selectGearToPin } from '../website/common/script/ops/pinnedGearUtils';
var getItemInfo = require('../website/common/script/libs/getItemInfo');
var migrationName = '20170928_redesign_launch.js';
var authorName = 'paglias'; // in case script author needs to know when their ...
var authorUuid = 'ed4c688c-6652-4a92-9d03-a5a79844174a'; //... own data is done
/*
* Migrate existing in app rewards lists to pinned items
* Award Veteran Pets
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers(lastId) {
// specify a query to limit the affected users (empty for all users):
var query = {
'migration': {$ne:migrationName},
'auth.timestamps.loggedin': {$gt: new Date('2017-09-21')},
};
var fields = {
'items.pets': 1,
'items.gear': 1,
'stats.class': 1,
}
if (lastId) {
query._id = {
$gt: lastId
}
}
return dbUsers.find(query, {
fields: fields,
sort: {_id: 1},
limit: 250,
})
.then(updateUsers)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 1000;
var count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
var userPromises = users.map(updateUser);
var lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(function () {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
var set = {'migration': migrationName};
var oldRewardsList = selectGearToPin(user);
var newPinnedItems = [
{
type: 'armoire',
path: 'armoire',
},
{
type: 'potion',
path: 'potion',
},
];
oldRewardsList.forEach(item => {
var type = 'marketGear';
var itemInfo = getItemInfo(user, 'marketGear', item);
newPinnedItems.push({
type,
path: itemInfo.path,
})
});
set.pinnedItems = newPinnedItems;
if (user.items.pets['Lion-Veteran']) {
set['items.pets.Bear-Veteran'] = 5;
} else if (user.items.pets['Tiger-Veteran']) {
set['items.pets.Lion-Veteran'] = 5;
} else if (user.items.pets['Wolf-Veteran']) {
set['items.pets.Tiger-Veteran'] = 5;
} else {
set['items.pets.Wolf-Veteran'] = 5;
}
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
return dbUsers.update({_id: user._id}, {$set:set});
}
function displayData() {
console.warn('\n' + count + ' users processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processUsers;

View File

@@ -0,0 +1,111 @@
var migrationName = '20171030_jackolanterns.js';
var authorName = 'Sabe'; // in case script author needs to know when their ...
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
/*
* Award the Jack-O'-Lantern ladder:
* Ghost Jack-O-Lantern Mount to owners of Ghost Jack-O-Lantern Pet
* Ghost Jack-O-Lantern Pet to owners of Jack-O-Lantern Mount
* Jack-O-Lantern Mount to owners of Jack-O-Lantern Pet
* Jack-O-Lantern Pet to everyone else
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers(lastId) {
// specify a query to limit the affected users (empty for all users):
var query = {
'migration':{$ne:migrationName},
};
if (lastId) {
query._id = {
$gt: lastId
}
}
dbUsers.find(query, {
sort: {_id: 1},
limit: 250,
fields: [
'items.pets',
'items.mounts',
] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
})
.then(updateUsers)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 1000;
var count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
var userPromises = users.map(updateUser);
var lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(function () {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
var set = {};
var inc = {
'items.food.Candy_Skeleton': 1,
'items.food.Candy_Base': 1,
'items.food.Candy_CottonCandyBlue': 1,
'items.food.Candy_CottonCandyPink': 1,
'items.food.Candy_Shade': 1,
'items.food.Candy_White': 1,
'items.food.Candy_Golden': 1,
'items.food.Candy_Zombie': 1,
'items.food.Candy_Desert': 1,
'items.food.Candy_Red': 1,
};
if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) {
set = {'migration':migrationName, 'items.mounts.JackOLantern-Ghost': true};
} else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) {
set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost': 5};
} else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) {
set = {'migration':migrationName, 'items.mounts.JackOLantern-Base': true};
} else {
set = {'migration':migrationName, 'items.pets.JackOLantern-Base': 5};
}
dbUsers.update({_id: user._id}, {$set:set, $inc:inc});
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
}
function displayData() {
console.warn('\n' + count + ' users processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processUsers;

View File

@@ -0,0 +1,128 @@
var migrationName = '20171117_turkey_ladder.js';
var authorName = 'Sabe'; // in case script author needs to know when their ...
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
/*
* Award the Turkey Day ladder:
* Grant Turkey Costume to those who have the Gilded Turkey mount
* Grant Gilded Turkey mount to those who have the Gilded Turkey pet
* Grant Gilded Turkey pet to those who have the Base Turkey mount
* Grant Base Turkey mount to those who have the Base Turkey pet
* Grant Base Turkey pet to those who have none of the above yet
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers(lastId) {
// specify a query to limit the affected users (empty for all users):
var query = {
'migration':{$ne:migrationName},
'auth.timestamps.loggedin':{$gt:new Date('2017-11-01')},
};
if (lastId) {
query._id = {
$gt: lastId
}
}
dbUsers.find(query, {
sort: {_id: 1},
limit: 250,
fields: [
'items.pets',
'items.mounts',
] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
})
.then(updateUsers)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 1000;
var count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
var userPromises = users.map(updateUser);
var lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(function () {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
var set = {};
if (user && user.items && user.items.mounts && user.items.mounts['Turkey-Gilded']) {
set = {
migration: migrationName,
'items.gear.owned.head_special_turkeyHelmBase': false,
'items.gear.owned.armor_special_turkeyArmorBase': false,
'items.gear.owned.back_special_turkeyTailBase': false,
};
var push = [
{
type: 'marketGear',
path: 'gear.flat.head_special_turkeyHelmBase',
_id: monk.id(),
},
{
type: 'marketGear',
path: 'gear.flat.armor_special_turkeyArmorBase',
_id: monk.id(),
},
{
type: 'marketGear',
path: 'gear.flat.back_special_turkeyTailBase',
_id: monk.id(),
},
];
} else if (user && user.items && user.items.pets && user.items.pets['Turkey-Gilded']) {
set = {'migration':migrationName, 'items.mounts.Turkey-Gilded':true};
} else if (user && user.items && user.items.mounts && user.items.mounts['Turkey-Base']) {
set = {'migration':migrationName, 'items.pets.Turkey-Gilded':5};
} else if (user && user.items && user.items.pets && user.items.pets['Turkey-Base']) {
set = {'migration':migrationName, 'items.mounts.Turkey-Base':true};
} else {
set = {'migration':migrationName, 'items.pets.Turkey-Base':5};
}
dbUsers.update({_id: user._id}, {$set: set});
if (push) {
dbUsers.update({_id: user._id}, {$push: {pinnedItems: {$each: push}}});
}
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
}
function displayData() {
console.warn('\n' + count + ' users processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processUsers;

View File

@@ -0,0 +1,103 @@
var migrationName = '20171230_nye_hats.js';
var authorName = 'Sabe'; // in case script author needs to know when their ...
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
/*
* Award New Year's Eve party hats to users in sequence
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers(lastId) {
// specify a query to limit the affected users (empty for all users):
var query = {
'migration': {$ne:migrationName},
'auth.timestamps.loggedin': {$gt:new Date('2017-11-30')},
};
if (lastId) {
query._id = {
$gt: lastId
}
}
dbUsers.find(query, {
sort: {_id: 1},
limit: 250,
fields: [
'items.gear.owned',
] // specify fields we are interested in to limit retrieved data (empty if we're not reading data):
})
.then(updateUsers)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 1000;
var count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
var userPromises = users.map(updateUser);
var lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(function () {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
var set = {};
var push = {};
if (typeof user.items.gear.owned.head_special_nye2016 !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2017':false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2017', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.head_special_nye2015 !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2016':false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2016', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.head_special_nye2014 !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2015':false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2015', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.head_special_nye !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2014':false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye2014', '_id': monk.id()}};
} else {
set = {'migration':migrationName, 'items.gear.owned.head_special_nye':false};
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_nye', '_id': monk.id()}};
}
dbUsers.update({_id: user._id}, {$set: set, $push: push});
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
}
function displayData() {
console.warn('\n' + count + ' users processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processUsers;

View File

@@ -0,0 +1,79 @@
/*
* Convert purchased.plan.nextPaymentProcessing from a double to a date field for Apple subscribers
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers(lastId) {
// specify a query to limit the affected users (empty for all users):
var query = {
'purchased.plan.paymentMethod': "Apple",
'purchased.plan.nextPaymentProcessing': {$type: 'double'},
};
if (lastId) {
query._id = {
$gt: lastId
}
}
dbUsers.find(query, {
sort: {_id: 1},
limit: 250,
})
.then(updateUsers)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 100;
var count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
var userPromises = users.map(updateUser);
var lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(function () {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
var set = {
'purchased.plan.nextPaymentProcessing': new Date(user.purchased.plan.nextPaymentProcessing),
};
dbUsers.update({_id: user._id}, {$set: set});
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
}
function displayData() {
console.warn('\n' + count + ' users processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processUsers;

View File

@@ -0,0 +1,118 @@
var migrationName = '20180130_habit_birthday.js';
var authorName = 'Sabe'; // in case script author needs to know when their ...
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
/*
* Award party robes: most recent user doesn't have of 2014-2018. Also cake!
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers(lastId) {
// specify a query to limit the affected users (empty for all users):
var query = {
'migration':{$ne:migrationName},
'auth.timestamps.loggedin':{$gt:new Date('2018-01-01')}, // remove after first run to cover remaining users
};
if (lastId) {
query._id = {
$gt: lastId
}
}
dbUsers.find(query, {
sort: {_id: 1},
limit: 250,
fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data)
'items.gear.owned'
],
})
.then(updateUsers)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 1000;
var count = 0;
function updateUsers (users) {
if (!users || users.length === 0) {
console.warn('All appropriate users found and modified.');
displayData();
return;
}
var userPromises = users.map(updateUser);
var lastUser = users[users.length - 1];
return Promise.all(userPromises)
.then(function () {
processUsers(lastUser._id);
});
}
function updateUser (user) {
count++;
var push;
var set = {'migration':migrationName};
if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2017')) {
set['items.gear.owned.armor_special_birthday2018'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2018', '_id': monk.id()}};
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2016')) {
set['items.gear.owned.armor_special_birthday2017'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2017', '_id': monk.id()}};
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) {
set['items.gear.owned.armor_special_birthday2016'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2016', '_id': monk.id()}};
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) {
set['items.gear.owned.armor_special_birthday2015'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2015', '_id': monk.id()}};
} else {
set['items.gear.owned.armor_special_birthday'] = false;
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday', '_id': monk.id()}};
}
var inc = {
'items.food.Cake_Skeleton':1,
'items.food.Cake_Base':1,
'items.food.Cake_CottonCandyBlue':1,
'items.food.Cake_CottonCandyPink':1,
'items.food.Cake_Shade':1,
'items.food.Cake_White':1,
'items.food.Cake_Golden':1,
'items.food.Cake_Zombie':1,
'items.food.Cake_Desert':1,
'items.food.Cake_Red':1,
'achievements.habitBirthdays':1
};
dbUsers.update({_id: user._id}, {$set: set, $inc: inc, $push: push});
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
}
function displayData() {
console.warn('\n' + count + ' users processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processUsers;

View File

@@ -0,0 +1,58 @@
# Indexes
This file contains a list of indexes that are on Habitica's production Mongo server.
If we ever have an issue, use this list to reindex.
## Challenges
- `{ "group": 1, "official": -1, "timestamp": -1 }`
- `{ "leader": 1, "official": -1, "timestamp": -1 }`
- `{ "official": -1, "timestamp": -1 }`
## Groups
- `{ "privacy": 1, "type": 1, "memberCount": -1 }`
- `{ "privacy": 1 }`
- `{ "purchased.plan.customerId": 1 }`
- `{ "purchased.plan.paymentMethod": 1 }`
- `{ "purchased.plan.planId": 1, "purchased.plan.dateTerminated": 1 }`
- `{ "type": 1, "memberCount": -1, "_id": 1 }`
- `{ "type": 1 }`
## Tasks
- `{ "challenge.id": 1 }`
- `{ "challenge.taskId": 1 }`
- `{ "group.id": 1 }`
- `{ "group.taskId": 1 }`
- `{ "type": 1, "everyX": 1, "frequency": 1 }`
- `{ "userId": 1 }`
- `{ "yesterDaily": 1, "type": 1 }`
## Users
- `{ "_id": 1, "apiToken": 1 }`
- `{ "auth.facebook.emails.value": 1 }`
- `{ "auth.facebook.id": 1 }`
- `{ "auth.google.emails.value": 1 }`
- `{ "auth.google.id": 1 }`
- `{ "auth.local.email": 1 }`
- `{ "auth.local.lowerCaseUsername": 1 }`
- `{ "auth.local.username": 1 }`
- `{ "auth.timestamps.created": 1 }`
- `{ "auth.timestamps.loggedin": 1, "_lastPushNotification": 1, "preferences.timezoneOffset": 1 }`
- `{ "auth.timestamps.loggedin": 1 }`
- `{ "backer.tier": -1 }`
- `{ "challenges": 1, "_id": 1 }`
- `{ "contributor.admin": 1, "contributor.level": -1, "backer.npc": -1, "profile.name": 1 }`
- `{ "contributor.level": 1 }`
- `{ "flags.newStuff": 1 }`
- `{ "guilds": 1, "_id": 1 }`
- `{ "invitations.guilds.id": 1, "_id": 1 }`
- `{ "invitations.party.id": 1 }`
- `{ "loginIncentives": 1 }`
- `{ "migration": 1 }`
- {` "party._id": 1, "_id": 1 }`
- `{ "preferences.sleep": 1, "_id": 1, "flags.lastWeeklyRecap": 1, "preferences.emailNotifications.unsubscribeFromAll": 1, "preferences.emailNotifications.weeklyRecaps": 1 }`
- `{ "preferences.sleep": 1, "_id": 1, "lastCron": 1, "preferences.emailNotifications.importantAnnouncements": 1, "preferences.emailNotifications.unsubscribeFromAll": 1, "flags.recaptureEmailsPhase": 1 }`
- `{ "profile.name": 1 }`
- `{ "purchased.plan.customerId": 1 }`
- `{ "purchased.plan.paymentMethod": 1 }`
- `{ "stats.score.overall": 1 }`
- `{ "webhooks.type": 1 }`

View File

@@ -17,8 +17,5 @@ function setUpServer () {
setUpServer();
// Replace this with your migration
var processUsers = require('./groups/update-groups-with-group-plans');
processUsers()
.catch(function (err) {
console.log(err)
})
const processUsers = require('./tasks/tasks-set-everyX');
processUsers();

View File

@@ -2,7 +2,7 @@ var _id = '';
var update = {
$addToSet: {
'purchased.plan.mysteryItems':{
$each:['shield_mystery_201709','back_mystery_201709']
$each:['back_mystery_201801','headAccessory_mystery_201801']
}
}
};

98
migrations/s3-upload.js Normal file
View File

@@ -0,0 +1,98 @@
let Bluebird = require('bluebird');
let request = require('superagent');
let last = require('lodash/last');
let AWS = require('aws-sdk');
let config = require('../config');
const S3_DIRECTORY = 'mobileApp/images'; //config.S3.SPRITES_DIRECTORY;
AWS.config.update({
accessKeyId: config.S3.accessKeyId,
secretAccessKey: config.S3.secretAccessKey,
// region: config.get('S3_REGION'),
});
let BUCKET_NAME = config.S3.bucket;
let s3 = new AWS.S3();
// Adapted from http://stackoverflow.com/a/22210077/2601552
function uploadFile (buffer, fileName) {
return new Promise((resolve, reject) => {
s3.putObject({
Body: buffer,
Key: fileName,
Bucket: BUCKET_NAME,
}, (error) => {
if (error) {
reject(error);
} else {
// console.info(`${fileName} uploaded to ${BUCKET_NAME} succesfully.`);
resolve(fileName);
}
});
});
}
function getFileName (file) {
let piecesOfPath = file.split('/');
let name = last(piecesOfPath);
let fullName = S3_DIRECTORY + name;
return fullName;
}
function getFileFromUrl (url) {
return new Promise((resolve, reject) => {
request.get(url).end((err, res) => {
if (err) return reject(err);
let file = res.body;
resolve(file);
});
});
}
let commit = '78f94e365c72cc58f66857d5941105638db7d35c';
commit = 'df0dbaba636c9ce424cc7040f7bd7fc1aa311015';
let gihuburl = `https://api.github.com/repos/HabitRPG/habitica/commits/${commit}`
let currentIndex = 0;
function uploadToS3(start, end, filesUrls) {
let urls = filesUrls.slice(start, end);
if (urls.length === 0) {
console.log("done");
return;
}
let promises = urls.map(fullUrl => {
return getFileFromUrl(fullUrl)
.then((buffer) => {
return uploadFile(buffer, getFileName(fullUrl));
});
});
console.log(promises.length)
return Bluebird.all(promises)
.then(() => {
currentIndex += 50;
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
})
.catch(e => {
console.log(e);
});
}
request.get(gihuburl)
.end((err, res) => {
console.log(err);
let files = res.body.files;
let filesUrls = [''];
filesUrls = files.map(file => {
return file.raw_url;
})
uploadToS3(currentIndex, currentIndex + 50, filesUrls);
});

View File

@@ -1,4 +1,4 @@
var migrationName = '20170502_takeThis.js'; // Update per month
var migrationName = '20180102_takeThis.js'; // Update per month
var authorName = 'Sabe'; // in case script author needs to know when their ...
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
@@ -7,14 +7,14 @@ var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
*/
var monk = require('monk');
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
var connectionString = 'mongodb://sabrecat:z8e8jyRA8CTofMQ@ds013393-a0.mlab.com:13393/habitica?auto_reconnect=true';
var dbUsers = monk(connectionString).get('users', { castIds: false });
function processUsers(lastId) {
// specify a query to limit the affected users (empty for all users):
var query = {
'migration':{$ne:migrationName},
'challenges':{$in:['69999331-d4ea-45a0-8c3f-f725d22b56c8']} // Update per month
'challenges':{$in:['5f70ce5b-2d82-4114-8e44-ca65615aae62']} // Update per month
};
if (lastId) {
@@ -65,19 +65,29 @@ function updateUser (user) {
set = {'migration':migrationName};
} else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', '_id': monk.id()}};
} else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') {
set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', '_id': monk.id()}};
} else {
set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false};
var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', '_id': monk.id()}};
}
dbUsers.update({_id: user._id}, {$set:set});
if (push) {
dbUsers.update({_id: user._id}, {$set: set, $push: push});
} else {
dbUsers.update({_id: user._id}, {$set: set});
}
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');

View File

@@ -0,0 +1,88 @@
var migrationName = 'tasks-set-everyX';
var authorName = 'Sabe'; // in case script author needs to know when their ...
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
/*
* Iterates over all tasks and sets invalid everyX values (less than 0 or more than 9999 or not an int) field to 0
*/
var monk = require('monk');
var connectionString = 'mongodb://sabrecat:z8e8jyRA8CTofMQ@ds013393-a0.mlab.com:13393/habitica?auto_reconnect=true';
var dbTasks = monk(connectionString).get('tasks', { castIds: false });
function processTasks(lastId) {
// specify a query to limit the affected tasks (empty for all tasks):
var query = {
type: "daily",
everyX: {
$not: {
$gte: 0,
$lte: 9999,
$type: "int",
}
},
};
if (lastId) {
query._id = {
$gt: lastId
}
}
dbTasks.find(query, {
sort: {_id: 1},
limit: 250,
fields: [],
})
.then(updateTasks)
.catch(function (err) {
console.log(err);
return exiting(1, 'ERROR! ' + err);
});
}
var progressCount = 1000;
var count = 0;
function updateTasks (tasks) {
if (!tasks || tasks.length === 0) {
console.warn('All appropriate tasks found and modified.');
displayData();
return;
}
var taskPromises = tasks.map(updatetask);
var lasttask = tasks[tasks.length - 1];
return Promise.all(taskPromises)
.then(function () {
processTasks(lasttask._id);
});
}
function updatetask (task) {
count++;
var set = {'everyX': 0};
dbTasks.update({_id: task._id}, {$set:set});
if (count % progressCount == 0) console.warn(count + ' ' + task._id);
if (task._id == authorUuid) console.warn(authorName + ' processed');
}
function displayData() {
console.warn('\n' + count + ' tasks processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}
module.exports = processTasks;

View File

@@ -0,0 +1,38 @@
var migrationName = 'AccountTransfer';
var authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
var authorUuid = ''; //... own data is done
/*
* This migraition will copy user data from prod to test
*/
const monk = require('monk');
const connectionString = '';
const Users = monk(connectionString).get('users', { castIds: false });
import uniq from 'lodash/uniq';
import Bluebird from 'bluebird';
module.exports = async function accountTransfer () {
const fromAccountId = '';
const toAccountId = '';
const fromAccount = await Users.findOne({_id: fromAccountId});
const toAccount = await Users.findOne({_id: toAccountId});
const newMounts = Object.assign({}, fromAccount.items.mounts, toAccount.items.mounts);
const newPets = Object.assign({}, fromAccount.items.pets, toAccount.items.pets);
const newBackgrounds = Object.assign({}, fromAccount.purchased.background, toAccount.purchased.background);
await Users.update({_id: toAccountId}, {
$set: {
'items.pets': newPets,
'items.mounts': newMounts,
'purchased.background': newBackgrounds,
},
})
.then((result) => {
console.log(result);
});
};

View File

@@ -0,0 +1,93 @@
const migrationName = 'AchievementRestore';
const authorName = 'TheHollidayInn'; // in case script author needs to know when their ...
const authorUuid = ''; //... own data is done
/*
* This migraition will copy user data from prod to test
*/
import Bluebird from 'bluebird';
const monk = require('monk');
const connectionString = 'mongodb://localhost/new-habit';
const Users = monk(connectionString).get('users', { castIds: false });
const monkOld = require('monk');
const oldConnectionSting = 'mongodb://localhost/old-habit';
const UsersOld = monk(oldConnectionSting).get('users', { castIds: false });
function getAchievementUpdate (newUser, oldUser) {
const oldAchievements = oldUser.achievements;
const newAchievements = newUser.achievements;
let achievementsUpdate = Object.assign({}, newAchievements);
// ultimateGearSets
if (!achievementsUpdate.ultimateGearSets && oldAchievements.ultimateGearSets) {
achievementsUpdate.ultimateGearSets = oldAchievements.ultimateGearSets;
} else if (oldAchievements.ultimateGearSets) {
for (let index in oldAchievements.ultimateGearSets) {
if (oldAchievements.ultimateGearSets[index]) achievementsUpdate.ultimateGearSets[index] = true;
}
}
// challenges
if (!newAchievements.challenges) newAchievements.challenges = [];
if (!oldAchievements.challenges) oldAchievements.challenges = [];
achievementsUpdate.challenges = newAchievements.challenges.concat(oldAchievements.challenges);
// Quests
if (!achievementsUpdate.quests) achievementsUpdate.quests = {};
for (let index in oldAchievements.quests) {
if (!achievementsUpdate.quests[index]) {
achievementsUpdate.quests[index] = oldAchievements.quests[index];
} else {
achievementsUpdate.quests[index] += oldAchievements.quests[index];
}
}
// Rebirth level
if (achievementsUpdate.rebirthLevel) {
achievementsUpdate.rebirthLevel = Math.max(achievementsUpdate.rebirthLevel, oldAchievements.rebirthLevel);
} else if (oldAchievements.rebirthLevel) {
achievementsUpdate.rebirthLevel = oldAchievements.rebirthLevel;
}
//All others
const indexsToIgnore = ['ultimateGearSets', 'challenges', 'quests', 'rebirthLevel'];
for (let index in oldAchievements) {
if (indexsToIgnore.indexOf(index) !== -1) continue;
if (!achievementsUpdate[index]) {
achievementsUpdate[index] = oldAchievements[index];
continue;
}
if (Number.isInteger(oldAchievements[index])) {
achievementsUpdate[index] += oldAchievements[index];
} else {
if (oldAchievements[index] === true) achievementsUpdate[index] = true;
}
}
return achievementsUpdate;
}
module.exports = async function achievementRestore () {
const userIds = [
];
for (let index in userIds) {
const userId = userIds[index];
const oldUser = await UsersOld.findOne({_id: userId}, 'achievements');
const newUser = await Users.findOne({_id: userId}, 'achievements');
const achievementUpdate = getAchievementUpdate(newUser, oldUser);
await Users.update(
{_id: userId},
{
$set: {
'achievements': achievementUpdate,
},
});
console.log(`Updated ${userId}`);
}
};

13885
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

19954
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "3.114.1",
"version": "4.23.0",
"main": "./website/server/index.js",
"dependencies": {
"@slack/client": "^3.8.1",
@@ -14,6 +14,7 @@
"autoprefixer": "^6.4.0",
"aws-sdk": "^2.0.25",
"axios": "^0.16.0",
"axios-progress-bar": "^0.1.7",
"babel-core": "^6.0.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^6.0.0",
@@ -26,43 +27,27 @@
"babel-preset-es2015": "^6.6.0",
"babel-register": "^6.6.0",
"babel-runtime": "^6.11.6",
"babelify": "^7.2.0",
"bcrypt": "^1.0.2",
"bluebird": "^3.3.5",
"body-parser": "^1.15.0",
"bootstrap": "^4.0.0-alpha.6",
"bootstrap-vue": "^0.18.0",
"bower": "~1.3.12",
"browserify": "~12.0.1",
"bootstrap": "^4.0.0",
"bootstrap-vue": "^1.5.0",
"compression": "^1.6.1",
"connect-ratelimit": "0.0.7",
"cookie-session": "^1.2.0",
"coupon-code": "^0.4.5",
"cross-env": "^4.0.0",
"css-loader": "^0.28.0",
"csv-stringify": "^1.0.2",
"cwait": "~1.0.1",
"domain-middleware": "~0.1.0",
"estraverse": "^4.1.1",
"express": "~4.14.0",
"express-basic-auth": "^1.0.1",
"express-csv": "~0.6.0",
"express-validator": "^2.18.0",
"extract-text-webpack-plugin": "^2.0.0-rc.3",
"file-loader": "^0.10.0",
"glob": "^4.3.5",
"got": "^6.1.1",
"grunt": "~0.4.1",
"grunt-cli": "~0.1.9",
"grunt-contrib-clean": "~0.6.0",
"grunt-contrib-copy": "~0.6.0",
"grunt-contrib-cssmin": "~0.10.0",
"grunt-contrib-stylus": "~0.20.0",
"grunt-contrib-uglify": "~0.6.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-hashres": "habitrpg/grunt-hashres#v0.4.2",
"gulp": "^3.9.0",
"gulp-babel": "^6.1.2",
"gulp-grunt": "^0.5.2",
"gulp-imagemin": "^2.4.0",
"gulp-nodemon": "^2.0.4",
"gulp-sourcemaps": "^1.6.0",
@@ -75,41 +60,37 @@
"in-app-purchase": "^1.1.6",
"intro.js": "^2.6.0",
"jade": "~1.11.0",
"jquery": "^3.1.1",
"jquery": ">=3.0.0",
"js2xmlparser": "~1.0.0",
"lodash": "^4.17.4",
"merge-stream": "^1.0.0",
"method-override": "^2.3.5",
"moment": "^2.13.0",
"moment-recur": "habitrpg/moment-recur#v1.0.6",
"moment-recur": "git://github.com/habitrpg/moment-recur.git#f147ef27bbc26ca67638385f3db4a44084c76626",
"mongoose": "~4.8.6",
"mongoose-id-autoinc": "~2013.7.14-4",
"morgan": "^1.7.0",
"nconf": "~0.8.2",
"nib": "^1.1.0",
"node-gcm": "^0.14.4",
"node-sass": "^4.5.0",
"nodemailer": "^2.3.2",
"object-path": "^0.9.2",
"ora": "^1.1.0",
"pageres": "^4.1.1",
"passport": "^0.3.2",
"passport-facebook": "^2.0.0",
"passport-google-oauth20": "1.0.0",
"paypal-ipn": "3.0.0",
"paypal-rest-sdk": "^1.2.1",
"paypal-rest-sdk": "^1.8.1",
"popper.js": "^1.13.0",
"postcss-easy-import": "^2.0.0",
"pretty-data": "^0.40.0",
"ps-tree": "^1.0.0",
"pug": "^2.0.0-beta.12",
"push-notify": "habitrpg/push-notify#v1.2.0",
"push-notify": "git://github.com/habitrpg/push-notify.git#6bc2b5fdb1bdc9649b9ec1964d79ca50187fc8a9",
"pusher": "^1.3.0",
"request": "~2.74.0",
"rimraf": "^2.4.3",
"run-sequence": "^1.1.4",
"s3-upload-stream": "^1.0.6",
"sass-loader": "^6.0.2",
"serve-favicon": "^2.3.0",
"shelljs": "^0.7.6",
"stripe": "^4.2.0",
"superagent": "^3.4.3",
@@ -121,16 +102,14 @@
"useragent": "^2.1.9",
"uuid": "^3.0.1",
"validator": "^4.9.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"vue": "^2.1.0",
"vue-loader": "^11.0.0",
"vue": "^2.5.2",
"vue-loader": "^13.3.0",
"vue-mugen-scroll": "^0.2.1",
"vue-notification": "^1.3.2",
"vue-router": "^2.0.0-rc.5",
"vue-router": "^3.0.0",
"vue-style-loader": "^3.0.0",
"vue-template-compiler": "^2.1.10",
"vuejs-datepicker": "^0.9.4",
"vue-template-compiler": "^2.5.2",
"vuedraggable": "^2.15.0",
"vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker.git#5d237615463a84a23dd6f3f77c6ab577d68593ec",
"webpack": "^2.2.1",
"webpack-merge": "^4.0.0",
"winston": "^2.1.0",
@@ -140,11 +119,11 @@
"private": true,
"engines": {
"node": "^6.9.1",
"npm": "^4.0.2"
"npm": "^5.0.0"
},
"scripts": {
"lint": "eslint --ext .js,.vue .",
"test": "npm run lint && gulp test && npm run client:unit && gulp apidoc",
"test": "npm run lint && gulp test && gulp apidoc",
"test:build": "gulp test:prepare:build",
"test:api-v3": "gulp test:api-v3",
"test:api-v3:unit": "gulp test:api-v3:unit",
@@ -153,22 +132,17 @@
"test:sanity": "istanbul cover --dir coverage/sanity --report lcovonly node_modules/mocha/bin/_mocha -- test/sanity --recursive",
"test:common": "istanbul cover --dir coverage/common --report lcovonly node_modules/mocha/bin/_mocha -- test/common --recursive",
"test:content": "istanbul cover --dir coverage/content --report lcovonly node_modules/mocha/bin/_mocha -- test/content --recursive",
"test:karma": "karma start test/client-old/spec/karma.conf.js --single-run",
"test:karma:watch": "karma start test/client-old/spec/karma.conf.js",
"test:prepare:webdriver": "webdriver-manager update",
"test:e2e:webdriver": "webdriver-manager start",
"test:e2e": "protractor test/client-old/e2e/protractor.conf.js",
"test:nodemon": "gulp test:nodemon",
"coverage": "COVERAGE=true mocha --require register-handlers.js --reporter html-cov > coverage.html; open coverage.html",
"sprites": "gulp sprites:compile",
"client:dev": "gulp bootstrap && node webpack/dev-server.js",
"client:build": "gulp bootstrap && node webpack/build.js",
"client:dev": "node webpack/dev-server.js",
"client:build": "gulp build:client",
"client:unit": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js --single-run",
"client:unit:watch": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js",
"client:e2e": "node test/client/e2e/runner.js",
"client:test": "npm run client:unit && npm run client:e2e",
"start": "gulp run:dev",
"postinstall": "bower --config.interactive=false install -f && gulp build && npm run client:build",
"start": "gulp nodemon",
"postinstall": "gulp build",
"apidoc": "gulp apidoc"
},
"devDependencies": {
@@ -179,22 +153,17 @@
"chromedriver": "^2.27.2",
"connect-history-api-fallback": "^1.1.0",
"coveralls": "^2.11.2",
"cross-env": "^4.0.0",
"cross-spawn": "^5.0.1",
"csv": "~0.3.6",
"deep-diff": "~0.1.4",
"eslint": "^3.0.0",
"eslint-config-habitrpg": "^3.0.0",
"eslint-friendly-formatter": "^2.0.5",
"eslint-loader": "^1.3.0",
"eslint-plugin-html": "^2.0.0",
"eslint-plugin-mocha": "^4.7.0",
"event-stream": "^3.2.2",
"eventsource-polyfill": "^0.9.6",
"expect.js": "~0.2.0",
"grunt-karma": "~0.12.1",
"http-proxy-middleware": "^0.17.0",
"inject-loader": "^3.0.0-beta4",
"istanbul": "^1.1.0-alpha.1",
"karma": "^1.3.0",
"karma-babel-preprocessor": "^6.0.1",
@@ -209,23 +178,17 @@
"karma-spec-reporter": "0.0.24",
"karma-webpack": "^2.0.2",
"lcov-result-merger": "^1.0.2",
"lolex": "^1.4.0",
"mocha": "^3.2.0",
"mongodb": "^2.0.46",
"mongodb": "^2.2.33",
"mongoskin": "~2.1.0",
"monk": "^4.0.0",
"nightwatch": "^0.9.12",
"phantomjs-prebuilt": "^2.1.12",
"protractor": "^3.1.1",
"raw-loader": "^0.5.1",
"require-again": "^2.0.0",
"rewire": "^2.3.3",
"selenium-server": "^3.0.1",
"sinon": "^1.17.2",
"sinon-chai": "^2.8.0",
"sinon-stub-promise": "^4.0.0",
"superagent-defaults": "^0.1.13",
"vinyl-transform": "^1.0.0",
"webpack-bundle-analyzer": "^2.2.1",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.6.1"

View File

@@ -48,8 +48,10 @@ describe('GET /challenges/:challengeId', () => {
});
expect(chal.group).to.eql({
_id: group._id,
categories: [],
id: group.id,
name: group.name,
summary: group.name,
type: group.type,
privacy: group.privacy,
leader: groupLeader.id,
@@ -100,8 +102,10 @@ describe('GET /challenges/:challengeId', () => {
});
expect(chal.group).to.eql({
_id: group._id,
categories: [],
id: group.id,
name: group.name,
summary: group.name,
type: group.type,
privacy: group.privacy,
leader: groupLeader.id,
@@ -153,7 +157,9 @@ describe('GET /challenges/:challengeId', () => {
expect(chal.group).to.eql({
_id: group._id,
id: group.id,
categories: [],
name: group.name,
summary: group.name,
type: group.type,
privacy: group.privacy,
leader: groupLeader.id,

View File

@@ -64,11 +64,11 @@ describe('GET /challenges/:challengeId/export/csv', () => {
let sortedMembers = _.sortBy([members[0], members[1], members[2], groupLeader], '_id');
let splitRes = res.split('\n');
expect(splitRes[0]).to.equal('UUID,name,Task,Value,Notes,Task,Value,Notes');
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},habit:Task 1,0,,todo:Task 2,0,`);
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},habit:Task 1,0,,todo:Task 2,0,`);
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},habit:Task 1,0,,todo:Task 2,0,`);
expect(splitRes[4]).to.equal(`${sortedMembers[3]._id},${sortedMembers[3].profile.name},habit:Task 1,0,,todo:Task 2,0,`);
expect(splitRes[0]).to.equal('UUID,name,Task,Value,Notes,Streak,Task,Value,Notes,Streak');
expect(splitRes[1]).to.equal(`${sortedMembers[0]._id},${sortedMembers[0].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
expect(splitRes[2]).to.equal(`${sortedMembers[1]._id},${sortedMembers[1].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
expect(splitRes[3]).to.equal(`${sortedMembers[2]._id},${sortedMembers[2].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
expect(splitRes[4]).to.equal(`${sortedMembers[3]._id},${sortedMembers[3].profile.name},habit:Task 1,0,,0,todo:Task 2,0,,0`);
expect(splitRes[5]).to.equal('');
});
});

View File

@@ -142,4 +142,25 @@ describe('GET /challenges/:challengeId/members', () => {
let resIds = res.concat(res2).map(member => member._id);
expect(resIds).to.eql(expectedIds.sort());
});
it('supports using req.query.search to get search members', async () => {
let group = await generateGroup(user, {type: 'party', name: generateUUID()});
let challenge = await generateChallenge(user, group);
let usersToGenerate = [];
for (let i = 0; i < 3; i++) {
usersToGenerate.push(generateUser({
challenges: [challenge._id],
'profile.name': `${i}profilename`,
}));
}
let generatedUsers = await Promise.all(usersToGenerate);
let profileNames = generatedUsers.map(generatedUser => generatedUser.profile.name);
let firstProfileName = profileNames[0];
let nameToSearch = firstProfileName.substring(0, 4);
let response = await user.get(`/challenges/${challenge._id}/members?search=${nameToSearch}`);
expect(response[0].profile.name).to.eql(firstProfileName);
});
});

View File

@@ -95,13 +95,23 @@ describe('GET /challenges/:challengeId/members/:memberId', () => {
expect(memberProgress.tasks[0].challenge.taskId).to.equal(chalTasks[0]._id);
});
it('returns the tasks without the tags', async () => {
it('returns the tasks without the tags and checklist', async () => {
let group = await generateGroup(user, {type: 'party', name: generateUUID()});
let challenge = await generateChallenge(user, group);
let taskText = 'Test Text';
await user.post(`/tasks/challenge/${challenge._id}`, [{type: 'habit', text: taskText}]);
await user.post(`/tasks/challenge/${challenge._id}`, [{
type: 'todo',
text: taskText,
checklist: [
{
_id: 123,
text: 'test',
},
],
}]);
let memberProgress = await user.get(`/challenges/${challenge._id}/members/${user._id}`);
expect(memberProgress.tasks[0]).not.to.have.key('tags');
expect(memberProgress.tasks[0].checklist).to.eql([]);
});
});

View File

@@ -4,6 +4,7 @@ import {
createAndPopulateGroup,
translate as t,
} from '../../../../helpers/api-v3-integration.helper';
import { TAVERN_ID } from '../../../../../website/common/script/constants';
describe('GET challenges/groups/:groupId', () => {
context('Public Guild', () => {
@@ -181,4 +182,123 @@ describe('GET challenges/groups/:groupId', () => {
expect(foundChallengeIndex).to.eql(1);
});
});
context('Party', () => {
let party, user, nonMember, challenge, challenge2;
before(async () => {
let { group, groupLeader } = await createAndPopulateGroup({
groupDetails: {
name: 'TestParty',
type: 'party',
},
});
party = group;
user = groupLeader;
nonMember = await generateUser();
challenge = await generateChallenge(user, group);
challenge2 = await generateChallenge(user, group);
});
it('should prevent non-member from seeing challenges', async () => {
await expect(nonMember.get(`/challenges/groups/${party._id}`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('should return group challenges for member with populated leader', async () => {
let challenges = await user.get(`/challenges/groups/${party._id}`);
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.exist;
expect(foundChallenge1.leader).to.eql({
_id: party.leader._id,
id: party.leader._id,
profile: {name: user.profile.name},
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.exist;
expect(foundChallenge2.leader).to.eql({
_id: party.leader._id,
id: party.leader._id,
profile: {name: user.profile.name},
});
});
it('should return group challenges for member using ID "party"', async () => {
let challenges = await user.get('/challenges/groups/party');
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.exist;
expect(foundChallenge1.leader).to.eql({
_id: party.leader._id,
id: party.leader._id,
profile: {name: user.profile.name},
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.exist;
expect(foundChallenge2.leader).to.eql({
_id: party.leader._id,
id: party.leader._id,
profile: {name: user.profile.name},
});
});
});
context('Tavern', () => {
let tavern, user, challenge, challenge2;
before(async () => {
user = await generateUser();
await user.update({balance: 0.5});
tavern = await user.get(`/groups/${TAVERN_ID}`);
challenge = await generateChallenge(user, tavern, {prize: 1});
challenge2 = await generateChallenge(user, tavern, {prize: 1});
});
it('should return tavern challenges with populated leader', async () => {
let challenges = await user.get(`/challenges/groups/${TAVERN_ID}`);
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.exist;
expect(foundChallenge1.leader).to.eql({
_id: user._id,
id: user._id,
profile: {name: user.profile.name},
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.exist;
expect(foundChallenge2.leader).to.eql({
_id: user._id,
id: user._id,
profile: {name: user.profile.name},
});
});
it('should return tavern challenges using ID "habitrpg', async () => {
let challenges = await user.get('/challenges/groups/habitrpg');
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.exist;
expect(foundChallenge1.leader).to.eql({
_id: user._id,
id: user._id,
profile: {name: user.profile.name},
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.exist;
expect(foundChallenge2.leader).to.eql({
_id: user._id,
id: user._id,
profile: {name: user.profile.name},
});
});
});
});

View File

@@ -41,10 +41,12 @@ describe('GET challenges/user', () => {
});
expect(foundChallenge.group).to.eql({
_id: publicGuild._id,
categories: [],
id: publicGuild._id,
type: publicGuild.type,
privacy: publicGuild.privacy,
name: publicGuild.name,
summary: publicGuild.name,
leader: publicGuild.leader._id,
});
});
@@ -61,10 +63,12 @@ describe('GET challenges/user', () => {
});
expect(foundChallenge1.group).to.eql({
_id: publicGuild._id,
categories: [],
id: publicGuild._id,
type: publicGuild.type,
privacy: publicGuild.privacy,
name: publicGuild.name,
summary: publicGuild.name,
leader: publicGuild.leader._id,
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
@@ -76,10 +80,12 @@ describe('GET challenges/user', () => {
});
expect(foundChallenge2.group).to.eql({
_id: publicGuild._id,
categories: [],
id: publicGuild._id,
type: publicGuild.type,
privacy: publicGuild.privacy,
name: publicGuild.name,
summary: publicGuild.name,
leader: publicGuild.leader._id,
});
});
@@ -96,10 +102,12 @@ describe('GET challenges/user', () => {
});
expect(foundChallenge1.group).to.eql({
_id: publicGuild._id,
categories: [],
id: publicGuild._id,
type: publicGuild.type,
privacy: publicGuild.privacy,
name: publicGuild.name,
summary: publicGuild.name,
leader: publicGuild.leader._id,
});
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
@@ -111,14 +119,26 @@ describe('GET challenges/user', () => {
});
expect(foundChallenge2.group).to.eql({
_id: publicGuild._id,
categories: [],
id: publicGuild._id,
type: publicGuild.type,
privacy: publicGuild.privacy,
name: publicGuild.name,
summary: publicGuild.name,
leader: publicGuild.leader._id,
});
});
it('should return not return challenges in user groups if we send member true param', async () => {
let challenges = await member.get(`/challenges/user?member=${true}`);
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.not.exist;
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.not.exist;
});
it('should return newest challenges first', async () => {
let challenges = await user.get('/challenges/user');
@@ -137,6 +157,7 @@ describe('GET challenges/user', () => {
let { group, groupLeader } = await createAndPopulateGroup({
groupDetails: {
name: 'TestPrivateGuild',
summary: 'summary for TestPrivateGuild',
type: 'guild',
privacy: 'private',
},
@@ -158,6 +179,7 @@ describe('GET challenges/user', () => {
let { group, groupLeader } = await createAndPopulateGroup({
groupDetails: {
name: 'TestGuild',
summary: 'summary for TestGuild',
type: 'guild',
privacy: 'public',
},

View File

@@ -101,19 +101,21 @@ describe('POST /challenges/:challengeId/join', () => {
});
it('syncs challenge tasks to joining user', async () => {
let taskText = 'A challenge task text';
const taskText = 'A challenge task text';
await groupLeader.post(`/tasks/challenge/${challenge._id}`, [
{type: 'habit', text: taskText},
{type: 'daily', text: taskText},
]);
await authorizedUser.post(`/challenges/${challenge._id}/join`);
let tasks = await authorizedUser.get('/tasks/user');
let tasksTexts = tasks.map((task) => {
return task.text;
const tasks = await authorizedUser.get('/tasks/user');
const syncedTask = tasks.find((task) => {
return task.text === taskText;
});
expect(tasksTexts).to.include(taskText);
expect(syncedTask.text).to.eql(taskText);
expect(syncedTask.isDue).to.exist;
expect(syncedTask.nextDue).to.exist;
});
it('adds challenge tag to user tags', async () => {

View File

@@ -149,13 +149,19 @@ describe('POST /challenges/:challengeId/winner/:winnerId', () => {
await sleep(0.5);
let tasks = await winningUser.get('/tasks/user');
let testTask = _.find(tasks, (task) => {
const tasks = await winningUser.get('/tasks/user');
const testTask = _.find(tasks, (task) => {
return task.text === taskText;
});
const updatedUser = await winningUser.sync();
const challengeTag = updatedUser.tags.find(tags => {
return tags.id === challenge._id;
});
expect(testTask.challenge.broken).to.eql('CHALLENGE_CLOSED');
expect(testTask.challenge.winner).to.eql(winningUser.profile.name);
expect(challengeTag.challenge).to.eql('false');
});
});
});

View File

@@ -1,5 +1,6 @@
import {
createAndPopulateGroup,
generateUser,
translate as t,
sleep,
server,
@@ -12,6 +13,7 @@ import {
import { v4 as generateUUID } from 'uuid';
import { getMatchesByWordArray, removePunctuationFromString } from '../../../../../website/server/libs/stringUtils';
import bannedWords from '../../../../../website/server/libs/bannedWords';
import guildsAllowingBannedWords from '../../../../../website/server/libs/guildsAllowingBannedWords';
import * as email from '../../../../../website/server/libs/email';
import { IncomingWebhook } from '@slack/client';
import nconf from 'nconf';
@@ -96,6 +98,24 @@ describe('POST /chat', () => {
});
});
it('returns an error when chat message contains a banned word in a public guild', async () => {
let { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'public guild',
type: 'guild',
privacy: 'public',
},
members: 1,
});
await expect(members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage}))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: bannedWordErrorMessage,
});
});
it('errors when word is part of a phrase', async () => {
let wordInPhrase = `phrase ${testBannedWordMessage} end`;
await expect(user.post('/groups/habitrpg/chat', { message: wordInPhrase}))
@@ -161,7 +181,7 @@ describe('POST /chat', () => {
expect(message.message.id).to.exist;
});
it('does not error when sending a chat message containing a banned word to a public guild', async () => {
it('does not error when sending a chat message containing a banned word to a public guild in which banned words are allowed', async () => {
let { group, members } = await createAndPopulateGroup({
groupDetails: {
name: 'public guild',
@@ -171,6 +191,8 @@ describe('POST /chat', () => {
members: 1,
});
guildsAllowingBannedWords[group._id] = true;
let message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage});
expect(message.message.id).to.exist;
@@ -224,7 +246,7 @@ describe('POST /chat', () => {
color: 'danger',
author_name: `${user.profile.name} - ${user.auth.local.email} - ${user._id}`,
title: 'Slur in Test Guild',
title_link: `${BASE_URL}/#/options/groups/guilds/${groupWithChat.id}`,
title_link: `${BASE_URL}/groups/guild/${groupWithChat.id}`,
text: testSlurMessage,
// footer: sandbox.match(/<.*?groupId=group-id&chatId=chat-id\|Flag this message>/),
mrkdwn_in: [
@@ -342,6 +364,24 @@ describe('POST /chat', () => {
expect(message.message.id).to.exist;
});
it('adds backer info to chat', async () => {
const backerInfo = {
npc: 'Town Crier',
tier: 800,
tokensApplied: true,
};
const backer = await generateUser({
backer: backerInfo,
});
const message = await backer.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage});
const messageBackerInfo = message.message.backer;
expect(messageBackerInfo.npc).to.equal(backerInfo.npc);
expect(messageBackerInfo.tier).to.equal(backerInfo.tier);
expect(messageBackerInfo.tokensApplied).to.equal(backerInfo.tokensApplied);
});
it('sends group chat received webhooks', async () => {
let userUuid = generateUUID();
let memberUuid = generateUUID();

View File

@@ -3,6 +3,7 @@ import {
generateUser,
translate as t,
} from '../../../../helpers/api-v3-integration.helper';
import config from '../../../../../config.json';
import { v4 as generateUUID } from 'uuid';
describe('POST /groups/:id/chat/:id/clearflags', () => {
@@ -74,7 +75,7 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
expect(messages[0].flagCount).to.eql(0);
});
it('can unflag a system message', async () => {
it('can\'t flag a system message', async () => {
let { group, members } = await createAndPopulateGroup({
groupDetails: {
type: 'party',
@@ -95,13 +96,15 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
await member.post('/user/class/cast/mpheal');
let [skillMsg] = await member.get(`/groups/${group.id}/chat`);
await member.post(`/groups/${group._id}/chat/${skillMsg.id}/flag`);
await admin.post(`/groups/${group._id}/chat/${skillMsg.id}/clearflags`);
let messages = await members[0].get(`/groups/${group._id}/chat`);
expect(messages[0].id).to.eql(skillMsg.id);
expect(messages[0].flagCount).to.eql(0);
await expect(member.post(`/groups/${group._id}/chat/${skillMsg.id}/flag`))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('messageCannotFlagSystemMessages', {communityManagerEmail: config.EMAILS.COMMUNITY_MANAGER_EMAIL}),
});
// let messages = await members[0].get(`/groups/${group._id}/chat`);
// expect(messages[0].id).to.eql(skillMsg.id);
// expect(messages[0].flagCount).to.eql(0);
});
});

View File

@@ -1,8 +1,8 @@
import {
generateUser,
translate as t,
resetHabiticaDB,
} from '../../../../helpers/api-v3-integration.helper';
import apiMessages from '../../../../../website/server/libs/apiMessages';
describe('GET /coupons/', () => {
let user;
@@ -19,7 +19,7 @@ describe('GET /coupons/', () => {
await expect(user.get('/coupons')).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('noSudoAccess'),
message: apiMessages('noSudoAccess'),
});
});

View File

@@ -4,6 +4,7 @@ import {
resetHabiticaDB,
} from '../../../../helpers/api-v3-integration.helper';
import couponCode from 'coupon-code';
import apiMessages from '../../../../../website/server/libs/apiMessages';
describe('POST /coupons/generate/:event', () => {
let user;
@@ -25,7 +26,7 @@ describe('POST /coupons/generate/:event', () => {
await expect(user.post('/coupons/generate/aaa')).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('noSudoAccess'),
message: apiMessages('noSudoAccess'),
});
});

View File

@@ -4,7 +4,7 @@ import {
} from '../../../../helpers/api-v3-integration.helper';
import { v4 as generateUUID } from 'uuid';
describe('GET /export/avatar-:memberId.html', () => {
xdescribe('GET /export/avatar-:memberId.html', () => {
let user;
before(async () => {

View File

@@ -0,0 +1,32 @@
import {
generateUser,
generateGroup,
} from '../../../../helpers/api-v3-integration.helper';
describe('GET /group-plans', () => {
let user;
let groupPlan;
before(async () => {
user = await generateUser({balance: 4});
groupPlan = await generateGroup(user,
{
name: 'public guild - is member',
type: 'guild',
privacy: 'public',
},
{
purchased: {
plan: {
customerId: 'existings',
},
},
});
});
it('returns group plans for the user', async () => {
let groupPlans = await user.get('/group-plans');
expect(groupPlans[0]._id).to.eql(groupPlan._id);
});
});

View File

@@ -72,7 +72,7 @@ describe('GET /groups/:groupId/members', () => {
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
'backer', 'contributor', 'auth', 'items', 'inbox',
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives',
]);
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
expect(Object.keys(memberRes.preferences).sort()).to.eql([
@@ -93,7 +93,7 @@ describe('GET /groups/:groupId/members', () => {
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
'backer', 'contributor', 'auth', 'items', 'inbox',
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives',
]);
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
expect(Object.keys(memberRes.preferences).sort()).to.eql([
@@ -161,4 +161,19 @@ describe('GET /groups/:groupId/members', () => {
let resIds = res.concat(res2).map(member => member._id);
expect(resIds).to.eql(expectedIds.sort());
});
it('searches members', async () => {
let group = await generateGroup(user, {type: 'party', name: generateUUID()});
let usersToGenerate = [];
for (let i = 0; i < 2; i++) {
usersToGenerate.push(generateUser({party: {_id: group._id}}));
}
const usersCreated = await Promise.all(usersToGenerate);
const userToSearch = usersCreated[0].profile.name;
let res = await user.get(`/groups/party/members?search=${userToSearch}`);
expect(res.length).to.equal(1);
expect(res[0].profile.name).to.equal(userToSearch);
});
});

View File

@@ -10,6 +10,8 @@ import { v4 as generateUUID } from 'uuid';
import {
each,
} from 'lodash';
import { model as User } from '../../../../../website/server/models/user';
import * as payments from '../../../../../website/server/libs/payments';
describe('POST /groups/:groupId/leave', () => {
let typesOfGroups = {
@@ -264,4 +266,45 @@ describe('POST /groups/:groupId/leave', () => {
expect(userWithNonExistentParty.party).to.eql({});
});
});
context('Leaving a group plan', () => {
it('cancels the free subscription', async () => {
// Create group
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: {
name: 'Test Private Guild',
type: 'guild',
},
members: 1,
});
let leader = groupLeader;
let member = members[0];
let userWithFreePlan = await User.findById(leader._id).exec();
// Create subscription
let paymentData = {
user: userWithFreePlan,
groupId: group._id,
sub: {
key: 'basic_3mo',
},
customerId: 'customer-id',
paymentMethod: 'Payment Method',
headers: {
'x-client': 'habitica-web',
'user-agent': '',
},
};
await payments.createSubscription(paymentData);
await member.sync();
expect(member.purchased.plan.planId).to.equal('group_plan_auto');
expect(member.purchased.plan.dateTerminated).to.not.exist;
// Leave
await member.post(`/groups/${group._id}/leave`);
await member.sync();
expect(member.purchased.plan.dateTerminated).to.exist;
});
});
});

View File

@@ -32,7 +32,7 @@ describe('GET /members/:memberId', () => {
let memberRes = await user.get(`/members/${member._id}`);
expect(memberRes).to.have.all.keys([ // works as: object has all and only these keys
'_id', 'id', 'preferences', 'profile', 'stats', 'achievements', 'party',
'backer', 'contributor', 'auth', 'items', 'inbox',
'backer', 'contributor', 'auth', 'items', 'inbox', 'loginIncentives',
]);
expect(Object.keys(memberRes.auth)).to.eql(['timestamps']);
expect(Object.keys(memberRes.preferences).sort()).to.eql([

View File

@@ -118,4 +118,56 @@ describe('POST /members/send-private-message', () => {
expect(sendersMessageInReceiversInbox).to.exist;
expect(sendersMessageInSendersInbox).to.exist;
});
it('allows admin to send when sender has blocked the admin', async () => {
userToSendMessage = await generateUser({
'contributor.admin': 1,
});
const receiver = await generateUser({'inbox.blocks': [userToSendMessage._id]});
await userToSendMessage.post('/members/send-private-message', {
message: messageToSend,
toUserId: receiver._id,
});
const updatedReceiver = await receiver.get('/user');
const updatedSender = await userToSendMessage.get('/user');
const sendersMessageInReceiversInbox = _.find(updatedReceiver.inbox.messages, (message) => {
return message.uuid === userToSendMessage._id && message.text === messageToSend;
});
const sendersMessageInSendersInbox = _.find(updatedSender.inbox.messages, (message) => {
return message.uuid === receiver._id && message.text === messageToSend;
});
expect(sendersMessageInReceiversInbox).to.exist;
expect(sendersMessageInSendersInbox).to.exist;
});
it('allows admin to send when to user has opted out of messaging', async () => {
userToSendMessage = await generateUser({
'contributor.admin': 1,
});
const receiver = await generateUser({'inbox.optOut': true});
await userToSendMessage.post('/members/send-private-message', {
message: messageToSend,
toUserId: receiver._id,
});
const updatedReceiver = await receiver.get('/user');
const updatedSender = await userToSendMessage.get('/user');
const sendersMessageInReceiversInbox = _.find(updatedReceiver.inbox.messages, (message) => {
return message.uuid === userToSendMessage._id && message.text === messageToSend;
});
const sendersMessageInSendersInbox = _.find(updatedSender.inbox.messages, (message) => {
return message.uuid === receiver._id && message.text === messageToSend;
});
expect(sendersMessageInReceiversInbox).to.exist;
expect(sendersMessageInSendersInbox).to.exist;
});
});

View File

@@ -13,14 +13,44 @@ describe('POST /notifications/:notificationId/read', () => {
it('errors when notification is not found', async () => {
let dummyId = generateUUID();
await expect(user.post(`/notifications/${dummyId}/read`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('messageNotificationNotFound'),
});
await expect(user.post(`/notifications/${dummyId}/read`)).to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('messageNotificationNotFound'),
});
});
xit('removes a notification', async () => {
it('removes a notification', async () => {
expect(user.notifications.length).to.equal(0);
const id = generateUUID();
const id2 = generateUUID();
await user.update({
notifications: [{
id,
type: 'DROPS_ENABLED',
data: {},
}, {
id: id2,
type: 'LOGIN_INCENTIVE',
data: {},
}],
});
await user.sync();
expect(user.notifications.length).to.equal(2);
const res = await user.post(`/notifications/${id}/read`);
expect(res).to.deep.equal([{
id: id2,
type: 'LOGIN_INCENTIVE',
data: {},
}]);
await user.sync();
expect(user.notifications.length).to.equal(1);
expect(user.notifications[0].id).to.equal(id2);
});
});

View File

@@ -0,0 +1,66 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-v3-integration.helper';
import { v4 as generateUUID } from 'uuid';
describe('POST /notifications/:notificationId/read', () => {
let user;
before(async () => {
user = await generateUser();
});
it('errors when notification is not found', async () => {
let dummyId = generateUUID();
await expect(user.post('/notifications/read', {
notificationIds: [dummyId],
})).to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('messageNotificationNotFound'),
});
});
it('removes multiple notifications', async () => {
expect(user.notifications.length).to.equal(0);
const id = generateUUID();
const id2 = generateUUID();
const id3 = generateUUID();
await user.update({
notifications: [{
id,
type: 'DROPS_ENABLED',
data: {},
}, {
id: id2,
type: 'LOGIN_INCENTIVE',
data: {},
}, {
id: id3,
type: 'CRON',
data: {},
}],
});
await user.sync();
expect(user.notifications.length).to.equal(3);
const res = await user.post('/notifications/read', {
notificationIds: [id, id3],
});
expect(res).to.deep.equal([{
id: id2,
type: 'LOGIN_INCENTIVE',
data: {},
}]);
await user.sync();
expect(user.notifications.length).to.equal(1);
expect(user.notifications[0].id).to.equal(id2);
});
});

View File

@@ -6,7 +6,7 @@ import superagent from 'superagent';
import nconf from 'nconf';
const API_TEST_SERVER_PORT = nconf.get('PORT');
describe('GET /qr-code/user/:memberId', () => {
xdescribe('GET /qr-code/user/:memberId', () => {
let user;
before(async () => {

View File

@@ -141,6 +141,16 @@ describe('DELETE /tasks/:id', () => {
});
});
it('removes a task from user.tasksOrder'); // TODO
it('removes a task from user.tasksOrder', async () => {
let task = await user.post('/tasks/user', {
text: 'test habit',
type: 'habit',
});
await user.del(`/tasks/${task._id}`);
await user.sync();
expect(user.tasksOrder.habits.indexOf(task._id)).to.eql(-1);
});
});
});

View File

@@ -13,7 +13,7 @@ describe('GET /tasks/user', () => {
it('returns all user\'s tasks', async () => {
let createdTasks = await user.post('/tasks/user', [{text: 'test habit', type: 'habit'}, {text: 'test todo', type: 'todo'}]);
let tasks = await user.get('/tasks/user');
expect(tasks.length).to.equal(createdTasks.length + 1); // + 1 because 1 is a default task
expect(tasks.length).to.equal(createdTasks.length + 1); // Plus one for generated todo
});
it('returns only a type of user\'s tasks if req.query.type is specified', async () => {

View File

@@ -130,6 +130,7 @@ describe('POST /tasks/:id/score/:direction', () => {
});
it('uncompletes todo when direction is down', async () => {
await user.post(`/tasks/${todo._id}/score/up`);
await user.post(`/tasks/${todo._id}/score/down`);
let updatedTask = await user.get(`/tasks/${todo._id}`);
@@ -137,9 +138,23 @@ describe('POST /tasks/:id/score/:direction', () => {
expect(updatedTask.dateCompleted).to.be.a('undefined');
});
it('scores up todo even if it is already completed'); // Yes?
it('doesn\'t let a todo be completed twice', async () => {
await user.post(`/tasks/${todo._id}/score/up`);
await expect(user.post(`/tasks/${todo._id}/score/up`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('sessionOutdated'),
});
});
it('scores down todo even if it is already uncompleted'); // Yes?
it('doesn\'t let a todo be uncompleted twice', async () => {
await expect(user.post(`/tasks/${todo._id}/score/down`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('sessionOutdated'),
});
});
context('user stats when direction is up', () => {
let updatedUser;
@@ -163,23 +178,25 @@ describe('POST /tasks/:id/score/:direction', () => {
});
context('user stats when direction is down', () => {
let updatedUser;
let updatedUser, initialUser;
beforeEach(async () => {
await user.post(`/tasks/${todo._id}/score/up`);
initialUser = await user.get('/user');
await user.post(`/tasks/${todo._id}/score/down`);
updatedUser = await user.get('/user');
});
it('decreases user\'s mp', () => {
expect(updatedUser.stats.mp).to.be.lessThan(user.stats.mp);
expect(updatedUser.stats.mp).to.be.lessThan(initialUser.stats.mp);
});
it('decreases user\'s exp', () => {
expect(updatedUser.stats.exp).to.be.lessThan(user.stats.exp);
expect(updatedUser.stats.exp).to.be.lessThan(initialUser.stats.exp);
});
it('decreases user\'s gold', () => {
expect(updatedUser.stats.gp).to.be.lessThan(user.stats.gp);
expect(updatedUser.stats.gp).to.be.lessThan(initialUser.stats.gp);
});
});
});
@@ -202,6 +219,7 @@ describe('POST /tasks/:id/score/:direction', () => {
});
it('uncompletes daily when direction is down', async () => {
await user.post(`/tasks/${daily._id}/score/up`);
await user.post(`/tasks/${daily._id}/score/down`);
let task = await user.get(`/tasks/${daily._id}`);
@@ -222,9 +240,22 @@ describe('POST /tasks/:id/score/:direction', () => {
expect(task.nextDue.length).to.eql(6);
});
it('scores up daily even if it is already completed'); // Yes?
it('doesn\'t let a daily be completed twice', async () => {
await user.post(`/tasks/${daily._id}/score/up`);
await expect(user.post(`/tasks/${daily._id}/score/up`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('sessionOutdated'),
});
});
it('scores down daily even if it is already uncompleted'); // Yes?
it('doesn\'t let a daily be uncompleted twice', async () => {
await expect(user.post(`/tasks/${daily._id}/score/down`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('sessionOutdated'),
});
});
context('user stats when direction is up', () => {
let updatedUser;
@@ -248,23 +279,25 @@ describe('POST /tasks/:id/score/:direction', () => {
});
context('user stats when direction is down', () => {
let updatedUser;
let updatedUser, initialUser;
beforeEach(async () => {
await user.post(`/tasks/${daily._id}/score/up`);
initialUser = await user.get('/user');
await user.post(`/tasks/${daily._id}/score/down`);
updatedUser = await user.get('/user');
});
it('decreases user\'s mp', () => {
expect(updatedUser.stats.mp).to.be.lessThan(user.stats.mp);
expect(updatedUser.stats.mp).to.be.lessThan(initialUser.stats.mp);
});
it('decreases user\'s exp', () => {
expect(updatedUser.stats.exp).to.be.lessThan(user.stats.exp);
expect(updatedUser.stats.exp).to.be.lessThan(initialUser.stats.exp);
});
it('decreases user\'s gold', () => {
expect(updatedUser.stats.gp).to.be.lessThan(user.stats.gp);
expect(updatedUser.stats.gp).to.be.lessThan(initialUser.stats.gp);
});
});
});

View File

@@ -40,9 +40,13 @@ describe('POST /tasks/:taskId/move/to/:position', () => {
let taskToMove = tasks[1];
expect(taskToMove.text).to.equal('habit 2');
let newOrder = await user.post(`/tasks/${tasks[1]._id}/move/to/3`);
await user.sync();
expect(newOrder[3]).to.equal(taskToMove._id);
expect(newOrder.length).to.equal(5);
expect(user.tasksOrder.habits).to.eql(newOrder);
});
it('can move task to new position using alias', async () => {

View File

@@ -95,7 +95,7 @@ describe('POST /tasks/unlink-all/:challengeId', () => {
// Have the leader delete the challenge and unlink the tasks
await user.del(`/challenges/${challenge._id}`);
await user.post(`/tasks/unlink-all/${challenge._id}?keep=keep-all`);
// Get the second task for the second user
// Get the task for the second user
const [, anotherUserTask] = await anotherUser.get('/tasks/user');
// Expect the second user to still have the task, but unlinked
expect(anotherUserTask.challenge).to.eql({
@@ -106,4 +106,4 @@ describe('POST /tasks/unlink-all/:challengeId', () => {
winner: null,
});
});
});
});

View File

@@ -131,7 +131,7 @@ describe('POST /tasks/user', () => {
expect(task.updatedAt).not.to.equal('tomorrow');
expect(task.challenge).not.to.equal('no');
expect(task.completed).to.equal(false);
expect(task.streak).not.to.equal('never');
expect(task.dateCompleted).not.to.equal('never');
expect(task.value).not.to.equal(324);
expect(task.yesterDaily).to.equal(true);
});
@@ -302,6 +302,17 @@ describe('POST /tasks/user', () => {
expect(task.alias).to.eql('a_alias012');
});
// This is a special case for iOS requests
it('will round a priority (difficulty)', async () => {
let task = await user.post('/tasks/user', {
text: 'test habit',
type: 'habit',
priority: 0.10000000000005,
});
expect(task.priority).to.eql(0.1);
});
});
context('habits', () => {
@@ -628,6 +639,43 @@ describe('POST /tasks/user', () => {
});
});
it('returns an error if everyX is a non int', async () => {
await expect(user.post('/tasks/user', {
text: 'test daily',
type: 'daily',
everyX: 2.5,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'daily validation failed',
});
});
it('returns an error if everyX is negative', async () => {
await expect(user.post('/tasks/user', {
text: 'test daily',
type: 'daily',
everyX: -1,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'daily validation failed',
});
});
it('returns an error if everyX is above 9999', async () => {
await expect(user.post('/tasks/user', {
text: 'test daily',
type: 'daily',
everyX: 10000,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'daily validation failed',
});
});
it('can create checklists', async () => {
let task = await user.post('/tasks/user', {
text: 'test daily',

View File

@@ -82,6 +82,13 @@ describe('POST /tasks/:id/score/:direction', () => {
});
it('should update the history', async () => {
let newCron = new Date(2015, 11, 20);
await user.post('/debug/set-cron', {
lastCron: newCron,
});
await user.post('/cron');
await user.post(`/tasks/${usersChallengeTaskId}/score/up`);
let tasks = await user.get(`/tasks/challenge/${challenge._id}`);

View File

@@ -139,6 +139,23 @@ describe('PUT /tasks/:id', () => {
expect(savedHabit.up).to.eql(false);
expect(savedHabit.down).to.eql(false);
});
it('allows user to update their copy', async () => {
const userTasks = await user.get('/tasks/user');
const userChallengeTasks = userTasks.filter(task => task.challenge.id === challenge._id);
const userCopyOfChallengeTask = userChallengeTasks[0];
await user.put(`/tasks/${userCopyOfChallengeTask._id}`, {
notes: 'some new notes',
counterDown: 1,
counterUp: 2,
});
const savedHabit = await user.get(`/tasks/${userCopyOfChallengeTask._id}`);
expect(savedHabit.notes).to.eql('some new notes');
expect(savedHabit.counterDown).to.eql(1);
expect(savedHabit.counterUp).to.eql(2);
});
});
context('todos', () => {

View File

@@ -41,8 +41,9 @@ describe('POST /tasks/:id/score/:direction', () => {
let memberTasks = await member.get('/tasks/user');
let syncedTask = find(memberTasks, findAssignedTask);
const direction = 'up';
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
await expect(member.post(`/tasks/${syncedTask._id}/score/${direction}`))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
@@ -58,6 +59,7 @@ describe('POST /tasks/:id/score/:direction', () => {
user: member.auth.local.username,
taskName: updatedTask.text,
taskId: updatedTask._id,
direction,
}, 'cs')); // This test only works if we have the notification translated
expect(user.notifications[1].data.groupId).to.equal(guild._id);
@@ -71,8 +73,9 @@ describe('POST /tasks/:id/score/:direction', () => {
});
let memberTasks = await member.get('/tasks/user');
let syncedTask = find(memberTasks, findAssignedTask);
const direction = 'up';
await expect(member.post(`/tasks/${syncedTask._id}/score/up`))
await expect(member.post(`/tasks/${syncedTask._id}/score/${direction}`))
.to.eventually.be.rejected.and.to.eql({
code: 401,
error: 'NotAuthorized',
@@ -88,6 +91,7 @@ describe('POST /tasks/:id/score/:direction', () => {
user: member.auth.local.username,
taskName: updatedTask.text,
taskId: updatedTask._id,
direction,
}));
expect(user.notifications[1].data.groupId).to.equal(guild._id);
@@ -97,6 +101,7 @@ describe('POST /tasks/:id/score/:direction', () => {
user: member.auth.local.username,
taskName: updatedTask.text,
taskId: updatedTask._id,
direction,
}));
expect(member2.notifications[0].data.groupId).to.equal(guild._id);
});

View File

@@ -75,15 +75,6 @@ describe('POST /tasks/:taskId/unassign/:memberId', () => {
});
});
it('returns error when non leader tries to create a task', async () => {
await expect(member.post(`/tasks/${task._id}/unassign/${member._id}`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlyGroupLeaderCanEditTasks'),
});
});
it('unassigns a user from a task', async () => {
await user.post(`/tasks/${task._id}/unassign/${member._id}`);
@@ -129,4 +120,26 @@ describe('POST /tasks/:taskId/unassign/:memberId', () => {
expect(groupTask[0].group.assignedUsers).to.not.contain(member._id);
expect(syncedTask).to.not.exist;
});
it('allows a user to unassign themselves', async () => {
await member.post(`/tasks/${task._id}/unassign/${member._id}`);
let groupTask = await user.get(`/tasks/group/${guild._id}`);
let memberTasks = await member.get('/tasks/user');
let syncedTask = find(memberTasks, findAssignedTask);
expect(groupTask[0].group.assignedUsers).to.not.contain(member._id);
expect(syncedTask).to.not.exist;
});
// @TODO: Which do we want? The user to unassign themselves or not. This test was in
// here, but then we had a request to allow to unaissgn.
xit('returns error when non leader tries to unassign their a task', async () => {
await expect(member.post(`/tasks/${task._id}/unassign/${member._id}`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlyGroupLeaderCanEditTasks'),
});
});
});

View File

@@ -86,6 +86,12 @@ describe('DELETE /user', () => {
});
it('deletes the user\'s tasks', async () => {
await user.post('/tasks/user', {
text: 'test habit',
type: 'habit',
});
await user.sync();
// gets the user's tasks ids
let ids = [];
each(user.tasksOrder, (idsForOrder) => {
@@ -302,7 +308,7 @@ describe('DELETE /user', () => {
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('incorrectDeletePhrase'),
message: t('incorrectDeletePhrase', {magicWord: 'DELETE'}),
});
});

View File

@@ -27,4 +27,13 @@ describe('GET /user', () => {
expect(returnedUser.auth.local.salt).to.not.exist;
expect(returnedUser.apiToken).to.not.exist;
});
it('returns only user properties requested', async () => {
let returnedUser = await user.get('/user?userFields=achievements,items.mounts');
expect(returnedUser._id).to.equal(user._id);
expect(returnedUser.achievements).to.exist;
expect(returnedUser.items.mounts).to.exist;
expect(returnedUser.stats).to.not.exist;
});
});

View File

@@ -82,7 +82,7 @@ describe('GET /user/anonymized', () => {
});
// tasks
expect(tasks2).to.exist;
expect(tasks2.length).to.eql(5); // +1 because generateUser() assigns one todo
expect(tasks2.length).to.eql(5);
expect(tasks2[0].checklist).to.exist;
_.forEach(tasks2, (task) => {
expect(task.text).to.eql('task text');

View File

@@ -1,67 +0,0 @@
/* eslint-disable camelcase */
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
import shared from '../../../../../website/common/script';
let content = shared.content;
describe('POST /user/buy/:key', () => {
let user;
beforeEach(async () => {
user = await generateUser({
'stats.gp': 400,
});
});
// More tests in common code unit tests
it('returns an error if the item is not found', async () => {
await expect(user.post('/user/buy/notExisting'))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('itemNotFound', {key: 'notExisting'}),
});
});
it('buys a potion', async () => {
await user.update({
'stats.gp': 400,
'stats.hp': 40,
});
let potion = content.potion;
let res = await user.post('/user/buy/potion');
await user.sync();
expect(user.stats.hp).to.equal(50);
expect(res.data).to.eql(user.stats);
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
});
it('returns an error if user tries to buy a potion with full health', async () => {
await user.update({
'stats.gp': 40,
'stats.hp': 50,
});
await expect(user.post('/user/buy/potion'))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('messageHealthAlreadyMax'),
});
});
it('buys a piece of gear', async () => {
let key = 'armor_warrior_1';
await user.post(`/user/buy/${key}`);
await user.sync();
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
});
});

View File

@@ -1,36 +0,0 @@
/* eslint-disable camelcase */
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
describe('POST /user/buy-gear/:key', () => {
let user;
beforeEach(async () => {
user = await generateUser({
'stats.gp': 400,
});
});
// More tests in common code unit tests
it('returns an error if the item is not found', async () => {
await expect(user.post('/user/buy-gear/notExisting'))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('itemNotFound', {key: 'notExisting'}),
});
});
it('buys a piece of gear', async () => {
let key = 'armor_warrior_1';
await user.post(`/user/buy-gear/${key}`);
await user.sync();
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
});
});

View File

@@ -98,4 +98,24 @@ describe('POST /user/purchase/:type/:key', () => {
await members[0].sync();
expect(members[0].balance).to.equal(oldBalance);
});
describe('bulk purchasing', () => {
it('purchases a gem item', async () => {
await user.post(`/user/purchase/${type}/${key}`, {quantity: 2});
await user.sync();
expect(user.items[type][key]).to.equal(2);
});
it('can convert gold to gems if subscribed', async () => {
let oldBalance = user.balance;
await user.update({
'purchased.plan.customerId': 'group-plan',
'stats.gp': 1000,
});
await user.post('/user/purchase/gems/gem', {quantity: 2});
await user.sync();
expect(user.balance).to.equal(oldBalance + 0.50);
});
});
});

View File

@@ -10,43 +10,31 @@ import nconf from 'nconf';
const API_TEST_SERVER_PORT = nconf.get('PORT');
describe('GET /user/auth/local/reset-password-set-new-one', () => {
// @TODO skipped because on travis the client isn't available and the redirect fails
xdescribe('GET /user/auth/local/reset-password-set-new-one', () => {
let endpoint = `http://localhost:${API_TEST_SERVER_PORT}/static/user/auth/local/reset-password-set-new-one`;
// Tests to validate the validatePasswordResetCodeAndFindUser function
it('renders an error page if the code is missing', async () => {
try {
await superagent.get(endpoint);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
const res = await superagent.get(endpoint);
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
});
it('renders an error page if the code is invalid json', async () => {
try {
await superagent.get(`${endpoint}?code=invalid`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
const res = await superagent.get(`${endpoint}?code=invalid`);
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
});
it('renders an error page if the code cannot be decrypted', async () => {
let user = await generateUser();
try {
let code = JSON.stringify({ // not encrypted
userId: user._id,
expiresAt: new Date(),
});
await superagent.get(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
let code = JSON.stringify({ // not encrypted
userId: user._id,
expiresAt: new Date(),
});
const res = await superagent.get(`${endpoint}?code=${code}`);
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
});
it('renders an error page if the code is expired', async () => {
@@ -60,12 +48,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': code,
});
try {
await superagent.get(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
const res = await superagent.get(`${endpoint}?code=${code}`);
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
});
it('renders an error page if the user does not exist', async () => {
@@ -74,12 +58,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
expiresAt: moment().add({days: 1}),
}));
try {
await superagent.get(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
const res = await superagent.get(`${endpoint}?code=${code}`);
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
});
it('renders an error page if the user has no local auth', async () => {
@@ -93,12 +73,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
auth: 'not an object with valid fields',
});
try {
await superagent.get(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
const res = await superagent.get(`${endpoint}?code=${code}`);
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
});
it('renders an error page if the code doesn\'t match the one saved at user.auth.passwordResetCode', async () => {
@@ -112,12 +88,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': 'invalid',
});
try {
await superagent.get(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
const res = await superagent.get(`${endpoint}?code=${code}`);
expect(res.req.path.indexOf('hasError=true') !== -1).to.equal(true);
});
//
@@ -134,7 +106,8 @@ describe('GET /user/auth/local/reset-password-set-new-one', () => {
});
let res = await superagent.get(`${endpoint}?code=${code}`);
expect(res.status).to.equal(200);
expect(res.req.path.indexOf('hasError=false') !== -1).to.equal(true);
expect(res.req.path.indexOf('code=') !== -1).to.equal(true);
});
});

View File

@@ -10,49 +10,46 @@ import {
import moment from 'moment';
import {
generateUser,
requester,
translate as t,
} from '../../../../../helpers/api-integration/v3';
import superagent from 'superagent';
import nconf from 'nconf';
const API_TEST_SERVER_PORT = nconf.get('PORT');
describe('POST /user/auth/local/reset-password-set-new-one', () => {
let endpoint = `http://localhost:${API_TEST_SERVER_PORT}/static/user/auth/local/reset-password-set-new-one`;
describe('POST /user/auth/reset-password-set-new-one', () => {
const endpoint = '/user/auth/reset-password-set-new-one';
const api = requester();
// Tests to validate the validatePasswordResetCodeAndFindUser function
it('renders an error page if the code is missing', async () => {
try {
await superagent.post(endpoint);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(endpoint)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('invalidPasswordResetCode'),
});
});
it('renders an error page if the code is invalid json', async () => {
try {
await superagent.post(`${endpoint}?code=invalid`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}?code=invalid`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('invalidPasswordResetCode'),
});
});
it('renders an error page if the code cannot be decrypted', async () => {
let user = await generateUser();
try {
let code = JSON.stringify({ // not encrypted
userId: user._id,
expiresAt: new Date(),
});
await superagent.post(`${endpoint}?code=${code}`);
let code = JSON.stringify({ // not encrypted
userId: user._id,
expiresAt: new Date(),
});
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}`, {
code,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('invalidPasswordResetCode'),
});
});
it('renders an error page if the code is expired', async () => {
@@ -66,12 +63,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': code,
});
try {
await superagent.post(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}`, {
code,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('invalidPasswordResetCode'),
});
});
it('renders an error page if the user does not exist', async () => {
@@ -80,12 +78,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
expiresAt: moment().add({days: 1}),
}));
try {
await superagent.post(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}`, {
code,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('invalidPasswordResetCode'),
});
});
it('renders an error page if the user has no local auth', async () => {
@@ -99,12 +98,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
auth: 'not an object with valid fields',
});
try {
await superagent.post(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}`, {
code,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('invalidPasswordResetCode'),
});
});
it('renders an error page if the code doesn\'t match the one saved at user.auth.passwordResetCode', async () => {
@@ -118,12 +118,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': 'invalid',
});
try {
await superagent.post(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}`, {
code,
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('invalidPasswordResetCode'),
});
});
//
@@ -139,12 +140,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': code,
});
try {
await superagent.post(`${endpoint}?code=${code}`);
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}`, {
code,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});
it('renders the error page if the password confirmation is missing', async () => {
@@ -158,14 +160,14 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': code,
});
try {
await superagent
.post(`${endpoint}?code=${code}`)
.send({newPassword: 'my new password'});
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}`, {
newPassword: 'my new password',
code,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});
it('renders the error page if the password confirmation does not match', async () => {
@@ -179,17 +181,15 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': code,
});
try {
await superagent
.post(`${endpoint}?code=${code}`)
.send({
newPassword: 'my new password',
confirmPassword: 'not matching',
});
throw new Error('Request should fail.');
} catch (err) {
expect(err.status).to.equal(401);
}
await expect(api.post(`${endpoint}`, {
newPassword: 'my new password',
confirmPassword: 'not matching',
code,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('passwordConfirmationMatch'),
});
});
it('renders the success page and save the user', async () => {
@@ -203,14 +203,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': code,
});
let res = await superagent
.post(`${endpoint}?code=${code}`)
.send({
newPassword: 'my new password',
confirmPassword: 'my new password',
});
let res = await api.post(`${endpoint}`, {
newPassword: 'my new password',
confirmPassword: 'my new password',
code,
});
expect(res.status).to.equal(200);
expect(res.message).to.equal(t('passwordChangeSuccess'));
await user.sync();
expect(user.auth.local.passwordResetCode).to.equal(undefined);
@@ -246,14 +245,13 @@ describe('POST /user/auth/local/reset-password-set-new-one', () => {
'auth.local.passwordResetCode': code,
});
let res = await superagent
.post(`${endpoint}?code=${code}`)
.send({
newPassword: 'my new password',
confirmPassword: 'my new password',
});
let res = await api.post(`${endpoint}`, {
newPassword: 'my new password',
confirmPassword: 'my new password',
code,
});
expect(res.status).to.equal(200);
expect(res.message).to.equal(t('passwordChangeSuccess'));
await user.sync();
expect(user.auth.local.passwordResetCode).to.equal(undefined);

View File

@@ -6,10 +6,14 @@ import {
getProperty,
} from '../../../../../helpers/api-integration/v3';
import { ApiUser } from '../../../../../helpers/api-integration/api-classes';
import { v4 as generateRandomUserName } from 'uuid';
import { v4 as uuid } from 'uuid';
import { each } from 'lodash';
import { encrypt } from '../../../../../../website/server/libs/encryption';
function generateRandomUserName () {
return (Date.now() + uuid()).substring(0, 20);
}
describe('POST /user/auth/local/register', () => {
context('username and email are free', () => {
let api;
@@ -37,6 +41,71 @@ describe('POST /user/auth/local/register', () => {
expect(user.newUser).to.eql(true);
});
xit('remove spaces from username', async () => {
// TODO can probably delete this test now
let username = ' usernamewithspaces ';
let email = 'test@example.com';
let password = 'password';
let user = await api.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
});
expect(user.auth.local.username).to.eql(username.trim());
expect(user.profile.name).to.eql(username.trim());
});
context('validates username', () => {
const email = 'test@example.com';
const password = 'password';
it('requires to username to be less than 20', async () => {
const username = (Date.now() + uuid()).substring(0, 21);
await expect(api.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'Invalid request parameters.',
});
});
it('rejects chracters not in [-_a-zA-Z0-9]', async () => {
const username = 'a-zA_Z09*';
await expect(api.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: 'Invalid request parameters.',
});
});
it('allows only [-_a-zA-Z0-9] characters', async () => {
const username = 'a-zA_Z09';
const user = await api.post('/user/auth/local/register', {
username,
email,
password,
confirmPassword: password,
});
expect(user.auth.local.username).to.eql(username);
});
});
context('provides default tags and tasks', async () => {
it('for a generic API consumer', async () => {
let username = generateRandomUserName();
@@ -59,13 +128,8 @@ describe('POST /user/auth/local/register', () => {
let tags = await requests.get('/tags');
expect(habits).to.have.a.lengthOf(0);
expect(dailys).to.have.a.lengthOf(0);
expect(todos).to.have.a.lengthOf(1);
expect(todos[0].text).to.eql(t('defaultTodo1Text'));
expect(todos[0].notes).to.eql(t('defaultTodoNotes'));
expect(rewards).to.have.a.lengthOf(0);
expect(tags).to.have.a.lengthOf(7);
@@ -78,7 +142,7 @@ describe('POST /user/auth/local/register', () => {
expect(tags[6].name).to.eql(t('defaultTag7'));
});
it('for Web', async () => {
xit('for Web', async () => {
api = requester(
null,
{'x-client': 'habitica-web'},
@@ -129,7 +193,9 @@ describe('POST /user/auth/local/register', () => {
expect(tags[5].name).to.eql(t('defaultTag6'));
expect(tags[6].name).to.eql(t('defaultTag7'));
});
});
context('does not provide default tags and tasks', async () => {
it('for Android', async () => {
api = requester(
null,
@@ -154,34 +220,11 @@ describe('POST /user/auth/local/register', () => {
let rewards = await requests.get('/tasks/user?type=rewards');
let tags = await requests.get('/tags');
expect(habits).to.have.a.lengthOf(2);
expect(habits[0].text).to.eql(t('defaultHabit4Text'));
expect(habits[0].notes).to.eql(t('defaultHabit4Notes'));
expect(habits[1].text).to.eql(t('defaultHabit5Text'));
expect(habits[1].notes).to.eql(t('defaultHabit5Notes'));
expect(dailys).to.have.a.lengthOf(1);
expect(dailys[0].text).to.eql(t('defaultDaily1Text'));
expect(dailys[0].notes).to.eql('');
expect(todos).to.have.a.lengthOf(2);
expect(todos[0].text).to.eql(t('defaultTodo1Text'));
expect(todos[0].notes).to.eql(t('defaultTodoNotes'));
expect(todos[1].text).to.eql(t('defaultTodo2Text'));
expect(todos[1].notes).to.eql(t('defaultTodo2Notes'));
expect(rewards).to.have.a.lengthOf(1);
expect(rewards[0].text).to.eql(t('defaultReward2Text'));
expect(rewards[0].notes).to.eql(t('defaultReward2Notes'));
expect(tags).to.have.a.lengthOf(7);
expect(tags[0].name).to.eql(t('defaultTag1'));
expect(tags[1].name).to.eql(t('defaultTag2'));
expect(tags[2].name).to.eql(t('defaultTag3'));
expect(tags[3].name).to.eql(t('defaultTag4'));
expect(tags[4].name).to.eql(t('defaultTag5'));
expect(tags[5].name).to.eql(t('defaultTag6'));
expect(tags[6].name).to.eql(t('defaultTag7'));
expect(habits).to.have.a.lengthOf(0);
expect(dailys).to.have.a.lengthOf(0);
expect(todos).to.have.a.lengthOf(0);
expect(rewards).to.have.a.lengthOf(0);
expect(tags).to.have.a.lengthOf(0);
});
it('for iOS', async () => {
@@ -208,34 +251,11 @@ describe('POST /user/auth/local/register', () => {
let rewards = await requests.get('/tasks/user?type=rewards');
let tags = await requests.get('/tags');
expect(habits).to.have.a.lengthOf(2);
expect(habits[0].text).to.eql(t('defaultHabit4Text'));
expect(habits[0].notes).to.eql(t('defaultHabit4Notes'));
expect(habits[1].text).to.eql(t('defaultHabit5Text'));
expect(habits[1].notes).to.eql(t('defaultHabit5Notes'));
expect(dailys).to.have.a.lengthOf(1);
expect(dailys[0].text).to.eql(t('defaultDaily1Text'));
expect(dailys[0].notes).to.eql('');
expect(todos).to.have.a.lengthOf(2);
expect(todos[0].text).to.eql(t('defaultTodo1Text'));
expect(todos[0].notes).to.eql(t('defaultTodoNotes'));
expect(todos[1].text).to.eql(t('defaultTodo2Text'));
expect(todos[1].notes).to.eql(t('defaultTodo2Notes'));
expect(rewards).to.have.a.lengthOf(1);
expect(rewards[0].text).to.eql(t('defaultReward2Text'));
expect(rewards[0].notes).to.eql(t('defaultReward2Notes'));
expect(tags).to.have.a.lengthOf(7);
expect(tags[0].name).to.eql(t('defaultTag1'));
expect(tags[1].name).to.eql(t('defaultTag2'));
expect(tags[2].name).to.eql(t('defaultTag3'));
expect(tags[3].name).to.eql(t('defaultTag4'));
expect(tags[4].name).to.eql(t('defaultTag5'));
expect(tags[5].name).to.eql(t('defaultTag6'));
expect(tags[6].name).to.eql(t('defaultTag7'));
expect(habits).to.have.a.lengthOf(0);
expect(dailys).to.have.a.lengthOf(0);
expect(todos).to.have.a.lengthOf(0);
expect(rewards).to.have.a.lengthOf(0);
expect(tags).to.have.a.lengthOf(0);
});
});
@@ -667,10 +687,10 @@ describe('POST /user/auth/local/register', () => {
confirmPassword: password,
});
expect(user.tasksOrder.todos).to.not.be.empty;
expect(user.tasksOrder.todos).to.be.empty;
expect(user.tasksOrder.dailys).to.be.empty;
expect(user.tasksOrder.habits).to.not.be.empty;
expect(user.tasksOrder.rewards).to.not.be.empty;
expect(user.tasksOrder.habits).to.be.empty;
expect(user.tasksOrder.rewards).to.be.empty;
});
it('populates user with default tags', async () => {
@@ -697,23 +717,8 @@ describe('POST /user/auth/local/register', () => {
let habits = await requests.get('/tasks/user?type=habits');
let todos = await requests.get('/tasks/user?type=todos');
function findTag (tagName) {
let tag = user.tags.find((userTag) => {
return userTag.name === t(tagName);
});
return tag.id;
}
expect(habits[0].tags).to.have.a.lengthOf(3);
expect(habits[0].tags).to.include.members(['defaultTag1', 'defaultTag4', 'defaultTag6'].map(findTag));
expect(habits[1].tags).to.have.a.lengthOf(1);
expect(habits[1].tags).to.include.members(['defaultTag3'].map(findTag));
expect(habits[2].tags).to.have.a.lengthOf(2);
expect(habits[2].tags).to.include.members(['defaultTag2', 'defaultTag3'].map(findTag));
expect(todos[0].tags).to.have.a.lengthOf(0);
expect(habits).to.have.a.lengthOf(0);
expect(todos).to.have.a.lengthOf(0);
});
});
});

View File

@@ -0,0 +1,100 @@
/* eslint-disable camelcase */
import {
generateUser,
translate as t,
} from '../../../../../helpers/api-integration/v3';
import shared from '../../../../../../website/common/script';
let content = shared.content;
describe('POST /user/buy/:key', () => {
let user;
beforeEach(async () => {
user = await generateUser({
'stats.gp': 400,
});
});
// More tests in common code unit tests
it('returns an error if the item is not found', async () => {
await expect(user.post('/user/buy/notExisting'))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('itemNotFound', {key: 'notExisting'}),
});
});
it('buys a potion', async () => {
await user.update({
'stats.gp': 400,
'stats.hp': 40,
});
let potion = content.potion;
let res = await user.post('/user/buy/potion');
await user.sync();
expect(user.stats.hp).to.equal(50);
expect(res.data).to.eql(user.stats);
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
});
it('returns an error if user tries to buy a potion with full health', async () => {
await user.update({
'stats.gp': 40,
'stats.hp': 50,
});
await expect(user.post('/user/buy/potion'))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('messageHealthAlreadyMax'),
});
});
it('buys a piece of gear', async () => {
let key = 'armor_warrior_1';
await user.post(`/user/buy/${key}`);
await user.sync();
expect(user.items.gear.owned.armor_warrior_1).to.eql(true);
});
it('buys a special spell', async () => {
let key = 'spookySparkles';
let item = content.special[key];
await user.update({'stats.gp': 250});
let res = await user.post(`/user/buy/${key}`);
await user.sync();
expect(res.data).to.eql({
items: JSON.parse(JSON.stringify(user.items)), // otherwise dates can't be compared
stats: user.stats,
});
expect(res.message).to.equal(t('messageBought', {
itemText: item.text(),
}));
});
it('allows for bulk purchases', async () => {
await user.update({
'stats.gp': 400,
'stats.hp': 20,
});
let potion = content.potion;
let res = await user.post('/user/buy/potion', {quantity: 2});
await user.sync();
expect(user.stats.hp).to.equal(50);
expect(res.data).to.eql(user.stats);
expect(res.message).to.equal(t('messageBought', {itemText: potion.text()}));
});
});

View File

@@ -1,7 +1,7 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/buy-armoire', () => {
let user;

View File

@@ -0,0 +1,56 @@
/* eslint-disable camelcase */
import {
generateUser,
translate as t,
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/buy-gear/:key', () => {
let user;
beforeEach(async () => {
user = await generateUser({
'stats.gp': 400,
});
});
// More tests in common code unit tests
it('returns an error if the item is not found', async () => {
await expect(user.post('/user/buy-gear/notExisting'))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('itemNotFound', {key: 'notExisting'}),
});
});
it('buys the first level weapon gear', async () => {
let key = 'weapon_warrior_0';
await user.post(`/user/buy-gear/${key}`);
await user.sync();
expect(user.items.gear.owned[key]).to.eql(true);
});
it('buys the first level armor gear', async () => {
let key = 'armor_warrior_1';
await user.post(`/user/buy-gear/${key}`);
await user.sync();
expect(user.items.gear.owned[key]).to.eql(true);
});
it('tries to buy subsequent, level gear', async () => {
let key = 'armor_warrior_2';
return expect(user.post(`/user/buy-gear/${key}`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: 'You need to purchase a lower level gear before this one.',
});
});
});

View File

@@ -1,8 +1,8 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
import shared from '../../../../../website/common/script';
} from '../../../../../helpers/api-integration/v3';
import shared from '../../../../../../website/common/script';
let content = shared.content;

View File

@@ -1,7 +1,7 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/buy-mystery-set/:key', () => {
let user;

View File

@@ -1,8 +1,8 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
import shared from '../../../../../website/common/script';
} from '../../../../../helpers/api-integration/v3';
import shared from '../../../../../../website/common/script';
let content = shared.content;

View File

@@ -1,8 +1,8 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
import shared from '../../../../../website/common/script';
} from '../../../../../helpers/api-integration/v3';
import shared from '../../../../../../website/common/script';
let content = shared.content;

View File

@@ -1,7 +1,7 @@
import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v3';
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/allocate', () => {
let user;

View File

@@ -0,0 +1,40 @@
import {
generateUser,
translate as t,
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/allocate-bulk', () => {
let user;
const statsUpdate = {
stats: {
con: 1,
str: 2,
},
};
beforeEach(async () => {
user = await generateUser();
});
// More tests in common code unit tests
it('returns an error if user does not have enough points', async () => {
await expect(user.post('/user/allocate-bulk', statsUpdate))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('notEnoughAttrPoints'),
});
});
it('allocates attribute points', async () => {
await user.update({'stats.points': 3});
await user.post('/user/allocate-bulk', statsUpdate);
await user.sync();
expect(user.stats.con).to.equal(1);
expect(user.stats.str).to.equal(2);
expect(user.stats.points).to.equal(0);
});
});

View File

@@ -1,6 +1,6 @@
import {
generateUser,
} from '../../../../helpers/api-integration/v3';
} from '../../../../../helpers/api-integration/v3';
describe('POST /user/allocate-now', () => {
// More tests in common code unit tests

View File

@@ -1,739 +0,0 @@
import moment from 'moment';
import cc from 'coupon-code';
import uuid from 'uuid';
import {
generateGroup,
} from '../../../../helpers/api-unit.helper.js';
import { model as User } from '../../../../../website/server/models/user';
import { model as Group } from '../../../../../website/server/models/group';
import { model as Coupon } from '../../../../../website/server/models/coupon';
import amzLib from '../../../../../website/server/libs/amazonPayments';
import payments from '../../../../../website/server/libs/payments';
import common from '../../../../../website/common';
const i18n = common.i18n;
describe('Amazon Payments', () => {
let subKey = 'basic_3mo';
describe('checkout', () => {
let user, orderReferenceId, headers;
let setOrderReferenceDetailsSpy;
let confirmOrderReferenceSpy;
let authorizeSpy;
let closeOrderReferenceSpy;
let paymentBuyGemsStub;
let paymentCreateSubscritionStub;
let amount = 5;
function expectAmazonStubs () {
expect(setOrderReferenceDetailsSpy).to.be.calledOnce;
expect(setOrderReferenceDetailsSpy).to.be.calledWith({
AmazonOrderReferenceId: orderReferenceId,
OrderReferenceAttributes: {
OrderTotal: {
CurrencyCode: amzLib.constants.CURRENCY_CODE,
Amount: amount,
},
SellerNote: amzLib.constants.SELLER_NOTE,
SellerOrderAttributes: {
SellerOrderId: common.uuid(),
StoreName: amzLib.constants.STORE_NAME,
},
},
});
expect(confirmOrderReferenceSpy).to.be.calledOnce;
expect(confirmOrderReferenceSpy).to.be.calledWith({ AmazonOrderReferenceId: orderReferenceId });
expect(authorizeSpy).to.be.calledOnce;
expect(authorizeSpy).to.be.calledWith({
AmazonOrderReferenceId: orderReferenceId,
AuthorizationReferenceId: common.uuid().substring(0, 32),
AuthorizationAmount: {
CurrencyCode: amzLib.constants.CURRENCY_CODE,
Amount: amount,
},
SellerAuthorizationNote: amzLib.constants.SELLER_NOTE,
TransactionTimeout: 0,
CaptureNow: true,
});
expect(closeOrderReferenceSpy).to.be.calledOnce;
expect(closeOrderReferenceSpy).to.be.calledWith({ AmazonOrderReferenceId: orderReferenceId });
}
beforeEach(function () {
user = new User();
headers = {};
orderReferenceId = 'orderReferenceId';
setOrderReferenceDetailsSpy = sinon.stub(amzLib, 'setOrderReferenceDetails');
setOrderReferenceDetailsSpy.returnsPromise().resolves({});
confirmOrderReferenceSpy = sinon.stub(amzLib, 'confirmOrderReference');
confirmOrderReferenceSpy.returnsPromise().resolves({});
authorizeSpy = sinon.stub(amzLib, 'authorize');
authorizeSpy.returnsPromise().resolves({});
closeOrderReferenceSpy = sinon.stub(amzLib, 'closeOrderReference');
closeOrderReferenceSpy.returnsPromise().resolves({});
paymentBuyGemsStub = sinon.stub(payments, 'buyGems');
paymentBuyGemsStub.returnsPromise().resolves({});
paymentCreateSubscritionStub = sinon.stub(payments, 'createSubscription');
paymentCreateSubscritionStub.returnsPromise().resolves({});
sinon.stub(common, 'uuid').returns('uuid-generated');
});
afterEach(function () {
amzLib.setOrderReferenceDetails.restore();
amzLib.confirmOrderReference.restore();
amzLib.authorize.restore();
amzLib.closeOrderReference.restore();
payments.buyGems.restore();
payments.createSubscription.restore();
common.uuid.restore();
});
it('should purchase gems', async () => {
sinon.stub(user, 'canGetGems').returnsPromise().resolves(true);
await amzLib.checkout({user, orderReferenceId, headers});
expect(paymentBuyGemsStub).to.be.calledOnce;
expect(paymentBuyGemsStub).to.be.calledWith({
user,
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
});
expectAmazonStubs();
expect(user.canGetGems).to.be.calledOnce;
user.canGetGems.restore();
});
it('should error if gem amount is too low', async () => {
let receivingUser = new User();
receivingUser.save();
let gift = {
type: 'gems',
gems: {
amount: 0,
uuid: receivingUser._id,
},
};
await expect(amzLib.checkout({gift, user, orderReferenceId, headers}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 400,
message: 'Amount must be at least 1.',
name: 'BadRequest',
});
});
it('should error if user cannot get gems gems', async () => {
sinon.stub(user, 'canGetGems').returnsPromise().resolves(false);
await expect(amzLib.checkout({user, orderReferenceId, headers})).to.eventually.be.rejected.and.to.eql({
httpCode: 401,
message: i18n.t('groupPolicyCannotGetGems'),
name: 'NotAuthorized',
});
user.canGetGems.restore();
});
it('should gift gems', async () => {
let receivingUser = new User();
await receivingUser.save();
let gift = {
type: 'gems',
uuid: receivingUser._id,
gems: {
amount: 16,
},
};
amount = 16 / 4;
await amzLib.checkout({gift, user, orderReferenceId, headers});
expect(paymentBuyGemsStub).to.be.calledOnce;
expect(paymentBuyGemsStub).to.be.calledWith({
user,
paymentMethod: amzLib.constants.PAYMENT_METHOD_GIFT,
headers,
gift,
});
expectAmazonStubs();
});
it('should gift a subscription', async () => {
let receivingUser = new User();
receivingUser.save();
let gift = {
type: 'subscription',
subscription: {
key: subKey,
uuid: receivingUser._id,
},
};
amount = common.content.subscriptionBlocks[subKey].price;
await amzLib.checkout({user, orderReferenceId, headers, gift});
gift.member = receivingUser;
expect(paymentCreateSubscritionStub).to.be.calledOnce;
expect(paymentCreateSubscritionStub).to.be.calledWith({
user,
paymentMethod: amzLib.constants.PAYMENT_METHOD_GIFT,
headers,
gift,
});
expectAmazonStubs();
});
});
describe('subscribe', () => {
let user, group, amount, billingAgreementId, sub, coupon, groupId, headers;
let amazonSetBillingAgreementDetailsSpy;
let amazonConfirmBillingAgreementSpy;
let amazongAuthorizeOnBillingAgreementSpy;
let createSubSpy;
beforeEach(async () => {
user = new User();
user.profile.name = 'sender';
user.purchased.plan.customerId = 'customer-id';
user.purchased.plan.planId = subKey;
user.purchased.plan.lastBillingDate = new Date();
group = generateGroup({
name: 'test group',
type: 'guild',
privacy: 'public',
leader: user._id,
});
group.purchased.plan.customerId = 'customer-id';
group.purchased.plan.planId = subKey;
await group.save();
amount = common.content.subscriptionBlocks[subKey].price;
billingAgreementId = 'billingAgreementId';
sub = {
key: subKey,
price: amount,
};
groupId = group._id;
headers = {};
amazonSetBillingAgreementDetailsSpy = sinon.stub(amzLib, 'setBillingAgreementDetails');
amazonSetBillingAgreementDetailsSpy.returnsPromise().resolves({});
amazonConfirmBillingAgreementSpy = sinon.stub(amzLib, 'confirmBillingAgreement');
amazonConfirmBillingAgreementSpy.returnsPromise().resolves({});
amazongAuthorizeOnBillingAgreementSpy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
amazongAuthorizeOnBillingAgreementSpy.returnsPromise().resolves({});
createSubSpy = sinon.stub(payments, 'createSubscription');
createSubSpy.returnsPromise().resolves({});
sinon.stub(common, 'uuid').returns('uuid-generated');
});
afterEach(function () {
amzLib.setBillingAgreementDetails.restore();
amzLib.confirmBillingAgreement.restore();
amzLib.authorizeOnBillingAgreement.restore();
payments.createSubscription.restore();
common.uuid.restore();
});
it('should throw an error if we are missing a subscription', async () => {
await expect(amzLib.subscribe({
billingAgreementId,
coupon,
user,
groupId,
headers,
}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 400,
name: 'BadRequest',
message: i18n.t('missingSubscriptionCode'),
});
});
it('should throw an error if we are missing a billingAgreementId', async () => {
await expect(amzLib.subscribe({
sub,
coupon,
user,
groupId,
headers,
}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 400,
name: 'BadRequest',
message: 'Missing req.body.billingAgreementId',
});
});
it('should throw an error when coupon code is missing', async () => {
sub.discount = 40;
await expect(amzLib.subscribe({
billingAgreementId,
sub,
coupon,
user,
groupId,
headers,
}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 400,
name: 'BadRequest',
message: i18n.t('couponCodeRequired'),
});
});
it('should throw an error when coupon code is invalid', async () => {
sub.discount = 40;
sub.key = 'google_6mo';
coupon = 'example-coupon';
let couponModel = new Coupon();
couponModel.event = 'google_6mo';
await couponModel.save();
sinon.stub(cc, 'validate').returns('invalid');
await expect(amzLib.subscribe({
billingAgreementId,
sub,
coupon,
user,
groupId,
headers,
}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: i18n.t('invalidCoupon'),
});
cc.validate.restore();
});
it('subscribes with amazon with a coupon', async () => {
sub.discount = 40;
sub.key = 'google_6mo';
coupon = 'example-coupon';
let couponModel = new Coupon();
couponModel.event = 'google_6mo';
let updatedCouponModel = await couponModel.save();
sinon.stub(cc, 'validate').returns(updatedCouponModel._id);
await amzLib.subscribe({
billingAgreementId,
sub,
coupon,
user,
groupId,
headers,
});
expect(createSubSpy).to.be.calledOnce;
expect(createSubSpy).to.be.calledWith({
user,
customerId: billingAgreementId,
paymentMethod: amzLib.constants.PAYMENT_METHOD,
sub,
headers,
groupId,
});
cc.validate.restore();
});
it('subscribes with amazon', async () => {
await amzLib.subscribe({
billingAgreementId,
sub,
coupon,
user,
groupId,
headers,
});
expect(amazonSetBillingAgreementDetailsSpy).to.be.calledOnce;
expect(amazonSetBillingAgreementDetailsSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
BillingAgreementAttributes: {
SellerNote: amzLib.constants.SELLER_NOTE_SUBSCRIPTION,
SellerBillingAgreementAttributes: {
SellerBillingAgreementId: common.uuid(),
StoreName: amzLib.constants.STORE_NAME,
CustomInformation: amzLib.constants.SELLER_NOTE_SUBSCRIPTION,
},
},
});
expect(amazonConfirmBillingAgreementSpy).to.be.calledOnce;
expect(amazonConfirmBillingAgreementSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
});
expect(amazongAuthorizeOnBillingAgreementSpy).to.be.calledOnce;
expect(amazongAuthorizeOnBillingAgreementSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
AuthorizationReferenceId: common.uuid().substring(0, 32),
AuthorizationAmount: {
CurrencyCode: amzLib.constants.CURRENCY_CODE,
Amount: amount,
},
SellerAuthorizationNote: amzLib.constants.SELLER_NOTE_ATHORIZATION_SUBSCRIPTION,
TransactionTimeout: 0,
CaptureNow: true,
SellerNote: amzLib.constants.SELLER_NOTE_ATHORIZATION_SUBSCRIPTION,
SellerOrderAttributes: {
SellerOrderId: common.uuid(),
StoreName: amzLib.constants.STORE_NAME,
},
});
expect(createSubSpy).to.be.calledOnce;
expect(createSubSpy).to.be.calledWith({
user,
customerId: billingAgreementId,
paymentMethod: amzLib.constants.PAYMENT_METHOD,
sub,
headers,
groupId,
});
});
it('subscribes with amazon with price to existing users', async () => {
user = new User();
user.guilds.push(groupId);
await user.save();
group.memberCount = 2;
await group.save();
sub.key = 'group_monthly';
sub.price = 9;
amount = 12;
await amzLib.subscribe({
billingAgreementId,
sub,
coupon,
user,
groupId,
headers,
});
expect(amazonSetBillingAgreementDetailsSpy).to.be.calledOnce;
expect(amazonSetBillingAgreementDetailsSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
BillingAgreementAttributes: {
SellerNote: amzLib.constants.SELLER_NOTE_SUBSCRIPTION,
SellerBillingAgreementAttributes: {
SellerBillingAgreementId: common.uuid(),
StoreName: amzLib.constants.STORE_NAME,
CustomInformation: amzLib.constants.SELLER_NOTE_SUBSCRIPTION,
},
},
});
expect(amazonConfirmBillingAgreementSpy).to.be.calledOnce;
expect(amazonConfirmBillingAgreementSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
});
expect(amazongAuthorizeOnBillingAgreementSpy).to.be.calledOnce;
expect(amazongAuthorizeOnBillingAgreementSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
AuthorizationReferenceId: common.uuid().substring(0, 32),
AuthorizationAmount: {
CurrencyCode: amzLib.constants.CURRENCY_CODE,
Amount: amount,
},
SellerAuthorizationNote: amzLib.constants.SELLER_NOTE_ATHORIZATION_SUBSCRIPTION,
TransactionTimeout: 0,
CaptureNow: true,
SellerNote: amzLib.constants.SELLER_NOTE_ATHORIZATION_SUBSCRIPTION,
SellerOrderAttributes: {
SellerOrderId: common.uuid(),
StoreName: amzLib.constants.STORE_NAME,
},
});
expect(createSubSpy).to.be.calledOnce;
expect(createSubSpy).to.be.calledWith({
user,
customerId: billingAgreementId,
paymentMethod: amzLib.constants.PAYMENT_METHOD,
sub,
headers,
groupId,
});
});
});
describe('cancelSubscription', () => {
let user, group, headers, billingAgreementId, subscriptionBlock, subscriptionLength;
let getBillingAgreementDetailsSpy;
let paymentCancelSubscriptionSpy;
function expectAmazonStubs () {
expect(getBillingAgreementDetailsSpy).to.be.calledOnce;
expect(getBillingAgreementDetailsSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
});
}
beforeEach(async () => {
user = new User();
user.profile.name = 'sender';
user.purchased.plan.customerId = 'customer-id';
user.purchased.plan.planId = subKey;
user.purchased.plan.lastBillingDate = new Date();
group = generateGroup({
name: 'test group',
type: 'guild',
privacy: 'public',
leader: user._id,
});
group.purchased.plan.customerId = 'customer-id';
group.purchased.plan.planId = subKey;
group.purchased.plan.lastBillingDate = new Date();
await group.save();
subscriptionBlock = common.content.subscriptionBlocks[subKey];
subscriptionLength = subscriptionBlock.months * 30;
headers = {};
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails');
getBillingAgreementDetailsSpy.returnsPromise().resolves({
BillingAgreementDetails: {
BillingAgreementStatus: {State: 'Closed'},
},
});
paymentCancelSubscriptionSpy = sinon.stub(payments, 'cancelSubscription');
paymentCancelSubscriptionSpy.returnsPromise().resolves({});
});
afterEach(function () {
amzLib.getBillingAgreementDetails.restore();
payments.cancelSubscription.restore();
});
it('should throw an error if we are missing a subscription', async () => {
user.purchased.plan.customerId = undefined;
await expect(amzLib.cancelSubscription({user}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: i18n.t('missingSubscription'),
});
});
it('should cancel a user subscription', async () => {
billingAgreementId = user.purchased.plan.customerId;
await amzLib.cancelSubscription({user, headers});
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
user,
groupId: undefined,
nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
cancellationReason: undefined,
});
expectAmazonStubs();
});
it('should close a user subscription if amazon not closed', async () => {
amzLib.getBillingAgreementDetails.restore();
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails')
.returnsPromise()
.resolves({
BillingAgreementDetails: {
BillingAgreementStatus: {State: 'Open'},
},
});
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').returnsPromise().resolves({});
billingAgreementId = user.purchased.plan.customerId;
await amzLib.cancelSubscription({user, headers});
expectAmazonStubs();
expect(closeBillingAgreementSpy).to.be.calledOnce;
expect(closeBillingAgreementSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
});
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
user,
groupId: undefined,
nextBill: moment(user.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
cancellationReason: undefined,
});
amzLib.closeBillingAgreement.restore();
});
it('should throw an error if group is not found', async () => {
await expect(amzLib.cancelSubscription({user, groupId: 'fake-id'}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 404,
name: 'NotFound',
message: i18n.t('groupNotFound'),
});
});
it('should throw an error if user is not group leader', async () => {
let nonLeader = new User();
nonLeader.guilds.push(group._id);
await nonLeader.save();
await expect(amzLib.cancelSubscription({user: nonLeader, groupId: group._id}))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
message: i18n.t('onlyGroupLeaderCanManageSubscription'),
});
});
it('should cancel a group subscription', async () => {
billingAgreementId = group.purchased.plan.customerId;
await amzLib.cancelSubscription({user, groupId: group._id, headers});
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
user,
groupId: group._id,
nextBill: moment(group.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
cancellationReason: undefined,
});
expectAmazonStubs();
});
it('should close a group subscription if amazon not closed', async () => {
amzLib.getBillingAgreementDetails.restore();
getBillingAgreementDetailsSpy = sinon.stub(amzLib, 'getBillingAgreementDetails')
.returnsPromise()
.resolves({
BillingAgreementDetails: {
BillingAgreementStatus: {State: 'Open'},
},
});
let closeBillingAgreementSpy = sinon.stub(amzLib, 'closeBillingAgreement').returnsPromise().resolves({});
billingAgreementId = group.purchased.plan.customerId;
await amzLib.cancelSubscription({user, groupId: group._id, headers});
expectAmazonStubs();
expect(closeBillingAgreementSpy).to.be.calledOnce;
expect(closeBillingAgreementSpy).to.be.calledWith({
AmazonBillingAgreementId: billingAgreementId,
});
expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
expect(paymentCancelSubscriptionSpy).to.be.calledWith({
user,
groupId: group._id,
nextBill: moment(group.purchased.plan.lastBillingDate).add({ days: subscriptionLength }),
paymentMethod: amzLib.constants.PAYMENT_METHOD,
headers,
cancellationReason: undefined,
});
amzLib.closeBillingAgreement.restore();
});
});
describe('#upgradeGroupPlan', () => {
let spy, data, user, group, uuidString;
beforeEach(async function () {
user = new User();
user.profile.name = 'sender';
data = {
user,
sub: {
key: 'basic_3mo', // @TODO: Validate that this is group
},
customerId: 'customer-id',
paymentMethod: 'Payment Method',
headers: {
'x-client': 'habitica-web',
'user-agent': '',
},
};
group = generateGroup({
name: 'test group',
type: 'guild',
privacy: 'public',
leader: user._id,
});
await group.save();
spy = sinon.stub(amzLib, 'authorizeOnBillingAgreement');
spy.returnsPromise().resolves([]);
uuidString = 'uuid-v4';
sinon.stub(uuid, 'v4').returns(uuidString);
data.groupId = group._id;
data.sub.quantity = 3;
});
afterEach(function () {
sinon.restore(amzLib.authorizeOnBillingAgreement);
uuid.v4.restore();
});
it('charges for a new member', async () => {
data.paymentMethod = amzLib.constants.PAYMENT_METHOD;
await payments.createSubscription(data);
let updatedGroup = await Group.findById(group._id).exec();
updatedGroup.memberCount += 1;
await updatedGroup.save();
await amzLib.chargeForAdditionalGroupMember(updatedGroup);
expect(spy.calledOnce).to.be.true;
expect(spy).to.be.calledWith({
AmazonBillingAgreementId: updatedGroup.purchased.plan.customerId,
AuthorizationReferenceId: uuidString.substring(0, 32),
AuthorizationAmount: {
CurrencyCode: amzLib.constants.CURRENCY_CODE,
Amount: 3,
},
SellerAuthorizationNote: amzLib.constants.SELLER_NOTE_GROUP_NEW_MEMBER,
TransactionTimeout: 0,
CaptureNow: true,
SellerNote: amzLib.constants.SELLER_NOTE_GROUP_NEW_MEMBER,
SellerOrderAttributes: {
SellerOrderId: uuidString,
StoreName: amzLib.constants.STORE_NAME,
},
});
});
});
});

View File

@@ -1,31 +0,0 @@
import {
getManifestFiles,
} from '../../../../../website/server/libs/buildManifest';
describe('Build Manifest', () => {
describe('getManifestFiles', () => {
it('returns an html string', () => {
let htmlCode = getManifestFiles('app');
expect(htmlCode.startsWith('<script') || htmlCode.startsWith('<link')).to.be.true;
});
it('can return only js files', () => {
let htmlCode = getManifestFiles('app', 'js');
expect(htmlCode.indexOf('<link') === -1).to.be.true;
});
it('can return only css files', () => {
let htmlCode = getManifestFiles('app', 'css');
expect(htmlCode.indexOf('<script') === -1).to.be.true;
});
it('throws an error in case the page does not exist', () => {
expect(() => {
getManifestFiles('strange name here');
}).to.throw(Error);
});
});
});

View File

@@ -365,6 +365,72 @@ describe('cron', () => {
expect(user.history.todos).to.be.lengthOf(1);
});
it('should remove completed todos from users taskOrder list', () => {
tasksByType.todos = [];
user.tasksOrder.todos = [];
let todo = {
text: 'test todo',
type: 'todo',
value: 0,
};
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
tasksByType.todos[0].completed = true;
user.tasksOrder.todos = tasksByType.todos.map(taskTodo => {
return taskTodo._id;
});
// Since ideally tasksByType should not contain completed todos, fake ids should be filtered too
user.tasksOrder.todos.push('00000000-0000-0000-0000-000000000000');
expect(tasksByType.todos).to.be.lengthOf(2);
expect(user.tasksOrder.todos).to.be.lengthOf(3);
cron({user, tasksByType, daysMissed, analytics});
// user.tasksOrder.todos should be filtered while tasks by type remains unchanged
expect(tasksByType.todos).to.be.lengthOf(2);
expect(user.tasksOrder.todos).to.be.lengthOf(1);
});
it('should preserve todos order in task list', () => {
tasksByType.todos = [];
user.tasksOrder.todos = [];
let todo = {
text: 'test todo',
type: 'todo',
value: 0,
};
let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap
tasksByType.todos.push(task);
// Set up user.tasksOrder list in a specific order
user.tasksOrder.todos = tasksByType.todos.map(todoTask => {
return todoTask._id;
}).reverse();
let original = user.tasksOrder.todos; // Preserve the original order
cron({user, tasksByType, daysMissed, analytics});
let listsAreEqual = true;
user.tasksOrder.todos.forEach((taskId, index) => {
if (original[index]._id !== taskId) {
listsAreEqual = false;
}
});
expect(listsAreEqual);
expect(user.tasksOrder.todos).to.be.lengthOf(original.length);
});
});
describe('dailys', () => {

Some files were not shown because too many files have changed in this diff Show More