Merge pull request #4236 from HabitRPG/share-avatar

Share avatar
This commit is contained in:
Tyler Renelle 2014-11-12 14:42:48 -07:00
commit 114cbe2760
13 changed files with 184 additions and 43 deletions

2
.buildpacks Normal file
View file

@ -0,0 +1,2 @@
https://github.com/heroku/heroku-buildpack-nodejs.git
https://github.com/stomita/heroku-buildpack-phantomjs.git

View file

@ -25,5 +25,11 @@
"PAYPAL_SIGNATURE": "PAYPAL_SIGNATURE", "PAYPAL_SIGNATURE": "PAYPAL_SIGNATURE",
"EMAIL_SERVER_URL": "http://example.com", "EMAIL_SERVER_URL": "http://example.com",
"EMAIL_SERVER_AUTH_USER": "user", "EMAIL_SERVER_AUTH_USER": "user",
"EMAIL_SERVER_AUTH_PASSWORD": "password" "EMAIL_SERVER_AUTH_PASSWORD": "password",
"S3":{
"bucket":"bucket",
"accessKeyId":"accessKeyId",
"secretAccessKey":"secretAccessKey"
}
} }

View file

@ -5,6 +5,7 @@
"main": "./src/server.js", "main": "./src/server.js",
"dependencies": { "dependencies": {
"async": "~0.9.0", "async": "~0.9.0",
"aws-sdk": "^2.0.25",
"bower": "~1.3.12", "bower": "~1.3.12",
"coffee-script": "1.6.x", "coffee-script": "1.6.x",
"connect-ratelimit": "0.0.7", "connect-ratelimit": "0.0.7",
@ -25,17 +26,18 @@
"grunt-nodemon": "~0.3.0", "grunt-nodemon": "~0.3.0",
"habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#develop", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#develop",
"icalendar": "git://github.com/lefnire/node-icalendar#master", "icalendar": "git://github.com/lefnire/node-icalendar#master",
"jade": "~1.7.0",
"js2xmlparser": "~0.1.2", "js2xmlparser": "~0.1.2",
"lodash": "~2.4.1", "lodash": "~2.4.1",
"jade": "~1.7.0",
"method-override": "~2.2.0", "method-override": "~2.2.0",
"mongoose": "~3.8.17",
"moment": "~2.8.3", "moment": "~2.8.3",
"mongoose": "~3.8.17",
"mongoose-id-autoinc": "~2013.7.14-4", "mongoose-id-autoinc": "~2013.7.14-4",
"nconf": "~0.6.9", "nconf": "~0.6.9",
"newrelic": "~1.11.2", "newrelic": "~1.11.2",
"nib": "~1.0.1", "nib": "~1.0.1",
"nodemailer": "~0.5.2", "nodemailer": "~0.5.2",
"pageres": "^1.0.1",
"passport": "~0.2.1", "passport": "~0.2.1",
"passport-facebook": "Fonger/passport-facebook#a8f98adcddad99caa9a918bc7b76462c92c5c9fd", "passport-facebook": "Fonger/passport-facebook#a8f98adcddad99caa9a918bc7b76462c92c5c9fd",
"paypal-express-checkout": "git://github.com/HabitRPG/node-paypal-express-checkout#habitrpg", "paypal-express-checkout": "git://github.com/HabitRPG/node-paypal-express-checkout#habitrpg",
@ -43,6 +45,7 @@
"paypal-recurring": "git://github.com/jaybryant/paypal-recurring#656b496f43440893c984700191666a5c5c535dca", "paypal-recurring": "git://github.com/jaybryant/paypal-recurring#656b496f43440893c984700191666a5c5c535dca",
"pretty-data": "git://github.com/vkiryukhin/pretty-data#master", "pretty-data": "git://github.com/vkiryukhin/pretty-data#master",
"request": "~2.44.0", "request": "~2.44.0",
"s3-upload-stream": "^1.0.6",
"stripe": "*", "stripe": "*",
"swagger-node-express": "git://github.com/lefnire/swagger-node-express#habitrpg", "swagger-node-express": "git://github.com/lefnire/swagger-node-express#habitrpg",
"universal-analytics": "~0.3.2", "universal-analytics": "~0.3.2",

View file

@ -75,6 +75,16 @@ future re: pets and whatnot, this is just temporary.
&:hover, &:focus &:hover, &:focus
background: lighten($color-herobox, 16.18%) background: lighten($color-herobox, 16.18%)
.addthis_pill_style
width: 50px !important
.addthis_native_toolbox
position: absolute
top:2px
right:2px
opacity:0
&:hover .addthis_native_toolbox
opacity:1
//Need to find a way to indicate who is leader without using background-color as it won't work when a background image is applied //Need to find a way to indicate who is leader without using background-color as it won't work when a background image is applied
//.herobox.isLeader.noBackgroundImage //.herobox.isLeader.noBackgroundImage
//background: lighten($color-herobox, 10%) //background: lighten($color-herobox, 10%)

View file

@ -39,7 +39,17 @@
// Scripts only for desktop // Scripts only for desktop
if (!window.env.IS_MOBILE) { if (!window.env.IS_MOBILE) {
// Add This // Add This
$.getScript("//s7.addthis.com/js/250/addthis_widget.js#pubid=lefnire"); //$.getScript("//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5016f6cc44ad68a4"); //FIXME why isn't this working when here? instead it's now in <head>
var addthisServices = 'facebook,twitter,googleplus,tumblr,'+window.env.BASE_URL.replace('https://','').replace('http://','');
window.addthis_config = {
services_custom:{
name: "Download",
url: window.env.BASE_URL+"/export/avatar-"+User.user._id+".png",
icon: window.env.BASE_URL+"/favicon.ico"
},
services_expanded:addthisServices,
services_compact:addthisServices
};
// Google Charts // Google Charts
$.getScript("//www.google.com/jsapi", function() { $.getScript("//www.google.com/jsapi", function() {

View file

@ -6,6 +6,21 @@ window.habitrpgStatic = angular.module('habitrpgStatic', ['notificationServices'
.constant("STORAGE_SETTINGS_ID", 'habit-mobile-settings') .constant("STORAGE_SETTINGS_ID", 'habit-mobile-settings')
.constant("MOBILE_APP", false) .constant("MOBILE_APP", false)
.controller("RootCtrl", ['$scope', '$location', '$modal', '$http', function($scope, $location, $modal, $http){
// must be #?memberId=xx, see https://github.com/angular/angular.js/issues/7239
var memberId = $location.search()['memberId'];
if (memberId) {
$http.get('/api/v2/members/'+memberId).success(function(data, status, headers, config){
$scope.profile = data;
$scope.Content = window.habitrpgShared.content;
$modal.open({
templateUrl: 'modals/member.html',
scope: $scope
});
})
}
}])
.controller("PlansCtrl", ['$rootScope', .controller("PlansCtrl", ['$rootScope',
function($rootScope) { function($rootScope) {
$rootScope.clickContact = function(){ $rootScope.clickContact = function(){

View file

@ -99,6 +99,8 @@
"bower_components/bootstrap/dist/css/bootstrap.css", "bower_components/bootstrap/dist/css/bootstrap.css",
"bower_components/css-social-buttons/css/zocial.css", "bower_components/css-social-buttons/css/zocial.css",
"bower_components/jquery-colorbox/example1/colorbox.css", "bower_components/jquery-colorbox/example1/colorbox.css",
"app.css",
"bower_components/habitrpg-shared/dist/habitrpg-shared.css",
"static.css" "static.css"
] ]
} }

View file

@ -8,6 +8,13 @@ var js2xmlparser = require("js2xmlparser");
var pd = require('pretty-data').pd; var pd = require('pretty-data').pd;
var User = require('../models/user').model; var User = require('../models/user').model;
// Avatar screenshot/static-page includes
var Pageres = require('pageres'); //https://github.com/sindresorhus/pageres
var AWS = require('aws-sdk');
AWS.config.update({accessKeyId: nconf.get("S3:accessKeyId"), secretAccessKey: nconf.get("S3:secretAccessKey")});
var s3Stream = require('s3-upload-stream')(new AWS.S3()); //https://github.com/nathanpeck/s3-upload-stream
var bucket = nconf.get("S3:bucket");
var request = require('request');
/* /*
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -83,3 +90,49 @@ expressres.jsonstring = function(obj, headers, status) {
body = pd.json(JSON.stringify(obj)); body = pd.json(JSON.stringify(obj));
return this.send(body, headers, status); return this.send(body, headers, status);
}; };
/*
------------------------------------------------------------------------
Static page and image screenshot of avatar
------------------------------------------------------------------------
*/
dataexport.avatarPage = function(req, res) {
User.findById(req.params.uuid).select('stats profile items achievements preferences backer contributor').exec(function(err, user){
res.render('avatar-static', {
title: user.profile.name,
env: _.defaults({user:user},res.locals.habitrpg)
});
})
};
dataexport.avatarImage = function(req, res, next) {
var filename = 'avatar-'+req.params.uuid+'.png';
request.head('https://'+bucket+'.s3.amazonaws.com/'+filename, function(err,response,body) {
// cache images for 10 minutes on aws, else upload a new one
if (response.statusCode==200 && moment().diff(response.headers['last-modified'], 'minutes') < 10)
return res.redirect(301, 'https://' + bucket + '.s3.amazonaws.com/' + filename);
new Pageres()//{delay:1}
.src(nconf.get('BASE_URL') + '/export/avatar-' + req.params.uuid + '.html', ['140x147'], {crop: true, filename: filename.replace('.png', '')})
.run(function (err, file) {
if (err) return next(err);
// see http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#createMultipartUpload-property
var upload = s3Stream.upload({
Bucket: bucket,
Key: filename,
ACL: "public-read",
StorageClass: "REDUCED_REDUNDANCY",
ContentType: "image/png",
Expires: +moment().add({minutes: 3})
});
upload.on('error', function (err) {
next(err);
});
upload.on('uploaded', function (details) {
res.redirect(details.Location);
});
file[0].pipe(upload);
});
})
};

View file

@ -3,11 +3,14 @@ var router = new express.Router();
var dataexport = require('../controllers/dataexport'); var dataexport = require('../controllers/dataexport');
var auth = require('../controllers/auth'); var auth = require('../controllers/auth');
var nconf = require('nconf'); var nconf = require('nconf');
var i18n = require('../i18n') var i18n = require('../i18n');
var middleware = require('../middleware.js');
/* Data export */ /* Data export */
router.get('/history.csv',auth.authWithSession,i18n.getUserLanguage,dataexport.history); //[todo] encode data output options in the data controller and use these to build routes router.get('/history.csv',auth.authWithSession,i18n.getUserLanguage,dataexport.history); //[todo] encode data output options in the data controller and use these to build routes
router.get('/userdata.xml',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.xml); router.get('/userdata.xml',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.xml);
router.get('/userdata.json',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.json); router.get('/userdata.json',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.json);
router.get('/avatar-:uuid.html', i18n.getUserLanguage, middleware.locals, dataexport.avatarPage);
router.get('/avatar-:uuid.png', i18n.getUserLanguage, middleware.locals, dataexport.avatarImage);
module.exports = router; module.exports = router;

28
views/avatar-static.jade Normal file
View file

@ -0,0 +1,28 @@
doctype html
html(ng-app="habitrpg")
head
title=title
link(rel='shortcut icon', href='#{env.getBuildUrl("favicon.ico")}?v=3')
meta(charset='utf-8')
meta(name='viewport', content='width=device-width, initial-scale=1.0')
meta(name='apple-mobile-web-app-capable', content='yes')
script(type='text/javascript').
window.env = !{JSON.stringify(env)};
!= env.getManifestFiles("app")
script(type='text/javascript').
window.habitrpg
.controller('StaticAvatarCtrl', ['$scope', function($scope){
$scope.profile = window.env.user;
}])
//webfonts
link(href='//fonts.googleapis.com/css?family=Lato:300,400,700,400italic,700italic', rel='stylesheet', type='text/css')
body(ng-cloak)
include ./shared/header/avatar
div(ng-controller='StaticAvatarCtrl')
+herobox({main:true})

View file

@ -10,6 +10,9 @@ html(ng-app="habitrpg", ng-controller="RootCtrl", ng-class='{"applying-action":a
meta(name='viewport', content='width=device-width, initial-scale=1.0') meta(name='viewport', content='width=device-width, initial-scale=1.0')
meta(name='apple-mobile-web-app-capable', content='yes') meta(name='apple-mobile-web-app-capable', content='yes')
//FIXME for some reason this won't load when in footerCtrl.js#deferredScripts()
script(type="text/javascript", src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5016f6cc44ad68a4", async="async")
script(type='text/javascript'). script(type='text/javascript').
window.env = !{JSON.stringify(env)}; window.env = !{JSON.stringify(env)};

View file

@ -7,6 +7,8 @@ mixin avatar(opts)
.character-sprites .character-sprites
.addthis_native_toolbox(ng-if='profile._id==user._id', data-url="#{env.BASE_URL}/static/front/#?memberId={{profile._id}}", data-title="Check out my HabitRPG progress!")
// Mount Body // Mount Body
if !opts.minimal if !opts.minimal
span(ng-if='profile.items.currentMount', class='Mount_Body_{{profile.items.currentMount}}') span(ng-if='profile.items.currentMount', class='Mount_Body_{{profile.items.currentMount}}')

View file

@ -8,6 +8,10 @@ block title
title=env.t('titleFront') title=env.t('titleFront')
block content block content
div(ng-controller='RootCtrl')
include ../shared/header/avatar
include ../shared/modals/members
.marketing .marketing
//we need to use something that's not jumbotron for this, but still keep it centered //we need to use something that's not jumbotron for this, but still keep it centered
//could someone write something else to make it pretty? //could someone write something else to make it pretty?