Mike Hayes <[EMAIL PROTECTED]> writes:

> In broad terms, my understanding of how Evolution treats 
> recurrence is as follows: 
> - When creating a recurring component, the component is passed to
>   the backend. The backend stores the components (for instance,
>   the file backend eventually writes the component to file) but
>   does not interpret its recurrence rules. In fact, this seems to
>   be an implicit rule for all backends - is this the case? 

True.  This seems to be the easiest and most economical way to write
the backends.

> - When a given view is ready to display it components, it gets
>   all the object in it's range, and interprets their recurrence 
>   rules to generate all instances that recur in the range.
> 
> I guess my problem with all this is that the recurrence is
> interpreted by the client. As I see it, this breaks the 
> model/view approach adopted for backend/client in a big way. 
> I had expected that the backend would be the only one to interpret 
> the recurrence rules (leaving aside the preview pane in the 
> recurrence tab).
> 
> Also, I believe it leads to a number of problems : 
> - To ensure you have retrieved correctly over a range, you have
>   to initially retrieve _all_ components in the backend, expand 
>   their recurrence rules in the client, and then check within 
>   that given range. This becomes a problem as the amount of data
>   you need to retrieve gets larger, and/or when you're getting 
>   your data over a slow network connection (I'm looking at 
>   writing a backend to support iPlanet Calendar Server, hence
>   these questions).

Ummm, not quite.  Let me clarify.

When you get the objects in a range from the Wombat, you get back a
list of UIDs of the objects that would actually occur in that range if
their recurrence set were expanded.  The client then is guaranteed
that when it expands the recurrence sets of those objects, they will
fall within the time range it requested.

This is done like that for historical reasons.  The alarm API, which
is newer, gives you back a list of actual occurrences.  From the IDL:

        /* An alarm trigger instance */
        struct CalAlarmInstance {
                CalAlarmUID auid;
                Time_t trigger;
                Time_t occur;
        };

Every alarm has a unique identifier within its parent component.  This
structure identifies an alarm, the time at which it triggers, and the
corresponding occurrence time of the object (e.g. trigger may be 4:55
if the occurrence is at 5:00 and the alarm is set to trigger 5 minutes
before the actual appointment).

        /* Used to represent a list of alarm triggers for a single component */
        typedef sequence<CalAlarmInstance> CalAlarmInstanceSeq;

        /* Alarms for a component */
        struct CalComponentAlarms {
                CalObj calobj;
                CalAlarmInstanceSeq alarms;
        };

This structure represents all the trigger instances that an object
would have within a time range.  The algorithm for computing this is
described in evolution/doc/devel/calendar/alarm-generation.sgml.
Basically you expand the recurrence set of the object for the
specified time range and figure out the alarm trigger times that would
also fall within the range.

So the structure gives you back an object an a bunch of alarm triggers
for it.

        /* Used to represent a list of components plus their triggers */
        typedef sequence<CalComponentAlarms> CalComponentAlarmsSeq;

        /* Gets a list of the components that have alarms that trigger
         * in the specified range of time, and the trigger/occurrence
         * structures themselves.  
         */
        CalComponentAlarmsSeq getAlarmsInRange (in Time_t start, in Time_t end)
                raises (InvalidRange);

This is basically the same as ::getObjectsInRange() but for alarm triggers.

        /* Gets the alarms for the specified component that trigger in
         * the specified time range.  
         */
        CalComponentAlarms getAlarmsForObject (in CalObjUID uid,
                                               in Time_t start, in Time_t end)
                raises (NotFound, InvalidRange);

And this is for a single object.

So, just as alarms are fully expanded in the server and then sent to
the client, you could do the same thing for recurrences.  At some
point I wanted to change the Wombat interfaces a bit so that you would
have

        typedef sequence<Time_t> Time_tSeq;

        struct CalObjectOccurrences {
                CalObj calobj;
                Time_tSeq occurrences;
        };

        typedef sequence<CalObjectOccurrences> CalObjectOccurrencesSeq;

        CalObjectOccurrencesSeq getObjectsInRange (in CalObjType type, 
                                                   in Time_t start, 
                                                   in Time_t end)
                raises (InvalidRange);

        CalObjectOccurrences getObjectOccurrences (in CalObjUID uid
                                                   in Time_t start, 
                                                   in Time_t end)
                raises (InvalidRange, NotFound);

This would make the API more consistent and would perhaps allow us to
avoid expanding recurrence rules in the client.

I think the only place where you *really* need to expand recurrences
in the client is in the event editor dialog, for the recurrence
preview widget.  The object you are editing may not be in the server
yet and you need to expand the recurrences so that the user can
preview them.

> - It makes it impossible to change recurring components in a
>   controlled way. Suppose I have an appointment that recurs every
>   week on Tuesday at 2pm. But this week I have to go someplace in
>   the afternoon and so I want to change this instance to occur at
>   10am. If I update the component, all instances get changed,
>   which is not what I want at all.

This is a canonical example from the IETF.  You would add an exception
date (EXDATE) to the component for this week's Tuesday and you would
add an explicit recurrence date (RDATE) for 10am.

Right now we do support editing EXDATEs but not RDATEs.  (We do not
support editing RDATEs or EXRULEs, but the actual recurrence engine
should expand them correctly and they should actually occur at all the
proper times for the views and alarms).

RDATEs may be a bit of overkill within Evolution since in this context
they are about the same as adding an EXDATE to the recurring component
and then adding another component to the calendar at the time you
want.  You could even do this with cut&paste :)

> - Ultimately, if you want to be a "client" of wombat for the 
>   purpose of retrieving calendaring data, this forces you to have
>   the capability to interpret recurrence rules.

I think you would just need this in the recurrence-preview case I
mentioned above.

Feel free to work on a patch to implement the API above; I don't know
how much work would be required in the calendar views to use the
Wombat in this way.

  Federico

_______________________________________________
evolution-hackers maillist  -  [EMAIL PROTECTED]
http://lists.ximian.com/mailman/listinfo/evolution-hackers

Reply via email to