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