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

This commit is contained in:
Matteo Pagliazzi
2016-01-13 18:40:04 +01:00
335 changed files with 13066 additions and 11799 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

+38 -1
View File
@@ -10,10 +10,47 @@
th.feature-name
text-align center
font-weight: bold
.dashed-underline
border-bottom:1px dashed #000
text-decoration:none
cursor pointer
cursor: pointer
.buy-gems .form-inline .form-control
padding:6px
p.resubscribe
margin-top:20px
.buy-gems a.purchase
height:25px
line-height:12px
font-size:12px
margin:5px
p.resubscribe a.purchase
display:block
.buy-gems button.customize-option
position:relative
.buy-gems .stack-count
position:absolute
bottom:-6px
right:-9px
.buy-gems .gem-count-popover
display:block;
.buy-gems h3
margin-top:0px
.buy-gems .well
margin-top:15px
.noninteractive-button
padding: 0.5em
margin-right: 0.5em
// Required by angular-ui-bootstrap
.nav, .pagination, .carousel, .panel-title a
+30
View File
@@ -69,3 +69,33 @@ a.h2.accordion.collapsed:before
color: grey
content: "\e080"
margin-right: 0.5em
// Merch Page
.merch-block
border: 1px dotted #553889
border-radius: 6px
padding: 15px
margin-bottom: 20px
&:hover
background: #f5f5f5
div
width 100%
text-align center
padding: 30px 0
a img
display inline
max-width: 350px
img
display: block
max-width: 200px
.btn
width 100%
background-color: #553889
margin-top: 20px
color: #ffffff
&:hover
color: #ffffff
background-color: #452d6e
border-color: #342253
@@ -25,7 +25,7 @@ function($rootScope, User, $http, Content) {
key: window.env.STRIPE_PUB_KEY,
address: false,
amount: amount,
name: 'HabitRPG',
name: 'Habitica',
description: sub ? window.env.t('subscribe') : window.env.t('checkout'),
image: "/apple-touch-icon-144-precomposed.png",
panelLabel: sub ? window.env.t('subscribe') : window.env.t('checkout'),
@@ -107,7 +107,7 @@ function($rootScope, User, $http, Content) {
// because no easy method to intercept those types of closings
// and we need to make some cleanup
keyboard: false,
backdrop: 'static'
backdrop: 'static'
});
modal.rendered.then(function(){
@@ -161,9 +161,9 @@ function($rootScope, User, $http, Content) {
if(Payments.amazonPayments.type === 'single'){
return Payments.amazonPayments.paymentSelected === true;
}else if(Payments.amazonPayments.type === 'subscription'){
return Payments.amazonPayments.paymentSelected === true &&
return Payments.amazonPayments.paymentSelected === true &&
// Mah.. one is a boolean the other a string...
Payments.amazonPayments.recurringConsent === 'true';
Payments.amazonPayments.recurringConsent === 'true';
}else{
return false;
}
@@ -179,7 +179,7 @@ function($rootScope, User, $http, Content) {
onPaymentSelect: function() {
$rootScope.$apply(function(){
Payments.amazonPayments.paymentSelected = true;
});
});
},
onError: amazonOnError
@@ -194,7 +194,7 @@ function($rootScope, User, $http, Content) {
new OffAmazonPayments.Widgets.Consent({
sellerId: window.env.AMAZON_PAYMENTS.SELLER_ID,
amazonBillingAgreementId: Payments.amazonPayments.billingAgreementId,
amazonBillingAgreementId: Payments.amazonPayments.billingAgreementId,
design: {
designMode: 'responsive'
},
@@ -213,7 +213,7 @@ function($rootScope, User, $http, Content) {
},
onError: amazonOnError
}).bind('AmazonPayRecurring');
}).bind('AmazonPayRecurring');
}
}else{
walletParams.amazonOrderReferenceId = Payments.amazonPayments.orderReferenceId;
@@ -267,7 +267,8 @@ function($rootScope, User, $http, Content) {
Payments.encodeGift = function(uuid, gift){
gift.uuid = uuid;
return JSON.stringify(gift);
var encodedString = JSON.stringify(gift);
return encodeURIComponent(encodedString);
}
return Payments;
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

+63
View File
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="206.8 158.6 1132.2 280" style="enable-background:new 206.8 158.6 1132.2 280;" xml:space="preserve">
<style type="text/css">
.st0{fill:#30ABE9;}
</style>
<path class="st0" d="M500.9,182.9c-0.3-0.1-0.6-0.2-0.8-0.2H500c-1.3-0.3-2.5-0.6-3.7-0.9l0,0h-0.1l0,0l-0.3-0.1l-0.6-0.1l0,0l0,0
l0,0h-0.1l0,0l0,0l0,0h-0.1l0,0l0,0l-0.5-0.1l0,0c-10.4-2.4-21.1-4.4-31.8-5.7c-11.1-1.4-22.5-2.3-33.8-2.5c-2.2,0-4.5-0.1-6.7-0.1
c-7.1,0-14.2,0.2-21.2,0.7h-0.5l0.1,0.5c1.1,9.2-1.8,18.4-8,25.3c-5.4,6-13.1,9.8-21.1,10.4c-0.8,0.1-1.6,0.1-2.5,0.1
c-8,0-15.7-3-21.6-8.4c-4.8-4.4-8.3-10.4-9.7-16.7l-0.1-0.5l-0.4,0.1c-10.6,3-21.2,6.5-31.5,10.6c-10,4-19.9,8.5-29.4,13.5
c-4.8,2.5-9.5,5.1-14.1,7.8c-2.4,1.4-4.9,2.9-7.3,4.4c-0.1,0-0.2,0.1-0.4,0.2c-0.5,0.3-1.2,0.8-1.9,1.2c-1.3,0.8-1.5,0.9-1.3,1.3
l27.3,60l0.2,0.4l0.4-0.2c9.8-5.7,20-10.9,30.3-15.5c0.7-0.3,1.3-0.6,2-0.9l21.4,89.2l0.1,0.6l0.5-0.3c5.7-3.1,10.4-5.4,15-7.3
c5.7-2.3,11-4.2,16.1-5.6c9.5-2.7,19.2-4,29.1-4c1.5,0,3.1,0,4.6,0.1c4.7,0.2,10.7,0.7,16.3,2.9c2.6,1,4.6,2.2,6.1,3.7
c1.7,1.7,2.6,4,2.5,6.5c0,5.2-4.1,9.4-7.4,12.1c-2.2,1.7-4.6,3.1-6.4,4.1c-2.3,1.3-4.8,2.4-7.3,3.4c-4.9,2-10.1,3.5-16,4.7
c-5.8,1.2-11.5,1.9-16.9,2.1c-0.5,0-1,0-1.6,0c-0.7,0-1.3,0-2,0h-0.5c-1.4,0-2.8-0.1-4.2-0.3c-1.5-0.2-2.7-0.3-4-0.7h-0.1
c-1.3-0.3-1.7-0.6-1.9-0.7c0.6-0.9,1.6-1.6,2.5-2.2c1-0.6,2.1-1.3,3.4-1.9c2.2-1.1,4.7-2.1,7.7-3c5.1-1.6,10.6-2.7,16.5-3.2
c2.8-0.3,5.6-0.4,8.4-0.4c0.3,0,0.7,0,1,0c0.9,0,2.1,0,3.2,0.1c0.2,0,0.5,0,0.7,0.1c0.4,0,0.8,0.1,1.2,0.1h0.1h0.1
c3-1,13.1-4.9,15.9-11.4l0.2-0.4l-0.4-0.2c-0.1-0.1-0.3-0.1-0.5-0.2c-0.1,0-0.1,0-0.2-0.1c-1.1-0.3-2.5-0.8-4-1.1
c-2.6-0.6-5.4-1.1-8.4-1.5c-3.1-0.4-6.3-0.5-9.5-0.5c-2.6,0-5.2,0.1-7.8,0.4c-6.3,0.6-11.8,1.5-16.7,2.8c-6,1.5-11.1,3.4-15.5,5.8
c-5.5,2.9-9.3,6.4-11.5,10.4c-2.4,4.4-2.5,9.6-0.3,13.7c2.1,4,6.3,6.8,12.3,8.4c4,1,8.5,1.6,13.7,1.6c0.9,0,1.9,0,2.9,0
c5.4-0.2,11.1-0.8,16.9-1.9c5.6-1.1,11.1-2.5,16.3-4.3c5.7-2,10.7-4.2,15.1-6.7c0.7-0.4,1.2-0.7,1.7-1l1.3-0.8c0,0.2,0,0.4,0,0.6
c0,0.6,0,1.1-0.2,1.8c-0.4,2.3-1.3,4.5-2.9,6.6c-2.5,3.4-6.3,6.3-11.6,9c-4.4,2.2-9.4,4.1-15.2,5.8c-5.3,1.5-10.9,2.7-16.4,3.5
c-1.2,0.2-2.4,0.3-3.5,0.5l-0.7,0.1c-0.3,0-0.7,0.1-1,0.1c-0.3,0-0.8,0.1-0.8,0.1h-0.4v0.4l0.8,14.4v0.5h0.5l2.3-0.2
c2.7-0.3,5.6-0.7,8.5-1.2c12.6-2.2,23-5.4,31.8-9.7c11.4-5.7,18.9-13,22.2-21.7c2-5.4,2.5-11.4,1.3-16.7c-0.3-1.3-0.7-2.6-1.2-3.8
c-0.1-0.2-0.2-0.4-0.3-0.7c-0.2-0.3-0.4-0.7-0.4-1c-0.1-0.2,0.2-0.7,0.5-1c0.1-0.1,0.2-0.3,0.3-0.4c1.4-2.3,2.5-4.8,3.2-7.5
c1.3-5.2,1-10.8-0.8-15.6c-1.8-4.7-5.2-8.8-9.9-11.8c-4.2-2.7-9.2-4.7-15.4-6c-5.2-1.1-10.7-1.6-16.9-1.6c-11.7,0-23.2,1.5-34,4.6
c-5.6,1.6-11.4,3.6-18.4,6.4l-20.9-86.8l-0.1-0.5l-0.5,0.2c-10.6,4-21.1,8.5-31.3,13.6c-1.5,0.7-2.9,1.5-4.4,2.2l-16.3-35.7
c0.3-0.2,0.7-0.4,1.4-0.8c0.1-0.1,0.2-0.1,0.2-0.1c4.7-2.7,9.5-5.3,14.3-7.8c9.6-4.9,19.5-9.3,29.6-13.2c4.9-1.9,9.9-3.6,14.9-5.3
c2.1,3.6,4.7,6.8,7.7,9.7c6.3,6,14.1,10.1,22.6,11.9c3,0.6,6,0.9,9.1,0.9c5.6,0,11-1,16.1-2.9c7.5-2.8,14.2-7.6,19.3-13.8
c2.6-3.1,4.7-6.5,6.4-10.1c0.8-1.8,1.6-3.7,2.2-5.7c0.3-1,0.6-2,0.8-3c0.1-0.5,0.2-1,0.3-1.4l0,0h0.2c0.4,0,0.8,0,1.2,0
c2.3-0.1,4.6-0.1,6.9-0.1c9.1,0,18.3,0.4,27.3,1.2c11.3,1,22.6,2.7,33.7,4.9h0.2c1,0.2,1.6,0.3,1.9,0.4l-7.9,38.7
c-0.9-0.1-3.2-0.4-4.9-0.7c-1.2-0.2-2.1-0.3-2.4-0.3c-2.8-0.4-5.7-0.7-8.6-0.9c-5.7-0.6-11.5-1-17.3-1.2c-2.9-0.1-5.8-0.2-8.7-0.2
c-0.1,0-0.7,0-0.7,0h-0.4v0.4l-3.6,85.5c0,0.2,0.1,0.5,0.5,0.6c0,0,0,0,0.1,0c1.2,0.6,2.4,1.2,3.6,1.8c2.5,1.4,4.7,3,6.5,4.7
c0.5,0.4,0.9,0.9,1.3,1.3c0.2,0.2,0.4,0.5,0.6,0.7c0.1,0.1,0.2,0.3,0.3,0.4l0.7,1.4l0.1-1.6l3.8-80.9c11.2,0.6,22.2,1.9,33.6,3.8
c1.4,0.2,2.8,0.5,4.3,0.8l0.5,0.1c1.4,0.3,1.4,0.3,1.4,0.3h0.4l0.1-0.4l13.1-64.5C501.9,183.1,501.5,183,500.9,182.9z"/>
<g>
<path class="st0" d="M522.4,339.3v-53h-15.2V269h15.2v-25.1h20V269H561v17.3h-18.6v48.1c0,6.3,3,11,8.7,11c3.8,0,7.2-1.7,8.7-3.4
l4.8,15.2c-3.6,3.2-9.5,5.9-18.6,5.9C530.4,363.1,522.4,354.7,522.4,339.3z"/>
<path class="st0" d="M573.3,314.8c0-26.6,19.4-48.1,46.6-48.1c27.5,0,45,20.9,45,49.8v4.9h-70.7c1.5,13.7,11.8,25.3,29.1,25.3
c9.1,0,19.6-3.6,26.2-10.3l9.1,13.1c-9.3,8.7-22.8,13.5-37.2,13.5C593.6,363.1,573.3,343.9,573.3,314.8z M619.7,283.1
c-16.9,0-24.9,13.1-25.7,24.1h51.7C645.1,296.4,637.7,283.1,619.7,283.1z"/>
<path class="st0" d="M679.1,314.8c0-26.6,19.4-48.1,46.6-48.1c27.5,0,45,20.9,45,49.8v4.9H700c1.5,13.7,11.8,25.3,29.1,25.3
c9.1,0,19.6-3.6,26.2-10.3l9.1,13.1c-9.3,8.7-22.8,13.5-37.2,13.5C699.5,363.1,679.1,343.9,679.1,314.8z M725.5,283.1
c-16.9,0-24.9,13.1-25.7,24.1h51.7C751,296.4,743.5,283.1,725.5,283.1z"/>
<path class="st0" d="M782.7,348.6l9.1-14.2c6.5,6.6,19.2,12.7,31,12.7c12,0,18.2-4.9,18.2-12.2c0-18.1-55.5-5.1-55.5-40.5
c0-15,12.9-27.7,36.1-27.7c15.6,0,27.4,5.5,35.5,12.7l-8.4,14.1c-5.5-6.1-15.6-11-27.2-11c-10.4,0-16.9,4.9-16.9,11.4
c0,16.2,55.5,4,55.5,40.5c0,16.3-13.5,28.7-38.2,28.7C806.2,363.1,792.2,357.9,782.7,348.6z"/>
<path class="st0" d="M900.1,348.1v47.7h-20V269h20v12.5c6.8-9.1,17.5-14.8,29.5-14.8c23.8,0,40.7,17.9,40.7,48.1
s-16.9,48.3-40.7,48.3C918,363.1,907.7,357.8,900.1,348.1z M949.7,314.8c0-17.9-10.3-30.4-26-30.4c-9.3,0-19,5.3-23.6,12.2v36.3
c4.4,6.8,14.3,12.5,23.6,12.5C939.4,345.4,949.7,332.7,949.7,314.8z"/>
<path class="st0" d="M990.2,360.8V269h20v13.3c7-8.7,18.1-15.6,29.8-15.6v19.8c-1.7-0.4-3.6-0.6-6.1-0.6c-8.6,0-19.8,5.7-23.8,12.4
v62.5H990.2z"/>
<path class="st0" d="M1053.8,245.5c0-6.8,5.5-12.4,12.3-12.4c6.8,0,12.4,5.5,12.4,12.4c0,6.8-5.5,12.4-12.4,12.4
S1053.8,252.3,1053.8,245.5z M1056.1,360.8V269h20v91.8H1056.1z"/>
<path class="st0" d="M1164,360.8v-57.4c0-14.4-7.4-19-18.6-19c-10.1,0-18.8,6.1-23.6,12.2v64.2h-20V269h20v12.4
c6.1-7.2,18.1-14.6,32.5-14.6c19.8,0,29.6,10.6,29.6,29.5v64.6L1164,360.8L1164,360.8z"/>
<path class="st0" d="M1208.7,384.4l9.3-14.4c7.6,8.6,17.1,11.8,29.3,11.8c13.1,0,27.2-6.1,27.2-25.7v-10.5
c-7,9.3-17.5,15.4-29.5,15.4c-23.4,0-40.7-16.9-40.7-47.1c0-29.8,16.9-47.1,40.7-47.1c11.6,0,22,5.1,29.5,15v-12.7h20v87
c0,33.1-24.7,42-47.1,42C1231.6,398,1220.1,394.6,1208.7,384.4z M1274.4,331v-34.4c-4.6-6.7-14.2-12.2-23.6-12.2
c-16,0-26,11.8-26,29.5c0,17.7,10.1,29.3,26,29.3C1260.1,343.1,1269.8,337.6,1274.4,331z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

+56 -17
View File
@@ -11,7 +11,8 @@ var Group = require('./../../models/group').model;
var Challenge = require('./../../models/challenge').model;
var moment = require('moment');
var logging = require('./../../libs/api-v2/logging');
var acceptablePUTPaths;
let restrictedPUTSubPaths;
var api = module.exports;
var firebase = require('../../libs/api-v2/firebase');
var webhook = require('../../libs/api-v2/webhook');
@@ -132,8 +133,9 @@ api.score = function(req, res, next) {
}
t.value += delta;
if (t.type == 'habit' || t.type == 'daily')
if (t.type == 'habit' || t.type == 'daily') {
t.history.push({value: t.value, date: +new Date});
}
chal.save();
clearMemory();
});
@@ -283,17 +285,44 @@ api.getUserAnonymized = function(req, res, next) {
* The trick here is to only accept leaf paths, not root/intermediate paths (see http://goo.gl/OEzkAs)
* FIXME - one-by-one we want to widdle down this list, instead replacing each needed set path with API operations
*/
acceptablePUTPaths = _.reduce(require('./../../models/user').schema.paths, function(m,v,leaf){
var found= _.find('achievements filters flags invitations lastCron party preferences profile stats inbox'.split(' '), function(root){
return leaf.indexOf(root) == 0;
acceptablePUTPaths = _.reduce(require('./../../models/user').schema.paths, (m, v, leaf) => {
let updatablePaths = 'achievements filters flags invitations lastCron party preferences profile stats inbox'.split(' ');
let found = _.find(updatablePaths, (rootPath) => {
return leaf.indexOf(rootPath) === 0;
});
if (found) m[leaf]=true;
return m;
}, {})
_.each('stats.class'.split(' '), function(removePath){
if (found) m[leaf] = true;
return m;
}, {});
restrictedPUTSubPaths = 'stats.class'.split(' ');
_.each(restrictedPUTSubPaths, (removePath) => {
delete acceptablePUTPaths[removePath];
})
});
let requiresPurchase = {
'preferences.background': 'background',
'preferences.shirt': 'shirt',
'preferences.size': 'size',
'preferences.skin': 'skin',
'preferences.hair.bangs': 'hair.bangs',
'preferences.hair.base': 'hair.base',
'preferences.hair.beard': 'hair.beard',
'preferences.hair.color': 'hair.color',
'preferences.hair.flower': 'hair.flower',
'preferences.hair.mustache': 'hair.mustache',
};
let checkPreferencePurchase = (user, path, item) => {
let itemPath = `${path}.${item}`;
let isDefaultPreference = _.get(shared.content.defaultAppearancePreferences, itemPath);
if (isDefaultPreference) return true;
return _.get(user.purchased, itemPath);
};
/**
* Update user
@@ -301,21 +330,31 @@ _.each('stats.class'.split(' '), function(removePath){
* PUT /user {'stats.hp':50, 'tasks.TASK_ID.repeat.m':false}
* See acceptablePUTPaths for which user paths are supported
*/
api.update = function(req, res, next) {
var user = res.locals.user;
var errors = [];
api.update = (req, res, next) => {
let user = res.locals.user;
let errors = [];
if (_.isEmpty(req.body)) return res.json(200, user);
_.each(req.body, function(v, k) {
if (acceptablePUTPaths[k])
_.each(req.body, (v, k) => {
let purchasable = requiresPurchase[k];
if (purchasable && !checkPreferencePurchase(user, purchasable, v)) {
return errors.push(`Must purchase ${v} to set it on ${k}`);
}
if (acceptablePUTPaths[k]) {
user.fns.dotSet(k, v);
else
} else {
errors.push(shared.i18n.t('messageUserOperationProtected', { operation: k }));
}
return true;
});
user.save(function(err) {
user.save((err) => {
if (!_.isEmpty(errors)) return res.json(401, {err: errors});
if (err) return next(err);
res.json(200, user);
user = errors = null;
});
@@ -19,15 +19,15 @@ paypal.configure({
});
// https://developer.paypal.com/docs/api/#billing-plans-and-agreements
var billingPlanTitle ="HabitRPG Subscription";
var billingPlanTitle ="Habitica Subscription";
var billingPlanAttributes = {
"name": billingPlanTitle,
"description": billingPlanTitle,
"type": "INFINITE",
"merchant_preferences": {
"auto_bill_amount": "yes",
"cancel_url": live ? 'https://habitrpg.com' : 'http://localhost:3000',
"return_url": (live ? 'https://habitrpg.com' : 'http://localhost:3000') + '/paypal/subscribe/success'
"cancel_url": live ? 'https://habitica.com' : 'http://localhost:3000',
"return_url": (live ? 'https://habitica.com' : 'http://localhost:3000') + '/paypal/subscribe/success'
},
payment_definitions: [{
"type": "REGULAR",
@@ -63,7 +63,7 @@ switch(OP) {
"op": "replace",
"path": "/merchant_preferences",
"value": {
"cancel_url": "https://habitrpg.com"
"cancel_url": "https://habitica.com"
}
};
paypal.billingPlan.update(nconf.get("PAYPAL:billing_plans:12"), update, function (err, res) {
+1
View File
@@ -378,6 +378,7 @@ export let schema = new Schema({
tagsCollapsed: {type: Boolean, default: false},
advancedCollapsed: {type: Boolean, default: false},
toolbarCollapsed: {type: Boolean, default: false},
reverseChatOrder: {type: Boolean, default: false},
background: String,
displayInviteToPartyWhenPartyIs1: {type: Boolean, default: true},
webhooks: {type: Schema.Types.Mixed, default: {}}, // TODO array? and proper controller... unless VersionError becomes problematic
+1 -1
View File
@@ -18,7 +18,7 @@ router.get('/', i18n.getUserLanguage, locals, function(req, res) {
// -------- Static Pages --------
var pages = ['front', 'privacy', 'terms', 'api', 'features', 'videos', 'contact', 'plans', 'new-stuff', 'community-guidelines', 'old-news', 'press-kit', 'faq', 'overview', 'apps', 'clear-browser-data'];
var pages = ['front', 'privacy', 'terms', 'api', 'features', 'videos', 'contact', 'plans', 'new-stuff', 'community-guidelines', 'old-news', 'press-kit', 'faq', 'overview', 'apps', 'clear-browser-data', 'merch'];
_.each(pages, function(name){
router.get('/static/' + name, i18n.getUserLanguage, locals, function(req, res) {
+1 -1
View File
@@ -136,7 +136,7 @@
| {{::pot.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems
li.customize-menu
// li.customize-menu
menu.pets-menu!=env.t('magicHatchingPotions') + " - " + env.t('winterEventAvailability')
p.muted=env.t('premiumPotionNoDropExplanation')
div(ng-repeat='pot in Content.hatchingPotions', ng-if='pot.premium && pot.canBuy(user)')
@@ -42,7 +42,7 @@
ng-click='purchase("special", Content.spells.special.snowball)')
p {{::Content.spells.special.snowball.value}}
span(class='shop_gold')
div
// div
button.customize-option(class='Pet_HatchingPotion_Peppermint',
popover='{{::Content.hatchingPotions.Peppermint.notes()}}',
popover-title!=env.t("potion", {potionType: "{{::Content.hatchingPotions.Peppermint.text()}}"}),
@@ -51,7 +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')
+1 -1
View File
@@ -85,7 +85,7 @@ mixin customizeProfile(mobile)
+buyPref('hair.color', ['rainbow','yellow','green','purple','blue','TRUred'], 'rainbowColors')
+buyPref('hair.color', ['pblue2','pgreen2','porange2','ppink2','ppurple2','pyellow2'], 'shimmerColors', 'disabled')
+buyPref('hair.color', ['candycorn','ghostwhite','halloween','midnight','pumpkin','zombie'], 'hauntedColors', 'disabled')
+buyPref('hair.color', ['aurora','festive','hollygreen','peppermint','snowy','winterstar'], 'winteryColors', 'disabled')
+buyPref('hair.color', ['aurora','festive','hollygreen','peppermint','snowy','winterstar'], 'winteryColors')
li.customize-menu
menu(label=env.t('bodyHair'))
+30 -25
View File
@@ -275,26 +275,21 @@ script(id='partials/options.settings.export.html', type="text/ng-template")
a(href="/export/userdata.json")= env.t('json')
mixin subPerks()
table.table.table-striped
tr
td
span.hint(popover=env.t('buyGemsGoldText', {gemCost: "{{Shared.planGemLimits.convRate}}", gemLimit: "{{Shared.planGemLimits.convCap}}"}),popover-trigger='mouseenter',popover-placement='right') #{env.t('buyGemsGold')}&nbsp;
span.badge.badge-success(ng-show='_subscription.key!="basic_earned"')=env.t('buyGemsGoldCap', {amount: '{{gemGoldCap(_subscription) | min }}'})
tr
td
span.hint(popover=env.t('retainHistoryText'),popover-trigger='mouseenter',popover-placement='right')=env.t('retainHistory')
tr
td
span.hint(popover=env.t('doubleDropsText'),popover-trigger='mouseenter',popover-placement='right')=env.t('doubleDrops')
tr
td
span.hint(popover=env.t('mysteryItemText'),popover-trigger='mouseenter',popover-placement='right') #{env.t('mysteryItem')}&nbsp;
div(ng-show='_subscription.key!="basic_earned"')
.badge.badge-success=env.t('mysticHourglass', {amount: '+{{numberOfMysticHourglasses(_subscription)}}'})
.small.muted=env.t('mysticHourglassText')
tr
td
span.hint(popover=env.t('supportDevsText'),popover-trigger='mouseenter',popover-placement='right')=env.t('supportDevs')
ul
li
span.hint(popover=env.t('buyGemsGoldText', {gemCost: "{{Shared.planGemLimits.convRate}}", gemLimit: "{{Shared.planGemLimits.convCap}}"}),popover-trigger='mouseenter',popover-placement='right') #{env.t('buyGemsGold')}&nbsp;
span.badge.badge-success(ng-show='_subscription.key!="basic_earned"')=env.t('buyGemsGoldCap', {amount: '{{gemGoldCap(_subscription) | min }}'})
li
span.hint(popover=env.t('retainHistoryText'),popover-trigger='mouseenter',popover-placement='right')=env.t('retainHistory')
li
span.hint(popover=env.t('doubleDropsText'),popover-trigger='mouseenter',popover-placement='right')=env.t('doubleDrops')
li
span.hint(popover=env.t('mysteryItemText'),popover-trigger='mouseenter',popover-placement='right') #{env.t('mysteryItem')}&nbsp;
div(ng-show='_subscription.key!="basic_earned"')
.badge.badge-success=env.t('mysticHourglass', {amount: '+{{numberOfMysticHourglasses(_subscription)}}'})
.small.muted=env.t('mysticHourglassText')
li
span.hint(popover=env.t('supportDevsText'),popover-trigger='mouseenter',popover-placement='right')=env.t('supportDevs')
script(id='partials/feature-matrix-check.html',type='text/ng-template')
span.task-checker.action-yesno
@@ -341,6 +336,7 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template')
.col-md-6
table.table.alert.alert-info(ng-if='user.purchased.plan.customerId')
tr(ng-if='user.purchased.plan.dateTerminated'): td.alert.alert-warning
span.noninteractive-button.btn-danger=env.t('canceledSubscription')
i.glyphicon.glyphicon-time
| #{env.t('subCanceled')} <strong>{{moment(user.purchased.plan.dateTerminated).format('MM/DD/YYYY')}}</strong>
tr(ng-if='!user.purchased.plan.dateTerminated'): td
@@ -357,6 +353,7 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template')
li #{env.t('gemCapExtra')} {{user.purchased.plan.consecutive.gemCapExtra}}
li #{env.t('mysticHourglasses')} {{user.purchased.plan.consecutive.trinkets}}
div(ng-if='!user.purchased.plan.customerId || (user.purchased.plan.customerId && user.purchased.plan.dateTerminated)')
h4(ng-if='(user.purchased.plan.customerId && user.purchased.plan.dateTerminated)')= env.t("resubscribe")
.form-group
.radio(ng-repeat='block in Content.subscriptionBlocks | toArray | omit: "discount==true" | orderBy:"months"')
label
@@ -372,12 +369,20 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template')
.form-group
input.form-control(type='text', ng-model='_subscription.coupon', placeholder= env.t('couponPlaceholder'))
.form-group
button.btn.btn-small(type='button',ng-click='applyCoupon(_subscription.coupon)')= env.t("apply")
button.pull-right.btn.btn-small(type='button',ng-click='applyCoupon(_subscription.coupon)')= env.t("apply")
h3(ng-if='(user.purchased.plan.customerId && user.purchased.plan.dateTerminated)')= env.t("resubscribe")
a.btn.btn-primary(ng-click='Payments.showStripe({subscription:_subscription.key, coupon:_subscription.coupon})', ng-disabled='!_subscription.key')= env.t('card')
a.btn.btn-success(href='/paypal/subscribe?_id={{user._id}}&apiToken={{user.apiToken}}&sub={{_subscription.key}}{{_subscription.coupon ? "&coupon="+_subscription.coupon : ""}}', ng-disabled='!_subscription.key')=env.t('paypal')
a.btn.btn-warning(ng-click="Payments.amazonPayments.init({type: 'subscription', subscription:_subscription.key, coupon:_subscription.coupon})")=env.t('amazonPayments')
p.resubscribe
small.muted=env.t('subscribeUsing')
a.purchase.btn.btn-primary(ng-click='Payments.showStripe({subscription:_subscription.key, coupon:_subscription.coupon})', ng-disabled='!_subscription.key')= env.t('card')
.container-fluid.slight-vertical-padding
.row.text-center
.col-xs-6
a.purchase(href='/paypal/subscribe?_id={{user._id}}&apiToken={{user.apiToken}}&sub={{_subscription.key}}{{_subscription.coupon ? "&coupon="+_subscription.coupon : ""}}', ng-disabled='!_subscription.key')
img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png',alt=env.t('paypal'))
.col-xs-6
a.purchase(ng-click="Payments.amazonPayments.init({type: 'subscription', subscription:_subscription.key, coupon:_subscription.coupon})")
img(src='https://payments.amazon.com/gp/cba/button',alt=env.t('amazonPayments'))
div(ng-if='user.purchased.plan.customerId')
.btn.btn-primary(ng-if='!user.purchased.plan.dateTerminated && user.purchased.plan.paymentMethod=="Stripe"', ng-click='Payments.showStripeEdit()')=env.t('subUpdateCard')
.btn.btn-sm.btn-danger(ng-if='!user.purchased.plan.dateTerminated', ng-click='Payments.cancelSubscription()')=env.t('cancelSub')
@@ -18,3 +18,10 @@ form.chat-form(ng-if='user.flags.communityGuidelinesAccepted' ng-submit='postCha
input.btn(type='submit', value=env.t('sendChat'), ng-disabled='_sending')
button.btn(type="button", ng-click='sync(group)', ng-disabled='_sending')=env.t('toolTipMsg')
include ../../shared/formatting-help
.chat-controls.clearfix
.chat-buttons
.checkbox
label
input(type='checkbox', ng-model='user.preferences.reverseChatOrder', ng-change='set({"preferences.reverseChatOrder": user.preferences.reverseChatOrder?true: false})')
span.hint(popover-trigger='mouseenter', popover-placement='top', popover=env.t('reverseChatOrderPop'))=env.t('reverseChatOrder')
@@ -1,6 +1,9 @@
mixin chatMessages(inbox)
ul.list-unstyled.tavern-chat
- var ngRepeat = inbox ? 'message in user.inbox.messages | toArray:true | orderBy:"sort":true' : 'message in group.chat track by message.id'
-
var ngRepeat = inbox ?
'message in user.inbox.messages | toArray:true | orderBy:"sort":true' :
'message in group.chat | orderBy:"timestamp":!user.preferences.reverseChatOrder track by message.id'
li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid}')
span.pull-right.text-danger(ng-if="user.contributor.admin && message.flagCount > 0")
| {{message.flagCount > 1 ? "Message Hidden" : "1 flag"}}
+1 -1
View File
@@ -9,7 +9,7 @@ include ./party
script(type='text/ng-template', id='partials/options.social.inbox.html')
.options-blurbmenu
small.muted=env.t('blurbInbox')
small.muted!=env.t('blurbInbox')
.container-fluid
.row
+2
View File
@@ -36,6 +36,8 @@ footer.footer(ng-controller='FooterCtrl')
a(href='/static/terms')=env.t('companyTerms')
li
a(href='/static/contact')=env.t('contactUs')
li
a(href='/static/merch')=env.t('merch')
.col-sm-3
h4=env.t('footerCommunity')
ul.list-unstyled
+67 -22
View File
@@ -1,30 +1,75 @@
mixin buyGemsDropdown()
.container-fluid
.row
.col-md-4.col-md-offset-4.alert.alert-info $5&nbsp;
=env.t('USD')
span#TotalGemPrice.dashed-underline(popover=env.t('donateText1'),popover-trigger='mouseenter',popover-placement='bottom') = +20 <span class="Pet_Currency_Gem1x inline-gems"/>
.container-fluid
.row
.col-md-10.col-md-offset-2
p
small.muted=env.t('paymentMethods')
a.purchase.btn.btn-primary(ng-click='Payments.showStripe({})')=env.t('card')
a.purchase(href='/paypal/checkout?_id={{user._id}}&apiToken={{user.apiToken}}')
img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png',alt='Pay now with Paypal')
a.purchase(ng-click="Payments.amazonPayments.init({type: 'single'})")
img(src='https://payments.amazon.com/gp/cba/button',alt='Pay now with Amazon Payments')
script(id='modals/buyGems.html', type='text/ng-template')
.modal-body
.buy-gems
+gemButton(true)
.well
h3=env.t('buyGems')
table.table
tr
td
span.dashed-underline(popover=env.t('donateText1'),popover-trigger='mouseenter',popover-placement='right')
| +20 <span class="Pet_Currency_Gem1x inline-gems"/>
tr
td
span.dashed-underline(popover=env.t('donateText3'),popover-trigger='mouseenter',popover-placement='right')
div(ng-if='user.purchased.plan.customerId && (user.purchased.plan.gemsBought >= User.user.purchased.plan.consecutive.gemCapExtra + Shared.planGemLimits.convCap)')
.panel.panel-default
.panel-body
h3=env.t('buyGemsGold')
p=env.t('maxBuyGems')
div(ng-if='user.purchased.plan.customerId && (user.purchased.plan.gemsBought < User.user.purchased.plan.consecutive.gemCapExtra + Shared.planGemLimits.convCap)')
.panel.panel-default
.panel-body
h3=env.t('buyGemsGold')
p=env.t('subGemPop')
.container-fluid
.row
.col-md-3
button.customize-option(ng-click='user.ops.purchase({params:{type:"gems",key:"gem"}})')
span.Pet_Currency_Gem.inline-gems
.badge.badge-success.stack-count {{Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought}}
p
| 20&nbsp;
span.shop_gold
.col-md-8
.popover.right.gem-count-popover
.arrow
.popover-content
p=env.t('buyGemsAllow1')
| &nbsp;{{Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought}}&nbsp;
=env.t('buyGemsAllow2')
div(ng-if='user.purchased.plan.customerId')
.well
h3=env.t('purchaseGemsSeparately')
+buyGemsDropdown()
div(ng-if='!user.purchased.plan.customerId')
.panel.panel-default
.panel-body
h3=env.t('purchaseGems')
.small
span.dashed-underline(popover=env.t('donateText3'),popover-trigger='mouseenter',popover-placement='bottom')
=env.t('donateText2')
tr
td.alert.alert-info $5&nbsp;
=env.t('USD')
tr
td
p
small.muted=env.t('paymentMethods')
.btn.btn-primary(ng-click='Payments.showStripe({})')=env.t('card')
a.btn.btn-success(href='/paypal/checkout?_id={{user._id}}&apiToken={{user.apiToken}}') PayPal
.btn.btn-warning(ng-click="Payments.amazonPayments.init({type: 'single'})") Amazon Payments
div(ng-include="'partials/options.settings.subscription.html'")
+buyGemsDropdown()
.container-fluid
h3=env.t('freeGemsTitle')
p!=env.t('subFreeGemsHow')
.well
h3
.small=env.t('buyGemsGoldTitle')
h3=env.t('becomeSubscriber')
div(ng-include="'partials/options.settings.subscription.html'")
.modal-footer
button.btn.btn-default(ng-click='$close()')=env.t('cancel')
+58 -20
View File
@@ -1,34 +1,72 @@
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
h2 1/8/2016 - HABITICA VIDEO AND CHALLENGE, PLUS WINTERY HAIR COLORS!
hr
tr
td
.npc_matt.pull-right
h3 Party Hats
p In honor of the new year, there's a free Party Hat 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
h3 Habitica Video
p We're excited to reveal <a href='https://youtu.be/hgdeJnSili0' target='_blank'>our first-ever Habitica video!</a> Check it out and let us know what you think. There's also a <a href='/#/options/groups/challenges/2d589b68-d5d1-4dbb-a047-7e29bb833a5f'>Challenge</a> for people who share the video on social media with a 25 Gem prize, so if you like the video enough to pass it along, be sure to enter the Challenge for a chance to win!
p.small.muted by redphoenix, Lemoness, and Blank Canvas Entertainment, LLC
tr
td
.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
.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!
.promo_winteryhair.pull-right
h3 Wintery Hair Colors
p The Seasonal Edition Wintery Hair Colors are now available for purchase from the <a href='/#/options/profile/avatar'>avatar customizations page</a>! Now you can dye your avatar's hair Snowy, Peppermint, Holly Green, Aurora, Winter Star, or Festive.
if menuItem !== 'oldNews'
hr
a(href='/static/old-news', target='_blank') Read older news
mixin oldNews
h2 1/6/2016 - JANUARY BACKGROUNDS AND ARMOIRE ITEMS REVEALED! PLUS, IMPORTANT HISTORY COMPRESSION UPDATE
tr
td
.background_snowman_army.pull-right
h3 January Backgrounds
p There are three new avatar backgrounds in the <a href='/#/options/profile/backgrounds'>Background Shop</a>! Now your avatar can build a Snowman Army, skate on a Frozen Lake, or look at the stars of a Winter Night!
p.small.muted by featherlight, louiselouise, and nonight
tr
td
.promo_enchanted_armoire_201601.pull-right
h3 January Armoire Items Revealed
p There is new equipment in Enchanted Armoire, a 100 GP Reward in the Rewards Column which unlocks after you've attained Ultimate Gear!
br
p Click on the Enchanted Armoire for a random chance at special Equipment, including the Dragon Tamer Set and the Glowing Spear! It may also give you random XP or food items. We'll be adding new equipment to it during the first week of each month, but even when you've exhausted the current supply, you can keep clicking for a chance at food and XP.
br
p Now go spend all that accumulated Gold! May the Random Number Generator smile upon you...
p.small.muted by Lemoness and SabreCat
p.small.muted Art by Balduranne
tr
td
h3 History Compression Update
p We're working on a big change to the site that will significantly improve performance, making things faster and smoother. However, in order to make these improvements, we need to compress some task and personal history. The following changes will go into effect on Sunday:
ul
li Non-subscribers will have access to their full, uncompressed history for the last 60 days. After that, their history will be compressed. (For more details, see <a href='http://habitica.wikia.com/wiki/Task_History_Compression' target='_blank'>the wiki</a>).
li Subscribers will have access to their full, uncompressed history for the last 365 days. After that, their history will be compressed. (For more details, see <a href='http://habitica.wikia.com/wiki/Task_History_Compression' target='_blank'>the wiki</a>). Subscribers will be able to gain their full history by <a href='/#/options/settings/export'>downloading it</a> prior to the change, or by posting in the <a href='/#/options/groups/guilds/5481ccf3-5d2d-48a9-a871-70a7380cee5a'>Newbies Guild</a> for an admin to access it.
br
p If you have any questions or concerns, please feel free to email <a href='mailto:admin@habitica.com' target='_blank'>admin@habitica.com</a>. We're looking forward to the performance improvements for the site!
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
tr
td
.npc_matt.pull-right
h3 Party Hats
p In honor of the new year, there's a free Party Hat 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
.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
.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!
h2 12/23/2015 - SNOWBALL TRANSFORMATION ITEM, DECEMBER SUBSCRIBER ITEMS, AND ANDROID UPDATE
tr
td
+47
View File
@@ -0,0 +1,47 @@
extends ./layout
// Merchandise page : when adding a merchandise make sure to add following resources
+ image of merchandise -- MERCHANT.KEY.png -- eg. teespring.png
+ logo of merchant -- MERCHANT.KEY-logo.png -- eg. teespring-logo.png
+ update merch.json with extra info about merchandise
- merch-MERCHANT.KEY-summary -- eg. merch-teespring-summary -> Teespring is and will always be...
- merch-MERCHANT.KEY-goto -- eg. merch-teespring-goto -> Get myself a Habitica T-Shirts
+ update this page with an extra merchant (var merchants)
- key (used in locale files)
- logo (do we have a logo for this merchant, true || false)
- name (if no logo, this will be shown instead)
- link
eg. -> merchants.push({key: 'teespring', logo: true, name: 'Teespring', link: 'https://teespring.com/habitica-gryphon-t-shirt'})
block vars
- var layoutEnv = env
- var merchants = []
- merchants.push({key: 'teespring', logo: true, name: 'Teespring', link: 'https://teespring.com/habitica-gryphon-t-shirt'})
- merchants.push({key: 'teespring-eu', logo: true, name: 'Teespring (EU)', link: 'https://teespring.com/habitica-gryphon-t-shirt_eu'})
- merchants.push({key: 'stickermule', logo: true, name: 'Stickermule', link: 'https://www.stickermule.com/uk/marketplace/9317-habitica-gryphon-sticker'})
block title
title=env.t('merch')
block content
.row
.col-md-12
.page-header
h3=env.t('merch')
// Merchandise block - START
each merchant, index in merchants
if (index+1) % 2 !== 0
.row
.col-lg-6.col-md-6.col-sm-12
.merch-block
if merchant.logo
img(src="/merch/" + "#{merchant.key}" + "-logo.png")
else
h3=merchant.name
div: a(href="#{merchant.link}" target='_blank'): img.img-rendering-auto(src="/merch/" + "#{merchant.key}" + ".png")
p=env.t('merch-' + merchant.key + '-summary')
a.btn(href="#{merchant.link}" target='_blank')=env.t('merch-' + merchant.key + '-goto')
// Merchandise block - END