Merge branch 'api-v3' into api-v3-groups
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 306 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@@ -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 |
|
After Width: | Height: | Size: 394 KiB |
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
| {{::pot.value}}
|
||||
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}}
|
||||
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')
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -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')}
|
||||
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')}
|
||||
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')}
|
||||
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')}
|
||||
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"}}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,30 +1,75 @@
|
||||
mixin buyGemsDropdown()
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-4.col-md-offset-4.alert.alert-info $5
|
||||
=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
|
||||
span.shop_gold
|
||||
.col-md-8
|
||||
.popover.right.gem-count-popover
|
||||
.arrow
|
||||
.popover-content
|
||||
p=env.t('buyGemsAllow1')
|
||||
| {{Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought}}
|
||||
=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
|
||||
=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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||