Page MenuHomePhorge

D4570.1775490475.diff
No OneTemporary

Authored By
Unknown
Size
7 KB
Referenced Files
None
Subscribers
None

D4570.1775490475.diff

diff --git a/lib/kolab_sync_data_calendar.php b/lib/kolab_sync_data_calendar.php
--- a/lib/kolab_sync_data_calendar.php
+++ b/lib/kolab_sync_data_calendar.php
@@ -803,21 +803,88 @@
throw new Syncroton_Exception_Status_MeetingResponse(Syncroton_Exception_Status_MeetingResponse::INVALID_REQUEST);
}
- if ($event['free_busy']) {
- $old['free_busy'] = $event['free_busy'];
+ $canceled = ($event['_method'] ?? '') == 'CANCEL' || ($event['status'] ?? '') == 'CANCELLED';
+
+ // A single recurring event occurrence
+ if (!empty($event['recurrence_date'])) {
+ $event['recurrence'] = [];
+
+ // A cancellation (it should not happen)
+ if ($canceled) {
+ // remove the matching RDATE entry
+ if (!empty($old['recurrence']['RDATE'])) {
+ foreach ($old['recurrence']['RDATE'] as $j => $rdate) {
+ if ($rdate->format('Ymd') == $event['recurrence_date']->format('Ymd')) {
+ unset($old['recurrence']['RDATE'][$j]);
+ break;
+ }
+ }
+ }
+
+ // add exception to master event
+ $old['recurrence']['EXDATE'][] = $event['recurrence_date'];
+ }
+ else {
+ if ($status) {
+ $this->update_attendee_status($event, $status);
+ $status = null;
+ }
+
+ if (!isset($old['exceptions'])) {
+ if (isset($old['recurrence']['EXCEPTIONS'])) {
+ $old['exceptions'] = &$old['recurrence']['EXCEPTIONS'];
+ }
+ else {
+ $old['exceptions'] = [];
+ }
+ }
+
+ $existing = false;
+ foreach ($old['exceptions'] as $i => $exception) {
+ if (libcalendaring::is_recurrence_exception($event, $exception)) {
+ $old['exceptions'][$i] = $event;
+ $existing = true;
+ }
+ }
+
+ if (!$existing) {
+ $old['exceptions'][] = $event;
+ }
+ }
+ }
+ // User accepted an event cancellation (it should not happen)
+ else if ($canceled) {
+ // FIXME: What should happen when user declined a cancellation?
+ $old['status'] = 'CANCELLED';
+ // FIXME: Should we still update the attendee status at all? Maybe force CANCELLED?
+ $status = null;
+ // FIXME: We probably should just delete the event. Some clients do not handle cancelled status
+ // properly (maybe it's our fault?).
+ }
+ // A main event update
+ else if (isset($event['sequence']) && $event['sequence'] > $old['sequence']) {
+ // FIXME: Can we be smarter here? Should we update everything? What about e.g. new attendees?
+ // And do we need to check the sequence?
+ $props = ['start', 'end', 'title', 'description', 'location', 'free_busy'];
+
+ foreach ($props as $prop) {
+ if (isset($event[$prop])) {
+ $old[$prop] = $event[$prop];
+ }
+ }
+
+ // Copy new custom properties
+ if (!empty($event['x-custom'])) {
+ foreach ($event['x-custom'] as $key => $val) {
+ $old['x-custom'][$key] = $val;
+ }
+ }
}
// Updating an existing event is most-likely a response
// to an iTip request with bumped SEQUENCE
$old['sequence'] += 1;
- // Copy new custom properties
- if (!empty($event['x-custom'])) {
- foreach ($event['x-custom'] as $key => $val) {
- $old['x-custom'][$key] = $val;
- }
- }
-
// Update the event
return $this->save_event($old, $status);
}
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -398,6 +398,8 @@
$result['messageClass'] = 'IPM.Note.SMIME';
}
else if ($event = $this->get_invitation_event_from_message($message)) {
+ // Note: Depending on MessageClass a client will display a proper set of buttons
+ // Either Accept/Maybe/Decline (REQUEST), or "Remove from Calendar" (CANCEL) or none (REPLY).
$result['messageClass'] = 'IPM.Schedule.Meeting.Request';
$result['contentClass'] = 'urn:content-classes:calendarmessage';
@@ -423,12 +425,17 @@
// $this->recurrence_from_kolab($collection, $event, $meeting);
}
- // Organizer
+ // Organizer and attendee status
+ $attendeeStatus = null;
if (!empty($event['attendees'])) {
foreach ($event['attendees'] as $idx => $attendee) {
- if (!empty($attendee['role']) && $attendee['role'] == 'ORGANIZER' && !empty($attendee['email'])) {
- $meeting['organizer'] = $attendee['email'];
- break;
+ if (!empty($attendee['role']) && $attendee['role'] == 'ORGANIZER') {
+ if (!empty($attendee['email'])) {
+ $meeting['organizer'] = $attendee['email'];
+ }
+ }
+ else if (!empty($attendee['status'])) {
+ $attendeeStatus = $attendee['status'];
}
}
}
@@ -447,7 +454,6 @@
'now' => $fileTime,
]);
- // TODO handle other methods
if ($event['_method'] == 'REQUEST') {
$meeting['meetingMessageType'] = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_REQUEST;
@@ -458,6 +464,25 @@
$meeting['responseRequested'] = 1;
} else {
$meeting['meetingMessageType'] = Syncroton_Model_EmailMeetingRequest::MESSAGE_TYPE_NORMAL;
+
+ if ($event['_method'] == 'CANCEL') {
+ $result['messageClass'] = 'IPM.Schedule.Meeting.Canceled';
+ }
+ else if ($event['_method'] == 'REPLY' && $attendeeStatus) {
+ switch ($attendeeStatus) {
+ case 'DECLINED':
+ $result['messageClass'] = 'IPM.Schedule.Meeting.Resp.Neg';
+ break;
+ case 'TENTATIVE':
+ $result['messageClass'] = 'IPM.Schedule.Meeting.Resp.Tent';
+ break;
+ case 'ACCEPTED':
+ $result['messageClass'] = 'IPM.Schedule.Meeting.Resp.Pos';
+ break;
+ // default:
+ // $result['messageClass'] = 'IPM.Schedule.Meeting.Resp'; // ????
+ }
+ }
}
// New time proposals aren't supported by Kolab.

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 6, 3:47 PM (7 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18834391
Default Alt Text
D4570.1775490475.diff (7 KB)

Event Timeline