Basic badge sharing and privacy control

This commit is contained in:
Mike Larsson 2013-06-13 09:38:01 -04:00
Родитель 11c582c9be
Коммит 8485adb25b
4 изменённых файлов: 210 добавлений и 4 удалений

Просмотреть файл

@ -1,4 +1,5 @@
const _ = require('underscore');
const async = require('async');
const openbadger = require('../openbadger');
const db = require('../db');
const isLearner = require('../middleware').isLearner;
@ -6,6 +7,7 @@ const isLearner = require('../middleware').isLearner;
const claim = db.model('Claim');
const favorite = db.model('Favorite');
const playlist = db.model('Playlist');
const shareToken = db.model('ShareToken');
const favoriteMiddleware = _.bind(favorite.middleware, favorite);
const playlistMiddleware = _.bind(playlist.middleware, playlist);
const applications = db.model('Application');
@ -129,6 +131,7 @@ module.exports = function (app) {
isLearner,
openbadger.middleware('getUserBadge')
], function (req, res, next) {
var user = req.session.user;
var data = req.remote;
// XXX: replace with API call to openbadger
@ -161,13 +164,100 @@ module.exports = function (app) {
const NSIMILAR = 4;
res.render('user/badge.html', {
badge: data.badge,
user: req.session.user,
similar: similar.slice(0, NSIMILAR)
if (user.underage) {
return res.render('user/badge.html', {
badge: data.badge,
user: req.session.user,
similar: similar.slice(0, NSIMILAR),
share: false
});
}
else {
async.waterfall([
function getToken (callback) {
shareToken.findOrCreate({
shortName: data.badge.shortname,
email: user.email
}).complete(callback);
},
function ensureTokenValue (token, callback) {
if (!token.token)
token.generateToken(callback);
else
callback(null, token);
}
], function renderPage (err, shareToken) {
var share = false;
if (shareToken) {
share = shareToken.values;
share.url = shareToken.getUrl();
share.toggleUrl = shareToken.getToggleUrl();
}
return res.render('user/badge.html', {
badge: data.badge,
user: req.session.user,
similar: similar.slice(0, NSIMILAR),
share: share
});
});
}
});
app.get('/share/:token', function (req, res, next) {
var token = req.params.token;
shareToken.find({
token: token
}).complete(function (err, token) {
if (err)
return next(err);
if (!token.shared)
return next('Invalid share token');
openbadger.getUserBadge({
id: token.shortName,
email: token.email
}, function (err, data) {
if (err)
return next(err);
return res.render('user/badge-share.html', {
badge: data.badge,
user: {
email: token.email
}
});
});
});
});
app.get('/share/toggle/:token', [
isLearner
], function (req, res, next) {
var token = req.params.token;
var user = req.session.user;
shareToken.find({
token: token
}).complete(function (err, token) {
if (err)
return next(err);
if (token.email !== user.email)
return next('User does not own token');
token.toggle(function (err) {
if (err)
return next(err);
return res.redirect('/mybadges/' + token.shortName);
});
});
});
app.post('/mybadges/:id/favorite', [
isLearner,
openbadger.middleware('getBadge')

69
models/shareToken.js Normal file
Просмотреть файл

@ -0,0 +1,69 @@
var db = require('../db');
var crypto = require('crypto');
var url = require('url');
const CSOL_HOST = process.env.CSOL_HOST;
function md5(value) {
return (
crypto
.createHash('md5')
.update(value)
.digest('hex')
);
}
function urlgen(value) {
const nonce = Math.random() * 0x10000000;
return md5('' + value + Date.now() + nonce);
}
module.exports = {
properties: {
token: {
type: db.type.STRING,
allowNull: true,
unique: true
},
email: {
type: db.type.STRING,
allowNull: false,
validate: {
isEmail: true
}
},
shortName: {
type: db.type.STRING,
allowNull: false
},
shared: {
type: db.type.BOOLEAN,
allowNull: false,
defaultValue: false
}
},
relationships: [
{
model: 'Learner',
type: 'hasMany'
}
],
instanceMethods: {
getUrl: function () {
return url.resolve(CSOL_HOST, 'share/' + this.token);
},
getToggleUrl: function () {
return url.resolve(CSOL_HOST, 'share/toggle/' + this.token);
},
generateToken: function (callback) {
this.updateAttributes({
token: urlgen(this.email + this.shortName)
}).complete(callback);
},
toggle: function (callback) {
this.updateAttributes({
shared: !this.shared
}).complete(callback);
}
}
};

Просмотреть файл

@ -0,0 +1,30 @@
{% extends 'layout.html' %}
{% set navitem = 'backpack' %}
{% set pageTitle = badge.name %}
{% block content %}
<div class="row">
<div class="span4">
<img src="{{ badge.image }}">
</div>
<div class="span8">
<p>
<b>Issued by:</b>
{% if badge.program.issuer.url %}
<a href="{{ badge.program.issuer.url }}">
{% endif %}
{{ badge.program.issuer.name }}
{% if badge.program.issuer.url %}
</a>
{% endif %}
</p>
<p>
<b>Issued to:</b> {{ user.email }}
</p>
<h3>What is this badge about?</h3>
<p class="description">{{ badge.description }}</p>
</div>
</div>
{% endblock %}

Просмотреть файл

@ -3,6 +3,23 @@
{% set pageTitle = badge.name %}
{% block content %}
{% if share %}
<div class="row share">
<div class="span12">
{% if share.shared %}
<p class="muted">
Public url: <a href="{{share.url}}">{{share.url}}</a>
<a class="share-toggle" href="{{share.toggleUrl}}">Make it private.</a>
</p>
{% else %}
<p class="muted">
This page is private.
<a class="share-toggle" href="{{share.toggleUrl}}">Make it public.</a>
</p>
{% endif %}
</div>
</div>
{% endif %}
<div class="row">
<div class="span4">
<img src="{{ badge.image }}">