Merge branch 'api-v3' into api-v3-groups

This commit is contained in:
Blade Barringer
2015-12-31 12:22:20 -06:00
25 changed files with 794 additions and 674 deletions

View File

@@ -1,12 +1,12 @@
.2014_Fall_HealerPROMO2 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -943px -728px;
background-position: -455px -1085px;
width: 90px;
height: 90px;
}
.2014_Fall_Mage_PROMO9 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -943px -252px;
background-position: -943px -343px;
width: 120px;
height: 90px;
}
@@ -24,7 +24,7 @@
}
.promo_android {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -943px 0px;
background-position: -452px -241px;
width: 175px;
height: 175px;
}
@@ -84,13 +84,13 @@
}
.promo_enchanted_armoire_201511 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1260px -493px;
background-position: -943px -176px;
width: 122px;
height: 90px;
}
.promo_habitica {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -452px -241px;
background-position: -943px 0px;
width: 175px;
height: 175px;
}
@@ -126,13 +126,13 @@
}
.promo_mystery_201406 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -943px -449px;
background-position: -943px -540px;
width: 90px;
height: 96px;
}
.promo_mystery_201407 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1034px -449px;
background-position: -1037px -637px;
width: 42px;
height: 62px;
}
@@ -162,13 +162,13 @@
}
.promo_mystery_201412 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1034px -343px;
background-position: -1034px -540px;
width: 42px;
height: 66px;
}
.promo_mystery_201501 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1333px -1006px;
background-position: -1066px -176px;
width: 48px;
height: 63px;
}
@@ -186,7 +186,7 @@
}
.promo_mystery_201504 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1272px -1006px;
background-position: -1034px -434px;
width: 60px;
height: 69px;
}
@@ -198,19 +198,19 @@
}
.promo_mystery_201506 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1064px -252px;
background-position: -1064px -343px;
width: 42px;
height: 69px;
}
.promo_mystery_201507 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -943px -343px;
background-position: -943px -434px;
width: 90px;
height: 105px;
}
.promo_mystery_201508 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -943px -546px;
background-position: -943px -728px;
width: 93px;
height: 90px;
}
@@ -252,43 +252,49 @@
}
.promo_partyhats {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1260px -584px;
background-position: -1272px -1006px;
width: 115px;
height: 47px;
}
.promo_pastel_skin {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -331px -912px;
background-position: 0px -912px;
width: 330px;
height: 83px;
}
.customize-option.promo_pastel_skin {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -356px -927px;
background-position: -25px -927px;
width: 60px;
height: 60px;
}
.promo_pet_skins {
.promo_peppermint_flame {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1119px -493px;
width: 140px;
height: 147px;
}
.promo_pet_skins {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1260px -493px;
width: 140px;
height: 147px;
}
.customize-option.promo_pet_skins {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -1144px -508px;
background-position: -1285px -508px;
width: 60px;
height: 60px;
}
.promo_shimmer_hair {
background-image: url(spritesmith-largeSprites-0.png);
background-position: 0px -912px;
background-position: -331px -912px;
width: 330px;
height: 83px;
}
.customize-option.promo_shimmer_hair {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -25px -927px;
background-position: -356px -927px;
width: 60px;
height: 60px;
}
@@ -336,7 +342,7 @@
}
.promo_veteran_pets {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -943px -176px;
background-position: -943px -267px;
width: 146px;
height: 75px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 212 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 131 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 KiB

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -524,6 +524,8 @@
"headSpecialFall2015HealerText": "Hat of Frog",
"headSpecialFall2015HealerNotes": "This is an extremely serious hat that is worthy of only the most advanced potioners. Increases Intelligence by <%= int %>. Limited Edition 2015 Autumn Gear.",
"headSpecialNye2015Text": "Ridiculous Party Hat",
"headSpecialNye2015Notes": "You've received a Ridiculous Party Hat! Wear it with pride while ringing in the New Year! Confers no benefit.",
"headSpecialWinter2016RogueText": "Cocoa Helm",
"headSpecialWinter2016RogueNotes": "The protective scarf on this cozy helm is only removed to sip warm winter beverages. Increases Perception by <%= per %>. Limited Edition 2015-2016 Winter Gear.",
"headSpecialWinter2016WarriorText": "Snowman Cap",

View File

@@ -86,6 +86,7 @@
"mysterySet201509": "Werewolf Set",
"mysterySet201510": "Horned Goblin Set",
"mysterySet201511": "Wood Warrior Set",
"mysterySet201512": "Winter Flame Set",
"mysterySet301404": "Steampunk Standard Set",
"mysterySet301405": "Steampunk Accessories Set",

View File

@@ -786,6 +786,12 @@ let head = {
value: 60,
int: 7,
},
nye2015: {
text: t('headSpecialNye2015Text'),
notes: t('headSpecialNye2015Notes'),
value: 0,
canOwn: ownsItem('head_special_nye2015'),
},
};
let headAccessory = {

View File

@@ -0,0 +1,70 @@
var migrationName = '20151229_new_years_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 2015 party hat if user has 2014 hat, 2014 hat if they have the 2013 hat,
* and 2013 hat otherwise
*/
var dbserver = 'localhost:27017'; // FOR TEST DATABASE
// var dbserver = 'username:password@ds031379-a0.mongolab.com:31379'; // FOR PRODUCTION DATABASE
var dbname = 'habitrpg';
var mongo = require('mongoskin');
var _ = require('lodash');
var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users');
// specify a query to limit the affected users (empty for all users):
var query = {
};
// specify fields we are interested in to limit retrieved data (empty if we're not reading data):
var fields = {
'items.gear.owned': 1,
};
console.warn('Updating users...');
var progressCount = 1000;
var count = 0;
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
if (err) { return exiting(1, 'ERROR! ' + err); }
if (!user) {
console.warn('All appropriate users found and modified.');
return displayData();
}
count++;
// specify user data to change:
var set = {};
if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('head_special_nye2014')) {
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2015':false};
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('head_special_nye')) {
set = {'migration':migrationName, 'items.gear.owned.head_special_nye2014':false};
} else {
set = {'migration':migrationName, 'items.gear.owned.head_special_nye':false};
}
dbUsers.update({_id:user._id}, {$set:set});
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);
}

View File

@@ -5,14 +5,11 @@ import {
} from '../../../helpers/api-integration.helper';
describe('POST /groups', () => {
context('All groups', () => {
let leader;
beforeEach(() => {
return generateUser().then((user) => {
leader = user;
});
beforeEach(async () => {
leader = await generateUser();
});
xit('returns defaults? (TODO: it\'s possible to create a group without a type. Should the group default to party? Should we require type to be set?', () => {
@@ -24,61 +21,58 @@ describe('POST /groups', () => {
});
});
it('returns a group object', () => {
let group = {
it('returns a group object', async () => {
let group = await leader.post('/groups', {
name: 'Test Group',
type: 'party',
leaderOnly: { challenges: true },
description: 'Test Group Description',
leaderMessage: 'Test Group Message',
};
return leader.post('/groups', group).then((createdGroup) => {
expect(createdGroup._id).to.exist;
expect(createdGroup.leader).to.eql(leader._id);
expect(createdGroup.name).to.eql(group.name);
expect(createdGroup.description).to.eql(group.description);
expect(createdGroup.leaderMessage).to.eql(group.leaderMessage);
expect(createdGroup.leaderOnly).to.eql(group.leaderOnly);
expect(createdGroup.memberCount).to.eql(1);
});
expect(group._id).to.exist;
expect(group.leader).to.eql(leader._id);
expect(group.name).to.eql(group.name);
expect(group.description).to.eql(group.description);
expect(group.leaderMessage).to.eql(group.leaderMessage);
expect(group.leaderOnly).to.eql(group.leaderOnly);
expect(group.memberCount).to.eql(1);
});
it('returns a populated members array', () => {
return leader.post('/groups', {
it('returns a populated members array', async () => {
let party = await leader.post('/groups', {
type: 'party',
}).then((party) => {
let member = party.members[0];
expect(member._id).to.eql(leader._id);
expect(member.profile).to.eql(leader.profile);
expect(member.contributor).to.eql(leader.contributor);
});
let member = party.members[0];
expect(member._id).to.eql(leader._id);
expect(member.profile).to.eql(leader.profile);
expect(member.contributor).to.eql(leader.contributor);
});
});
context('Parties', () => {
let leader;
beforeEach(() => {
return generateUser().then((user) => {
leader = user;
beforeEach(async () => {
leader = await generateUser();
});
it('allows party creation without gems', async () => {
let party = await leader.post('/groups', {
type: 'party',
});
expect(party._id).to.exist;
});
it('allows party creation without gems', () => {
return expect(leader.post('/groups', {
it('prevents party creation if user is already in party', async () => {
let party = await generateGroup(leader, {
type: 'party',
})).to.eventually.have.property('_id');
});
});
it('prevents party creation if user is already in party', () => {
return expect(generateGroup(leader, {
type: 'party',
}).then((group) => {
return leader.post('/groups', {
type: 'party',
});
})).to.eventually.be.rejected.and.eql({
await expect(leader.post('/groups', { type: 'party' })).to.eventually.be.rejected.and.eql({
code: 400,
text: t('messageGroupAlreadyInParty'),
});
@@ -98,49 +92,52 @@ describe('POST /groups', () => {
context('Guilds', () => {
let leader;
beforeEach(() => {
return generateUser({
beforeEach(async () => {
leader = await generateUser({
balance: 2,
}).then((user) => {
leader = user;
});
});
it('prevents guild creation when user does not have enough gems', () => {
return expect(generateUser({
it('prevents guild creation when user does not have enough gems', async () => {
let userWithoutGems = await generateUser({
balance: 0.75,
}).then((user) => {
return user.post('/groups', {
type: 'guild',
});
})).to.eventually.be.rejected.and.eql({
});
await expect(userWithoutGems.post('/groups', { type: 'guild' })).to.eventually.be.rejected.and.eql({
code: 401,
text: t('messageInsufficientGems'),
});
});
it('can create a public guild', () => {
return expect(leader.post('/groups', {
it('can create a public guild', async () => {
let guild = await leader.post('/groups', {
type: 'guild',
privacy: 'public',
})).to.eventually.have.property('leader', leader._id);
})
expect(guild.leader).to.eql(leader._id);
});
it('can create a private guild', () => {
return expect(leader.post('/groups', {
it('can create a private guild', async () => {
let privateGuild = await leader.post('/groups', {
type: 'guild',
privacy: 'private',
})).to.eventually.have.property('leader', leader._id);
});
expect(privateGuild.leader).to.eql(leader._id);
});
it('deducts gems from user and adds them to guild bank', () => {
return expect(leader.post('/groups', {
it('deducts gems from user and adds them to guild bank', async () => {
let guild = await leader.post('/groups', {
type: 'guild',
privacy: 'private',
}).then((group) => {
expect(group.balance).to.eql(1);
return leader.get('/user');
})).to.eventually.have.deep.property('balance', 1);
});
expect(guild.balance).to.eql(1);
let updatedUser = await leader.get('/user');
expect(updatedUser.balance).to.eql(1);
});
});
});

View File

@@ -5,12 +5,9 @@ import {
describe('GET /user', () => {
let user;
before(() => {
return generateUser().then((usr) => {
return usr.get('/user');
}).then((fetchedUser) => {
user = fetchedUser;
});
before(async () => {
let usr = await generateUser();
user = await usr.get('/user');
});
it('gets the user object', () => {

View File

@@ -33,6 +33,12 @@ class ApiUser {
this.put = _requestMaker(this, 'put');
this.del = _requestMaker(this, 'del');
}
update (options) {
return new Promise((resolve) => {
_updateDocument('users', this, options, resolve);
});
}
}
// Sets up an abject that can make all REST requests

View File

@@ -51,8 +51,7 @@
ng-click='purchase("hatchingPotions", Content.hatchingPotions.Peppermint)')
p {{::Content.hatchingPotions.Peppermint.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems
// div
div
button.customize-option(popover='{{::Content.spells.special.nye.notes()}}', popover-title='{{::Content.spells.special.nye.text()}}', popover-trigger='mouseenter', popover-placement='right', popover-append-to-body='true', ng-click='castStart(Content.spells.special.nye)', class='inventory_special_nye')
p {{Content.spells.special.nye.value}}
span(class='shop_gold')
p {{Content.spells.special.nye.value}}
span(class='shop_gold')

View File

@@ -1,27 +1,51 @@
h2 12/23/2015 - SNOWBALL TRANSFORMATION ITEM, DECEMBER SUBSCRIBER ITEMS, AND ANDROID UPDATE
h2 12/30/2015 - NEW YEAR'S EVE CELEBRATION: PARTY HATS, NEW YEAR'S CARDS, BEEMINDER GUEST POST, AND LAST CHANCE FOR WINTER FLAME ITEM SET
hr
tr
td
.promo_mystery_201512.pull-right
h3 December Subscriber Items
p The December Subscriber Items have been revealed: the Winter Flame Item Set! All December subscribers will receive the Winter Flame and the Cold Fire Armor. You still have eight days to <a href='/#/options/settings/subscription'>subscribe</a> and receive the item set! Thank you so much for your support - we really do rely on you to keep Habitica free to use and running smoothly.
p.small.muted by Lemoness
.npc_matt.pull-right
h3 Party Hats
p In honor of the new year, some free Party Hats are available in the Rewards store! New users get the ever-handsome Absurd Party Hat, and users who already received one last year get the Silly Party Hat or the new Ridiculous Party Hat. These hats will be available to purchase until January 31st, but once you've bought them, you'll have them forever. Enjoy!
p.small.muted by Lemoness and SabreCat
tr
td
.snowman.pull-right
h3 Snowball Transformation Item
p Hit your party mates with a snowball and they will undergo a mysterious transformation until their next day rollover! You can buy the Snowballs in the <a href='/#/options/inventory/seasonalshop'>Seasonal Shop</a> for Gold. Don't want to be transformed? Just buy some Salt from the Rewards Column to reverse it.
.inventory_special_nye.pull-right
h3 New Year's Cards (Until Jan 1st Only!)
p Until January 1st only, the <a href='/#/options/inventory/seasonalshop'>Seasonal Shop</a> is stocking New Year's Cards! Now you can send cards to your friends (and yourself) to wish them a Happy Habit New Year. All senders and recipients will receive the Auld Acquaintance badge!
tr
td
h3 Android Update
p Thanks to our awesome open-source contributors, we've released a <a href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica' target='_blank'>new Android update</a>! It contains plenty of bug fixes and some small requested improvements, like the floating action button hiding on scroll. Be sure to download it now! And if you'd like to take a break between Dailies to leave a review, we would love that. It really helps us out :)
p.small.muted by Data5tream, dlew, Vorgone, farsidesoul, viirus, negue and FranzeJR
.promo_peppermint_flame.pull-right
h3 Last Chance for Peppermint Potions and Winter Flame Set
p Reminder: this is the final day to <a href='/#/options/inventory/drops'>buy Peppermint Hatching Potions</a>! If you want to hatch some Peppermint Pets and Mounts, now is the time.
br
p It's also the final day to <a href='/#/options/settings/subscription'>subscribe</a> and receive the Winter Flame Item Set! Thanks so much for your support <3
p.small.muted by Lemoness and SabreCat
tr
td
h3 Beeminder Guest Post
p Our productivity pal Beeminder has written <a href='https://habitica.wordpress.com/2015/12/30/guest-post-beeminder-and-habitica-go-together-like-bees-and-carrots/' target='_blank'>an awesome post on our blog</a> about how Habitica and Beeminder can work together to help you with your life improvement goals. Go check it out!
if menuItem !== 'oldNews'
hr
a(href='/static/old-news', target='_blank') Read older news
mixin oldNews
h2 12/23/2015 - SNOWBALL TRANSFORMATION ITEM, DECEMBER SUBSCRIBER ITEMS, AND ANDROID UPDATE
tr
td
.promo_mystery_201512.pull-right
h3 December Subscriber Items
p The December Subscriber Items have been revealed: the Winter Flame Item Set! All December subscribers will receive the Winter Flame and the Cold Fire Armor. You still have eight days to <a href='/#/options/settings/subscription'>subscribe</a> and receive the item set! Thank you so much for your support - we really do rely on you to keep Habitica free to use and running smoothly.
p.small.muted by Lemoness
tr
td
.snowman.pull-right
h3 Snowball Transformation Item
p Hit your party mates with a snowball and they will undergo a mysterious transformation until their next day rollover! You can buy the Snowballs in the <a href='/#/options/inventory/seasonalshop'>Seasonal Shop</a> for Gold. Don't want to be transformed? Just buy some Salt from the Rewards Column to reverse it.
tr
td
h3 Android Update
p Thanks to our awesome open-source contributors, we've released a <a href='https://play.google.com/store/apps/details?id=com.habitrpg.android.habitica' target='_blank'>new Android update</a>! It contains plenty of bug fixes and some small requested improvements, like the floating action button hiding on scroll. Be sure to download it now! And if you'd like to take a break between Dailies to leave a review, we would love that. It really helps us out :)
p.small.muted by Data5tream, dlew, Vorgone, farsidesoul, viirus, negue and FranzeJR
h2 12/18/2015 - WINTER WONDERLAND BEGINS: WINTER CLASS OUTFITS, SEASONAL SHOP, AND NPC DECORATIONS!
tr
td