Calendar:Recurrence and Exceptions

From MozillaWiki
Jump to: navigation, search

Recurring and repeating items need to allow the user to modify properties of specific occurrences, but still allow the rest to be tied in to the main parent item. For example, a weekly meeting could have its location or time changed for one week, but modifying the attendees should change all occurrences.

How does one express this relationship with ics?
RECURRENCE-ID as a DATE-TIME is used to refer to an explicit occurrence. It is used as the effective DTSTART of the occurrence. DTSTART and UID should be the same across all occurrences of an item.

Some platforms, notably Outlook and Lotus Notes do strange things with this, including setting DTSTART and RECURRENCE-ID to be the same. The UID still refers to the parent, however, so we should rely on UID, and should just ignore DTSTART for occurrences.

What do we do currently?
Current calendar code supports recurring/repeating events, as well as exceptions. However, only the base event is parsed. There is no support for occurrences-with-changes.
Do caldav servers/ics clients support the full RECURRENCE-ID/DTSTART/UID mechanism?
Need some ICS to test this, and see what they round trip as.
Possible solutions
  • Non-Solution: When you need to update only one occurrence, duplicate the base item, add an exception to the base item for that date, and modify this item. This completely detaches the item from the base; if you update the base item, those updates won't propagate to the detached occurrence.

Other than that, we need to modify our back-end architecture in one of a few different ways:

  • Modify occurrence items to contain a reference to a base item, and a bitmask/list of properties that they explicitly declare. Any queries to properties not in that list would be passed up to the base item.
  • Modify occurence items to contain a reference to a base item, and have the base item contain a list of occurrences. When a property in the base item is changed, it looks at all its occurrences and checks to see if the occurrence value is the same as the old value that was just changed; if so, it updates it to the new value.


UI work necessary
We'll need some sort of "Do you want to modify all occurrences, or just this one?" dialog when modifying an occurrence. We'll also need a way to communicate what specifically was modified for that occurrence, perhaps through greying-out the non-modified bits or somesuch.
CalDAV Server handling of recurrence/exceptions

The current CalDAV server we have access to exposes a daily 5-day repeating event in this fashion:

  • 5 VEVENTs are in the .ics file
  • The first VEVENT has 4 RDATEs for the other 4 events
  • Each VEVENT has the same UID
  • Each VEVENT has a DTSTART and RECURRENCE-ID, both of which are equal, and correspond to that event's start time
  • Exceptions/changes are simply differences in appropriate values in each VEVENT; there is no "inheritance" from the frist VEVENT.
  • Events which are identical except for the start date are still represented as separate VEVENTs

The above doesn't quite conform to the spec; the spec says that repeat events of this form should:

  • Have distinct UIDs
  • All have a DTSTART of the first VEVENT (I don't understand why, but that's what it says)
  • Have the RECURRENCE-ID be a DATETIME that is used as the actual DTSTART (Again, I don't understand why; if anything, it would make much more sense if the DTSTART was the actual event start, and the RECURRENCE-ID contained the UID of the first/parent event)

The UI for editing these events is a normal event edit box, but with a list of all instances of this event. The user can select one or more of these events and make changes to those he has selected. Fields that don't match in all selected events (e.g. if the title of one event was changed) are cleared; setting a new value overwrites all selected events.


Proposal

The most straightforward approach is the proxy copy-on-write Item scheme. This would mean having an Item impl that looks at a local hash to see if a property exists locally on read; if it does, it returns that value. If it doesn't, it returns the value from the .baseItem's property. An additional interface would have to be created:

interface calIProxyItem : nsISupports {
 readonly attribute calIItemBase baseItem;
 void initializeProxy(in calIItemBase aBaseItem);
 boolean isPropertyModified(in string aPropertyName);
 void resetProperty(in string aPropertyName);
}

This interface would be available on any calIItemBase if it was created with the right ContractID. initializeProxy would be a one-time call to set the base item. isPropertyModified would return TRUE if the property has a local modification from the base item, and resetProperty would remove the item's private value for the given property.

calIRecurrenceInfo would gain new methods:

calIProxyItem createException();

Convenience call that would create the calIProxyItem and initialize it to the calIRecurrenceInfo's calIItemBase. The returned calIProxyItem would be mutable.

void putException(in calIDateTime aStartTime, in calIProxyItem aProxyItem);

Adds or updates the proxy itembase as an exception for the occurrence that starts at aStartTime.

calIProxyItem getException(in calIDateTime aStartTime);

Get the calIProxyItem that refers to the given base occurrence start time (i.e. the start time which this calIProxyItem is replacing). The result can be QI'd to calIItemBase.

void removeException(in calIDateTime aStartTime);

Removes the exception for aStartTime.