mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-07-14 18:22:21 +00:00
Merge branch 'develop' of git://github.com/HabitRPG/habitrpg into develop
This commit is contained in:
commit
d7351932aa
22 changed files with 235 additions and 78 deletions
|
|
@ -2,7 +2,7 @@ var _id = '';
|
|||
var update = {
|
||||
$push: {
|
||||
'purchased.plan.mysteryItems':{
|
||||
$each:['head_mystery_201406','armor_mystery_201406']
|
||||
$each:['head_mystery_201407','armor_mystery_201407']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
"karma-ng-html2js-preprocessor": "~0.1.0",
|
||||
"karma-chai-plugins": "~0.1.0",
|
||||
"mocha": "~1.12.1",
|
||||
"karma-mocha": "~0.1.0",
|
||||
"karma-mocha": "0.1.3",
|
||||
"csv": "~0.3.6",
|
||||
"mongoskin": "~0.6.1",
|
||||
"expect.js": "~0.2.0",
|
||||
|
|
|
|||
|
|
@ -228,7 +228,12 @@ window.habitrpg = angular.module('habitrpg',
|
|||
} else if (response.needRefresh) {
|
||||
$rootScope.$broadcast('responseError', "The site has been updated and the page needs to refresh. The last action has not been recorded, please refresh and try again.");
|
||||
|
||||
// 400 range?
|
||||
} else if (response.data.code && response.data.code === 'ACCOUNT_SUSPENDED') {
|
||||
confirm(response.data.err);
|
||||
localStorage.clear();
|
||||
window.location.href = '/logout';
|
||||
|
||||
// 400 range?
|
||||
} else if (response < 500) {
|
||||
$rootScope.$broadcast('responseText', response.data.err || response.data);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
|||
$rootScope.env = window.env;
|
||||
$rootScope.Math = Math;
|
||||
$rootScope.Groups = Groups;
|
||||
$rootScope.toJson = angular.toJson;
|
||||
|
||||
// Angular UI Router
|
||||
$rootScope.$state = $state;
|
||||
|
|
@ -82,6 +83,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
|||
scope: options.scope, // optional
|
||||
keyboard: (options.keyboard === undefined ? true : options.keyboard), // optional
|
||||
backdrop: (options.backdrop === undefined ? true : options.backdrop) // optional
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +123,26 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
|||
});
|
||||
}
|
||||
|
||||
$rootScope.showStripeEdit = function(){
|
||||
StripeCheckout.open({
|
||||
key: window.env.STRIPE_PUB_KEY,
|
||||
address: false,
|
||||
name: 'Update',
|
||||
description: 'Update the card to be charged for your subscription',
|
||||
panelLabel: 'Update Card',
|
||||
token: function(data) {
|
||||
var url = '/stripe/subscribe/edit?plan=basic_earned';
|
||||
$scope.$apply(function(){
|
||||
$http.post(url, data).success(function() {
|
||||
window.location.reload(true);
|
||||
}).error(function(err) {
|
||||
alert(err);
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$rootScope.cancelSubscription = function(){
|
||||
if (!confirm(window.env.t('sureCancelSub'))) return;
|
||||
window.location.href = '/' + user.purchased.plan.paymentMethod.toLowerCase() + '/subscribe/cancel?_id=' + user._id + '&apiToken=' + user.apiToken;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,20 @@ habitrpg.controller('SettingsCtrl',
|
|||
$rootScope.$state.go('tasks');
|
||||
}
|
||||
|
||||
$scope.changeUsername = function(changeUser){
|
||||
if (!changeUser.newUsername || !changeUser.password) {
|
||||
return alert(window.env.t('fillAll'));
|
||||
}
|
||||
$http.post(API_URL + '/api/v2/user/change-username', changeUser)
|
||||
.success(function(){
|
||||
alert(window.env.t('usernameSuccess'));
|
||||
$scope.changeUser = {};
|
||||
})
|
||||
.error(function(data){
|
||||
alert(data);
|
||||
});
|
||||
}
|
||||
|
||||
$scope.changePassword = function(changePass){
|
||||
if (!changePass.oldPassword || !changePass.newPassword || !changePass.confirmNewPassword) {
|
||||
return alert(window.env.t('fillAll'));
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ var api = module.exports;
|
|||
var NO_TOKEN_OR_UID = { err: "You must include a token and uid (user id) in your request"};
|
||||
var NO_USER_FOUND = {err: "No user found."};
|
||||
var NO_SESSION_FOUND = { err: "You must be logged in." };
|
||||
var accountSuspended = function(uuid){
|
||||
return {
|
||||
err: 'Account has been suspended, please contact leslie@habitrpg.com with your UUID ('+uuid+') for assistance.',
|
||||
code: 'ACCOUNT_SUSPENDED'
|
||||
};
|
||||
}
|
||||
|
||||
api.auth = function(req, res, next) {
|
||||
var uid = req.headers['x-api-user'];
|
||||
|
|
@ -22,6 +28,7 @@ api.auth = function(req, res, next) {
|
|||
User.findOne({_id: uid,apiToken: token}, function(err, user) {
|
||||
if (err) return next(err);
|
||||
if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND);
|
||||
if (user.auth.blocked) return res.json(401, accountSuspended(user._id));
|
||||
|
||||
res.locals.wasModified = req.query._v ? +user._v !== +req.query._v : true;
|
||||
res.locals.user = user;
|
||||
|
|
@ -121,9 +128,10 @@ api.loginLocal = function(req, res, next) {
|
|||
var username = req.body.username;
|
||||
var password = req.body.password;
|
||||
if (!(username && password)) return res.json(401, {err:'Missing :username or :password in request body, please provide both'});
|
||||
User.findOne({'auth.local.username': username}, function(err, user){
|
||||
User.findOne({'auth.local.username': username}, {auth:1}, function(err, user){
|
||||
if (err) return next(err);
|
||||
if (!user) return res.json(401, {err:"Username or password incorrect. Click 'Forgot Password' for help with either. (Note: usernames are case-sensitive)"});
|
||||
if (user.auth.blocked) return res.json(401, accountSuspended(user._id));
|
||||
// We needed the whole user object first so we can get his salt to encrypt password comparison
|
||||
User.findOne({
|
||||
'auth.local.username': username,
|
||||
|
|
@ -144,30 +152,17 @@ api.loginLocal = function(req, res, next) {
|
|||
api.loginFacebook = function(req, res, next) {
|
||||
var email, facebook_id, name, _ref;
|
||||
_ref = req.body, facebook_id = _ref.facebook_id, email = _ref.email, name = _ref.name;
|
||||
if (!facebook_id) {
|
||||
return res.json(401, {
|
||||
err: 'No facebook id provided'
|
||||
});
|
||||
}
|
||||
return User.findOne({
|
||||
'auth.facebook.id': facebook_id
|
||||
}, function(err, user) {
|
||||
if (!facebook_id)
|
||||
return res.json(401, {err: 'No facebook id provided'});
|
||||
User.findOne({'auth.facebook.id': facebook_id}, function(err, user) {
|
||||
if (err) {
|
||||
return res.json(401, {
|
||||
err: err
|
||||
});
|
||||
}
|
||||
if (user) {
|
||||
return res.json(200, {
|
||||
id: user.id,
|
||||
token: user.apiToken
|
||||
});
|
||||
return res.json(401, {err: err});
|
||||
} else if (user) {
|
||||
if (user.auth.blocked) return res.json(401, accountSuspended(user._id));
|
||||
return res.json(200, {id: user.id,token: user.apiToken});
|
||||
} else {
|
||||
/* FIXME: create a new user instead*/
|
||||
|
||||
return res.json(403, {
|
||||
err: "Please register with Facebook on https://habitrpg.com, then come back here and log in."
|
||||
});
|
||||
return res.json(403, {err: "Please register with Facebook on https://habitrpg.com, then come back here and log in."});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -195,6 +190,31 @@ api.resetPassword = function(req, res, next){
|
|||
});
|
||||
};
|
||||
|
||||
api.changeUsername = function(req, res, next) {
|
||||
var user = res.locals.user,
|
||||
password = req.body.password
|
||||
newUsername = req.body.newUsername;
|
||||
|
||||
User.findOne({'auth.local.username': newUsername}, function(err, result) {
|
||||
if (err) next(err);
|
||||
|
||||
if(result)
|
||||
return res.json(401, {err: "Username already taken"});
|
||||
|
||||
var salt = user.auth.local.salt,
|
||||
hashed_password = utils.encryptPassword(password, salt);
|
||||
|
||||
if (hashed_password !== user.auth.local.hashed_password)
|
||||
return res.json(401, {err:"Incorrect password"});
|
||||
|
||||
user.auth.local.username = newUsername;
|
||||
user.save(function(err, saved){
|
||||
if (err) next(err);
|
||||
res.send(200);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
api.changePassword = function(req, res, next) {
|
||||
var user = res.locals.user,
|
||||
oldPassword = req.body.oldPassword,
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ api.generateCoupons = function(req,res,next) {
|
|||
api.getCoupons = function(req,res,next) {
|
||||
var options = {sort:'seq'};
|
||||
if (req.query.limit) options.limit = req.query.limit;
|
||||
if (req.query.skip) options.limit = req.query.skip;
|
||||
if (req.query.skip) options.skip = req.query.skip;
|
||||
Coupon.find({},{}, options, function(err,coupons){
|
||||
//res.header('Content-disposition', 'attachment; filename=coupons.csv');
|
||||
res.csv([['code']].concat(_.map(coupons, function(c){
|
||||
return ['HabitRPG - To redeem your code, go to goo.gl/azsGaH and enter '+c._id];
|
||||
return [c._id];
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ api.getPatrons = function(req,res,next){
|
|||
|
||||
api.getHero = function(req,res,next) {
|
||||
User.findById(req.params.uid)
|
||||
.select('contributor balance profile.name purchased')
|
||||
.select('contributor balance profile.name purchased items')
|
||||
.select('auth.local.username auth.local.email auth.facebook auth.blocked')
|
||||
.exec(function(err, user){
|
||||
if (err) return next(err)
|
||||
if (!user) return res.json(400,{err:'User not found'});
|
||||
|
|
@ -61,6 +62,12 @@ api.updateHero = function(req,res,next) {
|
|||
member.contributor = req.body.contributor;
|
||||
member.purchased.ads = req.body.purchased.ads;
|
||||
if (member.contributor.level >= 6) member.items.pets['Dragon-Hydra'] = 5;
|
||||
if (req.body.itemPath && req.body.itemVal
|
||||
&& req.body.itemPath.indexOf('items.')===0
|
||||
&& User.schema.paths[req.body.itemPath]) {
|
||||
shared.dotSet(member, req.body.itemPath, req.body.itemVal); // Sanitization at 5c30944 (deemed unnecessary)
|
||||
}
|
||||
if (_.isBoolean(req.body.auth.blocked)) member.auth.blocked = req.body.auth.blocked;
|
||||
member.save(cb);
|
||||
}
|
||||
], function(err, saved){
|
||||
|
|
|
|||
|
|
@ -142,6 +142,32 @@ api.stripeSubscribeCancel = function(req, res, next) {
|
|||
});
|
||||
}
|
||||
|
||||
api.stripeSubscribeEdit = function(req, res, next) {
|
||||
var stripe = require("stripe")(nconf.get('STRIPE_API_KEY'));
|
||||
var token = req.body.id;
|
||||
var user = res.locals.user;
|
||||
var user_id = user.purchased.plan.customerId;
|
||||
var sub_id;
|
||||
|
||||
async.waterfall([
|
||||
function(cb){
|
||||
stripe.customers.listSubscriptions(user_id, cb);
|
||||
},
|
||||
function(response, cb) {
|
||||
sub_id = response.data[0].id;
|
||||
console.warn(sub_id);
|
||||
console.warn([user_id, sub_id, { card: token }]);
|
||||
stripe.customers.updateSubscription(user_id, sub_id, { card: token }, cb);
|
||||
},
|
||||
function(response, cb) {
|
||||
user.save(cb);
|
||||
}
|
||||
], function(err, saved){
|
||||
if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors
|
||||
res.send(200);
|
||||
});
|
||||
}
|
||||
|
||||
api.paypalSubscribe = function(req,res,next) {
|
||||
var uuid = res.locals.user._id;
|
||||
// Authenticate a future subscription of ~5 USD
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ api.getContent = function(req, res, next) {
|
|||
res.json(content);
|
||||
}
|
||||
|
||||
api.getModelPaths = function(req,res,next){
|
||||
res.json(_.reduce(User.schema.paths,function(m,v,k){
|
||||
m[k] = v.instance || 'Boolean';
|
||||
return m;
|
||||
},{}));
|
||||
}
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------
|
||||
Tasks
|
||||
|
|
|
|||
|
|
@ -9,17 +9,19 @@ if (logger == null) {
|
|||
logger = new (winston.Logger)({});
|
||||
if (nconf.get('NODE_ENV') == 'production') {
|
||||
logger.add(winston.transports.newrelic, {});
|
||||
logger.add(winston.transports.Mail, {
|
||||
to: nconf.get('ADMIN_EMAIL') || nconf.get('SMTP_USER'),
|
||||
from: "HabitRPG <" + nconf.get('SMTP_USER') + ">",
|
||||
subject: "HabitRPG Error",
|
||||
host: nconf.get('SMTP_HOST'),
|
||||
port: nconf.get('SMTP_PORT'),
|
||||
tls: nconf.get('SMTP_TLS'),
|
||||
username: nconf.get('SMTP_USER'),
|
||||
password: nconf.get('SMTP_PASS'),
|
||||
level: 'error'
|
||||
});
|
||||
if (!nconf.get('DISABLE_ERROR_EMAILS')) {
|
||||
logger.add(winston.transports.Mail, {
|
||||
to: nconf.get('ADMIN_EMAIL') || nconf.get('SMTP_USER'),
|
||||
from: "HabitRPG <" + nconf.get('SMTP_USER') + ">",
|
||||
subject: "HabitRPG Error",
|
||||
host: nconf.get('SMTP_HOST'),
|
||||
port: nconf.get('SMTP_PORT'),
|
||||
tls: nconf.get('SMTP_TLS'),
|
||||
username: nconf.get('SMTP_USER'),
|
||||
password: nconf.get('SMTP_PASS'),
|
||||
level: 'error'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
logger.add(winston.transports.Console, {colorize:true});
|
||||
logger.add(winston.transports.File, {filename: 'habitrpg.log'});
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ var UserSchema = new Schema({
|
|||
valentine: Number
|
||||
},
|
||||
auth: {
|
||||
blocked: Boolean,
|
||||
facebook: Schema.Types.Mixed,
|
||||
local: {
|
||||
email: String,
|
||||
|
|
@ -405,6 +406,7 @@ UserSchema.pre('save', function(next) {
|
|||
|
||||
//our own version incrementer
|
||||
this._v++;
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ module.exports = (swagger, v2) ->
|
|||
]
|
||||
action: user.getContent
|
||||
|
||||
'/content/paths':
|
||||
spec:
|
||||
description: "Show user model tree"
|
||||
action: user.getModelPaths
|
||||
|
||||
"/export/history":
|
||||
spec:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ router.post('/api/v2/user/auth/local', i18n.getUserLanguage, auth.loginLocal);
|
|||
router.post('/api/v2/user/auth/facebook', i18n.getUserLanguage, auth.loginFacebook);
|
||||
router.post('/api/v2/user/reset-password', i18n.getUserLanguage, auth.resetPassword);
|
||||
router.post('/api/v2/user/change-password', i18n.getUserLanguage, auth.auth, auth.changePassword);
|
||||
router.post('/api/v2/user/change-username', i18n.getUserLanguage, auth.auth, auth.changeUsername);
|
||||
|
||||
router.post('/api/v1/register', i18n.getUserLanguage, auth.registerUser);
|
||||
router.post('/api/v1/user/auth/local', i18n.getUserLanguage, auth.loginLocal);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ router.get('/paypal/subscribe/cancel', auth.authWithUrl, i18n.getUserLanguage, p
|
|||
router.post('/paypal/ipn', i18n.getUserLanguage, payments.paypalIPN); // misc ipn handling
|
||||
|
||||
router.post("/stripe/checkout", auth.auth, i18n.getUserLanguage, payments.stripeCheckout);
|
||||
router.post("/stripe/subscribe/edit", auth.auth, i18n.getUserLanguage, payments.stripeSubscribeEdit)
|
||||
//router.get("/stripe/subscribe", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribe); // checkout route is used (above) with ?plan= instead
|
||||
router.get("/stripe/subscribe/cancel", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribeCancel);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,18 @@ script(type='text/ng-template', id='partials/options.settings.settings.html')
|
|||
input.form-control(type='text', ng-model='_couponCode', placeholder='Enter Coupon Code')
|
||||
button.btn.btn-primary(type='submit') Submit
|
||||
|
||||
div(ng-if='user.auth.local.username')
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
span=env.t('changeUsername')
|
||||
.panel-body
|
||||
form(ng-submit='changeUsername(changeUser)', ng-show='user.auth.local')
|
||||
.form-group
|
||||
input.form-control(type='text', placeholder=env.t('newUsername'), ng-model='changeUser.newUsername', required)
|
||||
.form-group
|
||||
input.form-control(type='password', placeholder=env.t('password'), ng-model='changeUser.password', required)
|
||||
input.btn.btn-default(type='submit', value=env.t('submit'))
|
||||
|
||||
//- Why is ng-if='user.auth.local' validating for users *without* user.auth.local (facebook users)? adding .username here for extra
|
||||
div(ng-if='user.auth.local.username')
|
||||
.panel.panel-default
|
||||
|
|
@ -207,4 +219,5 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template')
|
|||
|
|
||||
span.glyphicon.glyphicon-ok
|
||||
div(ng-include="'partials/options.settings.subscription.perks.html'")
|
||||
.btn.btn-sm.btn-danger(ng-if='!user.purchased.plan.dateTerminated', ng-click='cancelSubscription()')=env.t('cancelSub')
|
||||
.btn.btn-primary(ng-if=':: !user.purchased.plan.dateTerminated && user.purchased.plan.paymentMethod=="Stripe"', ng-click='showStripeEdit()') Update Card
|
||||
.btn.btn-sm.btn-danger(ng-if=':: !user.purchased.plan.dateTerminated', ng-click='cancelSubscription()')=env.t('cancelSub')
|
||||
|
|
@ -3,7 +3,7 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title=env.t('guild
|
|||
span.task-action-btn.tile.flush.neutral
|
||||
.Pet_Currency_Gem2x.Gems
|
||||
| {{group.balance * 4 | number:0 }}
|
||||
=env.t('guildGems')
|
||||
=' ' + env.t('guildGems')
|
||||
|
||||
.container-fluid
|
||||
.row
|
||||
|
|
|
|||
|
|
@ -37,24 +37,41 @@ script(type='text/ng-template', id='partials/options.social.hall.heroes.html')
|
|||
label
|
||||
input(type='checkbox', ng-model='hero.contributor.admin')
|
||||
=env.t('admin')
|
||||
hr
|
||||
|
||||
hr
|
||||
.form-group
|
||||
label=env.t('balance')
|
||||
input.form-control(type='number', step="any", ng-model='hero.balance')
|
||||
small!= '`user.balance`' + env.t('notGems')
|
||||
.form-group
|
||||
.checkbox
|
||||
label
|
||||
input(type='checkbox', ng-model='hero.purchased.ads')
|
||||
=env.t('hideAds')
|
||||
accordion
|
||||
accordion-group(heading='Items')
|
||||
h4 Update Item
|
||||
.form-group.well
|
||||
input.form-control(type='text',placeholder='Path (eg, items.pets.BearCub-Base)',ng-model='hero.itemPath')
|
||||
small.muted Enter the <strong>item path</strong>. E.g., <code>items.pets.BearCub-Zombie</code> or <code>items.gear.owned.head_special_0</code> or <code>items.gear.equipped.head</code>. <a href='/api/v2/content/paths' target='_blank'>See all paths here.</a> When in doubt, ask Tyler.
|
||||
br
|
||||
input.form-control(type='text',placeholder='Value (eg, 5)',ng-model='hero.itemVal')
|
||||
small.muted Enter the <strong>item value</strong>. E.g., <code>5</code> or <code>false</code> or <code>head_warrior_3</code> (respectively from above examples).
|
||||
h4 Current Items
|
||||
pre {{::toJson(hero.items,true)}}
|
||||
accordion-group(heading='Auth')
|
||||
h4 Auth
|
||||
pre {{::toJson(hero.auth)}}
|
||||
.form-group
|
||||
.checkbox
|
||||
label
|
||||
input(type='checkbox', ng-model='hero.auth.blocked')
|
||||
| Blocked
|
||||
|
||||
.form-group
|
||||
label=env.t('balance')
|
||||
input.form-control(type='number', step="any", ng-model='hero.balance')
|
||||
small!= '`user.balance`' + env.t('notGems')
|
||||
|
||||
.form-group
|
||||
.checkbox
|
||||
label
|
||||
input(type='checkbox', ng-model='hero.purchased.ads')
|
||||
=env.t('hideAds')
|
||||
|
||||
// h4 Backer Status
|
||||
// Add backer stuff like tier, disable adds, etcs
|
||||
.form-group
|
||||
input.form-control.btn.btn-primary(type='submit')=env.t('save')
|
||||
// h4 Backer Status
|
||||
// Add backer stuff like tier, disable adds, etcs
|
||||
.form-group
|
||||
input.form-control.btn.btn-primary(type='submit')=env.t('save')
|
||||
|
||||
|
||||
table.table.table-striped
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ script(type='text/ng-template', id='modals/reset.html')
|
|||
button.btn.btn-default(ng-click='$close();')=env.t('neverMind')
|
||||
button.btn.btn-danger(ng-click='$close(); reset()')=env.t('resetDo')
|
||||
|
||||
script(type='text/ng-template', id='modals/restore.html')
|
||||
script(type='text/ng-template', id='modals/restore.html')
|
||||
.modal-header
|
||||
h4=env.t('fixValues')
|
||||
.modal-body
|
||||
|
|
|
|||
|
|
@ -11,21 +11,29 @@ table
|
|||
table.table.table-striped
|
||||
tr
|
||||
td
|
||||
h5 Mobile App Update
|
||||
p We’ve released another update to the mobile app! Now you can feed and select pets from the app. Carry your cute pets with you everywhere you go! The app is available for <a href='https://itunes.apple.com/us/app/habitrpg/id689569235?mt=8' target='_blank'>iOS here</a>, and <a href='https://play.google.com/store/apps/details?id=com.ocdevel.habitrpg' target='_blank'>Android here</a>. We’re continuing to release updates on a regular basis, so if you like the direction that we’ve been taking the app, please do consider leaving us a review. Thank you!
|
||||
tr
|
||||
td
|
||||
h5 Neglect Strike: Tavern Art Swap
|
||||
p The Dread Drag'on's Rage Bar has filled, and it has unleashed its Neglect Strike, leading to a new look for the Tavern! As a reminder, the Drag'on's rage will NEVER hurt any users or interfere with their ability to be productive, so the chat and inn are still functional. Even so... poor Daniel!
|
||||
p All users are automatically damaging the Drag'on with their tasks. There is nothing bad that can happen to you or your account by being in this fight!
|
||||
tr
|
||||
td
|
||||
h5 Dread Drag'on Prize Change: Food Reward!
|
||||
p We've received a lot of feedback due to the weekend's confusion, and it seems that awarding GP and XP for defeating the world boss significantly unbalanced the game for newer players. Based on your feedback, XP and GP will no longer be awarded. Instead, players will receive an assortment of food! The Mantis Shrimps will still be awarded.
|
||||
p If you were looking forward to receiving the 900XP and 90 GP upon completion of the battle, feel free to award it to yourself using Settings > Site > Fix Character Values when the battle is done!
|
||||
p Thank you for bearing with us through the confusion. We love you guys.
|
||||
small.muted 7/16/2014
|
||||
h5 July Subscriber Item
|
||||
.promo_mystery_201407.pull-right
|
||||
p The July Subscriber Item has been revealed: the Undersea Explorer Item Set! All July subscribers will receive the Undersea Explorer Helm and the Undersea Explorer Suit. You still have six days to subscribe and receive the item set! Thank you so much for your support - we really do rely on you to keep HabitRPG free to use and running smoothly.
|
||||
small.muted 7/25/2014
|
||||
|
||||
h5 7/16/2014
|
||||
table.table.table-striped
|
||||
tr
|
||||
td
|
||||
h5 Mobile App Update
|
||||
p We’ve released another update to the mobile app! Now you can feed and select pets from the app. Carry your cute pets with you everywhere you go! The app is available for <a href='https://itunes.apple.com/us/app/habitrpg/id689569235?mt=8' target='_blank'>iOS here</a>, and <a href='https://play.google.com/store/apps/details?id=com.ocdevel.habitrpg' target='_blank'>Android here</a>. We’re continuing to release updates on a regular basis, so if you like the direction that we’ve been taking the app, please do consider leaving us a review. Thank you!
|
||||
tr
|
||||
td
|
||||
h5 Neglect Strike: Tavern Art Swap
|
||||
p The Dread Drag'on's Rage Bar has filled, and it has unleashed its Neglect Strike, leading to a new look for the Tavern! As a reminder, the Drag'on's rage will NEVER hurt any users or interfere with their ability to be productive, so the chat and inn are still functional. Even so... poor Daniel!
|
||||
p All users are automatically damaging the Drag'on with their tasks. There is nothing bad that can happen to you or your account by being in this fight!
|
||||
tr
|
||||
td
|
||||
h5 Dread Drag'on Prize Change: Food Reward!
|
||||
p We've received a lot of feedback due to the weekend's confusion, and it seems that awarding GP and XP for defeating the world boss significantly unbalanced the game for newer players. Based on your feedback, XP and GP will no longer be awarded. Instead, players will receive an assortment of food! The Mantis Shrimps will still be awarded.
|
||||
p If you were looking forward to receiving the 900XP and 90 GP upon completion of the battle, feel free to award it to yourself using Settings > Site > Fix Character Values when the battle is done!
|
||||
p Thank you for bearing with us through the confusion. We love you guys.
|
||||
hr
|
||||
h5 7/12/2014
|
||||
table.table.table-striped
|
||||
tr
|
||||
|
|
@ -116,7 +124,7 @@ table.table.table-striped
|
|||
tr
|
||||
td
|
||||
h5 June Subscriber Item
|
||||
img.pull-right(src='/bower_components/habitrpg-shared/img/sprites/spritesmith/gear/events/mystery_201406/promo_mystery_201406.png')
|
||||
.pull-right.promo_mystery_201406.png
|
||||
p The June Subscriber Item has been revealed: the Octomage Item Set! All June subscribers will receive the Octopus Robe and the Crown of Tentacles. You still have six days to subscribe and receive the item set! Thank you so much for your support - we really do rely on you to keep HabitRPG free to use and running smoothly.
|
||||
h5 Mobile App Update
|
||||
p There's a new mobile app update available! In addition to bug fixes, there are many improvements, including a new button-based menu, tap-and-hold to edit tasks, and the return of stats and in-app avatar customization! Working on the mobile app is our biggest To-Do this summer, so expect more in the coming months. If you feel that the app is improving, we'd love it if you would take the time to give us a review and let us know what you think!
|
||||
|
|
@ -185,7 +193,7 @@ table.table.table-striped
|
|||
tr
|
||||
td
|
||||
h5 May Mystery Outfit Revealed!
|
||||
img.pull-right(src='/bower_components/habitrpg-shared/img/sprites/spritesmith/gear/events/mystery_201405/promo_mystery_201405.png')
|
||||
.pull-right.promo_mystery_201405.png
|
||||
p The May Mystery Item Set has been revealed for all subscribers... <strong>Flame Wielder Item Set</strong>! All people who are subscribed this May will receive two items:
|
||||
ul
|
||||
li Flame of Mind (helm)
|
||||
|
|
@ -262,7 +270,7 @@ table.table.table-striped
|
|||
tr
|
||||
td
|
||||
h5 April Mystery Outfit Revealed!
|
||||
img.pull-right(src='/marketing/promos/April14SAMPLE2.png')
|
||||
//-img.pull-right(src='/marketing/promos/April14SAMPLE2.png')
|
||||
p The April Mystery Item Set has been revealed for all subscribers... <strong>Twilight Butterfly Armor Set</strong>! All people who are subscribed this April will receive two items:
|
||||
ul
|
||||
li Twilight Butterfly Antennae
|
||||
|
|
@ -317,7 +325,7 @@ table.table.table-striped
|
|||
tr
|
||||
td
|
||||
h5 March Mystery Item Set
|
||||
img.pull-right(src='/marketing/promos/201403_Forest_Walker.png')
|
||||
//-img.pull-right(src='/marketing/promos/201403_Forest_Walker.png')
|
||||
p The March Mystery Item Set has been revealed for all subscribers... The Forest Walker Set! All people who are subscribed this March will receive two items: <strong>Forest Walker Armor</strong> and <strong>Forest Walker Antlers</strong>!
|
||||
br
|
||||
p The antlers are a head accessory, so they can be worn with any helmet.
|
||||
|
|
|
|||
|
|
@ -154,3 +154,11 @@ div(ng-if='::profile.achievements.valentine')
|
|||
small
|
||||
=env.t('adoringFriendsText', {cards: "{{::profile.achievements.valentine}}"})
|
||||
hr
|
||||
|
||||
div(ng-if='::profile.achievements.quests.dilatory')
|
||||
.achievement.achievement-dilatory
|
||||
h5=env.t('achievementDilatory')
|
||||
small
|
||||
=env.t('achievementDilatoryText')
|
||||
hr
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ html
|
|||
h2 Extensions / Scripts
|
||||
p HabitRPG has a simple API for up-scoring and down-scoring third party Habits: <code>POST /api/v2/user/tasks/{id}/{direction}</code> (headers <code>x-api-user</code> and <code>x-api-key</code> required).
|
||||
h4 Example
|
||||
p <code>curl -X POST -H "x-api-key: YOUR_API_TOKEN" -H "x-api-user: YOUR_USER_ID" https://beta.habitrpg.com/api/v2/user/tasks/productivity/up</code>
|
||||
p <code>curl -X POST -H "x-api-key: YOUR_API_TOKEN" -H "x-api-user: YOUR_USER_ID" https://habitrpg.com/api/v2/user/tasks/productivity/up</code>
|
||||
p Note: You may need to add <code>--compressed -H "Content-Type:application/json"</code> to your curl if you get errors.
|
||||
ul
|
||||
li POST to the URL <code>/api/v2/user/tasks/{id}/{direction}</code>
|
||||
|
|
@ -91,7 +91,7 @@ html
|
|||
h2 Full API
|
||||
p All API requests should be prefaced by <code>https://habitrpg.com</code>. Every authenticated request should include two headers. Your api key (<code>x-api-key</code>) and your user id (<code>x-api-user</code>). Do not include <code>{}</code> braces in your header (<code>-H 'x-api-user: a94b6d9d-6b64-43ae-856c-2c3f211bd426'</code>)
|
||||
h2 Requirements:
|
||||
p The base-url for all routes is /api/v2. So /user actions will be at https://beta.habitrpg.com/api/v2/user/*. You need to send x-api-user and x-api-key headers for each request.
|
||||
p The base-url for all routes is /api/v2. So /user actions will be at https://habitrpg.com/api/v2/*. You need to send x-api-user and x-api-key headers for each request.
|
||||
p For create & edit paths (PUT & POST), you'll need to know the schema of the object you're trying to create or edit. See Schema <a href='https://github.com/HabitRPG/habitrpg/tree/develop/src/models' target="_blank">definitions here</a>
|
||||
p If any of the documentation is lacking or you're having trouble with it, please post an issue to <a href='https://github.com/HabitRPG/habitrpg/issues' target='_blank'>Github</a>
|
||||
#message-bar.swagger-ui-wrap
|
||||
|
|
|
|||
Loading…
Reference in a new issue