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