mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-07-22 03:34:14 +00:00
refactor(bs3): sync with develop & misc
This commit is contained in:
commit
cc348b2f11
37 changed files with 508 additions and 355 deletions
7
.bowerrc
7
.bowerrc
|
|
@ -1,3 +1,8 @@
|
|||
{
|
||||
"directory": "public/bower_components"
|
||||
"directory": "public/bower_components",
|
||||
"storage": {
|
||||
"packages": ".bower-cache",
|
||||
"registry": ".bower-registry"
|
||||
},
|
||||
"tmp": ".bower-tmp"
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -8,6 +8,7 @@ npm-debug.log
|
|||
lib
|
||||
public/bower_components
|
||||
build
|
||||
newrelic_agent.log
|
||||
|
||||
src/*/*.map
|
||||
src/*/*/*.map
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ Gruntfile.js
|
|||
CHANGELOG.md
|
||||
.idea*
|
||||
.git*
|
||||
newrelic_agent.log
|
||||
|
|
|
|||
|
|
@ -13,5 +13,6 @@
|
|||
"SMTP_SERVICE":"Gmail",
|
||||
"STRIPE_API_KEY":"aaaabbbbccccddddeeeeffff00001111",
|
||||
"STRIPE_PUB_KEY":"22223333444455556666777788889999",
|
||||
"PAYPAL_MERCHANT":"paypal-merchant@gmail.com"
|
||||
"PAYPAL_MERCHANT":"paypal-merchant@gmail.com",
|
||||
"NEW_RELIC_LICENSE_KEY":"NEW_RELIC_LICENSE_KEY"
|
||||
}
|
||||
|
|
|
|||
1
migrations/20140130_birthdayEnd.js
Normal file
1
migrations/20140130_birthdayEnd.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
db.users.update({},{$set:{'achievements.habitBirthday':true}},{multi:1})
|
||||
12
migrations/20140130_birthdayStart.js
Normal file
12
migrations/20140130_birthdayStart.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
db.users.update({},{$set:{
|
||||
'items.food.Cake_Skeleton':1,
|
||||
'items.food.Cake_Base':1,
|
||||
'items.food.Cake_CottonCandyBlue':1,
|
||||
'items.food.Cake_CottonCandyPink':1,
|
||||
'items.food.Cake_Shade':1,
|
||||
'items.food.Cake_White':1,
|
||||
'items.food.Cake_Golden':1,
|
||||
'items.food.Cake_Zombie':1,
|
||||
'items.food.Cake_Desert':1,
|
||||
'items.food.Cake_Red':1
|
||||
}},{multi:1})
|
||||
25
newrelic.js
Normal file
25
newrelic.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* New Relic agent configuration.
|
||||
*
|
||||
* See lib/config.defaults.js in the agent distribution for a more complete
|
||||
* description of configuration variables and their potential values.
|
||||
*/
|
||||
var nconf = require('nconf')
|
||||
exports.config = {
|
||||
/**
|
||||
* Array of application names.
|
||||
*/
|
||||
app_name : ['HabitRPG'],
|
||||
/**
|
||||
* Your New Relic license key.
|
||||
*/
|
||||
license_key : nconf.get('NEW_RELIC_LICENSE_KEY'),
|
||||
logging : {
|
||||
/**
|
||||
* Level at which to log. 'trace' is most useful to New Relic when diagnosing
|
||||
* issues with the agent, 'info' and higher will impose the least overhead on
|
||||
* production applications.
|
||||
*/
|
||||
level : 'warning'
|
||||
}
|
||||
};
|
||||
|
|
@ -43,7 +43,9 @@
|
|||
"domain-middleware": "~0.1.0",
|
||||
"swagger-node-express": "git://github.com/lefnire/swagger-node-express#habitrpg",
|
||||
"passport": "~0.1.18",
|
||||
"passport-facebook": "~1.0.2"
|
||||
"passport-facebook": "~1.0.2",
|
||||
"newrelic": "~1.3.0",
|
||||
"connect-ratelimit": "0.0.6"
|
||||
},
|
||||
"private": true,
|
||||
"subdomain": "habitrpg",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ api.auth = function(req, res, next) {
|
|||
var token = req.headers['x-api-key'];
|
||||
if (!(uid && token)) return res.json(401, NO_TOKEN_OR_UID);
|
||||
User.findOne({_id: uid,apiToken: token}, function(err, user) {
|
||||
if (err) return res.json(500, {err: err});
|
||||
if (err) return next(err);
|
||||
if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND);
|
||||
|
||||
res.locals.wasModified = req.query._v ? +user._v !== +req.query._v : true;
|
||||
|
|
|
|||
|
|
@ -147,16 +147,15 @@ api.create = function(req, res, next) {
|
|||
group.balance = 1;
|
||||
user.balance--;
|
||||
|
||||
user.save(function(err){
|
||||
if(err) return res.json(500,{err:err});
|
||||
group.save(function(err, saved){
|
||||
if (err) return res.json(500,{err:err});
|
||||
saved.populate('members', nameFields, function(err, populated){
|
||||
if (err) return res.json(500,{err:err});
|
||||
return res.json(populated);
|
||||
});
|
||||
});
|
||||
});
|
||||
async.waterfall([
|
||||
function(cb){user.save(cb)},
|
||||
function(saved,ct,cb){group.save(cb)},
|
||||
function(saved,ct,cb){saved.populate('members',nameFields,cb)}
|
||||
],function(err,saved){
|
||||
if (err) return next(err);
|
||||
res.json(saved);
|
||||
});
|
||||
|
||||
}else{
|
||||
async.waterfall([
|
||||
function(cb){
|
||||
|
|
@ -250,7 +249,7 @@ api.deleteChatMessage = function(req, res){
|
|||
});
|
||||
}
|
||||
|
||||
api.likeChatMessage = function(req, res) {
|
||||
api.likeChatMessage = function(req, res, next) {
|
||||
var user = res.locals.user;
|
||||
var group = res.locals.group;
|
||||
var message = _.find(group.chat, {id: req.params.mid});
|
||||
|
|
@ -264,6 +263,7 @@ api.likeChatMessage = function(req, res) {
|
|||
}
|
||||
group.markModified('chat');
|
||||
group.save(function(err,_saved){
|
||||
if (err) return next(err);
|
||||
return res.send(_saved.chat);
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ api.getHeroes = function(req,res,next) {
|
|||
api.getPatrons = function(req,res,next){
|
||||
var page = req.query.page || 0,
|
||||
perPage = 50;
|
||||
User.find({'backer.tier':{$ne:null}})
|
||||
User.find({'backer.tier':{$gt:0}})
|
||||
.select('contributor backer profile.name')
|
||||
.sort('-backer.tier')
|
||||
.skip(page*perPage)
|
||||
|
|
|
|||
|
|
@ -166,6 +166,9 @@ acceptablePUTPaths = _.reduce(require('./../models/user').schema.paths, function
|
|||
if (found) m[leaf]=true;
|
||||
return m;
|
||||
}, {})
|
||||
_.each('stats.gp'.split(' '), function(removePath){
|
||||
delete acceptablePUTPaths[removePath];
|
||||
})
|
||||
|
||||
/**
|
||||
* Update user
|
||||
|
|
@ -186,8 +189,8 @@ api.update = function(req, res, next) {
|
|||
return true;
|
||||
});
|
||||
user.save(function(err) {
|
||||
if (!_.isEmpty(errors)) return res.json(500, {err: errors});
|
||||
if (err) {return res.json(500, {err: err})}
|
||||
if (!_.isEmpty(errors)) return res.json(401, {err: errors});
|
||||
if (err) return res.json(500, {err: err});
|
||||
res.json(200, user);
|
||||
});
|
||||
};
|
||||
|
|
@ -257,6 +260,8 @@ api.addTenGems = function(req, res) {
|
|||
})
|
||||
}
|
||||
|
||||
// TODO delete plan
|
||||
|
||||
/*
|
||||
Setup Stripe response when posting payment
|
||||
*/
|
||||
|
|
@ -264,20 +269,39 @@ api.buyGems = function(req, res) {
|
|||
var api_key = nconf.get('STRIPE_API_KEY');
|
||||
var stripe = require("stripe")(api_key);
|
||||
var token = req.body.id;
|
||||
// console.dir {token:token, req:req}, 'stripe'
|
||||
var user = res.locals.user;
|
||||
|
||||
async.waterfall([
|
||||
function(cb){
|
||||
stripe.charges.create({
|
||||
amount: "500", // $5
|
||||
currency: "usd",
|
||||
card: token
|
||||
}, cb);
|
||||
if (req.query.plan) {
|
||||
stripe.customers.create({
|
||||
email: req.body.email,
|
||||
metadata: {uuid: res.locals.user._id},
|
||||
card: token,
|
||||
plan: req.query.plan,
|
||||
}, cb);
|
||||
} else {
|
||||
stripe.charges.create({
|
||||
amount: "500", // $5
|
||||
currency: "usd",
|
||||
card: token
|
||||
}, cb);
|
||||
}
|
||||
},
|
||||
function(response, cb) {
|
||||
res.locals.user.balance += 5;
|
||||
res.locals.user.purchased.ads = true;
|
||||
res.locals.user.save(cb);
|
||||
//user.purchased.ads = true;
|
||||
if (req.query.plan) {
|
||||
user.purchased.plan = {
|
||||
planId:'basic_earned',
|
||||
customerId: response.id,
|
||||
dateCreated: new Date,
|
||||
dateUpdated: new Date,
|
||||
gemsBought: 0
|
||||
};
|
||||
} else {
|
||||
user.balance += 5;
|
||||
}
|
||||
user.save(cb);
|
||||
}
|
||||
], function(err, saved){
|
||||
if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors
|
||||
|
|
@ -285,6 +309,29 @@ api.buyGems = function(req, res) {
|
|||
});
|
||||
};
|
||||
|
||||
api.cancelSubscription = function(req, res) {
|
||||
var api_key = nconf.get('STRIPE_API_KEY');
|
||||
var stripe = require("stripe")(api_key);
|
||||
var user = res.locals.user;
|
||||
if (!user.purchased.plan.customerId)
|
||||
return res.json(401, {err: "User does not have a plan subscription"});
|
||||
|
||||
async.waterfall([
|
||||
function(cb) {
|
||||
stripe.customers.del(user.purchased.plan.customerId, cb);
|
||||
},
|
||||
function(response, cb) {
|
||||
user.purchased.plan = {};
|
||||
user.markModified('purchased.plan');
|
||||
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, saved);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
api.buyGemsPaypalIPN = function(req, res, next) {
|
||||
res.send(200);
|
||||
ipn.verify(req.body, function callback(err, msg) {
|
||||
|
|
@ -298,7 +345,7 @@ api.buyGemsPaypalIPN = function(req, res, next) {
|
|||
if (_.isEmpty(user)) err = "user not found with uuid " + uuid + " when completing paypal transaction";
|
||||
if (err) return nex(err);
|
||||
user.balance += 5;
|
||||
user.purchased.ads = true;
|
||||
//user.purchased.ads = true;
|
||||
user.save();
|
||||
console.log('PayPal transaction completed and user updated');
|
||||
});
|
||||
|
|
@ -326,6 +373,7 @@ api.cast = function(req, res) {
|
|||
var targetId = req.query.targetId;
|
||||
var klass = shared.content.spells.special[req.params.spell] ? 'special' : user.stats.class
|
||||
var spell = shared.content.spells[klass][req.params.spell];
|
||||
if (!spell) return res.json(404, {err: 'Spell "' + req.params.spell + '" not found.'});
|
||||
|
||||
var done = function(){
|
||||
var err = arguments[0];
|
||||
|
|
@ -336,6 +384,7 @@ api.cast = function(req, res) {
|
|||
|
||||
switch (targetType) {
|
||||
case 'task':
|
||||
if (!user.tasks[targetId]) return res.json(404, {err: 'Task "' + targetId + '" not found.'});
|
||||
spell.cast(user, user.tasks[targetId]);
|
||||
user.save(done);
|
||||
break;
|
||||
|
|
@ -415,7 +464,7 @@ _.each(shared.wrap({}).ops, function(op,k){
|
|||
api.batchUpdate = function(req, res, next) {
|
||||
if (_.isEmpty(req.body)) req.body = []; // cases of {} or null
|
||||
if (req.body[0] && req.body[0].data)
|
||||
return res.json(400, {err: "API has been updated, please refresh your browser or upgrade your mobile app."})
|
||||
return res.json(501, {err: "API has been updated, please refresh your browser or upgrade your mobile app."})
|
||||
|
||||
var user = res.locals.user;
|
||||
var oldSend = res.send;
|
||||
|
|
@ -423,9 +472,8 @@ api.batchUpdate = function(req, res, next) {
|
|||
|
||||
var callOp = function(_req, cb) {
|
||||
res.send = res.json = function(code, data) {
|
||||
if (_.isNumber(code) && code >= 400)
|
||||
console.error({code: code, data: data});
|
||||
//FIXME send error messages down
|
||||
if (_.isNumber(code) && code >= 500)
|
||||
return cb(code+": "+ (data.message ? data.message : data.err ? data.err : JSON.stringify(data)));
|
||||
return cb();
|
||||
};
|
||||
api[_req.op](_req, res);
|
||||
|
|
@ -446,7 +494,7 @@ api.batchUpdate = function(req, res, next) {
|
|||
async.series(ops, function(err) {
|
||||
res.json = oldJson;
|
||||
res.send = oldSend;
|
||||
if (err) return res.json(500, {err: err});
|
||||
if (err) return next(err);
|
||||
|
||||
var response = user.toJSON();
|
||||
response.wasModified = res.locals.wasModified;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,24 @@ var _ = require('lodash');
|
|||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var User = require('./models/user').model
|
||||
var limiter = require('connect-ratelimit');
|
||||
|
||||
module.exports.apiThrottle = function(app) {
|
||||
app.use(limiter({
|
||||
end:false,
|
||||
catagories:{
|
||||
normal: {
|
||||
// 2 req/s, but split as minutes
|
||||
totalRequests: 120,
|
||||
every: 60000
|
||||
}
|
||||
}
|
||||
})).use(function(req,res,next){
|
||||
//console.log(res.ratelimit);
|
||||
if (res.ratelimit.exceeded) return res.json(429,{err:'Rate limit exceeded'});
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.forceSSL = function(req, res, next){
|
||||
var baseUrl = nconf.get("BASE_URL");
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ var UserSchema = new Schema({
|
|||
quests: Schema.Types.Mixed,
|
||||
rebirths: Number,
|
||||
rebirthLevel: Number,
|
||||
perfect: Number
|
||||
perfect: Number,
|
||||
habitBirthday: Boolean
|
||||
},
|
||||
auth: {
|
||||
facebook: Schema.Types.Mixed,
|
||||
|
|
@ -78,6 +79,13 @@ var UserSchema = new Schema({
|
|||
skin: {type: Schema.Types.Mixed, 'default': {}}, // eg, {skeleton: true, pumpkin: true, eb052b: true}
|
||||
hair: {type: Schema.Types.Mixed, 'default': {}},
|
||||
shirt: {type: Schema.Types.Mixed, 'default': {}},
|
||||
plan: {
|
||||
planId: String,
|
||||
customerId: String,
|
||||
dateCreated: Date,
|
||||
dateUpdated: Date,
|
||||
gemsBought: {type: Number, 'default': 0}
|
||||
}
|
||||
},
|
||||
|
||||
flags: {
|
||||
|
|
|
|||
|
|
@ -304,6 +304,11 @@ module.exports = (swagger, v2) ->
|
|||
middleware: auth.auth
|
||||
action:user.buyGems
|
||||
|
||||
"/user/cancel-subscription":
|
||||
spec: method: 'POST', description: "Do not use this route"
|
||||
middleware: auth.auth
|
||||
action:user.cancelSubscription
|
||||
|
||||
"/user/buy-gems/paypal-ipn":
|
||||
spec:
|
||||
method: 'POST'
|
||||
|
|
|
|||
235
src/server.js
235
src/server.js
|
|
@ -1,141 +1,148 @@
|
|||
// Only do the minimal amount of work before forking just in case of a dyno restart
|
||||
var cluster = require("cluster");
|
||||
var _ = require('lodash');
|
||||
var nconf = require('nconf');
|
||||
var utils = require('./utils');
|
||||
utils.setupConfig();
|
||||
|
||||
if (false && cluster.isMaster && (nconf.get('NODE_ENV') == 'development' || nconf.get('NODE_ENV') == 'production')) {
|
||||
var numCPUs = require('os').cpus().length;
|
||||
var isProd = nconf.get('NODE_ENV') === 'production';
|
||||
var isDev = nconf.get('NODE_ENV') === 'development';
|
||||
|
||||
// Fork workers.
|
||||
for (var i = 0; i < numCPUs; i++) {
|
||||
cluster.fork();
|
||||
}
|
||||
if (cluster.isMaster && (isDev || isProd)) {
|
||||
// Fork workers.
|
||||
_.times(require('os').cpus().length, function(){
|
||||
cluster.fork();
|
||||
})
|
||||
|
||||
cluster.on('exit', function(worker, code, signal) {
|
||||
cluster.fork(); // replace the dead worker
|
||||
});
|
||||
cluster.on('exit', function(worker, code, signal) {
|
||||
var w = cluster.fork(); // replace the dead worker
|
||||
console.error('[%s] [master:%s] worker:%s disconnect! new worker:%s fork', new Date(), process.pid, worker.process.pid, w.process.pid);
|
||||
});
|
||||
|
||||
} else {
|
||||
require('coffee-script') // remove this once we've fully converted over
|
||||
var express = require("express");
|
||||
var http = require("http");
|
||||
var path = require("path");
|
||||
var domainMiddleware = require('domain-middleware');
|
||||
var swagger = require("swagger-node-express");
|
||||
require('coffee-script'); // remove this once we've fully converted over
|
||||
var express = require("express");
|
||||
var http = require("http");
|
||||
var path = require("path");
|
||||
var domainMiddleware = require('domain-middleware');
|
||||
var swagger = require("swagger-node-express");
|
||||
|
||||
var utils = require('./utils');
|
||||
var middleware = require('./middleware');
|
||||
var middleware = require('./middleware');
|
||||
|
||||
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
|
||||
var app = express();
|
||||
var server;
|
||||
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
|
||||
var app = express();
|
||||
var server = http.createServer();
|
||||
|
||||
// ------------ Setup configurations ------------
|
||||
utils.setupConfig();
|
||||
|
||||
// ------------ MongoDB Configuration ------------
|
||||
mongoose = require('mongoose');
|
||||
require('./models/user'); //load up the user schema - TODO is this necessary?
|
||||
require('./models/group');
|
||||
require('./models/challenge');
|
||||
mongoose.connect(nconf.get('NODE_DB_URI'), {auto_reconnect:true}, function(err) {
|
||||
if (err) throw err;
|
||||
console.info('Connected with Mongoose');
|
||||
});
|
||||
// ------------ MongoDB Configuration ------------
|
||||
mongoose = require('mongoose');
|
||||
require('./models/user'); //load up the user schema - TODO is this necessary?
|
||||
require('./models/group');
|
||||
require('./models/challenge');
|
||||
mongoose.connect(nconf.get('NODE_DB_URI'), {auto_reconnect:true}, function(err) {
|
||||
if (err) throw err;
|
||||
console.info('Connected with Mongoose');
|
||||
});
|
||||
|
||||
|
||||
// ------------ Passport Configuration ------------
|
||||
var passport = require('passport')
|
||||
var util = require('util')
|
||||
var FacebookStrategy = require('passport-facebook').Strategy;
|
||||
// Passport session setup.
|
||||
// To support persistent login sessions, Passport needs to be able to
|
||||
// serialize users into and deserialize users out of the session. Typically,
|
||||
// this will be as simple as storing the user ID when serializing, and finding
|
||||
// the user by ID when deserializing. However, since this example does not
|
||||
// have a database of user records, the complete Facebook profile is serialized
|
||||
// and deserialized.
|
||||
passport.serializeUser(function(user, done) {
|
||||
done(null, user);
|
||||
});
|
||||
// ------------ Passport Configuration ------------
|
||||
var passport = require('passport')
|
||||
var util = require('util')
|
||||
var FacebookStrategy = require('passport-facebook').Strategy;
|
||||
// Passport session setup.
|
||||
// To support persistent login sessions, Passport needs to be able to
|
||||
// serialize users into and deserialize users out of the session. Typically,
|
||||
// this will be as simple as storing the user ID when serializing, and finding
|
||||
// the user by ID when deserializing. However, since this example does not
|
||||
// have a database of user records, the complete Facebook profile is serialized
|
||||
// and deserialized.
|
||||
passport.serializeUser(function(user, done) {
|
||||
done(null, user);
|
||||
});
|
||||
|
||||
passport.deserializeUser(function(obj, done) {
|
||||
done(null, obj);
|
||||
});
|
||||
passport.deserializeUser(function(obj, done) {
|
||||
done(null, obj);
|
||||
});
|
||||
|
||||
|
||||
// Use the FacebookStrategy within Passport.
|
||||
// Strategies in Passport require a `verify` function, which accept
|
||||
// credentials (in this case, an accessToken, refreshToken, and Facebook
|
||||
// profile), and invoke a callback with a user object.
|
||||
passport.use(new FacebookStrategy({
|
||||
clientID: nconf.get("FACEBOOK_KEY"),
|
||||
clientSecret: nconf.get("FACEBOOK_SECRET"),
|
||||
callbackURL: nconf.get("BASE_URL") + "/auth/facebook/callback"
|
||||
},
|
||||
function(accessToken, refreshToken, profile, done) {
|
||||
// asynchronous verification, for effect...
|
||||
//process.nextTick(function () {
|
||||
// Use the FacebookStrategy within Passport.
|
||||
// Strategies in Passport require a `verify` function, which accept
|
||||
// credentials (in this case, an accessToken, refreshToken, and Facebook
|
||||
// profile), and invoke a callback with a user object.
|
||||
passport.use(new FacebookStrategy({
|
||||
clientID: nconf.get("FACEBOOK_KEY"),
|
||||
clientSecret: nconf.get("FACEBOOK_SECRET"),
|
||||
callbackURL: nconf.get("BASE_URL") + "/auth/facebook/callback"
|
||||
},
|
||||
function(accessToken, refreshToken, profile, done) {
|
||||
// asynchronous verification, for effect...
|
||||
//process.nextTick(function () {
|
||||
|
||||
// To keep the example simple, the user's Facebook profile is returned to
|
||||
// represent the logged-in user. In a typical application, you would want
|
||||
// to associate the Facebook account with a user record in your database,
|
||||
// and return that user instead.
|
||||
return done(null, profile);
|
||||
//});
|
||||
}
|
||||
));
|
||||
// To keep the example simple, the user's Facebook profile is returned to
|
||||
// represent the logged-in user. In a typical application, you would want
|
||||
// to associate the Facebook account with a user record in your database,
|
||||
// and return that user instead.
|
||||
return done(null, profile);
|
||||
//});
|
||||
}
|
||||
));
|
||||
|
||||
// ------------ Server Configuration ------------
|
||||
app.set("port", nconf.get('PORT'));
|
||||
// ------------ Server Configuration ------------
|
||||
|
||||
if (!process.env.SUPPRESS) app.use(express.logger("dev"));
|
||||
app.use(express.compress());
|
||||
app.set("views", __dirname + "/../views");
|
||||
app.set("view engine", "jade");
|
||||
app.use(express.favicon());
|
||||
app.use(middleware.cors);
|
||||
app.use(middleware.forceSSL);
|
||||
app.use(express.urlencoded());
|
||||
app.use(express.json());
|
||||
app.use(express.methodOverride());
|
||||
//app.use(express.cookieParser(nconf.get('SESSION_SECRET')));
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.cookieSession({ secret: nconf.get('SESSION_SECRET'), httpOnly: false, cookie: { maxAge: TWO_WEEKS }}));
|
||||
//app.use(express.session());
|
||||
domainMiddleware({
|
||||
server: server,
|
||||
killTimeout: 3000
|
||||
}),
|
||||
|
||||
// Initialize Passport! Also use passport.session() middleware, to support
|
||||
// persistent login sessions (recommended).
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
app.set("port", nconf.get('PORT'));
|
||||
|
||||
app.use(app.router);
|
||||
middleware.apiThrottle(app);
|
||||
if (!isProd) app.use(express.logger("dev"));
|
||||
app.use(express.compress());
|
||||
app.set("views", __dirname + "/../views");
|
||||
app.set("view engine", "jade");
|
||||
app.use(express.favicon());
|
||||
app.use(middleware.cors);
|
||||
app.use(middleware.forceSSL);
|
||||
app.use(express.urlencoded());
|
||||
app.use(express.json());
|
||||
app.use(express.methodOverride());
|
||||
//app.use(express.cookieParser(nconf.get('SESSION_SECRET')));
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.cookieSession({ secret: nconf.get('SESSION_SECRET'), httpOnly: false, cookie: { maxAge: TWO_WEEKS }}));
|
||||
//app.use(express.session());
|
||||
|
||||
var maxAge = (nconf.get('NODE_ENV') === 'production') ? 31536000000 : 0;
|
||||
app.use(express['static'](path.join(__dirname, "/../build"), { maxAge: maxAge }));
|
||||
app.use(express['static'](path.join(__dirname, "/../public")));
|
||||
// Initialize Passport! Also use passport.session() middleware, to support
|
||||
// persistent login sessions (recommended).
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
// development only
|
||||
//if ("development" === app.get("env")) {
|
||||
// app.use(express.errorHandler());
|
||||
//}
|
||||
app.use(app.router);
|
||||
|
||||
// Custom Directives
|
||||
app.use(require('./routes/pages').middleware);
|
||||
app.use(require('./routes/auth').middleware);
|
||||
var v2 = express();
|
||||
app.use('/api/v2', v2);
|
||||
app.use('/api/v1', require('./routes/apiv1').middleware);
|
||||
app.use('/export', require('./routes/dataexport').middleware);
|
||||
var maxAge = isProd ? 31536000000 : 0;
|
||||
app.use(express['static'](path.join(__dirname, "/../build"), { maxAge: maxAge }));
|
||||
app.use(express['static'](path.join(__dirname, "/../public")));
|
||||
|
||||
app.use(utils.errorHandler);
|
||||
// development only
|
||||
//if ("development" === app.get("env")) {
|
||||
// app.use(express.errorHandler());
|
||||
//}
|
||||
|
||||
require('./routes/apiv2.coffee')(swagger, v2);
|
||||
// Custom Directives
|
||||
app.use(require('./routes/pages').middleware);
|
||||
app.use(require('./routes/auth').middleware);
|
||||
var v2 = express();
|
||||
app.use('/api/v2', v2);
|
||||
app.use('/api/v1', require('./routes/apiv1').middleware);
|
||||
app.use('/export', require('./routes/dataexport').middleware);
|
||||
|
||||
server = http.createServer(app).listen(app.get("port"), function() {
|
||||
return console.log("Express server listening on port " + app.get("port"));
|
||||
});
|
||||
app.use(domainMiddleware({
|
||||
server: server
|
||||
//killTimeout: 30000,
|
||||
}));
|
||||
}
|
||||
app.use(utils.errorHandler);
|
||||
|
||||
require('./routes/apiv2.coffee')(swagger, v2);
|
||||
|
||||
server.on('request', app);
|
||||
server.listen(app.get("port"), function() {
|
||||
return console.log("Express server listening on port " + app.get("port"));
|
||||
});
|
||||
|
||||
module.exports = server;
|
||||
}
|
||||
|
|
@ -56,6 +56,7 @@ module.exports.setupConfig = function(){
|
|||
if (nconf.get('NODE_ENV') === "development") {
|
||||
Error.stackTraceLimit = Infinity;
|
||||
}
|
||||
if (nconf.get('NODE_ENV') === 'production') require('newrelic');
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -75,7 +76,8 @@ module.exports.errorHandler = function(err, req, res, next) {
|
|||
text: stack
|
||||
});
|
||||
console.error(stack);
|
||||
var shortMessage = (err.message.length < 200) ? err.message :
|
||||
err.message.substring(0,100) + err.message.substring(err.message.length-100,err.message.length);
|
||||
res.json(500,{err:shortMessage}); //res.end(err.message);
|
||||
var message = err.message ? err.message : err;
|
||||
message = (message.length < 200) ? message : message.substring(0,100) + message.substring(message.length-100,message.length);
|
||||
res.json(500,{err:message}); //res.end(err.message);
|
||||
process.exit(0);
|
||||
}
|
||||
|
|
@ -78,24 +78,9 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
|
|||
.popover-content
|
||||
p=env.t('welcomeMarket')
|
||||
p
|
||||
button.btn.btn-primary(ng-show='selectedEgg', ng-click='sellInventory()')
|
||||
=env.t('sell')
|
||||
| {{selectedEgg.text}}
|
||||
=env.t('for')
|
||||
| {{selectedEgg.value}}
|
||||
=env.t('gold')
|
||||
button.btn.btn-primary(ng-show='selectedPotion', ng-click='sellInventory()')
|
||||
=env.t('sell')
|
||||
| {{selectedPotion.text}}
|
||||
=env.t('for')
|
||||
| {{selectedPotion.value}}
|
||||
=env.t('gold')
|
||||
button.btn.btn-primary(ng-show='selectedFood', ng-click='sellInventory()')
|
||||
=env.t('sell')
|
||||
| {{selectedFood.text}}
|
||||
=env.t('for')
|
||||
| {{selectedFood.value}}
|
||||
=env.t('gold')
|
||||
button.btn.btn-primary(ng-show='selectedEgg', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedEgg.text}}", gold: "{{selectedEgg.value}}"})
|
||||
button.btn.btn-primary(ng-show='selectedPotion', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedPotion.text}}", gold: "{{selectedPotion.value}}"})
|
||||
button.btn.btn-primary(ng-show='selectedFood', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedFood.text}}", gold: "{{selectedFood.value}}"})
|
||||
menu.inventory-list(type='list')
|
||||
li.customize-menu
|
||||
menu.pets-menu(label=env.t('eggs'))
|
||||
|
|
@ -115,7 +100,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
|
|||
|
||||
li.customize-menu
|
||||
menu.pets-menu(label=env.t('food'))
|
||||
div(ng-repeat='food in Content.food', ng-show='food.key !== "Saddle"')
|
||||
div(ng-repeat='food in Content.food', ng-if='food.key !== "Saddle" && food.canBuy')
|
||||
button.customize-option(popover='{{food.notes}}', popover-title='{{food.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", food)', class='Pet_Food_{{food.key}}')
|
||||
p
|
||||
| {{food.value}}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ script(type='text/ng-template', id='partials/options.inventory.mounts.html')
|
|||
h3.popover-title
|
||||
a(target='_blank', href='http://www.kickstarter.com/profile/mattboch')=env.t('mattBoch')
|
||||
.popover-content
|
||||
p=env.t('mattShall1') + ' {{user.profile.name}} ' + env.t('mattShall2')
|
||||
p=env.t('mattShall', {name: "{{user.profile.name}}"})
|
||||
h4= '{{mountCount}} / {{totalPets}} ' + env.t('mountsTamed')
|
||||
menu.pets(type='list')
|
||||
li.customize-menu(ng-repeat='egg in Content.eggs')
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
script(id='partials/options.profile.avatar.html', type='text/ng-template')
|
||||
.row-fluid
|
||||
.span4
|
||||
h3=env.t('bodybody')
|
||||
h3=env.t('bodyBody')
|
||||
small
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"/> /
|
||||
= ' ' + env.t('locked')
|
||||
|
||||
h5=env.t('bodysize')
|
||||
h5=env.t('bodySize')
|
||||
.btn-group
|
||||
button.btn.btn-small(ng-class='{active: user.preferences.size=="slim"}', ng-click='set({"preferences.size":"slim"})')=env.t('bodyslim')
|
||||
button.btn.btn-small(ng-class='{active: user.preferences.size=="broad"}', ng-click='set({"preferences.size":"broad"})')=env.t('bodybroad')
|
||||
button.btn.btn-small(ng-class='{active: user.preferences.size=="slim"}', ng-click='set({"preferences.size":"slim"})')=env.t('bodySlim')
|
||||
button.btn.btn-small(ng-class='{active: user.preferences.size=="broad"}', ng-click='set({"preferences.size":"broad"})')=env.t('bodyBroad')
|
||||
|
||||
menu(type='list')
|
||||
li.customize-menu
|
||||
|
|
@ -17,15 +17,15 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template')
|
|||
each shirt in ['black', 'blue', 'green', 'pink', 'white', 'yellow']
|
||||
button.customize-option(class='{{user.preferences.size}}_shirt_'+shirt, type='button', ng-click='set({"preferences.shirt":"'+shirt+'"})')
|
||||
|
||||
menu(label=env.t('specialshirts'))
|
||||
menu(label=env.t('specialShirts'))
|
||||
each shirt in ['convict', 'cross', 'fire', 'horizon', 'ocean', 'purple', 'rainbow', 'redblue', 'thunder', 'tropical', 'zombie']
|
||||
button.customize-option(type='button', class='{{user.preferences.size}}_shirt_'+shirt, ng-class='{locked: !user.purchased.shirt.'+shirt+'}', ng-click='unlock("shirt.'+shirt+'")')
|
||||
menu
|
||||
button.btn.btn-small.btn-primary(ng-hide="user.purchased.shirt.convict && user.purchased.shirt.cross && user.purchased.shirt.fire && user.purchased.shirt.horizon && user.purchased.shirt.ocean && user.purchased.shirt.purple && user.purchased.shirt.rainbow && user.purchased.shirt.redblue && user.purchased.shirt.thunder && user.purchased.shirt.tropical && user.purchased.shirt.zombie", ng-click='unlock("shirt.convict,shirt.cross,shirt.fire,shirt.horizon,shirt.ocean,shirt.purple,shirt.rainbow,shirt.redblue,shirt.thunder,shirt.tropical,shirt.zombie")')!= env.t('unlockset5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
button.btn.btn-small.btn-primary(ng-hide="user.purchased.shirt.convict && user.purchased.shirt.cross && user.purchased.shirt.fire && user.purchased.shirt.horizon && user.purchased.shirt.ocean && user.purchased.shirt.purple && user.purchased.shirt.rainbow && user.purchased.shirt.redblue && user.purchased.shirt.thunder && user.purchased.shirt.tropical && user.purchased.shirt.zombie", ng-click='unlock("shirt.convict,shirt.cross,shirt.fire,shirt.horizon,shirt.ocean,shirt.purple,shirt.rainbow,shirt.redblue,shirt.thunder,shirt.tropical,shirt.zombie")')!= env.t('unlockSet5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
|
||||
|
||||
.span4
|
||||
h3=env.t('bodyhead')
|
||||
h3=env.t('bodyHead')
|
||||
small
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"/> /
|
||||
= ' ' + env.t('locked')
|
||||
|
|
@ -38,35 +38,35 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template')
|
|||
|
||||
// Special Events
|
||||
li.customize-menu.well.limited-edition
|
||||
.label.label-info.pull-right(popover=env.t('limited31Jan'), popover-title=env.t('limitededition'), popover-placement='right', popover-trigger='mouseenter')
|
||||
=env.t('limitededition')
|
||||
.label.label-info.pull-right(popover=env.t('limited31Jan'), popover-title=env.t('limitedEdition'), popover-placement='right', popover-trigger='mouseenter')
|
||||
=env.t('limitedEdition')
|
||||
|
|
||||
span.glyphicon.glyphicon.icon-question-sign
|
||||
menu(label=env.t('wintercolors'))
|
||||
span.glyphicon.glyphicon-question-sign
|
||||
menu(label=env.t('winterColors'))
|
||||
each color in ['candycane','frost','winternight','holly']
|
||||
button(type='button', ng-class='{locked: !user.purchased.hair.color.#{color}}', class='customize-option hair_bangs_1_#{color}', style='width: 40px; height: 40px;', ng-click='unlock("hair.color.#{color}")')
|
||||
button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.color.candycane && user.purchased.hair.color.frost && user.purchased.hair.color.winternight && user.purchased.hair.color.holly', ng-click='unlock("hair.color.candycane,hair.color.frost,hair.color.winternight,hair.color.holly")')!= env.t('unlockset5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.color.candycane && user.purchased.hair.color.frost && user.purchased.hair.color.winternight && user.purchased.hair.color.holly', ng-click='unlock("hair.color.candycane,hair.color.frost,hair.color.winternight,hair.color.holly")')!= env.t('unlockSet5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
|
||||
h5=env.t('bodyhair')
|
||||
h5=env.t('bodyHair')
|
||||
// Bangs
|
||||
li.customize-menu
|
||||
menu(label=env.t('hairbangs'))
|
||||
menu(label=env.t('hairBangs'))
|
||||
button(class='head_0 customize-option', type='button', ng-click='set({"preferences.hair.bangs":0})')
|
||||
each num in [1,2,3]
|
||||
button(class='hair_bangs_#{num}_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.bangs":#{num}})')
|
||||
|
||||
// Base
|
||||
li.customize-menu
|
||||
menu(label=env.t('hairbase'))
|
||||
menu(label=env.t('hairBase'))
|
||||
button(class='head_0 customize-option', type='button', ng-click='set({"preferences.hair.base":0})')
|
||||
each v,k in {1:true,2:false,3:true,4:false,5:false,6:false,7:false,8:false}
|
||||
case v
|
||||
when true: button(class='hair_base_#{k}_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":#{k}})')
|
||||
when false: button(class='hair_base_#{k}_{{user.preferences.hair.color}} customize-option', type='button', ng-class='{locked: !user.purchased.hair.base.#{k}}', ng-click='unlock("hair.base.#{k}")')
|
||||
|
||||
button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.base.2 && user.purchased.hair.base.4 && user.purchased.hair.base.5 && user.purchased.hair.base.6 && user.purchased.hair.base.7 && user.purchased.hair.base.8', ng-click='unlock("hair.base.2,hair.base.4,hair.base.5,hair.base.6,hair.base.7,hair.base.8")')!= env.t('unlockset5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.base.2 && user.purchased.hair.base.4 && user.purchased.hair.base.5 && user.purchased.hair.base.6 && user.purchased.hair.base.7 && user.purchased.hair.base.8', ng-click='unlock("hair.base.2,hair.base.4,hair.base.5,hair.base.6,hair.base.7,hair.base.8")')!= env.t('unlockSet5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
|
||||
h5=env.t('bodyfacialhair')
|
||||
h5=env.t('bodyFacialHair')
|
||||
|
||||
// Beard
|
||||
li.customize-menu
|
||||
|
|
@ -82,31 +82,31 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template')
|
|||
each num in [1,2]
|
||||
button(class='hair_mustache_#{num}_{{user.preferences.hair.color}} customize-option', type='button', ng-class='{locked: !user.purchased.hair.mustache.#{num}}', ng-click='unlock("hair.mustache.#{num}")')
|
||||
|
||||
button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.mustache.1 && user.purchased.hair.mustache.2 && user.purchased.hair.beard.1 && user.purchased.hair.beard.2 && user.purchased.hair.beard.3', ng-click='unlock("hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3")')!= env.t('unlockset5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.mustache.1 && user.purchased.hair.mustache.2 && user.purchased.hair.beard.1 && user.purchased.hair.beard.2 && user.purchased.hair.beard.3', ng-click='unlock("hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3")')!= env.t('unlockSet5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
|
||||
.span4
|
||||
h3=env.t('bodyskin')
|
||||
h3=env.t('bodySkin')
|
||||
small
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"/> /
|
||||
= ' ' + env.t('locked')
|
||||
// skin
|
||||
li.customize-menu
|
||||
menu(label=env.t('basicskins'))
|
||||
menu(label=env.t('basicSkins'))
|
||||
each color in ['ddc994','f5a76e','ea8349','c06534','98461a','915533','c3e1dc','6bd049']
|
||||
button.customize-option(type='button', class='skin_#{color}', ng-click='set({"preferences.skin":"#{color}"})')
|
||||
|
||||
// Rainbow Skin
|
||||
h5=env.t('rainbowskins')
|
||||
h5=env.t('rainbowSkins')
|
||||
//menu(label='Rainbow Skins (2G / skin)')
|
||||
menu
|
||||
each color in ['eb052b','f69922','f5d70f','0ff591','2b43f6','d7a9f7','800ed0','rainbow']
|
||||
button.customize-option(type='button', class='skin_#{color}', ng-class='{locked: !user.purchased.skin.#{color}}', ng-click='unlock("skin.#{color}")')
|
||||
button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.eb052b && user.purchased.skin.f69922 && user.purchased.skin.f5d70f && user.purchased.skin.0ff591 && user.purchased.skin.2b43f6 && user.purchased.skin.d7a9f7 && user.purchased.skin.800ed0 && user.purchased.skin.rainbow', ng-click='unlock("skin.eb052b,skin.f69922,skin.f5d70f,skin.0ff591,skin.2b43f6,skin.d7a9f7,skin.800ed0,skin.rainbow")')!= env.t('unlockset5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.eb052b && user.purchased.skin.f69922 && user.purchased.skin.f5d70f && user.purchased.skin.0ff591 && user.purchased.skin.2b43f6 && user.purchased.skin.d7a9f7 && user.purchased.skin.800ed0 && user.purchased.skin.rainbow', ng-click='unlock("skin.eb052b,skin.f69922,skin.f5d70f,skin.0ff591,skin.2b43f6,skin.d7a9f7,skin.800ed0,skin.rainbow")')!= env.t('unlockSet5') + ' <span class="Pet_Currency_Gem1x inline-gems"/>'
|
||||
|
||||
// Special Events
|
||||
// restore to d4df481 to see purchasing + "limited edition" code
|
||||
div(ng-if='user.purchased.skin.monster || user.purchased.skin.pumpkin || user.purchased.skin.skeleton || user.purchased.skin.zombie || user.purchased.skin.ghost || user.purchased.skin.shadow')
|
||||
h5=env.t('spookyskins')
|
||||
h5=env.t('spookySkins')
|
||||
menu
|
||||
each color in ['monster','pumpkin','skeleton','zombie','ghost','shadow']
|
||||
button.customize-option(type='button', class='skin_#{color}', ng-if='user.purchased.skin.#{color}', ng-click='unlock("skin.#{color}")')
|
||||
|
|
@ -116,11 +116,11 @@ script(id='partials/options.profile.stats.html', type='text/ng-template')
|
|||
.border-right(ng-class='user.flags.classSelected && !user.preferences.disableClasses ? "span4" : "span6"')
|
||||
include ../shared/profiles/stats
|
||||
.span4.border-right.allocate-stats(ng-if='user.flags.classSelected && !user.preferences.disableClasses')
|
||||
h3=env.t('characterbuild')
|
||||
h3=env.t('characterBuild')
|
||||
h4
|
||||
=env.t('class') + ': '
|
||||
span {{ {warrior:'Warrior',wizard:'Mage',healer:'Healer',rogue:'Rogue'}[user.stats.class] }}
|
||||
a.btn.btn-danger.btn-mini(ng-click='changeClass(null)')=env.t('changeclass')
|
||||
a.btn.btn-danger.btn-mini(ng-click='changeClass(null)')=env.t('changeClass')
|
||||
small 3 <span class="Pet_Currency_Gem1x inline-gems"/>
|
||||
table.table.table-striped
|
||||
tr
|
||||
|
|
@ -129,48 +129,53 @@ script(id='partials/options.profile.stats.html', type='text/ng-template')
|
|||
{{user.stats.points}}
|
||||
=env.t('unallocated')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('levelpopover'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('levelPopover'))
|
||||
td
|
||||
tr
|
||||
td(colspan=2)
|
||||
fieldset.auto-allocate
|
||||
label.checkbox
|
||||
input(type='checkbox', ng-model='user.preferences.automaticAllocation', ng-change='set({"preferences.automaticAllocation": user.preferences.automaticAllocation?true: false})', ng-click='set({"preferences.allocationMode":"taskbased"})')
|
||||
=env.t('automaticallocation')
|
||||
=env.t('autoAllocation')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('automaticallocationpopover'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('autoAllocationPop'))
|
||||
form(ng-show='user.preferences.automaticAllocation',style='margin-left:1em')
|
||||
label.radio
|
||||
input(type='radio', name='allocationMode', value='flat', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "flat"})')
|
||||
=env.t('evenallocation')
|
||||
=env.t('evenAllocation')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('evenallocationpopover'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('evenAllocationPop'))
|
||||
label.radio
|
||||
input(type='radio', name='allocationMode', value='classbased', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "classbased"})')
|
||||
=env.t('classallocation')
|
||||
=env.t('classAllocation')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('classallocationpopover'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('classAllocationPop'))
|
||||
label.radio
|
||||
input(type='radio', name='allocationMode', value='taskbased', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "taskbased"})')
|
||||
=env.t('taskallocation')
|
||||
=env.t('taskAllocation')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('taskallocationpopover'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('taskAllocationPop'))
|
||||
div(ng-show='user.preferences.automaticAllocation && !(user.preferences.allocationMode === "taskbased") && (user.stats.points > 0)')
|
||||
a.btn.btn-primary.btn-mini(ng-click='user.ops.allocateNow({})', popover-trigger='mouseenter', popover-placement='right', popover=env.t('distributePointsPop'))
|
||||
span.glyphicon.glyphicon-download
|
||||
|
||||
=env.t('distributePoints')
|
||||
tr
|
||||
td= env.t('allocatestr') + ' {{user.stats.str}}'
|
||||
td= env.t('allocateStr') + ' {{user.stats.str}}'
|
||||
td
|
||||
a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("str")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocatestrpop')) +
|
||||
a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("str")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateStrPop')) +
|
||||
tr
|
||||
td= env.t('allocatecon') + ' {{user.stats.con}}'
|
||||
td= env.t('allocateCon') + ' {{user.stats.con}}'
|
||||
td
|
||||
a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("con")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateconpop')) +
|
||||
a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("con")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateConPop')) +
|
||||
tr
|
||||
td= env.t('allocateper') + ' {{user.stats.per}}'
|
||||
td= env.t('allocatePer') + ' {{user.stats.per}}'
|
||||
td
|
||||
a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("per")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateperpop')) +
|
||||
a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("per")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocatePerPop')) +
|
||||
tr
|
||||
td= env.t('allocateint') + ' {{user.stats.int}}'
|
||||
td= env.t('allocateInt') + ' {{user.stats.int}}'
|
||||
td
|
||||
a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("int")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateintpop')) +
|
||||
a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("int")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateIntPop')) +
|
||||
div(ng-class='user.flags.classSelected && !user.preferences.disableClasses ? "span4" : "span6"')
|
||||
include ../shared/profiles/achievements
|
||||
|
||||
|
|
@ -178,19 +183,19 @@ script(id='partials/options.profile.profile.html', type='text/ng-template')
|
|||
button.btn.btn-default(ng-click='_editing.profile = true', ng-show='!_editing.profile')= env.t('edit')
|
||||
button.btn.btn-primary(ng-click='save()', ng-show='_editing.profile')= env.t('save')
|
||||
div(ng-show='!_editing.profile')
|
||||
h4=env.t('displayname')
|
||||
h4=env.t('displayName')
|
||||
span(ng-show='profile.profile.name') {{profile.profile.name}}
|
||||
span.muted(ng-hide='profile.profile.name') -
|
||||
=env.t('none')
|
||||
| -
|
||||
|
||||
h4=env.t('displayphoto')
|
||||
h4=env.t('displayPhoto')
|
||||
img(ng-show='profile.profile.imageUrl', ng-src='{{profile.profile.imageUrl}}')
|
||||
span.muted(ng-hide='profile.profile.imageUrl') -
|
||||
=env.t('none')
|
||||
| -
|
||||
|
||||
h4=env.t('displayblurb')
|
||||
h4=env.t('displayBlurb')
|
||||
markdown(ng-show='profile.profile.blurb', ng-model='profile.profile.blurb')
|
||||
span.muted(ng-hide='profile.profile.blurb') -
|
||||
=env.t('none')
|
||||
|
|
@ -200,14 +205,14 @@ script(id='partials/options.profile.profile.html', type='text/ng-template')
|
|||
div.whatever-options(ng-show='_editing.profile')
|
||||
// TODO use photo-upload instead: https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/xMmADvxBOak
|
||||
.control-group.option-large
|
||||
label.control-label=env.t('displayname')
|
||||
label.control-label=env.t('displayName')
|
||||
input.option-content(type='text', placeholder=env.t('fullName'), ng-model='editingProfile.name')
|
||||
.control-group.option-large
|
||||
label.control-label=env.t('photourl')
|
||||
input.option-content(type='url', ng-model='editingProfile.imageUrl', placeholder=env.t('imageurl'))
|
||||
label.control-label=env.t('photoUrl')
|
||||
input.option-content(type='url', ng-model='editingProfile.imageUrl', placeholder=env.t('imageUrl'))
|
||||
.control-group.option-large
|
||||
label.control-label=env.t('displayblurb')
|
||||
textarea.option-content(style='height:15em;', placeholder=env.t("displayblurb"), ng-model='editingProfile.blurb')
|
||||
label.control-label=env.t('displayBlurb')
|
||||
textarea.option-content(style='height:15em;', placeholder=env.t('displayBlurb'), ng-model='editingProfile.blurb')
|
||||
include ../shared/formatting-help
|
||||
|
||||
script(id='partials/options.profile.html', type="text/ng-template")
|
||||
|
|
@ -217,7 +222,7 @@ script(id='partials/options.profile.html', type="text/ng-template")
|
|||
=env.t('avatar')
|
||||
li(ng-class="{ active: $state.includes('options.profile.stats') }")
|
||||
a(ui-sref='options.profile.stats')
|
||||
=env.t('statsach')
|
||||
=env.t('statsAch')
|
||||
li(ng-class="{ active: $state.includes('options.profile.profile') }")
|
||||
a(ui-sref='options.profile.profile')
|
||||
=env.t('profile')
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@ script(id='partials/options.settings.html', type="text/ng-template")
|
|||
=env.t('API')
|
||||
li(ng-class="{ active: $state.includes('options.settings.export') }")
|
||||
a(ui-sref='options.settings.export')
|
||||
=env.t('dataexport')
|
||||
=env.t('dataExport')
|
||||
li(ng-class="{ active: $state.includes('options.settings.subscription') }")
|
||||
a(ui-sref='options.settings.subscription')
|
||||
| Subscription
|
||||
a(ui-sref='options.settings.subscription')=env.t('subscription')
|
||||
|
||||
.tab-content
|
||||
.tab-pane.active
|
||||
|
|
@ -28,7 +27,10 @@ script(type='text/ng-template', id='partials/options.settings.settings.html')
|
|||
div
|
||||
small
|
||||
div=env.t('clockInfo')
|
||||
.alert.alert-danger Warning: this is a highly-experimental feature, and many <a href='https://github.com/HabitRPG/habitrpg/issues/1057' target='_blank'>experience issues</a> with it.
|
||||
.alert.alert-danger
|
||||
=env.t('subWarning1')
|
||||
a(href='https://github.com/HabitRPG/habitrpg/issues/1057' target='_blank')=env.t('subWarning2')
|
||||
=env.t('subWarning3')
|
||||
hr
|
||||
h4=env.t('language')
|
||||
select(ng-model='language.code', ng-options='lang.code as lang.name for lang in avalaibleLanguages', ng-change='changeLanguage()')
|
||||
|
|
@ -38,36 +40,36 @@ script(type='text/ng-template', id='partials/options.settings.settings.html')
|
|||
input(type='checkbox', ng-click='set({"preferences.hideHeader":user.preferences.hideHeader?false:true})', ng-checked='user.preferences.hideHeader!==true')
|
||||
=env.t('showHeader')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('showHeaderpop'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('showHeaderPop'))
|
||||
label.checkbox
|
||||
input(type='checkbox', ng-click='toggleStickyHeader()', ng-checked='user.preferences.stickyHeader!==false')
|
||||
=env.t('stickyHeader')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('stickyHeaderpop'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('stickyHeaderPop'))
|
||||
label.checkbox
|
||||
input(type='checkbox', ng-model='user.preferences.newTaskEdit', ng-change='set({"preferences.newTaskEdit": user.preferences.newTaskEdit?true: false})')
|
||||
=env.t('newtaskedit')
|
||||
=env.t('newTaskEdit')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('newtaskeditpop'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('newTaskEditPop'))
|
||||
label.checkbox
|
||||
input(type='checkbox', ng-model='user.preferences.tagsCollapsed', ng-change='set({"preferences.tagsCollapsed": user.preferences.tagsCollapsed?true: false})')
|
||||
=env.t('startcollapsed')
|
||||
=env.t('startCollapsed')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('startcollapsedpop'))
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('startCollapsedPop'))
|
||||
label.checkbox
|
||||
input(type='checkbox', ng-model='user.preferences.advancedCollapsed', ng-change='set({"preferences.advancedCollapsed": user.preferences.advancedCollapsed?true: false})')
|
||||
=env.t('startacollapsed')
|
||||
=env.t('startAdvCollapsed')
|
||||
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('startacollapsedpop'))
|
||||
button.btn(ng-click='showTour()', popover-placement='right', popover-trigger='mouseenter', popover=env.t('showtourpop1'))= env.t('showtour')
|
||||
span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('startAdvCollapsedPop'))
|
||||
button.btn(ng-click='showTour()', popover-placement='right', popover-trigger='mouseenter', popover=env.t('restartTour'))= env.t('showTour')
|
||||
br
|
||||
button.btn(ng-click='showBailey()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('showtourpop'))= env.t('showtour2')
|
||||
button.btn(ng-click='showBailey()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('showBaileyPop'))= env.t('showBailey')
|
||||
br
|
||||
button.btn(ng-click='modals.restore = true', popover-trigger='mouseenter', popover-placement='right', popover=env.t('fixvalpop'))= env.t('fixval')
|
||||
button.btn(ng-click='modals.restore = true', popover-trigger='mouseenter', popover-placement='right', popover=env.t('fixValPop'))= env.t('fixVal')
|
||||
div(ng-if='user.preferences.disableClasses==true')
|
||||
button.btn(ng-click='user.ops.changeClass({})', popover-trigger='mouseenter', popover-placement='right', popover=env.t('enableclasspop'))= env.t('enableclass')
|
||||
button.btn(ng-click='user.ops.changeClass({})', popover-trigger='mouseenter', popover-placement='right', popover=env.t('enableClassPop'))= env.t('enableClass')
|
||||
div(ng-if='!user.preferences.disableClasses && user.flags.classSelected')
|
||||
button.btn(ng-click='showClassesTour()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('classtourpop'))= env.t('showclass')
|
||||
button.btn(ng-click='showClassesTour()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('classTourPop'))= env.t('showClass')
|
||||
|
||||
//- 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')
|
||||
|
|
@ -84,8 +86,8 @@ script(type='text/ng-template', id='partials/options.settings.settings.html')
|
|||
|
||||
hr
|
||||
h4=env.t('dangerZone')
|
||||
a.btn.btn-danger(ng-click='modals.reset = true', popover-trigger='mouseenter', popover-placement='right', popover=env.t('resetaccpop'))= env.t('resetaccount')
|
||||
a.btn.btn-danger(ng-click='modals.delete = true', popover-trigger='mouseenter', popover=env.t('deleteaccpop'))= env.t('deleteaccount')
|
||||
a.btn.btn-danger(ng-click='modals.reset = true', popover-trigger='mouseenter', popover-placement='right', popover=env.t('resetAccPop'))= env.t('resetAccount')
|
||||
a.btn.btn-danger(ng-click='modals.delete = true', popover-trigger='mouseenter', popover=env.t('deleteAccPop'))= env.t('deleteAccount')
|
||||
|
||||
script(type='text/ng-template', id='partials/options.settings.api.html')
|
||||
.row.fluid
|
||||
|
|
@ -96,19 +98,19 @@ script(type='text/ng-template', id='partials/options.settings.api.html')
|
|||
pre.prettyprint {{user.id}}
|
||||
h6=env.t('APIToken')
|
||||
pre.prettyprint {{user.apiToken}}
|
||||
h6=env.t('qrcode')
|
||||
h6=env.t('qrCode')
|
||||
img(src='https://chart.googleapis.com/chart?cht=qr&chs=200x200&chl=%7B%22address%22%3A%22https%3A%2F%2Fhabitrpg.com%22%2C%22user%22%3A%22{{user.id}}%22%2C%22key%22%3A%22{{user.apiToken}}%22%7D&choe=UTF-8&chld=L', alt='qrcode')
|
||||
|
||||
script(id='partials/options.settings.export.html', type="text/ng-template")
|
||||
.row.fluid
|
||||
.span6
|
||||
h2=env.t('dataexport')
|
||||
small=env.t('dataexports')
|
||||
h4=env.t('habithistory')
|
||||
=env.t('exporthistory')
|
||||
h2=env.t('dataExport')
|
||||
small=env.t('saveData')
|
||||
h4=env.t('habitHistory')
|
||||
=env.t('exportHistory')
|
||||
a(href="/export/history.csv")= ' ' + env.t('csv')
|
||||
h4=env.t('userdata')
|
||||
=env.t('exportuserdata')
|
||||
h4=env.t('userData')
|
||||
=env.t('exportUserData')
|
||||
a(href="/export/userdata.xml")= ' ' + env.t('xml') + ' '
|
||||
a(href="/export/userdata.json")= env.t('json')
|
||||
|
||||
|
|
@ -116,28 +118,24 @@ script(id='partials/options.settings.subscription.perks.html',type='text/ng-temp
|
|||
table.table.table-striped
|
||||
tr
|
||||
td
|
||||
span.dashed-underline(popover="Ads will stay disabled while you have an active account (original users with disabled ads are grandfathered).",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Disable ads
|
||||
span.dashed-underline(popover=env.t('disableAdsText'),popover-trigger='mouseenter',popover-placement='right')=env.t('disableAds')
|
||||
tr
|
||||
td
|
||||
span.dashed-underline(popover="(1 Gem costs {{Shared.planGemLimits.convRate}} Gold) Addresses the \"pay to win\" concern, as everything is now achievable through hard work. There's a {{Shared.planGemLimits.convCap}}G monthly conversion cap to prevent farming.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Buy Gems with Gold
|
||||
span.dashed-underline(popover=env.t('buyGemsGoldText', {gemCost: "{{Shared.planGemLimits.convRate}}", gemLimit: "{{Shared.planGemLimits.convCap}}"}),popover-trigger='mouseenter',popover-placement='right')=env.t('buyGemsGold')
|
||||
tr
|
||||
td
|
||||
span.dashed-underline(popover="Makes your full history available in graphs and export. Non-subscriber histories get consolidated for database optimization.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Retain full history entries
|
||||
span.dashed-underline(popover=env.t('retainHistoryText'),popover-trigger='mouseenter',popover-placement='right')=env.t('retainHistory')
|
||||
tr
|
||||
td
|
||||
span.dashed-underline(popover="Complete your stable faster!",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Daily drop-caps doubled
|
||||
span.dashed-underline(popover=env.t('doubleDropsText'),popover-trigger='mouseenter',popover-placement='right')=env.t('doubleDrops')
|
||||
//-tr
|
||||
//- td +20 gems to your account
|
||||
tr
|
||||
td
|
||||
span.dashed-underline(popover="This open source project can use all the help it can get. Help us keep Habit alive!",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Supports the developers
|
||||
span.dashed-underline(popover=env.t('supportDevsText'),popover-trigger='mouseenter',popover-placement='right')=env.t('supportDevs')
|
||||
tr
|
||||
td.alert.alert-info $5 USD / Month
|
||||
td.alert.alert-info $5
|
||||
=env.t('monthUSD')
|
||||
|
||||
script(id='partials/feature-matrix-check.html',type='text/ng-template')
|
||||
span.task-checker.action-yesno
|
||||
|
|
@ -147,16 +145,15 @@ script(id='partials/feature-matrix-check.html',type='text/ng-template')
|
|||
script(id='partials/options.settings.subscription.html',type='text/ng-template')
|
||||
|
||||
.well
|
||||
h2 Individual Subscription
|
||||
h2=env.t('individualSub')
|
||||
div(ng-if='!user.purchased.plan.customerId')
|
||||
div(ng-include="'partials/options.settings.subscription.perks.html'")
|
||||
.btn.btn-primary(ng-click='showStripe(true)') Subscribe
|
||||
.btn.btn-primary(ng-click='showStripe(true)')=env.t('subscribe')
|
||||
//-small.muted PayPal coming soon.
|
||||
|
||||
div(ng-if='user.purchased.plan.customerId')
|
||||
.well
|
||||
div(style='font-size:22px')
|
||||
span.glyphicon.glyphicon-ok
|
||||
| Subscribed
|
||||
span.glyphicon.glyphicon-ok=env.t('subscribed')
|
||||
div(ng-include="'partials/options.settings.subscription.perks.html'")
|
||||
.btn.btn-small.btn-danger(ng-click='cancelSubscription()') Cancel Subscription
|
||||
.btn.btn-small.btn-danger(ng-click='cancelSubscription()')=env.t('cancelSub')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
script(type='text/ng-template', id='partials/options.social.challenges.detail.close.html')
|
||||
a.btn.btn-small.btn-danger(ng-click="delete(closingChal)")=env.t('delete')
|
||||
h5=env.t('minusOr')
|
||||
h5= '- ' + env.t('or') + ' -'
|
||||
select(ui-select2, ng-required=true, ng-model='closingChal.winner', data-placeholder=env.t('selectWinner'), ng-change='selectWinner(closingChal)', )
|
||||
option(value='')
|
||||
option(ng-repeat='u in closingChal.members', value='{{u._id}}') {{u.profile.name}}
|
||||
|
|
@ -111,7 +111,7 @@ script(type='text/ng-template', id='partials/options.social.challenges.html')
|
|||
span.input-suffix.Pet_Currency_Gem1x.inline-gems
|
||||
span.glyphicon.glyphicon-question-sign(popover=env.t('prizePop'), popover-trigger='mouseenter', popover-placement='right')
|
||||
span(ng-show='newChallenge.group=="habitrpg"')
|
||||
!=env.t('min1Gem') + ' <strong>' + env.t('publicChallenges') + '</strong> ' + env.t('helpsPrevent')
|
||||
!=env.t('publicChallenges')
|
||||
|
||||
.option-medium(ng-if='user.contributor.admin')
|
||||
label.checkbox
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title=env.t('guild
|
|||
// ------ Bosses -------
|
||||
.modal.inline-modal(bindonce='group', ng-if='group.type==="party" && group.quest.key')
|
||||
.modal-header
|
||||
h3(ng-if='group.quest.active==false')=env.t('questInv') + '{{Content.quests[group.quest.key].text}}'
|
||||
h3(ng-if='group.quest.active==false')=env.t('questInvitation') + '{{Content.quests[group.quest.key].text}}'
|
||||
h3(ng-if='group.quest.active==true') {{Content.quests[group.quest.key].text}}
|
||||
.modal-body
|
||||
div(ng-if='group.quest.active==false')
|
||||
|
|
@ -61,9 +61,9 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title=env.t('guild
|
|||
|
||||
div(ng-if='Content.quests[group.quest.key].boss')
|
||||
.npc_ian.pull-left
|
||||
p!=env.t('bossDmg1') + ' <strong>' + env.t('bossDmg2') + '</strong>'
|
||||
p!=env.t('bossDmg1')
|
||||
br
|
||||
p=env.t('bossDmg3')
|
||||
p=env.t('bossDmg2')
|
||||
|
||||
div(ng-if='Content.quests[group.quest.key].collect')
|
||||
.npc_ian.pull-left
|
||||
|
|
@ -88,7 +88,7 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title=env.t('guild
|
|||
input.option-content(type='text', ng-model='group.name', placeholder=env.t('groupName'))
|
||||
.control-group.option-large
|
||||
label.control-label=env.t('description')
|
||||
textarea.option-content(style='height:15em;', placeholder=env.t('groupdescr'), ng-model='group.description')
|
||||
textarea.option-content(style='height:15em;', placeholder=env.t('groupDescr'), ng-model='group.description')
|
||||
.control-group.option-large
|
||||
label.control-label=env.t('logoUrl')
|
||||
input.option-content(type='url', placeholder=env.t('logoUrl'), ng-model='group.logo')
|
||||
|
|
@ -131,7 +131,7 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title=env.t('guild
|
|||
tr(ng-if='group.memberCount > group.members.length')
|
||||
td
|
||||
|<span class='badge'>{{group.memberCount - group.members.length}}
|
||||
= ' ' + env.t('moremembers')
|
||||
= ' ' + env.t('moreMembers')
|
||||
|</span>
|
||||
|
||||
h4(ng-show='group.invites.length > 0')=env.t('invited')
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ script(type='text/ng-template', id='partials/options.social.hall.heroes.html')
|
|||
|
||||
.control-group.option-medium
|
||||
input.option-content(type='number', step="any", ng-model='hero.balance')
|
||||
span.input-suffix Balance
|
||||
span.input-suffix=env.t('balance')
|
||||
p
|
||||
small!= '`user.balance`' + env.t('USD1') + ' <em>' + env.t('not') + '</em> ' + env.t('USD2')
|
||||
small!= '`user.balance`' + env.t('notGems')
|
||||
|
||||
.control-group.option-medium
|
||||
label.checkbox
|
||||
|
|
@ -70,7 +70,7 @@ script(type='text/ng-template', id='partials/options.social.hall.heroes.html')
|
|||
span(ng-if='hero.contributor.admin',popover=env.t('gamemaster'),popover-trigger='mouseenter',popover-placement='right')
|
||||
a.label(class='label-contributor-{{hero.contributor.level}}', ng-class='{"label-npc": hero.backer.npc}', ng-click='clickMember(hero._id, true)')
|
||||
| {{hero.profile.name}}
|
||||
span.glyphicon.glyphicon-star.icon-white
|
||||
span.glyphicon.glyphicon-star
|
||||
span(ng-if='!hero.contributor.admin')
|
||||
a.label(class='label-contributor-{{hero.contributor.level}}', ng-class='{"label-npc": hero.backer.npc}', ng-click='clickMember(hero._id, true)') {{hero.profile.name}}
|
||||
td(ng-if='user.contributor.admin') {{hero._id}}
|
||||
|
|
|
|||
|
|
@ -68,35 +68,40 @@
|
|||
a.label.label-contributor-1(ng-click='toggleUserTier($event)')=env.t('friendBadge')
|
||||
div(style='display:none;')
|
||||
p
|
||||
!= '<span class="achievement achievement-firefox"></span> ' + env.t('friendText') + '<strong> ' + env.t('first') + '</strong> ' + env.t('friend1Text') + '<strong> 2' + env.t('gems') + '</strong>.'
|
||||
span.achievement.achievement-firefox
|
||||
!=env.t('friendFirst')
|
||||
hr
|
||||
p
|
||||
!= '<span class="shop_armor_special_1 shop-sprite item-img"></span> ' + env.t('friendText') + '<strong> ' + env.t('second') + '</strong> ' + env.t('friendText2') + '<strong> ' + env.t('crystalArmor') + '</strong> ' + env.t('friendText3') + '<strong> 2' + env.t('gems') + '</strong>.'
|
||||
span.shop-sprite.item-img(class='shop_armor_special_1')
|
||||
!=env.t('friendSecond')
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-3(ng-click='toggleUserTier($event)')=env.t('eliteBadge')
|
||||
div(style='display:none;')
|
||||
p
|
||||
!= '<span class="shop_head_special_1 shop-sprite item-img"></span> ' + env.t('friendText') + ' <strong>' + env.t('third') + '</strong> ' + env.t('friendText2') + ' <strong>' + env.t('crystalHelmet') + '</strong> ' + env.t('friendText3') + '<strong> 2' + env.t('gems') + '</strong>.'
|
||||
span.shop-sprite.item-img(class='shop_head_special_1')
|
||||
!=env.t('eliteThird')
|
||||
hr
|
||||
p
|
||||
!= '<span class="shop_weapon_special_1 shop-sprite item-img"></span> ' + env.t('friendText') + ' <strong>' + env.t('fourth') + '</strong> ' + env.t('friendText2') + ' <strong>' + env.t('crystalSword') + '</strong> ' + env.t('friendText3') + '<strong> 2' + env.t('gems') + '</strong>.'
|
||||
|
||||
span.shop-sprite.item-img(class='shop_weapon_special_1')
|
||||
!=env.t('eliteFourth')
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-5(ng-click='toggleUserTier($event)')=env.t('championBadge')
|
||||
div(style='display:none;')
|
||||
p
|
||||
!= '<span class="shop_shield_special_1 shop-sprite item-img"></span> ' + env.t('friendText') + ' <em>' + env.t('fifth') + '</em> ' + env.t('friendText2') + ' <strong>' + env.t('crystalShield') + '</strong> ' + env.t('friendText3') + '<strong> 2' + env.t('gems') + '</strong>.'
|
||||
span.shop-sprite.item-img(class='shop_shield_special_1')
|
||||
!=env.t('championFifth')
|
||||
hr
|
||||
p
|
||||
!= '<div class="Pet-Dragon-Hydra pull-left"></div>' + env.t('friendText') + ' <em>' + env.t('sixth') + '</em> ' + env.t('friendText2b') + ' <strong>' + env.t('hydraPet') + '</strong> ' + env.t('friendText3b') + '<strong> 2' + env.t('gems') + '</strong>.'
|
||||
div(class='Pet-Dragon-Hydra pull-left')
|
||||
!=env.t('championSixth')
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-7(ng-click='toggleUserTier($event)')=env.t('legendaryBadge')
|
||||
div(style='display:none;')
|
||||
p
|
||||
!= env.t('friendText') + ' <em>' + env.t('seventh') + '</em> ' + env.t('friendText2c') + '<strong> 2' + env.t('gems') + ' </strong>' + env.t('friendText3c')
|
||||
!=env.t('legSeventh')
|
||||
tr
|
||||
td
|
||||
a.label.label-contributor-8(ng-click='toggleUserTier($event)')=env.t('heroicBadge')
|
||||
|
|
@ -111,12 +116,12 @@
|
|||
include ./challenge-box
|
||||
|
||||
.span8(ng-controller='ChatCtrl')
|
||||
h3=env.t('taverntalk')
|
||||
h3=env.t('tavernTalk')
|
||||
include ./chat-box
|
||||
small(style='position: relative; top: -20px; left: 25px;')
|
||||
include ../../shared/formatting-help
|
||||
small(style='position: relative; top: 18px; left: 0px')
|
||||
alert.alert-info
|
||||
!=env.t('tavernalert1') + ' <a href="https://github.com/HabitRPG/habitrpg/issues" target="_blank">' + env.t('tavernalert2') + '</a>.'
|
||||
!=env.t('tavernAlert1') + ' <a href="https://github.com/HabitRPG/habitrpg/issues" target="_blank">' + env.t('tavernAlert2') + '</a>.'
|
||||
ul.unstyled.tavern-chat
|
||||
include ./chat-message
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ figure.herobox(ng-click='spell ? castEnd(profile, "user", $event) : clickMember(
|
|||
// FIXME handle @minimal, this might have to be a directive
|
||||
span.current-pet(class='Pet-{{profile.items.currentPet}}', ng-show='profile.items.currentPet && !minimal')
|
||||
.avatar-level(ng-class='userLevelStyle(profile,"label")')
|
||||
span.glyphicon.glyphicon-circle-arrow-up.icon-white(ng-show='profile.stats.buffs.str || profile.stats.buffs.per || profile.stats.buffs.con || profile.stats.buffs.int || profile.stats.buffs.stealth', tooltip=env.t('buffed'), style='margin-right:5px;')
|
||||
span.glyphicon.glyphicon-circle-arrow-up(ng-show='profile.stats.buffs.str || profile.stats.buffs.per || profile.stats.buffs.con || profile.stats.buffs.int || profile.stats.buffs.stealth', tooltip=env.t('buffed'), style='margin-right:5px;')
|
||||
=env.t('lvl')
|
||||
| {{profile.stats.lvl}}
|
||||
span.glyphicon.glyphicon-plus-sign.icon-white(ng-show='profile.achievements.rebirths', tooltip=env.t('reborn') + '{{profile.achievements.rebirthLevel}}', style='margin-left:5px')
|
||||
span.glyphicon.glyphicon-plus-sign(ng-show='profile.achievements.rebirths', tooltip=env.t('reborn', {reLevel: "{{profile.achievements.rebirthLevel}}"}), style='margin-left:5px')
|
||||
|
|
|
|||
|
|
@ -10,19 +10,19 @@
|
|||
span.glyphicon.glyphicon-ok
|
||||
=env.t('tasks')
|
||||
span(ng-show='$state.includes("tasks")', ui-sref='options')
|
||||
span.glyphicon.glyphicon.icon-wrench
|
||||
span.glyphicon.glyphicon-wrench
|
||||
=env.t('options')
|
||||
li
|
||||
a.task-action-btn.tile.solid(href="http://habitrpg.wikia.com/wiki/FAQ", target='_blank')
|
||||
span.glyphicon.glyphicon.icon-book
|
||||
span.glyphicon.glyphicon.-book
|
||||
=env.t('FAQ')
|
||||
li
|
||||
a.task-action-btn.tile.solid(href="https://vimeo.com/57654086", target='_blank')
|
||||
span.glyphicon.glyphicon.icon-film
|
||||
span.glyphicon.glyphicon-film
|
||||
=env.t('tutorials')
|
||||
li
|
||||
a.task-action-btn.tile.solid(ng-click='User.sync()')
|
||||
span.glyphicon.glyphicon.icon-refresh
|
||||
span.glyphicon.glyphicon-refresh
|
||||
=env.t('sync')
|
||||
li
|
||||
a.task-action-btn.tile.solid(ng-click='logout()')
|
||||
|
|
|
|||
|
|
@ -37,11 +37,8 @@ script(id='modals/achievements/contributor.html', type='text/ng-template')
|
|||
.modal-body
|
||||
.npc_justin.float-left
|
||||
p
|
||||
| {{user.profile.name}},
|
||||
=env.t('contribText1')
|
||||
| {{user.contributor.level}}
|
||||
=env.t('contribText2')
|
||||
a(href='http://habitrpg.wikia.com/wiki/Contributor_Rewards' target='_blank')=env.t('contribText3')
|
||||
!=env.t('contribModal', {name: "{{user.profile.name}}", level: "{{user.contributor.level}}"})
|
||||
a(href='http://habitrpg.wikia.com/wiki/Contributor_Rewards' target='_blank')=env.t('contribLink')
|
||||
.modal-footer
|
||||
button.btn.btn-default.cancel(ng-click='set({"flags.contributor":false})')=env.t('ok')
|
||||
|
||||
|
|
@ -51,10 +48,6 @@ script(id='modals/achievements/rebirth.html', type='text/ng-template')
|
|||
h3=env.t('modalAchievement')
|
||||
.modal-body
|
||||
.achievement.achievement-sun
|
||||
=env.t('rebirthAchievement1')
|
||||
| {{user.achievements.rebirths}}
|
||||
=env.t('rebirthAchievement2')
|
||||
| {{user.achievements.rebirthLevel}}.
|
||||
=env.t('rebirthAchievement3')
|
||||
=env.t('rebirthAchievement', {number: "{{user.achievements.rebirths}}", level: "{{user.achievements.rebirthLevel}}"})
|
||||
.modal-footer
|
||||
button.btn.btn-default.cancel(ng-click='modals.achievements.rebirth = false')=env.t('ok')
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ script(id='modals/buyGems.html', type='text/ng-template')
|
|||
.buy-gems
|
||||
include ../gems
|
||||
.well
|
||||
h3 Buy Gems
|
||||
h3=env.t('buyGems')
|
||||
table.table
|
||||
tr
|
||||
td
|
||||
|
|
@ -15,7 +15,8 @@ script(id='modals/buyGems.html', type='text/ng-template')
|
|||
span.dashed-underline(popover=env.t('donateText3'),popover-trigger='mouseenter',popover-placement='right')
|
||||
=env.t('donateText2')
|
||||
tr
|
||||
td.alert.alert-info=env.t('fiveUSD1')
|
||||
td.alert.alert-info $5
|
||||
=env.t('USD')
|
||||
tr
|
||||
td
|
||||
.btn.btn-primary(ng-click='showStripe()',style='margin-left:10px;')=env.t('payWithCard')
|
||||
|
|
|
|||
|
|
@ -30,24 +30,50 @@ script(type='text/ng-template', id='modals/newStuff.html')
|
|||
table.table.table-striped
|
||||
tr
|
||||
td
|
||||
h5 Group Plans
|
||||
p We've begun adding <a href='/static/plans' targte='_blank'>plans for groups</a> (parents, teachers, health & wellness administrators, etc). These plans will provide group leaders with more control, privacy, security, and support. Currently only the Organization Plan (top tier) is available (due to tech limitations believe it or not), and we'll be releasing the Family & Group plans later. <a href='/static/plans' targte='_blank'>Click the "Contact Us" buttons</a> if you're interested, and we'll keep you updated!
|
||||
h5 Happy Birthday, HabitRPG!
|
||||
p The fair land of Habitica is two years old on January 31st! The NPCs are celebrating in style, and it looks like some of the staff is, too! Won't you join in?
|
||||
tr
|
||||
td
|
||||
h5 Individual Plan
|
||||
p We've introduced a $5/mo basic subscription plan. It comes with a number of perks, which <a href='https://habitrpg.com/#/options/settings/subscription' target='_blank'>you can see here</a>. We'll likely add more benefits over time, follow <a href='https://trello.com/c/euDUHPpn/371-basic-plan-subscription' target='_blank'>the conversation here</a>.
|
||||
h5 Absurd Party Robtes
|
||||
p As part of the festivities, Absurd Party Robes are available free of charge in the Item Store! Swath yourself in those silly garbs and don your matching hats to celebrate this momentous day.
|
||||
tr
|
||||
td
|
||||
h5 Perfect Day Achievement
|
||||
p Now when you complete all your dailies, you stack this badge, plus and additional perk: you get a +(level/2) buff to all stats!
|
||||
h5 Delicious Cake
|
||||
p What would a birthday be without birthday cake in a myriad of flavors? Of course, pets are very picky, but luckily Lemoness and her team of bakers have plenty of slices to go around. Mmm, delicious!
|
||||
tr
|
||||
td
|
||||
h5 <a href='/#/options/groups/challenges/95533e05-1ff9-4e46-970b-d77219f199e9' target='_blank'>Spread The Word Challenge</a> Update
|
||||
p We have 2k+ submissions, holy cow! Great job everyone! Now, we need to go through these manually, so it will take a few days to a couple weeks to process. The challenge will stay open until we're done choosing our winners, but be sure to edit the To-Do with your submission URL before 1/31, as that's the cut-off date for processing. We'll send a Tweet out when the winner has been selected, so follow <a href='https://twitter.com/habitrpg' target='_blank'>@habitrpg</a> and stay tuned.
|
||||
h5 Last Day of Winter Wonderland Event
|
||||
p Also, just a reminder - January 31st is the final day of the Winter Wonderland event, so it's your last day to get the Limited Edition Winter Hair Colors, the Winter Outfits, the snowballs, and the Trapper Santa and Find the Cub quest scrolls. Remember that mid-progress Trapper Santa and Find the Cub quests will not abort, nor will you lose your scrolls - they will simply be removed from Alexander's marketplace. We hope that you've had a wonderful winter!
|
||||
tr
|
||||
td
|
||||
h5 Birthday Bash Badge
|
||||
p Finally, to commemorate the fun, all party participants receive a birthday badge! Polish it frequently and wear it fondly.
|
||||
|
||||
p Thanks so much for being a part of the HabitRPG community. We love you guys, and we can't wait to have you at our sides in the upcoming year! Stay productive, Habiteers, and have an awesome day.
|
||||
|
||||
p.muted By @lemoness
|
||||
|
||||
small.muted 01/28/2014
|
||||
small.muted 01/30/2014
|
||||
|
||||
hr
|
||||
h5 01/28/2014
|
||||
table.table.table-striped
|
||||
tr
|
||||
td
|
||||
h5 Group Plans
|
||||
p We've begun adding <a href='/static/plans' targte='_blank'>plans for groups</a> (parents, teachers, health & wellness administrators, etc). These plans will provide group leaders with more control, privacy, security, and support. Currently only the Organization Plan (top tier) is available (due to tech limitations believe it or not), and we'll be releasing the Family & Group plans later. <a href='/static/plans' targte='_blank'>Click the "Contact Us" buttons</a> if you're interested, and we'll keep you updated!
|
||||
tr
|
||||
td
|
||||
h5 Individual Plan
|
||||
p We've introduced a $5/mo basic subscription plan. It comes with a number of perks, which <a href='https://habitrpg.com/#/options/settings/subscription' target='_blank'>you can see here</a>. We'll likely add more benefits over time, follow <a href='https://trello.com/c/euDUHPpn/371-basic-plan-subscription' target='_blank'>the conversation here</a>.
|
||||
tr
|
||||
td
|
||||
h5 Perfect Day Achievement
|
||||
p Now when you complete all your dailies, you stack this badge, plus and additional perk: you get a +(level/2) buff to all stats!
|
||||
tr
|
||||
td
|
||||
h5 <a href='/#/options/groups/challenges/95533e05-1ff9-4e46-970b-d77219f199e9' target='_blank'>Spread The Word Challenge</a> Update
|
||||
p We have 1k+ submissions, holy cow! Great job everyone! Now, we need to go through these manually, so it will take a few days to a couple weeks to process. The challenge will stay open until we're done choosing our winners, but be sure to edit the To-Do with your submission URL before 1/31, as that's the cut-off date for processing. We'll send a Tweet out when the winner has been selected, so follow <a href='https://twitter.com/habitrpg' target='_blank'>@habitrpg</a> and stay tuned.
|
||||
|
||||
hr
|
||||
h5 01/25/2014
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
div(modal='modals.reset')
|
||||
script(type='text/ng-template', id='modals/reset.html')
|
||||
.modal-header
|
||||
h3=env.t('resetaccount')
|
||||
h3=env.t('resetAccount')
|
||||
.modal-body
|
||||
p=env.t('resetText1')
|
||||
p=env.t('resetText2')
|
||||
|
|
@ -67,11 +67,9 @@ script(type='text/ng-template', id='modals/restore.html')
|
|||
//div(modal='modals.delete')
|
||||
script(type='text/ng-template', id='modals/delete.html')
|
||||
.modal-header
|
||||
h3=env.t('deleteaccount')
|
||||
h3=env.t('deleteAccount')
|
||||
.modal-body
|
||||
p=env.t('deleteText1')
|
||||
strong=env.t('deleteText2')
|
||||
=env.t('deleteText3')
|
||||
p!=env.t('deleteText')
|
||||
p
|
||||
input(type='text', ng-model='_deleteAccount')
|
||||
.modal-footer
|
||||
|
|
|
|||
|
|
@ -123,3 +123,11 @@
|
|||
small
|
||||
=env.t('annoyingFriendsText', {snowballs: "{{profile.achievements.snowball}}"})
|
||||
hr
|
||||
|
||||
div(ng-if='profile.achievements.habitBirthday')
|
||||
.achievement.achievement-habitBirthday
|
||||
h5=env.t('habitBirthday')
|
||||
small
|
||||
=env.t('habitBirthdayText')
|
||||
hr
|
||||
|
||||
|
|
|
|||
|
|
@ -93,9 +93,7 @@ li(bindonce='list', bo-id='"task-"+task.id', ng-repeat='task in obj[list.type+"s
|
|||
a(ng-click='unlink(task, "remove-all")')=env.t('removeThem')
|
||||
div(ng-if='task.challenge.broken=="CHALLENGE_CLOSED"')
|
||||
p
|
||||
=env.t('challengeCompleted1')
|
||||
span.badge {{task.challenge.winner}}
|
||||
=env.t('challengeCompleted2')
|
||||
!=env.t('challengeCompleted', {user: "{{task.challenge.winner}}"})
|
||||
p
|
||||
a(ng-click='unlink(task, "keep-all")')=env.t('keepThem')
|
||||
| |
|
||||
|
|
@ -117,7 +115,7 @@ li(bindonce='list', bo-id='"task-"+task.id', ng-repeat='task in obj[list.type+"s
|
|||
legend.option-title
|
||||
=env.t('checklist')
|
||||
|
|
||||
span.glyphicon.glyphicon.icon-question-sign(popover=env.t('checklistText'),popover-trigger='mouseenter',popover-placement='bottom')
|
||||
span.glyphicon.glyphicon-question-sign(popover=env.t('checklistText'),popover-trigger='mouseenter',popover-placement='bottom')
|
||||
ul.unstyled
|
||||
li(ng-repeat='item in task.checklist')
|
||||
a.pull-right.checklist-icons(ng-click='removeChecklistItem(task,$event,$index,true)')
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ block vars
|
|||
- var menuItem = 'about'
|
||||
|
||||
block title
|
||||
title About
|
||||
title=env.t('companyAbout')
|
||||
|
||||
block content
|
||||
.row
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ block vars
|
|||
- var layoutEnv = env
|
||||
- var menuItem = 'contact'
|
||||
block title
|
||||
title Contact
|
||||
title=env.t('contact')
|
||||
block content
|
||||
// Probably just add linkks to the respective contact locations?
|
||||
// Bugs? Github
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@ html
|
|||
.collapse.navbar-collapse(collapse="isNavbarCollapsed")
|
||||
ul.nav.navbar-nav
|
||||
li(class='#{menuItem=="about" ? "active" : ""}')
|
||||
a(href='/static/about') Learn More
|
||||
a(href='/static/about')=env.t('learnMore')
|
||||
li
|
||||
a(href='http://blog.habitrpg.com/') Blog
|
||||
a(href='http://blog.habitrpg.com/')=env.t('companyBlog')
|
||||
li(class='#{menuItem=="plans" ? "active" : ""}')
|
||||
a(href='/static/plans') Plans
|
||||
a(href='/static/plans')=env.t('groupPlans')
|
||||
//li(class='#{menuItem=="contact" ? "active" : ""}')
|
||||
a(href='/static/contact') Contact
|
||||
a(href='/static/contact')=env.t('contact')
|
||||
|
||||
button#header-play-button.btn.btn-primary.navbar-btn.navbar-right(ng-click='playButtonClick()')=env.t('playButton')
|
||||
|
||||
|
|
|
|||
|
|
@ -5,29 +5,37 @@ block vars
|
|||
- var menuItem = 'plans'
|
||||
|
||||
block title
|
||||
title Group Plans
|
||||
title=env.t('groupPlans')
|
||||
|
||||
block content
|
||||
.row
|
||||
.col-md-12
|
||||
h2 Group Plans
|
||||
h2=env.t('groupPlans')
|
||||
|
||||
p For individuals, HabitRPG is free to play. Even for small interest groups, free (or cheap) <a href='http://habitrpg.wikia.com/wiki/Guilds' target='_blank'>guilds</a> and <a href='http://habitrpg.wikia.com/wiki/Challenges' target='_blank'>challenges</a> can be used to motivate participants in behavioral modification. Think writing groups, art challenges, and more.
|
||||
p But some group leaders will want more control, privacy, security, and support. Examples of such groups are families, health and wellness groups, employee groups, and more. These plans provide private instances of HabitRPG for your group or organization, secure and independent of <a href='http://habitrpg.wikia.com/wiki/Habitica' target='_blank'>Habitica</a>. See below for additional plan perks, and contact us for more information!
|
||||
p
|
||||
=env.t('indivPlan1')
|
||||
a(href='http://habitrpg.wikia.com/wiki/Guilds' target='_blank')=env.t('guilds')
|
||||
= ' ' + env.t('and') + ' '
|
||||
a(href='http://habitrpg.wikia.com/wiki/Challenges' target='_blank')=env.t('challenges')
|
||||
=env.t('indivPlan2')
|
||||
p
|
||||
=env.t('groupText1')
|
||||
a(href='http://habitrpg.wikia.com/wiki/Habitica' target='_blank')=env.t('habitica')
|
||||
|.
|
||||
=env.t('groupText2')
|
||||
|
||||
.subscription-features
|
||||
table.table.table-striped
|
||||
thead
|
||||
tr
|
||||
th.feature-name
|
||||
th.feature-name.muted Family (Coming Soon)
|
||||
th.feature-name.muted Group (Coming Soon)
|
||||
th.feature-name Organization
|
||||
th.feature-name.muted=env.t('planFamily')
|
||||
th.feature-name.muted=env.t('planGroup')
|
||||
th.feature-name=env.t('organization')
|
||||
tbody
|
||||
tr
|
||||
th
|
||||
span.dashed-underline(popover="Members of the organization participate outside of HabitRPG proper, providing focus for your participants.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Private Organization
|
||||
span.dashed-underline(popover=env.t('organizationSubText'),popover-trigger='mouseenter',popover-placement='right')=env.t('organizationSub')
|
||||
td.muted
|
||||
span.glyphicon.glyphicon-ok
|
||||
td.muted
|
||||
|
|
@ -36,53 +44,46 @@ block content
|
|||
span.glyphicon.glyphicon-ok
|
||||
tr
|
||||
th
|
||||
span.dashed-underline(popover="Dedicated Hosting: you get your own database and server hosted by HabitRPG, or optionally we'll install it in your organization's network. If not checked, the plan uses \"Shared Hosting\": your organization uses the same database as HabitRPG proper while performing independently Habitica. Your members are shielded from Tavern & Guilds, but still on the same server/database.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Dedicated Hosting
|
||||
span.dashed-underline(popover=env.t('dedicatedHostText'),popover-trigger='mouseenter',popover-placement='right')=env.t('dedicatedHost')
|
||||
td
|
||||
td
|
||||
td
|
||||
span.glyphicon.glyphicon-ok
|
||||
tr
|
||||
th
|
||||
span.dashed-underline(popover="We can optionally give you your own domain for the installation.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Custom Domain
|
||||
span.dashed-underline(popover=env.t('customDomainText'),popover-trigger='mouseenter',popover-placement='right')=env.t('customDomain')
|
||||
td
|
||||
td
|
||||
td
|
||||
span.glyphicon.glyphicon-ok
|
||||
tr
|
||||
th
|
||||
span.dashed-underline(popover="The maximum number of players in your private organization.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Max Participants
|
||||
span.dashed-underline(popover=env.t('maxPlayersText'),popover-trigger='mouseenter',popover-placement='right')=env.t('maxPlayers')
|
||||
td.muted 10
|
||||
td.muted 75
|
||||
td Unlimited
|
||||
td=env.t('unlimited')
|
||||
tr
|
||||
th
|
||||
span.dashed-underline(popover="First to be provided for with support.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Priority Support On Tickets & Hosting
|
||||
span.dashed-underline(popover=env.t('priSupportText'),popover-trigger='mouseenter',popover-placement='right')=env.t('priSupport')
|
||||
td
|
||||
td
|
||||
td
|
||||
span.glyphicon.glyphicon-ok
|
||||
tr
|
||||
th
|
||||
span.dashed-underline(popover="We will provide support for training, bugs, installation, and feature requests. Additional hours available at an hourly rate.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Support Hours / Month
|
||||
span.dashed-underline(popover=env.t('timeSupportText'),popover-trigger='mouseenter',popover-placement='right')=env.t('timeSupport')
|
||||
td.muted -
|
||||
td.muted 5
|
||||
td 10
|
||||
tr
|
||||
th
|
||||
h5 Game features:
|
||||
h5=env.t('gameFeatures') + ':'
|
||||
ul
|
||||
li Ads disabled for members
|
||||
li=env.t('gameNoAds')
|
||||
li
|
||||
span.dashed-underline(popover="Members will be able to purchase gems with gold, meaning none of your participants need to buy anything with real money.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Gems purchasable with gold
|
||||
span.dashed-underline(popover=env.t('gold2GemText'),popover-trigger='mouseenter',popover-placement='right')=env.t('gold2Gem')
|
||||
li
|
||||
span.dashed-underline(popover="We will provide the organization leaders with as many gems as they need, for things like challenge prizes, guild-creation, etc.",popover-trigger='mouseenter',popover-placement='right')
|
||||
| Infinite leader gems
|
||||
span.dashed-underline(popover=env.t('infiniteGemText'),popover-trigger='mouseenter',popover-placement='right')=env.t('infiniteGem')
|
||||
td.muted
|
||||
span.glyphicon.glyphicon-ok
|
||||
td.muted
|
||||
|
|
@ -93,8 +94,8 @@ block content
|
|||
th
|
||||
//| Price
|
||||
td
|
||||
a.btn(href='https://docs.google.com/forms/d/17torT7OlxgtbHAPdBDRQNG2lTdIQyrGXk4O2YXvUMig/viewform',popover="Plan not yet available, but click to contact us and we'll keep you updated.",popover-placement='right',popover-trigger='mouseenter') Contact Us
|
||||
a.btn.muted(href='https://docs.google.com/forms/d/17torT7OlxgtbHAPdBDRQNG2lTdIQyrGXk4O2YXvUMig/viewform',popover=env.t('notYetPlan'),popover-placement='right',popover-trigger='mouseenter')=env.t('contactUs')
|
||||
td
|
||||
a.btn.muted(href='https://docs.google.com/forms/d/17torT7OlxgtbHAPdBDRQNG2lTdIQyrGXk4O2YXvUMig/viewform',popover="Plan not yet available, but click to contact us and we'll keep you updated.",popover-placement='right',popover-trigger='mouseenter') Contact Us
|
||||
a.btn.muted(href='https://docs.google.com/forms/d/17torT7OlxgtbHAPdBDRQNG2lTdIQyrGXk4O2YXvUMig/viewform',popover=env.t('notYetPlan'),popover-placement='right',popover-trigger='mouseenter')=env.t('contactUs')
|
||||
td
|
||||
a.btn.btn-primary(href='https://docs.google.com/forms/d/12Jqj_8f3oQS0B3ZUHewHbK61uLjBdzBeB0zyEqB9lxM/viewform') Contact Us
|
||||
a.btn.btn-primary(href='https://docs.google.com/forms/d/12Jqj_8f3oQS0B3ZUHewHbK61uLjBdzBeB0zyEqB9lxM/viewform')=env.t('contactUs')
|
||||
Loading…
Reference in a new issue