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