/**
* 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;
}