diff --git a/src/components/TaskBody.vue b/src/components/TaskBody.vue index 6b0848504..651862c5f 100644 --- a/src/components/TaskBody.vue +++ b/src/components/TaskBody.vue @@ -126,6 +126,15 @@ License along with this library. If not, see . {{ t('tasks', 'Delete task') }} + + + {{ t('tasks', 'Duplicate task') }} + } The newly created duplicate task + */ + async duplicateTask(context, payload) { + // Support being called with either the Task directly or a payload object + // called as: duplicateTask(task) or duplicateTask({ task, calendar, parent }) + const task = payload && payload.task ? payload.task : payload + const calendar = payload && payload.calendar ? payload.calendar : (task ? task.calendar : null) + const parent = payload && Object.prototype.hasOwnProperty.call(payload, 'parent') ? payload.parent : null + + // Don't try to duplicate non-existing tasks + if (!task) { + return null + } + // Don't try to duplicate tasks into read-only calendars + if (!calendar || calendar.readOnly) { + return null + } + // Don't duplicate tasks with access class not PUBLIC into calendars shared with me + if (calendar.isSharedWithMe && task.class !== 'PUBLIC') { + return null + } + + // Create a new Task from the existing task's jCal + const vData = ICAL.stringify(task.jCal) + const newTask = new Task(vData, calendar) + + // Assign a new UID and created timestamp + newTask.uid = randomUUID() + newTask.created = ICAL.Time.fromJSDate(new Date(), true) + newTask.dav = null + newTask.conflict = false + + // If a parent was provided, link to it. Otherwise, if the original task had + // a related parent and that parent exists in the target calendar, keep relation. + if (parent) { + newTask.related = parent.uid + } else if (task.related) { + const existingParent = context.getters.getTaskByUid(task.related) + if (existingParent && existingParent.calendar && existingParent.calendar.id === calendar.id) { + newTask.related = task.related + } + else { + newTask.related = null + } + } + + // Create the new vObject on the server + try { + const response = await calendar.dav.createVObject(ICAL.stringify(newTask.jCal)) + newTask.dav = response + newTask.syncStatus = new SyncStatus('success', t('tasks', 'Successfully duplicated the task.')) + context.commit('appendTask', newTask) + context.commit('addTaskToCalendar', newTask) + const parentLocal = context.getters.getTaskByUid(newTask.related) + context.commit('addTaskToParent', { task: newTask, parent: parentLocal }) + } catch (error) { + console.error(error) + showError(t('tasks', 'Could not duplicate the task.')) + return null + } + + // Duplicate subtasks recursively, attaching them to the new parent + await Promise.all(Object.values(task.subTasks).map(async (subTask) => { + await context.dispatch('duplicateTask', { task: subTask, calendar, parent: newTask }) + })) + + return newTask + }, + /** * Deletes a task *