From 1c028267c146015d14a13a833bea9c9265471f54 Mon Sep 17 00:00:00 2001 From: "Daniel Boelzle [:dbo]" Date: Sat, 14 Feb 2009 17:08:12 +0100 Subject: [PATCH] Fix bug 345607 - Copy recurring event and paste to another day appears to work but event is not saved (has RECURRENCE-ID but no parent item) [clipboard]. r=philipp --- calendar/base/content/calendar-clipboard.js | 12 +---- calendar/base/modules/calUtils.jsm | 32 +++++++++++ calendar/base/public/calIRecurrenceInfo.idl | 7 +++ calendar/base/src/calEvent.js | 2 +- calendar/base/src/calItemBase.js | 6 ++- calendar/base/src/calRecurrenceInfo.js | 59 +++++++++++---------- calendar/base/src/calTodo.js | 2 +- 7 files changed, 78 insertions(+), 42 deletions(-) diff --git a/calendar/base/content/calendar-clipboard.js b/calendar/base/content/calendar-clipboard.js index 8789d5c55e..999b7eb0ee 100644 --- a/calendar/base/content/calendar-clipboard.js +++ b/calendar/base/content/calendar-clipboard.js @@ -222,17 +222,7 @@ function pasteFromClipboard() { // Set new UID to allow multiple paste actions of the same // clipboard content. newItem.id = cal.getUUID(); - if (item.startDate) { - newItem.startDate.addDuration(offset); - newItem.endDate.addDuration(offset); - } else { - if (item.entryDate) { - newItem.entryDate.addDuration(offset); - } - if (item.dueDate) { - newItem.dueDate.addDuration(offset); - } - } + cal.shiftItem(newItem, offset); doTransaction('add', newItem, destCal, null, null); } endBatchTransaction(); diff --git a/calendar/base/modules/calUtils.jsm b/calendar/base/modules/calUtils.jsm index 9d46c16493..f1febdbd61 100644 --- a/calendar/base/modules/calUtils.jsm +++ b/calendar/base/modules/calUtils.jsm @@ -87,6 +87,38 @@ let cal = { return (!tz.icalComponent && !tz.isUTC && !tz.isFloating); }, + /** + * Shifts an item by the given timely offset. + * + * @param item an item + * @param offset an offset (calIDuration) + */ + shiftItem: function cal_shiftItem(item, offset) { + // When modifying dates explicitly using the setters is important + // since those may triggers e.g. calIRecurrenceInfo::onStartDateChange + // or invalidate other properties. Moreover don't modify the date-time objects + // without cloning, because changes cannot be calculated if doing so. + if (cal.isEvent(item)) { + let date = item.startDate.clone(); + date.addDuration(offset); + item.startDate = date; + date = item.endDate.clone(); + date.addDuration(offset); + item.endDate = date; + } else /* isToDo */ { + if (item.entryDate) { + let date = item.entryDate.clone(); + date.addDuration(offset); + item.entryDate = date; + } + if (item.dueDate) { + let date = item.dueDate.clone(); + date.addDuration(offset); + item.dueDate = date; + } + } + }, + /** * Shortcut function to serialize an item (including all overridden items). */ diff --git a/calendar/base/public/calIRecurrenceInfo.idl b/calendar/base/public/calIRecurrenceInfo.idl index 538cc2b45f..8946e6c216 100644 --- a/calendar/base/public/calIRecurrenceInfo.idl +++ b/calendar/base/public/calIRecurrenceInfo.idl @@ -72,6 +72,13 @@ interface calIRecurrenceInfo : nsISupports */ void onStartDateChange(in calIDateTime aNewStartTime, in calIDateTime aOldStartTime); + /** + * If the base item's UID changes, this implicitly has to change all overridden items' UID, too. + * + * @param id new UID + */ + void onIdChange(in AUTF8String aNewId); + /* * Set of recurrence items; the order of these matters. */ diff --git a/calendar/base/src/calEvent.js b/calendar/base/src/calEvent.js index 2aea79de25..bb071829db 100644 --- a/calendar/base/src/calEvent.js +++ b/calendar/base/src/calEvent.js @@ -203,7 +203,7 @@ calEvent.prototype = { } } - this.setProperty("DTSTART", value); + return this.setProperty("DTSTART", value); }, get startDate() { diff --git a/calendar/base/src/calItemBase.js b/calendar/base/src/calItemBase.js index 6e6ac7b469..2b195a2853 100644 --- a/calendar/base/src/calItemBase.js +++ b/calendar/base/src/calItemBase.js @@ -105,7 +105,11 @@ calItemBase.prototype = { }, set id cIB_set_id(uid) { this.mHashId = null; // recompute hashId - return this.setProperty("UID", uid); + this.setProperty("UID", uid); + if (this.mRecurrenceInfo) { + this.mRecurrenceInfo.onIdChange(uid); + } + return uid; }, // attribute calIDateTime recurrenceId; diff --git a/calendar/base/src/calRecurrenceInfo.js b/calendar/base/src/calRecurrenceInfo.js index fbf591dd97..7d2a52f446 100644 --- a/calendar/base/src/calRecurrenceInfo.js +++ b/calendar/base/src/calRecurrenceInfo.js @@ -38,6 +38,8 @@ * * ***** END LICENSE BLOCK ***** */ +Components.utils.import("resource://calendar/modules/calUtils.jsm"); + function getRidKey(dt) { if (!dt) { return null; @@ -126,13 +128,13 @@ calRecurrenceInfo.prototype = { return; } - for each (ritem in this.mRecurrenceItems) { + for each (let ritem in this.mRecurrenceItems) { if (ritem.isMutable) { ritem.makeImmutable(); } } - for each (item in this.mExceptionMap) { + for each (let item in this.mExceptionMap) { if (item.isMutable) { item.makeImmutable(); } @@ -172,7 +174,7 @@ calRecurrenceInfo.prototype = { value = calTryWrappedJSObject(value); this.mBaseItem = value; // patch exception's parentItem: - for each (exitem in this.mExceptionMap) { + for each (let exitem in this.mExceptionMap) { exitem.parentItem = value; } }, @@ -763,12 +765,9 @@ calRecurrenceInfo.prototype = { // in case we're about to modify a parentItem (aka 'folded' item), we need // to modify the recurrenceId's of all possibly existing exceptions as well. onStartDateChange: function cRI_onStartDateChange(aNewStartTime, aOldStartTime) { - // passing null for the new starttime would indicate an error condition, // since having a recurrence without a starttime is invalid. - if (!aNewStartTime) { - throw Components.results.NS_ERROR_INVALID_ARG; - } + cal.ASSERT(aNewStartTime, "invalid arg!", true); // no need to check for changes if there's no previous starttime. if (!aOldStartTime) { @@ -776,37 +775,36 @@ calRecurrenceInfo.prototype = { } // convert both dates to UTC since subtractDate is not timezone aware. - var timeDiff = aNewStartTime.getInTimezone(UTC()).subtractDate(aOldStartTime.getInTimezone(UTC())); + let timeDiff = aNewStartTime.getInTimezone(UTC()).subtractDate(aOldStartTime.getInTimezone(UTC())); - var rdates = {}; + let rdates = {}; // take RDATE's and EXDATE's into account. const kCalIRecurrenceDate = Components.interfaces.calIRecurrenceDate; const kCalIRecurrenceDateSet = Components.interfaces.calIRecurrenceDateSet; - var ritems = this.getRecurrenceItems({}); - for each (var ritem in ritems) { - if (calInstanceOf(ritem, kCalIRecurrenceDate)) { + let ritems = this.getRecurrenceItems({}); + for each (let ritem in ritems) { + if (cal.calInstanceOf(ritem, kCalIRecurrenceDate)) { ritem = ritem.QueryInterface(kCalIRecurrenceDate); - var date = ritem.date; + let date = ritem.date; date.addDuration(timeDiff); if (!ritem.isNegative) { rdates[getRidKey(date)] = date; } ritem.date = date; - } else if (calInstanceOf(ritem, kCalIRecurrenceDateSet)) { + } else if (cal.calInstanceOf(ritem, kCalIRecurrenceDateSet)) { ritem = ritem.QueryInterface(kCalIRecurrenceDateSet); - var rdates = ritem.getDates({}); - for each (var date in rdates) { + for each (let date in ritem.getDates({})) { date.addDuration(timeDiff); if (!ritem.isNegative) { rdates[getRidKey(date)] = date; } } ritem.setDates(rdates.length,rdates); - } else if (calInstanceOf(ritem, Components.interfaces.calIRecurrenceRule)) { + } else if (cal.calInstanceOf(ritem, Components.interfaces.calIRecurrenceRule)) { ritem = ritem.QueryInterface(Components.interfaces.calIRecurrenceRule); if (!ritem.isByCount) { - var endDate = ritem.endDate; + let endDate = ritem.endDate; if (endDate) { endDate.addDuration(timeDiff); ritem.endDate = endDate; @@ -815,28 +813,33 @@ calRecurrenceInfo.prototype = { } } - var startTimezone = aNewStartTime.timezone; - var exceptions = this.getExceptionIds({}); - var modifiedExceptions = []; - for each (var exid in exceptions) { + let startTimezone = aNewStartTime.timezone; + let modifiedExceptions = []; + for each (let exid in this.getExceptionIds({})) { let ex = this.getExceptionFor(exid); if (ex) { ex = ex.clone(); // track RECURRENCE-IDs in DTSTART's or RDATE's timezone, // otherwise those won't match any longer w.r.t DST: - var rid = ex.recurrenceId; - rid = rid.getInTimezone(rdates[getRidKey(rid)] - ? rdates[getRidKey(rid)].timezone - : startTimezone); + let rid = ex.recurrenceId; + let rdate = rdates[getRidKey(rid)]; + rid = rid.getInTimezone(rdate ? rdate.timezone : startTimezone); rid.addDuration(timeDiff); ex.recurrenceId = rid; - + cal.shiftItem(ex, timeDiff); modifiedExceptions.push(ex); this.removeExceptionFor(exid); } } - for each (var modifiedEx in modifiedExceptions) { + for each (let modifiedEx in modifiedExceptions) { this.modifyException(modifiedEx, true); } + }, + + onIdChange: function cRI_onIdChange(aNewId) { + // patch all overridden items' id: + for each (let item in this.mExceptionMap) { + item.id = aNewId; + } } }; diff --git a/calendar/base/src/calTodo.js b/calendar/base/src/calTodo.js index 7d94021fa5..872943dfbe 100644 --- a/calendar/base/src/calTodo.js +++ b/calendar/base/src/calTodo.js @@ -232,7 +232,7 @@ calTodo.prototype = { } } - this.setProperty("DTSTART", value); + return this.setProperty("DTSTART", value); }, get entryDate() {