mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
add unlinkTask route and refactor user.unlink (now user.unlinkChallengesTasks)
This commit is contained in:
@@ -37,6 +37,7 @@
|
|||||||
"memberCannotRemoveYourself": "You cannot remove yourself!",
|
"memberCannotRemoveYourself": "You cannot remove yourself!",
|
||||||
"groupMemberNotFound": "User not found among group's members",
|
"groupMemberNotFound": "User not found among group's members",
|
||||||
"keepOrRemoveAll": "req.query.keep must be either \"keep-all\" or \"remove-all\"",
|
"keepOrRemoveAll": "req.query.keep must be either \"keep-all\" or \"remove-all\"",
|
||||||
|
"keepOrRemove": "req.query.keep must be either \"keep\" or \"remove\"",
|
||||||
"canOnlyInviteEmailUuid": "Can only invite using uuids or emails.",
|
"canOnlyInviteEmailUuid": "Can only invite using uuids or emails.",
|
||||||
"inviteMissingEmail": "Missing email address in invite.",
|
"inviteMissingEmail": "Missing email address in invite.",
|
||||||
"onlyGroupLeaderChal": "Only the group leader can create challenges",
|
"onlyGroupLeaderChal": "Only the group leader can create challenges",
|
||||||
@@ -59,5 +60,6 @@
|
|||||||
"userWithIDNotFound": "User with id \"<%= userId %>\" not found.",
|
"userWithIDNotFound": "User with id \"<%= userId %>\" not found.",
|
||||||
"uuidsMustBeAnArray": "UUIDs invites must be a an Array.",
|
"uuidsMustBeAnArray": "UUIDs invites must be a an Array.",
|
||||||
"emailsMustBeAnArray": "Email invites must be a an Array.",
|
"emailsMustBeAnArray": "Email invites must be a an Array.",
|
||||||
"canOnlyInviteMaxInvites": "You can only invite \"<%= maxInvites %>\" at a time"
|
"canOnlyInviteMaxInvites": "You can only invite \"<%= maxInvites %>\" at a time",
|
||||||
|
"cantOnlyUnlinkChalTask": "Only challenges tasks can be unlinked."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -540,7 +540,7 @@ api.moveTask = {
|
|||||||
}).exec();
|
}).exec();
|
||||||
|
|
||||||
if (!task) throw new NotFound(res.t('taskNotFound'));
|
if (!task) throw new NotFound(res.t('taskNotFound'));
|
||||||
if (task.type === 'todo' && task.completed) throw new NotFound(res.t('cantMoveCompletedTodo'));
|
if (task.type === 'todo' && task.completed) throw new BadRequest(res.t('cantMoveCompletedTodo'));
|
||||||
let order = user.tasksOrder[`${task.type}s`];
|
let order = user.tasksOrder[`${task.type}s`];
|
||||||
let currentIndex = order.indexOf(task._id);
|
let currentIndex = order.indexOf(task._id);
|
||||||
|
|
||||||
@@ -837,20 +837,62 @@ api.removeTagFromTask = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Remove a task from (user|challenge).tasksOrder
|
// Remove a task from (user|challenge).tasksOrder
|
||||||
function _removeTaskTasksOrder (userOrChallenge, taskId) {
|
function _removeTaskTasksOrder (userOrChallenge, taskId, taskType) {
|
||||||
// Loop through all lists and when the task is found, remove it and return
|
let list = userOrChallenge.tasksOrder[taskType];
|
||||||
for (let i = 0; i < Tasks.tasksTypes.length; i++) {
|
|
||||||
let list = userOrChallenge.tasksOrder[`${Tasks.tasksTypes[i]}s`];
|
|
||||||
let index = list.indexOf(taskId);
|
let index = list.indexOf(taskId);
|
||||||
|
|
||||||
if (index !== -1) {
|
if (index !== -1) list.splice(index, 1);
|
||||||
list.splice(index, 1);
|
}
|
||||||
break;
|
|
||||||
|
// TODO this method needs some limitation, like to check if the challenge is really broken?
|
||||||
|
/**
|
||||||
|
* @api {post} /tasks/unlink/:taskId Unlink a challenge task
|
||||||
|
* @apiVersion 3.0.0
|
||||||
|
* @apiName UnlinkTask
|
||||||
|
* @apiGroup Task
|
||||||
|
*
|
||||||
|
* @apiParam {UUID} taskId The task _id
|
||||||
|
*
|
||||||
|
* @apiSuccess {object} empty An empty object
|
||||||
|
*/
|
||||||
|
api.unlinkTask = {
|
||||||
|
method: 'POST',
|
||||||
|
url: '/tasks/unlink/:taskId',
|
||||||
|
middlewares: [authWithHeaders(), cron],
|
||||||
|
async handler (req, res) {
|
||||||
|
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
|
||||||
|
req.checkQuery('keep', res.t('keepOrRemove')).notEmpty().isIn(['keep', 'remove']);
|
||||||
|
|
||||||
|
let validationErrors = req.validationErrors();
|
||||||
|
if (validationErrors) throw validationErrors;
|
||||||
|
|
||||||
|
let user = res.locals.user;
|
||||||
|
let keep = req.query.keep;
|
||||||
|
let taskId = req.params.taskId;
|
||||||
|
|
||||||
|
let task = await Tasks.Task.findOne({
|
||||||
|
_id: taskId,
|
||||||
|
userId: user._id,
|
||||||
|
}).exec();
|
||||||
|
|
||||||
|
if (!task) throw new NotFound(res.t('taskNotFound'));
|
||||||
|
if (!task.challenge.id) throw new BadRequest(res.t('cantOnlyUnlinkChalTask'));
|
||||||
|
|
||||||
|
if (keep === 'keep') {
|
||||||
|
task.challenge = {};
|
||||||
|
await task.save();
|
||||||
|
} else { // remove
|
||||||
|
if (task.type !== 'todo' || !task.completed) { // eslint-disable-line no-lonely-if
|
||||||
|
_removeTaskTasksOrder(user, taskId, task.type);
|
||||||
|
await Q.all([user.save(), task.remove()]);
|
||||||
|
} else {
|
||||||
|
await task.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
res.respond(200, {}); // TODO what to return
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {delete} /task/:taskId Delete a user task given its id
|
* @api {delete} /task/:taskId Delete a user task given its id
|
||||||
@@ -875,9 +917,8 @@ api.deleteTask = {
|
|||||||
let validationErrors = req.validationErrors();
|
let validationErrors = req.validationErrors();
|
||||||
if (validationErrors) throw validationErrors;
|
if (validationErrors) throw validationErrors;
|
||||||
|
|
||||||
let task = await Tasks.Task.findOne({
|
let taskId = req.params.taskId;
|
||||||
_id: req.params.taskId,
|
let task = await Tasks.Task.findById(taskId).exec();
|
||||||
}).exec();
|
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
throw new NotFound(res.t('taskNotFound'));
|
throw new NotFound(res.t('taskNotFound'));
|
||||||
@@ -891,8 +932,12 @@ api.deleteTask = {
|
|||||||
throw new NotAuthorized(res.t('cantDeleteChallengeTasks'));
|
throw new NotAuthorized(res.t('cantDeleteChallengeTasks'));
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeTaskTasksOrder(challenge || user, req.params.taskId);
|
if (task.type !== 'todo' || !task.completed) {
|
||||||
await Q.all([user.save(), task.remove()]);
|
_removeTaskTasksOrder(challenge || user, taskId, task.type);
|
||||||
|
await Q.all([(challenge || user).save(), task.remove()]);
|
||||||
|
} else {
|
||||||
|
await task.remove();
|
||||||
|
}
|
||||||
|
|
||||||
res.respond(200, {});
|
res.respond(200, {});
|
||||||
if (challenge) challenge.removeTask(task);
|
if (challenge) challenge.removeTask(task);
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ schema.methods.removeTask = async function challengeRemoveTask (task) {
|
|||||||
'challenge.taskId': task._id,
|
'challenge.taskId': task._id,
|
||||||
}, {
|
}, {
|
||||||
$set: {'challenge.broken': 'TASK_DELETED'}, // TODO what about updatedAt?
|
$set: {'challenge.broken': 'TASK_DELETED'}, // TODO what about updatedAt?
|
||||||
}).lean().exec();
|
}, {multi: true}).exec();
|
||||||
};
|
};
|
||||||
|
|
||||||
export let model = mongoose.model('Challenge', schema);
|
export let model = mongoose.model('Challenge', schema);
|
||||||
|
|||||||
@@ -439,7 +439,7 @@ schema.statics.bossQuest = function bossQuest (user, progress) {
|
|||||||
|
|
||||||
// Remove user from this group
|
// Remove user from this group
|
||||||
// TODO this is highly inefficient
|
// TODO this is highly inefficient
|
||||||
schema.methods.leave = function leaveGroup (user, keep = 'keep-all') {
|
schema.methods.leave = function leaveGroup (user, keep) {
|
||||||
let group = this;
|
let group = this;
|
||||||
|
|
||||||
return Q.all([
|
return Q.all([
|
||||||
@@ -455,12 +455,12 @@ schema.methods.leave = function leaveGroup (user, keep = 'keep-all') {
|
|||||||
{_id: {$in: _.pluck(challenges, '_id')}},
|
{_id: {$in: _.pluck(challenges, '_id')}},
|
||||||
{$pull: {members: user._id}},
|
{$pull: {members: user._id}},
|
||||||
{multi: true}
|
{multi: true}
|
||||||
).then(() => challenges); // pass `challenges` above to next promise TODO ok to return a non-promise?
|
).then(() => challenges); // pass `challenges` above to next promise
|
||||||
}).then(challenges => {
|
}).then(challenges => {
|
||||||
return Q.all(challenges.map(chal => {
|
return Q.all(challenges.map(chal => {
|
||||||
let i = user.challenges.indexOf(chal._id);
|
let i = user.challenges.indexOf(chal._id);
|
||||||
if (i !== -1) user.challenges.splice(i, 1);
|
if (i !== -1) user.challenges.splice(i, 1);
|
||||||
return user.unlink({cid: chal._id, keep});
|
return user.unlinkChallengeTasks(chal._id, keep);
|
||||||
}));
|
}));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -643,40 +643,34 @@ schema.methods.isSubscribed = function isSubscribed () {
|
|||||||
return !!this.purchased.plan.customerId; // eslint-disable-line no-implicit-coercion
|
return !!this.purchased.plan.customerId; // eslint-disable-line no-implicit-coercion
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.methods.unlink = function unlink (options, cb) {
|
// Unlink challenges tasks from user
|
||||||
let cid = options.cid;
|
schema.methods.unlinkChallengeTasks = async function unlinkChallengeTasks (challengeId, keep) {
|
||||||
let keep = options.keep;
|
let user = this;
|
||||||
let tid = options.tid;
|
let findQuery = {
|
||||||
|
userId: user._id,
|
||||||
|
'challenge.id': challengeId,
|
||||||
|
};
|
||||||
|
|
||||||
if (!cid) {
|
if (keep === 'keep-all') {
|
||||||
return cb('Could not remove challenge tasks. Please delete them manually.');
|
await Tasks.Task.update(findQuery, {
|
||||||
|
$set: {challenge: {}}, // TODO what about updatedAt?
|
||||||
|
}, {multi: true}).exec();
|
||||||
|
} else { // keep = 'remove-all'
|
||||||
|
let tasks = Tasks.Task.find(findQuery).select('_id type completed').exec();
|
||||||
|
tasks = tasks.map(task => {
|
||||||
|
// Remove task from user.tasksOrder and delete them
|
||||||
|
if (task.type !== 'todo' || !task.completed) {
|
||||||
|
let list = user.tasksOrder[task.type];
|
||||||
|
let index = list.indexOf(task._id);
|
||||||
|
if (index !== -1) list.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let self = this;
|
return task.remove();
|
||||||
|
|
||||||
if (keep === 'keep') {
|
|
||||||
self.tasks[tid].challenge = {};
|
|
||||||
} else if (keep === 'remove') {
|
|
||||||
self.ops.deleteTask({params: {id: tid}}, () => {});
|
|
||||||
} else if (keep === 'keep-all') {
|
|
||||||
_.each(self.tasks, (t) => {
|
|
||||||
if (t.challenge && t.challenge.id === cid) {
|
|
||||||
t.challenge = {};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else if (keep === 'remove-all') {
|
|
||||||
_.each(self.tasks, (t) => {
|
|
||||||
if (t.challenge && t.challenge.id === cid) {
|
|
||||||
this.ops.deleteTask({params: {id: tid}}, () => {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.markModified('habits');
|
tasks.push(user.save());
|
||||||
self.markModified('dailys');
|
await Q.all(tasks);
|
||||||
self.markModified('todos');
|
}
|
||||||
self.markModified('rewards');
|
|
||||||
self.save(cb);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export let model = mongoose.model('User', schema);
|
export let model = mongoose.model('User', schema);
|
||||||
|
|||||||
Reference in New Issue
Block a user