/** * Imports the values for this event from a MS ActiveSync Message. * * @param Horde_ActiveSync_Message_Appointment $message * @throws Kronolith_Exception */ public function fromASAppointment(Horde_ActiveSync_Message_Appointment $message) { /* New event? */ if ($this->id === null) { $this->creator = $GLOBALS['registry']->getAuth(); } // EAS 16.0 sends new/changed exceptions as "orphaned" instances so // they need to be handled separately. if ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_SIXTEEN && !empty($message->instanceid)) { if (!$this->_handleEas16Exception($message)) { throw new Kronolith_Exception('Error handling EAS 16 exceptions.'); } return; } // Meeting requests come with their own UID value, but only if we // are not using EAS 16.0 (16 sends a ClientUID value, but it's only // purpose is to prevent duplicate events. We currently don't store // this value. if ($message->getProtocolVersion < Horde_ActiveSync::VERSION_SIXTEEN) { $client_uid = $message->getUid(); if (empty($this->uid) && !empty($client_uid)) { $this->uid = $message->getUid(); } } // EAS 16 disallows the client to send/set the ORGANIZER. // Even so, add the extra check of not allowing the organizer to // be changed by the client. if (!$message->isGhosted('organizer')) { $organizer = $message->getOrganizer(); if ($message->getProtocolVersion() < Horde_ActiveSync::VERSION_SIXTEEN) { if ($organizer['email'] && empty($this->organizer)) { $this->organizer = $organizer['email']; } } } if (!$message->isGhosted('subject') && strlen($title = $message->getSubject())) { $this->title = $title; } if ($message->getProtocolVersion() == Horde_ActiveSync::VERSION_TWOFIVE && !$message->isGhosted('body') && strlen($description = $message->getBody())) { $this->description = $description; } elseif ($message->getProtocolVersion() > Horde_ActiveSync::VERSION_TWOFIVE && !$message->isGhosted('airsyncbasebody')) { if ($message->airsyncbasebody->type == Horde_ActiveSync::BODYPREF_TYPE_HTML) { $this->description = Horde_Text_Filter::filter($message->airsyncbasebody->data, 'Html2text'); } else { $this->description = $message->airsyncbasebody->data; } } // EAS 16 location property is an AirSyncBaseLocation object, not // a string. $location = $message->getLocation(); if (is_object($location)) { // @todo - maybe build a more complete name based on city/country? $location = $location->displayname; } if (!$message->isGhosted('location') && strlen($location)) { $this->location = $location; } /* Date/times */ $dates = $message->getDatetime(); if (!$message->isGhosted('alldayevent')) { $this->allday = $dates['allday']; } if (!empty($this->id) && $dates['allday'] && $message->getProtocolVersion() == Horde_ActiveSync::VERSION_SIXTEEN) { // allday events are handled differently when updating vs creating // new when using EAS 16.0 $this->start = new Horde_Date(array('year' => !$message->isGhosted('starttime') ? $dates['start']->year : $this->start->year, 'month' => !$message->isGhosted('starttime') ? $dates['start']->month : $this->start->month, 'mday' => !$message->isGhosted('starttime') ? $dates['start']->mday : $this->start->mday), !empty($this->timezone) ? $this->timezone : date_default_timezone_get()); $this->end = new Horde_Date(array('year' => !$message->isGhosted('endtime') ? $dates['end']->year : $this->end->year, 'month' => !$message->isGhosted('endtime') ? $dates['end']->month : $this->end->month, 'mday' => !$message->isGhosted('endtime') ? $dates['end']->mday : $this->end->mday), !empty($this->timezone) ? $this->timezone : date_default_timezone_get()); } else { $tz = !$message->isGhosted('timezone') ? $message->getTimezone() : $this->timezone; $this->start = !$message->isGhosted('starttime') ? clone $dates['start'] : $this->start; try { $this->start->setTimezone($tz); } catch (Horde_Date_Exception $e) { $tz = date_default_timezone_get(); Horde::log(sprintf('Unable to set timezone. Using %s.', $tz), 'WARN'); $this->start->setTimezone($tz); } $this->end = !$message->isGhosted('endtime') ? clone $dates['end'] : $this->end; $this->end->setTimezone($tz); if ($tz != date_default_timezone_get()) { $this->timezone = $tz; } } /* Sensitivity */ if (!$message->isGhosted('sensitivity')) { $this->private = $message->getSensitivity() == Horde_ActiveSync_Message_Appointment::SENSITIVITY_PRIVATE || $message->getSensitivity() == Horde_ActiveSync_Message_Appointment::SENSITIVITY_CONFIDENTIAL ? true : false; } /* Busy Status */ if (!$message->isGhosted('meetingstatus')) { if ($message->getMeetingStatus() == Horde_ActiveSync_Message_Appointment::MEETING_CANCELLED) { $status = Kronolith::STATUS_CANCELLED; } else { $status = $message->getBusyStatus(); switch ($status) { case Horde_ActiveSync_Message_Appointment::BUSYSTATUS_BUSY: case Horde_ActiveSync_Message_Appointment::BUSYSTATUS_ELSEWHERE: $status = Kronolith::STATUS_CONFIRMED; break; case Horde_ActiveSync_Message_Appointment::BUSYSTATUS_FREE: $status = Kronolith::STATUS_FREE; break; case Horde_ActiveSync_Message_Appointment::BUSYSTATUS_TENTATIVE: $status = Kronolith::STATUS_TENTATIVE; break; // @TODO: not sure how "Out" should show in kronolith... // @TODO: not sure how "Out" should show in kronolith... case Horde_ActiveSync_Message_Appointment::BUSYSTATUS_OUT: $status = Kronolith::STATUS_CONFIRMED; default: // EAS Specifies default should be free. $status = Kronolith::STATUS_FREE; } } $this->status = $status; } // Alarms: // EAS allows setting an alarm at the time of the event, and // signifies this with a '0' minutes before. Kronolith does not // support this, and uses '0' to mean no alarm. Make these fire // at 1 minute prior. if (!$message->isGhosted('reminder')) { $alarm = $message->getReminder(); if ($alarm === 0 || $alarm === "0") { // "At time of event" $this->alarm = 1; } elseif ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_SIXTEEN) { if (empty($alarm)) { // Client sent an empty reminder tag meaning no alarm. $this->alarm = 0; } else { // It was either missing (no alarm) or set with a value. $this->alarm = $alarm; } } elseif ($alarm) { $this->alarm = $alarm; } else { $this->alarm = 0; } } /* Recurrence */ if (!$message->isGhosted('recurrence') && ($rrule = $message->getRecurrence())) { /* Exceptions */ /* Since AS keeps exceptions as part of the original event, we need * to delete all existing exceptions and re-create them. The only * drawback to this is that the UIDs will change. */ $kronolith_driver = $this->getDriver(); // EAS 16 doesn't update exception data on edits of the base event // but still sends the recurrence rule. We need to replace the // recurrence rule if it changed (and overwrite any exceptions), // otherwise leave it alone. if ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_SIXTEEN) { if (!empty($this->uid) && !empty($this->recurrence) && !$this->recurrence->isEqual($rrule)) { $this->disconnectExceptions(true); $this->recurrence = $rrule; } } if (!empty($this->uid) && $message->getProtocolVersion() < Horde_ActiveSync::VERSION_SIXTEEN) { // EAS 16.0 NEVER adds exceptions from withing the base event, // so we can't delete the existing exceptions - we don't have // the current list to replace them with. $search = new StdClass(); $search->baseid = $this->uid; $results = $kronolith_driver->search($search); foreach ($results as $days) { foreach ($days as $exception) { $kronolith_driver->deleteEvent($exception->id); } } $erules = $message->getExceptions(); foreach ($erules as $rule) { /* Readd the exception event, but only if not deleted */ if (!$rule->deleted) { $event = $kronolith_driver->getEvent(); $times = $rule->getDatetime(); if ($message->getProtocolVersion() < Horde_ActiveSync::VERSION_SIXTEEN) { $original = $rule->getExceptionStartTime(); } else { $original = $rule->instanceid; } try { $original->setTimezone($tz); } catch (Horde_Date_Exception $e) { $tz = date_default_timezone_get(); Horde::log(sprintf('Unable to set timezone. Using %s.', $tz), 'WARN'); $original->setTimezone($tz); } $this->recurrence->addException($original->format('Y'), $original->format('m'), $original->format('d')); $event->start = $times['start']; $event->end = $times['end']; $event->start->setTimezone($tz); $event->end->setTimezone($tz); $event->allday = $times['allday']; $event->title = $rule->getSubject(); $event->title = empty($event->title) ? $this->title : $event->title; $event->description = $rule->getBody(); $event->description = empty($event->description) ? $this->description : $event->description; $event->baseid = $this->uid; $event->exceptionoriginaldate = $original; $event->initialized = true; if ($tz != date_default_timezone_get()) { $event->timezone = $tz; } $event->save(); } else { /* For exceptions that are deletions, just add the exception */ if ($message->getProtocolVersion() < Horde_ActiveSync::VERSION_SIXTEEN) { $exceptiondt = $rule->getExceptionStartTime(); } else { $exceptiondt = $rule->instanceid; } try { $exceptiondt->setTimezone($tz); } catch (Horde_Date_Exception $e) { $tz = date_default_timezone_get(); Horde::log(sprintf('Unable to set timezone. Using %s.', $tz), 'WARN'); $exceptiondt->setTimezone($tz); } $this->recurrence->addException($exceptiondt->format('Y'), $exceptiondt->format('m'), $exceptiondt->format('d')); } } } } /* Attendees */ if (!$message->isGhosted('attendees')) { $attendees = $message->getAttendees(); foreach ($attendees as $attendee) { $response_code == false; if ($message->getProtocolVersion < Horde_ActiveSync::VERSION_SIXTEEN) { switch ($attendee->status) { case Horde_ActiveSync_Message_Attendee::STATUS_ACCEPT: $response_code = Kronolith::RESPONSE_ACCEPTED; break; case Horde_ActiveSync_Message_Attendee::STATUS_DECLINE: $response_code = Kronolith::RESPONSE_DECLINED; break; case Horde_ActiveSync_Message_Attendee::STATUS_TENTATIVE: $response_code = Kronolith::RESPONSE_TENTATIVE; break; default: $response_code = Kronolith::RESPONSE_NONE; } switch ($attendee->type) { case Horde_ActiveSync_Message_Attendee::TYPE_REQUIRED: $part_type = Kronolith::PART_REQUIRED; break; case Horde_ActiveSync_Message_Attendee::TYPE_OPTIONAL: $part_type = Kronolith::PART_OPTIONAL; break; case Horde_ActiveSync_Message_Attendee::TYPE_RESOURCE: $part_type = Kronolith::PART_REQUIRED; } } $this->addAttendee($attendee->email, $part_type, $response_code, $attendee->name); } } /* Categories (Tags) */ if (!$message->isGhosted('categories')) { $this->_tags = $message->getCategories(); } // 14.1 if ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_FOURTEENONE && !$message->isGhosted('onlinemeetingexternallink')) { $this->url = $message->onlinemeetingexternallink; } /* Flag that we are initialized */ $this->initialized = true; }