/** * testXMozSnooze * * @see 0007682: CalDav - Tine - Thunderbird - Palm Pre */ public function testXMozSnooze() { $vcalendarStream = Calendar_Frontend_WebDAV_EventTest::getVCalendar(dirname(__FILE__) . '/../../../Import/files/lightning_snooze.ics'); $converter = Calendar_Convert_Event_VCalendar_Factory::factory(Calendar_Convert_Event_VCalendar_Factory::CLIENT_THUNDERBIRD); $event = $converter->toTine20Model($vcalendarStream); $alarmSnoozeTime = Calendar_Controller_Alarm::getSnoozeTime($event->alarms->getFirstRecord()); $this->assertTrue($alarmSnoozeTime instanceof DateTime); $this->assertEquals('2013-04-12 06:24:46', $alarmSnoozeTime->toString()); }
/** * converts an iTIP event to a tine20 event * * @param Calendar_Model_Event $_event * @param Calendar_Model_Event $_currentEvent (not iTIP!) */ protected function _fromiTIP($_event, $_currentEvent) { if (!$_event->rrule) { $_event->exdate = NULL; } if ($_event->exdate instanceof Tinebase_Record_RecordSet) { try { $currExdates = $this->_eventController->getRecurExceptions($_event, TRUE); $this->getAlarms($currExdates); $currClientExdates = $this->_eventController->getRecurExceptions($_event, TRUE, $this->getEventFilter()); $this->getAlarms($currClientExdates); } catch (Tinebase_Exception_NotFound $e) { $currExdates = NULL; $currClientExdates = NULL; } foreach ($_event->exdate as $idx => $exdate) { try { $this->_prepareException($_event, $exdate); } catch (Exception $e) { } $currExdate = $currExdates instanceof Tinebase_Record_RecordSet ? $currExdates->filter('recurid', $exdate->recurid)->getFirstRecord() : NULL; if ($exdate->is_deleted) { // reset implicit filter fallouts and mark as don't touch (seq = -1) $currClientExdate = $currClientExdates instanceof Tinebase_Record_RecordSet ? $currClientExdates->filter('recurid', $exdate->recurid)->getFirstRecord() : NULL; if ($currClientExdate && $currClientExdate->is_deleted) { $_event->exdate[$idx] = $currExdate; $currExdate->seq = -1; continue; } } $this->_fromiTIP($exdate, $currExdate ? $currExdate : clone $_currentEvent); } } // assert organizer $_event->organizer = $_event->organizer ?: ($_currentEvent->organizer ?: $this->_calendarUser->user_id); $this->_addAttendeeWithoutEmail($_event, $_currentEvent); $CUAttendee = Calendar_Model_Attender::getAttendee($_event->attendee, $this->_calendarUser); $currentCUAttendee = Calendar_Model_Attender::getAttendee($_currentEvent->attendee, $this->_calendarUser); $isOrganizer = $_event->isOrganizer($this->_calendarUser); // remove perspective if ($CUAttendee && !$isOrganizer) { $CUAttendee->transp = $_event->transp; $_event->transp = $_currentEvent->transp ? $_currentEvent->transp : $_event->transp; } // apply changes to original alarms $_currentEvent->alarms = $_currentEvent->alarms instanceof Tinebase_Record_RecordSet ? $_currentEvent->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); $_event->alarms = $_event->alarms instanceof Tinebase_Record_RecordSet ? $_event->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); foreach ($_currentEvent->alarms as $currentAlarm) { if (Calendar_Model_Attender::isAlarmForAttendee($this->_calendarUser, $currentAlarm)) { $alarmUpdate = Calendar_Controller_Alarm::getMatchingAlarm($_event->alarms, $currentAlarm); if ($alarmUpdate) { // we could map the alarm => save ack & snooze options if ($dtAck = Calendar_Controller_Alarm::getAcknowledgeTime($alarmUpdate)) { Calendar_Controller_Alarm::setAcknowledgeTime($currentAlarm, $dtAck, $this->getCalendarUser()->user_id); } if ($dtSnooze = Calendar_Controller_Alarm::getSnoozeTime($alarmUpdate)) { Calendar_Controller_Alarm::setSnoozeTime($currentAlarm, $dtSnooze, $this->getCalendarUser()->user_id); } $_event->alarms->removeRecord($alarmUpdate); } else { // alarm is to be skiped/deleted if (!$currentAlarm->getOption('attendee')) { Calendar_Controller_Alarm::skipAlarm($currentAlarm, $this->_calendarUser); } else { $_currentEvent->alarms->removeRecord($currentAlarm); } } } } if (!$isOrganizer) { $_event->alarms->setOption('attendee', Calendar_Controller_Alarm::attendeeToOption($this->_calendarUser)); } $_event->alarms->merge($_currentEvent->alarms); // assert organizer for personal calendars to be calendar owner if ($this->_currentEventFacadeContainer && $this->_currentEventFacadeContainer->getId() == $_event->container_id && $this->_currentEventFacadeContainer->type == Tinebase_Model_Container::TYPE_PERSONAL && !$_event->hasExternalOrganizer()) { $_event->organizer = $this->_calendarUser->user_id; } // in MS world only cal_user can do status updates if ($CUAttendee) { $CUAttendee->status_authkey = $currentCUAttendee ? $currentCUAttendee->status_authkey : NULL; } }
/** * convert calendar event to Sabre\VObject\Component * * @param \Sabre\VObject\Component\VCalendar $vcalendar * @param Calendar_Model_Event $_event * @param Calendar_Model_Event $_mainEvent */ protected function _convertCalendarModelEvent(\Sabre\VObject\Component\VCalendar $vcalendar, Calendar_Model_Event $_event, Calendar_Model_Event $_mainEvent = null) { // clone the event and change the timezone $event = clone $_event; $event->setTimezone($event->originator_tz); $lastModifiedDateTime = $_event->last_modified_time ? $_event->last_modified_time : $_event->creation_time; if (!$event->creation_time instanceof Tinebase_DateTime) { throw new Tinebase_Exception_Record_Validation('creation_time needed for conversion to Sabre\\VObject\\Component'); } $vevent = $vcalendar->create('VEVENT', array('CREATED' => $_event->creation_time->getClone()->setTimezone('UTC'), 'LAST-MODIFIED' => $lastModifiedDateTime->getClone()->setTimezone('UTC'), 'DTSTAMP' => Tinebase_DateTime::now(), 'UID' => $event->uid)); $vevent->add('SEQUENCE', $event->hasExternalOrganizer() ? $event->external_seq : $event->seq); if ($event->isRecurException()) { $originalDtStart = $_event->getOriginalDtStart()->setTimezone($_event->originator_tz); $recurrenceId = $vevent->add('RECURRENCE-ID', $originalDtStart); if ($_mainEvent && $_mainEvent->is_all_day_event == true) { $recurrenceId['VALUE'] = 'DATE'; } } // dtstart and dtend $dtstart = $vevent->add('DTSTART', $_event->dtstart->getClone()->setTimezone($event->originator_tz)); if ($event->is_all_day_event == true) { $dtstart['VALUE'] = 'DATE'; // whole day events ends at 23:59:(00|59) in Tine 2.0 but 00:00 the next day in vcalendar $event->dtend->addSecond($event->dtend->get('s') == 59 ? 1 : 0); $event->dtend->addMinute($event->dtend->get('i') == 59 ? 1 : 0); $dtend = $vevent->add('DTEND', $event->dtend); $dtend['VALUE'] = 'DATE'; } else { $dtend = $vevent->add('DTEND', $event->dtend); } // auto status for deleted events if ($event->is_deleted) { $event->status = Calendar_Model_Event::STATUS_CANCELED; } // event organizer if (!empty($event->organizer)) { $organizerContact = $event->resolveOrganizer(); if ($organizerContact instanceof Addressbook_Model_Contact && !empty($organizerContact->email)) { $organizer = $vevent->add('ORGANIZER', 'mailto:' . $organizerContact->email, array('CN' => $organizerContact->n_fileas, 'EMAIL' => $organizerContact->email)); } } $this->_addEventAttendee($vevent, $event); $optionalProperties = array('class', 'status', 'description', 'geo', 'location', 'priority', 'summary', 'transp', 'url'); foreach ($optionalProperties as $property) { if (!empty($event->{$property})) { $vevent->add(strtoupper($property), $event->{$property}); } } $class = $event->class == Calendar_Model_Event::CLASS_PUBLIC ? 'PUBLIC' : 'CONFIDENTIAL'; $vcalendar->add('X-CALENDARSERVER-ACCESS', $class); $vevent->add('X-CALENDARSERVER-ACCESS', $class); // categories if (!isset($event->tags)) { $event->tags = Tinebase_Tags::getInstance()->getTagsOfRecord($event); } if (isset($event->tags) && count($event->tags) > 0) { $vevent->add('CATEGORIES', (array) $event->tags->name); } // repeating event properties if ($event->rrule) { if ($event->is_all_day_event == true) { $vevent->add('RRULE', preg_replace_callback('/UNTIL=([\\d :-]{19})(?=;?)/', function ($matches) { $dtUntil = new Tinebase_DateTime($matches[1]); $dtUntil->setTimezone((string) Tinebase_Core::getUserTimezone()); return 'UNTIL=' . $dtUntil->format('Ymd'); }, $event->rrule)); } else { $vevent->add('RRULE', preg_replace('/(UNTIL=)(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})/', '$1$2$3$4T$5$6$7Z', $event->rrule)); } if ($event->exdate instanceof Tinebase_Record_RecordSet) { $event->exdate->addIndices(array('is_deleted')); $deletedEvents = $event->exdate->filter('is_deleted', true); foreach ($deletedEvents as $deletedEvent) { $dateTime = $deletedEvent->getOriginalDtStart(); $exdate = $vevent->add('EXDATE'); if ($event->is_all_day_event == true) { $dateTime->setTimezone($event->originator_tz); $exdate['VALUE'] = 'DATE'; } $exdate->setValue($dateTime); } } } $ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee); if ($event->alarms instanceof Tinebase_Record_RecordSet) { $mozLastAck = NULL; $mozSnooze = NULL; foreach ($event->alarms as $alarm) { $valarm = $vcalendar->create('VALARM'); $valarm->add('ACTION', 'DISPLAY'); $valarm->add('DESCRIPTION', $event->summary); if ($dtack = Calendar_Controller_Alarm::getAcknowledgeTime($alarm)) { $valarm->add('ACKNOWLEDGED', $dtack->getClone()->setTimezone('UTC')->format('Ymd\\THis\\Z')); $mozLastAck = $dtack > $mozLastAck ? $dtack : $mozLastAck; } if ($dtsnooze = Calendar_Controller_Alarm::getSnoozeTime($alarm)) { $mozSnooze = $dtsnooze > $mozSnooze ? $dtsnooze : $mozSnooze; } if (is_numeric($alarm->minutes_before)) { if ($event->dtstart == $alarm->alarm_time) { $periodString = 'PT0S'; } else { $interval = $event->dtstart->diff($alarm->alarm_time); $periodString = sprintf('%sP%s%s%s%s', $interval->format('%r'), $interval->format('%d') > 0 ? $interval->format('%dD') : null, $interval->format('%h') > 0 || $interval->format('%i') > 0 ? 'T' : null, $interval->format('%h') > 0 ? $interval->format('%hH') : null, $interval->format('%i') > 0 ? $interval->format('%iM') : null); } # TRIGGER;VALUE=DURATION:-PT1H15M $trigger = $valarm->add('TRIGGER', $periodString); $trigger['VALUE'] = "DURATION"; } else { # TRIGGER;VALUE=DATE-TIME:... $trigger = $valarm->add('TRIGGER', $alarm->alarm_time->getClone()->setTimezone('UTC')->format('Ymd\\THis\\Z')); $trigger['VALUE'] = "DATE-TIME"; } $vevent->add($valarm); } if ($mozLastAck instanceof DateTime) { $vevent->add('X-MOZ-LASTACK', $mozLastAck->getClone()->setTimezone('UTC'), array('VALUE' => 'DATE-TIME')); } if ($mozSnooze instanceof DateTime) { $vevent->add('X-MOZ-SNOOZE-TIME', $mozSnooze->getClone()->setTimezone('UTC'), array('VALUE' => 'DATE-TIME')); } } $baseUrl = Tinebase_Core::getHostname() . "/webdav/Calendar/records/Calendar_Model_Event/{$event->getId()}/"; if ($event->attachments instanceof Tinebase_Record_RecordSet) { foreach ($event->attachments as $attachment) { $filename = rawurlencode($attachment->name); $attach = $vcalendar->createProperty('ATTACH', "{$baseUrl}{$filename}", array('MANAGED-ID' => $attachment->hash, 'FMTTYPE' => $attachment->contenttype, 'SIZE' => $attachment->size, 'FILENAME' => $filename), 'TEXT'); $vevent->add($attach); } } $vcalendar->add($vevent); }