2013-10-27 00:24:45 +00:00
|
|
|
// @see ../routes for routing
|
|
|
|
|
|
|
|
|
|
var _ = require('lodash');
|
|
|
|
|
var nconf = require('nconf');
|
|
|
|
|
var async = require('async');
|
2013-12-11 16:30:39 +00:00
|
|
|
var shared = require('habitrpg-shared');
|
2013-10-27 00:24:45 +00:00
|
|
|
var User = require('./../models/user').model;
|
|
|
|
|
var Group = require('./../models/group').model;
|
|
|
|
|
var Challenge = require('./../models/challenge').model;
|
2014-01-23 06:20:16 +00:00
|
|
|
var csv = require('express-csv');
|
2013-10-27 00:24:45 +00:00
|
|
|
var api = module.exports;
|
|
|
|
|
|
2013-10-31 08:24:13 +00:00
|
|
|
|
2013-10-27 00:24:45 +00:00
|
|
|
/*
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
|
Challenges
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2013-10-30 00:49:49 +00:00
|
|
|
api.list = function(req, res) {
|
|
|
|
|
var user = res.locals.user;
|
2013-10-31 22:48:21 +00:00
|
|
|
async.waterfall([
|
|
|
|
|
function(cb){
|
|
|
|
|
// Get all available groups I belong to
|
|
|
|
|
Group.find({members: {$in: [user._id]}}).select('_id').exec(cb);
|
|
|
|
|
},
|
|
|
|
|
function(gids, cb){
|
|
|
|
|
// and their challenges
|
|
|
|
|
Challenge.find({
|
|
|
|
|
$or:[
|
|
|
|
|
{leader: user._id},
|
|
|
|
|
{members:{$in:[user._id]}}, // all challenges I belong to (is this necessary? thought is a left a group, but not its challenge)
|
|
|
|
|
{group:{$in:gids}}, // all challenges in my groups
|
|
|
|
|
{group: 'habitrpg'} // public group
|
|
|
|
|
]
|
|
|
|
|
})
|
2014-01-03 20:36:13 +00:00
|
|
|
.select('name leader description group members prize official')
|
2013-10-31 22:48:21 +00:00
|
|
|
.populate('group', '_id name')
|
2013-11-16 02:57:16 +00:00
|
|
|
.populate('leader', 'profile.name')
|
2014-01-03 20:36:13 +00:00
|
|
|
.sort('-official -timestamp')
|
2013-10-31 22:48:21 +00:00
|
|
|
.exec(cb);
|
|
|
|
|
}
|
|
|
|
|
], function(err, challenges){
|
|
|
|
|
if (err) return res.json(500,{err:err});
|
|
|
|
|
_.each(challenges, function(c){
|
|
|
|
|
c._isMember = !!~c.members.indexOf(user._id);
|
|
|
|
|
c.memberCount = _.size(c.members);
|
|
|
|
|
c.members = undefined;
|
2013-10-30 00:49:49 +00:00
|
|
|
})
|
2013-10-31 22:48:21 +00:00
|
|
|
res.json(challenges);
|
|
|
|
|
});
|
2013-10-30 00:49:49 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-27 00:24:45 +00:00
|
|
|
// GET
|
|
|
|
|
api.get = function(req, res) {
|
2013-11-04 08:27:44 +00:00
|
|
|
// TODO use mapReduce() or aggregate() here to
|
|
|
|
|
// 1) Find the sum of users.tasks.values within the challnege (eg, {'profile.name':'tyler', 'sum': 100})
|
|
|
|
|
// 2) Sort by the sum
|
|
|
|
|
// 3) Limit 30 (only show the 30 users currently in the lead)
|
2013-10-30 00:49:49 +00:00
|
|
|
Challenge.findById(req.params.cid)
|
2013-11-02 06:11:28 +00:00
|
|
|
.populate('members', 'profile.name _id')
|
2013-10-30 00:49:49 +00:00
|
|
|
.exec(function(err, challenge){
|
2013-10-28 00:46:39 +00:00
|
|
|
if(err) return res.json(500, {err:err});
|
2013-11-01 07:30:39 +00:00
|
|
|
if (!challenge) return res.json(404, {err: 'Challenge ' + req.params.cid + ' not found'});
|
2013-11-02 06:11:28 +00:00
|
|
|
res.json(challenge);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-23 06:20:16 +00:00
|
|
|
api.csv = function(req, res) {
|
|
|
|
|
var cid = req.params.cid;
|
|
|
|
|
var challenge;
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function(cb){
|
|
|
|
|
Challenge.findById(cid,cb)
|
|
|
|
|
},
|
|
|
|
|
function(_challenge,cb) {
|
|
|
|
|
challenge = _challenge;
|
|
|
|
|
if (!challenge) return cb('Challenge ' + cid + ' not found');
|
|
|
|
|
var elemMatch = {$elemMatch:{'challenge.id':cid}};
|
|
|
|
|
User.find(
|
|
|
|
|
{_id:{'$in':challenge.members}},
|
|
|
|
|
{todos:elemMatch,habits:elemMatch,dailys:elemMatch,rewards:elemMatch,_id:1,'profile.name':1},
|
|
|
|
|
cb);
|
|
|
|
|
}
|
|
|
|
|
],function(err,users){
|
|
|
|
|
if(err) return res.json(500, {err:err});
|
|
|
|
|
var output = ['UUID','name'];
|
|
|
|
|
_.each(challenge.tasks,function(t){
|
|
|
|
|
output.push(t.type+':'+t.text);
|
|
|
|
|
output.push('Value');
|
|
|
|
|
output.push('Notes');
|
|
|
|
|
})
|
|
|
|
|
output = [output];
|
|
|
|
|
_.each(users, function(u){
|
|
|
|
|
var uData = [u._id,u.profile.name];
|
|
|
|
|
_.each(u.tasks,function(t){
|
|
|
|
|
uData = uData.concat(['', t.value, t.notes]);
|
|
|
|
|
})
|
|
|
|
|
output.push(uData);
|
|
|
|
|
});
|
|
|
|
|
res.csv(output);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-02 06:11:28 +00:00
|
|
|
api.getMember = function(req, res) {
|
|
|
|
|
var cid = req.params.cid, uid = req.params.uid;
|
2013-11-11 07:41:42 +00:00
|
|
|
// TMK we can't use $elemMatch (which would make things much cleaner) @see http://goo.gl/MxmWdQ & http://goo.gl/Iku44w
|
|
|
|
|
// Revert back to 9fbb45c to see the $elemMatch solution
|
2013-11-04 08:27:44 +00:00
|
|
|
User.findById(uid)
|
2013-11-11 07:41:42 +00:00
|
|
|
.select('profile.name habits dailys rewards todos')
|
2013-11-02 06:11:28 +00:00
|
|
|
.exec(function(err, member){
|
|
|
|
|
if(err) return res.json(500, {err:err});
|
|
|
|
|
if (!member) return res.json(404, {err: 'Member '+uid+' for challenge '+cid+' not found'});
|
2013-11-11 07:41:42 +00:00
|
|
|
_.each(['habits','dailys','todos', 'rewards'], function(type){
|
|
|
|
|
member[type] = _.filter(member[type], function(task){
|
|
|
|
|
return task.challenge && task.challenge.id && task.challenge.id == cid;
|
|
|
|
|
});
|
|
|
|
|
});
|
2013-11-02 06:11:28 +00:00
|
|
|
res.json(member);
|
2013-10-28 00:46:39 +00:00
|
|
|
})
|
2013-10-27 00:24:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CREATE
|
|
|
|
|
api.create = function(req, res){
|
2013-10-31 19:39:10 +00:00
|
|
|
var user = res.locals.user;
|
2013-11-03 01:01:29 +00:00
|
|
|
var group, chal;
|
|
|
|
|
|
|
|
|
|
// First, make sure they've selected a legit group, and store it for later
|
|
|
|
|
var waterfall = [
|
|
|
|
|
function(cb){
|
|
|
|
|
Group.findById(req.body.group).exec(cb);
|
|
|
|
|
},
|
|
|
|
|
function(_group, cb){
|
|
|
|
|
if (!_group) return cb("Group." + req.body.group + " not found");
|
|
|
|
|
group = _group;
|
|
|
|
|
cb(null);
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// If they're adding a prize, do some validation
|
2013-10-31 21:16:01 +00:00
|
|
|
if (+req.body.prize < 0) return res.json(401, {err: 'Challenge prize must be >= 0'});
|
2013-11-01 02:30:07 +00:00
|
|
|
if (req.body.group=='habitrpg' && +req.body.prize < 1) return res.json(401, {err: 'Prize must be at least 1 Gem for public challenges.'});
|
2013-10-31 19:39:10 +00:00
|
|
|
if (+req.body.prize > 0) {
|
2013-11-03 01:01:29 +00:00
|
|
|
waterfall.push(function(cb){
|
|
|
|
|
var groupBalance = ((group.balance && group.leader==user._id) ? group.balance : 0);
|
2013-12-13 23:48:18 +00:00
|
|
|
var prizeCost = req.body.prize/4; // I really should have stored user.balance as gems rather than dollars... stupid...
|
|
|
|
|
if (prizeCost > user.balance + groupBalance)
|
|
|
|
|
return cb("You can't afford this prize. Purchase more gems or lower the prize amount.")
|
2013-10-31 19:39:10 +00:00
|
|
|
|
2013-12-13 23:48:18 +00:00
|
|
|
if (groupBalance >= prizeCost) {
|
|
|
|
|
// Group pays for all of prize
|
|
|
|
|
group.balance -= prizeCost;
|
|
|
|
|
} else if (groupBalance > 0) {
|
|
|
|
|
// User pays remainder of prize cost after group
|
|
|
|
|
var remainder = prizeCost - group.balance;
|
|
|
|
|
group.balance = 0;
|
|
|
|
|
user.balance -= remainder;
|
|
|
|
|
} else {
|
|
|
|
|
// User pays for all of prize
|
|
|
|
|
user.balance -= prizeCost;
|
|
|
|
|
}
|
2013-11-03 01:01:29 +00:00
|
|
|
cb(null)
|
|
|
|
|
});
|
2013-10-31 19:39:10 +00:00
|
|
|
}
|
2013-11-03 01:01:29 +00:00
|
|
|
|
2013-11-01 00:49:51 +00:00
|
|
|
waterfall = waterfall.concat([
|
2013-11-03 01:01:29 +00:00
|
|
|
function(cb) { // if we're dealing with prize above, arguemnts will be `group, numRows, cb` - else `cb`
|
2014-01-03 07:00:27 +00:00
|
|
|
req.body.leader = user._id;
|
2014-01-03 20:36:13 +00:00
|
|
|
req.body.official = user.contributor.admin && req.body.official;
|
2013-11-03 01:01:29 +00:00
|
|
|
var chal = new Challenge(req.body); // FIXME sanitize
|
|
|
|
|
chal.members.push(user._id);
|
|
|
|
|
chal.save(cb)
|
2013-11-01 00:49:51 +00:00
|
|
|
},
|
2013-11-03 01:01:29 +00:00
|
|
|
function(_chal, num, cb){
|
|
|
|
|
chal = _chal;
|
|
|
|
|
group.challenges.push(chal._id);
|
|
|
|
|
group.save(cb);
|
|
|
|
|
},
|
|
|
|
|
function(_group, num, cb) {
|
2013-11-01 00:49:51 +00:00
|
|
|
// Auto-join creator to challenge (see members.push above)
|
2013-11-04 06:33:53 +00:00
|
|
|
chal.syncToUser(user, cb);
|
2013-11-01 00:49:51 +00:00
|
|
|
}
|
|
|
|
|
]);
|
2013-11-03 01:01:29 +00:00
|
|
|
async.waterfall(waterfall, function(err){
|
2013-11-01 00:49:51 +00:00
|
|
|
if (err) return res.json(500, {err:err});
|
|
|
|
|
res.json(chal);
|
2013-10-27 00:24:45 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UPDATE
|
|
|
|
|
api.update = function(req, res){
|
2013-10-27 21:44:50 +00:00
|
|
|
var cid = req.params.cid;
|
2013-10-31 22:22:37 +00:00
|
|
|
var user = res.locals.user;
|
2013-10-31 08:24:13 +00:00
|
|
|
var before;
|
2013-10-27 21:44:50 +00:00
|
|
|
async.waterfall([
|
|
|
|
|
function(cb){
|
|
|
|
|
// We first need the original challenge data, since we're going to compare against new & decide to sync users
|
|
|
|
|
Challenge.findById(cid, cb);
|
|
|
|
|
},
|
2013-10-31 08:24:13 +00:00
|
|
|
function(_before, cb) {
|
2013-10-31 22:22:37 +00:00
|
|
|
if (!_before) return cb('Challenge ' + cid + ' not found');
|
|
|
|
|
if (_before.leader != user._id) return cb("You don't have permissions to edit this challenge");
|
2013-10-31 08:24:13 +00:00
|
|
|
// Update the challenge, since syncing will need the updated challenge. But store `before` we're going to do some
|
|
|
|
|
// before-save / after-save comparison to determine if we need to sync to users
|
|
|
|
|
before = _before;
|
2013-11-04 20:19:40 +00:00
|
|
|
var attrs = _.pick(req.body, 'name shortName description habits dailys todos rewards date'.split(' '));
|
|
|
|
|
Challenge.findByIdAndUpdate(cid, {$set:attrs}, cb);
|
2013-10-31 08:24:13 +00:00
|
|
|
},
|
|
|
|
|
function(saved, cb) {
|
|
|
|
|
// after saving, we're done as far as the client's concerned. We kick of syncing (heavy task) in the background
|
|
|
|
|
cb(null, saved);
|
2013-10-27 21:44:50 +00:00
|
|
|
|
|
|
|
|
// Compare whether any changes have been made to tasks. If so, we'll want to sync those changes to subscribers
|
2013-11-04 06:33:53 +00:00
|
|
|
if (before.isOutdated(req.body)) {
|
2013-10-31 08:24:13 +00:00
|
|
|
User.find({_id: {$in: saved.members}}, function(err, users){
|
2013-10-27 21:44:50 +00:00
|
|
|
console.log('Challenge updated, sync to subscribers');
|
|
|
|
|
if (err) throw err;
|
|
|
|
|
_.each(users, function(user){
|
2013-11-04 06:33:53 +00:00
|
|
|
saved.syncToUser(user);
|
2013-10-27 21:44:50 +00:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
], function(err, saved){
|
2013-10-27 00:24:45 +00:00
|
|
|
if(err) res.json(500, {err:err});
|
|
|
|
|
res.json(saved);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-31 20:49:31 +00:00
|
|
|
/**
|
|
|
|
|
* Called by either delete() or selectWinner(). Will delete the challenge and set the "broken" property on all users' subscribed tasks
|
|
|
|
|
* @param {cid} the challenge id
|
|
|
|
|
* @param {broken} the object representing the broken status of the challenge. Eg:
|
|
|
|
|
* {broken: 'CHALLENGE_DELETED', id: CHALLENGE_ID}
|
|
|
|
|
* {broken: 'CHALLENGE_CLOSED', id: CHALLENGE_ID, winner: USER_NAME}
|
|
|
|
|
*/
|
|
|
|
|
function closeChal(cid, broken, cb) {
|
2013-10-30 03:37:22 +00:00
|
|
|
var removed;
|
|
|
|
|
async.waterfall([
|
2013-10-31 20:49:31 +00:00
|
|
|
function(cb2){
|
|
|
|
|
Challenge.findOneAndRemove({_id:cid}, cb2)
|
2013-10-30 03:37:22 +00:00
|
|
|
},
|
2013-10-31 20:49:31 +00:00
|
|
|
function(_removed, cb2) {
|
2013-10-30 03:37:22 +00:00
|
|
|
removed = _removed;
|
2013-10-31 22:48:21 +00:00
|
|
|
var pull = {'$pull':{}}; pull['$pull'][_removed._id] = 1;
|
|
|
|
|
Group.findByIdAndUpdate(_removed.group, pull);
|
2013-10-31 20:49:31 +00:00
|
|
|
User.find({_id:{$in: removed.members}}, cb2);
|
2013-10-30 03:37:22 +00:00
|
|
|
},
|
2013-10-31 20:49:31 +00:00
|
|
|
function(users, cb2) {
|
2013-10-30 03:37:22 +00:00
|
|
|
var parallel = [];
|
2013-10-27 21:44:50 +00:00
|
|
|
_.each(users, function(user){
|
2013-11-03 01:01:29 +00:00
|
|
|
var tag = _.find(user.tags, {id:cid});
|
|
|
|
|
if (tag) tag.challenge = undefined;
|
2013-10-27 21:44:50 +00:00
|
|
|
_.each(user.tasks, function(task){
|
|
|
|
|
if (task.challenge && task.challenge.id == removed._id) {
|
2013-10-31 20:57:12 +00:00
|
|
|
_.merge(task.challenge, broken);
|
2013-10-27 21:44:50 +00:00
|
|
|
}
|
|
|
|
|
})
|
2013-10-31 20:49:31 +00:00
|
|
|
parallel.push(function(cb3){
|
|
|
|
|
user.save(cb3);
|
2013-10-30 03:37:22 +00:00
|
|
|
})
|
2013-10-27 21:44:50 +00:00
|
|
|
})
|
2013-10-31 20:49:31 +00:00
|
|
|
async.parallel(parallel, cb2);
|
|
|
|
|
}
|
|
|
|
|
], cb);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-31 20:57:12 +00:00
|
|
|
/**
|
|
|
|
|
* Delete & close
|
|
|
|
|
*/
|
2013-10-31 20:49:31 +00:00
|
|
|
api['delete'] = function(req, res){
|
2013-10-31 22:22:37 +00:00
|
|
|
var user = res.locals.user;
|
|
|
|
|
var cid = req.params.cid;
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function(cb){
|
|
|
|
|
Challenge.findById(cid, cb);
|
|
|
|
|
},
|
|
|
|
|
function(chal, cb){
|
|
|
|
|
if (!chal) return cb('Challenge ' + cid + ' not found');
|
|
|
|
|
if (chal.leader != user._id) return cb("You don't have permissions to edit this challenge");
|
|
|
|
|
closeChal(req.params.cid, {broken: 'CHALLENGE_DELETED'}, cb);
|
|
|
|
|
}
|
|
|
|
|
], function(err){
|
2013-10-31 20:49:31 +00:00
|
|
|
if (err) return res.json(500, {err: err});
|
|
|
|
|
res.send(200);
|
2013-10-31 22:22:37 +00:00
|
|
|
});
|
2013-10-31 20:49:31 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-31 20:57:12 +00:00
|
|
|
/**
|
|
|
|
|
* Select Winner & Close
|
|
|
|
|
*/
|
2013-10-31 20:49:31 +00:00
|
|
|
api.selectWinner = function(req, res) {
|
|
|
|
|
if (!req.query.uid) return res.json(401, {err: 'Must select a winner'});
|
2013-10-31 22:22:37 +00:00
|
|
|
var user = res.locals.user;
|
|
|
|
|
var cid = req.params.cid;
|
2013-10-31 20:49:31 +00:00
|
|
|
var chal;
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function(cb){
|
2013-10-31 22:22:37 +00:00
|
|
|
Challenge.findById(cid, cb);
|
2013-10-31 20:49:31 +00:00
|
|
|
},
|
|
|
|
|
function(_chal, cb){
|
|
|
|
|
chal = _chal;
|
2013-10-31 22:22:37 +00:00
|
|
|
if (!chal) return cb('Challenge ' + cid + ' not found');
|
|
|
|
|
if (chal.leader != user._id) return cb("You don't have permissions to edit this challenge");
|
2013-10-31 20:49:31 +00:00
|
|
|
User.findById(req.query.uid, cb)
|
|
|
|
|
},
|
|
|
|
|
function(winner, cb){
|
|
|
|
|
if (!winner) return cb('Winner ' + req.query.uid + ' not found.');
|
|
|
|
|
_.defaults(winner.achievements, {challenges: []});
|
|
|
|
|
winner.achievements.challenges.push(chal.name);
|
|
|
|
|
winner.balance += chal.prize/4;
|
|
|
|
|
winner.save(cb);
|
|
|
|
|
},
|
|
|
|
|
function(saved, num, cb) {
|
2013-10-31 22:22:37 +00:00
|
|
|
closeChal(cid, {broken: 'CHALLENGE_CLOSED', winner: saved.profile.name}, cb);
|
2013-10-30 03:37:22 +00:00
|
|
|
}
|
2013-10-31 21:06:18 +00:00
|
|
|
], function(err){
|
2013-10-30 03:37:22 +00:00
|
|
|
if (err) return res.json(500, {err: err});
|
|
|
|
|
res.send(200);
|
2013-10-27 21:44:50 +00:00
|
|
|
})
|
|
|
|
|
}
|
2013-10-27 00:24:45 +00:00
|
|
|
|
|
|
|
|
api.join = function(req, res){
|
|
|
|
|
var user = res.locals.user;
|
|
|
|
|
var cid = req.params.cid;
|
|
|
|
|
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function(cb){
|
|
|
|
|
Challenge.findByIdAndUpdate(cid, {$addToSet:{members:user._id}}, cb);
|
|
|
|
|
},
|
|
|
|
|
function(challenge, cb){
|
|
|
|
|
if (!~user.challenges.indexOf(cid))
|
|
|
|
|
user.challenges.unshift(cid);
|
|
|
|
|
// Add all challenge's tasks to user's tasks
|
2013-11-04 06:33:53 +00:00
|
|
|
challenge.syncToUser(user, function(err){
|
2013-10-27 00:24:45 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
|
cb(null, challenge); // we want the saved challenge in the return results, due to ng-resource
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
], function(err, result){
|
|
|
|
|
if(err) return res.json(500,{err:err});
|
2013-10-30 03:37:22 +00:00
|
|
|
result._isMember = true;
|
2013-10-27 00:24:45 +00:00
|
|
|
res.json(result);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-27 17:38:28 +00:00
|
|
|
|
|
|
|
|
api.leave = function(req, res){
|
2013-10-27 00:24:45 +00:00
|
|
|
var user = res.locals.user;
|
|
|
|
|
var cid = req.params.cid;
|
2013-10-27 17:38:28 +00:00
|
|
|
// whether or not to keep challenge's tasks. strictly default to true if "keep-all" isn't provided
|
|
|
|
|
var keep = (/^remove-all/i).test(req.query.keep) ? 'remove-all' : 'keep-all';
|
2013-10-27 00:24:45 +00:00
|
|
|
|
|
|
|
|
async.waterfall([
|
|
|
|
|
function(cb){
|
|
|
|
|
Challenge.findByIdAndUpdate(cid, {$pull:{members:user._id}}, cb);
|
|
|
|
|
},
|
|
|
|
|
function(chal, cb){
|
|
|
|
|
var i = user.challenges.indexOf(cid)
|
|
|
|
|
if (~i) user.challenges.splice(i,1);
|
2013-11-04 06:33:53 +00:00
|
|
|
user.unlink({cid:chal._id, keep:keep}, function(err){
|
2013-10-27 00:24:45 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
|
cb(null, chal);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
], function(err, result){
|
|
|
|
|
if(err) return res.json(500,{err:err});
|
2013-10-30 03:37:22 +00:00
|
|
|
result._isMember = false;
|
2013-10-27 00:24:45 +00:00
|
|
|
res.json(result);
|
|
|
|
|
});
|
2013-10-27 17:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-27 22:27:01 +00:00
|
|
|
api.unlink = function(req, res, next) {
|
|
|
|
|
// they're scoring the task - commented out, we probably don't need it due to route ordering in api.js
|
|
|
|
|
//var urlParts = req.originalUrl.split('/');
|
|
|
|
|
//if (_.contains(['up','down'], urlParts[urlParts.length -1])) return next();
|
|
|
|
|
|
2013-10-27 17:38:28 +00:00
|
|
|
var user = res.locals.user;
|
|
|
|
|
var tid = req.params.id;
|
|
|
|
|
var cid = user.tasks[tid].challenge.id;
|
|
|
|
|
if (!req.query.keep)
|
|
|
|
|
return res.json(400, {err: 'Provide unlink method as ?keep=keep-all (keep, keep-all, remove, remove-all)'});
|
2013-11-04 06:33:53 +00:00
|
|
|
user.unlink({cid:cid, keep:req.query.keep, tid:tid}, function(err, saved){
|
2013-10-27 17:38:28 +00:00
|
|
|
if (err) return res.json(500,{err:err});
|
|
|
|
|
res.send(200);
|
|
|
|
|
});
|
2013-12-13 23:48:18 +00:00
|
|
|
}
|