dav icon indicating copy to clipboard operation
dav copied to clipboard

Scheduling with external organizer

Open pk1234 opened this issue 5 years ago • 13 comments

We are using SabreDAV 3.2.2 and recently activated scheduling, i.e. added

  $server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin());
  $server->addPlugin(new \Sabre\CalDAV\Schedule\IMipPlugin('[email protected]'));

to our server.php.

Case 1: An internal organizer O creates a calendar entry with an internal attendee A. SabreDAV will then create two calendarobjects in the calendars of O and A and the partstat of A will be tentative in both of them. SabreDAV also sends an email to A. If A accepts the invitation (either from within the email-application or from the calendar-application) a confirmation email is be sent to O.

Case 2: An external orgainzer E creates a calendar entry and invites A. In this case no calendarobjects will be created until A accepts the invitation. At this point I would expect SabreDAV to sent a confirmation email to E. But SabreDAV does not send such an email, neither does the calendar app of A since auto-scheduling prevents this to happen in the client.

I tried to analyse the problem and here's what I found out so far:

  • Sabre\CalDAV\Schedule\Plugin::calendarObjectChange() is called after A accepts the invitation. In case 1 parameter $isNew is false, since the calendar of A already contains the event with partstat=tentative. In case 2 $isNew is true.
  • calendarObjectChange() calls processICalendarChange() with either $oldObject==NULL or $oldObject!=NULL
  • processICalendarChange() then calls Sabre\VObject\ITip\Broker::parseEvent() with either $oldCalendar==NULL or $oldCalendar!=NULL
  • parseEvent() has the following lines:
...
} elseif ($oldCalendar) {
    // We need to figure out if the user is an attendee, but we're only
    // doing so if there's an oldCalendar, because we only want to
    // process updates, not creation of new events.
    foreach ($eventInfo['attendees'] as $attendee) {
        if (in_array($attendee['href'], $userHref)) {
            return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']);
        }
    }
}
...
  • so parseEvent() returns an empty array in case 2 since then $oldCalendar==null

So here are my questions:

  • Why is parseEvent() only processing updates of existing events?

It seems to me that this is causing the problem in case 2. I will try to fix this and let you know what happens.

Any other ideas how to fix this?

Peter

pk1234 avatar Feb 17 '20 18:02 pk1234

It seems like the above problem is caused by three minor SabreDAV bugs, namely:

  • Sabre\VObject\ITip\Broker::parseEvent() does not create scheduling events for new events.
  • Email addresses are compared case sensitive. I have created a separate issue for that (#1250)
  • the schedule-agent property of organizers is handeled in such a way, that no messages are sent when I believe they should. This might be caused by a misunderstanding of RFC6638. I have created another issue for this problem a well (#1252)

Here's what I did so far to make SabreDAV handle new vevents: When Sabre\CalDAV\Schedule\Plugin::calendarObjectChange() is called with $isNew==true, processICalendarChange() was called with $oldObj==null. I changed that such that processICalendarChange() is called with a fake old vevent that I create by cloning the new vevent and removing all attendees.

Here's the old code from vendor/sabre/dav/lib/CalDAV/Schedule/Plugin.php:

if (!$isNew) {
    $node = $this->server->tree->getNodeForPath($request->getPath());
    $oldObj = Reader::read($node->get());
} else {
    $oldObj = null;
}

and here's the new one:

if (!$isNew) {
    $node = $this->server->tree->getNodeForPath($request->getPath());
    $oldObj = Reader::read($node->get());
} else {
    $oldObj = null;
    if($vCal->VEVENT){
        $oldObj=clone $vCal;
        unset($oldObj->VEVENT->ATTENDEE);
    }
}

I have no idea wether this has unwanted side-effects. With our setup this worked (when #1250 and #1252 are fixed as well)

Peter

pk1234 avatar Feb 19 '20 18:02 pk1234

@pk1234 Do you get valid ics files with your replies? See also my comments in #1168.

Thanks a lot for your other suggestions.

m-a-v avatar Apr 21 '20 15:04 m-a-v

The solution with the fake $oldObj seems quite hacky.

@evert I know it is sometimes difficult to remember commits that are years old. But could you shed some light on the subject? What I have seen is that creation of new events are not processed (tested with sabre/dav 4.1.0).

because we only want to process updates, not creation of new events.

See changes in (Broker.php): https://github.com/sabre-io/vobject/commit/18c86bcbf8c962b5f1b10b25bd03148aa596f664

The question is when an external organzier sends an event. Which would be the correct method to send a reply for the initial invitation? If I change the else if into an else path then a reply is sent to the organzier using the IMipPlugin. I dont't know if this would have any side effects.

https://github.com/sabre-io/vobject/blob/18c86bcbf8c962b5f1b10b25bd03148aa596f664/lib/Sabre/VObject/ITip/Broker.php#L191

m-a-v avatar May 17 '20 20:05 m-a-v

Just looking at that line you sent:

My best guess is that, when an event is changed by an attendee of an event, the initial event creation is always triggered by the organizer.

When the event is first created, no attendee action has taken place. Only after the attendee performs an action (update RSVP, delete instance, etc) this would result in a message.

But I'm not sure. I don't really have the time to dig into this or the original issues. I'm no longer involved with this project

evert avatar May 17 '20 22:05 evert

OK. Thank you very much for your reply and your great work you've done.

I think this is a bug. If an external organzier creates an event (REQUEST) then no action is taken by the attendee since $oldObj is null. The initial action for an event (e.g. ACCEPT) should also create a reply in my point of view.

m-a-v avatar May 18 '20 13:05 m-a-v

Under what condition / what clients will create events like this? Just curious, your reasoning seems sound

evert avatar May 19 '20 19:05 evert

I've only tested it with an external organizer (Google Calendar and Outlook 365) and the attendee is using eM Client in conjunction with sabre/dav. The external organzier creates an event and sends the invitation to the client (eM Client). In the client software I then accept the event.

image

m-a-v avatar May 19 '20 20:05 m-a-v

Is this going to be fixed officially? I tried changing the else if to an else but RSVP emails are still not being sent. My use case is simple: I routinely get invited to events by people using Google Calendar. I RSVP in my client (Thunderbird), but the person who sent me the event invite never gets my RSVP email.

Emails are properly sent to attendees when I create and edit events, they only don't work for RSVPs.

The solution with the fake $oldObj seems quite hacky.

@evert I know it is sometimes difficult to remember commits that are years old. But could you shed some light on the subject? What I have seen is that creation of new events are not processed (tested with sabre/dav 4.1.0).

because we only want to process updates, not creation of new events.

See changes in (Broker.php): sabre-io/vobject@18c86bc

The question is when an external organzier sends an event. Which would be the correct method to send a reply for the initial invitation? If I change the else if into an else path then a reply is sent to the organzier using the IMipPlugin. I dont't know if this would have any side effects.

https://github.com/sabre-io/vobject/blob/18c86bcbf8c962b5f1b10b25bd03148aa596f664/lib/Sabre/VObject/ITip/Broker.php#L191

mc-buckets avatar Dec 01 '20 03:12 mc-buckets

Update: changing the elseif to an else and also doing the fixes outlined in #1250 and #1252 has everything working. I'm still curious why this wouldn't just work out of the box, the use case is common.

If I knew more PHP I would submit PRs w/ tests!

mc-buckets avatar Dec 01 '20 03:12 mc-buckets

I'm no longer active on this project, so it's up to others to submit a PR and release it.

evert avatar Dec 01 '20 04:12 evert

I tried to find examples regarding Scheduling but I couldn't so far.

I got CalDAV working on my localhost environment (php8 + apache 2.4, dockerized), added to Mac OS Calendar.

If on Mac OS' Calendar app I add some invitees and right click Mail Event it goes as .ics file but there's no option natively on the email clients I tested to reply that (Accept, Decline, Tentative, Propose New Time).

I initially did a small iCal library to handle events (mostly meetings) from one software that I develop/maintain, and it was being handled as a subscribed calendar (not CalDAV, just plain iCalendar format), with REST API and a simple frontend to change invitee's statuses but it was getting big responses, taking longer to update, etc.

Then I decided to find a native solution and I came to sabre/dav and a few other libraries, but I couldn't find enough reading material in any library (or RFCs) to describe the process of Scheduling an event and how "replying" to that would work.

Closest to what I got was just displaying the event, but the user has no option to Confirm presence.

Screen Shot 2021-02-03 at 1 10 45 PM

Any suggestions on how to implement that?

icaroscherma avatar Feb 03 '21 21:02 icaroscherma

@icaroscherma please don't hijack this bug report. This is off-topic.

You need IMIP: https://tools.ietf.org/html/rfc6047

evert avatar Feb 03 '21 22:02 evert

Hi everybody,

an external organizer does not get a reply if an attendee accepts his invitation. I temporarily fixed that in our 3.2.2 SabreDAV installation (see above) and the 2020-plan was to fix it in version 4 as well.

Now its 2022, we migrated to SabreDAV 4.3.1 and the problem is still there.

It seems obvious to me that an external organizer should receive a reply if an attendee accepts his invitation. In that case processICalendarChange() is called with $oldObject===null (unless my dirty hack is used)

Do we agree, that this is a BUG? Or should I do some RFC-reading first to find out whether this is a bug or not?

Peter

pk1234 avatar Feb 20 '22 17:02 pk1234