/** * enforce acl restrictions to alarm options * * @param Calendar_Model_Event $_event * @param Calendar_Model_Event $_currentEvent * @return bool true if alarms have updates */ public static function enforceACL($_event, $_currentEvent = NULL) { $alarms = $_event->alarms instanceof Tinebase_Record_RecordSet ? $_event->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); $currentAlarms = $_currentEvent && $_currentEvent->alarms instanceof Tinebase_Record_RecordSet ? $_currentEvent->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); // 1. assemble attendeeSet curruser has rights for // 2. enforcethe rights ;-) if ($_currentEvent) { $alarms->record_id = $_currentEvent->getId(); } }
public function testRruleDiff() { $event = $event = new Calendar_Model_Event(array('dtstart' => new Tinebase_DateTime('2011-11-23 14:25:00'), 'dtend' => new Tinebase_DateTime('2011-11-23 15:25:00'), 'rrule' => 'FREQ=WEEKLY;INTERVAL=1;WKST=MO;BYDAY=TH;UNTIL=2011-12-24 15:25:00', 'summary' => 'test event', 'organizer' => Tinebase_Core::getUser()->contact_id)); $update = clone $event; $update->rrule = 'FREQ=WEEKLY;INTERVAL=1;BYDAY=TH;WKST=MO;UNTIL=2011-12-24 22:59:59'; $diff = $event->diff($update); $this->assertFalse(isset($diff->diff['rrule']) || array_key_exists('rrule', $diff->diff), 'parts order change:' . print_r($diff->toArray(), TRUE)); // real change $update->rrule = 'FREQ=WEEKLY;INTERVAL=;BYDAY=TH;WKST=SU'; $diff = $event->diff($update); $this->assertTrue(isset($diff->diff['rrule']) || array_key_exists('rrule', $diff->diff), 'real change should have diff! diff:' . print_r($diff->toArray(), TRUE)); }
public function testIsRescheduled() { $event1 = new Calendar_Model_Event(array('dtstart' => new Tinebase_DateTime('2011-11-23 14:25:00'), 'dtend' => new Tinebase_DateTime('2011-11-23 15:25:00'), 'rrule' => 'FREQ=DAILY;INTERVAL=2')); $event2 = clone $event1; $this->assertFalse($event1->isRescheduled($event2), 'failed same'); $event2->dtstart->addMinute(30); $this->assertTrue($event1->isRescheduled($event2), 'failed by dtstart'); $event2 = clone $event1; $event2->dtend->addMinute(30); $this->assertTrue($event1->isRescheduled($event2), 'failed by dtend'); $event2 = clone $event1; $event2->rrule = 'FREQ=DAILY;INTERVAL=1'; $this->assertTrue($event1->isRescheduled($event2), 'failed by rrule'); }
public function testSearchEvents() { $from = '2009-04-03 00:00:00'; $until = '2009-04-10 23:59:59'; $events = new Tinebase_Record_RecordSet('Calendar_Model_Event', array(array('dtstart' => '2009-04-02 22:00:00', 'dtend' => '2009-04-02 23:59:59', 'summary' => 'non recur event ending before search period => should _not_ be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-04-02 23:30:00', 'dtend' => '2009-04-03 00:30:00', 'summary' => 'non recur event ending within search period => should be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-04-06 12:00:00', 'dtend' => '2009-04-07 12:00:00', 'summary' => 'non recur event completly within search period => should be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-04-10 23:30:00', 'dtend' => '2009-04-11 00:30:00', 'summary' => 'non recur event starting within search period => should be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-04-11 00:00:00', 'dtend' => '2009-04-11 02:00:00', 'summary' => 'non recur event starting after search period => should _not_ be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-03-27 22:00:00', 'dtend' => '2009-03-27 23:59:59', 'rrule' => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-02 23:59:59', 'summary' => 'recur event ending before search period => should _not_ be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), 'rrule_until' => '2009-04-02 23:59:59', Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-03-27 22:00:00', 'dtend' => '2009-03-27 23:59:59', 'rrule' => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-05 23:59:59', 'summary' => 'recur event ending within search period => should be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), 'rrule_until' => '2009-04-05 23:59:59', Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-04-03 22:00:00', 'dtend' => '2009-04-03 23:59:59', 'rrule' => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-06 23:59:59', 'summary' => 'recur event completly within search period => should be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), 'rrule_until' => '2009-04-06 23:59:59', Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-04-03 22:00:00', 'dtend' => '2009-04-03 23:59:59', 'rrule' => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-12 23:59:59', 'summary' => 'recur event starting within search period => should be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), 'rrule_until' => '2009-04-12 23:59:59', Tinebase_Model_Grants::GRANT_READ => true), array('dtstart' => '2009-04-11 00:00:00', 'dtend' => '2009-04-11 02:00:00', 'rrule' => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-15 02:00:00', 'summary' => 'recur event starting after search period => should _not_ be found', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->getId(), 'uid' => Calendar_Model_Event::generateUID(), 'rrule_until' => '2009-04-15 02:00:00', Tinebase_Model_Grants::GRANT_READ => true))); foreach ($events as $event) { $persistentEvent = $this->_backend->create($event); $event->attendee->cal_event_id = $persistentEvent->getId(); foreach ($event->attendee as $attender) { $this->_backend->createAttendee($attender); } } $filter = new Calendar_Model_EventFilter(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId()), array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)))); $eventsFound = $this->_backend->search($filter, new Tinebase_Model_Pagination()); $eventsFoundIds = $eventsFound->getArrayOfIds(); foreach ($events as $event) { $eventId = $event->getId(); if (strpos($event->summary, '_not_') === false) { $this->assertTrue(in_array($eventId, $eventsFoundIds), 'The following event is missing in the search result :' . print_r($event->toArray(), true)); } else { $this->assertFalse(in_array($eventId, $eventsFoundIds), 'The following event is in the search result, but should not be :' . print_r($event->toArray(), true)); } } $expectedAttendee = $this->_getAttendee(); foreach ($eventsFound as $fetchedEvent) { $this->_assertAttendee($expectedAttendee, $fetchedEvent->attendee); } }
/** * return Calendar_Model_Event and convert contact id to model if needed * * @return Calendar_Model_Event */ public function getRecord() { if (!$this->_event instanceof Calendar_Model_Event) { Calendar_Controller_MSEventFacade::getInstance()->assertEventFacadeParams($this->_container); $this->_event = Calendar_Controller_MSEventFacade::getInstance()->get($this->_event); Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . print_r($this->_event->toArray(), true)); } return $this->_event; }
/** * Computes the Recurrence set of the given event leaving out $_event->exdate and $_exceptions * * @todo respect rrule_until! * * @param Calendar_Model_Event $_event * @param Tinebase_Record_RecordSet $_exceptions * @param Tinebase_DateTime $_from * @param Tinebase_DateTime $_until * @return Tinebase_Record_RecordSet * @throws Tinebase_Exception_UnexpectedValue */ public static function computeRecurrenceSet($_event, $_exceptions, $_from, $_until) { if (!$_event->dtstart instanceof Tinebase_DateTime) { throw new Tinebase_Exception_UnexpectedValue('Event needs DateTime dtstart: ' . print_r($_event->toArray(), TRUE)); } $rrule = new Calendar_Model_Rrule(NULL, TRUE); $rrule->setFromString($_event->rrule); $exceptionRecurIds = self::getExceptionsRecurIds($_event, $_exceptions); $recurSet = new Tinebase_Record_RecordSet('Calendar_Model_Event'); switch ($rrule->freq) { case self::FREQ_DAILY: self::_computeRecurDaily($_event, $rrule, $exceptionRecurIds, $_from, $_until, $recurSet); break; case self::FREQ_WEEKLY: // default BYDAY clause if (!$rrule->byday) { $rrule->byday = array_search($_event->dtstart->format('w'), self::$WEEKDAY_DIGIT_MAP); } if (!$rrule->wkst) { // @TODO if organizer has an account get its locales wkst $rrule->wkst = self::WDAY_MONDAY; } $weekDays = array_keys(self::$WEEKDAY_DIGIT_MAP); array_splice($weekDays, 0, 0, array_splice($weekDays, array_search($rrule->wkst, $weekDays))); $dailyrrule = clone $rrule; $dailyrrule->freq = self::FREQ_DAILY; $dailyrrule->interval = 7 * $rrule->interval; $eventLength = $_event->dtstart->diff($_event->dtend); foreach (explode(',', $rrule->byday) as $recurWeekDay) { // NOTE: in weekly computation, each wdays base event is a recur instance itself $baseEvent = clone $_event; // NOTE: skipping must be done in organizer_tz $baseEvent->dtstart->setTimezone($_event->originator_tz); $direction = array_search($recurWeekDay, $weekDays) >= array_search(array_search($baseEvent->dtstart->format('w'), self::$WEEKDAY_DIGIT_MAP), $weekDays) ? +1 : -1; self::skipWday($baseEvent->dtstart, $recurWeekDay, $direction, TRUE); $baseEvent->dtstart->setTimezone('UTC'); $baseEvent->dtend = clone $baseEvent->dtstart; $baseEvent->dtend->add($eventLength); self::_computeRecurDaily($baseEvent, $dailyrrule, $exceptionRecurIds, $_from, $_until, $recurSet); // check if base event (recur instance) needs to be added to the set if ($baseEvent->dtstart->isLater($_event->dtstart) && $baseEvent->dtstart->isLater($_from) && $baseEvent->dtstart->isEarlier($_until)) { if (!in_array($baseEvent->setRecurId(), $exceptionRecurIds)) { self::addRecurrence($baseEvent, $recurSet); } } } break; case self::FREQ_MONTHLY: if ($rrule->byday) { self::_computeRecurMonthlyByDay($_event, $rrule, $exceptionRecurIds, $_from, $_until, $recurSet); } else { self::_computeRecurMonthlyByMonthDay($_event, $rrule, $exceptionRecurIds, $_from, $_until, $recurSet); } break; case self::FREQ_YEARLY: $yearlyrrule = clone $rrule; $yearlyrrule->freq = self::FREQ_MONTHLY; $yearlyrrule->interval = 12; $baseEvent = clone $_event; $originatorsDtstart = clone $baseEvent->dtstart; $originatorsDtstart->setTimezone($_event->originator_tz); // @TODO respect BYMONTH if ($rrule->bymonth && $rrule->bymonth != $originatorsDtstart->format('n')) { // adopt $diff = (12 + $rrule->bymonth - $originatorsDtstart->format('n')) % 12; // NOTE: skipping must be done in organizer_tz $baseEvent->dtstart->setTimezone($_event->originator_tz); $baseEvent->dtend->setTimezone($_event->originator_tz); $baseEvent->dtstart->addMonth($diff); $baseEvent->dtend->addMonth($diff); $baseEvent->dtstart->setTimezone('UTC'); $baseEvent->dtend->setTimezone('UTC'); // check if base event (recur instance) needs to be added to the set if ($baseEvent->dtstart->isLater($_from) && $baseEvent->dtstart->isEarlier($_until)) { if (!in_array($baseEvent->setRecurId(), $exceptionRecurIds)) { self::addRecurrence($baseEvent, $recurSet); } } } if ($rrule->byday) { self::_computeRecurMonthlyByDay($baseEvent, $yearlyrrule, $exceptionRecurIds, $_from, $_until, $recurSet); } else { self::_computeRecurMonthlyByMonthDay($baseEvent, $yearlyrrule, $exceptionRecurIds, $_from, $_until, $recurSet); } break; } return $recurSet; }
/** * returns a simple event * * @return Calendar_Model_Event * @param bool $_now * @param bool $mute * @todo replace with TestCase::_getEvent */ protected function _getEvent($now = FALSE, $mute = NULL) { return new Calendar_Model_Event(array('summary' => 'Sleep very long', 'dtstart' => '2012-03-25 01:00:00', 'dtend' => '2012-03-25 11:15:00', 'description' => 'Early to bed and early to rise, makes a men healthy, wealthy and wise ... not.', 'attendee' => $this->_getAttendee(), 'organizer' => Tinebase_Core::getUser()->contact_id, 'uid' => Calendar_Model_Event::generateUID())); }
/** * get notification subject and method * * @param Calendar_Model_Event $_event * @param string $_notificationLevel * @param string $_action * @param array $_updates * @param string $timezone * @param Zend_Locale $locale * @param Zend_Translate $translate * @param atring $method * @param Calendar_Model_Attender * @return string * @throws Tinebase_Exception_UnexpectedValue */ protected function _getSubject($_event, $_notificationLevel, $_action, $_updates, $timezone, $locale, $translate, &$method, Calendar_Model_Attender $attender) { $startDateString = Tinebase_Translation::dateToStringInTzAndLocaleFormat($_event->dtstart, $timezone, $locale); $endDateString = Tinebase_Translation::dateToStringInTzAndLocaleFormat($_event->dtend, $timezone, $locale); switch ($_action) { case 'alarm': $messageSubject = sprintf($translate->_('Alarm for event "%1$s" at %2$s'), $_event->summary, $startDateString); break; case 'created': $messageSubject = sprintf($translate->_('Event invitation "%1$s" at %2$s'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case 'booked': if ($attender->user_type !== Calendar_Model_Attender::USERTYPE_RESOURCE) { throw new Tinebase_Exception_UnexpectedValue('not a resource'); } $resource = Calendar_Controller_Resource::getInstance()->get($attender->user_id); $messageSubject = sprintf($translate->_('Resource "%1$s" was booked for "%2$s" at %3$s'), $resource->name, $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case 'deleted': $messageSubject = sprintf($translate->_('Event "%1$s" at %2$s has been canceled'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_CANCEL; break; case 'changed': switch ($_notificationLevel) { case self::NOTIFICATION_LEVEL_EVENT_RESCHEDULE: if (isset($_updates['dtstart']) || array_key_exists('dtstart', $_updates)) { $oldStartDateString = Tinebase_Translation::dateToStringInTzAndLocaleFormat($_updates['dtstart'], $timezone, $locale); $messageSubject = sprintf($translate->_('Event "%1$s" has been rescheduled from %2$s to %3$s'), $_event->summary, $oldStartDateString, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; } // fallthrough if dtstart didn't change // fallthrough if dtstart didn't change case self::NOTIFICATION_LEVEL_EVENT_UPDATE: $messageSubject = sprintf($translate->_('Event "%1$s" at %2$s has been updated'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case self::NOTIFICATION_LEVEL_ATTENDEE_STATUS_UPDATE: if (!empty($_updates['attendee']) && !empty($_updates['attendee']['toUpdate']) && count($_updates['attendee']['toUpdate']) == 1) { // single attendee status update $attender = $_updates['attendee']['toUpdate']->getFirstRecord(); switch ($attender->status) { case Calendar_Model_Attender::STATUS_ACCEPTED: $messageSubject = sprintf($translate->_('%1$s accepted event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_DECLINED: $messageSubject = sprintf($translate->_('%1$s declined event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_TENTATIVE: $messageSubject = sprintf($translate->_('Tentative response from %1$s for event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_NEEDSACTION: $messageSubject = sprintf($translate->_('No response from %1$s for event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; } } else { $messageSubject = sprintf($translate->_('Attendee changes for event "%1$s" at %2$s'), $_event->summary, $startDateString); } // we don't send iMIP parts to organizers with an account cause event is already up to date if ($_event->organizer && !$_event->resolveOrganizer()->account_id) { $method = Calendar_Model_iMIP::METHOD_REPLY; } break; } break; default: $messageSubject = 'unknown action'; if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " unknown action '{$_action}'"); } break; } if ($attender->user_type === Calendar_Model_Attender::USERTYPE_RESOURCE) { $messageSubject = '[' . $translate->_('Resource Management') . '] ' . $messageSubject; } return $messageSubject; }
/** * send notification to a single attender * * @param Calendar_Model_Attender $_attender * @param Calendar_Model_Event $_event * @param Tinebase_Model_FullAccount $_updater * @param Sting $_action * @param String $_notificationLevel * @param array $_updates * @param array $attachs * @return void */ public function sendNotificationToAttender($_attender, $_event, $_updater, $_action, $_notificationLevel, $_updates = NULL, $attachs = FALSE) { try { // find organizer account if ($_event->organizer && $_event->resolveOrganizer()->account_id) { $organizer = Tinebase_User::getInstance()->getFullUserById($_event->resolveOrganizer()->account_id); } else { // use creator as organizer $organizer = Tinebase_User::getInstance()->getFullUserById($_event->created_by); } // get prefered language, timezone and notification level $prefUser = $_attender->getUserAccountId(); $locale = Tinebase_Translation::getLocale(Tinebase_Core::getPreference()->getValueForUser(Tinebase_Preference::LOCALE, $prefUser ? $prefUser : $organizer->getId())); $timezone = Tinebase_Core::getPreference()->getValueForUser(Tinebase_Preference::TIMEZONE, $prefUser ? $prefUser : $organizer->getId()); $translate = Tinebase_Translation::getTranslation('Calendar', $locale); // check if user wants this notification $sendLevel = $prefUser ? Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::NOTIFICATION_LEVEL, $prefUser) : 100; $sendOnOwnActions = $prefUser ? Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::SEND_NOTIFICATION_OF_OWN_ACTIONS, $prefUser) : 0; // NOTE: organizer gets mails unless she set notificationlevel to NONE if ($prefUser == $_updater->getId() && !$sendOnOwnActions || $sendLevel < $_notificationLevel && ($prefUser != $organizer->getId() || $sendLevel == self::NOTIFICATION_LEVEL_NONE)) { return; } // get date strings $startDateString = Tinebase_Translation::dateToStringInTzAndLocaleFormat($_event->dtstart, $timezone, $locale); $endDateString = Tinebase_Translation::dateToStringInTzAndLocaleFormat($_event->dtend, $timezone, $locale); switch ($_action) { case 'alarm': $messageSubject = sprintf($translate->_('Alarm for event "%1$s" at %2$s'), $_event->summary, $startDateString); break; case 'created': $messageSubject = sprintf($translate->_('Event invitation "%1$s" at %2$s'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case 'deleted': $messageSubject = sprintf($translate->_('Event "%1$s" at %2$s has been canceled'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_CANCEL; break; case 'changed': switch ($_notificationLevel) { case self::NOTIFICATION_LEVEL_EVENT_RESCHEDULE: $messageSubject = sprintf($translate->_('Event "%1$s" at %2$s has been rescheduled'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case self::NOTIFICATION_LEVEL_EVENT_UPDATE: $messageSubject = sprintf($translate->_('Event "%1$s" at %2$s has been updated'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case self::NOTIFICATION_LEVEL_ATTENDEE_STATUS_UPDATE: if (!empty($_updates['attendee']) && !empty($_updates['attendee']['toUpdate']) && count($_updates['attendee']['toUpdate']) == 1) { // single attendee status update $attender = $_updates['attendee']['toUpdate'][0]; switch ($attender->status) { case Calendar_Model_Attender::STATUS_ACCEPTED: $messageSubject = sprintf($translate->_('%1$s accepted event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_DECLINED: $messageSubject = sprintf($translate->_('%1$s declined event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_TENTATIVE: $messageSubject = sprintf($translate->_('Tentative response from %1$s for event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_NEEDSACTION: $messageSubject = sprintf($translate->_('No response from %1$s for event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; } } else { $messageSubject = sprintf($translate->_('Attendee changes for event "%1$s" at %2$s'), $_event->summary, $startDateString); } // we don't send iMIP parts to organizers with an account cause event is already up to date if ($_event->organizer && !$_event->resolveOrganizer()->account_id) { $method = Calendar_Model_iMIP::METHOD_REPLY; } break; } break; default: if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " unknown action '{$_action}'"); } break; } $view = new Zend_View(); $view->setScriptPath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views'); $view->translate = $translate; $view->timezone = $timezone; $view->event = $_event; $view->updater = $_updater; $view->updates = $_updates; $messageBody = $view->render('eventNotification.php'); if (isset($method) && version_compare(PHP_VERSION, '5.3.0', '>=')) { $converter = Calendar_Convert_Event_VCalendar_Factory::factory(Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC); $converter->setMethod($method); $vcalendar = $converter->fromTine20Model($_event); // in Tine 2.0 non organizers might be given the grant to update events // @see rfc6047 section 2.2.1 & rfc5545 section 3.2.18 if ($method != Calendar_Model_iMIP::METHOD_REPLY && $_event->organizer !== $_updater->contact_id) { foreach ($vcalendar->children() as $component) { if ($component->name == 'VEVENT') { if (isset($component->{'ORGANIZER'})) { $component->{'ORGANIZER'}->add(new Sabre_VObject_Parameter('SEND-BY', 'mailto:' . $_updater->accountEmailAddress)); } } } } /* not yet supported // in Tine 2.0 status updater might not be updater if ($method == Calendar_Model_iMIP::METHOD_REPLY) { } */ $calendarPart = new Zend_Mime_Part($vcalendar->serialize()); $calendarPart->charset = 'UTF-8'; $calendarPart->type = 'text/calendar; method=' . $method; $calendarPart->encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE; $attachment = new Zend_Mime_Part($vcalendar->serialize()); $attachment->type = 'application/ics'; $attachment->encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE; $attachment->disposition = Zend_Mime::DISPOSITION_ATTACHMENT; $attachment->filename = 'event.ics'; $attachments = array($attachment); if ($attachs) { foreach ($attachs as $file) { $stream = fopen($file['tempFile']['path'], 'r'); $part = new Zend_Mime_Part($stream); $part->type = $file['tempFile']['type']; $part->encoding = Zend_Mime::ENCODING_BASE64; $part->disposition = Zend_Mime::DISPOSITION_ATTACHMENT; $part->filename = $file['tempFile']['name']; $attachments[] = $part; } } } else { $calendarPart = null; $attachments = null; } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " receiver: '{$_attender->getEmail()}'"); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " subject: '{$messageSubject}'"); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " body: {$messageBody}"); } // NOTE: this is a contact as we only support users and groupmembers $contact = $_attender->getResolvedUser(); $sender = $_action == 'alarm' ? $organizer : $_updater; Tinebase_Notification::getInstance()->send($sender, array($contact), $messageSubject, $messageBody, $calendarPart, $attachments); } catch (Exception $e) { Tinebase_Core::getLogger()->WARN(__METHOD__ . '::' . __LINE__ . " could not send notification :" . $e); return; } }
/** * updated a recur series * * @param array $recordData * @param bool $checkBusyConflicts * @noparamyet JSONstring $returnPeriod NOT IMPLEMENTED YET * @return array */ public function updateRecurSeries($recordData, $checkBusyConflicts = FALSE) { $recurInstance = new Calendar_Model_Event(array(), TRUE); $recurInstance->setFromJsonInUsersTimezone($recordData); //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(print_r($recurInstance->toArray(), true)); $baseEvent = Calendar_Controller_Event::getInstance()->updateRecurSeries($recurInstance, $checkBusyConflicts); return $this->getEvent($baseEvent->getId()); }
/** * 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); }
/** * returns a simple event * * @return Calendar_Model_Event */ protected function _getEvent() { return new Calendar_Model_Event(array('summary' => 'Wakeup', 'dtstart' => '2009-03-25 06:00:00', 'dtend' => '2009-03-25 06:15:00', 'description' => 'Early to bed and early to rise, makes a men healthy, wealthy and wise', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_testCalendar->getId(), 'organizer' => $this->_testUserContact->getId(), 'uid' => Calendar_Model_Event::generateUID(), Tinebase_Model_Grants::GRANT_READ => true, Tinebase_Model_Grants::GRANT_EDIT => true, Tinebase_Model_Grants::GRANT_DELETE => true)); }
/** * testUpdateEvent */ public function testUpdateEvent() { $event = new Calendar_Model_Event($this->testCreateEvent(), true); $event->dtstart->addHour(5); $event->dtend->addHour(5); $event->description = 'are you kidding?'; $eventData = $event->toArray(); foreach ($eventData['attendee'] as $key => $attenderData) { if ($eventData['attendee'][$key]['user_id'] != $this->_getTestUserContact()->getId()) { unset($eventData['attendee'][$key]); } } $updatedEventData = $this->_uit->saveEvent($eventData); $this->_assertJsonEvent($eventData, $updatedEventData, 'failed to update event'); return $updatedEventData; }
break; default: echo sprintf($this->translate->_('"%2$s" response from %1$s'), $attender->getName(), $attender->status) . "\n"; break; } } } } ?> <?php } echo $this->translate->_('Event details'); ?> : <?php $orderedFields = array('dtstart', 'dtend', 'summary', 'location', 'description', 'rrule'); foreach ($orderedFields as $field) { if ($this->event->{$field}) { echo str_pad(Calendar_Model_Event::getTranslatedFieldName($field, $this->translate) . ':', 20) . Calendar_Model_Event::getTranslatedValue($field, $this->event->{$field}, $this->translate, $this->timezone) . "\n"; } } echo $this->translate->plural('Attender', 'Attendee', count($this->event->attendee)) . ":\n"; foreach ($this->event->attendee as $attender) { $role = $this->translate->_($attender->getRoleString()); $status = $this->translate->_($attender->getStatusString()); echo " {$attender->getName()} ({$role}, {$status}) \n"; } ?>
/** * gets attendee of a given event * * @param Calendar_Model_Event $_event * @return Tinebase_Record_RecordSet */ public function getEventAttendee(Calendar_Model_Event $_event) { $attendee = $this->_attendeeBackend->getMultipleByProperty($_event->getId(), Calendar_Backend_Sql_Attendee::FOREIGNKEY_EVENT); return $attendee; }
/** * converts events to calendar objects * * @param Calendar_Model_Event $event (from MSFacade atm.) */ protected function _convertCalendarObject($event) { $eventId = $event->getId(); $lastModified = $event->last_modified_time ? $event->last_modified_time : $event->creation_time; // we always use a event set to return exdates at once $eventSet = new Tinebase_Record_RecordSet('Calendar_Model_Event', array($event)); if ($event->rrule) { foreach ($event->exdate as $exEvent) { if (!$exEvent->is_deleted) { $eventSet->addRecord($exEvent); $event->exdate->removeRecord($exEvent); } } // remaining exdates are fallouts $event->exdate = $event->exdate->getOriginalDtStart(); } $exporter = new Calendar_Export_Ical(); $ics = $exporter->eventToIcal($eventSet); // work arround broken exdate handling in apple ical // -> not neccesary at the moment this is done generally in ics export return array('id' => $eventId, 'uri' => $eventId, 'lastmodified' => $lastModified->getTimeStamp(), 'calendardata' => $ics); }
/** * increases content sequence of attender display container * * @param Calendar_Model_Attender $attender * @param Calendar_Model_Event $event * @param string $action */ protected function _increaseDisplayContainerContentSequence($attender, $event, $action = Tinebase_Model_ContainerContent::ACTION_UPDATE) { if ($event->container_id === $attender->displaycontainer_id || empty($attender->displaycontainer_id)) { // no need to increase sequence return; } Tinebase_Container::getInstance()->increaseContentSequence($attender->displaycontainer_id, $action, $event->getId()); }
/** * adds feast days to feast calendar * * @param array|Tinebase_DateTime $date */ protected function _createFeastDay($date) { if (!$this->_feast_calendar) { $this->_getFeastCalendar(); } $organizer = Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId()); if (is_array($date)) { $allDay = TRUE; if (count($date) == 1) { $dtstart = $date[0]->setTimezone(Tinebase_Core::getUserTimezone())->setTime(0, 0, 0); $dtend = clone $dtstart; } else { $dtstart = $date[0]->setTimezone(Tinebase_Core::getUserTimezone())->setTime(0, 0, 0); $dtend = clone $dtstart; $dtend = $dtend->addDay(count($date))->subHour(5); } } else { $allDay = FALSE; $dtstart = $date->setTimezone(Tinebase_Core::getUserTimezone())->setTime(6, 0, 0); $dtend = clone $dtstart; $dtend->addMinute(15); } $event = new Calendar_Model_Event(array('summary' => 'Feast Day', 'dtstart' => $dtstart->format('Y-m-d H:i:s'), 'dtend' => $dtend->format('Y-m-d H:i:s'), 'description' => Tinebase_Record_Abstract::generateUID(10), 'container_id' => $this->_feast_calendar->getId(), 'organizer' => $organizer->getId(), 'uid' => Calendar_Model_Event::generateUID(), 'is_all_day_event' => $allDay, 'attendee' => new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(array('user_id' => $organizer->getId(), 'user_type' => Calendar_Model_Attender::USERTYPE_USER, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'status_authkey' => Tinebase_Record_Abstract::generateUID()))), Tinebase_Model_Grants::GRANT_READ => true, Tinebase_Model_Grants::GRANT_EDIT => true, Tinebase_Model_Grants::GRANT_DELETE => true)); return Calendar_Controller_Event::getInstance()->create($event); }
/** * test alarm inspection from 24.03.2012 -> 25.03.2012 */ public function testAdoptAlarmDSTBoundaryWithSkipping() { $event = new Calendar_Model_Event(array('summary' => 'Cleanup', 'dtstart' => '2012-01-31 07:30:00', 'dtend' => '2012-01-31 10:30:00', 'container_id' => $this->_getTestCalendar()->getId(), 'uid' => Calendar_Model_Event::generateUID(), 'rrule' => 'FREQ=WEEKLY;INTERVAL=1;WKST=MO;BYDAY=TU', 'originator_tz' => 'Europe/Berlin')); $alarm = new Tinebase_Model_Alarm(array('model' => 'Calendar_Model_Event', 'alarm_time' => '2012-03-26 06:30:00', 'minutes_before' => 1440, 'options' => '{"minutes_before":1440,"recurid":"a7c55ce09cea9aec4ac37d9d72789183b12cad7c-2012-03-27 06:30:00","custom":false}')); $this->_eventController->adoptAlarmTime($event, $alarm, 'instance'); $this->assertEquals('2012-04-02 06:30:00', $alarm->alarm_time->toString()); }
public function testIsRecurException() { $event = new Calendar_Model_Event(array('uid' => '839404a34a8005d2ebd0da68b6aa922460ae945a', 'summary' => 'conference exception (late)', 'dtstart' => '2003-03-28 10:00:00', 'dtend' => '2003-03-28 12:00:00', 'originator_tz' => 'US/Pacific', 'recurid' => '839404a34a8005d2ebd0da68b6aa922460ae945a-2003-03-28 08:00:00')); $this->assertTrue($event->isRecurException(), 'recur exception was not detected'); $this->assertEquals('2003-03-28 08:00:00', $event->getOriginalDtStart()->format(Tinebase_Record_Abstract::ISO8601LONG)); }
/** * create event exception * * @param Calendar_Model_Event $event * @return Calendar_Model_Event */ protected function _createEventException($event) { $newException = clone $event; $newException->id = NULL; $newException->base_event_id = $event->getId(); $newException->recurid = clone $newException->dtstart; $newException->recurid->addDay(3); $newException->dtstart->addDay(3)->addHour(2); $newException->dtend->addDay(3)->addHour(2); $newException->summary = 'new exception'; $newException->exdate = NULL; return $newException; }
/** * prepares an exception instance for persitence * * @param Calendar_Model_Event $_baseEvent * @param Calendar_Model_Event $_exception * @return void * @throws Tinebase_Exception_InvalidArgument */ protected function _prepareException($_baseEvent, $_exception) { if ($_exception instanceof Tinebase_Record_RecordSet) { foreach ($_exception as $exception) { $this->_prepareException($_baseEvent, $exception); } return; } if (!$_baseEvent->uid) { throw new Tinebase_Exception_InvalidArgument('base event has no uid'); } if ($_exception->is_deleted == false) { $_exception->container_id = $_baseEvent->container_id; } $_exception->uid = $_baseEvent->uid; $_exception->recurid = $_baseEvent->uid . '-' . $_exception->getOriginalDtStart()->format(Tinebase_Record_Abstract::ISO8601LONG); }
/** * append organizer name and email * * @param Syncroton_Model_Event $syncrotonEvent * @param Calendar_Model_Event $event */ protected function _addOrganizer(Syncroton_Model_Event $syncrotonEvent, Calendar_Model_Event $event) { $organizer = NULL; if (!empty($event->organizer)) { try { $organizer = $event->resolveOrganizer(); } catch (Tinebase_Exception_AccessDenied $tead) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . $tead); } } } if ($organizer instanceof Addressbook_Model_Contact) { $organizerName = $organizer->n_fileas; $organizerEmail = $organizer->getPreferedEmailAddress(); } else { // set the current account as organizer // if organizer is not set, you can not edit the event on the Motorola Milestone $organizerName = Tinebase_Core::getUser()->accountFullName; $organizerEmail = Tinebase_Core::getUser()->accountEmailAddress; } $syncrotonEvent->organizerName = $organizerName; if ($organizerEmail) { $syncrotonEvent->organizerEmail = $organizerEmail; } }
/** * returns a simple event * * @param bool $now * @param bool $mute * @return Calendar_Model_Event */ protected function _getEvent($now = FALSE, $mute = NULL) { $event = new Calendar_Model_Event(array('summary' => 'Wakeup', 'dtstart' => '2009-03-25 06:00:00', 'dtend' => '2009-03-25 06:15:00', 'description' => 'Early to bed and early to rise, makes a men healthy, wealthy and wise', 'attendee' => $this->_getAttendee(), 'container_id' => $this->_getTestCalendar()->getId(), 'organizer' => $this->_getTestUserContact()->getId(), 'uid' => Calendar_Model_Event::generateUID(), 'mute' => $mute, Tinebase_Model_Grants::GRANT_READ => true, Tinebase_Model_Grants::GRANT_EDIT => true, Tinebase_Model_Grants::GRANT_DELETE => true)); if ($now) { $event->dtstart = Tinebase_DateTime::now(); $event->dtend = Tinebase_DateTime::now()->addMinute(15); } return $event; }
/** * prepares an exception instance for persistence * * @param Calendar_Model_Event $_baseEvent * @param Calendar_Model_Event $_exception * @return void * @throws Tinebase_Exception_InvalidArgument */ protected function _prepareException(Calendar_Model_Event $_baseEvent, Calendar_Model_Event $_exception) { if (!$_baseEvent->uid) { throw new Tinebase_Exception_InvalidArgument('base event has no uid'); } if ($_exception->is_deleted == false) { $_exception->container_id = $_baseEvent->container_id; } $_exception->uid = $_baseEvent->uid; $_exception->base_event_id = $_baseEvent->getId(); $_exception->recurid = $_baseEvent->uid . '-' . $_exception->getOriginalDtStart()->format(Tinebase_Record_Abstract::ISO8601LONG); // NOTE: we always refetch the base event as it might be touched in the meantime $currBaseEvent = $this->_eventController->get($_baseEvent, null, false); $_exception->last_modified_time = $currBaseEvent->last_modified_time; }
/** * eventToIcal * * @param Tinebase_Record_RecordSet|Calendar_Model_Event $_event * @return qCal_Component_Vcalendar */ public function eventToIcal($_event) { if ($_event instanceof Tinebase_Record_RecordSet) { foreach ($_event as $event) { $this->eventToIcal($event); } return $this->_vcalendar; } // NOTE: we deliver events in originators tz $_event->setTimezone($_event->originator_tz); if (!in_array($_event->originator_tz, $this->_attachedTimezones)) { $this->_vcalendar->attach(self::getVtimezone($_event->originator_tz)); $this->_attachedTimezones[] = $_event->originator_tz; } if ($_event->is_all_day_event) { $dtstart = new qCal_Property_Dtstart($_event->dtstart->format('Ymd'), array('VALUE' => 'DATE')); $dtend = new qCal_Property_Dtend($_event->dtend->format('Ymd'), array('VALUE' => 'DATE')); } else { $dtstart = new qCal_Property_Dtstart(qCal_DateTime::factory($_event->dtstart->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); $dtend = new qCal_Property_Dtend(qCal_DateTime::factory($_event->dtend->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); } $vevent = new qCal_Component_Vevent(array('uid' => $_event->uid, 'sequence' => $_event->seq, 'summary' => $_event->summary, 'dtstart' => $dtstart, 'dtend' => $dtend)); foreach (self::$veventMap as $icalProp => $tineField) { if (isset($_event[$tineField])) { $vevent->addProperty($icalProp, $_event->{$tineField}); } } // rrule if ($_event->rrule) { $vevent->addProperty('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 ($exdateArray = $_event->exdate) { // use multiple EXDATE for the moment, as apple ical uses them foreach ($_event->exdate as $exdate) { $exdates = new qCal_Property_Exdate(qCal_DateTime::factory($exdate->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); $vevent->addProperty($exdates); } // $exdates = new qCal_Property_Exdate(qCal_DateTime::factory(array_shift($exdateArray)->format('Ymd\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); // foreach($exdateArray as $exdate) { // $exdates->addValue(qCal_DateTime::factory($exdate->format('Ymd\THis'), $_event->originator_tz)); // } // // $vevent->addProperty($exdates); } } // recurid if ($_event->isRecurException()) { $originalDtStart = $_event->getOriginalDtStart(); $originalDtStart->setTimezone($_event->originator_tz); $vevent->addProperty(new qCal_Property_RecurrenceId(qCal_DateTime::factory($originalDtStart->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz))); } // organizer $organizerId = $_event->organizer instanceof Addressbook_Model_Contact ? array($_event->organizer->getId()) : array($_event->organizer); $organizer = Addressbook_Controller_Contact::getInstance()->getMultiple($organizerId, TRUE)->getFirstRecord(); if ($organizer && ($organizerEmail = $organizer->getPreferedEmailAddress())) { $vevent->addProperty(new qCal_Property_Organizer("mailto:{$organizerEmail}", array('CN' => $organizer->n_fileas))); } // attendee if ($_event->attendee) { Calendar_Model_Attender::resolveAttendee($_event->attendee, FALSE); foreach ($_event->attendee as $attender) { $attenderEmail = $attender->getEmail(); if ($attenderEmail) { $vevent->addProperty(new qCal_Property_Attendee("mailto:{$attenderEmail}", array('CN' => $attender->getName(), 'CUTYPE' => self::$cutypeMap[$attender->user_type], 'EMAIL' => $attenderEmail, 'PARTSTAT' => $attender->status, 'ROLE' => "{$attender->role}-PARTICIPANT", 'RSVP' => 'FALSE'))); } } } // alarms if ($_event->alarms) { foreach ($_event->alarms as $alarm) { $valarm = new qCal_Component_Valarm(array('ACTION' => 'DISPLAY', 'DESCRIPTION' => $_event->summary)); // qCal only support DURATION ;-( $diffSeconds = $_event->dtstart->php52compat_diff($alarm->alarm_time); $valarm->addProperty(new qCal_Property_Trigger($diffSeconds)); // if (is_numeric($alarm->minutes_before)) { // $valarm->addProperty(new qCal_Property_Trigger("-PT{$alarm->minutes_before}M")); // } else { // $valarm->addProperty(new qCal_Property_Trigger(qCal_DateTime::factory($alarm->alarm_time->format('Ymd\THis'), $_event->originator_tz)), array('TZID' => $_event->originator_tz)); // } $vevent->attach($valarm); } } // @todo status $this->_vcalendar->attach($vevent); return $this->_vcalendar; }
/** * returns a simple event * * @param array $_contact * @return Calendar_Model_Event */ protected function _getEvent($_contact) { $testCalendar = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array('name' => 'PHPUnit test calendar', 'type' => Tinebase_Model_Container::TYPE_PERSONAL, 'backend' => 'Sql', 'application_id' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId()), true)); return new Calendar_Model_Event(array('summary' => 'Wakeup', 'dtstart' => '2009-03-25 06:00:00', 'dtend' => '2009-03-25 06:15:00', 'description' => 'Early to bed and early to rise, makes a men healthy, wealthy and wise', 'attendee' => new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(array('user_id' => Tinebase_Core::getUser()->contact_id, 'user_type' => Calendar_Model_Attender::USERTYPE_USER, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'status_authkey' => Tinebase_Record_Abstract::generateUID()), array('user_id' => $_contact['id'], 'user_type' => Calendar_Model_Attender::USERTYPE_USER, 'role' => Calendar_Model_Attender::ROLE_OPTIONAL, 'status_authkey' => Tinebase_Record_Abstract::generateUID()))), 'container_id' => $testCalendar->getId(), 'organizer' => Tinebase_Core::getUser()->contact_id, 'uid' => Calendar_Model_Event::generateUID(), Tinebase_Model_Grants::GRANT_READ => true, Tinebase_Model_Grants::GRANT_EDIT => true, Tinebase_Model_Grants::GRANT_DELETE => true)); }
/** * gets implicit exceptions due to status settings * * @param array $_egwEventAttendee * @return Tinebase_Record_RecordSet of Calendar_Model_Event */ protected function _getRecurImplicitExceptions($_egwEventData) { $implictExceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); if (empty($_egwEventData['attendee'])) { return $implictExceptions; } $select = $this->_egwDb->select()->from(array('attendee' => 'egw_cal_user'), 'DISTINCT(' . $this->_egwDb->quoteIdentifier('attendee.cal_recur_date') . ')')->where($this->_egwDb->quoteInto($this->_egwDb->quoteIdentifier('cal_id') . ' = ?', $_egwEventData['attendee'][0]['cal_id']))->where($this->_egwDb->quoteInto($this->_egwDb->quoteIdentifier('cal_recur_date') . ' != ?', 0)); $groupSelect = new Tinebase_Backend_Sql_Filter_GroupSelect($select); foreach ($_egwEventData['attendee'] as $attender) { $groupSelect->orWhere($this->_egwDb->quoteInto($this->_egwDb->quoteIdentifier('attendee.cal_user_type') . ' = ?', $attender['cal_user_type']) . ' AND ' . $this->_egwDb->quoteInto($this->_egwDb->quoteIdentifier('attendee.cal_user_id') . ' = ?', $attender['cal_user_id']) . ' AND ' . $this->_egwDb->quoteInto($this->_egwDb->quoteIdentifier('attendee.cal_status') . ' NOT LIKE ?', $attender['cal_status'])); } $groupSelect->appendWhere(Zend_Db_Select::SQL_AND); $egwExceptionDates = $this->_egwDb->fetchAll($select, NULL, Zend_Db::FETCH_ASSOC); if (count($egwExceptionDates) > 0) { $this->_log->debug(__METHOD__ . '::' . __LINE__ . ' found ' . count($egwExceptionDates) . ' implicit exceptions for event ' . $_egwEventData['attendee'][0]['cal_id']); //print_r($_egwEventAttendee); } if (count($egwExceptionDates) > 500) { $this->_log->err(__METHOD__ . '::' . __LINE__ . " egw's horizont for event " . $_egwEventData['attendee'][0]['cal_id'] . " seems to be broken. Status exceptions will not be considered/migrated"); return $implictExceptions; } //print_r($egwExceptionDates); $eventDuration = $_egwEventData['cal_end'] - $_egwEventData['cal_start']; foreach ($egwExceptionDates as $exdate) { $select = $this->_egwDb->select()->from(array('attendee' => 'egw_cal_user'))->where($this->_egwDb->quoteInto($this->_egwDb->quoteIdentifier('cal_recur_date') . ' = ?', $exdate['cal_recur_date'])); $egwExceptionEventAttendee = $this->_egwDb->fetchAll($select, NULL, Zend_Db::FETCH_ASSOC); $exEventData = $_egwEventData; $exEventData['cal_id'] = Calendar_Model_Event::generateUID(); $exEventData['cal_start'] = $exdate['cal_recur_date']; $exEventData['cal_end'] = $exdate['cal_recur_date'] + $eventDuration; $exEventData['attendee'] = $egwExceptionEventAttendee; $event = $this->_getTineEventRecord($exEventData); $event->attendee = $this->_getEventAttendee($exEventData); $implictExceptions->addRecord($event); } return $implictExceptions; }
/** * computes monthly (byday) recurring events and inserts them into given $_recurSet * * @param Calendar_Model_Event $_event * @param Calendar_Model_Rrule $_rrule * @param array $_exceptionRecurIds * @param Tinebase_DateTime $_from * @param Tinebase_DateTime $_until * @param Tinebase_Record_RecordSet $_recurSet * @return void */ protected static function _computeRecurMonthlyByDay($_event, $_rrule, $_exceptionRecurIds, $_from, $_until, $_recurSet) { $eventInOrganizerTZ = clone $_event; $eventInOrganizerTZ->setTimezone($_event->originator_tz); $computationStartDateArray = self::date2array($eventInOrganizerTZ->dtstart); // if period contains base events dtstart, we let computation start one intervall to early to catch // the cases when dtstart of base event not equals the first instance. If it fits, we filter the additional // instance out later if ($eventInOrganizerTZ->dtstart->isLater($_from) && $eventInOrganizerTZ->dtstart->isEarlier($_until)) { $computationStartDateArray = self::addMonthIgnoringDay($computationStartDateArray, -1 * $_rrule->interval); } $computationEndDate = $_event->rrule_until instanceof DateTime && $_until->isLater($_event->rrule_until) ? $_event->rrule_until : $_until; // if dtstart is before $_from, we compute the offset where to start our calculations if ($eventInOrganizerTZ->dtstart->isEarlier($_from)) { $computationOffsetMonth = self::getMonthDiff($eventInOrganizerTZ->dtend, $_from); // NOTE: $computationOffsetMonth must be multiple of interval! $computationOffsetMonth = floor($computationOffsetMonth / $_rrule->interval) * $_rrule->interval; $computationStartDateArray = self::addMonthIgnoringDay($computationStartDateArray, $computationOffsetMonth - $_rrule->interval); } $eventLength = $eventInOrganizerTZ->dtstart->diff($eventInOrganizerTZ->dtend); $computationStartDateArray['day'] = 1; $byDayInterval = (int) substr($_rrule->byday, 0, -2); $byDayWeekday = substr($_rrule->byday, -2); if ($byDayInterval === 0 || !(isset(self::$WEEKDAY_DIGIT_MAP[$byDayWeekday]) || array_key_exists($byDayWeekday, self::$WEEKDAY_DIGIT_MAP))) { throw new Exception('mal formated rrule byday part: "' . $_rrule->byday . '"'); } while (true) { $computationStartDateArray = self::addMonthIgnoringDay($computationStartDateArray, $_rrule->interval); $computationStartDate = self::array2date($computationStartDateArray, $eventInOrganizerTZ->originator_tz); $recurEvent = self::cloneEvent($eventInOrganizerTZ); $recurEvent->dtstart = clone $computationStartDate; if ($byDayInterval < 0) { $recurEvent->dtstart = self::array2date(self::addMonthIgnoringDay($computationStartDateArray, 1), $eventInOrganizerTZ->originator_tz); $recurEvent->dtstart->subDay(1); } self::skipWday($recurEvent->dtstart, $byDayWeekday, $byDayInterval, TRUE); // we calculate dtend from the event length, as events during a dst boundary could get dtend less than dtstart otherwise $recurEvent->dtend = clone $recurEvent->dtstart; $recurEvent->dtend->add($eventLength); $recurEvent->setTimezone('UTC'); if ($computationEndDate->isEarlier($recurEvent->dtstart)) { break; } // skip non existing dates if ($computationStartDate->get('m') != $recurEvent->dtstart->get('m')) { continue; } // skip events ending before our period. // NOTE: such events could be included, cause our offset only calcs months and not seconds if ($_from->compare($recurEvent->dtend) >= 0) { continue; } // skip instances begining before the baseEvent if ($recurEvent->dtstart->compare($_event->dtstart) < 0) { continue; } // skip if event equal baseevent if ($_event->dtstart->equals($recurEvent->dtstart)) { continue; } $recurEvent->setRecurId($_event->getId()); if (!in_array($recurEvent->recurid, $_exceptionRecurIds)) { self::addRecurrence($recurEvent, $_recurSet); } } }
/** * converts VEVENT to an Calendar_Model_Event * * @param qCal_Component $vevent * @return Calendar_Model_Event */ protected function _getEvent(qCal_Component $vevent) { $eventData = array(); // timezone if ($vevent->hasComponent('VTIMEZONE')) { $tz = array_value(0, $vevent->getComponent('VTIMEZONE')); $eventData['originator_tz'] = array_value(0, $tz->getProperty('TZID'))->getValue(); } else { $eventData['originator_tz'] = $this->_defaultTimezoneId; } foreach ($this->_eventPropertyMap as $tineName => $icalName) { if ($vevent->hasProperty($icalName)) { $icalValue = array_value(0, $vevent->getProperty($icalName)); switch ($icalValue->getType()) { case 'DATE': $value = new Tinebase_DateTime($icalValue->getValue() . 'T000000', $eventData['originator_tz']); // events with dtstart given as date are allday events! if ($tineName == 'dtstart') { $eventData['is_all_day_event'] = true; } if ($tineName == 'dtend') { $value = $value->addSecond(-1); } break; case 'DATE-TIME': $value = new Tinebase_DateTime($icalValue->getValue(), $eventData['originator_tz']); case 'TEXT': $value = str_replace(array('\\,', '\\n'), array(',', "\n"), $icalValue->getValue()); break; default: $value = $icalValue->getValue(); break; } $eventData[$tineName] = $value; } } // truncate string if (array_key_exists('uid', $eventData) && strlen($eventData['uid']) > 40) { $eventData['uid'] = substr($eventData['uid'], 0, 40); } $event = new Calendar_Model_Event($eventData); $event->setTimezone('UTC'); return $event; }