2013-05-28 23:46:24 +04:00
|
|
|
var async = require('async');
|
2013-05-31 18:25:04 +04:00
|
|
|
var aestimia = require('../aestimia');
|
2013-05-13 22:53:01 +04:00
|
|
|
var badger = require('../openbadger');
|
2013-05-22 18:37:54 +04:00
|
|
|
var db = require('../db');
|
2013-05-28 23:46:24 +04:00
|
|
|
var errors = require('../lib/errors');
|
|
|
|
var mime = require('mime');
|
|
|
|
var _ = require('underscore');
|
2013-05-22 18:37:54 +04:00
|
|
|
|
|
|
|
var applications = db.model('Application');
|
2013-05-28 23:46:24 +04:00
|
|
|
var evidence = db.model('Evidence');
|
2013-05-08 18:14:31 +04:00
|
|
|
|
2013-03-25 00:52:09 +04:00
|
|
|
module.exports = function (app) {
|
|
|
|
|
2013-03-28 17:14:16 +04:00
|
|
|
function getFilters() {
|
|
|
|
var filters = [],
|
|
|
|
requested;
|
2013-03-25 00:52:09 +04:00
|
|
|
|
2013-03-28 17:14:16 +04:00
|
|
|
if (arguments.length) {
|
|
|
|
requested = Array.prototype.splice.call(arguments, 0);
|
|
|
|
} else {
|
2013-03-28 20:42:32 +04:00
|
|
|
requested = ['categories', 'grouped_programs', 'ages'];
|
2013-03-28 17:14:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
requested.forEach(function(filter) {
|
|
|
|
switch (filter) {
|
2013-03-28 20:42:32 +04:00
|
|
|
case 'categories':
|
|
|
|
case 'category':
|
2013-03-28 17:14:16 +04:00
|
|
|
filters.push({
|
2013-03-28 20:42:32 +04:00
|
|
|
name: 'category',
|
|
|
|
label: 'Category',
|
2013-03-28 17:14:16 +04:00
|
|
|
options: {
|
|
|
|
science: 'Science',
|
|
|
|
technology: 'Technology',
|
|
|
|
engineering: 'Engineering',
|
|
|
|
art: 'Art',
|
|
|
|
math: 'Math'
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'orgs':
|
|
|
|
case 'org':
|
|
|
|
filters.push({
|
|
|
|
name: 'org',
|
2013-06-02 02:32:45 +04:00
|
|
|
label: 'Organization',
|
2013-03-28 20:42:32 +04:00
|
|
|
options: {
|
|
|
|
'org1': 'Org 1',
|
|
|
|
'org2': 'Org 2',
|
|
|
|
'org3': 'Org 3',
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'programs':
|
|
|
|
case 'program':
|
|
|
|
filters.push({
|
|
|
|
name: 'program',
|
|
|
|
label: 'Program',
|
|
|
|
options: {
|
|
|
|
'p1': 'Program 1',
|
|
|
|
'p2': 'Program 2',
|
|
|
|
'p3': 'Program 3',
|
|
|
|
'p4': 'Program 4',
|
|
|
|
'p5': 'Program 5',
|
|
|
|
'p6': 'Program 6'
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'grouped_programs':
|
|
|
|
case 'grouped_program':
|
|
|
|
filters.push({
|
|
|
|
name: 'program',
|
|
|
|
label: 'Program',
|
|
|
|
options: {
|
|
|
|
'Org 1': {
|
|
|
|
'p1': 'Program 1',
|
|
|
|
'p2': 'Program 2'
|
|
|
|
},
|
|
|
|
'Org 2': {
|
|
|
|
'p3': 'Program 3',
|
|
|
|
'p4': 'Program 4'
|
|
|
|
},
|
|
|
|
'Org 3': {
|
|
|
|
'p5': 'Program 5',
|
|
|
|
'p6': 'Program 6'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
is_grouped: true
|
2013-03-28 17:14:16 +04:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'ages':
|
|
|
|
case 'age':
|
|
|
|
filters.push({
|
|
|
|
name: 'age',
|
|
|
|
label: 'Age Group',
|
|
|
|
options: {
|
|
|
|
'lt-13': 'Under 13',
|
|
|
|
'13-14': '13 to 14',
|
|
|
|
'15-16': '15 to 16',
|
|
|
|
'17-18': '17 to 18',
|
|
|
|
'gt-18': 'Over 18'
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return filters;
|
|
|
|
}
|
|
|
|
|
2013-05-15 00:18:25 +04:00
|
|
|
app.param('programName', function (req, res, next, programName) {
|
|
|
|
badger.getProgram(programName, function(err, data) {
|
|
|
|
if (err)
|
2013-05-16 17:19:30 +04:00
|
|
|
return next(err);
|
2013-03-28 20:42:32 +04:00
|
|
|
|
2013-05-15 00:18:25 +04:00
|
|
|
req.params.program = data.program;
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
});
|
2013-03-28 20:42:32 +04:00
|
|
|
|
2013-06-02 02:44:36 +04:00
|
|
|
app.get('/explore', badger.middleware('getPrograms'), function (req, res, next) {
|
2013-05-15 00:18:25 +04:00
|
|
|
var data = req.remote;
|
2013-03-28 20:42:32 +04:00
|
|
|
|
|
|
|
res.render('programs/list.html', {
|
|
|
|
filters: getFilters('categories', 'orgs', 'ages'),
|
2013-05-15 00:18:25 +04:00
|
|
|
items: data.programs,
|
|
|
|
page: data.page,
|
|
|
|
pages: data.pages
|
2013-03-28 20:42:32 +04:00
|
|
|
});
|
2013-03-25 00:52:09 +04:00
|
|
|
});
|
|
|
|
|
2013-06-02 02:44:36 +04:00
|
|
|
app.get('/explore/:programName', function (req, res, next) {
|
2013-05-15 00:18:25 +04:00
|
|
|
res.render('programs/single.html', {
|
|
|
|
program: req.params.program
|
|
|
|
});
|
2013-03-28 20:42:32 +04:00
|
|
|
});
|
|
|
|
|
2013-06-02 02:44:36 +04:00
|
|
|
app.get('/explore/:programName/favorite', function (req, res, next) {
|
2013-03-28 20:42:32 +04:00
|
|
|
return res.redirect('/login', 303);
|
|
|
|
});
|
|
|
|
|
2013-06-02 02:44:36 +04:00
|
|
|
app.get('/explore/:programName/unfavorite', function (req, res, next) {
|
2013-03-28 20:42:32 +04:00
|
|
|
return res.redirect('/login', 303);
|
|
|
|
});
|
|
|
|
|
2013-05-31 18:25:04 +04:00
|
|
|
app.post('/applications', function (req, res, next) {
|
|
|
|
function finish (err) {
|
|
|
|
if (err)
|
|
|
|
return res.json({status: err.message || err});
|
|
|
|
|
|
|
|
return res.json({status: 'ok'});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!req.body['_id'])
|
|
|
|
return finish('Could not find submission ID in request');
|
|
|
|
|
|
|
|
applications.find({where: {submissionId: req.body['_id']}})
|
|
|
|
.complete(function(err, application) {
|
|
|
|
if (err)
|
|
|
|
return finish(err);
|
|
|
|
|
|
|
|
aestimia.update(application);
|
|
|
|
|
|
|
|
finish();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-05-28 23:46:24 +04:00
|
|
|
app.param('evidenceSlug', function (req, res, next, slug) {
|
|
|
|
var parts = /^([a-z0-9]+?)(?:_(thumb))?$/.exec(slug);
|
2013-05-30 16:12:30 +04:00
|
|
|
var shouldAuthenticate = (req.method !== 'GET');
|
2013-05-28 23:46:24 +04:00
|
|
|
|
|
|
|
if (!parts)
|
|
|
|
return next(new errors.NotFound());
|
|
|
|
|
2013-05-30 16:12:30 +04:00
|
|
|
if (shouldAuthenticate && !req.session.user)
|
2013-05-28 23:46:24 +04:00
|
|
|
return next(new errors.Forbidden());
|
|
|
|
|
|
|
|
var key = parts[1],
|
|
|
|
thumb = !!parts[2];
|
|
|
|
|
|
|
|
evidence.find({where: {key: key}})
|
|
|
|
.complete(function (err, instance) {
|
|
|
|
if (err)
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
if (!instance)
|
|
|
|
return next(new errors.NotFound());
|
|
|
|
|
|
|
|
req.params.evidence = instance;
|
|
|
|
req.params.thumb = thumb;
|
|
|
|
|
|
|
|
instance.getApplication()
|
|
|
|
.complete(function (err, application) {
|
|
|
|
if (err)
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
req.params.application = application;
|
|
|
|
|
|
|
|
// We'll need to decide how to authenticate external services
|
|
|
|
// like, for example, Aestimia. For now, restrict access to either
|
|
|
|
// the learner who uploaded it, or their guardian
|
|
|
|
|
2013-05-30 16:12:30 +04:00
|
|
|
if (!shouldAuthenticate)
|
|
|
|
return next();
|
|
|
|
|
2013-05-28 23:46:24 +04:00
|
|
|
application.getLearner()
|
|
|
|
.complete(function (err, learner) {
|
|
|
|
if (err)
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
if (learner.id === req.session.user.id)
|
|
|
|
return next();
|
|
|
|
|
|
|
|
learner.getGuardian()
|
|
|
|
.complete(function (err, guardian) {
|
|
|
|
if (err)
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
if (!guardian || guardian.id !== req.session.user.id)
|
|
|
|
return next(new errors.Forbidden());
|
|
|
|
|
|
|
|
return next();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get('/evidence/:evidenceSlug', function (req, res, next) {
|
|
|
|
var evidence = req.params.evidence,
|
|
|
|
thumb = req.params.thumb;
|
|
|
|
|
|
|
|
res.type(evidence.mediaType);
|
|
|
|
res.header('Vary', 'Cookie');
|
|
|
|
res.header('Cache-Control', 'max-age=2419200'); // 4 weeks
|
|
|
|
res.header('Last-Modified', evidence.updatedAt);
|
|
|
|
|
|
|
|
evidence[thumb ? 'fetchThumb' : 'fetch'](function (remote) {
|
|
|
|
remote
|
|
|
|
.on('response', function(proxy) {
|
|
|
|
proxy.pipe(res);
|
|
|
|
})
|
|
|
|
.end();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
app.post('/evidence/:evidenceSlug', function (req, res, next) {
|
|
|
|
var evidence = req.params.evidence,
|
|
|
|
application = req.params.application,
|
|
|
|
thumb = req.params.thumb;
|
|
|
|
|
|
|
|
if (thumb)
|
|
|
|
return next(new errors.NotAllowed());
|
|
|
|
|
|
|
|
if (!req.body.action || req.body.action !== 'delete')
|
|
|
|
return next(new errors.NotAllowed());
|
|
|
|
|
2013-05-29 15:24:36 +04:00
|
|
|
if (application.state !== 'open')
|
2013-05-28 23:46:24 +04:00
|
|
|
return next(new errors.NotAllowed());
|
|
|
|
|
|
|
|
evidence.destroy()
|
|
|
|
.complete(function (err) {
|
|
|
|
if (err)
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
evidence.getApplication()
|
|
|
|
.complete(function (err, application) {
|
|
|
|
if (err)
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
return res.redirect('/earn/' + application.badgeId + '/apply');
|
|
|
|
});
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2013-03-28 17:14:16 +04:00
|
|
|
app.param('badgeName', function (req, res, next, badgeName) {
|
2013-05-15 00:18:25 +04:00
|
|
|
badger.getBadge(badgeName, function(err, data) {
|
2013-05-08 18:14:31 +04:00
|
|
|
if (err)
|
2013-05-16 17:19:30 +04:00
|
|
|
return next(err);
|
2013-05-08 18:14:31 +04:00
|
|
|
|
|
|
|
req.params.badge = data.badge;
|
|
|
|
next();
|
|
|
|
});
|
2013-03-28 17:14:16 +04:00
|
|
|
});
|
|
|
|
|
2013-05-15 23:25:41 +04:00
|
|
|
app.get('/earn', badger.middleware('getBadges'), function (req, res, next) {
|
2013-05-09 21:27:55 +04:00
|
|
|
var data = req.remote;
|
2013-05-08 18:14:31 +04:00
|
|
|
|
2013-05-08 18:55:23 +04:00
|
|
|
res.render('badges/list.html', {
|
|
|
|
filters: getFilters(),
|
2013-05-09 21:28:41 +04:00
|
|
|
items: data.badges,
|
2013-05-08 18:55:23 +04:00
|
|
|
page: data.page,
|
|
|
|
pages: data.pages
|
2013-05-08 18:14:31 +04:00
|
|
|
});
|
2013-03-28 17:14:16 +04:00
|
|
|
});
|
|
|
|
|
2013-05-15 00:18:25 +04:00
|
|
|
app.get('/earn/:badgeName', function (req, res, next) {
|
2013-05-08 18:14:31 +04:00
|
|
|
res.render('badges/single.html', {
|
|
|
|
badge: req.params.badge
|
|
|
|
});
|
2013-03-28 17:14:16 +04:00
|
|
|
});
|
|
|
|
|
2013-05-22 18:37:54 +04:00
|
|
|
app.get('/earn/:badgeName/apply', function (req, res, next) {
|
|
|
|
var badge = req.params.badge;
|
|
|
|
|
2013-05-30 16:41:08 +04:00
|
|
|
if (!req.session.user) {
|
|
|
|
req.session.afterLogin = '/earn/' + req.params.badgeName + '/apply';
|
2013-05-28 23:46:24 +04:00
|
|
|
return res.redirect('/login');
|
2013-05-30 16:41:08 +04:00
|
|
|
}
|
2013-05-22 18:37:54 +04:00
|
|
|
|
|
|
|
applications.findOrCreate({
|
|
|
|
badgeId: badge.id,
|
2013-05-30 17:24:01 +04:00
|
|
|
LearnerId: req.session.user.id
|
2013-05-22 18:37:54 +04:00
|
|
|
})
|
|
|
|
.complete(function(err, application) {
|
|
|
|
if (err || !application)
|
|
|
|
return next(err || new Error('There was a problem loading your application'));
|
|
|
|
|
|
|
|
application.getEvidence()
|
|
|
|
.complete(function(err, evidence) {
|
|
|
|
if (err)
|
|
|
|
return next(err);
|
|
|
|
|
|
|
|
var state = evidence.length ? application.state : 'new';
|
|
|
|
|
|
|
|
res.render('applications/' + state + '.html', {
|
|
|
|
evidence: evidence,
|
|
|
|
application: application,
|
|
|
|
badge: badge
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
app.post('/earn/:badgeName/apply', function (req, res, next) {
|
|
|
|
var badge = req.params.badge;
|
|
|
|
|
2013-05-28 23:46:24 +04:00
|
|
|
if (!req.session.user)
|
|
|
|
return res.redirect(badge.url);
|
|
|
|
|
|
|
|
function finish (err, evidence) {
|
|
|
|
if (req.xhr) {
|
|
|
|
return res.json({
|
|
|
|
status: err ? 'failed' : 'ok',
|
|
|
|
message: err || null,
|
|
|
|
evidence: evidence || []
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
req.flash('error', err);
|
|
|
|
|
|
|
|
res.redirect(badge.url + '/apply');
|
|
|
|
}
|
2013-05-22 18:37:54 +04:00
|
|
|
|
|
|
|
applications.find({where: {
|
|
|
|
badgeId: badge.id,
|
2013-05-28 23:46:24 +04:00
|
|
|
LearnerId: req.session.user.id
|
2013-05-22 18:37:54 +04:00
|
|
|
}})
|
|
|
|
.complete(function(err, application) {
|
2013-05-28 23:46:24 +04:00
|
|
|
if (err || !application)
|
|
|
|
return finish(err);
|
|
|
|
|
|
|
|
if (req.body.action === 'apply')
|
|
|
|
return application.submit(finish);
|
|
|
|
|
2013-05-31 20:15:40 +04:00
|
|
|
if (req.body.action === 'reopen')
|
|
|
|
return application.reopen(finish);
|
|
|
|
|
2013-05-28 23:46:24 +04:00
|
|
|
if ('description' in req.body) {
|
|
|
|
application.updateAttributes({
|
|
|
|
description: req.body.description
|
|
|
|
});
|
2013-05-22 18:37:54 +04:00
|
|
|
}
|
|
|
|
|
2013-05-28 23:46:24 +04:00
|
|
|
var files = (req.files||{}).media;
|
|
|
|
|
|
|
|
if (!files)
|
|
|
|
return finish();
|
2013-05-22 18:37:54 +04:00
|
|
|
|
2013-05-28 23:46:24 +04:00
|
|
|
if (!_.isArray(files))
|
|
|
|
files = [files];
|
|
|
|
|
|
|
|
async.map(files, function (file, callback) {
|
|
|
|
application.process(file, function (err, item) {
|
|
|
|
if (err || !item)
|
|
|
|
return callback(err);
|
|
|
|
|
|
|
|
callback(null, {
|
|
|
|
type: item.mediaType,
|
|
|
|
thumbnail: item.getThumbnailUrl(),
|
|
|
|
location: item.getLocationUrl(),
|
|
|
|
original: item.original
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}, finish);
|
2013-05-22 18:37:54 +04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-05-15 00:18:25 +04:00
|
|
|
app.get('/earn/:badgeName/claim', function (req, res, next) {
|
2013-03-28 17:14:16 +04:00
|
|
|
res.render('badges/claim.html');
|
|
|
|
});
|
|
|
|
|
2013-05-15 00:18:25 +04:00
|
|
|
app.get('/earn/:badgeName/favorite', function (req, res, next) {
|
2013-03-28 17:14:16 +04:00
|
|
|
return res.redirect('/login', 303);
|
|
|
|
});
|
|
|
|
|
2013-05-15 00:18:25 +04:00
|
|
|
app.get('/earn/:badgeName/unfavorite', function (req, res, next) {
|
2013-03-28 17:14:16 +04:00
|
|
|
return res.redirect('/favorites', 303);
|
2013-03-25 00:52:09 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
app.get('/orgs', function (req, res, next) {
|
2013-03-28 17:14:16 +04:00
|
|
|
var orgs = [];
|
|
|
|
|
|
|
|
for (var i = 0; i < 12; ++i) {
|
|
|
|
orgs.push({
|
|
|
|
thumbnail: '/media/images/org.png',
|
2013-06-02 02:32:45 +04:00
|
|
|
description: 'Organization blah irure...',
|
|
|
|
url: '/orgs/some-organization'
|
2013-03-28 17:14:16 +04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
res.render('orgs/list.html', {
|
2013-03-28 20:42:32 +04:00
|
|
|
filters: getFilters('categories', 'ages'),
|
2013-03-28 17:14:16 +04:00
|
|
|
items: orgs
|
|
|
|
});
|
2013-03-25 00:52:09 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
app.param('orgName', function (req, res, next, orgName) {
|
|
|
|
// pull some stuff from the database probably
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
|
2013-03-28 17:14:16 +04:00
|
|
|
app.get('/orgs/:orgName', function (req, res, next) {
|
|
|
|
res.render('orgs/single.html');
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get('/orgs/:orgName/favorite', function (req, res, next) {
|
|
|
|
return res.redirect('/login', 303);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get('/orgs/:orgName/unfavorite', function (req, res, next) {
|
|
|
|
return res.redirect('/login', 303);
|
2013-03-25 00:52:09 +04:00
|
|
|
});
|
|
|
|
};
|