3 * Tine 2.0 - http://www.tine20.org
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>
12 * Test class for Calendar_Controller_Event
16 class Calendar_Controller_EventTests extends Calendar_TestCase
19 * @var Calendar_Controller_Event controller unter test
21 protected $_controller;
25 * @see Calendar_TestCase::setUp()
27 public function setUp()
30 $this->_controller = Calendar_Controller_Event::getInstance();
36 * @return Calendar_Model_Event
38 public function testCreateEvent()
40 $event = $this->_getEvent();
41 $persistentEvent = $this->_controller->create($event);
43 $this->assertEquals($event->description, $persistentEvent->description);
44 $this->assertTrue($event->dtstart->equals($persistentEvent->dtstart));
45 $this->assertEquals(Tinebase_Core::getUserTimezone(), $persistentEvent->originator_tz);
47 return $persistentEvent;
51 * testCreateAlldayEventWithoutDtend
53 public function testCreateAlldayEventWithoutDtend()
55 $event = $this->_getEvent();
56 $event->is_all_day_event = true;
59 $persistentEvent = $this->_controller->create($event);
60 $persistentEvent->setTimezone(Tinebase_Core::getUserTimezone());
61 $this->assertEquals('2009-04-06 23:59:59', $persistentEvent->dtend->toString());
67 public function testGetEvent()
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});
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});
80 public function testGetRecurInstance()
82 // create event and invite admin group
83 $event = $this->_getEvent();
84 $event->rrule = 'FREQ=DAILY;INTERVAL=1';
86 $persistentEvent = $this->_controller->create($event);
87 $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
88 $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, Tinebase_DateTime::now());
90 $deepLink = $nextOccurance->getDeepLink();
91 preg_match('/fakeid.*$/', $deepLink, $matches);
94 $recurInstance = $this->_controller->get($id);
95 $this->assertTrue($recurInstance->isRecurInstance());
96 $this->assertEquals($nextOccurance->getId(), $recurInstance->getId());
98 // create recur exception in the meantime
99 $nextOccurance->summary = 'exception';
100 $this->_controller->createRecurException($nextOccurance);
101 $recurInstance = $this->_controller->get($id);
102 // print_r($recurInstance->toArray());
103 $this->assertFalse($recurInstance->isRecurInstance());
104 $this->assertEquals($nextOccurance->summary, $recurInstance->summary);
110 public function testUpdateEvent()
112 $persistentEvent = $this->testCreateEvent();
114 $currentTz = Tinebase_Core::getUserTimezone();
115 Tinebase_Core::set(Tinebase_Core::USERTIMEZONE, 'farfaraway');
117 $persistentEvent->summary = 'Lunchtime';
118 $updatedEvent = $this->_controller->update($persistentEvent);
119 $this->assertEquals($persistentEvent->summary, $updatedEvent->summary);
120 $this->assertEquals($currentTz, $updatedEvent->originator_tz, 'originator_tz must not be touchet if dtsart is not updatet!');
122 $updatedEvent->dtstart->addHour(1);
123 $updatedEvent->dtend->addHour(1);
124 $secondUpdatedEvent = $this->_controller->update($updatedEvent);
125 $this->assertEquals(Tinebase_Core::getUserTimezone(), $secondUpdatedEvent->originator_tz, 'originator_tz must be adopted if dtsart is updatet!');
127 Tinebase_Core::set(Tinebase_Core::USERTIMEZONE, $currentTz);
130 public function testConcurrentUpdate()
132 $event = $this->testCreateEvent();
135 $resolvableConcurrentEvent1 = clone $event;
136 $resolvableConcurrentEvent1->dtstart = $resolvableConcurrentEvent1->dtstart->addMonth(1);
137 $resolvableConcurrentEvent1->dtend = $resolvableConcurrentEvent1->dtend->addMonth(1);
138 $resolvableConcurrentEvent1Update = $this->_controller->update($resolvableConcurrentEvent1);
141 $resolvableConcurrentEvent2 = clone $event;
142 $resolvableConcurrentEvent2->summary = 'Updated Event';
143 $resolvableConcurrentEvent2Update = $this->_controller->update($resolvableConcurrentEvent2);
145 $this->assertEquals($resolvableConcurrentEvent1Update->dtstart, $resolvableConcurrentEvent2Update->dtstart);
148 public function testUpdateAttendeeStatus()
150 $event = $this->_getEvent();
151 $event->attendee = $this->_getAttendee();
152 $event->attendee[1] = new Calendar_Model_Attender(array(
153 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
154 'user_id' => $this->_getPersonasContacts('pwulf')->getId(),
157 $persistentEvent = $this->_controller->create($event);
159 foreach ($persistentEvent->attendee as $attender) {
160 $attender->status = Calendar_Model_Attender::STATUS_DECLINED;
161 $this->_controller->attenderStatusUpdate($persistentEvent, $attender, $attender->status_authkey);
165 $persistentEvent->last_modified_time = $this->_controller->get($persistentEvent->getId())->last_modified_time;
168 $persistentEvent->dtstart->addHour(2);
169 $persistentEvent->dtend->addHour(2);
170 // NOTE: in normal operations the status authkey is removed by resolveAttendee
171 // we simulate this here by removeing the keys per hand. (also note that current user does not need an authkey)
172 $persistentEvent->attendee->status_authkey = null;
173 $updatedEvent = $this->_controller->update($persistentEvent);
175 $currentUser = $updatedEvent->attendee
176 ->filter('user_type', Calendar_Model_Attender::USERTYPE_USER)
177 ->filter('user_id', Tinebase_Core::getUser()->contact_id)
180 $pwulf = $updatedEvent->attendee
181 ->filter('user_type', Calendar_Model_Attender::USERTYPE_USER)
182 ->filter('user_id', $this->_getPersonasContacts('pwulf')->getId())
185 $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $currentUser->status, 'current users status must not be touched');
186 $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, $pwulf->status, 'pwulfs status must be reset');
189 public function testUpdateMultiple()
191 $persistentEvent = $this->testCreateEvent();
193 $filter = new Calendar_Model_EventFilter(array(
194 array('field' => 'id', 'operator' => 'in', 'value' => array($persistentEvent->getId()))
198 'summary' => 'multipleTest'
201 $this->_controller->updateMultiple($filter, $data);
203 $updatedEvent = $this->_controller->get($persistentEvent->getId());
204 $this->assertEquals('multipleTest', $updatedEvent->summary);
207 public function testAttendeeBasics()
209 $event = $this->_getEvent();
210 $event->attendee = $this->_getAttendee();
211 $event->attendee[1] = new Calendar_Model_Attender(array(
212 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
213 'user_id' => $this->_getPersonasContacts('pwulf')->getId()
216 $persistendEvent = $this->_controller->create($event);
217 $this->assertEquals(2, count($persistendEvent->attendee));
219 unset($persistendEvent->attendee[0]);
220 $updatedEvent = $this->_controller->update($persistendEvent);
221 $this->assertEquals(1, count($updatedEvent->attendee));
223 $updatedEvent->attendee->getFirstRecord()->role = Calendar_Model_Attender::ROLE_OPTIONAL;
224 $updatedEvent->attendee->getFirstRecord()->transp = Calendar_Model_Event::TRANSP_TRANSP;
226 $secondUpdatedEvent = $this->_controller->update($updatedEvent);
227 $this->assertEquals(1, count($secondUpdatedEvent->attendee));
228 $this->assertEquals(Calendar_Model_Attender::ROLE_OPTIONAL, $secondUpdatedEvent->attendee->getFirstRecord()->role);
229 $this->assertEquals(Calendar_Model_Event::TRANSP_TRANSP, $secondUpdatedEvent->attendee->getFirstRecord()->transp);
232 public function testAttendeeFilter()
234 $event1 = $this->_getEvent();
235 $event1->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
236 array('user_id' => Tinebase_Core::getUser()->contact_id),
237 array('user_id' => $this->_getPersonasContacts('pwulf')->getId())
239 $this->_controller->create($event1);
241 $event2 = $this->_getEvent();
242 $event2->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
243 array('user_id' => Tinebase_Core::getUser()->contact_id),
244 array('user_id' => $this->_getPersonasContacts('sclever')->getId()),
246 $this->_controller->create($event2);
248 $event3 = $this->_getEvent();
249 $event3->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
250 array('user_id' => Tinebase_Core::getUser()->contact_id),
251 array('user_id' => $this->_getPersonasContacts('sclever')->getId()),
253 $this->_controller->create($event3);
256 $filter = new Calendar_Model_EventFilter(array(
257 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
258 array('field' => 'attender' , 'operator' => 'equals', 'value' => array(
259 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
260 'user_id' => $this->_getPersonasContacts('sclever')->getId()
263 $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
264 $this->assertEquals(2, count($eventsFound), 'sclever attends to two events');
267 $filter = new Calendar_Model_EventFilter(array(
268 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
269 array('field' => 'attender' , 'operator' => 'equals', 'value' => array(
270 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
271 'user_id' => $this->_getPersonasContacts('pwulf')->getId()
274 $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
275 $this->assertEquals(1, count($eventsFound), 'pwulf attends to one events');
277 // test sclever OR pwulf
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(
282 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
283 'user_id' => $this->_getPersonasContacts('sclever')->getId()
286 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
287 'user_id' => $this->_getPersonasContacts('pwulf')->getId()
291 $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
292 $this->assertEquals(3, count($eventsFound), 'sclever OR pwulf attends to tree events');
295 public function testAttendeeGroupFilter()
297 $event = $this->_getEvent();
298 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
299 array('user_id' => Tinebase_Core::getUser()->contact_id),
300 array('user_id' => $this->_getPersonasContacts('sclever')->getId())
302 $this->_controller->create($event);
304 $filter = new Calendar_Model_EventFilter(array(
305 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
306 array('field' => 'attender' , 'operator' => 'in', 'value' => array(
308 'user_type' => Calendar_Model_AttenderFilter::USERTYPE_MEMBEROF,
309 'user_id' => $this->_getPersona('sclever')->accountPrimaryGroup
313 $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
314 $this->assertEquals(1, count($eventsFound), 'sclever is groupmember');
318 * @see 0006702: CalDAV: single event appears in personal and shared calendar
320 * TODO fix for pgsql: Failed asserting that 0 matches expected 1.
323 public function testAttendeeNotInFilter()
325 if ($this->_dbIsPgsql()) {
326 $this->markTestSkipped('0011674: problem with Attendee "NotIn" Filter (pgsql)');
329 foreach(array(Tinebase_Core::getUser()->contact_id, $this->_personasContacts['sclever']->getId()) as $attId) {
330 $event = $this->_getEvent();
331 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
332 array('user_id' => $attId),
334 $this->_controller->create($event);
337 $filter = new Calendar_Model_EventFilter(array(
338 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
339 array('field' => 'attender' , 'operator' => 'notin', 'value' => array(
341 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
342 'user_id' => $this->_personasContacts['sclever']->getId()
346 $eventsFound = $this->_controller->search($filter, new Tinebase_Model_Pagination());
347 $this->assertEquals(1, count($eventsFound), 'should be exactly one event');
349 Tinebase_Core::getUser()->contact_id,
350 $eventsFound->getFirstRecord()->attendee->getFirstRecord()->user_id,
351 'sclevers event should not be found');
355 * test get free busy info with single event
357 * @return Calendar_Model_Event
359 public function testGetFreeBusyInfo()
361 $event = $this->_getEvent();
362 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
363 array('user_id' => $this->_getPersonasContacts('sclever')->getId()),
364 array('user_id' => $this->_getPersonasContacts('pwulf')->getId())
366 $persistentEvent = $this->_controller->create($event);
368 $period = new Calendar_Model_EventFilter(array(array(
370 'operator' => 'within',
372 'from' => $persistentEvent->dtstart,
373 'until' => $persistentEvent->dtend
376 $fbinfo = $this->_controller->getFreeBusyInfo($period, $persistentEvent->attendee);
378 $this->assertGreaterThanOrEqual(2, count($fbinfo));
380 return $persistentEvent;
384 * test get free busy info with single event
386 * @return Calendar_Model_Event
388 public function testGetFreeBusyInfoWithGroup()
390 $event = $this->_getEvent();
391 $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
393 'user_id' => Tinebase_Group::getInstance()->getDefaultGroup(),
394 'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
395 'role' => Calendar_Model_Attender::ROLE_OPTIONAL,
398 $event->attendee = clone $attendee;
399 $persistentEvent = $this->_controller->create($event);
401 $period = new Calendar_Model_EventFilter(array(array(
403 'operator' => 'within',
405 'from' => $persistentEvent->dtstart,
406 'until' => $persistentEvent->dtend
409 $fbinfo = $this->_controller->getFreeBusyInfo($period, $attendee);
411 $this->assertGreaterThanOrEqual(1, count($fbinfo));
413 return $persistentEvent;
416 public function testSearchFreeTime()
418 $event = $this->_getEvent();
419 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
420 array('user_id' => $this->_getPersonasContacts('sclever')->getId(), 'user_type' => Calendar_Model_Attender::USERTYPE_USER),
421 array('user_id' => $this->_getPersonasContacts('pwulf')->getId(), 'user_type' => Calendar_Model_Attender::USERTYPE_USER)
423 $event->rrule = 'FREQ=WEEKLY;INTERVAL=1;BYDAY=TU,FR';
426 'constraints' => array(array(
427 'dtstart' => $event->dtstart->getClone()->setHour(6),
428 'dtend' => $event->dtstart->getClone()->setHour(22),
429 'rrule' => 'FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR'
433 $result = $this->_controller->searchFreeTime($event, $options);
434 static::assertEquals(1, $result->count());
435 /** @var Calendar_Model_Event $suggestedEvent */
436 $suggestedEvent = $result->getFirstRecord();
437 $dtstartExpected = $event->dtstart->getClone()->addDay(1)->setHour(6); // '2009-04-07 06:00:00'
438 $dtendExpected = $event->dtend->getClone()->addDay(1)->setHour(6); // '2009-04-07 06:30:00'
439 static::assertEquals($dtstartExpected, $suggestedEvent->dtstart);
440 static::assertEquals($dtendExpected, $suggestedEvent->dtend);
442 $newEvent = clone $event;
443 $newEvent->rrule = null;
444 $newEvent->dtstart = $dtstartExpected->getClone()->addMinute(29); // '2009-04-07 06:29:00'
445 $newEvent->dtend = $dtendExpected->getClone(); // '2009-04-07 06:30:00'
446 $this->_controller->create($newEvent);
447 $newEvent->setId(null);
448 $newEvent->uid = null;
449 $newEvent->attendee->id = null;
450 $newEvent->attendee->cal_event_id = null;
451 $newEvent->dtstart->addDay(7); // '2009-04-14 06:29:00'
452 $newEvent->dtend->addDay(7); // '2009-04-14 06:30:00'
453 $this->_controller->create($newEvent);
455 $result = $this->_controller->searchFreeTime($event, $options);
456 static::assertEquals(1, $result->count());
457 /** @var Calendar_Model_Event $suggestedEvent */
458 $suggestedEvent = $result->getFirstRecord();
459 static::assertEquals($dtstartExpected->addMinute(30), $suggestedEvent->dtstart); // '2009-04-07 06:30:00'
460 static::assertEquals($dtendExpected->addMinute(30), $suggestedEvent->dtend); // '2009-04-07 07:00:00'
462 $newEvent->setId(null);
463 $newEvent->uid = null;
464 $newEvent->attendee->id = null;
465 $newEvent->attendee->cal_event_id = null;
466 $newEvent->dtstart->addMinute(1); // '2009-04-14 06:30:00'
467 $newEvent->dtend->addMinute(1); // '2009-04-14 06:31:00'
468 $this->_controller->create($newEvent);
470 $result = $this->_controller->searchFreeTime($event, $options);
471 static::assertEquals(1, $result->count());
472 /** @var Calendar_Model_Event $suggestedEvent */
473 $suggestedEvent = $result->getFirstRecord();
474 static::assertEquals($dtstartExpected->addMinute(15), $suggestedEvent->dtstart); // '2009-04-07 06:45:00'
475 static::assertEquals($dtendExpected->addMinute(15), $suggestedEvent->dtend); // '2009-04-07 07:15:00'
479 * events from deleted calendars should not be shown
481 public function testSearchEventFromDeletedCalendar() {
482 $testCal = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
483 'name' => 'PHPUnit test calendar',
484 'type' => Tinebase_Model_Container::TYPE_PERSONAL,
485 'owner_id' => Tinebase_Core::getUser(),
486 'backend' => $this->_backend->getType(),
487 'application_id' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId()
490 $this->_getTestCalendars()->addRecord($testCal);
492 // create event in testcal
493 $event = $this->_getEvent();
494 $event->container_id = $testCal->getId();
495 $event->attendee = $this->_getAttendee();
496 $persistentEvent = $this->_controller->create($event);
499 Tinebase_Container::getInstance()->deleteContainer($testCal, TRUE);
501 // search by attendee
502 $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
503 array('field' => 'attender', 'operator' => 'equals', 'value' => array(
504 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
505 'user_id' => $this->_getTestUserContact()->getId()
507 )), NULL, FALSE, FALSE);
509 $this->assertFalse(in_array($persistentEvent->getId(), $events->getId()), 'event in deleted (display) container shuld not be found');
512 public function testCreateEventWithConflict()
514 $this->_testNeedsTransaction();
516 $event = $this->_getEvent();
517 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
518 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
519 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
521 $this->_controller->create($event);
523 $conflictEvent = $this->_getEvent();
524 $conflictEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
525 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
526 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
530 $exectionRaised = FALSE;
531 $this->_controller->create($conflictEvent, TRUE);
532 } catch (Calendar_Exception_AttendeeBusy $busyException) {
533 $fbData = $busyException->toArray();
534 $this->assertGreaterThanOrEqual(2, count($fbData['freebusyinfo']));
535 $exectionRaised = TRUE;
537 if (! $exectionRaised) {
538 $this->fail('An expected exception has not been raised.');
540 $persitentConflictEvent = $this->_controller->create($conflictEvent, FALSE);
542 return $persitentConflictEvent;
545 public function testCreateEventWithConflictFromGroupMember()
547 $event = $this->_getEvent();
548 $event->attendee = $this->_getAttendee();
549 $this->_controller->create($event);
551 $conflictEvent = $this->_getEvent();
552 $conflictEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
553 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
554 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
558 $this->_controller->create($conflictEvent, TRUE);
559 $this->assertTrue(false, 'Failed to detect conflict from groupmember');
560 } catch (Calendar_Exception_AttendeeBusy $busyException) {
561 $fbData = $busyException->toArray();
562 $this->assertGreaterThanOrEqual(2, count($fbData['freebusyinfo']));
566 $this->fail('An expected exception has not been raised.');
569 public function testCreateTransparentEventNoConflict()
571 $event = $this->_getEvent();
572 $event->attendee = $this->_getAttendee();
573 $this->_controller->create($event);
575 $nonConflictEvent = $this->_getEvent();
576 $nonConflictEvent->transp = Calendar_Model_Event::TRANSP_TRANSP;
577 $nonConflictEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
578 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
579 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
582 $this->_controller->create($nonConflictEvent, TRUE);
585 public function testCreateNoConflictParallelTrasparentEvent()
587 $event = $this->_getEvent();
588 $event->attendee = $this->_getAttendee();
589 $event->transp = Calendar_Model_Event::TRANSP_TRANSP;
590 $this->_controller->create($event);
592 $nonConflictEvent = $this->_getEvent();
593 $nonConflictEvent->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
594 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('sclever')->getId()),
595 array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $this->_getPersonasContacts('pwulf')->getId())
598 $this->_controller->create($nonConflictEvent, TRUE);
601 public function testCreateNoConflictParallelAtendeeTrasparentEvent()
603 $event = $this->_getEvent();
604 $event->attendee = $this->_getAttendee();
605 unset ($event->attendee[1]); // no group here
606 $event->attendee->transp = Calendar_Model_Event::TRANSP_TRANSP;
607 $this->_controller->create($event);
609 $nonConflictEvent = $this->_getEvent();
610 $nonConflictEvent->attendee = $this->_getAttendee();
612 $this->_controller->create($nonConflictEvent, TRUE);
615 public function testCreateConflictResourceUnavailable()
617 $event = $this->_getEvent();
619 // create & add resource
620 $rt = new Calendar_Controller_ResourceTest();
622 $resource = $rt->testCreateResource();
623 $resource->busy_type = Calendar_Model_FreeBusy::FREEBUSY_BUSY_UNAVAILABLE;
624 Calendar_Controller_Resource::getInstance()->update($resource);
626 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(new Calendar_Model_Attender(array(
627 'user_type' => Calendar_Model_Attender::USERTYPE_RESOURCE,
628 'user_id' => $resource->getId()
631 $conflictEvent = clone $event;
632 $this->_controller->create($event);
634 $this->_controller->create($conflictEvent, TRUE);
635 $this->fail('Calendar_Exception_AttendeeBusy was not thrown');
636 } catch (Calendar_Exception_AttendeeBusy $abe) {
637 $fb = $abe->getFreeBusyInfo();
638 $this->assertEquals(Calendar_Model_FreeBusy::FREEBUSY_BUSY_UNAVAILABLE, $fb[0]->type);
643 public function testUpdateWithConflictNoTimechange()
645 $persitentConflictEvent = $this->testCreateEventWithConflict();
646 $persitentConflictEvent->summary = 'only time updates should recheck free/busy';
648 $this->_controller->update($persitentConflictEvent, TRUE);
651 public function testUpdateWithConflictAttendeeChange()
653 $persitentConflictEvent = $this->testCreateEventWithConflict();
654 $persitentConflictEvent->summary = 'attendee adds should recheck free/busy';
656 $defaultUserGroup = Tinebase_Group::getInstance()->getDefaultGroup();
657 $persitentConflictEvent->attendee->addRecord(new Calendar_Model_Attender(array(
658 'user_id' => $defaultUserGroup->getId(),
659 'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
660 'role' => Calendar_Model_Attender::ROLE_REQUIRED
663 $this->setExpectedException('Calendar_Exception_AttendeeBusy');
664 $this->_controller->update($persitentConflictEvent, TRUE);
667 public function testUpdateWithConflictWithTimechange()
669 $persitentConflictEvent = $this->testCreateEventWithConflict();
670 $persitentConflictEvent->summary = 'time updates should recheck free/busy';
671 $persitentConflictEvent->dtend->addHour(1);
673 $this->setExpectedException('Calendar_Exception_AttendeeBusy');
674 $this->_controller->update($persitentConflictEvent, TRUE);
677 public function testAttendeeAuthKeyPreserv()
679 $event = $this->_getEvent();
680 $event->attendee = $this->_getAttendee();
682 $persistendEvent = $this->_controller->create($event);
683 $newAuthKey = Tinebase_Record_Abstract::generateUID();
684 $persistendEvent->attendee->status_authkey = $newAuthKey;
686 $updatedEvent = $this->_controller->update($persistendEvent);
687 foreach ($updatedEvent->attendee as $attender) {
688 $this->assertNotEquals($newAuthKey, $attender->status_authkey);
692 public function testAttendeeStatusPreservViaSave()
694 $event = $this->_getEvent();
695 $event->attendee = $this->_getAttendee();
696 $event->attendee[0]->user_id = Tinebase_User::getInstance()->getUserByLoginName('sclever')->contact_id;
697 $event->attendee[0]->status = Calendar_Model_Attender::STATUS_ACCEPTED;
698 unset($event->attendee[1]);
700 $persistendEvent = $this->_controller->create($event);
701 $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, $persistendEvent->attendee[0]->status, 'creation of other attedee must not set status');
703 $persistendEvent->attendee[0]->status = Calendar_Model_Attender::STATUS_ACCEPTED;
704 $persistendEvent->attendee[0]->status_authkey = NULL;
705 $updatedEvent = $this->_controller->update($persistendEvent);
706 $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, $updatedEvent->attendee[0]->status, 'updateing of other attedee must not set status');
709 public function testAttendeeSetStatus()
711 $event = $this->_getEvent();
712 $event->attendee = $this->_getAttendee();
713 unset($event->attendee[1]);
715 $persistendEvent = $this->_controller->create($event);
716 $attendee = $persistendEvent->attendee[0];
718 $attendee->status = Calendar_Model_Attender::STATUS_DECLINED;
719 $this->_controller->attenderStatusUpdate($persistendEvent, $attendee, $attendee->status_authkey);
721 $loadedEvent = $this->_controller->get($persistendEvent->getId());
722 $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $loadedEvent->attendee[0]->status, 'status not set');
726 public function testAttendeeStatusFilter()
728 $event = $this->_getEvent();
729 $event->attendee = $this->_getAttendee();
730 unset($event->attendee[1]);
732 $persistentEvent = $this->_controller->create($event);
734 $filter = new Calendar_Model_EventFilter(array(
735 array('field' => 'uid', 'operator' => 'equals', 'value' => $persistentEvent->uid),
736 array('field' => 'attender_status', 'operator' => 'not', 'value' => Calendar_Model_Attender::STATUS_DECLINED),
739 $events = $this->_controller->search($filter);
740 $this->assertEquals(1, count($events), 'event should be found, but is not');
742 $attender = $persistentEvent->attendee[0];
743 $attender->status = Calendar_Model_Attender::STATUS_DECLINED;
744 $this->_controller->update($persistentEvent);
746 $events = $this->_controller->search($filter);
747 $this->assertEquals(0, count($events), 'event should _not_ be found, but is');
751 public function testAttendeeDisplaycontainerContact()
753 $contact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array(
754 'n_given' => 'phpunit',
755 'n_family' => 'cal attender'
758 $event = $this->_getEvent();
759 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
761 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
762 'user_id' => $contact->getId(),
763 'role' => Calendar_Model_Attender::ROLE_REQUIRED
766 $persistentEvent = $this->_controller->create($event);
767 $attender = $persistentEvent->attendee[0];
769 $this->assertTrue(empty($attender->displaycontainer_id), 'displaycontainer_id must not be set for contacts');
772 public function testAttendeeGroupMembersResolving()
774 if (Tinebase_User::getConfiguredBackend() === Tinebase_User::ACTIVEDIRECTORY) {
775 $this->markTestSkipped('only working in non-AD setups');
778 $defaultUserGroup = Tinebase_Group::getInstance()->getDefaultGroup();
779 Tinebase_Group::getInstance()->getDefaultAdminGroup();
781 $event = $this->_getEvent();
782 $event->attendee = $this->_getAttendee();
783 $event->attendee[1] = new Calendar_Model_Attender(array(
784 'user_id' => $defaultUserGroup->getId(),
785 'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
786 'role' => Calendar_Model_Attender::ROLE_REQUIRED
789 $persistentEvent = $this->_controller->create($event);
790 $defaultUserGroupMembers = Tinebase_Group::getInstance()->getGroupMembers($defaultUserGroup->getId());
791 // user as attender + group + all members
792 $expectedAttendeeCount = 1 + 1 + count($defaultUserGroupMembers);
793 if (in_array(Tinebase_Core::getUser()->getId(), $defaultUserGroupMembers)) {
794 // remove suppressed user (only if user is member of default group)
795 $expectedAttendeeCount--;
797 $this->assertEquals($expectedAttendeeCount, count($persistentEvent->attendee),
798 'attendee: ' . print_r($persistentEvent->attendee->toArray(), true));
800 $groupAttender = $persistentEvent->attendee->find('user_type', Calendar_Model_Attender::USERTYPE_GROUP);
801 $persistentEvent->attendee->removeRecord($groupAttender);
803 $updatedPersistentEvent = $this->_controller->update($persistentEvent);
804 $this->assertEquals(1, count($updatedPersistentEvent->attendee));
807 public function testAttendeeGroupMembersChange()
809 $defaultAdminGroup = Tinebase_Group::getInstance()->getDefaultAdminGroup();
811 // create event and invite admin group
812 $event = $this->_getEvent();
814 // only events in future will be changed!
815 $event->dtstart = Tinebase_DateTime::now()->addHour(1);
816 $event->dtend = Tinebase_DateTime::now()->addHour(2);
818 $event->attendee = $this->_getAttendee();
819 $event->attendee[1] = new Calendar_Model_Attender(array(
820 'user_id' => $defaultAdminGroup->getId(),
821 'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
822 'role' => Calendar_Model_Attender::ROLE_REQUIRED
824 $persistentEvent = $this->_controller->create($event);
826 // assert test condition
827 $pwulf = $persistentEvent->attendee
828 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
829 ->filter('user_id', $this->_getPersonasContacts('pwulf')->getId());
830 $this->assertEquals(0, count($pwulf), 'invalid test condition, pwulf should not be member or admin group');
832 Admin_Controller_Group::getInstance()->addGroupMember($defaultAdminGroup->getId(), $this->_getPersonasContacts('pwulf')->account_id);
833 if (isset(Tinebase_Core::getConfig()->actionqueue)) {
834 Tinebase_ActionQueue::getInstance()->processQueue(10000);
837 $loadedEvent = $this->_controller->get($persistentEvent->getId());
838 // assert pwulf is in
839 $pwulf = $loadedEvent->attendee
840 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
841 ->filter('user_id', $this->_getPersonasContacts('pwulf')->getId());
842 $this->assertEquals(1, count($pwulf), 'pwulf is not attender of event, but should be');
845 Admin_Controller_Group::getInstance()->removeGroupMember($defaultAdminGroup->getId(), $this->_getPersonasContacts('pwulf')->account_id);
846 if (isset(Tinebase_Core::getConfig()->actionqueue)) {
847 Tinebase_ActionQueue::getInstance()->processQueue(10000);
850 $loadedEvent = $this->_controller->get($persistentEvent->getId());
851 // assert pwulf is missing
852 $pwulf = $loadedEvent->attendee
853 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
854 ->filter('user_id', $this->_getPersonasContacts('pwulf')->getId());
855 $this->assertEquals(0, count($pwulf), 'pwulf is attender of event, but not should be');
857 // Test the same with update
858 $group = Admin_Controller_Group::getInstance()->get($defaultAdminGroup->getId());
859 $group->members = array_merge(Admin_Controller_Group::getInstance()->getGroupMembers($defaultAdminGroup->getId()), array(Tinebase_Helper::array_value('pwulf', Zend_Registry::get('personas'))->getId()));
860 Admin_Controller_Group::getInstance()->update($group);
861 if (isset(Tinebase_Core::getConfig()->actionqueue)) {
862 Tinebase_ActionQueue::getInstance()->processQueue(10000);
865 // assert pwulf is in
866 $loadedEvent = $this->_controller->get($persistentEvent->getId());
867 $pwulf = $loadedEvent->attendee
868 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
869 ->filter('user_id', $this->_getPersonasContacts('pwulf')->getId());
870 $this->assertEquals(1, count($pwulf), 'pwulf is not attender of event, but should be (via update)');
872 $group->members = array_diff(Admin_Controller_Group::getInstance()->getGroupMembers($defaultAdminGroup->getId()), array(Tinebase_Helper::array_value('pwulf', Zend_Registry::get('personas'))->getId()));
873 Admin_Controller_Group::getInstance()->update($group);
874 if (isset(Tinebase_Core::getConfig()->actionqueue)) {
875 Tinebase_ActionQueue::getInstance()->processQueue(10000);
877 // assert pwulf is missing
878 $loadedEvent = $this->_controller->get($persistentEvent->getId());
879 $pwulf = $loadedEvent->attendee
880 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
881 ->filter('user_id', $this->_getPersonasContacts('pwulf')->getId());
882 $this->assertEquals(0, count($pwulf), 'pwulf is attender of event, but not should be');
885 public function testAttendeeGroupMembersAddUser()
888 // clean up if exists
889 $cleanupUser = Tinebase_User::getInstance()->getFullUserByLoginName('testAttendeeGroupMembersAddUser');
890 Tinebase_User::getInstance()->deleteUser($cleanupUser);
891 } catch (Exception $e) {
896 $defaultGroup = Tinebase_Group::getInstance()->getDefaultGroup();
898 // create event and invite admin group
899 $event = $this->_getEvent();
901 // only events in future will be changed!
902 $event->dtstart = Tinebase_DateTime::now()->addHour(1);
903 $event->dtend = Tinebase_DateTime::now()->addHour(2);
905 $event->attendee = $this->_getAttendee();
906 $event->attendee[1] = new Calendar_Model_Attender(array(
907 'user_id' => $defaultGroup->getId(),
908 'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
909 'role' => Calendar_Model_Attender::ROLE_REQUIRED
911 $persistentEvent = $this->_controller->create($event);
913 $newUser = $this->_createNewUser();
914 if (isset(Tinebase_Core::getConfig()->actionqueue)) {
915 Tinebase_ActionQueue::getInstance()->processQueue(10000);
918 // check if this user was added to event
919 $loadedEvent = $this->_controller->get($persistentEvent->getId());
920 $user = $loadedEvent->attendee
921 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
922 ->filter('user_id', $newUser->contact_id);
923 $this->assertEquals(1, count($user), 'added user is not attender of event, but should be. user: ' . print_r($newUser->toArray(), TRUE));
926 Admin_Controller_User::getInstance()->delete($newUser->getId());
927 if (isset(Tinebase_Core::getConfig()->actionqueue)) {
928 Tinebase_ActionQueue::getInstance()->processQueue(10000);
931 // check if user was removed from event
932 $loadedEvent = $this->_controller->get($persistentEvent->getId());
933 $user = $loadedEvent->attendee
934 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
935 ->filter('user_id', $newUser->contact_id);
936 $this->assertEquals(0, count($user), 'added user is attender of event, but should be (after deleting user)');
940 * testAttendeeGroupMembersRecurringAddUser
942 * FIXME 0007352: fix Calendar_Controller_EventTests::testAttendeeGroupMembersRecurringAddUser
944 public function testAttendeeGroupMembersRecurringAddUser()
946 $this->markTestIncomplete('test fails sometimes / needs fixing');
950 $cleanupUser = Tinebase_User::getInstance()->getFullUserByLoginName('testAttendeeGroupMembersAddUser');
951 Tinebase_User::getInstance()->deleteUser($cleanupUser);
952 } catch (Exception $e) {
956 $defaultGroup = Tinebase_Group::getInstance()->getDefaultGroup();
958 // create event and invite admin group
959 $event = $this->_getEvent();
960 $event->rrule = 'FREQ=DAILY;INTERVAL=1';
962 $event->attendee = $this->_getAttendee();
963 $event->attendee[1] = new Calendar_Model_Attender(array(
964 'user_id' => $defaultGroup->getId(),
965 'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
966 'role' => Calendar_Model_Attender::ROLE_REQUIRED
968 $persistentEvent = $this->_controller->create($event);
970 $newUser = $this->_createNewUser();
971 if (isset(Tinebase_Core::getConfig()->actionqueue)) {
972 Tinebase_ActionQueue::getInstance()->processQueue(10000);
975 $events = $this->_backend->search(new Calendar_Model_EventFilter(array(
976 array('field' => 'container_id', 'operator' => 'in', 'value' => $this->_getTestCalendars()->getId()),
977 )), new Tinebase_Model_Pagination(array()));
979 $oldSeries = $events->filter('rrule_until', '/.+/', TRUE)->getFirstRecord();
980 $newSeries = $events->filter('rrule_until', '/^$/', TRUE)->getFirstRecord();
982 $this->assertEquals(2, $events->count(), 'recur event must be splitted '. print_r($events->toArray(), TRUE));
983 // check if this user was added to event
984 $this->_controller->get($persistentEvent->getId());
985 $user = $oldSeries->attendee
986 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
987 ->filter('user_id', $newUser->contact_id);
988 $this->assertEquals(0, count($user), 'added user is attender of old event, but should not be');
989 $user = $newSeries->attendee
990 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
991 ->filter('user_id', $newUser->contact_id);
992 $this->assertEquals(1, count($user), 'added user is not attender of new event, but should be');
995 Admin_Controller_User::getInstance()->delete($newUser->getId());
996 if (isset(Tinebase_Core::getConfig()->actionqueue)) {
997 Tinebase_ActionQueue::getInstance()->processQueue(10000);
1000 $events = $this->_backend->search(new Calendar_Model_EventFilter(array(
1001 array('field' => 'container_id', 'operator' => 'in', 'value' => $this->_getTestCalendars()->getId()),
1002 )), new Tinebase_Model_Pagination(array()));
1004 $newSeries = $events->filter('rrule_until', '/^$/', TRUE)->getFirstRecord();
1006 // check if this user was deleted from event
1007 $user = $newSeries->attendee
1008 ->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER)
1009 ->filter('user_id', $newUser->contact_id);
1010 $this->assertEquals(0, count($user), 'deleted user is attender of new event, but should not be');
1013 protected function _createNewUser()
1015 $pw = Tinebase_Record_Abstract::generateUID(10) . '*A53x';
1016 $newUser = Admin_Controller_User::getInstance()->create(new Tinebase_Model_FullUser(array(
1017 'accountLoginName' => 'testAttendeeGroupMembersAddUser',
1018 'accountStatus' => 'enabled',
1019 'accountExpires' => NULL,
1020 'accountPrimaryGroup' => Tinebase_Group::getInstance()->getDefaultGroup()->getId(),
1021 'accountLastName' => 'Tine 2.0',
1022 'accountFirstName' => 'PHPUnit',
1023 'accountEmailAddress' => 'phpunit@metaways.de'
1028 public function testRruleUntil()
1030 $event = $this->_getEvent();
1032 $event->rrule_until = Tinebase_DateTime::now();
1033 $persistentEvent = $this->_controller->create($event);
1034 $this->assertNull($persistentEvent->rrule_until, 'rrul_until is not unset');
1036 $persistentEvent->rrule = 'FREQ=YEARLY;INTERVAL=1;BYMONTH=2;UNTIL=2010-04-01 21:59:59';
1037 $updatedEvent = $this->_controller->update($persistentEvent);
1038 $this->assertEquals('2010-04-01 21:59:59', $updatedEvent->rrule_until->get(Tinebase_Record_Abstract::ISO8601LONG));
1041 public function testUpdateRecuingDtstart()
1043 $event = $this->_getEvent();
1044 $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 21:59:59';
1045 $event->exdate = array(new Tinebase_DateTime('2009-04-07 13:00:00'));
1046 $persistentEvent = $this->_controller->create($event);
1048 $this->assertEquals('2009-04-30 21:59:59', $persistentEvent->rrule->until->toString(), 'rrule is not adapted');
1050 $exception = clone $persistentEvent;
1051 $exception->dtstart->addDay(2);
1052 $exception->dtend->addDay(2);
1054 $exception->setId(NULL);
1055 unset($exception->rrule);
1056 unset($exception->exdate);
1057 $exception->setRecurId($event->getId());
1058 $persistentException = $this->_controller->create($exception);
1060 $persistentEvent->dtstart->addHour(5);
1061 $persistentEvent->dtend->addHour(5);
1063 $updatedEvent = $this->_controller->update($persistentEvent);
1065 $updatedException = $this->_controller->get($persistentException->getId());
1067 $this->assertEquals(1, count($updatedEvent->exdate), 'failed to reset exdate');
1068 $this->assertEquals('2009-04-08 18:00:00', $updatedEvent->exdate[0]->get(Tinebase_Record_Abstract::ISO8601LONG), 'failed to update exdate');
1069 $this->assertEquals('2009-04-08 18:00:00', substr($updatedException->recurid, -19), 'failed to update persistent exception');
1070 $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');
1071 $this->assertEquals('2009-04-30 21:59:59', $updatedEvent->rrule_until->get(Tinebase_Record_Abstract::ISO8601LONG), 'rrule_until must not be changed');
1073 $updatedEvent->dtstart->subHour(5);
1074 $updatedEvent->dtend->subHour(5);
1075 $secondUpdatedEvent = $this->_controller->update($updatedEvent);
1076 $secondUpdatedException = $this->_controller->get($persistentException->getId());
1077 $this->assertEquals('2009-04-08 13:00:00', $secondUpdatedEvent->exdate[0]->get(Tinebase_Record_Abstract::ISO8601LONG), 'failed to update exdate (sub)');
1078 $this->assertEquals('2009-04-08 13:00:00', substr($secondUpdatedException->recurid, -19), 'failed to update persistent exception (sub)');
1082 * testUpdateRecurDtstartOverDst
1084 public function testUpdateRecurDtstartOverDst()
1086 // note: 2009-03-29 Europe/Berlin switched to DST
1087 $event = new Calendar_Model_Event(array(
1088 'uid' => Tinebase_Record_Abstract::generateUID(),
1089 'summary' => 'Abendessen',
1090 'dtstart' => '2009-03-25 18:00:00',
1091 'dtend' => '2009-03-25 18:30:00',
1092 'originator_tz' => 'Europe/Berlin',
1093 'rrule' => 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-02 21:59:59',
1094 'exdate' => '2009-03-27 18:00:00,2009-03-31 17:00:00',
1095 'container_id' => $this->_getTestCalendar()->getId(),
1096 Tinebase_Model_Grants::GRANT_EDIT => true,
1099 $persistentEvent = $this->_controller->create($event);
1101 $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
1102 $from = new Tinebase_DateTime('2009-03-26 00:00:00');
1103 $until = new Tinebase_DateTime('2009-04-03 23:59:59');
1104 $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); // 9 days
1106 // skip 27(exception), 31(exception), 03(until)
1107 $this->assertEquals(6, count($recurSet));
1109 $exceptionBeforeDstBoundary = clone $recurSet[1]; // 28.
1110 $persistentExceptionBeforeDstBoundary = $this->_controller->createRecurException($exceptionBeforeDstBoundary);
1112 $updatedBaseEvent = $this->_controller->getRecurBaseEvent($recurSet[5]);
1113 $recurSet[5]->last_modified_time = $updatedBaseEvent->last_modified_time;
1114 $exceptionAfterDstBoundary = clone $recurSet[5]; // 02.
1115 $persistentExceptionAfterDstBoundary = $this->_controller->createRecurException($exceptionAfterDstBoundary);
1117 $persistentEvent->dtstart->addDay(5); //30.
1118 $persistentEvent->dtend->addDay(5);
1119 $from->addDay(5); //31
1120 $until->addDay(5); //08
1122 $this->_controller->get($persistentEvent);
1123 $persistentEvent->seq = 3; // satisfy modlog
1124 $updatedPersistenEvent = $this->_controller->update($persistentEvent);
1126 $persistentEvents = $this->_controller->search(new Calendar_Model_EventFilter(array(
1127 array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)),
1128 array('field' => 'uid', 'operator' => 'equals', 'value' => $persistentEvent->uid)
1131 // we don't 'see' the persistent exception from 28/
1132 $this->assertEquals(2, count($persistentEvents));
1134 $exceptions = $persistentEvents->filter('recurid', "/^{$persistentEvent->uid}-.*/", TRUE);
1135 $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($updatedPersistenEvent, $exceptions, $from, $until);
1138 $this->assertEquals(3, count($recurSet));
1141 public function testDeleteImplicitDeleteRcuringExceptions()
1143 $event = $this->_getEvent();
1144 $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
1145 $event->exdate = array(new Tinebase_DateTime('2009-04-07 13:00:00'));
1146 $persistentEvent = $this->_controller->create($event);
1148 $exception = clone $persistentEvent;
1149 $exception->dtstart->addDay(2);
1150 $exception->dtend->addDay(2);
1151 $exception->setId(NULL);
1152 unset($exception->rrule);
1153 unset($exception->exdate);
1154 $exception->setRecurId($persistentEvent->getId());
1155 $persistentException = $this->_controller->create($exception);
1157 unset($persistentEvent->rrule);
1158 $this->_controller->delete($persistentEvent);
1159 $this->setExpectedException('Tinebase_Exception_NotFound');
1160 $this->_controller->get($persistentException->getId());
1165 * - check here if content sequence of container has been increased
1167 public function testDeleteEvent()
1169 $event = $this->_getEvent();
1170 $persistentEvent = $this->_controller->create($event);
1172 $this->_controller->delete($persistentEvent->getId());
1174 $contentSeq = Tinebase_Container::getInstance()->getContentSequence($this->_getTestCalendar());
1175 $this->assertEquals(3, $contentSeq, 'container content seq should be increased 3 times!');
1177 $this->setExpectedException('Tinebase_Exception_NotFound');
1178 $this->_controller->get($persistentEvent->getId());
1182 * @todo use exception api once we have it!
1185 public function testDeleteRecurExceptions()
1187 $event = $this->_getEvent();
1188 $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
1189 $event->exdate = array(new Tinebase_DateTime('2009-04-07 13:00:00'));
1190 $persistentEvent = $this->_controller->create($event);
1192 $exception = clone $persistentEvent;
1193 $exception->dtstart->addDay(2);
1194 $exception->dtend->addDay(2);
1195 $exception->setId(NULL);
1196 unset($exception->rrule);
1197 unset($exception->exdate);
1198 $exception->setRecurId($persistentEvent->getId());
1199 $persistentException = $this->_controller->create($exception);
1201 $this->_controller->delete($persistentEvent->getId());
1202 $this->setExpectedException('Tinebase_Exception_NotFound');
1203 $this->_controller->get($persistentException->getId());
1206 public function testDeleteNonPersistentRecurException()
1208 $event = $this->_getEvent();
1209 $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
1210 $persistentEvent = $this->_controller->create($event);
1212 // create an exception (a fallout)
1213 $exception = clone $persistentEvent;
1214 $exception->dtstart->addDay(3);
1215 $exception->dtend->addDay(3);
1216 $exception->summary = 'Abendbrot';
1217 $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
1218 $persistentEventWithExdate = $this->_controller->createRecurException($exception, true);
1220 $persistentEvent = $this->_controller->get($persistentEvent->getId());
1221 $this->assertEquals('Tinebase_DateTime', get_class($persistentEventWithExdate->exdate[0]));
1222 $this->assertEquals($persistentEventWithExdate->exdate[0]->format('c'), $persistentEvent->exdate[0]->format('c'));
1223 $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
1224 array('field' => 'uid', 'operator' => 'equals', 'value' => $persistentEvent->uid),
1226 $this->assertEquals(1, count($events));
1229 public function testDeletePersistentRecurException()
1231 $event = $this->_getEvent();
1232 $event->rrule = 'FREQ=DAILY;INTERVAL=1;UNTIL=2009-04-30 13:30:00';
1233 $persistentEvent = $this->_controller->create($event);
1235 $exception = clone $persistentEvent;
1236 $exception->dtstart->addDay(3);
1237 $exception->dtend->addDay(3);
1238 $exception->summary = 'Abendbrot';
1239 $exception->recurid = $exception->uid . '-' . $exception->dtstart->get(Tinebase_Record_Abstract::ISO8601LONG);
1240 $persistentException = $this->_controller->createRecurException($exception);
1242 $this->_controller->delete($persistentException->getId());
1244 $persistentEvent = $this->_controller->get($persistentEvent->getId());
1246 $this->assertEquals('Tinebase_DateTime', get_class($persistentEvent->exdate[0]));
1247 $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
1248 array('field' => 'uid', 'operator' => 'equals', 'value' => $persistentEvent->uid),
1250 $this->assertEquals(1, count($events));
1253 public function testSetAlarm()
1255 $event = $this->_getEvent();
1256 $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
1257 new Tinebase_Model_Alarm(array(
1258 'minutes_before' => 30
1261 $persistentEvent = $this->_controller->create($event);
1262 $alarmTime = clone $persistentEvent->dtstart;
1263 $alarmTime->subMinute(30);
1264 $this->assertTrue($alarmTime->equals($persistentEvent->alarms->getFirstRecord()->alarm_time), 'initial alarm is not at expected time');
1267 $persistentEvent->dtstart->addHour(5);
1268 $persistentEvent->dtend->addHour(5);
1269 $updatedEvent = $this->_controller->update($persistentEvent);
1270 $alarmTime = clone $updatedEvent->dtstart;
1271 $alarmTime->subMinute(30);
1272 $this->assertTrue($alarmTime->equals($updatedEvent->alarms->getFirstRecord()->alarm_time), 'alarm of updated event is not adjusted');
1276 * testSetAlarmOfRecurSeries
1278 public function testSetAlarmOfRecurSeries()
1280 $event = $this->_getEvent();
1281 $event->dtstart = Tinebase_DateTime::now()->addHour(1);
1282 $event->dtend = Tinebase_DateTime::now()->addHour(2);
1284 $event->rrule = 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;INTERVAL=1';
1285 $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
1286 new Tinebase_Model_Alarm(array(
1287 'minutes_before' => 30
1290 $persistentEvent = $this->_controller->create($event);
1291 $alarm = $persistentEvent->alarms->getFirstRecord();
1292 $this->assertEquals($event->dtstart->subMinute(30)->toString(), $alarm->alarm_time->toString(),
1293 'inital alarm fails: ' . print_r($alarm->toArray(), TRUE));
1295 // move whole series
1296 $persistentEvent->dtstart->addHour(5);
1297 $persistentEvent->dtend->addHour(5);
1298 $updatedEvent = $this->_controller->update($persistentEvent);
1299 $this->assertEquals($persistentEvent->dtstart->subMinute(30)->toString(), $updatedEvent->alarms->getFirstRecord()->alarm_time->toString(),
1300 'update alarm fails');
1304 * testSetAlarmOfRecurSeriesException
1306 public function testSetAlarmOfRecurSeriesException()
1308 $event = $this->_getEvent();
1309 $event->rrule = 'FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;INTERVAL=1';
1310 $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
1311 new Tinebase_Model_Alarm(array(
1312 'minutes_before' => 30
1315 $persistentEvent = $this->_controller->create($event);
1317 $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
1318 $exception = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, new Tinebase_DateTime());
1319 $exception->dtstart->subHour(6);
1320 $exception->dtend->subHour(6);
1321 $persistentException = $this->_controller->createRecurException($exception);
1323 $baseEvent = $this->_controller->getRecurBaseEvent($persistentException);
1324 $this->_controller->getAlarms($baseEvent);
1326 $exceptions = $this->_controller->getRecurExceptions($persistentException);
1327 $nextOccurance = Calendar_Model_Rrule::computeNextOccurrence($baseEvent, $exceptions, Tinebase_DateTime::now());
1329 $nextAlarmEventStart = new Tinebase_DateTime(substr($baseEvent->alarms->getFirstRecord()->getOption('recurid'), -19));
1331 $this->assertTrue($nextOccurance->dtstart->equals($nextAlarmEventStart), 'next alarm got not adjusted');
1333 $alarmTime = clone $persistentException->dtstart;
1334 $alarmTime->subMinute(30);
1335 $this->assertTrue($alarmTime->equals($persistentException->alarms->getFirstRecord()->alarm_time), 'alarmtime of persistent exception is not correnct/set');
1339 * testAdoptAlarmTimeOfYearlyEvent
1341 * @see 0009320: Wrong notification on first occurrence exceptions
1343 public function testAdoptAlarmTimeOfYearlyEvent()
1345 $event = $this->_getEvent();
1346 $event->dtstart = new Tinebase_DateTime('2012-10-26 22:00:00');
1347 $event->dtend = new Tinebase_DateTime('2012-10-27 21:59:00');
1348 $event->is_all_day_event = 1;
1349 $event->rrule = 'FREQ=YEARLY;BYMONTH=10;BYMONTHDAY=27;INTERVAL=1';
1350 $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(
1351 new Tinebase_Model_Alarm(array(
1352 'minutes_before' => 2880
1355 $persistentEvent = $this->_controller->create($event);
1356 $alarm = $persistentEvent->alarms->getFirstRecord();
1357 $this->_controller->adoptAlarmTime($persistentEvent, $alarm);
1359 $now = Tinebase_DateTime::now();
1360 $year = $now->get('Y');
1361 if ($now->isLater(new Tinebase_DateTime($year . '-10-27'))) {
1364 // might be at 22:00 or 23.00 (daylight saving ...)
1365 // TODO verify that (@see 0011404: fix failing testAdoptAlarmTimeOfYearlyEvent)
1366 $expectedAlarmTimes = array($year . '-10-24 22:00:00', $year . '-10-24 23:00:00');
1367 $this->assertTrue(in_array($alarm->alarm_time->toString(), $expectedAlarmTimes),
1368 'alarm time mismatch:' . print_r($alarm->toArray(), true)
1369 . ' expected: ' . print_r($expectedAlarmTimes, true));
1371 if ($now->isLater(new Tinebase_DateTime($year . '-10-24'))) {
1372 // FIXME test fails if current date is between 10-24 and 10-27
1373 // @see 0011404: fix failing testAdoptAlarmTimeOfYearlyEvent
1377 // mock send alarm and check next occurrence
1378 $alarm->sent_status = Tinebase_Model_Alarm::STATUS_PENDING;
1379 $alarm->sent_time = new Tinebase_DateTime('2012-10-24 22:01:03');
1380 $alarm->alarm_time = new Tinebase_DateTime('2013-10-24 22:00:00');
1381 $alarm->options = '{"custom":false,"minutes_before":2880,"recurid":"' . $persistentEvent->uid . '-2013-10-26 22:00:00"}';
1382 $alarmBackend = new Tinebase_Backend_Sql(array(
1383 'modelName' => 'Tinebase_Model_Alarm',
1384 'tableName' => 'alarm',
1387 $updatedAlarm = $alarmBackend->update($alarm);
1389 $updatedAlarm->sent_time = Tinebase_DateTime::now();
1390 $updatedAlarm->sent_status = Tinebase_Model_Alarm::STATUS_SUCCESS;
1391 $updatedAlarm->minutes_before = 2880;
1393 $this->_controller->adoptAlarmTime($persistentEvent, $updatedAlarm, 'instance');
1394 $this->assertTrue(in_array($updatedAlarm->alarm_time->toString(), $expectedAlarmTimes),
1395 'alarm time mismatch:' . print_r($updatedAlarm->toArray(), true)
1396 . ' expected: ' . print_r($expectedAlarmTimes, true));
1399 public function testPeriodFilter()
1401 $this->testCreateEvent();
1403 $events = $this->_controller->search(new Calendar_Model_EventFilter(array(
1404 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
1405 array('field' => 'period', 'operator' => 'within', 'value' => array(
1406 'from' => '2009-04-07',
1407 'until' => '2010-04-07'
1409 )), NULL, FALSE, FALSE);
1411 $this->assertEquals(0, count($events));
1415 * returns a simple event
1417 * @return Calendar_Model_Event
1420 * @todo replace with TestCase::_getEvent
1422 public function _getEvent($_now = FALSE, $mute = NULL)
1424 $event = new Calendar_Model_Event(array(
1425 'summary' => 'Mittagspause',
1426 'dtstart' => '2009-04-06 13:00:00',
1427 'dtend' => '2009-04-06 13:30:00',
1428 'description' => 'Wieslaw Brudzinski: Das Gesetz garantiert zwar die Mittagspause, aber nicht das Mittagessen...',
1430 'container_id' => $this->_getTestCalendar()->getId(),
1431 Tinebase_Model_Grants::GRANT_EDIT => true,
1435 $event->dtstart = Tinebase_DateTime::now();
1436 $event->dtend = Tinebase_DateTime::now()->addMinute(15);
1444 * @see Calendar_TestCase::_getAttendee()
1446 protected function _getAttendee()
1448 return new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
1450 'user_id' => Tinebase_Core::getUser()->contact_id,
1451 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
1452 'role' => Calendar_Model_Attender::ROLE_REQUIRED
1455 'user_id' => Tinebase_Group::getInstance()->getDefaultGroup()->getId(),
1456 'user_type' => Calendar_Model_Attender::USERTYPE_GROUP,
1457 'role' => Calendar_Model_Attender::ROLE_REQUIRED
1463 * tests if customfields gets saved properly
1465 public function testCustomFields()
1467 $cfData = new Tinebase_Model_CustomField_Config(array(
1468 'application_id' => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
1469 'name' => 'unittest',
1470 'model' => 'Calendar_Model_Event',
1471 'definition' => array(
1472 'label' => Tinebase_Record_Abstract::generateUID(),
1474 'uiconfig' => array(
1475 'xtype' => Tinebase_Record_Abstract::generateUID(),
1477 'group' => 'unittest',
1484 Tinebase_CustomField::getInstance()->addCustomField($cfData);
1485 } catch (Zend_Db_Statement_Exception $zdse) {
1486 // custom field already exists
1489 $event = new Calendar_Model_Event(array(
1490 'summary' => 'Abendessen',
1491 'dtstart' => '2014-04-06 18:00:00',
1492 'dtend' => '2014-04-06 19:00:00',
1493 'description' => 'Guten Appetit',
1495 'container_id' => $this->_getTestCalendar()->getId(),
1496 Tinebase_Model_Grants::GRANT_EDIT => true,
1497 'customfields' => array('unittest' => 'Hello')
1500 $event = $this->_controller->create($event);
1502 $this->assertEquals('Hello', $event->customfields['unittest']);
1506 * @see 0010454: cli script for comparing calendars
1508 public function testCompareCalendars()
1510 $cal1 = $this->_testCalendar;
1511 $cal2 = $this->_getTestContainer('Calendar');
1513 $this->_buildCompareCalendarsFixture($cal1, $cal2);
1515 $from = Tinebase_DateTime::now()->subDay(1);
1516 $until = Tinebase_DateTime::now()->addWeek(2);
1517 $result = Calendar_Controller_Event::getInstance()->compareCalendars($cal1->getId(), $cal2->getId(), $from, $until);
1519 $this->assertEquals(2, count($result['matching']), 'events 3+4 / 8+9 should have matched: ' . print_r($result['matching']->toArray(), true));
1520 $this->assertEquals(1, count($result['changed']), 'event 5 should appear in changed: ' . print_r($result['changed']->toArray(), true));
1521 $this->assertEquals(1, count($result['missingInCal1']), 'event 2 should miss from cal1: ' . print_r($result['missingInCal1']->toArray(), true));
1522 $this->assertEquals(1, count($result['missingInCal2']), 'event 6 should miss from cal2 ' . print_r($result['missingInCal2']->toArray(), true));
1526 * create some events to compare
1528 * - event1: in calendar 1
1529 * - event2: only in calendar 2
1530 * - event3+4: in both calendars
1531 * - event5: slightly different from event1 (same summary) / in cal2
1532 * - event6: only in cal1 (next week)
1533 * - event7: only in displaycontainer
1534 * - event8+9: in both calendars (whole day)
1536 * @param Tinebase_Model_Container $cal1
1537 * @param Tinebase_Model_Container $cal2
1539 protected function _buildCompareCalendarsFixture($cal1, $cal2)
1541 $event1 = $this->_getEvent(true);
1542 $event1->summary = 'event 1';
1543 $this->_controller->create($event1);
1545 $event2 = $this->_getEvent(true);
1546 $event2->dtstart->addDay(1);
1547 $event2->dtend->addDay(1);
1548 $event2->summary = 'event 2';
1549 $event2->container_id = $cal2->getId();
1550 $this->_controller->create($event2);
1552 $event3 = $this->_getEvent(true);
1553 $event3->dtstart->addDay(2);
1554 $event3->dtend->addDay(2);
1555 $event3->summary = 'event 3';
1556 $event4 = clone $event3;
1557 $this->_controller->create($event3);
1559 $event4->container_id = $cal2->getId();
1560 $this->_controller->create($event4);
1562 $event5 = $this->_getEvent(true);
1563 $event5->summary = 'event 1';
1564 $event5->dtstart->addMinute(30);
1565 $event5->container_id = $cal2->getId();
1566 $this->_controller->create($event5);
1568 // this tests weekly processing, too
1569 $event6 = $this->_getEvent(true);
1570 $event6->summary = 'event 6';
1571 $event6->dtstart->addDay(8);
1572 $event6->dtend->addDay(8);
1573 $this->_controller->create($event6);
1575 // add event that is only in displaycontainer (should not appear in report)
1576 $currentDefault = Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::DEFAULTCALENDAR, Tinebase_Core::getUser()->getId());
1577 Tinebase_Core::getPreference('Calendar')->setValue(Calendar_Preference::DEFAULTCALENDAR, $cal1->getId());
1578 $event7 = $this->_getEvent(true);
1579 $event7->summary = 'event 7';
1580 $event7->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
1582 'user_id' => Tinebase_Core::getUser()->contact_id,
1583 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
1584 'role' => Calendar_Model_Attender::ROLE_REQUIRED,
1587 Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['sclever']);
1588 $cal3 = $this->_getTestContainer('Calendar');
1589 $event7->container_id = $cal3->getId();
1590 $this->_controller->create($event7);
1591 Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
1592 Tinebase_Core::getPreference('Calendar')->setValue(Calendar_Preference::DEFAULTCALENDAR, $currentDefault);
1594 $event8 = $this->_getEvent(true);
1595 $event8->summary = 'event 8';
1596 $event8->is_all_day_event = true;
1597 $event9 = clone $event8;
1598 $this->_controller->create($event8);
1600 $event9->container_id = $cal2->getId();
1601 $this->_controller->create($event9);
1604 public function testRepairAttendee()
1606 $event = $this->_getEvent(true);
1607 $event->attendee = null;
1608 $persistentEvent = $this->_controller->create($event);
1610 $result = $this->_controller->repairAttendee($persistentEvent->container_id, Tinebase_DateTime::now()->subDay(1), Tinebase_DateTime::now());
1612 $this->assertEquals(1, $result, 'should repair 1 event');
1614 $repairedEvent = $this->_controller->get($persistentEvent->getId());
1615 $this->assertEquals(1, count($repairedEvent->attendee));
1616 $ownAttender = Calendar_Model_Attender::getOwnAttender($repairedEvent->attendee);
1617 $this->assertTrue($ownAttender !== null);
1621 * @see 0011130: handle bad originator timzone in VCALENDAR converter
1623 public function testBrokenTimezoneInEvent()
1625 $event = $this->_getEvent(true);
1626 $event->originator_tz = 'AWSTTTT';
1628 $event = $this->_controller->create($event);
1629 $this->fail('should throw Tinebase_Exception_Record_Validation because of bad TZ: ' . print_r($event->toArray(), true));
1630 } catch (Tinebase_Exception_Record_Validation $terv) {
1631 $this->assertEquals('Bad Timezone: AWSTTTT', $terv->getMessage());