0011556: sending mails to multiple recipients fails
[tine20] / tests / tine20 / Calendar / Controller / RecurTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     Calendar
6  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
7  * @copyright   Copyright (c) 2010-2014 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Goekmen Ciyiltepe <g.ciyiltepe@metaways.de>
9  */
10
11 /**
12  * Test class for Calendar_Controller_Event
13  * 
14  * @package     Calendar
15  */
16 class Calendar_Controller_RecurTest extends Calendar_TestCase
17 {
18     /**
19      * @var Calendar_Controller_Event controller
20      */
21     protected $_controller;
22     
23     public function setUp()
24     {
25         parent::setUp();
26         $this->_controller = Calendar_Controller_Event::getInstance();
27     }
28     
29     public function testInvalidRruleUntil()
30     {
31         $event = new Calendar_Model_Event(array(
32             'uid'           => Tinebase_Record_Abstract::generateUID(),
33             'summary'       => 'Abendessen',
34             'dtstart'       => '2012-06-01 18:00:00',
35             'dtend'         => '2012-06-01 18:30:00',
36             'originator_tz' => 'Europe/Berlin',
37             'rrule'         => 'FREQ=DAILY;INTERVAL=1;UNTIL=2011-05-31 17:30:00',
38             'container_id'  => $this->_getTestCalendar()->getId(),
39         ));
40         
41         $this->setExpectedException('Tinebase_Exception_Record_Validation');
42         $persistentEvent = $this->_controller->create($event);
43     }
44     
45     /**
46      * imcomplete rrule clauses should be filled in automatically
47      */
48     public function testIncompleteRrule()
49     {
50         $event = $this->_getRecurEvent();
51         
52         $event->rrule = 'FREQ=WEEKLY';
53         $persistentEvent = $this->_controller->create(clone $event);
54         $this->assertEquals(Calendar_Model_Rrule::getWeekStart(), $persistentEvent->rrule->wkst, 'wkst not normalized');
55         $this->assertEquals('TH', $persistentEvent->rrule->byday, 'byday not normalized');
56         
57         $rrule = Calendar_Model_Rrule::getRruleFromString('FREQ=MONTHLY');
58         $rrule->normalize($event);
59         $this->assertEquals(20, $rrule->bymonthday, 'bymonthday not normalized');
60         
61         $rrule = Calendar_Model_Rrule::getRruleFromString('FREQ=MONTHLY;BYDAY=1TH');
62         $rrule->normalize($event);
63         $this->assertEquals(NULL, $rrule->bymonthday, 'bymonthday must not be added');
64         
65         $rrule = Calendar_Model_Rrule::getRruleFromString('FREQ=YEARLY');
66         $rrule->normalize($event);
67         $this->assertEquals(5, $rrule->bymonth, 'bymonth not normalized');
68         $this->assertEquals(20, $rrule->bymonthday, 'bymonthday not normalized');
69         
70         $rrule = Calendar_Model_Rrule::getRruleFromString('FREQ=YEARLY;BYDAY=1TH');
71         $rrule->normalize($event);
72         $this->assertEquals(5, $rrule->bymonth, 'bymonth not normalized');
73         $this->assertEquals(NULL, $rrule->bymonthday, 'bymonthday must not be added');
74     }
75     
76     public function testFirstInstanceException()
77     {
78         $from = new Tinebase_DateTime('2011-04-18 00:00:00');
79         $until = new Tinebase_DateTime('2011-04-24 23:59:59');
80         
81         $event = new Calendar_Model_Event(array(
82             'uid'           => Tinebase_Record_Abstract::generateUID(),
83             'summary'       => 'Abendessen',
84             'dtstart'       => '2011-04-20 14:00:00',
85             'dtend'         => '2011-04-20 15:30:00',
86             'originator_tz' => 'Europe/Berlin',
87             'rrule'         => 'FREQ=WEEKLY;INTERVAL=3;WKST=SU;BYDAY=TU,TH',
88             'container_id'  => $this->_getTestCalendar()->getId(),
89             Tinebase_Model_Grants::GRANT_EDIT     => true,
90         ));
91         
92         $persistentEvent = $this->_controller->create($event);
93         
94         $eventException = clone $persistentEvent;
95         $eventException->summary = 'Dinner';
96         $eventException->dtstart->subHour(2);
97         $eventException->dtend->subHour(2);
98         $persistentEventException = $this->_controller->createRecurException($eventException);
99         
100         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
101             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
102         )));
103         
104         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
105         $this->assertEquals(2, count($weekviewEvents), 'there should only be 2 events in the set');
106         $this->assertFalse(in_array($persistentEvent->getId(), $weekviewEvents->getId()), 'baseEvent should not be in the set!');
107         
108         return $weekviewEvents;
109     }
110     
111     /**
112      * @see 8618: delete exdate / range this and future fails
113      */
114     public function testFirstInstanceExceptionDeleteRangeThisAndFuture()
115     {
116         $events = $this->testFirstInstanceException();
117         $firstInstanceException = $events->getFirstRecord();
118         
119         $this->_controller->delete($firstInstanceException->getId(), Calendar_Model_Event::RANGE_THISANDFUTURE);
120         
121         $this->setExpectedException('Tinebase_Exception_NotFound');
122         $this->_controller->get($firstInstanceException->getId());
123     }
124     
125     /**
126      * @see 8618: delete exdate / range this and future fails
127      */
128     public function testFirstInstanceExceptionUpdateRangeThisAndFuture()
129     {
130         $events = $this->testFirstInstanceException();
131         $firstInstanceException = $events->getFirstRecord();
132         $location = 'At Home';
133         $firstInstanceException->location = $location;
134     
135         $result = $this->_controller->update($firstInstanceException, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE);
136         $this->assertEquals($location, $result->location);
137     }
138
139     /**
140      * testFirstInstanceExceptionUpdateRangeAll
141      * 
142      * @see 0008826: update range:all does not work on first occurrence exception
143      */
144     public function testFirstInstanceExceptionUpdateRangeAll()
145     {
146         $events = $this->testFirstInstanceException();
147         $firstInstanceException = $events->getFirstRecord();
148         $location = 'At Home';
149         $firstInstanceException->location = $location;
150     
151         $result = $this->_controller->update($firstInstanceException, FALSE, Calendar_Model_Event::RANGE_ALL);
152         $this->assertEquals($result->location, $location);
153         
154         // @todo check other instances?
155     }
156     
157     /**
158      * @see #5802: moving last event of a recurring set with count part creates a instance a day later
159      */
160     public function testLastInstanceException()
161     {
162         $from = new Tinebase_DateTime('2012-02-20 00:00:00');
163         $until = new Tinebase_DateTime('2012-02-26 23:59:59');
164         
165         $event = new Calendar_Model_Event(array(
166                 'uid'           => Tinebase_Record_Abstract::generateUID(),
167                 'summary'       => 'Abendessen',
168                 'dtstart'       => '2012-02-22 14:00:00',
169                 'dtend'         => '2012-02-22 15:30:00',
170                 'originator_tz' => 'Europe/Berlin',
171                 'rrule'         => 'FREQ=DAILY;COUNT=3',
172                 'container_id'  => $this->_getTestCalendar()->getId(),
173         ));
174         
175         $persistentEvent = $this->_controller->create($event);
176         
177         // create exception
178         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
179             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
180         )));
181         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
182         $weekviewEvents[2]->dtstart->subHour(5);
183         $weekviewEvents[2]->dtend->subHour(5);
184         $this->_controller->createRecurException($weekviewEvents[2]);
185         
186         // load series
187         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
188             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
189         )));
190         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
191         $weekviewEvents->sort('dtstart', 'ASC');
192         
193         $this->assertEquals(3, count($weekviewEvents), 'wrong count');
194         $this->assertEquals('2012-02-24 09:00:00', $weekviewEvents[2]->dtstart->toString());
195     }
196     
197     /**
198      * http://forge.tine20.org/mantisbt/view.php?id=4810
199      */
200     public function testWeeklyException()
201     {
202         $from = new Tinebase_DateTime('2011-09-01 00:00:00');
203         $until = new Tinebase_DateTime('2011-09-30 23:59:59');
204         
205         $event = new Calendar_Model_Event(array(
206             'uid'               => Tinebase_Record_Abstract::generateUID(),
207             'summary'           => 'weekly',
208             'dtstart'           => '2011-09-11 22:00:00',
209             'dtend'             => '2011-09-12 21:59:59',
210             'is_all_day_event'  => true,
211             'originator_tz' => 'Europe/Berlin',
212             'rrule'         => 'FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH',
213             'container_id'  => $this->_getTestCalendar()->getId(),
214             Tinebase_Model_Grants::GRANT_EDIT     => true,
215         ));
216         
217         $persistentEvent = $this->_controller->create($event);
218         
219         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
220             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
221         )));
222         
223         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
224         $this->assertEquals(12, count($weekviewEvents), 'there should be 12 events in the set');
225         
226         // delte one instance
227         $exception = $weekviewEvents->filter('dtstart', new Tinebase_DateTime('2011-09-19 22:00:00'))->getFirstRecord();
228         $persistentEventException = $this->_controller->createRecurException($exception, TRUE);
229         
230         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
231             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
232         )));
233         
234         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
235         $this->assertEquals(11, count($weekviewEvents), 'there should be 11 events in the set');
236         
237         $exception = $weekviewEvents->filter('dtstart', new Tinebase_DateTime('2011-09-19 22:00:00'))->getFirstRecord();
238         $this->assertTrue(!$exception, 'exception must not be in eventset');
239     }
240     
241     public function testAttendeeSetStatusRecurException()
242     {
243         // note: 2009-03-29 Europe/Berlin switched to DST
244         $event = new Calendar_Model_Event(array(
245             'uid'           => Tinebase_Record_Abstract::generateUID(),
246             'summary'       => 'Abendessen',
247             'dtstart'       => '2009-03-25 18:00:00',
248             'dtend'         => '2009-03-25 18:30:00',
249             'originator_tz' => 'Europe/Berlin',
250             'rrule'         => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-03-31 17:30:00',
251             'exdate'        => '2009-03-27 18:00:00,2009-03-29 17:00:00',
252             'container_id'  => $this->_getTestCalendar()->getId(),
253             Tinebase_Model_Grants::GRANT_EDIT     => true,
254         ));
255         $event->attendee = $this->_getAttendee();
256         unset($event->attendee[1]);
257         
258         $persistentEvent = $this->_controller->create($event);
259         $attendee = $persistentEvent->attendee[0];
260         
261         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
262         $from = new Tinebase_DateTime('2009-03-26 00:00:00');
263         $until = new Tinebase_DateTime('2009-04-01 23:59:59');
264         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
265         
266         $exception = $recurSet->getFirstRecord();
267         $attendee = $exception->attendee[0];
268         $attendee->status = Calendar_Model_Attender::STATUS_ACCEPTED;
269         
270         $this->_controller->attenderStatusCreateRecurException($exception, $attendee, $attendee->status_authkey);
271         
272         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
273             array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)),
274             array('field' => 'uid', 'operator' => 'equals', 'value' => $persistentEvent->uid)
275         )));
276         
277         $recurid = array_values(array_filter($events->recurid));
278         $this->assertEquals(1, count($recurid), 'only recur instance must have a recurid');
279         $this->assertEquals('2009-03-26 18:00:00', substr($recurid[0], -19));
280         $this->assertEquals(2, count($events));
281     }
282     
283     public function testFirstInstanceAttendeeSetStatusRecurException()
284     {
285         $from = new Tinebase_DateTime('2011-04-18 00:00:00');
286         $until = new Tinebase_DateTime('2011-04-24 23:59:59');
287         
288         $event = new Calendar_Model_Event(array(
289             'uid'           => Tinebase_Record_Abstract::generateUID(),
290             'summary'       => 'Abendessen',
291             'dtstart'       => '2011-04-20 14:00:00',
292             'dtend'         => '2011-04-20 15:30:00',
293             'originator_tz' => 'Europe/Berlin',
294             'rrule'         => 'FREQ=WEEKLY;INTERVAL=3;WKST=SU;BYDAY=TU,TH',
295             'container_id'  => $this->_getTestCalendar()->getId(),
296             Tinebase_Model_Grants::GRANT_EDIT     => true,
297         ));
298         $event->attendee = $this->_getAttendee();
299         unset($event->attendee[1]);
300         
301         $persistentEvent = $this->_controller->create($event);
302         $attendee = $persistentEvent->attendee[0];
303         $attendee->status = Calendar_Model_Attender::STATUS_ACCEPTED;
304         
305         $this->_controller->attenderStatusCreateRecurException(clone $persistentEvent, $attendee, $attendee->status_authkey);
306         
307         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
308             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
309         )));
310         
311         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
312         
313         $this->assertEquals(2, count($weekviewEvents), 'there should only be 2 events in the set');
314         $this->assertFalse(in_array($persistentEvent->getId(), $weekviewEvents->getId()), 'baseEvent should not be in the set!');
315     }
316     
317     /**
318      * Conflict between an existing and recurring event when create the event
319      */
320     public function testCreateConflictBetweenRecurAndExistEvent()
321     {
322         $event = $this->_getEvent();
323         $event->dtstart = '2010-05-20 06:00:00';
324         $event->dtend = '2010-05-20 06:15:00';
325         $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
326             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
327             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
328         ));
329         $this->_controller->create($event);
330
331         $event1 = $this->_getRecurEvent();
332         $event1->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
333             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
334             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
335         ));
336         
337         $this->setExpectedException('Calendar_Exception_AttendeeBusy');
338         $this->_controller->create($event1, TRUE);
339     }
340     
341     /**
342      * Conflict between an existing and recurring event when update the event
343      */
344     public function testUpdateConflictBetweenRecurAndExistEvent()
345     {
346         $event = $this->_getEvent();
347         $event->dtstart = '2010-05-20 06:00:00';
348         $event->dtend = '2010-05-20 06:15:00';
349         $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
350             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
351             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
352         ));
353         $this->_controller->create($event);
354
355         $event1 = $this->_getRecurEvent();
356         $event1->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
357             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
358             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
359         ));
360         
361         $event1 = $this->_controller->create($event1);
362         $event1->rrule = "FREQ=DAILY;INTERVAL=2";
363         
364         $this->setExpectedException('Calendar_Exception_AttendeeBusy');
365         $this->_controller->update($event1, TRUE);
366     }
367     
368     /**
369      * check that fake clones of dates of persistent exceptions are left out in recur set calculation
370      */
371     public function testRecurSetCalcLeafOutPersistentExceptionDates()
372     {
373         // month 
374         $from = new Tinebase_DateTime('2010-06-01 00:00:00');
375         $until = new Tinebase_DateTime('2010-06-31 23:59:59');
376         
377         $event = $this->_getRecurEvent();
378         $event->rrule = "FREQ=MONTHLY;INTERVAL=1;BYDAY=3TH";
379         $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
380             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
381             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
382         ));
383         
384         $persistentRecurEvent = $this->_controller->create($event);
385         
386         // get first recurrance
387         $eventSet = new Tinebase_Record_RecordSet('Calendar_Model_Event', array($persistentRecurEvent));
388         Calendar_Model_Rrule::mergeRecurrenceSet($eventSet, 
389             new Tinebase_DateTime('2010-06-01 00:00:00'),
390             new Tinebase_DateTime('2010-06-31 23:59:59')
391         );
392         $firstRecurrance = $eventSet[1];
393         
394         // create exception of this first occurance: 17.6. -> 24.06.
395         $firstRecurrance->dtstart->add(1, Tinebase_DateTime::MODIFIER_WEEK);
396         $firstRecurrance->dtend->add(1, Tinebase_DateTime::MODIFIER_WEEK);
397         $this->_controller->createRecurException($firstRecurrance);
398         
399         // fetch weekview 14.06 - 20.06.
400         $from = new Tinebase_DateTime('2010-06-14 00:00:00');
401         $until = new Tinebase_DateTime('2010-06-20 23:59:59');
402         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
403             array('field' => 'uid', 'operator' => 'equals', 'value' => $persistentRecurEvent->uid),
404             array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until),
405         ))));
406         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
407         
408         // make sure the 17.6. is not in the set
409         $this->assertEquals(1, count($weekviewEvents),
410                 '17.6. is an exception date and must not be part of this weekview: '
411                 . print_r($weekviewEvents->toArray(), true));
412     }
413     
414     public function testCreateRecurExceptionPreserveAttendeeStatus()
415     {
416         $from = new Tinebase_DateTime('2012-03-01 00:00:00');
417         $until = new Tinebase_DateTime('2012-03-31 23:59:59');
418         
419         $event = new Calendar_Model_Event(array(
420                 'summary'       => 'Some Daily Event',
421                 'dtstart'       => '2012-03-13 09:00:00',
422                 'dtend'         => '2012-03-13 10:00:00',
423                 'rrule'         => 'FREQ=DAILY;INTERVAL=1',
424                 'container_id'  => $this->_getTestCalendar()->getId(),
425                 'attendee'      => $this->_getAttendee(),
426         ));
427         
428         $persistentEvent = $this->_controller->create($event);
429         $persistentSClever = Calendar_Model_Attender::getAttendee($persistentEvent->attendee, $event->attendee[1]);
430         
431         // accept series for sclever
432         $persistentSClever->status = Calendar_Model_Attender::STATUS_ACCEPTED;
433         $this->_controller->attenderStatusUpdate($persistentEvent, $persistentSClever, $persistentSClever->status_authkey);
434         
435         // create recur exception w.o. scheduling change
436         $persistentEvent = $this->_controller->get($persistentEvent->getId());
437         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
438         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
439         
440         $recurSet[5]->description = 'From now on, everything will be better'; //2012-03-19
441         $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[5], FALSE, FALSE);
442         
443         $updatedPersistentSClever = Calendar_Model_Attender::getAttendee($updatedPersistentEvent->attendee, $event->attendee[1]);
444         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $updatedPersistentSClever->status, 'status must not change');
445         
446         
447         // create recur exception with scheduling change
448         $updatedBaseEvent = $this->_controller->getRecurBaseEvent($recurSet[6]);
449         $recurSet[6]->last_modified_time = $updatedBaseEvent->last_modified_time;
450         $recurSet[6]->dtstart->addHour(2);
451         $recurSet[6]->dtend->addHour(2);
452         $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[6], FALSE, FALSE);
453         
454         $updatedPersistentSClever = Calendar_Model_Attender::getAttendee($updatedPersistentEvent->attendee, $event->attendee[1]);
455         $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, $updatedPersistentSClever->status, 'status must change');
456     }
457     
458     public function testCreateRecurExceptionAllFollowingGeneral()
459     {
460         $from = new Tinebase_DateTime('2011-04-21 00:00:00');
461         $until = new Tinebase_DateTime('2011-04-28 23:59:59');
462         
463         $event = new Calendar_Model_Event(array(
464             'uid'           => Tinebase_Record_Abstract::generateUID(),
465             'summary'       => 'Latte bei Schweinske',
466             'dtstart'       => '2011-04-21 10:00:00',
467             'dtend'         => '2011-04-21 12:00:00',
468             'originator_tz' => 'Europe/Berlin',
469             'rrule'         => 'FREQ=DAILY;INTERVAL=1;UNTIL=2011-04-27 21:59:59',
470             'container_id'  => $this->_getTestCalendar()->getId()
471         ));
472         
473         $persistentEvent = $this->_controller->create($event);
474         
475         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
476         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
477         
478         // create exceptions
479         $recurSet->summary = 'Limo bei Schweinske';
480         $recurSet[5]->dtstart->addHour(2);
481         $recurSet[5]->dtend->addHour(2);
482         
483         $this->_controller->createRecurException($recurSet[1], TRUE);  // (23) delete instance
484         
485         $updatedBaseEvent = $this->_controller->getRecurBaseEvent($recurSet[2]);
486         $recurSet[2]->last_modified_time = $updatedBaseEvent->last_modified_time;
487         $this->_controller->createRecurException($recurSet[2], FALSE); // (24) move instance
488         
489         $updatedBaseEvent = $this->_controller->getRecurBaseEvent($recurSet[4]);
490         $recurSet[4]->last_modified_time = $updatedBaseEvent->last_modified_time;
491         $this->_controller->createRecurException($recurSet[4], TRUE);  // (26) delete instance
492         
493         $updatedBaseEvent = $this->_controller->getRecurBaseEvent($recurSet[5]);
494         $recurSet[5]->last_modified_time = $updatedBaseEvent->last_modified_time;
495         $this->_controller->createRecurException($recurSet[5], FALSE); // (27) move instance
496         
497         // now test update allfollowing
498         $recurSet[3]->summary = 'Spezi bei Schwinske';
499         $recurSet[3]->dtstart->addHour(4);
500         $recurSet[3]->dtend->addHour(4);
501         
502         $updatedBaseEvent = $this->_controller->getRecurBaseEvent($recurSet[3]);
503         $recurSet[3]->last_modified_time = $updatedBaseEvent->last_modified_time;
504         $newBaseEvent = $this->_controller->createRecurException($recurSet[3], FALSE, TRUE);
505         
506         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
507             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
508             array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until),
509         ))));
510         
511         Calendar_Model_Rrule::mergeRecurrenceSet($events, $from, $until);
512         $this->assertEquals(6, count($events), 'there should be exactly 6 events');
513         
514         $oldSeries = $events->filter('uid', $persistentEvent->uid);
515         $newSeries = $events->filter('uid', $newBaseEvent->uid);
516         $this->assertEquals(3, count($oldSeries), 'there should be exactly 3 events with old uid');
517         $this->assertEquals(3, count($newSeries), 'there should be exactly 3 events with new uid');
518         
519         $this->assertEquals(1, count($oldSeries->filter('recurid', "/^$/", TRUE)), 'there should be exactly one old base event');
520         $this->assertEquals(1, count($newSeries->filter('recurid', "/^$/", TRUE)), 'there should be exactly one new base event');
521         
522         $this->assertEquals(1, count($oldSeries->filter('recurid', "/^.+/", TRUE)->filter('rrule', '/^$/', TRUE)), 'there should be exactly one old persitent event exception');
523         $this->assertEquals(1, count($newSeries->filter('recurid', "/^.+/", TRUE)->filter('rrule', '/^$/', TRUE)), 'there should be exactly one new persitent event exception');
524         
525         $this->assertEquals(1, count($oldSeries->filter('id', "/^fake.*/", TRUE)), 'there should be exactly one old fake event');
526         $this->assertEquals(1, count($newSeries->filter('id', "/^fake.*/", TRUE)), 'there should be exactly one new fake event'); //26 (reset)
527         
528         $oldBaseEvent = $oldSeries->filter('recurid', "/^$/", TRUE)->getFirstRecord();
529         $newBaseEvent = $newSeries->filter('recurid', "/^$/", TRUE)->getFirstRecord();
530         
531         $this->assertFalse(!!array_diff($oldBaseEvent->exdate, array(
532             new Tinebase_DateTime('2011-04-23 10:00:00'),
533             new Tinebase_DateTime('2011-04-24 10:00:00'),
534         )), 'exdate of old series');
535         
536         $this->assertFalse(!!array_diff($newBaseEvent->exdate, array(
537             new Tinebase_DateTime('2011-04-27 14:00:00'),
538         )), 'exdate of new series');
539         
540         $this->assertFalse(!!array_diff($oldSeries->dtstart, array(
541             new Tinebase_DateTime('2011-04-21 10:00:00'),
542             new Tinebase_DateTime('2011-04-22 10:00:00'),
543             new Tinebase_DateTime('2011-04-24 10:00:00'),
544         )), 'dtstart of old series');
545         
546         $this->assertFalse(!!array_diff($newSeries->dtstart, array(
547             new Tinebase_DateTime('2011-04-25 14:00:00'),
548             new Tinebase_DateTime('2011-04-26 14:00:00'),
549             new Tinebase_DateTime('2011-04-27 12:00:00'),
550         )), 'dtstart of new series');
551     }
552
553     public function testCreateRecurExceptionAllFollowingAllDay()
554     {
555         $from = new Tinebase_DateTime('2015-07-01 00:00:00');
556         $until = new Tinebase_DateTime('2015-09-29 23:59:59');
557
558         $event = new Calendar_Model_Event(array(
559             'summary'           => 'Mettwoch',
560             'dtstart'           => '2015-02-10 23:00:00',
561             'dtend'             => '2015-02-11 22:59:59',
562             'is_all_day_event'  => 1,
563             'description'       => '2 Pfund Mett. 15 Brotchen. 1ne Zwiebel',
564             'rrule'             => 'FREQ=MONTHLY;INTERVAL=1;BYDAY=2WE',
565             'container_id'      => method_exists($this, '_getTestCalendar') ?
566                 $this->_getTestCalendar()->getId() :
567                 $this->_testCalendar->getId(),
568             'attendee'          => $this->_getAttendee(),
569         ));
570
571         $persistentEvent = $this->_controller->create($event);
572
573         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
574         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
575
576         $recurSet[1]->description = '4 Pfund Mett. 15 Brotchen. 2 Zwiebeln';
577         $newBaseEvent = $this->_controller->createRecurException($recurSet[1], FALSE, TRUE);
578
579         $oldBaseEvent = $this->_controller->get($persistentEvent->getId());
580         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
581         $oldRecurSet = Calendar_Model_Rrule::computeRecurrenceSet($oldBaseEvent, $exceptions, $from, $until);
582
583         $this->assertCount(1, $oldRecurSet, 'one event should be left in given period ');
584         $this->assertEquals('2015-07-07 22:00:00', $oldRecurSet[0]->dtstart);
585     }
586
587     /**
588      * if not resheduled, attendee status must be preserved
589      */
590     public function testCreateRecurExceptionAllFollowingPreserveAttendeeStatus()
591     {
592         $from = new Tinebase_DateTime('2012-02-01 00:00:00');
593         $until = new Tinebase_DateTime('2012-02-29 23:59:59');
594         
595         $event = new Calendar_Model_Event(array(
596             'summary'       => 'Some Daily Event',
597             'dtstart'       => '2012-02-03 09:00:00',
598             'dtend'         => '2012-02-03 10:00:00',
599             'rrule'         => 'FREQ=DAILY;INTERVAL=1',
600             'container_id'  => $this->_getTestCalendar()->getId(),
601             'attendee'      => $this->_getAttendee(),
602         ));
603         
604         $persistentEvent = $this->_controller->create($event);
605         $persistentSClever = Calendar_Model_Attender::getAttendee($persistentEvent->attendee, $event->attendee[1]);
606         
607         // accept series for sclever
608         $persistentSClever->status = Calendar_Model_Attender::STATUS_ACCEPTED;
609         $this->_controller->attenderStatusUpdate($persistentEvent, $persistentSClever, $persistentSClever->status_authkey);
610         
611         // update "allfollowing" w.o. scheduling change
612         $persistentEvent = $this->_controller->get($persistentEvent->getId());
613         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
614         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
615         
616         $recurSet[5]->description = 'From now on, everything will be better'; //2012-02-09 
617         $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[5], FALSE, TRUE);
618         
619         $updatedPersistentSClever = Calendar_Model_Attender::getAttendee($updatedPersistentEvent->attendee, $event->attendee[1]);
620         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $updatedPersistentSClever->status, 'status must not change');
621     }
622     
623     /**
624      * @see https://forge.tine20.org/mantisbt/view.php?id=6548
625      */
626     public function testCreateRecurExceptionsConcurrently()
627     {
628         $from = new Tinebase_DateTime('2012-06-01 00:00:00');
629         $until = new Tinebase_DateTime('2012-06-30 23:59:59');
630         
631         $event = new Calendar_Model_Event(array(
632             'uid'           => Tinebase_Record_Abstract::generateUID(),
633             'summary'       => 'Concurrent Recur updates',
634             'dtstart'       => '2012-06-01 10:00:00',
635             'dtend'         => '2012-06-01 12:00:00',
636             'originator_tz' => 'Europe/Berlin',
637             'rrule'         => 'FREQ=WEEKLY;INTERVAL=1',
638             'container_id'  => $this->_getTestCalendar()->getId()
639         ));
640         
641         $persistentEvent = $this->_controller->create($event);
642         
643         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
644         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
645         
646         // create all following exception with first session
647         $firstSessionExdate = clone $recurSet[1];
648         $firstSessionExdate->summary = 'all following update';
649         $this->_controller->createRecurException($firstSessionExdate, FALSE, TRUE);
650         
651         // try to update exception concurrently
652         $this->setExpectedException('Tinebase_Timemachine_Exception_ConcurrencyConflict');
653         $secondSessionExdate = clone $recurSet[1];
654         $secondSessionExdate->summary = 'just an update';
655         $this->_controller->createRecurException($secondSessionExdate, FALSE, TRUE);
656     }
657     
658     /**
659      * test implicit recur (exception) series creation for attendee status only
660      */
661     public function testAttendeeSetStatusRecurExceptionAllFollowing()
662     {
663         $from = new Tinebase_DateTime('2012-02-01 00:00:00');
664         $until = new Tinebase_DateTime('2012-02-29 23:59:59');
665         
666         $event = new Calendar_Model_Event(array(
667             'summary'       => 'Some Daily Event',
668             'dtstart'       => '2012-02-03 09:00:00',
669             'dtend'         => '2012-02-03 10:00:00',
670             'rrule'         => 'FREQ=DAILY;INTERVAL=1',
671             'container_id'  => $this->_getTestCalendar()->getId(),
672             'attendee'      => $this->_getAttendee(),
673         ));
674         
675         $persistentEvent = $this->_controller->create($event);
676         
677         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
678         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
679         
680         // accept for sclever thisandfuture
681         $start = $recurSet[10];
682         $sclever = Calendar_Model_Attender::getAttendee($start->attendee, $event->attendee[1]);
683         $sclever->status = Calendar_Model_Attender::STATUS_ACCEPTED;
684         $this->_controller->attenderStatusCreateRecurException($start, $sclever, $sclever->status_authkey, TRUE);
685         
686         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
687             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId())
688         )))->sort('dtstart', 'ASC');
689         
690         // assert two baseEvents
691         $this->assertTrue($events[0]->rrule_until instanceof Tinebase_DateTime, 'rrule_until of first baseEvent is not set');
692         $this->assertTrue($events[0]->rrule_until < new Tinebase_DateTime('2012-02-14 09:00:00'), 'rrule_until of first baseEvent is not adopted properly');
693         $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, Calendar_Model_Attender::getAttendee($events[0]->attendee, $event->attendee[1])->status, 'first baseEvent status must not be touched');
694         
695         $this->assertEquals($events[1]->dtstart, new Tinebase_DateTime('2012-02-14 09:00:00'), 'start of second baseEvent is wrong');
696         $this->assertTrue(empty($events[1]->recurid), 'second baseEvent is not a baseEvent');
697         $this->assertEquals((string) $event->rrule, (string) $events[1]->rrule, 'rrule of second baseEvent must be set');
698         $this->assertFalse($events[1]->rrule_until instanceof Tinebase_DateTime, 'rrule_until of second baseEvent must not be set');
699         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, Calendar_Model_Attender::getAttendee($events[1]->attendee, $event->attendee[1])->status, 'second baseEvent status is not touched');
700     }
701
702     public function testCreateRecurException()
703     {
704         $event = $this->_getEvent();
705         $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
706         $persistentEvent = $this->_controller->create($event);
707
708         $exception = clone $persistentEvent;
709         $exception->dtstart->addDay(3);
710         $exception->dtend->addDay(3);
711         $exception->summary = 'Abendbrot';
712         $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
713         $persistentException = $this->_controller->createRecurException($exception);
714
715         $persistentEvent = $this->_controller->get($persistentEvent->getId());
716         $this->assertEquals(1, count($persistentEvent->exdate));
717
718         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
719             array('field' => 'uid',     'operator' => 'equals', 'value' => $persistentEvent->uid),
720         )));
721         $this->assertEquals(2, count($events));
722
723         return $persistentException;
724     }
725
726     public function testGetRecurExceptions()
727     {
728         $persistentException = $this->testCreateRecurException();
729
730         $baseEvent = $this->_controller->getRecurBaseEvent($persistentException);
731
732         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
733         $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, $exceptions, $baseEvent->dtend);
734         $this->_controller->createRecurException($nextOccurance, TRUE);
735
736         $exceptions = $this->_controller->getRecurExceptions($persistentException, TRUE);
737         $dtstarts = $exceptions->dtstart;
738
739         $this->assertTrue(in_array($nextOccurance->dtstart, $dtstarts), 'deleted instance missing');
740         $this->assertTrue(in_array($persistentException->dtstart, $dtstarts), 'exception instance missing');
741     }
742
743     /**
744      * testUpdateEventWithRruleAndRecurId
745      *
746      * @see 0008696: do not allow both rrule and recurId in event
747      */
748     public function testUpdateEventWithRruleAndRecurId()
749     {
750         $persistentRecurEvent = $this->testCreateRecurException();
751         $persistentRecurEvent->rrule = 'FREQ=DAILY;INTERVAL=1';
752
753         $updatedEvent = $this->_controller->update($persistentRecurEvent);
754
755         $this->assertEquals(NULL, $updatedEvent->rrule);
756     }
757
758    /**
759     * @see {http://forge.tine20.org/mantisbt/view.php?id=5686}
760     */
761     public function testCreateRecurExceptionAllFollowingAttendeeAdd()
762     {
763         $from = new Tinebase_DateTime('2012-02-01 00:00:00');
764         $until = new Tinebase_DateTime('2012-02-29 23:59:59');
765         
766         $persistentEvent = $this->_getDailyEvent(new Tinebase_DateTime('2012-02-03 09:00:00'));
767         
768         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
769         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
770
771         $pwulf = new Calendar_Model_Attender(array(
772             'user_type'   => Calendar_Model_Attender::USERTYPE_USER,
773             'user_id'     => $this->_getPersonasContacts('pwulf')->getId()
774         ));
775         $recurSet[5]->attendee->addRecord($pwulf);
776         
777         $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[5], FALSE, TRUE);
778         
779         $this->assertEquals(3, count($updatedPersistentEvent->attendee));
780
781         $persistentPwulf = Calendar_Model_Attender::getAttendee($updatedPersistentEvent->attendee, $pwulf);
782         $this->assertNotNull($persistentPwulf->displaycontainer_id);
783     }
784     
785     /**
786      * Events don't show up in attendees personal calendar
787      */
788     public function testCreateRecurExceptionAllFollowingAttendeeAdd2()
789     {
790         $from = new Tinebase_DateTime('2014-04-01 00:00:00');
791         $until = new Tinebase_DateTime('2014-04-29 23:59:59');
792         
793         $persistentEvent = $this->_getDailyEvent(new Tinebase_DateTime('2014-04-03 09:00:00'));
794         
795         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
796         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until);
797         
798         $recurSet[5]->attendee->addRecord(new Calendar_Model_Attender(array(
799                 'user_type'   => Calendar_Model_Attender::USERTYPE_USER,
800                 'user_id'     => $this->_getPersonasContacts('pwulf')->getId()
801         )));
802         
803         $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[5], FALSE, TRUE);
804         $this->assertEquals(3, count($updatedPersistentEvent->attendee));
805         
806         $filter = new Calendar_Model_EventFilter(array(
807                 array('field' => 'container_id',             'operator' => 'equals', 'value' => $this->_personasDefaultCals['pwulf']->id),
808                 array('field' => 'attender_status', 'operator' => 'not',    'value' => Calendar_Model_Attender::STATUS_DECLINED),
809         ));
810         
811         $events = $this->_controller->search($filter);
812         $this->assertEquals(1, count($events), 'event should be found, but is not');
813     }
814     
815     /**
816      * create daily recur series
817      * 
818      * @param Tinebase_DateTime $dtstart
819      * @return Calendar_Model_Event
820      */
821     protected function _getDailyEvent(Tinebase_DateTime $dtstart)
822     {
823         $event = new Calendar_Model_Event(array(
824             'summary'       => 'Some Daily Event',
825             'dtstart'       => $dtstart->toString(),
826             'dtend'         => $dtstart->addHour(1)->toString(),
827             'rrule'         => 'FREQ=DAILY;INTERVAL=1',
828             'container_id'  => $this->_getTestCalendar()->getId(),
829             'attendee'      => $this->_getAttendee(),
830         ));
831         return $this->_controller->create($event);
832     }
833     
834    /**
835     * @see #5806: thisandfuture range updates with count part fail
836     */
837     public function testCreateRecurExceptionAllFollowingWithCount()
838     {
839         $from = new Tinebase_DateTime('2012-02-20 00:00:00');
840         $until = new Tinebase_DateTime('2012-02-26 23:59:59');
841         
842         $event = new Calendar_Model_Event(array(
843             'uid'           => Tinebase_Record_Abstract::generateUID(),
844             'summary'       => 'Abendessen',
845             'dtstart'       => '2012-02-21 14:00:00',
846             'dtend'         => '2012-02-21 15:30:00',
847             'originator_tz' => 'Europe/Berlin',
848             'rrule'         => 'FREQ=DAILY;COUNT=5',
849             'container_id'  => $this->_getTestCalendar()->getId(),
850         ));
851         
852         $persistentEvent = $this->_controller->create($event);
853         
854         // create exception
855         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
856             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
857         )));
858         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
859         $weekviewEvents[2]->dtstart->subHour(5);
860         $weekviewEvents[2]->dtend->subHour(5);
861         $this->_controller->createRecurException($weekviewEvents[2], FALSE, TRUE);
862         
863         // load events
864         $weekviewEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
865             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
866         )));
867         Calendar_Model_Rrule::mergeRecurrenceSet($weekviewEvents, $from, $until);
868         $weekviewEvents->sort('dtstart', 'ASC');
869         
870         $this->assertEquals(2, count($weekviewEvents->filter('uid', $weekviewEvents[0]->uid)), 'shorten failed');
871         $this->assertEquals(5, count($weekviewEvents), 'wrong total count');
872     }
873
874     public function testCreateRecurExceptionAllFollowingContainerMove()
875     {
876         $this->markTestSkipped('exdate container move not yet forbidden');
877         $exception = $this->testCreateRecurException();
878         $baseEvent = $this->_controller->getRecurBaseEvent($exception);
879
880         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
881         $from = $baseEvent->dtstart;
882         $until = $baseEvent->dtstart->getClone()->addDay(1);
883         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($baseEvent, $exceptions, $from, $until);
884
885         $recurSet->getFirstRecord()->container_id = $this->_getTestContainer('Calendar')->getId();
886         $newSeries = $this->_controller->createRecurException($recurSet->getFirstRecord(), false, true);
887         $newExceptions = $this->_controller->getRecurExceptions($newSeries);
888
889 //        print_r($newSeries->toArray());
890 //        print_r($newExceptions->toArray());
891     }
892
893     /**
894      * testMoveRecurException
895      * 
896      * @see 0008704: moving a recur exception twice creates concurrency exception
897      * - this has been a client problem, server did everything right
898      */
899     public function testMoveRecurException()
900     {
901         $from = new Tinebase_DateTime('2012-02-01 00:00:00');
902         $until = new Tinebase_DateTime('2012-02-29 23:59:59');
903         
904         $persistentEvent1 = $this->_getDailyEvent(new Tinebase_DateTime('2012-02-03 09:00:00'));
905         $persistentEvent2 = $this->_getDailyEvent(new Tinebase_DateTime('2012-02-03 13:00:00'));
906         
907         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
908         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent1, $exceptions, $from, $until);
909         
910         $recurSet[5]->dtstart = new Tinebase_DateTime('2012-02-09 13:00:00');
911         $recurSet[5]->dtend = new Tinebase_DateTime('2012-02-09 14:00:00');
912         
913         $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[5]);
914         
915         $updatedPersistentEvent->dtstart = new Tinebase_DateTime('2012-03-09 13:00:00');
916         $updatedPersistentEvent->dtend = new Tinebase_DateTime('2012-03-09 14:00:00');
917         
918         $this->setExpectedException('Tinebase_Timemachine_Exception_ConcurrencyConflict');
919         $updatedPersistentEvent = $this->_controller->createRecurException($updatedPersistentEvent);
920     }
921
922     public function testExdateContainerMoveCreateException()
923     {
924         $this->markTestSkipped('exdate container move not yet forbidden');
925         $event = $this->_getDailyEvent(new Tinebase_DateTime('2014-02-03 09:00:00'));
926
927         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
928         
929         $from = new Tinebase_DateTime('2014-02-01 00:00:00');
930         $until = new Tinebase_DateTime('2014-02-29 23:59:59');
931         
932         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $from, $until);
933
934         $this->setExpectedException('Calendar_Exception_ExdateContainer');
935
936         $recurSet[2]->container_id = $this->_getTestContainer('Calendar')->getId();
937         $this->_controller->createRecurException($recurSet[2]);
938     }
939
940     public function testExdateContainerMoveUpdateException()
941     {
942         $this->markTestSkipped('exdate container move not yet forbidden');
943         $event = $this->_getDailyEvent(new Tinebase_DateTime('2014-02-03 09:00:00'));
944
945         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
946
947         $from = new Tinebase_DateTime('2014-02-01 00:00:00');
948         $until = new Tinebase_DateTime('2014-02-29 23:59:59');
949
950         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($event, $exceptions, $from, $until);
951
952         $recurSet[2]->summary = 'exdate';
953
954         $updatedPersistentEvent = $this->_controller->createRecurException($recurSet[2]);
955
956         $this->setExpectedException('Calendar_Exception_ExdateContainer');
957
958         $updatedPersistentEvent->container_id = $this->_getTestContainer('Calendar')->getId();
959         $this->_controller->update($updatedPersistentEvent);
960
961     }
962
963     /**
964      * test get free busy info with recurring event and dst
965      *
966      * @see 0009558: sometimes free/busy conflicts are not detected
967      */
968     public function testFreeBusyWithRecurSeriesAndRessourceInDST()
969     {
970         $event = $this->_getEvent();
971         $resource = Calendar_Controller_Resource::getInstance()->create($this->_getResource());
972         $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(array(
973             'user_id'   => $resource->getId(),
974             'user_type' => Calendar_Model_Attender::USERTYPE_RESOURCE,
975         )));
976         $event->dtstart = new Tinebase_DateTime('2013-10-14 10:30:00'); // this is UTC
977         $event->dtend = new Tinebase_DateTime('2013-10-14 11:45:00');
978         $event->rrule = 'FREQ=WEEKLY;INTERVAL=1;WKST=SU;BYDAY=MO';
979         $persistentEvent = Calendar_Controller_Event::getInstance()->create($event);
980
981         // check free busy in DST
982         $newEvent = $this->_getEvent();
983         $newEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(array(
984             'user_id'   => $resource->getId(),
985             'user_type' => Calendar_Model_Attender::USERTYPE_RESOURCE,
986         )));
987         $newEvent->dtstart = new Tinebase_DateTime('2014-01-20 12:30:00');
988         $newEvent->dtend = new Tinebase_DateTime('2014-01-20 13:30:00');
989
990         $this->setExpectedException('Calendar_Exception_AttendeeBusy');
991         $savedEvent = Calendar_Controller_Event::getInstance()->create($newEvent, /* $checkBusyConflicts = */ true);
992     }
993
994     /**
995      * returns a simple recure event
996      *
997      * @return Calendar_Model_Event
998      */
999     protected function _getRecurEvent()
1000     {
1001         return new Calendar_Model_Event(array(
1002             'summary'     => 'Breakfast',
1003             'dtstart'     => '2010-05-20 06:00:00',
1004             'dtend'       => '2010-05-20 06:15:00',
1005             'description' => 'Breakfast',
1006             'rrule'       => 'FREQ=DAILY;INTERVAL=1',    
1007             'container_id' => $this->_getTestCalendar()->getId(),
1008             Tinebase_Model_Grants::GRANT_EDIT    => true,
1009         ));
1010     }
1011 }