diff --git a/lib/kolab_sync_data.php b/lib/kolab_sync_data.php --- a/lib/kolab_sync_data.php +++ b/lib/kolab_sync_data.php @@ -1265,10 +1265,12 @@ if ($count == 2 && $name_items[0] == 'x-custom') { $value = null; - foreach ((array) $data['x-custom'] as $val) { - if (is_array($val) && $val[0] == $name_items[1]) { - $value = $val[1]; - break; + if (!empty($data['x-custom']) && is_array($data['x-custom'])) { + foreach ($data['x-custom'] as $val) { + if (is_array($val) && $val[0] == $name_items[1]) { + $value = $val[1]; + break; + } } } 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 @@ -116,7 +116,8 @@ const SENSITIVITY_PRIVATE = 2; const SENSITIVITY_CONFIDENTIAL = 3; - const KEY_DTSTAMP = 'x-custom.X-ACTIVESYNC-DTSTAMP'; + const KEY_DTSTAMP = 'x-custom.X-ACTIVESYNC-DTSTAMP'; + const KEY_REPLYTIME = 'x-custom.X-ACTIVESYNC-REPLYTIME'; /** * Mapping of attendee status @@ -231,7 +232,7 @@ break; case 'free_busy': - if (!$is_outlook && !empty($value)) { + if (!empty($value)) { $value = $this->busyStatusMap[$value]; } break; @@ -287,17 +288,17 @@ $user_emails = $this->user_emails(); foreach ($event['attendees'] as $idx => $attendee) { - $att = array(); - - if ($email = $attendee['email']) { - $att['email'] = $email; - } - else { + if (empty($attendee['email'])) { // In Activesync email is required continue; } - $att['name'] = $attendee['name'] ?: $email; + $email = $attendee['email']; + + $att = [ + 'email' => $email, + 'name' => !empty($attendee['name']) ? $attendee['name'] : $email, + ]; $type = isset($attendee['role']) ? $this->attendeeTypeMap[$attendee['role']] : null; $status = isset($attendee['status']) ? $this->attendeeStatusMap[$attendee['status']] : null; @@ -306,7 +307,7 @@ if (isset($attendee['cutype']) && strtolower($attendee['cutype']) == 'resource') { $att['attendeeType'] = self::ATTENDEE_TYPE_RESOURCE; } else { - $att['attendeeType'] = $type ?: self::ATTENDEE_TYPE_REQUIRED; + $att['attendeeType'] = $type ?: self::ATTENDEE_TYPE_REQUIRED; } $att['attendeeStatus'] = $status ?: self::ATTENDEE_STATUS_UNKNOWN; } @@ -316,7 +317,7 @@ $resp_type = $status ?: self::ATTENDEE_STATUS_UNKNOWN; // Synchronize the attendee status to the event status to get the same behaviour as outlook. - if ($is_outlook) { + if ($is_outlook && isset($attendee['status'])) { if ($attendee['status'] == 'ACCEPTED') { $result['busyStatus'] = self::BUSY_STATUS_BUSY; } @@ -324,7 +325,6 @@ $result['busyStatus'] = self::BUSY_STATUS_TENTATIVE; } } - } $result['attendees'][] = new Syncroton_Model_EventAttendee($att); @@ -341,6 +341,17 @@ $result['responseRequested'] = $result['meetingStatus'] == 3 && $user_rsvp ? 1 : 0; $result['responseType'] = $result['meetingStatus'] == 3 ? $resp_type : null; + // Appointment Reply Time (without it Outlook displays e.g. "Accepted on None") + if ($resp_type != self::ATTENDEE_STATUS_UNKNOWN) { + if ($reply_time = $this->getKolabDataItem($event, self::KEY_REPLYTIME)) { + $result['appointmentReplyTime'] = new DateTime($reply_time, new DateTimeZone('UTC')); + } elseif (!empty($event['changed'])) { + $reply_time = clone $event['changed']; + $reply_time->setTimezone(new DateTimeZone('UTC')); + $result['appointmentReplyTime'] = $reply_time; + } + } + return $as_array ? $result : new Syncroton_Model_Event($result); } @@ -654,11 +665,9 @@ case 'DECLINED': $event['free_busy'] = 'free'; break; } */ - // Store Outlook response timestamp for further use - if (stripos($this->device->devicetype, 'outlook') !== false) { - $dtstamp = new DateTime('now', new DateTimeZone('UTC')); - $dtstamp = $dtstamp->format(DateTime::ATOM); - } + // Store response timestamp for further use + $reply_time = new DateTime('now', new DateTimeZone('UTC')); + $this->setKolabDataItem($event, self::KEY_REPLYTIME, $reply_time->format('Ymd\THis\Z')); // Update/Save the event if (empty($existing)) { @@ -778,6 +787,13 @@ // 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); }