public function testExportRecurId() { $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($this->_testEvent, $exceptions, $this->_testEvent->dtend); $exporter = new Calendar_Export_Ical(); $ics = $exporter->eventToIcal($nextOccurance); // assert recurid $this->assertEquals(1, preg_match("/RECURRENCE-ID;TZID=Europe\\/Berlin:20101231T130000\r\n/", $ics), 'RECURRENCE-ID broken'); }
public function testComputeNextOccurrenceWithNegativeWhich() { $event = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'weekly', 'dtstart' => '2013-10-11 08:00:00', 'dtend' => '2013-10-11 10:00:00', 'rrule' => 'FREQ=WEEKLY;BYDAY=FR;INTERVAL=1', 'originator_tz' => 'Europe/Berlin')); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); // NOTE: ongoing event is considered as previous/next $from = new Tinebase_DateTime('2013-11-01 07:59:59'); $previousOccurrence = Calendar_Model_Rrule::computeNextOccurrence($event, $exceptions, $from, -1); $this->assertEquals('2013-10-25 08:00:00', $previousOccurrence->dtstart->toString()); // DST switch $from = new Tinebase_DateTime('2013-11-08 08:59:59'); $previousOccurrence = Calendar_Model_Rrule::computeNextOccurrence($event, $exceptions, $from, -1); $this->assertEquals('2013-11-01 09:00:00', $previousOccurrence->dtstart->toString()); }
/** * sets rrule until helper field * * @return void */ public function setRruleUntil() { if (empty($this->rrule)) { $this->rrule_until = NULL; } else { $rrule = $this->rrule; if (!$this->rrule instanceof Calendar_Model_Rrule) { $rrule = new Calendar_Model_Rrule(array()); $rrule->setFromString($this->rrule); $this->rrule = $rrule; } if (isset($rrule->count)) { $this->rrule_until = NULL; $exdates = $this->exdate; $this->exdate = NULL; $lastOccurrence = Calendar_Model_Rrule::computeNextOccurrence($this, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $this->dtend, $rrule->count - 1); $this->rrule_until = $lastOccurrence->dtend; $this->exdate = $exdates; } else { // set until to end of day in organizers timezone. // NOTE: this is in contrast to the iCal spec which says until should be the // dtstart of the last occurence. But as the client with the name of the // spec sets it to the end of the day, we do it also. if ($rrule->until instanceof Tinebase_DateTime && !$this->is_all_day_event) { $rrule->until->setTimezone($this->originator_tz); // NOTE: subSecond cause some clients send 00:00:00 for midnight $rrule->until->subSecond(1)->setTime(23, 59, 59); $rrule->until->setTimezone('UTC'); } $this->rrule_until = $rrule->until; } } if ($this->rrule_until && $this->rrule_until->getTimeStamp() - $this->dtstart->getTimeStamp() < -1) { throw new Tinebase_Exception_Record_Validation('rrule until must not be before dtstart'); } }
public function testGetRecurExceptions() { $persitentException = $this->testCreateRecurException(); $baseEvent = $this->_controller->getRecurBaseEvent($persitentException); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, $exceptions, $baseEvent->dtstart); $this->_controller->createRecurException($nextOccurance, TRUE); $exceptions = $this->_controller->getRecurExceptions($persitentException, TRUE); $dtstarts = $exceptions->dtstart; $this->assertTrue(in_array($nextOccurance->dtstart, $dtstarts), 'deleted instance missing'); $this->assertTrue(in_array($persitentException->dtstart, $dtstarts), 'exception instance missing'); }
/** * (non-PHPdoc) * @see Syncroton_Data_IDataCalendar::setAttendeeStatus() */ public function setAttendeeStatus(Syncroton_Model_MeetingResponse $response) { $event = $instance = $this->_contentController->get($response->requestId); $method = 'attenderStatusUpdate'; if ($response->instanceId instanceof DateTime) { $instance = $event->exdate->filter('recurid', $event->uid . '-' . $response->instanceId->format(Tinebase_Record_Abstract::ISO8601LONG))->getFirstRecord(); if (!$instance) { $exceptions = $event->exdate; $event->exdate = $exceptions->getOriginalDtStart(); $instance = Calendar_Model_Rrule::computeNextOccurrence($event, $exceptions, new Tinebase_DateTime($response->instanceId)); } $method = 'attenderStatusCreateRecurException'; } $attendee = Calendar_Model_Attender::getOwnAttender($instance->attendee); if (!$attendee) { throw new Syncroton_Exception_Status_MeetingResponse("party crushing not allowed", Syncroton_Exception_Status_MeetingResponse::INVALID_REQUEST); } $attendee->status = $this->_meetingResponseAttendeeStatusMapping[$response->userResponse]; Calendar_Controller_Event::getInstance()->{$method}($instance, $attendee, $attendee->status_authkey); // return id of calendar event return $response->requestId; }
/** * testAcceptInvitationForRecurringEventException * * @see 0009022: can not accept invitation to recurring event exception * @see 0009510: is it allowed to have no main vevent in ics? */ public function testAcceptInvitationForRecurringEventException() { Tinebase_Container::getInstance()->setGrants($this->objects['initialContainer'], new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array($this->_getAllCalendarGrants(), array('account_id' => 0, 'account_type' => 'anyone', Tinebase_Model_Grants::GRANT_READ => true, Tinebase_Model_Grants::GRANT_ADD => false, Tinebase_Model_Grants::GRANT_EDIT => false, Tinebase_Model_Grants::GRANT_DELETE => false, Tinebase_Model_Grants::GRANT_FREEBUSY => true, Tinebase_Model_Grants::GRANT_ADMIN => false))), true); $persistentEvent = Calendar_Controller_Event::getInstance()->create(new Calendar_Model_Event(array('container_id' => $this->objects['initialContainer']->getId(), 'rrule' => 'FREQ=WEEKLY;BYDAY=WE', 'dtstart' => new Tinebase_DateTime('20131016T120000'), 'dtend' => new Tinebase_DateTime('20131016T130000'), 'summary' => 'Meeting', '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' => 'e4546f26cb37f69baf59135e7bd379bf94bba429'))), 'uid' => '3ef8b44333aea7c01aa5a9308e2cb014807c60b3'))); // add pwulf as attender to create exception $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $exception = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, new Tinebase_DateTime('20131017T140000')); $exception->attendee->addRecord(new Calendar_Model_Attender(array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId()))); $persistentException = Calendar_Controller_Event::getInstance()->createRecurException($exception); $persistentEvent = Calendar_Controller_Event::getInstance()->get($persistentEvent->getId()); // pwulf tries to accept invitation Tinebase_Core::set(Tinebase_Core::USER, Tinebase_User::getInstance()->getFullUserByLoginName('pwulf')); $_SERVER['HTTP_USER_AGENT'] = 'Mac OS X/10.8.5 (12F45) CalendarAgent/57'; // this ics only has an exdate vevent $vcalendarStream = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/accept_exdate_invite.ics'); $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $persistentEvent); $event->put($vcalendarStream); $exdateWebDAVEvent = $event->getRecord()->exdate[0]; $this->_assertCurrentUserAttender($exdateWebDAVEvent); $exdateCalEvent = Calendar_Controller_Event::getInstance()->get($persistentException->getId()); $this->_assertCurrentUserAttender($exdateCalEvent); }
/** * testSetAlarmOfRecurSeriesException */ public function testSetAlarmOfRecurSeriesException() { $event = $this->_getEvent(); $event->rrule = 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;INTERVAL=1'; $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(new Tinebase_Model_Alarm(array('minutes_before' => 30), TRUE))); $persistentEvent = $this->_controller->create($event); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $exception = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, new Tinebase_DateTime()); $exception->dtstart->subHour(6); $exception->dtend->subHour(6); $persistentException = $this->_controller->createRecurException($exception); $baseEvent = $this->_controller->getRecurBaseEvent($persistentException); $this->_controller->getAlarms($baseEvent); $exceptions = $this->_controller->getRecurExceptions($persistentException); $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, $exceptions, Tinebase_DateTime::now()); $nextAlarmEventStart = new Tinebase_DateTime(substr($baseEvent->alarms->getFirstRecord()->getOption('recurid'), -19)); $this->assertTrue($nextOccurance->dtstart->equals($nextAlarmEventStart), 'next alarm got not adjusted'); $alarmTime = clone $persistentException->dtstart; $alarmTime->subMinute(30); $this->assertTrue($alarmTime->equals($persistentException->alarms->getFirstRecord()->alarm_time), 'alarmtime of persistent exception is not correnct/set'); }
/** * sets rrule until helper field * * @return void */ public function setRruleUntil() { if (empty($this->rrule)) { $this->rrule_until = NULL; } else { $rrule = $this->rrule; if (!$this->rrule instanceof Calendar_Model_Rrule) { $rrule = new Calendar_Model_Rrule(array()); $rrule->setFromString($this->rrule); } if (isset($rrule->count)) { $this->rrule_until = NULL; $exdates = $this->exdate; $this->exdate = NULL; $lastOccurrence = Calendar_Model_Rrule::computeNextOccurrence($this, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $this->dtend, $rrule->count - 1); $this->rrule_until = $lastOccurrence->dtend; $this->exdate = $exdates; } else { $this->rrule_until = $rrule->until; } } }
/** * event handler for group updates * * @param Tinebase_Model_Group $_group * @return void */ public function onUpdateGroup($_groupId) { $doContainerACLChecks = $this->doContainerACLChecks(FALSE); $filter = new Calendar_Model_EventFilter(array(array('field' => 'attender', 'operator' => 'equals', 'value' => array('user_type' => Calendar_Model_Attender::USERTYPE_GROUP, 'user_id' => $_groupId)), array('field' => 'period', 'operator' => 'within', 'value' => array('from' => Tinebase_DateTime::now()->get(Tinebase_Record_Abstract::ISO8601LONG), 'until' => Tinebase_DateTime::now()->addYear(100)->get(Tinebase_Record_Abstract::ISO8601LONG))))); $events = $this->search($filter, new Tinebase_Model_Pagination(), FALSE, FALSE); foreach ($events as $event) { try { if (!$event->rrule) { // update non recurring futrue events Calendar_Model_Attender::resolveGroupMembers($event->attendee); $this->update($event); } else { // update thisandfuture for recurring events $nextOccurrence = Calendar_Model_Rrule::computeNextOccurrence($event, $this->getRecurExceptions($event), Tinebase_DateTime::now()); Calendar_Model_Attender::resolveGroupMembers($nextOccurrence->attendee); if ($nextOccurrence->dtstart != $event->dtstart) { $this->createRecurException($nextOccurrence, FALSE, TRUE); } else { $this->update($nextOccurrence); } } } catch (Exception $e) { Tinebase_Core::getLogger()->NOTICE(__METHOD__ . '::' . __LINE__ . " could not update attendee"); } } $this->doContainerACLChecks($doContainerACLChecks); }
/** * migrates given event page * * @param array $_eventPage * @return void */ public function _migrateEventPage($_eventPage) { foreach ($_eventPage as $egwEventData) { try { $event = $this->_getTineEventRecord($egwEventData); $event->attendee = $this->_getEventAttendee($egwEventData); if ($event->rrule) { $exceptions = $this->_getRecurExceptions($egwEventData['cal_id']); $exceptions->merge($this->_getRecurImplicitExceptions($egwEventData)); foreach ($exceptions as $exception) { $exception['exdate'] = NULL; $exception['rrule'] = NULL; $exception->uid = $event->uid; $exception->setRecurId(); $exdateKey = array_search($exception->dtstart, $event->exdate); if ($exdateKey !== FALSE) { $this->_log->debug("removing persistent exception at {$exception->dtstart} from exdate of {$event->getId()}"); unset($event->exdate[$exdateKey]); } if (count($exception->alarms == 0) && count($event->alarms > 0)) { $exception->alarms = clone $event->alarms; } $this->_saveTineEvent($exception); } $nextOccurrence = Calendar_Model_Rrule::computeNextOccurrence($event, $exceptions, $this->_migrationStartTime); $event->alarms->setTime($nextOccurrence->dtstart); } // save baseevent $this->_saveTineEvent($event); } catch (Exception $e) { $this->_log->err('could not migrate event "' . $egwEventData['cal_id'] . '" cause: ' . $e->getMessage()); } } }
/** * adopt alarm time to next occurance for recurring events * * @param Tinebase_Record_Abstract $_record * @param Tinebase_Model_Alarm $_alarm * @param bool $_nextBy {instance|time} set recurr alarm to next from given instance or next by current time * @return void * @throws Tinebase_Exception_InvalidArgument */ public function adoptAlarmTime(Tinebase_Record_Abstract $_record, Tinebase_Model_Alarm $_alarm, $_nextBy = 'time') { if ($_record->rrule) { if ($_nextBy == 'time') { // NOTE: this also finds instances running right now $from = Tinebase_DateTime::now(); } else { $recurid = $_alarm->getOption('recurid'); $instanceStart = $recurid ? new Tinebase_DateTime(substr($recurid, -19)) : clone $_record->dtstart; $eventLength = $_record->dtstart->diff($_record->dtend); // make sure we hit the next instance $from = $instanceStart->add($eventLength)->addMinute(1); } // this would break if minutes_before > interval //$from->addMinute((int) $_alarm->getOption('minutes_before')); // compute next $exceptions = $this->getRecurExceptions($_record); $nextOccurrence = Calendar_Model_Rrule::computeNextOccurrence($_record, $exceptions, $from); // save recurid so we know for which recurrance the alarm is for $_alarm->setOption('recurid', isset($nextOccurrence) ? $nextOccurrence->recurid : NULL); $_alarm->sent_status = $nextOccurrence ? Tinebase_Model_Alarm::STATUS_PENDING : Tinebase_Model_Alarm::STATUS_SUCCESS; $_alarm->sent_message = $nextOccurrence ? '' : 'Nothing to send, series is over'; if (!$nextOccurrence) { return; } $eventStart = clone $nextOccurrence->dtstart; } else { $eventStart = clone $_record->dtstart; } // save minutes before / compute it for custom alarms $_alarm->setOption('minutes_before', $_alarm->minutes_before == Tinebase_Model_Alarm::OPTION_CUSTOM ? ($_record->dtstart->getTimestamp() - $_alarm->alarm_time->getTimestamp()) / 60 : $_alarm->minutes_before); $_alarm->setTime($eventStart); }
public function testComputeNextOccurrence() { $event = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'weekly', 'dtstart' => '2009-09-09 08:00:00', 'dtend' => '2009-09-09 10:00:00', 'rrule' => 'FREQ=WEEKLY;BYDAY=WE,FR;INTERVAL=1;UNTIL=2009-09-27 10:00:00', 'originator_tz' => 'Europe/Berlin')); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = new Tinebase_DateTime('2008-01-21 00:00:00'); $nextOccurrence = Calendar_Model_Rrule::computeNextOccurrence($event, $exceptions, $from); $this->assertTrue($event === $nextOccurrence, 'given event is next occurrence'); $nextOccurrence = Calendar_Model_Rrule::computeNextOccurrence($event, $exceptions, $nextOccurrence->dtstart); $this->assertEquals('2009-09-11 08:00:00', $nextOccurrence->dtstart->toString(Tinebase_Record_Abstract::ISO8601LONG)); $nextOccurrence = Calendar_Model_Rrule::computeNextOccurrence($event, $exceptions, $nextOccurrence->dtstart); $this->assertEquals('2009-09-16 08:00:00', $nextOccurrence->dtstart->toString(Tinebase_Record_Abstract::ISO8601LONG)); }