2013-06-03 03:28:19 +04:00
|
|
|
const _ = require('underscore');
|
2013-05-29 04:56:49 +04:00
|
|
|
const openbadger = require('../openbadger');
|
2013-05-30 04:12:01 +04:00
|
|
|
const db = require('../db');
|
2013-06-01 03:17:52 +04:00
|
|
|
const isLearner = require('../middleware').isLearner;
|
2013-05-29 04:56:49 +04:00
|
|
|
|
2013-05-30 04:12:01 +04:00
|
|
|
const claim = db.model('Claim');
|
2013-06-03 07:29:33 +04:00
|
|
|
const favorite = db.model('Favorite');
|
2013-06-04 22:31:38 +04:00
|
|
|
const playlist = db.model('Playlist');
|
2013-05-30 22:00:19 +04:00
|
|
|
const loggedIn = require('../middleware').loggedIn;
|
2013-06-04 19:29:45 +04:00
|
|
|
const favoriteMiddleware = _.bind(favorite.middleware, favorite);
|
2013-06-04 22:31:38 +04:00
|
|
|
const playlistMiddleware = _.bind(playlist.middleware, playlist);
|
2013-06-03 03:28:19 +04:00
|
|
|
const applications = db.model('Application');
|
2013-05-29 04:56:49 +04:00
|
|
|
|
2013-06-05 17:45:26 +04:00
|
|
|
// groupByCategory groups an array of badges by their category(s).
|
|
|
|
// Note that badges can be in multiple categories. It is up to the caller to
|
|
|
|
// handle this appropriately.
|
|
|
|
function groupByCategory(badges, categories) {
|
|
|
|
var group = _.object(_.map(categories, function(c) { return [c, []]; }));
|
|
|
|
_.each(badges, function(badge) {
|
|
|
|
_.each(badge.categories, function(c) {
|
|
|
|
if (c in group) {
|
|
|
|
group[c].push(badge);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fisher-Yates shuffle
|
|
|
|
// Adapted from http://bost.ocks.org/mike/shuffle/
|
|
|
|
function shuffle(array) {
|
|
|
|
var m = array.length, t, i;
|
|
|
|
|
|
|
|
// While there remain elements to shuffle…
|
|
|
|
while (m) {
|
|
|
|
|
|
|
|
// Pick a remaining element…
|
|
|
|
i = Math.floor(Math.random() * m--);
|
|
|
|
|
|
|
|
// And swap it with the current element.
|
|
|
|
t = array[m];
|
|
|
|
array[m] = array[i];
|
|
|
|
array[i] = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
2013-03-25 00:52:09 +04:00
|
|
|
module.exports = function (app) {
|
|
|
|
|
2013-06-05 00:54:00 +04:00
|
|
|
app.get('/claim', [
|
|
|
|
isLearner
|
|
|
|
], function (req, res, next) {
|
2013-05-29 04:56:49 +04:00
|
|
|
var claimCode = req.query.code;
|
|
|
|
var user = res.locals.user;
|
|
|
|
|
|
|
|
if (!claimCode)
|
|
|
|
return res.render('claim.html');
|
2013-05-30 00:39:24 +04:00
|
|
|
claimCode = claimCode.trim();
|
2013-05-29 04:56:49 +04:00
|
|
|
|
2013-05-30 00:39:24 +04:00
|
|
|
openbadger.getBadgeFromCode({
|
|
|
|
code: claimCode,
|
2013-05-29 04:56:49 +04:00
|
|
|
email: user.email
|
|
|
|
}, function(err, data) {
|
2013-05-29 17:29:25 +04:00
|
|
|
if (err) {
|
2013-05-30 00:39:24 +04:00
|
|
|
if (err.code === 404)
|
2013-05-29 17:29:25 +04:00
|
|
|
req.flash('error', "That claim code appears to be invalid.");
|
|
|
|
else if (err.code === 409)
|
2013-05-30 00:39:24 +04:00
|
|
|
req.flash('warn', "You have already used that claim code.");
|
2013-05-29 17:29:25 +04:00
|
|
|
else
|
2013-05-30 00:39:24 +04:00
|
|
|
req.flash('error', "A problem was encountered.");
|
|
|
|
return res.render('claim.html', {
|
|
|
|
code: claimCode
|
|
|
|
});
|
2013-05-29 17:29:25 +04:00
|
|
|
}
|
|
|
|
else {
|
2013-05-30 00:39:24 +04:00
|
|
|
return res.render('claim.html', {
|
|
|
|
code: claimCode,
|
|
|
|
badge: data.badge
|
|
|
|
});
|
2013-05-29 17:29:25 +04:00
|
|
|
}
|
2013-05-30 00:39:24 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2013-06-05 00:54:00 +04:00
|
|
|
app.post('/claim', [
|
|
|
|
isLearner
|
|
|
|
], function (req, res, next) {
|
2013-05-30 00:39:24 +04:00
|
|
|
var claimCode = req.query.code;
|
|
|
|
var user = res.locals.user;
|
|
|
|
|
2013-05-30 04:12:01 +04:00
|
|
|
claim.findOrCreate({
|
2013-05-30 00:39:24 +04:00
|
|
|
code: claimCode,
|
2013-05-30 04:12:01 +04:00
|
|
|
LearnerId: user.id
|
|
|
|
}).complete(function(err, claim) {
|
|
|
|
if (err) {
|
|
|
|
return next(err);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
claim.submit(function(err, claim){
|
|
|
|
if (err) {
|
|
|
|
if (err.code === 409)
|
|
|
|
req.flash('warn', "You already have that badge.");
|
|
|
|
else
|
|
|
|
req.flash('error', "There has been an error claiming your badge.");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (claim.state === 'waiting')
|
|
|
|
req.flash('info', "Your badge claim is awaiting approval from your parent or guardian.");
|
|
|
|
else
|
|
|
|
req.flash('success', "You've claimed a new badge!");
|
|
|
|
}
|
2013-05-30 22:00:19 +04:00
|
|
|
return res.redirect('/mybadges');
|
2013-05-30 04:12:01 +04:00
|
|
|
});
|
|
|
|
}
|
2013-05-29 04:56:49 +04:00
|
|
|
});
|
2013-03-25 00:52:09 +04:00
|
|
|
});
|
|
|
|
|
2013-05-31 20:21:00 +04:00
|
|
|
app.get('/mybadges', [
|
2013-06-02 02:32:45 +04:00
|
|
|
loggedIn,
|
2013-06-03 19:07:59 +04:00
|
|
|
openbadger.middleware('getUserBadges'),
|
2013-06-04 19:29:45 +04:00
|
|
|
favoriteMiddleware
|
2013-05-31 20:21:00 +04:00
|
|
|
], function (req, res, next) {
|
2013-05-30 22:00:19 +04:00
|
|
|
var data = req.remote;
|
2013-03-28 17:14:16 +04:00
|
|
|
|
|
|
|
res.render('user/backpack.html', {
|
2013-05-30 22:00:19 +04:00
|
|
|
items: data.badges
|
2013-03-28 17:14:16 +04:00
|
|
|
});
|
2013-03-25 00:52:09 +04:00
|
|
|
});
|
|
|
|
|
2013-05-31 20:21:00 +04:00
|
|
|
app.get('/mybadges/:id', [
|
2013-06-01 03:17:52 +04:00
|
|
|
isLearner,
|
2013-05-31 20:21:00 +04:00
|
|
|
openbadger.middleware('getUserBadge')
|
|
|
|
], function (req, res, next) {
|
|
|
|
var data = req.remote;
|
|
|
|
|
2013-06-03 01:08:54 +04:00
|
|
|
// XXX: replace with API call to openbadger
|
|
|
|
var similar = [
|
|
|
|
{
|
|
|
|
url: "/mybadges/this-badge",
|
|
|
|
image: "http://openbadger-csol.mofostaging.net/badge/image/this-badge.png",
|
|
|
|
name: "Test Badge CLM",
|
|
|
|
description: "This is a test badge!"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
url: "/mybadges/this-badge",
|
|
|
|
image: "http://openbadger-csol.mofostaging.net/badge/image/this-badge.png",
|
|
|
|
name: "Test Badge CLM",
|
|
|
|
description: "This is a test badge!"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
url: "/mybadges/this-badge",
|
|
|
|
image: "http://openbadger-csol.mofostaging.net/badge/image/this-badge.png",
|
|
|
|
name: "Test Badge CLM",
|
|
|
|
description: "This is a test badge!"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
url: "/mybadges/this-badge",
|
|
|
|
image: "http://openbadger-csol.mofostaging.net/badge/image/this-badge.png",
|
|
|
|
name: "Test Badge CLM",
|
|
|
|
description: "This is a test badge!"
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
const NSIMILAR = 4;
|
|
|
|
|
2013-05-31 20:21:00 +04:00
|
|
|
res.render('user/badge.html', {
|
2013-06-03 00:24:21 +04:00
|
|
|
badge: data.badge,
|
2013-06-03 01:08:54 +04:00
|
|
|
user: req.session.user,
|
|
|
|
similar: similar.slice(0, NSIMILAR)
|
2013-05-31 20:21:00 +04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-06-03 07:29:33 +04:00
|
|
|
app.post('/mybadges/:id/favorite', [
|
|
|
|
loggedIn,
|
2013-06-05 00:02:59 +04:00
|
|
|
openbadger.middleware('getBadge')
|
2013-06-03 07:29:33 +04:00
|
|
|
], function (req, res, next) {
|
|
|
|
var data = req.remote;
|
|
|
|
var badge = data.badge;
|
|
|
|
var user = res.locals.user;
|
2013-06-05 08:34:17 +04:00
|
|
|
var shortname = req.params.id; // assume if we got here id is valid input
|
2013-06-03 07:29:33 +04:00
|
|
|
|
2013-06-05 08:34:17 +04:00
|
|
|
favorite.favoriteBadge(user, shortname, function(err, fav) {
|
2013-06-03 07:29:33 +04:00
|
|
|
if (err) {
|
|
|
|
return next(err);
|
|
|
|
}
|
|
|
|
res.render('user/badge-favorited.html', {
|
2013-06-03 18:12:06 +04:00
|
|
|
badge: badge
|
2013-06-03 07:29:33 +04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-06-04 19:54:10 +04:00
|
|
|
app.get('/myplaylist', [
|
|
|
|
loggedIn,
|
2013-06-05 01:06:09 +04:00
|
|
|
openbadger.middleware('getAllBadges'),
|
2013-06-05 06:17:51 +04:00
|
|
|
openbadger.middleware('getUserRecommendations'),
|
2013-06-04 22:31:38 +04:00
|
|
|
playlistMiddleware
|
2013-06-04 19:54:10 +04:00
|
|
|
], function (req, res, next) {
|
2013-06-05 17:45:26 +04:00
|
|
|
|
|
|
|
// Group recommendations into STEAM categories.
|
|
|
|
// We also limit to `maxPerCat` badges per category.
|
|
|
|
var maxPerCat = 4;
|
|
|
|
var categories = openbadger.getFilters().categories.options;
|
|
|
|
var grouped = groupByCategory(req.remote.recommendations, _.pluck(categories, 'value'));
|
|
|
|
var recommended = _.object(_.map(categories, function(c) {
|
|
|
|
return [c.value, {label: c.label, badges: shuffle(grouped[c.value]).slice(0, maxPerCat)}];
|
|
|
|
}));
|
|
|
|
|
2013-06-04 19:54:10 +04:00
|
|
|
res.render('user/myplaylist.html', {
|
2013-06-04 22:31:38 +04:00
|
|
|
user: res.locals.user,
|
2013-06-05 17:45:26 +04:00
|
|
|
recommended: recommended,
|
2013-06-04 22:31:38 +04:00
|
|
|
playlist: req.playlist
|
2013-06-04 19:54:10 +04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-06-05 21:51:24 +04:00
|
|
|
// This view supports both adding a badge to the playlist and removing a badge
|
|
|
|
// from the playlist. It dispatches on which action by the HTTP method, which is
|
|
|
|
// multiplexed onto POST -- the `_method` param, if present, overrides the
|
|
|
|
// HTTP method. (This enables DELETEs from regular browser form submissions.)
|
2013-06-05 00:02:59 +04:00
|
|
|
app.post('/myplaylist', [
|
|
|
|
loggedIn
|
|
|
|
], function (req, res, next) {
|
2013-06-05 21:51:24 +04:00
|
|
|
var method = req.body._method ? req.body._method : "POST";
|
|
|
|
|
|
|
|
var actions = {
|
|
|
|
POST: _.bind(playlist.add, playlist),
|
|
|
|
DELETE: _.bind(playlist.remove, playlist)
|
|
|
|
};
|
|
|
|
|
2013-06-05 00:02:59 +04:00
|
|
|
var user = res.locals.user;
|
2013-06-05 08:34:17 +04:00
|
|
|
var shortname = req.body.shortname;
|
2013-06-05 21:51:24 +04:00
|
|
|
|
|
|
|
actions[method](user, shortname, function(err) {
|
2013-06-05 00:02:59 +04:00
|
|
|
if (err) next(err);
|
|
|
|
res.redirect('/myplaylist');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-06-02 23:10:26 +04:00
|
|
|
app.get('/myapplications', [
|
2013-06-05 00:54:00 +04:00
|
|
|
isLearner
|
2013-06-02 23:10:26 +04:00
|
|
|
], function (req, res, next) {
|
2013-06-03 03:28:19 +04:00
|
|
|
var user = req.session.user;
|
2013-06-04 23:03:35 +04:00
|
|
|
applications.findAll({where: ['LearnerId = ? AND State != ?', user.id, 'accepted']}).success(function (applications) {
|
2013-06-03 07:33:03 +04:00
|
|
|
openbadger.getBadges(function (err, data) {
|
|
|
|
_.each(applications, function(app) {
|
|
|
|
_.extend(app, _.findWhere(data.badges, {id: app.badgeId}));
|
2013-06-03 06:28:56 +04:00
|
|
|
});
|
2013-06-03 03:28:19 +04:00
|
|
|
res.render('user/applications.html', {
|
2013-06-03 07:33:03 +04:00
|
|
|
items: _.map(applications, function(badge) {
|
2013-06-03 06:28:56 +04:00
|
|
|
badge.url = '/myapplications/' + badge.id;
|
2013-06-03 03:28:19 +04:00
|
|
|
return badge;
|
|
|
|
})
|
|
|
|
});
|
|
|
|
});
|
2013-06-02 23:10:26 +04:00
|
|
|
});
|
|
|
|
});
|
2013-06-05 00:54:00 +04:00
|
|
|
|
2013-06-04 06:58:16 +04:00
|
|
|
app.get('/myapplications/:id', [
|
2013-06-05 00:54:00 +04:00
|
|
|
isLearner
|
2013-06-04 06:58:16 +04:00
|
|
|
], function (req, res, next) {
|
|
|
|
var user = req.session.user;
|
2013-06-04 08:22:20 +04:00
|
|
|
openbadger.getBadge({id: req.params.id}, function(err, data) {
|
|
|
|
var badge = data.badge;
|
|
|
|
applications.find({where: {LearnerId: user.id, BadgeId: req.params.id}}).success(function (application) {
|
|
|
|
res.render('user/application.html', {
|
|
|
|
badge: _.extend(badge, application)
|
|
|
|
});
|
2013-06-04 06:58:16 +04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2013-06-02 23:10:26 +04:00
|
|
|
|
2013-03-28 20:42:32 +04:00
|
|
|
app.get('/favorites/:view?', function (req, res, next) {
|
|
|
|
var badge = {
|
|
|
|
thumbnail: '/media/images/badge.png',
|
|
|
|
description: 'Badge blah in voluptate velit...',
|
|
|
|
url: '/badges/ae784f'
|
|
|
|
};
|
2013-03-28 17:14:16 +04:00
|
|
|
|
2013-03-28 20:42:32 +04:00
|
|
|
var org = {
|
|
|
|
thumbnail: '/media/images/org.png',
|
2013-06-02 02:32:45 +04:00
|
|
|
description: 'Organization blah irure...',
|
|
|
|
url: '/orgs/some-organization'
|
2013-03-28 20:42:32 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
var program = {
|
|
|
|
thumbnail: '/media/images/program.png',
|
|
|
|
description: 'Program blah sed eiusmod...',
|
|
|
|
url: '/programs/ae784f'
|
|
|
|
};
|
|
|
|
|
|
|
|
var view = req.params.view,
|
|
|
|
favorites = [badge, org, badge, program];
|
|
|
|
|
|
|
|
switch (view) {
|
|
|
|
case 'badges':
|
|
|
|
favorites = [badge, badge]; break;
|
|
|
|
case 'programs':
|
|
|
|
favorites = [program];
|
|
|
|
case 'orgs':
|
|
|
|
favorites = [org]; break;
|
|
|
|
default:
|
|
|
|
view = null;
|
2013-03-28 17:14:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
res.render('user/bookmarks.html', {
|
2013-03-28 20:42:32 +04:00
|
|
|
items: favorites,
|
|
|
|
view: view
|
2013-03-28 17:14:16 +04:00
|
|
|
})
|
2013-03-27 22:00:30 +04:00
|
|
|
})
|
|
|
|
|
2013-06-03 07:29:33 +04:00
|
|
|
};
|