Group managers (#8591)

* Added abiltiy to add group managers

* Added ability to remove managers

* Added ability for managers to add group tasks

* Allower managers to assign tasks

* Allowed managers to unassign tasks

* Allow managers to delete group tasks

* Allowed managers to approve

* Added initial ui

* Added approval view for managers

* Allowed managers to edit

* Fixed lint issues

* Added spacing to buttons

* Removed leader from selection of group managers

* Code review updates

* Ensured approvals are only done once

* Added ability for parties to add managers

* Add notifications to all managers when approval is requests

* Removed tasks need approval notifications from all managers when task is approve

* Fixed linting issues

* Hid add managers UI from groups that are not subscribed

* Removed let from front end

* Fixed issues with post task url params

* Fixed string locales

* Removed extra limited strings

* Added cannotedit tasks function

* Added limit fields and notification check by taskId

* Localized string and other minor issues

* Added manager and leader indicator

* Added group notifications refresh on sync

* Added close button for group notifications

* Removed group approval notifications when manager is removed

* Moved leader/manager indicators to after hp

* Added manager fields to groups

* Spelling and syntax fixes
This commit is contained in:
Keith Holliday
2017-04-25 08:28:56 -06:00
committed by GitHub
parent 369702884a
commit e2f4b0e3dc
25 changed files with 708 additions and 55 deletions

View File

@@ -25,6 +25,13 @@ import logger from '../../libs/logger';
const MAX_SCORE_NOTES_LENGTH = 256;
function canNotEditTasks (group, user, assignedUserId) {
let isNotGroupLeader = group.leader !== user._id;
let isManager = Boolean(group.managers[user._id]);
let userIsAssigningToSelf = Boolean(assignedUserId && user._id === assignedUserId);
return isNotGroupLeader && !isManager && !userIsAssigningToSelf;
}
/**
* @apiDefine TaskNotFound
* @apiError (404) {NotFound} TaskNotFound The specified task could not be found.
@@ -413,9 +420,10 @@ api.updateTask = {
throw new NotFound(res.t('taskNotFound'));
} else if (task.group.id && !task.userId) {
// @TODO: Abstract this access snippet
group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
let fields = requiredGroupFields.concat(' managers');
group = await Group.getGroup({user, groupId: task.group.id, fields});
if (!group) throw new NotFound(res.t('groupNotFound'));
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
@@ -530,18 +538,29 @@ api.scoreTask = {
task.group.approval.requested = true;
task.group.approval.requestedDate = new Date();
let group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
let groupLeader = await User.findById(group.leader).exec(); // Use this method so we can get access to notifications
let fields = requiredGroupFields.concat(' managers');
let group = await Group.getGroup({user, groupId: task.group.id, fields});
groupLeader.addNotification('GROUP_TASK_APPROVAL', {
message: res.t('userHasRequestedTaskApproval', {
user: user.profile.name,
taskName: task.text,
}, groupLeader.preferences.language),
groupId: group._id,
// @TODO: we can use the User.pushNotification function because we need to ensure notifications are translated
let managerIds = Object.keys(group.managers);
managerIds.push(group.leader);
let managers = await User.find({_id: managerIds}, 'notifications preferences').exec(); // Use this method so we can get access to notifications
let managerPromises = [];
managers.forEach((manager) => {
manager.addNotification('GROUP_TASK_APPROVAL', {
message: res.t('userHasRequestedTaskApproval', {
user: user.profile.name,
taskName: task.text,
}, manager.preferences.language),
groupId: group._id,
taskId: task._id,
});
managerPromises.push(manager.save());
});
await Bluebird.all([groupLeader.save(), task.save()]);
managerPromises.push(task.save());
await Bluebird.all(managerPromises);
throw new NotAuthorized(res.t('taskApprovalHasBeenRequested'));
}
@@ -694,9 +713,9 @@ api.addChecklistItem = {
if (!task) {
throw new NotFound(res.t('taskNotFound'));
} else if (task.group.id && !task.userId) {
group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
if (!group) throw new NotFound(res.t('groupNotFound'));
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
let fields = requiredGroupFields.concat(' managers');
group = await Group.getGroup({user, groupId: task.group.id, fields});
if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
@@ -803,9 +822,10 @@ api.updateChecklistItem = {
if (!task) {
throw new NotFound(res.t('taskNotFound'));
} else if (task.group.id && !task.userId) {
group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
let fields = requiredGroupFields.concat(' managers');
group = await Group.getGroup({user, groupId: task.group.id, fields});
if (!group) throw new NotFound(res.t('groupNotFound'));
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
@@ -867,9 +887,10 @@ api.removeChecklistItem = {
if (!task) {
throw new NotFound(res.t('taskNotFound'));
} else if (task.group.id && !task.userId) {
group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
let fields = requiredGroupFields.concat(' managers');
group = await Group.getGroup({user, groupId: task.group.id, fields});
if (!group) throw new NotFound(res.t('groupNotFound'));
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();
if (!challenge) throw new NotFound(res.t('challengeNotFound'));
@@ -1185,9 +1206,10 @@ api.deleteTask = {
throw new NotFound(res.t('taskNotFound'));
} else if (task.group.id && !task.userId) {
// @TODO: Abstract this access snippet
let group = await Group.getGroup({user, groupId: task.group.id, fields: requiredGroupFields});
let fields = requiredGroupFields.concat(' managers');
let group = await Group.getGroup({user, groupId: task.group.id, fields});
if (!group) throw new NotFound(res.t('groupNotFound'));
if (group.leader !== user._id) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
if (canNotEditTasks(group, user)) throw new NotAuthorized(res.t('onlyGroupLeaderCanEditTasks'));
await group.removeTask(task);
} else if (task.challenge.id && !task.userId) { // If the task belongs to a challenge make sure the user has rights
challenge = await Challenge.findOne({_id: task.challenge.id}).exec();