/** * (non-PHPdoc) * @see ActiveSync/ActiveSync_TestCase::setUp() */ protected function setUp() { parent::setUp(); // replace email to make current user organizer and attendee $this->_testXMLInput = str_replace('*****@*****.**', Tinebase_Core::getUser()->accountEmailAddress, $this->_testXMLInput); $event = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'SyncTest', 'dtstart' => Tinebase_DateTime::now()->addMonth(1)->toString(Tinebase_Record_Abstract::ISO8601LONG), 'dtend' => Tinebase_DateTime::now()->addMonth(1)->addHour(1)->toString(Tinebase_Record_Abstract::ISO8601LONG), 'originator_tz' => 'Europe/Berlin', 'container_id' => $this->_getContainerWithSyncGrant()->getId(), Tinebase_Model_Grants::GRANT_EDIT => true, 'attendee' => new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(array('user_id' => Tinebase_Core::getUser()->contact_id, 'user_type' => Calendar_Model_Attender::USERTYPE_USER, 'status' => Calendar_Model_Attender::STATUS_ACCEPTED))))); $event = Calendar_Controller_Event::getInstance()->create($event); $this->objects['event'] = $event; $event2MonthsBack = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'SyncTest', 'dtstart' => Tinebase_DateTime::now()->subMonth(2)->toString(Tinebase_Record_Abstract::ISO8601LONG), 'dtend' => Tinebase_DateTime::now()->subMonth(2)->addHour(1)->toString(Tinebase_Record_Abstract::ISO8601LONG), 'originator_tz' => 'Europe/Berlin', 'container_id' => $this->_getContainerWithSyncGrant()->getId(), Tinebase_Model_Grants::GRANT_EDIT => true)); $event = Calendar_Controller_Event::getInstance()->create($event2MonthsBack); $this->objects['event2MonthsBack'] = $event; $eventDaily = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'SyncTest', 'dtstart' => Tinebase_DateTime::now()->addMonth(1)->toString(Tinebase_Record_Abstract::ISO8601LONG), 'dtend' => Tinebase_DateTime::now()->addMonth(1)->addHour(1)->toString(Tinebase_Record_Abstract::ISO8601LONG), 'originator_tz' => 'Europe/Berlin', 'rrule' => 'FREQ=DAILY;INTERVAL=1;UNTIL=' . Tinebase_DateTime::now()->addMonth(1)->addDay(6)->setHour(22)->setMinute(59)->setSecond(59)->toString(Tinebase_Record_Abstract::ISO8601LONG), 'container_id' => $this->_getContainerWithSyncGrant()->getId(), Tinebase_Model_Grants::GRANT_EDIT => true)); $eventDaily = Calendar_Controller_Event::getInstance()->create($eventDaily); // compute recurset $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($eventDaily, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $eventDaily->dtstart, $eventDaily->rrule_until); // first deleted instance Calendar_Controller_Event::getInstance()->createRecurException($recurSet[0], true); // second deleted instance Calendar_Controller_Event::getInstance()->createRecurException($recurSet[1], true); // first exception instance $recurSet[2]->dtstart->addHour(2); $recurSet[2]->dtend->addHour(2); $recurSet[2]->summary = 'Test Exception 1'; Calendar_Controller_Event::getInstance()->createRecurException($recurSet[2]); // first exception instance $recurSet[3]->dtstart->addHour(3); $recurSet[3]->dtend->addHour(3); $recurSet[3]->summary = 'Test Exception 2'; Calendar_Controller_Event::getInstance()->createRecurException($recurSet[3]); // reread event from database again $eventDaily = Calendar_Controller_Event::getInstance()->get($eventDaily); #var_dump($eventDaily->toArray()); $this->objects['eventDaily'] = $eventDaily; Tinebase_Core::getPreference('ActiveSync')->setValue(ActiveSync_Preference::DEFAULTCALENDAR, $this->_getContainerWithSyncGrant()->getId()); ########### define test filter $filterBackend = new Tinebase_PersistentFilter_Backend_Sql(); try { $filter = $filterBackend->getByProperty('Calendar Sync Test', 'name'); } catch (Tinebase_Exception_NotFound $e) { $filter = new Tinebase_Model_PersistentFilter(array('application_id' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(), 'account_id' => Tinebase_Core::getUser()->getId(), 'model' => 'Calendar_Model_EventFilter', 'filters' => array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getContainerWithSyncGrant()->getId())), 'name' => 'Calendar Sync Test', 'description' => 'Created by unit test')); $filter = $filterBackend->create($filter); } $this->objects['filter'] = $filter; ########### define test devices $palm = ActiveSync_Backend_DeviceTests::getTestDevice(Syncope_Model_Device::TYPE_WEBOS); $palm->owner_id = $this->_testUser->getId(); $palm->calendarfilter_id = $this->objects['filter']->getId(); $this->objects['deviceWebOS'] = ActiveSync_Controller_Device::getInstance()->create($palm); $iphone = ActiveSync_Backend_DeviceTests::getTestDevice(Syncope_Model_Device::TYPE_IPHONE); $iphone->owner_id = $this->_testUser->getId(); $iphone->calendarfilter_id = $this->objects['filter']->getId(); $this->objects['deviceIPhone'] = ActiveSync_Controller_Device::getInstance()->create($iphone); }
public function testExportRecurSet() { $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = clone $this->_testEvent->dtstart; $until = clone $this->_testEvent->dtend; $until->addDay(2); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($this->_testEvent, $exceptions, $from, $until); $this->_testEvent->exdate = array($recurSet->dtstart[0]); $eventSet = new Tinebase_Record_RecordSet('Calendar_Model_Event', array($this->_testEvent, $recurSet[1])); $exporter = new Calendar_Export_Ical(); $ics = $exporter->eventToIcal($eventSet); $this->assertEquals(2, preg_match_all('/BEGIN:VEVENT\\r\\n/', $ics, $matches), 'There should be exactly 2 VEVENT compontents'); }
/** * merges Recurrences of given events into the given event set * * @param Tinebase_Record_RecordSet $_events * @param Tinebase_DateTime $_from * @param Tinebase_DateTime $_until * @return void */ public static function mergeRecurrenceSet($_events, $_from, $_until) { if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " from: {$_from} until: {$_until}"); } //compute recurset $candidates = $_events->filter('rrule', "/^FREQ.*/", TRUE); foreach ($candidates as $candidate) { try { $exceptions = $_events->filter('recurid', "/^{$candidate->uid}-.*/", TRUE); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($candidate, $exceptions, $_from, $_until); foreach ($recurSet as $event) { $_events->addRecord($event); } // check if candidate/baseEvent has an exception itself -> in this case remove baseEvent from set if (is_array($candidate->exdate) && in_array($candidate->dtstart, $candidate->exdate)) { $_events->removeRecord($candidate); } } catch (Exception $e) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Could not compute recurSet of event: {$candidate->getId()}"); } Tinebase_Exception::log($e); continue; } } }
public function testAlarmAdjust() { $event = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'custom alarm', 'dtstart' => '2011-12-30 10:30:00', 'dtend' => '2011-12-30 12:30:00', 'rrule' => 'FREQ=YEARLY', 'originator_tz' => 'Europe/Berlin', 'alarms' => array(array('alarm_time' => '2011-12-20 10:30:00', 'minutes_before' => 'custom', 'options' => Zend_Json::encode(array('minutes_before' => 14400, 'recurid' => NULL, 'custom' => TRUE)))), Tinebase_Model_Grants::GRANT_EDIT => true)); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = new Tinebase_DateTime('2012-01-01 00:00:00'); $until = new Tinebase_DateTime('2012-12-31 23:59:59'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $from, $until); $this->assertEquals(1, count($recurSet), 'recur set failed'); $this->assertEquals('2012-12-20 10:30:00', (string) $recurSet->getFirstRecord()->alarms->getFirstRecord()->alarm_time); }
public function testUpdateRecurDtstartOverDst() { // note: 2009-03-29 Europe/Berlin switched to DST $event = new Calendar_Model_Event(array('uid' => Tinebase_Record_Abstract::generateUID(), 'summary' => 'Abendessen', 'dtstart' => '2009-03-25 18:00:00', 'dtend' => '2009-03-25 18:30:00', 'originator_tz' => 'Europe/Berlin', 'rrule' => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-02 17:30:00', 'exdate' => '2009-03-27 18:00:00,2009-03-31 17:00:00', 'container_id' => $this->_testCalendar->getId(), Tinebase_Model_Grants::GRANT_EDIT => true)); $persistentEvent = $this->_controller->create($event); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = new Tinebase_DateTime('2009-03-26 00:00:00'); $until = new Tinebase_DateTime('2009-04-03 23:59:59'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); // 9 days // skip 27(exception), 31(exception), 03(until) $this->assertEquals(6, count($recurSet)); $exceptionBeforeDstBoundary = clone $recurSet[1]; // 28. $persistentExceptionBeforeDstBoundary = $this->_controller->createRecurException($exceptionBeforeDstBoundary); $exceptionAfterDstBoundary = clone $recurSet[5]; // 02. $persistentExceptionAfterDstBoundary = $this->_controller->createRecurException($exceptionAfterDstBoundary); $persistentEvent->dtstart->addDay(5); //30. $persistentEvent->dtend->addDay(5); $from->addDay(5); //31 $until->addDay(5); //08 $updatedPersistenEvent = $this->_controller->update($persistentEvent); $persistentEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)), array('field' => 'uid', 'operator' => 'equals', 'value' => $persistentEvent->uid)))); //print_r($persistentEvents->toArray()); // we don't 'see' the persistent exception from 28/ $this->assertEquals(2, count($persistentEvents)); $exceptions = $persistentEvents->filter('recurid', "/^{$persistentEvent->uid}-.*/", TRUE); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($updatedPersistenEvent, $exceptions, $from, $until); // until is not adopted $this->assertEquals(2, count($recurSet)); }
/** * testUpdatePreserveAlarmProperties * * @see #7430: Calendar sends too much alarms for recurring events */ public function testUpdatePreserveAlarmProperties() { $alarm30 = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(array('minutes_before' => 30)), TRUE); $event = $this->_getEvent(); $event->dtstart = Tinebase_DateTime::now()->subDay(1)->addMinute(15); $event->dtend = clone $event->dtstart; $event->dtend->addHour(2); $event->rrule = 'FREQ=DAILY;INTERVAL=1;COUNT=3'; $event->alarms = clone $alarm30; $event->organizer = Tinebase_Core::getUser()->contact_id; $event = Calendar_Controller_Event::getInstance()->create($event); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $event->dtstart, Tinebase_DateTime::now()->addDay(1)); $exceptionEvent = Calendar_Controller_Event::getInstance()->createRecurException($recurSet->getFirstRecord()); Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); Calendar_Controller_EventNotificationsTests::flushMailer(); $event = $this->_uit->get($event->getId()); $persistentAlarm = $event->exdate[0]->alarms->getFirstRecord(); $event->alarms = $event->alarms = clone $alarm30; Calendar_Controller_Alarm::setAcknowledgeTime($event->alarms, Tinebase_DateTime::now()); foreach ($event->exdate as $exdate) { $exdate->alarms = clone $alarm30; } $updatedEvent = $this->_uit->update($event); $updatedAlarm = $updatedEvent->exdate[0]->alarms->getFirstRecord(); $this->assertNotNull($persistentAlarm); $diff = $persistentAlarm->diff($updatedAlarm); $this->assertTrue($diff->isEmpty(), 'no diff'); $this->assertTrue(Calendar_Controller_Alarm::getAcknowledgeTime($updatedEvent->alarms->getFirstRecord()) instanceof Tinebase_DateTime, 'ack time missing'); }
/** * testRecuringAlarmWithThisAndFutureSplit * * @see 0008386: alarm is sent for recur series that is already over */ public function testRecuringAlarmWithThisAndFutureSplit() { $this->markTestSkipped('@see 0009816: fix failing testRecuringAlarmWithThisAndFutureSplit test'); $event = $this->_getEvent(); // lets flush mailer so next flushing ist faster! Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); self::flushMailer(); // make sure next occurence contains now $event->dtstart = Tinebase_DateTime::now()->subMonth(1)->addDay(1)->subHour(2); $event->dtend = clone $event->dtstart; $event->dtend->addMinute(60); $event->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=' . $event->dtstart->format('d'); $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(new Tinebase_Model_Alarm(array('minutes_before' => 2880), TRUE))); $persistentEvent = $this->_eventController->create($event); // make sure, next alarm is for next month's event Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); self::flushMailer(); // split THISANDFUTURE, alarm of old series should be set to SUCCESS because it no longer should be sent $from = $event->dtstart; $until = $event->dtend->addMonth(2); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); $recurEvent = count($recurSet) > 1 ? $recurSet[1] : $recurSet[0]; // next month $recurEvent->summary = 'split series'; $newPersistentEvent = $this->_eventController->createRecurException($recurEvent, FALSE, TRUE); // check alarms $oldSeriesAlarm = Tinebase_Alarm::getInstance()->getAlarmsOfRecord('Calendar_Model_Event', $persistentEvent->getId())->getFirstRecord(); $this->assertEquals(Tinebase_Model_Alarm::STATUS_SUCCESS, $oldSeriesAlarm->sent_status, 'no pending alarm should exist for old series: ' . print_r($oldSeriesAlarm->toArray(), TRUE)); }
/** * @see {http://forge.tine20.org/mantisbt/view.php?id=5686} */ public function testCreateRecurExceptionAllFollowingAttendeeAdd() { $from = new Tinebase_DateTime('2012-02-01 00:00:00'); $until = new Tinebase_DateTime('2012-02-29 23:59:59'); $event = new Calendar_Model_Event(array('summary' => 'Some Daily Event', 'dtstart' => '2012-02-03 09:00:00', 'dtend' => '2012-02-03 10:00:00', 'rrule' => 'FREQ=DAILY;INTERVAL=1', 'container_id' => $this->_testCalendar->getId(), 'attendee' => $this->_getAttendee())); $persistentEvent = $this->_controller->create($event); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); $recurSet[5]->attendee->addRecord(new Calendar_Model_Attender(array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['pwulf']->getId()))); $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[5], FALSE, TRUE); $this->assertEquals(3, count($updatedPersistentEvent->attendee)); }
public function testExdateContainerMoveUpdateException() { $this->markTestSkipped('exdate container move not yet forbidden'); $event = $this->_getDailyEvent(new Tinebase_DateTime('2014-02-03 09:00:00')); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = new Tinebase_DateTime('2014-02-01 00:00:00'); $until = new Tinebase_DateTime('2014-02-29 23:59:59'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $from, $until); $recurSet[2]->summary = 'exdate'; $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[2]); $this->setExpectedException('Calendar_Exception_ExdateContainer'); $updatedPersistentEvent->container_id = $this->_getTestContainer('Calendar')->getId(); $this->_controller->update($updatedPersistentEvent); }
/** * creates an exception instance of a recurring event * * NOTE: deleting persistent exceptions is done via a normal delete action * and handled in the deleteInspection * * @param Calendar_Model_Event $_event * @param bool $_deleteInstance * @param bool $_allFollowing * @param bool $_checkBusyConflicts * @return Calendar_Model_Event exception Event | updated baseEvent * * @todo replace $_allFollowing param with $range * @deprecated replace with create/update/delete */ public function createRecurException($_event, $_deleteInstance = FALSE, $_allFollowing = FALSE, $_checkBusyConflicts = FALSE) { $baseEvent = $this->getRecurBaseEvent($_event); if ($baseEvent->last_modified_time != $_event->last_modified_time) { if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) { Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " It is not allowed to create recur instance if it is clone of base event"); } throw new Tinebase_Timemachine_Exception_ConcurrencyConflict('concurrency conflict!'); } // // Maybe Later // // exdates needs to stay in baseEvents container // if ($_event->container_id != $baseEvent->container_id) { // throw new Calendar_Exception_ExdateContainer(); // } // check if this is an exception to the first occurence if ($baseEvent->getId() == $_event->getId()) { if ($_allFollowing) { throw new Exception('please edit or delete complete series!'); } // NOTE: if the baseEvent gets a time change, we can't compute the recurdid w.o. knowing the original dtstart $recurid = $baseEvent->setRecurId($baseEvent->getId()); unset($baseEvent->recurid); $_event->recurid = $recurid; } // just do attender status update if user has no edit grant if ($this->_doContainerACLChecks && !$baseEvent->{Tinebase_Model_Grants::GRANT_EDIT}) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " user has no editGrant for event: '{$baseEvent->getId()}'. Only creating exception for attendee status"); } if ($_event->attendee instanceof Tinebase_Record_RecordSet) { foreach ($_event->attendee as $attender) { if ($attender->status_authkey) { $exceptionAttender = $this->attenderStatusCreateRecurException($_event, $attender, $attender->status_authkey, $_allFollowing); } } } if (!isset($exceptionAttender)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG) && $_event->attendee instanceof Tinebase_Record_RecordSet) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Failed to update attendee: " . print_r($_event->attendee->toArray(), true)); } throw new Tinebase_Exception_AccessDenied('Failed to update attendee, status authkey might be missing'); } return $this->get($exceptionAttender->cal_event_id); } // NOTE: recurid is computed by rrule recur computations and therefore is already part of the event. if (empty($_event->recurid)) { throw new Exception('recurid must be present to create exceptions!'); } // we do notifications ourself $sendNotifications = $this->sendNotifications(FALSE); // EDIT for baseEvent is checked above, CREATE, DELETE for recur exceptions is implied with it $doContainerACLChecks = $this->doContainerACLChecks(FALSE); $db = $this->_backend->getAdapter(); $transactionId = Tinebase_TransactionManager::getInstance()->startTransaction($db); $exdate = new Tinebase_DateTime(substr($_event->recurid, -19)); $exdates = is_array($baseEvent->exdate) ? $baseEvent->exdate : array(); $originalDtstart = $_event->getOriginalDtStart(); $originalEvent = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $originalDtstart); if ($_allFollowing != TRUE) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Adding exdate for: '{$_event->recurid}'"); } array_push($exdates, $exdate); $baseEvent->exdate = $exdates; $updatedBaseEvent = $this->update($baseEvent, FALSE); if ($_deleteInstance == FALSE) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Creating persistent exception for: '{$_event->recurid}'"); } if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " Recur exception: " . print_r($_event->toArray(), TRUE)); } $_event->base_event_id = $baseEvent->getId(); $_event->setId(NULL); unset($_event->rrule); unset($_event->exdate); foreach (array('attendee', 'notes', 'alarms') as $prop) { if ($_event->{$prop} instanceof Tinebase_Record_RecordSet) { $_event->{$prop}->setId(NULL); } } $originalDtstart = $_event->getOriginalDtStart(); $dtStartHasDiff = $originalDtstart->compare($_event->dtstart) != 0; // php52 compat if (!$dtStartHasDiff) { $attendees = $_event->attendee; unset($_event->attendee); } $note = $_event->notes; unset($_event->notes); $persistentExceptionEvent = $this->create($_event, $_checkBusyConflicts); if (!$dtStartHasDiff) { // we save attendee seperatly to preserve their attributes if ($attendees instanceof Tinebase_Record_RecordSet) { $attendees->cal_event_id = $persistentExceptionEvent->getId(); $calendar = Tinebase_Container::getInstance()->getContainerById($_event->container_id); foreach ($attendees as $attendee) { $this->_createAttender($attendee, $_event, TRUE, $calendar); $this->_increaseDisplayContainerContentSequence($attendee, $persistentExceptionEvent, Tinebase_Model_ContainerContent::ACTION_CREATE); } } } // @todo save notes and add a update note -> what was updated? -> modlog is also missing $persistentExceptionEvent = $this->get($persistentExceptionEvent->getId()); } } else { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " shorten recur series for/to: '{$_event->recurid}'"); } // split past/future exceptions $pastExdates = array(); $futureExdates = array(); foreach ($exdates as $exdate) { $exdate->isLater($_event->dtstart) ? $futureExdates[] = $exdate : ($pastExdates[] = $exdate); } $persistentExceptionEvents = $this->getRecurExceptions($_event); $pastPersistentExceptionEvents = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $futurePersistentExceptionEvents = new Tinebase_Record_RecordSet('Calendar_Model_Event'); foreach ($persistentExceptionEvents as $persistentExceptionEvent) { $persistentExceptionEvent->getOriginalDtStart()->isLater($_event->dtstart) ? $futurePersistentExceptionEvents->addRecord($persistentExceptionEvent) : $pastPersistentExceptionEvents->addRecord($persistentExceptionEvent); } // update baseEvent $rrule = Calendar_Model_Rrule::getRruleFromString($baseEvent->rrule); if (isset($rrule->count)) { // get all occurences and find the split $exdate = $baseEvent->exdate; $baseEvent->exdate = NULL; //$baseCountOccurrence = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $baseEvent->rrule_until, $baseCount); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($baseEvent, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $baseEvent->dtstart, $baseEvent->rrule_until); $baseEvent->exdate = $exdate; $originalDtstart = $_event->getOriginalDtStart(); foreach ($recurSet as $idx => $rInstance) { if ($rInstance->dtstart >= $originalDtstart) { break; } } $rrule->count = $idx + 1; } else { $lastBaseOccurence = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, new Tinebase_Record_RecordSet('Calendar_Model_Event'), $_event->getOriginalDtStart()->subSecond(1), -1); $rrule->until = $lastBaseOccurence ? $lastBaseOccurence->getOriginalDtStart() : $baseEvent->dtstart; } $baseEvent->rrule = (string) $rrule; $baseEvent->exdate = $pastExdates; // NOTE: we don't want implicit attendee updates //$updatedBaseEvent = $this->update($baseEvent, FALSE); $this->_inspectEvent($baseEvent); $updatedBaseEvent = parent::update($baseEvent); if ($_deleteInstance == TRUE) { // delete all future persistent events $this->delete($futurePersistentExceptionEvents->getId()); } else { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " create new recur series for/at: '{$_event->recurid}'"); } // NOTE: in order to move exceptions correctly in time we need to find out the original dtstart // and create the new baseEvent with this time. A following update also updates its exceptions $originalDtstart = new Tinebase_DateTime(substr($_event->recurid, -19)); $adoptedDtstart = clone $_event->dtstart; $dtStartHasDiff = $adoptedDtstart->compare($originalDtstart) != 0; // php52 compat $eventLength = $_event->dtstart->diff($_event->dtend); $_event->dtstart = clone $originalDtstart; $_event->dtend = clone $originalDtstart; $_event->dtend->add($eventLength); // adopt count if (isset($rrule->count)) { $baseCount = $rrule->count; $rrule = Calendar_Model_Rrule::getRruleFromString($_event->rrule); $rrule->count = $rrule->count - $baseCount; $_event->rrule = (string) $rrule; } $_event->setId(Tinebase_Record_Abstract::generateUID()); $_event->uid = $futurePersistentExceptionEvents->uid = Tinebase_Record_Abstract::generateUID(); $_event->setId(Tinebase_Record_Abstract::generateUID()); $futurePersistentExceptionEvents->setRecurId($_event->getId()); unset($_event->recurid); unset($_event->base_event_id); foreach (array('attendee', 'notes', 'alarms') as $prop) { if ($_event->{$prop} instanceof Tinebase_Record_RecordSet) { $_event->{$prop}->setId(NULL); } } $_event->exdate = $futureExdates; $attendees = $_event->attendee; unset($_event->attendee); $note = $_event->notes; unset($_event->notes); $persistentExceptionEvent = $this->create($_event, $_checkBusyConflicts && $dtStartHasDiff); // we save attendee separately to preserve their attributes if ($attendees instanceof Tinebase_Record_RecordSet) { foreach ($attendees as $attendee) { $this->_createAttender($attendee, $persistentExceptionEvent, true); } } // @todo save notes and add a update note -> what was updated? -> modlog is also missing $persistentExceptionEvent = $this->get($persistentExceptionEvent->getId()); foreach ($futurePersistentExceptionEvents as $futurePersistentExceptionEvent) { $this->update($futurePersistentExceptionEvent, FALSE); } if ($dtStartHasDiff) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " new recur series has adpted dtstart -> update to adopt exceptions'"); } $persistentExceptionEvent->dtstart = clone $adoptedDtstart; $persistentExceptionEvent->dtend = clone $adoptedDtstart; $persistentExceptionEvent->dtend->add($eventLength); $persistentExceptionEvent = $this->update($persistentExceptionEvent, $_checkBusyConflicts); } } } Tinebase_TransactionManager::getInstance()->commitTransaction($transactionId); // restore original notification handling $this->sendNotifications($sendNotifications); $notificationAction = $_deleteInstance ? 'deleted' : 'changed'; $notificationEvent = $_deleteInstance ? $_event : $persistentExceptionEvent; // restore acl $this->doContainerACLChecks($doContainerACLChecks); // send notifications if ($this->_sendNotifications && $_event->mute != 1) { // NOTE: recur exception is a fake event from client. // this might lead to problems, so we wrap the calls try { if (count($_event->attendee) > 0) { $_event->attendee->bypassFilters = TRUE; } $_event->created_by = $baseEvent->created_by; $this->doSendNotifications($notificationEvent, Tinebase_Core::getUser(), $notificationAction, $originalEvent); } catch (Exception $e) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " " . $e->getTraceAsString()); } Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . " could not send notification {$e->getMessage()}"); } } return $_deleteInstance ? $updatedBaseEvent : $persistentExceptionEvent; }
public function testAdoptAlarmDSTBoundary() { $event = $this->_getEvent(); $event->rrule = 'FREQ=DAILY;INTERVAL=1'; $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(new Tinebase_Model_Alarm(array('minutes_before' => 30), TRUE))); $persistentEvent = $this->_eventController->create($event); // prepare alarm for last non DST instance $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $from = new Tinebase_DateTime('2012-03-24 00:00:00'); $until = new Tinebase_DateTime('2012-03-24 23:59:59'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); $alarm = $persistentEvent->alarms->getFirstRecord(); $alarm->setOption('recurid', $recurSet[0]->recurid); Tinebase_Alarm::getInstance()->update($alarm); $loadedBaseEvent = $this->_eventController->get($persistentEvent->getId()); $alarm = $loadedBaseEvent->alarms->getFirstRecord(); $this->assertEquals('2012-03-24', substr($alarm->getOption('recurid'), -19, -9), 'precondition failed'); // adopt alarm $this->_eventController->adoptAlarmTime($loadedBaseEvent, $alarm, 'instance'); $this->assertEquals('2012-03-25', substr($alarm->getOption('recurid'), -19, -9), 'alarm adoption failed'); }