Completed
Push — master ( 862cae...854c71 )
by Raimund
13s
created

src/store/calendars.js (18 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
/**
2
 * Nextcloud - Tasks
3
 *
4
 * @author Raimund Schlüßler
5
 * @copyright 2018 Raimund Schlüßler <[email protected]>
6
 *
7
 * @author John Molakvoæ
8
 * @copyright 2018 John Molakvoæ <[email protected]>
9
 *
10
 * @author Georg Ehrke
11
 * @copyright 2018 Georg Ehrke <[email protected]>
12
 *
13
 * @author Thomas Citharel <[email protected]>
14
 * @copyright 2018 Thomas Citharel <[email protected]>
15
 *
16
 * This library is free software; you can redistribute it and/or
17
 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
18
 * License as published by the Free Software Foundation; either
19
 * version 3 of the License, or any later version.
20
 *
21
 * This library is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public
27
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
'use strict'
31
32
import Vue from 'vue'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
33
import ICAL from 'ical.js'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
34
import parseIcs from '../services/parseIcs'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
35
import client from '../services/cdav'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
36
import Task from '../models/task'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
37
import pLimit from 'p-limit'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
38
import { isParentInList, searchSubTasks } from './storeHelper'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
39
import { findVTODObyState } from './cdav-requests'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
40
import TaskStatus from '../models/taskStatus'
0 ignored issues
show
'import' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
41
42
const calendarModel = {
43
	id: '',
44
	color: '',
45
	displayName: '',
46
	enabled: true,
47
	owner: '',
48
	shares: [],
49
	tasks: [],
50
	url: '',
51
	readOnly: false,
52
	dav: false,
53
	supportsTasks: true,
54
	loadedCompleted: false,
55
}
56
57
const state = {
58
	calendars: []
59
}
60
61
/**
62
 * Maps a dav collection to our calendar object model
63
 *
64
 * @param {Object} calendar The calendar object from the cdav library
65
 * @returns {Object}
66
 */
67
export function mapDavCollectionToCalendar(calendar) {
0 ignored issues
show
'export' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
68
	return {
69
		// get last part of url
70
		id: calendar.url.split('/').slice(-2, -1)[0],
71
		displayName: calendar.displayname,
72
		color: calendar.color,
73
		enabled: calendar.enabled !== false,
74
		owner: calendar.owner,
75
		readOnly: !calendar.isWriteable(),
76
		tasks: [],
77
		url: calendar.url,
78
		dav: calendar,
79
		supportsTasks: calendar.components.includes('VTODO'),
80
		loadedCompleted: false,
81
	}
82
}
83
84
const getters = {
85
86
	/**
87
	 * Returns the calendars sorted alphabetically
88
	 *
89
	 * @param {Object} state The store data
90
	 * @returns {Array<Calendar>} Array of the calendars sorted alphabetically
91
	 */
92
	getSortedCalendars: state => {
0 ignored issues
show
'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
93
		return state.calendars.sort(function(cal1, cal2) {
94
			var n1 = cal1.displayName.toUpperCase()
95
			var n2 = cal2.displayName.toUpperCase()
96
			return (n1 < n2) ? -1 : (n1 > n2) ? 1 : 0
97
		})
98
	},
99
100
	/**
101
	 * Returns the calendars sorted alphabetically
102
	 *
103
	 * @param {Object} state The store data
104
	 * @returns {Array<Calendar>} Array of the calendars sorted alphabetically
105
	 */
106
	getSortedWritableCalendars: state => {
0 ignored issues
show
'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
107
		return state.calendars.filter(calendar => {
0 ignored issues
show
'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
108
			return !calendar.readOnly
109
		})
110
			.sort(function(cal1, cal2) {
111
				var n1 = cal1.displayName.toUpperCase()
112
				var n2 = cal2.displayName.toUpperCase()
113
				return (n1 < n2) ? -1 : (n1 > n2) ? 1 : 0
114
			})
115
	},
116
117
	/**
118
	 * Returns the calendar with the given calendarId
119
	 *
120
	 * @param {Object} state The store data
121
	 * @param {String} calendarId The id of the requested calendar
122
	 * @returns {Calendar} The requested calendar
123
	 */
124
	getCalendarById: state => (calendarId) => {
0 ignored issues
show
'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
125
		var calendar = state.calendars.find(search => search.id === calendarId)
0 ignored issues
show
'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
126
		return calendar
127
	},
128
129
	/**
130
	 * Returns the number of tasks in a calendar
131
	 *
132
	 * Tasks have to be
133
	 *	- a root task
134
	 *	- uncompleted
135
	 *
136
	 * @param {Object} state The store data
137
	 * @param {Object} getters The store getters
138
	 * @param {Object} rootState The store root state
139
	 * @param {String} calendarId The id of the requested calendar
140
	 * @returns {Integer} The number of tasks
141
	 */
142
	getCalendarCount: (state, getters, rootState) => (calendarId) => {
0 ignored issues
show
'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
143
		let calendar = getters.getCalendarById(calendarId)
144
		let tasks = Object.values(calendar.tasks)
145
			.filter(task => {
0 ignored issues
show
'arrow function syntax (=>)' is only available in ES6 (use 'esversion: 6').

Generally using ECMAScript 6 specific syntax is fine if you are sure that it is already supported by all engines which are supposed to run this code.

Further Reading:

Loading history...
There were too many errors found in this file; checking aborted after 22%.

If JSHint finds too many errors in a file, it aborts checking altogether because it suspects a configuration issue.

Further Reading:

Loading history...
146
				return task.completed === false && (!task.related || !isParentInList(task, calendar.tasks))
147
			})
148
		if (rootState.tasks.searchQuery) {
149
			tasks = tasks.filter(task => {
150
				if (task.matches(rootState.tasks.searchQuery)) {
151
					return true
152
				}
153
				// We also have to show tasks for which one sub(sub...)task matches.
154
				return searchSubTasks(task, rootState.tasks.searchQuery)
155
			})
156
		}
157
		return tasks.length
158
	},
159
160
	/**
161
	 * Returns the count of completed tasks in a calendar
162
	 *
163
	 * Tasks have to be
164
	 *	- a root task
165
	 *	- completed
166
	 *
167
	 * @param {Object} state The store data
168
	 * @param {Object} getters The store getters
169
	 * @param {String} calendarId The id of the calendar in question
170
	 * @returns {Integer} The count of completed tasks in a calendar
171
	 */
172
	getCalendarCountCompleted: (state, getters) => (calendarId) => {
173
		var calendar = getters.getCalendarById(calendarId)
174
		return Object.values(calendar.tasks)
175
			.filter(task => {
176
				return task.completed === true && (!task.related || !isParentInList(task, calendar.tasks))
177
			}).length
178
	},
179
180
	/**
181
	 * Returns if a calendar name is already used by an other calendar
182
	 *
183
	 * @param {Object} state The store data
184
	 * @param {String} name The name to check
185
	 * @param {String} id The id of the calendar to exclude
186
	 * @returns {Boolean} If a calendar name is already used
187
	 */
188
	isCalendarNameUsed: state => (name, id) => {
189
		return state.calendars.some(calendar => {
190
			return (calendar.displayName === name && calendar.id !== id)
191
		})
192
	},
193
194
	/**
195
	 * Returns the current calendar
196
	 *
197
	 * @param {Object} state The store data
198
	 * @param {Object} getters The store getters
199
	 * @param {Object} rootState The store root state
200
	 * @returns {Calendar} The calendar by route
201
	 */
202
	getCalendarByRoute: (state, getters, rootState) => {
203
		return getters.getCalendarById(rootState.route.params.calendarId)
204
	},
205
206
	/**
207
	 * Returns the default calendar
208
	 *
209
	 * @param {Object} state The store data
210
	 * @param {Object} getters The store getters
211
	 * @param {Object} rootState The store root state
212
	 * @returns {Calendar} The default calendar
213
	 */
214
	getDefaultCalendar: (state, getters, rootState) => {
215
		return getters.getCalendarById(rootState.settings.settings.defaultCalendarId) || getters.getSortedCalendars[0]
216
	}
217
}
218
219
const mutations = {
220
221
	/**
222
	 * Adds a calendar to the state
223
	 *
224
	 * @param {Object} state The store data
225
	 * @param {Calendar} calendar The calendar to add
226
	 */
227
	addCalendar(state, calendar) {
228
		// extend the calendar to the default model
229
		state.calendars.push(Object.assign({}, calendarModel, calendar))
230
	},
231
232
	/**
233
	 * Delete calendar
234
	 *
235
	 * @param {Object} state The store data
236
	 * @param {Calendar} calendar The calendar to delete
237
	 */
238
	deleteCalendar(state, calendar) {
239
		state.calendars.splice(state.calendars.indexOf(calendar), 1)
240
	},
241
242
	/**
243
	 * Toggles whether a calendar is enabled
244
	 *
245
	 * @param {Object} context The store mutations
246
	 * @param {Calendar} calendar The calendar to toggle
247
	 */
248
	toggleCalendarEnabled(context, calendar) {
249
		calendar.enabled = !calendar.enabled
250
	},
251
252
	/**
253
	 * Changes the name and the color of a calendar
254
	 *
255
	 * @param {Object} context The store mutations
256
	 * @param {Object} data Destructuring object
257
	 * @param {Calendar} data.calendar The calendar to change
258
	 * @param {String} data.newName The new name of the calendar
259
	 * @param {String} data.newColor The new color of the calendar
260
	 */
261
	renameCalendar(context, { calendar, newName, newColor }) {
262
		calendar.displayName = newName
263
		calendar.color = newColor
264
	},
265
266
	/**
267
	 * Appends a list of tasks to a calendar
268
	 * and removes duplicates
269
	 *
270
	 * @param {Object} state The store data
271
	 * @param {Object} data Destructuring object
272
	 * @param {Calendar} data.calendar The calendar to add the tasks to
273
	 * @param {Task[]} data.tasks Array of tasks to append
274
	 */
275
	appendTasksToCalendar(state, { calendar, tasks }) {
276
		// Convert list into an array and remove duplicate
277
		calendar.tasks = tasks.reduce((list, task) => {
278
			if (list[task.uid]) {
279
				console.debug('Duplicate task overridden', list[task.uid], task)
280
			}
281
			Vue.set(list, task.uid, task)
282
			return list
283
		}, calendar.tasks)
284
285
	},
286
287
	/**
288
	 * Adds a task to a calendar and overwrites if duplicate uid
289
	 *
290
	 * @param {Object} state The store data
291
	 * @param {Task} task The task to add
292
	 */
293
	addTaskToCalendar(state, task) {
294
		Vue.set(task.calendar.tasks, task.uid, task)
295
	},
296
297
	/**
298
	 * Deletes a task from its calendar
299
	 *
300
	 * @param {Object} state The store data
301
	 * @param {Task} task The task to delete
302
	 */
303
	deleteTaskFromCalendar(state, task) {
304
		Vue.delete(task.calendar.tasks, task.uid)
305
	},
306
307
	/**
308
	 * Shares a calendar with a user or group
309
	 *
310
	 * @param {Object} state The store data
311
	 * @param {Object} data Destructuring object
312
	 * @param {Calendar} data.calendar The calendar
313
	 * @param {String} data.sharee The sharee
314
	 * @param {String} data.id The id
315
	 * @param {Boolean} data.group The group
316
	 */
317
	shareCalendar(state, { calendar, sharee, id, group }) {
318
		let newSharee = {
319
			displayname: sharee,
320
			id,
321
			writeable: false,
322
			group
323
		}
324
		calendar.shares.push(newSharee)
325
	},
326
327
	/**
328
	 * Removes a sharee from calendar shares list
329
	 *
330
	 * @param {Object} state The store data
331
	 * @param {Object} sharee The sharee
332
	 */
333
	removeSharee(state, sharee) {
334
		let calendar = state.calendars.find(search => {
335
			for (let i in search.shares) {
336
				if (search.shares[i] === sharee) {
337
					return true
338
				}
339
			}
340
		})
341
		calendar.shares.splice(calendar.shares.indexOf(sharee), 1)
342
	},
343
344
	/**
345
	 * Toggles sharee's writable permission
346
	 *
347
	 * @param {Object} state The store data
348
	 * @param {Object} sharee The sharee
349
	 */
350
	updateShareeWritable(state, sharee) {
351
		let calendar = state.calendars.find(search => {
352
			for (let i in search.shares) {
353
				if (search.shares[i] === sharee) {
354
					return true
355
				}
356
			}
357
		})
358
		sharee = calendar.shares.find(search => search === sharee)
359
		sharee.writeable = !sharee.writeable
360
	}
361
}
362
363
const actions = {
364
	/**
365
	 * Retrieves and commits calendars
366
	 *
367
	 * @param {Object} context The store mutations
368
	 * @returns {Promise<Array>} The calendars
369
	 */
370
	async getCalendars(context) {
371
		let calendars = await client.calendarHomes[0].findAllCalendars()
372
			.then(calendars => {
373
				return calendars.map(calendar => {
374
					return mapDavCollectionToCalendar(calendar)
375
				})
376
			})
377
378
		// Remove calendars which don't support tasks
379
		calendars = calendars.filter(calendar => calendar.supportsTasks)
380
381
		calendars.forEach(calendar => {
382
			context.commit('addCalendar', calendar)
383
		})
384
385
		return calendars
386
	},
387
388
	/**
389
	 * Appends a new calendar to array of existing calendars
390
	 *
391
	 * @param {Object} context The store mutations
392
	 * @param {Calendar} calendar The calendar to append
393
	 * @returns {Promise}
394
	 */
395
	async appendCalendar(context, calendar) {
396
		return client.calendarHomes[0].createCalendarCollection(calendar.displayName, calendar.color, ['VTODO'])
397
			.then((response) => {
398
				calendar = mapDavCollectionToCalendar(response)
399
				context.commit('addCalendar', calendar)
400
			})
401
			.catch((error) => { throw error })
402
	},
403
404
	/**
405
	 * Delete calendar
406
	 * @param {Object} context The store mutations Current context
407
	 * @param {Calendar} calendar The calendar to delete
408
	 * @returns {Promise}
409
	 */
410
	async deleteCalendar(context, calendar) {
411
		return calendar.dav.delete()
412
			.then((response) => {
413
				// Delete all the tasks from the store that belong to this calendar
414
				Object.values(calendar.tasks)
415
					.forEach(task => context.commit('deleteTask', task))
416
				// Then delete the calendar
417
				context.commit('deleteCalendar', calendar)
418
			})
419
			.catch((error) => { throw error })
420
	},
421
422
	/**
423
	 * Toggles whether a calendar is enabled
424
	 * @param {Object} context The store mutations current context
425
	 * @param {Calendar} calendar The calendar to toggle
426
	 * @returns {Promise}
427
	 */
428
	async toggleCalendarEnabled(context, calendar) {
429
		calendar.dav.enabled = !calendar.dav.enabled
430
		return calendar.dav.update()
431
			.then((response) => context.commit('toggleCalendarEnabled', calendar))
432
			.catch((error) => { throw error })
433
	},
434
435
	/**
436
	 * Changes the name and the color of a calendar
437
	 *
438
	 * @param {Object} context The store mutations Current context
439
	 * @param {Calendar} data.calendar The calendar to change
440
	 * @param {String} data.newName The new name of the calendar
441
	 * @param {String} data.newColor The new color of the calendar
442
	 * @returns {Promise}
443
	 */
444
	async changeCalendar(context, { calendar, newName, newColor }) {
445
		calendar.dav.displayname = newName
446
		calendar.dav.color = newColor
447
		return calendar.dav.update()
448
			.then((response) => context.commit('renameCalendar', { calendar, newName, newColor }))
449
			.catch((error) => { throw error })
450
	},
451
452
	/**
453
	 * Retrieves the tasks of the specified calendar
454
	 * and commits the results
455
	 *
456
	 * @param {Object} context The store mutations
457
	 * @param {Object} data Destructuring object
458
	 * @param {Calendar} data.calendar The calendar
459
	 * @param {String} data.completed Are the requested tasks completed
460
	 * @param {String} data.related The uid of the parent task
461
	 * @returns {Promise}
462
	 */
463
	async getTasksFromCalendar(context, { calendar, completed = false, related = null }) {
464
		return findVTODObyState(calendar, completed, related)
465
			.then((response) => {
466
				// We don't want to lose the url information
467
				// so we need to parse one by one
468
				const tasks = response.map(item => {
469
					let task = new Task(item.data, calendar)
470
					Vue.set(task, 'dav', item)
471
					return task
472
				})
473
474
				// Initialize subtasks so we don't have to search for them on every change.
475
				// We do have to manually adjust this list when a task is added, deleted or moved.
476
				tasks.forEach(
477
					parent => {
478
						var subTasks = tasks.filter(task => {
479
							return task.related === parent.uid
480
						})
481
482
						// Convert list into an array and remove duplicate
483
						parent.subTasks = subTasks.reduce((list, task) => {
484
							if (list[task.uid]) {
485
								console.debug('Duplicate task overridden', list[task.uid], task)
486
							}
487
							Vue.set(list, task.uid, task)
488
							return list
489
						}, parent.subTasks)
490
491
						// If necessary, add the tasks as subtasks to parent tasks already present in the store.
492
						if (!related) {
493
							context.commit('addTaskToParent', parent)
494
						}
495
					}
496
				)
497
498
				// If the requested tasks are related to a task, add the tasks as subtasks
499
				if (related) {
500
					let parent = Object.values(calendar.tasks).find(search => search.uid === related)
501
					if (parent) {
502
						parent.loadedCompleted = true
503
						tasks.map(task => Vue.set(parent.subTasks, task.uid, task))
504
					}
505
				}
506
507
				context.commit('appendTasksToCalendar', { calendar, tasks })
508
				context.commit('appendTasks', tasks)
509
				return tasks
510
			})
511
			.catch((error) => {
512
				// unrecoverable error, if no tasks were loaded,
513
				// remove the calendar
514
				// TODO: create a failed calendar state and show that there was an issue?
515
				context.commit('deleteCalendar', calendar)
516
				console.error(error)
517
			})
518
	},
519
520
	/**
521
	 * Imports tasks into a calendar from an ics file
522
	 *
523
	 * @param {Object} context The store mutations
524
	 * @param {Object} importDetails = { ics, calendar }
525
	 */
526
	async importTasksIntoCalendar(context, { ics, calendar }) {
527
		const tasks = parseIcs(ics, calendar)
528
		context.commit('changeStage', 'importing')
529
530
		// max simultaneous requests
531
		const limit = pLimit(3)
532
		const requests = []
533
534
		// create the array of requests to send
535
		tasks.map(async task => {
536
			// Get vcard string
537
			try {
538
				let vData = ICAL.stringify(task.vCard.jCal)
539
				// push task to server and use limit
540
				requests.push(limit(() => task.calendar.dav.createVCard(vData)
541
					.then((response) => {
542
						// setting the task dav property
543
						Vue.set(task, 'dav', response)
544
545
						// success, update store
546
						context.commit('addTask', task)
547
						context.commit('addTaskToCalendar', task)
548
						context.commit('incrementAccepted')
549
					})
550
					.catch((error) => {
551
						// error
552
						context.commit('incrementDenied')
553
						console.error(error)
554
					})
555
				))
556
			} catch (e) {
557
				context.commit('incrementDenied')
558
			}
559
		})
560
561
		Promise.all(requests).then(() => {
562
			context.commit('changeStage', 'default')
563
		})
564
	},
565
566
	/**
567
	 * Removes a sharee from a calendar
568
	 *
569
	 * @param {Object} context The store mutations Current context
570
	 * @param {Object} sharee Calendar sharee object
571
	 */
572
	removeSharee(context, sharee) {
573
		context.commit('removeSharee', sharee)
574
	},
575
576
	/**
577
	 * Toggles permissions of calendar sharees writeable rights
578
	 *
579
	 * @param {Object} context The store mutations Current context
580
	 * @param {Object} sharee Calendar sharee object
581
	 */
582
	toggleShareeWritable(context, sharee) {
583
		context.commit('updateShareeWritable', sharee)
584
	},
585
586
	/**
587
	 * Shares a calendar with a user or a group
588
	 *
589
	 * @param {Object} context The store mutations Current context
590
	 * @param {Calendar} data.calendar The calendar
591
	 * @param {String} data.sharee The sharee
592
	 * @param {Boolean} data.id The id
593
	 * @param {Boolean} data.group The group
594
	 */
595
	shareCalendar(context, { calendar, sharee, id, group }) {
596
		// Share a calendar with the entered group or user
597
		context.commit('shareCalendar', { calendar, sharee, id, group })
598
	},
599
600
	/**
601
	 * Moves a task to the provided calendar
602
	 *
603
	 * @param {Object} context The store mutations
604
	 * @param {Object} data Destructuring object
605
	 * @param {Task} data.task The task to move
606
	 * @param {Calendar} data.calendar The calendar to move the task to
607
	 * @param {Boolean} data.removeParent If the task has a parent, remove the link to the parent
608
	 * @returns {Task} The moved task
609
	 */
610
	async moveTaskToCalendar(context, { task, calendar, removeParent = true }) {
611
		// Only local move if the task doesn't exist on the server.
612
		// Don't move if source and target calendar are the same.
613
		if (task.dav && task.calendar !== calendar) {
614
			// Move all subtasks first
615
			await Promise.all(Object.values(task.subTasks).map(async(subTask) => {
616
				await context.dispatch('moveTaskToCalendar', { task: subTask, calendar: calendar, removeParent: false })
617
			}))
618
619
			// If a task has a parent task which is not moved, remove the reference to it.
620
			if (removeParent && task.related !== null) {
621
				// Remove the task from the parents subtask list
622
				context.commit('deleteTaskFromParent', task)
623
				// Unlink the related parent task
624
				context.commit('setTaskParent', { task: task, related: null })
625
				// We have to send an update.
626
				await context.dispatch('updateTask', task)
627
			}
628
629
			await task.dav.move(calendar.dav)
630
				.then((response) => {
631
					context.commit('deleteTaskFromCalendar', task)
632
					// Update the calendar of the task
633
					context.commit('setTaskCalendar', { task: task, calendar: calendar })
634
					// Remove the task from the calendar, add it to the new one
635
					context.commit('addTaskToCalendar', task)
636
					task.syncstatus = new TaskStatus('success', 'Task successfully moved to new calendar.')
637
				})
638
				.catch((error) => {
639
					console.error(error)
640
					OC.Notification.showTemporary(t('calendars', 'An error occurred'))
641
				})
642
		}
643
		return task
644
	}
645
}
646
647
export default { state, getters, mutations, actions }
648