0004934: Ical subscription support: new container
[tine20] / tests / tine20 / Calendar / Controller / EventTests.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) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Cornelius Weiss <c.weiss@metaways.de>
9  */
10
11 /**
12  * Test helper
13  */
14 require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
15
16 /**
17  * Test class for Calendar_Controller_Event
18  * 
19  * @package     Calendar
20  */
21 class Calendar_Controller_EventTests extends Calendar_TestCase
22 {
23     /**
24      * @var Calendar_Controller_Event controller unter test
25      */
26     protected $_controller;
27     
28     /**
29      * @var Tinebase_Model_Container
30      */
31     protected $_testCalendar;
32     
33     /**
34      * (non-PHPdoc)
35      * @see Calendar_TestCase::setUp()
36      */
37     public function setUp()
38     {
39         parent::setUp();
40         $this->_controller = Calendar_Controller_Event::getInstance();
41
42     }
43     
44     /**
45      * testCreateEvent
46      * 
47      * @return Calendar_Model_Event
48      */
49     public function testCreateEvent()
50     {
51         $event = $this->_getEvent();
52         $persistentEvent = $this->_controller->create($event);
53         
54         $this->assertEquals($event->description, $persistentEvent->description);
55         $this->assertTrue($event->dtstart->equals($persistentEvent->dtstart));
56         $this->assertEquals(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE), $persistentEvent->originator_tz);
57         
58         return $persistentEvent;
59     }
60     
61     /**
62      * testCreateAlldayEventWithoutDtend
63      */
64     public function testCreateAlldayEventWithoutDtend()
65     {
66         $event = $this->_getEvent();
67         $event->is_all_day_event = true;
68         $event->dtend = null;
69         
70         $persistentEvent = $this->_controller->create($event);
71         $persistentEvent->setTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
72         $this->assertEquals('2009-04-06 23:59:59', $persistentEvent->dtend->toString());
73     }
74     
75     /**
76      * testGetEvent
77      */
78     public function testGetEvent()
79     {
80         $persistentEvent = $this->testCreateEvent();
81         $this->assertTrue((bool) $persistentEvent->{Tinebase_Model_Grants::GRANT_READ});
82         $this->assertTrue((bool) $persistentEvent->{Tinebase_Model_Grants::GRANT_EDIT});
83         $this->assertTrue((bool) $persistentEvent->{Tinebase_Model_Grants::GRANT_DELETE});
84         
85         $loadedEvent = $this->_controller->get($persistentEvent->getId());
86         $this->assertTrue((bool) $loadedEvent->{Tinebase_Model_Grants::GRANT_READ});
87         $this->assertTrue((bool) $loadedEvent->{Tinebase_Model_Grants::GRANT_EDIT});
88         $this->assertTrue((bool) $loadedEvent->{Tinebase_Model_Grants::GRANT_DELETE});
89     }
90     
91     /**
92      * testUpdateEvent
93      */
94     public function testUpdateEvent()
95     {
96         $persistentEvent = $this->testCreateEvent();
97         
98         $currentTz = Tinebase_Core::get(Tinebase_Core::USERTIMEZONE);
99         Tinebase_Core::set(Tinebase_Core::USERTIMEZONE, 'farfaraway');
100         
101         $persistentEvent->summary = 'Lunchtime';
102         $updatedEvent = $this->_controller->update($persistentEvent);
103         $this->assertEquals($persistentEvent->summary, $updatedEvent->summary);
104         $this->assertEquals($currentTz, $updatedEvent->originator_tz, 'originator_tz must not be touchet if dtsart is not updatet!');
105         
106         $updatedEvent->dtstart->addHour(1);
107         $updatedEvent->dtend->addHour(1);
108         $secondUpdatedEvent = $this->_controller->update($updatedEvent);
109         $this->assertEquals(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE), $secondUpdatedEvent->originator_tz, 'originator_tz must be adopted if dtsart is updatet!');
110     
111         Tinebase_Core::set(Tinebase_Core::USERTIMEZONE, $currentTz);
112     }
113
114     /**
115      * testUpdateEventWithRruleAndRecurId
116      * 
117      * @see 0008696: do not allow both rrule and recurId in event
118      */
119     public function testUpdateEventWithRruleAndRecurId()
120     {
121         $persistentRecurEvent = $this->testCreateRecurException();
122         $persistentRecurEvent->rrule = 'FREQ=DAILY;INTERVAL=1';
123         
124         $updatedEvent = $this->_controller->update($persistentRecurEvent);
125         
126         $this->assertEquals(NULL, $updatedEvent->rrule);
127     }
128     
129     public function testConcurrentUpdate()
130     {
131         $event = $this->testCreateEvent();
132         
133         sleep(1);
134         $resolvableConcurrentEvent1 = clone $event;
135         $resolvableConcurrentEvent1->dtstart = $resolvableConcurrentEvent1->dtstart->addMonth(1);
136         $resolvableConcurrentEvent1->dtend = $resolvableConcurrentEvent1->dtend->addMonth(1);
137         $resolvableConcurrentEvent1Update = $this->_controller->update($resolvableConcurrentEvent1);
138         
139         sleep(1);
140         $resolvableConcurrentEvent2 = clone $event;
141         $resolvableConcurrentEvent2->summary = 'Updated Event';
142         $resolvableConcurrentEvent2Update = $this->_controller->update($resolvableConcurrentEvent2);
143         
144         $this->assertEquals($resolvableConcurrentEvent1Update->dtstart, $resolvableConcurrentEvent2Update->dtstart);
145     }
146     
147     public function testUpdateAttendeeStatus()
148     {
149         $event = $this->_getEvent();
150         $event->attendee = $this->_getAttendee();
151         $event->attendee[1] = new Calendar_Model_Attender(array(
152             'user_type' => Calendar_Model_Attender::USERTYPE_USER,
153             'user_id'   => $this->_personasContacts['pwulf']->getId(),
154         ));
155         
156         $persistentEvent = $this->_controller->create($event);
157         
158         foreach ($persistentEvent->attendee as $attender) {
159             $attender->status = Calendar_Model_Attender::STATUS_DECLINED;
160             $this->_controller->attenderStatusUpdate($persistentEvent, $attender, $attender->status_authkey);
161         }
162         
163         
164         $persistentEvent->last_modified_time = $this->_controller->get($persistentEvent->getId())->last_modified_time;
165         
166         // update time
167         $persistentEvent->dtstart->addHour(2);
168         $persistentEvent->dtend->addHour(2);
169         // NOTE: in normal operations the status authkey is removed by resolveAttendee
170         //       we simulate this here by removeing the keys per hand. (also note that current user does not need an authkey)
171         $persistentEvent->attendee->status_authkey = null;
172         $updatedEvent = $this->_controller->update($persistentEvent);
173
174         $currentUser = $updatedEvent->attendee
175             ->filter('user_type', Calendar_Model_Attender::USERTYPE_USER)
176             ->filter('user_id', Tinebase_Core::getUser()->contact_id)
177             ->getFirstRecord();
178             
179         $pwulf = $updatedEvent->attendee
180             ->filter('user_type', Calendar_Model_Attender::USERTYPE_USER)
181             ->filter('user_id', $this->_personasContacts['pwulf']->getId())
182             ->getFirstRecord();
183
184         $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $currentUser->status, 'current users status must not be touched');
185         $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, $pwulf->status, 'pwulfs status must be reset');
186     }
187     
188     public function testUpdateMultiple()
189     {
190         $persistentEvent = $this->testCreateEvent();
191         
192         $filter = new Calendar_Model_EventFilter(array(
193             array('field' => 'id', 'operator' => 'in', 'value' => array($persistentEvent->getId()))
194         ));
195         
196         $data = array(
197             'summary' => 'multipleTest'
198         );
199         
200         $this->_controller->updateMultiple($filter, $data);
201         
202         $updatedEvent = $this->_controller->get($persistentEvent->getId());
203         $this->assertEquals('multipleTest', $updatedEvent->summary);
204     }
205     
206     public function testAttendeeBasics()
207     {
208         $event = $this->_getEvent();
209         $event->attendee = $this->_getAttendee();
210         $event->attendee[1] = new Calendar_Model_Attender(array(
211             'user_type' => Calendar_Model_Attender::USERTYPE_USER,
212             'user_id'   => $this->_personasContacts['pwulf']->getId()
213         ));
214         
215         $persistendEvent = $this->_controller->create($event);
216         $this->assertEquals(2, count($persistendEvent->attendee));
217         
218         unset($persistendEvent->attendee[0]);
219         $updatedEvent = $this->_controller->update($persistendEvent);
220         $this->assertEquals(1, count($updatedEvent->attendee));
221         
222         $updatedEvent->attendee->getFirstRecord()->role = Calendar_Model_Attender::ROLE_OPTIONAL;
223         $updatedEvent->attendee->getFirstRecord()->transp = Calendar_Model_Event::TRANSP_TRANSP;
224         
225         $secondUpdatedEvent = $this->_controller->update($updatedEvent);
226         $this->assertEquals(1, count($secondUpdatedEvent->attendee));
227         $this->assertEquals(Calendar_Model_Attender::ROLE_OPTIONAL, $secondUpdatedEvent->attendee->getFirstRecord()->role);
228         $this->assertEquals(Calendar_Model_Event::TRANSP_TRANSP, $secondUpdatedEvent->attendee->getFirstRecord()->transp);
229     }
230     
231     public function testAttendeeFilter()
232     {
233         $event1 = $this->_getEvent();
234         $event1->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
235             array('user_id' => Tinebase_Core::getUser()->contact_id),
236             array('user_id' => $this->_personasContacts['pwulf']->getId())
237         ));
238         $persistentEvent1 = $this->_controller->create($event1);
239         
240         $event2 = $this->_getEvent();
241         $event2->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
242             array('user_id' => Tinebase_Core::getUser()->contact_id),
243             array('user_id' => $this->_personasContacts['sclever']->getId()),
244         ));
245         $persistentEvent2 = $this->_controller->create($event2);
246         
247         $event3 = $this->_getEvent();
248         $event3->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
249             array('user_id' => Tinebase_Core::getUser()->contact_id),
250             array('user_id' => $this->_personasContacts['sclever']->getId()),
251         ));
252         $persistentEvent3 = $this->_controller->create($event3);
253         
254         // test sclever
255         $filter = new Calendar_Model_EventFilter(array(
256             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId()),
257             array('field' => 'attender'    , 'operator' => 'equals', 'value' => array(
258                 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
259                 'user_id'   => $this->_personasContacts['sclever']->getId()
260             )),
261         ));
262         $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
263         $this->assertEquals(2, count($eventsFound), 'sclever attends to two events');
264         
265         // test pwulf
266         $filter = new Calendar_Model_EventFilter(array(
267             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId()),
268             array('field' => 'attender'    , 'operator' => 'equals', 'value' => array(
269                 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
270                 'user_id'   => $this->_personasContacts['pwulf']->getId()
271             )),
272         ));
273         $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
274         $this->assertEquals(1, count($eventsFound), 'pwulf attends to one events');
275         
276         // test sclever OR pwulf
277         $filter = new Calendar_Model_EventFilter(array(
278             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId()),
279             array('field' => 'attender'    , 'operator' => 'in',     'value' => array(
280                 array(
281                     'user_type' => Calendar_Model_Attender::USERTYPE_USER,
282                     'user_id'   => $this->_personasContacts['sclever']->getId()
283                 ),
284                 array (
285                     'user_type' => Calendar_Model_Attender::USERTYPE_USER,
286                     'user_id'   => $this->_personasContacts['pwulf']->getId()
287                 )
288             )),
289         ));
290         $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
291         $this->assertEquals(3, count($eventsFound), 'sclever OR pwulf attends to tree events');
292     }
293     
294     public function testAttendeeGroupFilter()
295     {
296         $event = $this->_getEvent();
297         $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
298             array('user_id' => Tinebase_Core::getUser()->contact_id),
299             array('user_id' => $this->_personasContacts['sclever']->getId())
300         ));
301         $persistentEvent = $this->_controller->create($event);
302         
303         $filter = new Calendar_Model_EventFilter(array(
304             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId()),
305             array('field' => 'attender'    , 'operator' => 'in',     'value' => array(
306                 array(
307                     'user_type' => Calendar_Model_AttenderFilter::USERTYPE_MEMBEROF,
308                     'user_id'   => $this->_personas['sclever']->accountPrimaryGroup
309                 )
310             )),
311         ));
312         $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
313         $this->assertEquals(1, count($eventsFound), 'sclever is groupmember');
314     }
315         
316     public function testGetFreeBusyInfo()
317     {
318         $event = $this->_getEvent();
319         $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
320             array('user_id' => $this->_personasContacts['sclever']->getId()),
321             array('user_id' => $this->_personasContacts['pwulf']->getId())
322         ));
323         $persistentEvent = $this->_controller->create($event);
324         
325         $fbinfo = $this->_controller->getFreeBusyInfo(array(array('from' => $persistentEvent->dtstart, 'until' => $persistentEvent->dtend)), $persistentEvent->attendee);
326        
327         $this->assertGreaterThanOrEqual(2, count($fbinfo));
328         
329         return $persistentEvent;
330     }
331     
332     public function testSearchFreeTime() {
333         $persistentEvent = $this->testGetFreeBusyInfo();
334         
335         $this->_controller->searchFreeTime($persistentEvent->dtstart->setHour(6), $persistentEvent->dtend->setHour(22), $persistentEvent->attendee);
336     }
337     
338     /**
339      * events from deleted calendars should not be shown
340      */
341     public function testSearchEventFromDeletedCalendar() {
342         $testCal = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
343             'name'           => 'PHPUnit test calendar',
344             'type'           => Tinebase_Model_Container::TYPE_PERSONAL,
345             'owner_id'       => Tinebase_Core::getUser(),
346             'backend'        => $this->_backend->getType(),
347             'application_id' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId()
348         ), true));
349         
350         $this->_testCalendars->addRecord($testCal);
351         
352         // create event in testcal
353         $event = $this->_getEvent();
354         $event->container_id = $testCal->getId();
355         $event->attendee = $this->_getAttendee();
356         $persistentEvent = $this->_controller->create($event);
357
358         // delete testcal
359         Tinebase_Container::getInstance()->deleteContainer($testCal, TRUE);
360         
361         // search by attendee
362         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
363             array('field' => 'attender', 'operator' => 'equals', 'value' => array(
364                 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
365                 'user_id'   => $this->_testUserContact->getId()
366             ))
367         )), NULL, FALSE, FALSE);
368         
369         $this->assertFalse(in_array($persistentEvent->getId(), $events->getId()), 'event in deleted (display) container shuld not be found');
370     }
371     
372     public function testCreateEventWithConfict()
373     {
374         $this->_testNeedsTransaction();
375         
376         $event = $this->_getEvent();
377         $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
378             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['sclever']->getId()),
379             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['pwulf']->getId())
380         ));
381         $persistentEvent = $this->_controller->create($event);
382         
383         $conflictEvent = $this->_getEvent();
384         $conflictEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
385             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['sclever']->getId()),
386             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['pwulf']->getId())
387         ));
388         
389         try {
390             $exectionRaised = FALSE;
391             $this->_controller->create($conflictEvent, TRUE);
392         } catch (Calendar_Exception_AttendeeBusy $busyException) {
393             $fbData = $busyException->toArray();
394             $this->assertGreaterThanOrEqual(2, count($fbData['freebusyinfo']));
395             $exectionRaised = TRUE;
396         }
397         if (! $exectionRaised) {
398             $this->fail('An expected exception has not been raised.');
399         }
400         $persitentConflictEvent = $this->_controller->create($conflictEvent, FALSE);
401         
402         return $persitentConflictEvent;
403     }
404     
405     public function testCreateEventWithConfictFromGroupMember()
406     {
407         $event = $this->_getEvent();
408         $event->attendee = $this->_getAttendee();
409         $persistentEvent = $this->_controller->create($event);
410         
411         $conflictEvent = $this->_getEvent();
412         $conflictEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
413             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['sclever']->getId()),
414             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['pwulf']->getId())
415         ));
416         
417         try {
418             $this->_controller->create($conflictEvent, TRUE);
419             $this->assertTrue(false, 'Failed to detect conflict from groupmember');
420         } catch (Calendar_Exception_AttendeeBusy $busyException) {
421             $fbData = $busyException->toArray();
422             $this->assertGreaterThanOrEqual(2, count($fbData['freebusyinfo']));
423             return;
424         }
425         
426         $this->fail('An expected exception has not been raised.');
427     }
428     
429     public function testCreateTransparentEventNoConflict()
430     {
431         $event = $this->_getEvent();
432         $event->attendee = $this->_getAttendee();
433         $persistentEvent = $this->_controller->create($event);
434         
435         $nonConflictEvent = $this->_getEvent();
436         $nonConflictEvent->transp = Calendar_Model_Event::TRANSP_TRANSP;
437         $nonConflictEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
438             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['sclever']->getId()),
439             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['pwulf']->getId())
440         ));
441         
442         $this->_controller->create($nonConflictEvent, TRUE);
443     }
444     
445     public function testCreateNoConflictParallelTrasparentEvent()
446     {
447         $event = $this->_getEvent();
448         $event->attendee = $this->_getAttendee();
449         $event->transp = Calendar_Model_Event::TRANSP_TRANSP;
450         $persistentEvent = $this->_controller->create($event);
451         
452         $nonConflictEvent = $this->_getEvent();
453         $nonConflictEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
454             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['sclever']->getId()),
455             array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_personasContacts['pwulf']->getId())
456         ));
457         
458         $this->_controller->create($nonConflictEvent, TRUE);
459     }
460     
461     public function testCreateNoConflictParallelAtendeeTrasparentEvent()
462     {
463         $event = $this->_getEvent();
464         $event->attendee = $this->_getAttendee();
465         unset ($event->attendee[1]); // no group here
466         $event->attendee->transp = Calendar_Model_Event::TRANSP_TRANSP;
467         $persistentEvent = $this->_controller->create($event);
468         
469         $nonConflictEvent = $this->_getEvent();
470         $nonConflictEvent->attendee = $this->_getAttendee();
471         
472         $this->_controller->create($nonConflictEvent, TRUE);
473     }
474     
475     public function testUpdateWithConflictNoTimechange()
476     {
477         $persitentConflictEvent = $this->testCreateEventWithConfict();
478         $persitentConflictEvent->summary = 'only time updates should recheck free/busy';
479         
480         $this->_controller->update($persitentConflictEvent, TRUE);
481     }
482     
483     public function testUpdateWithConflictAttendeeChange()
484     {
485         $persitentConflictEvent = $this->testCreateEventWithConfict();
486         $persitentConflictEvent->summary = 'attendee adds should recheck free/busy';
487         
488         $defaultUserGroup = Tinebase_Group::getInstance()->getDefaultGroup();
489         $persitentConflictEvent->attendee->addRecord(new Calendar_Model_Attender(array(
490             'user_id'   => $defaultUserGroup->getId(),
491             'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
492             'role'      => Calendar_Model_Attender::ROLE_REQUIRED
493         )));
494         
495         $this->setExpectedException('Calendar_Exception_AttendeeBusy');
496         $this->_controller->update($persitentConflictEvent, TRUE);
497     }
498     
499     public function testUpdateWithConflictWithTimechange()
500     {
501         $persitentConflictEvent = $this->testCreateEventWithConfict();
502         $persitentConflictEvent->summary = 'time updates should recheck free/busy';
503         $persitentConflictEvent->dtend->addHour(1);
504         
505         $this->setExpectedException('Calendar_Exception_AttendeeBusy');
506         $this->_controller->update($persitentConflictEvent, TRUE);
507     }
508     
509     public function testAttendeeAuthKeyPreserv()
510     {
511         $event = $this->_getEvent();
512         $event->attendee = $this->_getAttendee();
513         
514         $persistendEvent = $this->_controller->create($event);
515         $newAuthKey = Tinebase_Record_Abstract::generateUID();
516         $persistendEvent->attendee->status_authkey = $newAuthKey;
517         
518         $updatedEvent = $this->_controller->update($persistendEvent);
519         foreach ($updatedEvent->attendee as $attender) {
520             $this->assertNotEquals($newAuthKey, $attender->status_authkey);
521         }
522     }
523     
524     public function testAttendeeStatusPreservViaSave()
525     {
526         $event = $this->_getEvent();
527         $event->attendee = $this->_getAttendee();
528         $event->attendee[0]->user_id = Tinebase_User::getInstance()->getUserByLoginName('sclever')->contact_id;
529         $event->attendee[0]->status = Calendar_Model_Attender::STATUS_ACCEPTED;
530         unset($event->attendee[1]);
531         
532         $persistendEvent = $this->_controller->create($event);
533         $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, $persistendEvent->attendee[0]->status, 'creation of other attedee must not set status');
534         
535         $persistendEvent->attendee[0]->status = Calendar_Model_Attender::STATUS_ACCEPTED;
536         $persistendEvent->attendee[0]->status_authkey = NULL;
537         $updatedEvent = $this->_controller->update($persistendEvent);
538         $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, $updatedEvent->attendee[0]->status, 'updateing of other attedee must not set status');
539     }
540     
541     public function testAttendeeSetStatus()
542     {
543         $event = $this->_getEvent();
544         $event->attendee = $this->_getAttendee();
545         unset($event->attendee[1]);
546         
547         $persistendEvent = $this->_controller->create($event);
548         $attendee = $persistendEvent->attendee[0];
549         
550         $attendee->status = Calendar_Model_Attender::STATUS_DECLINED;
551         $this->_controller->attenderStatusUpdate($persistendEvent, $attendee, $attendee->status_authkey);
552         
553         $loadedEvent = $this->_controller->get($persistendEvent->getId());
554         $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $loadedEvent->attendee[0]->status, 'status not set');
555         
556     }
557     
558     public function testAttendeeStatusFilter()
559     {
560         $event = $this->_getEvent();
561         $event->attendee = $this->_getAttendee();
562         unset($event->attendee[1]);
563         
564         $persistentEvent = $this->_controller->create($event);
565         
566         $filter = new Calendar_Model_EventFilter(array(
567             array('field' => 'uid',             'operator' => 'equals', 'value' => $persistentEvent->uid),
568             array('field' => 'attender_status', 'operator' => 'not',    'value' => Calendar_Model_Attender::STATUS_DECLINED),
569         ));
570         
571         $events = $this->_controller->search($filter);
572         $this->assertEquals(1, count($events), 'event should be found, but is not');
573         
574         $attender = $persistentEvent->attendee[0];
575         $attender->status = Calendar_Model_Attender::STATUS_DECLINED;
576         $updatedPersistentEvent = $this->_controller->update($persistentEvent);
577         
578         $events = $this->_controller->search($filter);
579         $this->assertEquals(0, count($events), 'event should _not_ be found, but is');
580         
581     }
582     
583     public function testAttendeeDisplaycontainerContact()
584     {
585         $contact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array(
586            'n_given'  => 'phpunit',
587            'n_family' => 'cal attender'
588         )));
589          
590         $event = $this->_getEvent();
591         $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
592             array(
593                 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
594                 'user_id'   => $contact->getId(),
595                 'role'      => Calendar_Model_Attender::ROLE_REQUIRED
596             ),
597         ));
598         $persistentEvent = $this->_controller->create($event);
599         $attender = $persistentEvent->attendee[0];
600         
601         $this->assertTrue(empty($attender->displaycontainer_id), 'displaycontainer_id must not be set for contacts');
602     }
603     
604     public function testAttendeeGroupMembers()
605     {
606         $defaultUserGroup = Tinebase_Group::getInstance()->getDefaultGroup();
607         $defaultAdminGroup = Tinebase_Group::getInstance()->getDefaultAdminGroup();
608         
609         $event = $this->_getEvent();
610         $event->attendee = $this->_getAttendee();
611         $event->attendee[1] = new Calendar_Model_Attender(array(
612             'user_id'   => $defaultUserGroup->getId(),
613             'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
614             'role'      => Calendar_Model_Attender::ROLE_REQUIRED
615         ));
616         
617         $persistentEvent = $this->_controller->create($event);
618         $defaultUserGroupMembers = Tinebase_Group::getInstance()->getGroupMembers($defaultUserGroup->getId());
619         // user as attender + group + all members - supressed user 
620         $this->assertEquals(1 + 1 + count($defaultUserGroupMembers) -1, count($persistentEvent->attendee));
621         
622         $groupAttender = $persistentEvent->attendee->find('user_type', Calendar_Model_Attender::USERTYPE_GROUP);
623         $persistentEvent->attendee->removeRecord($groupAttender);
624         
625         $updatedPersistentEvent = $this->_controller->update($persistentEvent);
626         $this->assertEquals(1, count($updatedPersistentEvent->attendee));
627     }
628     
629     public function testAttendeeGroupMembersChange()
630     {
631         $defaultAdminGroup = Tinebase_Group::getInstance()->getDefaultAdminGroup();
632         
633         // create event and invite admin group
634         $event = $this->_getEvent();
635         
636         // only events in future will be changed!
637         $event->dtstart = Tinebase_DateTime::now()->addHour(1);
638         $event->dtend = Tinebase_DateTime::now()->addHour(2);
639         
640         $event->attendee = $this->_getAttendee();
641         $event->attendee[1] = new Calendar_Model_Attender(array(
642             'user_id'   => $defaultAdminGroup->getId(),
643             'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
644             'role'      => Calendar_Model_Attender::ROLE_REQUIRED
645         ));
646         $persistentEvent = $this->_controller->create($event);
647         
648         // assert test condition
649         $pwulf = $persistentEvent->attendee
650             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
651             ->filter('user_id', $this->_personasContacts['pwulf']->getId());
652         $this->assertEquals(0, count($pwulf), 'invalid test condition, pwulf should not be member or admin group');
653         
654         Admin_Controller_Group::getInstance()->addGroupMember($defaultAdminGroup->getId(), $this->_personasContacts['pwulf']->account_id);
655         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
656             Tinebase_ActionQueue::getInstance()->processQueue(10000);
657         }
658         
659         $loadedEvent = $this->_controller->get($persistentEvent->getId());
660         // assert pwulf is in
661         $pwulf = $loadedEvent->attendee
662             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
663             ->filter('user_id', $this->_personasContacts['pwulf']->getId());
664         $this->assertEquals(1, count($pwulf), 'pwulf is not attender of event, but should be');
665         
666         
667         Admin_Controller_Group::getInstance()->removeGroupMember($defaultAdminGroup->getId(), $this->_personasContacts['pwulf']->account_id);
668         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
669             Tinebase_ActionQueue::getInstance()->processQueue(10000);
670         }
671         
672         $loadedEvent = $this->_controller->get($persistentEvent->getId());
673         // assert pwulf is missing
674         $pwulf = $loadedEvent->attendee
675             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
676             ->filter('user_id', $this->_personasContacts['pwulf']->getId());
677         $this->assertEquals(0, count($pwulf), 'pwulf is attender of event, but not should be');
678         
679         // Test the same with update
680         $group = Admin_Controller_Group::getInstance()->get($defaultAdminGroup->getId());
681         $group->members = array_merge(Admin_Controller_Group::getInstance()->getGroupMembers($defaultAdminGroup->getId()), array(array_value('pwulf', Zend_Registry::get('personas'))->getId()));
682         Admin_Controller_Group::getInstance()->update($group);
683         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
684             Tinebase_ActionQueue::getInstance()->processQueue(10000);
685         }
686         
687         // assert pwulf is in
688         $loadedEvent = $this->_controller->get($persistentEvent->getId());
689         $pwulf = $loadedEvent->attendee
690             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
691             ->filter('user_id', $this->_personasContacts['pwulf']->getId());
692         $this->assertEquals(1, count($pwulf), 'pwulf is not attender of event, but should be (via update)');
693         
694         $group->members = array_diff(Admin_Controller_Group::getInstance()->getGroupMembers($defaultAdminGroup->getId()), array(array_value('pwulf', Zend_Registry::get('personas'))->getId()));
695         Admin_Controller_Group::getInstance()->update($group);
696         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
697             Tinebase_ActionQueue::getInstance()->processQueue(10000);
698         }
699         // assert pwulf is missing
700         $loadedEvent = $this->_controller->get($persistentEvent->getId());
701         $pwulf = $loadedEvent->attendee
702             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
703             ->filter('user_id', $this->_personasContacts['pwulf']->getId());
704         $this->assertEquals(0, count($pwulf), 'pwulf is attender of event, but not should be');
705     }
706     
707     public function testAttendeeGroupMembersAddUser()
708     {
709         try {
710             // clenup if exists
711             $cleanupUser = Tinebase_User::getInstance()->getFullUserByLoginName('testAttendeeGroupMembersAddUser');
712             Tinebase_User::getInstance()->deleteUser($cleanupUser);
713         } catch (Exception $e) {
714             // do nothing
715         }
716         
717         
718         $defaultGroup = Tinebase_Group::getInstance()->getDefaultGroup();
719         
720         // create event and invite admin group
721         $event = $this->_getEvent();
722         
723         // only events in future will be changed!
724         $event->dtstart = Tinebase_DateTime::now()->addHour(1);
725         $event->dtend = Tinebase_DateTime::now()->addHour(2);
726         
727         $event->attendee = $this->_getAttendee();
728         $event->attendee[1] = new Calendar_Model_Attender(array(
729             'user_id'   => $defaultGroup->getId(),
730             'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
731             'role'      => Calendar_Model_Attender::ROLE_REQUIRED
732         ));
733         $persistentEvent = $this->_controller->create($event);
734         
735         // create a new user
736         $newUser = Admin_Controller_User::getInstance()->create(new Tinebase_Model_FullUser(array(
737 //            'accountId'             => 'dflkjgldfgdfgd',
738             'accountLoginName'      => 'testAttendeeGroupMembersAddUser',
739             'accountStatus'         => 'enabled',
740             'accountExpires'        => NULL,
741             'accountPrimaryGroup'   => $defaultGroup->getId(),
742             'accountLastName'       => 'Tine 2.0',
743             'accountFirstName'      => 'PHPUnit',
744             'accountEmailAddress'   => 'phpunit@metaways.de'
745         )), Zend_Registry::get('testConfig')->password, Zend_Registry::get('testConfig')->password);
746         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
747             Tinebase_ActionQueue::getInstance()->processQueue(10000);
748         }
749         
750         // check if this user was added to event
751         $loadedEvent = $this->_controller->get($persistentEvent->getId());
752         $user = $loadedEvent->attendee
753             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
754             ->filter('user_id', $newUser->contact_id);
755         $this->assertEquals(1, count($user), 'added user is not attender of event, but should be. user: ' . print_r($newUser->toArray(), TRUE));
756         
757         // cleanup user
758         Admin_Controller_User::getInstance()->delete($newUser->getId());
759         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
760             Tinebase_ActionQueue::getInstance()->processQueue(10000);
761         }
762         
763         // check if user was removed from event
764         $loadedEvent = $this->_controller->get($persistentEvent->getId());
765         $user = $loadedEvent->attendee
766             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
767             ->filter('user_id', $newUser->contact_id);
768         $this->assertEquals(0, count($user), 'added user is attender of event, but should be (after deleting user)');
769     }
770     
771     /**
772      * testAttendeeGroupMembersRecurringAddUser
773      * 
774      * FIXME 0007352: fix Calendar_Controller_EventTests::testAttendeeGroupMembersRecurringAddUser
775      */
776     public function testAttendeeGroupMembersRecurringAddUser()
777     {
778         $this->markTestIncomplete('test fails sometimes / needs fixing');
779         
780         try {
781             // cleanup if exists
782             $cleanupUser = Tinebase_User::getInstance()->getFullUserByLoginName('testAttendeeGroupMembersAddUser');
783             Tinebase_User::getInstance()->deleteUser($cleanupUser);
784         } catch (Exception $e) {
785             // do nothing
786         }
787         
788         $defaultGroup = Tinebase_Group::getInstance()->getDefaultGroup();
789         
790         // create event and invite admin group
791         $event = $this->_getEvent();
792         $event->rrule = 'FREQ=DAILY;INTERVAL=1';
793         
794         $event->attendee = $this->_getAttendee();
795         $event->attendee[1] = new Calendar_Model_Attender(array(
796             'user_id'   => $defaultGroup->getId(),
797             'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
798             'role'      => Calendar_Model_Attender::ROLE_REQUIRED
799         ));
800         $persistentEvent = $this->_controller->create($event);
801         
802         // create a new user
803         $newUser = Admin_Controller_User::getInstance()->create(new Tinebase_Model_FullUser(array(
804             'accountLoginName'      => 'testAttendeeGroupMembersAddUser',
805             'accountStatus'         => 'enabled',
806             'accountExpires'        => NULL,
807             'accountPrimaryGroup'   => $defaultGroup->getId(),
808             'accountLastName'       => 'Tine 2.0',
809             'accountFirstName'      => 'PHPUnit',
810             'accountEmailAddress'   => 'phpunit@metaways.de'
811         )), Zend_Registry::get('testConfig')->password, Zend_Registry::get('testConfig')->password);
812         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
813             Tinebase_ActionQueue::getInstance()->processQueue(10000);
814         }
815         
816         $events = $this->_backend->search(new Calendar_Model_EventFilter(array(
817             array('field' => 'container_id', 'operator' => 'in', 'value' => $this->_testCalendars->getId()),
818         )), new Tinebase_Model_Pagination(array()));
819         
820         $oldSeries = $events->filter('rrule_until', '/.+/', TRUE)->getFirstRecord();
821         $newSeries = $events->filter('rrule_until', '/^$/', TRUE)->getFirstRecord();
822         
823         $this->assertEquals(2, $events->count(), 'recur event must be splitted '. print_r($events->toArray(), TRUE));
824         // check if this user was added to event
825         $loadedEvent = $this->_controller->get($persistentEvent->getId());
826         $user = $oldSeries->attendee
827             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
828             ->filter('user_id', $newUser->contact_id);
829         $this->assertEquals(0, count($user), 'added user is attender of old event, but should not be');
830         $user = $newSeries->attendee
831             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
832             ->filter('user_id', $newUser->contact_id);
833         $this->assertEquals(1, count($user), 'added user is not attender of new event, but should be');
834         
835         // cleanup user
836         Admin_Controller_User::getInstance()->delete($newUser->getId());
837         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
838             Tinebase_ActionQueue::getInstance()->processQueue(10000);
839         }
840         
841         $events = $this->_backend->search(new Calendar_Model_EventFilter(array(
842             array('field' => 'container_id', 'operator' => 'in', 'value' => $this->_testCalendars->getId()),
843         )), new Tinebase_Model_Pagination(array()));
844         
845         $newSeries = $events->filter('rrule_until', '/^$/', TRUE)->getFirstRecord();
846         
847         // check if this user was deleted from event
848         $user = $newSeries->attendee
849             ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
850             ->filter('user_id', $newUser->contact_id);
851         $this->assertEquals(0, count($user), 'deleted user is attender of new event, but should not be');
852     }
853     
854     public function testRruleUntil()
855     {
856         $event = $this->_getEvent();
857         
858         $event->rrule_until = Tinebase_DateTime::now();
859         $persistentEvent = $this->_controller->create($event);
860         $this->assertNull($persistentEvent->rrule_until, 'rrul_until is not unset');
861         
862         $persistentEvent->rrule = 'FREQ=YEARLY;INTERVAL=1;BYMONTH=2;UNTIL=2010-04-01 21:59:59';
863         $updatedEvent = $this->_controller->update($persistentEvent);
864         $this->assertEquals('2010-04-01 21:59:59', $updatedEvent->rrule_until->get(Tinebase_Record_Abstract::ISO8601LONG));
865     }
866     
867     public function testUpdateRecuingDtstart()
868     {
869         $event = $this->_getEvent();
870         $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 21:59:59';
871         $event->exdate = array(new Tinebase_DateTime('2009-04-07 13:00:00'));
872         $persistentEvent = $this->_controller->create($event);
873         
874         $this->assertEquals('2009-04-30 21:59:59', $persistentEvent->rrule->until->toString(), 'rrule is not adapted');
875         
876         $exception = clone $persistentEvent;
877         $exception->dtstart->addDay(2);
878         $exception->dtend->addDay(2);
879         
880         $exception->setId(NULL);
881         unset($exception->rrule);
882         unset($exception->exdate);
883         $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
884         $persistentException = $this->_controller->create($exception);
885         
886         $persistentEvent->dtstart->addHour(5);
887         $persistentEvent->dtend->addHour(5);
888         
889         $updatedEvent = $this->_controller->update($persistentEvent);
890         
891         $updatedException = $this->_controller->get($persistentException->getId());
892         
893         $this->assertEquals(1, count($updatedEvent->exdate), 'failed to reset exdate');
894         $this->assertEquals('2009-04-08 18:00:00', $updatedEvent->exdate[0]->get(Tinebase_Record_Abstract::ISO8601LONG), 'failed to update exdate');
895         $this->assertEquals('2009-04-08 18:00:00', substr($updatedException->recurid, -19), 'failed to update persistent exception');
896         $this->assertEquals('2009-04-30 21:59:59', Calendar_Model_Rrule::getRruleFromString($updatedEvent->rrule)->until->get(Tinebase_Record_Abstract::ISO8601LONG), 'until in rrule must not be changed');
897         $this->assertEquals('2009-04-30 21:59:59', $updatedEvent->rrule_until->get(Tinebase_Record_Abstract::ISO8601LONG), 'rrule_until must not be changed');
898         
899         $updatedEvent->dtstart->subHour(5);
900         $updatedEvent->dtend->subHour(5);
901         $secondUpdatedEvent = $this->_controller->update($updatedEvent);
902         $secondUpdatedException = $this->_controller->get($persistentException->getId());
903         $this->assertEquals('2009-04-08 13:00:00', $secondUpdatedEvent->exdate[0]->get(Tinebase_Record_Abstract::ISO8601LONG), 'failed to update exdate (sub)');
904         $this->assertEquals('2009-04-08 13:00:00', substr($secondUpdatedException->recurid, -19), 'failed to update persistent exception (sub)');
905     }
906     
907     /**
908      * testUpdateRecurDtstartOverDst
909      */
910     public function testUpdateRecurDtstartOverDst()
911     {
912         // note: 2009-03-29 Europe/Berlin switched to DST
913         $event = new Calendar_Model_Event(array(
914             'uid'           => Tinebase_Record_Abstract::generateUID(),
915             'summary'       => 'Abendessen',
916             'dtstart'       => '2009-03-25 18:00:00',
917             'dtend'         => '2009-03-25 18:30:00',
918             'originator_tz' => 'Europe/Berlin',
919             'rrule'         => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-02 21:59:59',
920             'exdate'        => '2009-03-27 18:00:00,2009-03-31 17:00:00',
921             'container_id'  => $this->_testCalendar->getId(),
922             Tinebase_Model_Grants::GRANT_EDIT     => true,
923         ));
924         
925         $persistentEvent = $this->_controller->create($event);
926         
927         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
928         $from = new Tinebase_DateTime('2009-03-26 00:00:00');
929         $until = new Tinebase_DateTime('2009-04-03 23:59:59');
930         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); // 9 days
931         
932         // skip 27(exception), 31(exception), 03(until)
933         $this->assertEquals(6, count($recurSet));
934         
935         $exceptionBeforeDstBoundary = clone $recurSet[1]; // 28. 
936         $persistentExceptionBeforeDstBoundary = $this->_controller->createRecurException($exceptionBeforeDstBoundary);
937         
938         $updatedBaseEvent = $this->_controller->getRecurBaseEvent($recurSet[5]);
939         $recurSet[5]->last_modified_time = $updatedBaseEvent->last_modified_time;
940         $exceptionAfterDstBoundary = clone $recurSet[5]; // 02.
941         $persistentExceptionAfterDstBoundary = $this->_controller->createRecurException($exceptionAfterDstBoundary);
942         
943         $persistentEvent->dtstart->addDay(5); //30.
944         $persistentEvent->dtend->addDay(5);
945         $from->addDay(5); //31
946         $until->addDay(5); //08
947         
948         $currentPersistentEvent = $this->_controller->get($persistentEvent);
949         $persistentEvent->seq = 3; // satisfy modlog
950         $updatedPersistenEvent = $this->_controller->update($persistentEvent);
951         
952         $persistentEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
953             array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)),
954             array('field' => 'uid', 'operator' => 'equals', 'value' => $persistentEvent->uid)
955         )));
956         
957         // we don't 'see' the persistent exception from 28/
958         $this->assertEquals(2, count($persistentEvents));
959                 
960         $exceptions = $persistentEvents->filter('recurid', "/^{$persistentEvent->uid}-.*/", TRUE);
961         $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($updatedPersistenEvent, $exceptions, $from, $until);
962         
963         // 31, 01, 02
964         $this->assertEquals(3, count($recurSet));
965     }
966     
967     public function testDeleteImplicitDeleteRcuringExceptions()
968     {
969         $event = $this->_getEvent();
970         $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
971         $event->exdate = array(new Tinebase_DateTime('2009-04-07 13:00:00'));
972         $persistentEvent = $this->_controller->create($event);
973         
974         $exception = clone $persistentEvent;
975         $exception->dtstart->addDay(2);
976         $exception->dtend->addDay(2);
977         $exception->setId(NULL);
978         unset($exception->rrule);
979         unset($exception->exdate);
980         $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
981         $persistentException = $this->_controller->create($exception);
982         
983         unset($persistentEvent->rrule);
984         $updatedEvent = $this->_controller->delete($persistentEvent);
985         $this->setExpectedException('Tinebase_Exception_NotFound');
986         $this->_controller->get($persistentException->getId());
987     }
988     
989     /**
990      * test delete event
991      * - check here if content sequence of container has been increased
992      */
993     public function testDeleteEvent()
994     {
995         $event = $this->_getEvent();
996         $persistentEvent = $this->_controller->create($event);
997         
998         $this->_controller->delete($persistentEvent->getId());
999         
1000         $contentSeq = Tinebase_Container::getInstance()->getContentSequence($this->_testCalendar);
1001         $this->assertEquals(2, $contentSeq, 'container content seq should be increased 2 times!');
1002         
1003         $this->setExpectedException('Tinebase_Exception_NotFound');
1004         $this->_controller->get($persistentEvent->getId());
1005     }
1006     
1007     /**
1008      * @todo use exception api once we have it!
1009      *
1010      */
1011     public function testDeleteRecurExceptions()
1012     {
1013         $event = $this->_getEvent();
1014         $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
1015         $event->exdate = array(new Tinebase_DateTime('2009-04-07 13:00:00'));
1016         $persistentEvent = $this->_controller->create($event);
1017         
1018         $exception = clone $persistentEvent;
1019         $exception->dtstart->addDay(2);
1020         $exception->dtend->addDay(2);
1021         $exception->setId(NULL);
1022         unset($exception->rrule);
1023         unset($exception->exdate);
1024         $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
1025         $persistentException = $this->_controller->create($exception);
1026         
1027         $this->_controller->delete($persistentEvent->getId());
1028         $this->setExpectedException('Tinebase_Exception_NotFound');
1029         $this->_controller->get($persistentException->getId());
1030     }
1031     
1032     public function testCreateRecurException()
1033     {
1034         $event = $this->_getEvent();
1035         $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
1036         $persistentEvent = $this->_controller->create($event);
1037         
1038         $exception = clone $persistentEvent;
1039         $exception->dtstart->addDay(3);
1040         $exception->dtend->addDay(3);
1041         $exception->summary = 'Abendbrot';
1042         $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
1043         $persistentException = $this->_controller->createRecurException($exception);
1044         
1045         $persistentEvent = $this->_controller->get($persistentEvent->getId());
1046         $this->assertEquals(1, count($persistentEvent->exdate));
1047         
1048         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
1049             array('field' => 'uid',     'operator' => 'equals', 'value' => $persistentEvent->uid),
1050         )));
1051         $this->assertEquals(2, count($events));
1052         
1053         return $persistentException;
1054     }
1055     
1056     public function testDeleteNonPersistentRecurException()
1057     {
1058         $event = $this->_getEvent();
1059         $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
1060         $persistentEvent = $this->_controller->create($event);
1061         
1062         // create an exception (a fallout)
1063         $exception = clone $persistentEvent;
1064         $exception->dtstart->addDay(3);
1065         $exception->dtend->addDay(3);
1066         $exception->summary = 'Abendbrot';
1067         $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
1068         $persistentEventWithExdate = $this->_controller->createRecurException($exception, true);
1069         
1070         $persistentEvent = $this->_controller->get($persistentEvent->getId());
1071         $this->assertEquals('Tinebase_DateTime', get_class($persistentEventWithExdate->exdate[0]));
1072         $this->assertEquals($persistentEventWithExdate->exdate[0]->format('c'), $persistentEvent->exdate[0]->format('c'));
1073         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
1074             array('field' => 'uid',     'operator' => 'equals', 'value' => $persistentEvent->uid),
1075         )));
1076         $this->assertEquals(1, count($events));
1077     }
1078     
1079     public function testDeletePersistentRecurException()
1080     {
1081         $event = $this->_getEvent();
1082         $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
1083         $persistentEvent = $this->_controller->create($event);
1084         
1085         $exception = clone $persistentEvent;
1086         $exception->dtstart->addDay(3);
1087         $exception->dtend->addDay(3);
1088         $exception->summary = 'Abendbrot';
1089         $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
1090         $persistentException = $this->_controller->createRecurException($exception);
1091         
1092         $this->_controller->delete($persistentException->getId());
1093         
1094         $persistentEvent = $this->_controller->get($persistentEvent->getId());
1095         
1096         $this->assertEquals('Tinebase_DateTime', get_class($persistentEvent->exdate[0]));
1097         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
1098             array('field' => 'uid',     'operator' => 'equals', 'value' => $persistentEvent->uid),
1099         )));
1100         $this->assertEquals(1, count($events));
1101     }
1102     
1103     public function testSetAlarm()
1104     {
1105         $event = $this->_getEvent();
1106         $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
1107             new Tinebase_Model_Alarm(array(
1108                 'minutes_before' => 30
1109             ), TRUE)
1110         ));
1111         $persistentEvent = $this->_controller->create($event);
1112         $alarmTime = clone $persistentEvent->dtstart;
1113         $alarmTime->subMinute(30);
1114         $this->assertTrue($alarmTime->equals($persistentEvent->alarms->getFirstRecord()->alarm_time), 'initial alarm is not at expected time');
1115         
1116         
1117         $persistentEvent->dtstart->addHour(5);
1118         $persistentEvent->dtend->addHour(5);
1119         $updatedEvent = $this->_controller->update($persistentEvent);
1120         $alarmTime = clone $updatedEvent->dtstart;
1121         $alarmTime->subMinute(30);
1122         $this->assertTrue($alarmTime->equals($updatedEvent->alarms->getFirstRecord()->alarm_time), 'alarm of updated event is not adjusted');
1123     }
1124     
1125     /**
1126      * testSetAlarmOfRecurSeries
1127      */
1128     public function testSetAlarmOfRecurSeries()
1129     {
1130         $event = $this->_getEvent();
1131         $event->dtstart = Tinebase_DateTime::now()->addHour(1);
1132         $event->dtend = Tinebase_DateTime::now()->addHour(2);
1133         
1134         $event->rrule = 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;INTERVAL=1';
1135         $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
1136             new Tinebase_Model_Alarm(array(
1137                 'minutes_before' => 30
1138             ), TRUE)
1139         ));
1140         $persistentEvent = $this->_controller->create($event);
1141         $alarm = $persistentEvent->alarms->getFirstRecord();
1142         $this->assertEquals($event->dtstart->subMinute(30)->toString(), $alarm->alarm_time->toString(),
1143             'inital alarm fails: ' . print_r($alarm->toArray(), TRUE));
1144         
1145         // move whole series
1146         $persistentEvent->dtstart->addHour(5);
1147         $persistentEvent->dtend->addHour(5);
1148         $updatedEvent = $this->_controller->update($persistentEvent);
1149         $this->assertEquals($persistentEvent->dtstart->subMinute(30)->toString(), $updatedEvent->alarms->getFirstRecord()->alarm_time->toString(),
1150             'update alarm fails');
1151     }
1152     
1153     /**
1154      * testSetAlarmOfRecurSeriesException
1155      */
1156     public function testSetAlarmOfRecurSeriesException()
1157     {
1158         $event = $this->_getEvent();
1159         $event->rrule = 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;INTERVAL=1';
1160         $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
1161             new Tinebase_Model_Alarm(array(
1162                 'minutes_before' => 30
1163             ), TRUE)
1164         ));
1165         $persistentEvent = $this->_controller->create($event);
1166         
1167         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
1168         $exception = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, new Tinebase_DateTime());
1169         $exception->dtstart->subHour(6);
1170         $exception->dtend->subHour(6);
1171         $persistentException = $this->_controller->createRecurException($exception);
1172         
1173         $baseEvent = $this->_controller->getRecurBaseEvent($persistentException);
1174         $this->_controller->getAlarms($baseEvent);
1175         
1176         $exceptions = $this->_controller->getRecurExceptions($persistentException);
1177         $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, $exceptions, Tinebase_DateTime::now());
1178         
1179         $nextAlarmEventStart = new Tinebase_DateTime(substr($baseEvent->alarms->getFirstRecord()->getOption('recurid'), -19));
1180         
1181         $this->assertTrue($nextOccurance->dtstart->equals($nextAlarmEventStart), 'next alarm got not adjusted');
1182         
1183         $alarmTime = clone $persistentException->dtstart;
1184         $alarmTime->subMinute(30);
1185         $this->assertTrue($alarmTime->equals($persistentException->alarms->getFirstRecord()->alarm_time), 'alarmtime of persistent exception is not correnct/set');
1186     }
1187     
1188     /**
1189      * testAdoptAlarmTimeOfYearlyEvent
1190      * 
1191      * @see 0009320: Wrong notification on first occurrence exceptions
1192      */
1193     public function testAdoptAlarmTimeOfYearlyEvent()
1194     {
1195         $event = $this->_getEvent();
1196         $event->dtstart = new Tinebase_DateTime('2012-10-26 22:00:00');
1197         $event->dtend = new Tinebase_DateTime('2012-10-27 21:59:00');
1198         $event->is_all_day_event = 1;
1199         $event->rrule = 'FREQ=YEARLY;BYMONTH=10;BYMONTHDAY=27;INTERVAL=1';
1200         $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
1201             new Tinebase_Model_Alarm(array(
1202                 'minutes_before' => 2880
1203             ), TRUE)
1204         ));
1205         $persistentEvent = $this->_controller->create($event);
1206         //print_r($persistentEvent->toArray());
1207         $alarm = $persistentEvent->alarms->getFirstRecord();
1208         $this->_controller->adoptAlarmTime($persistentEvent, $alarm);
1209         
1210         $this->assertEquals('2014-10-24 23:00:00', $alarm->alarm_time->toString(), print_r($alarm->toArray(), true));
1211         
1212         // mock send alarm and check next occurrence
1213         $alarm->sent_status = Tinebase_Model_Alarm::STATUS_PENDING;
1214         $alarm->sent_time = new Tinebase_DateTime('2012-10-24 22:01:03');
1215         $alarm->alarm_time = new Tinebase_DateTime('2013-10-24 22:00:00');
1216         $alarm->options = '{"custom":false,"minutes_before":2880,"recurid":"' . $persistentEvent->uid . '-2013-10-26 22:00:00"}';
1217         $alarmBackend = new Tinebase_Backend_Sql(array(
1218             'modelName' => 'Tinebase_Model_Alarm', 
1219             'tableName' => 'alarm',
1220         ));
1221         
1222         $updatedAlarm = $alarmBackend->update($alarm);
1223         
1224         $updatedAlarm->sent_time = Tinebase_DateTime::now();
1225         $updatedAlarm->sent_status = Tinebase_Model_Alarm::STATUS_SUCCESS;
1226         $updatedAlarm->minutes_before = 2880;
1227         
1228         $this->_controller->adoptAlarmTime($persistentEvent, $updatedAlarm, 'instance');
1229         $this->assertEquals('2014-10-24 23:00:00', $updatedAlarm->alarm_time->toString(), print_r($updatedAlarm->toArray(), true));
1230     }
1231     
1232     public function testGetRecurExceptions()
1233     {
1234         $persistentException = $this->testCreateRecurException();
1235         
1236         $baseEvent = $this->_controller->getRecurBaseEvent($persistentException);
1237         
1238         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
1239         $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, $exceptions, $baseEvent->dtend);
1240         $this->_controller->createRecurException($nextOccurance, TRUE);
1241         
1242         $exceptions = $this->_controller->getRecurExceptions($persistentException, TRUE);
1243         $dtstarts = $exceptions->dtstart;
1244
1245         $this->assertTrue(in_array($nextOccurance->dtstart, $dtstarts), 'deleted instance missing');
1246         $this->assertTrue(in_array($persistentException->dtstart, $dtstarts), 'exception instance missing');
1247     }
1248     
1249     public function testPeriodFilter()
1250     {
1251         $persistentEvent = $this->testCreateEvent();
1252         
1253         $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
1254             array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId()),
1255             array('field' => 'period', 'operator' => 'within', 'value' => array(
1256                 'from'  => '2009-04-07',
1257                 'until' => '2010-04-07'
1258             ))
1259         )), NULL, FALSE, FALSE);
1260         
1261         $this->assertEquals(0, count($events));
1262     }
1263     
1264     /**
1265      * returns a simple event
1266      *
1267      * @return Calendar_Model_Event
1268      */
1269     protected function _getEvent($_now=FALSE)
1270     {
1271         $event = new Calendar_Model_Event(array(
1272             'summary'     => 'Mittagspause',
1273             'dtstart'     => '2009-04-06 13:00:00',
1274             'dtend'       => '2009-04-06 13:30:00',
1275             'description' => 'Wieslaw Brudzinski: Das Gesetz garantiert zwar die Mittagspause, aber nicht das Mittagessen...',
1276         
1277             'container_id' => $this->_testCalendar->getId(),
1278             Tinebase_Model_Grants::GRANT_EDIT    => true,
1279         ));
1280         
1281         if ($_now) {
1282             $event->dtstart = Tinebase_DateTime::now();
1283             $event->dtend = Tinebase_DateTime::now()->addMinute(15);
1284         }
1285         
1286         return $event;
1287     }
1288     
1289     /**
1290      * (non-PHPdoc)
1291      * @see Calendar_TestCase::_getAttendee()
1292      */
1293     protected function _getAttendee()
1294     {
1295         return new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
1296             array(
1297                 'user_id'   => Tinebase_Core::getUser()->contact_id,
1298                 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
1299                 'role'      => Calendar_Model_Attender::ROLE_REQUIRED
1300             ),
1301             array(
1302                 'user_id'   => Tinebase_Group::getInstance()->getDefaultGroup()->getId(),
1303                 'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
1304                 'role'      => Calendar_Model_Attender::ROLE_REQUIRED
1305             )
1306         ));
1307     }
1308     
1309     /**
1310      * tests if customfields gets saved properly
1311      */
1312     public function testCustomFields()
1313     {
1314         $cfData = new Tinebase_Model_CustomField_Config(array(
1315             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
1316             'name'              => 'unittest',
1317             'model'             => 'Calendar_Model_Event',
1318             'definition'        => array(
1319                 'label' => Tinebase_Record_Abstract::generateUID(),
1320                 'type'  => 'string',
1321                 'uiconfig' => array(
1322                     'xtype'  => Tinebase_Record_Abstract::generateUID(),
1323                     'length' => 10,
1324                     'group'  => 'unittest',
1325                     'order'  => 100,
1326                 )
1327             )
1328         ));
1329     
1330         try {
1331             $cf = Tinebase_CustomField::getInstance()->addCustomField($cfData);
1332         } catch (Zend_Db_Statement_Exception $zdse) {
1333             // customfield already exists
1334             $cfs = Tinebase_CustomField::getInstance()->getCustomFieldsForApplication('Calendar');
1335             $cf = $cfs->filter('name', $name)->getFirstRecord();
1336         }
1337     
1338         $event = new Calendar_Model_Event(array(
1339             'summary'     => 'Abendessen',
1340             'dtstart'     => '2014-04-06 18:00:00',
1341             'dtend'       => '2014-04-06 19:00:00',
1342             'description' => 'Guten Appetit',
1343             
1344             'container_id' => $this->_testCalendar->getId(),
1345             Tinebase_Model_Grants::GRANT_EDIT    => true,
1346             'customfields' => array('unittest' => 'Hello')
1347         ));
1348         
1349         $event = $this->_controller->create($event);
1350         
1351         $this->assertEquals('Hello', $event->customfields['unittest']);
1352     }
1353 }