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-2014 Metaways Infosystems GmbH (http://www.metaways.de)
8 * @author Cornelius Weiss <c.weiss@metaways.de>
12 * Test class for Json Frontend
16 class Calendar_JsonTests extends Calendar_TestCase
19 * Calendar Json Object
21 * @var Calendar_Frontend_Json
23 protected $_uit = null;
26 * Calendar Controller Event Object
28 * @var Calendar_Controller_Event
30 protected $_eventController = null;
34 * @see Calendar/Calendar_TestCase::setUp()
36 public function setUp()
40 Calendar_Controller_Event::getInstance()->doContainerACLChecks(true);
42 $this->_uit = new Calendar_Frontend_Json();
43 $this->_eventController = Calendar_Controller_Event::getInstance();
49 public function testGetRegistryData()
51 // enforce fresh instance of calendar preferences
52 Tinebase_Core::set(Tinebase_Core::PREFERENCES, array());
54 $registryData = $this->_uit->getRegistryData();
56 $this->assertTrue(is_array($registryData['defaultContainer']['account_grants']));
57 $this->assertTrue(is_array($registryData['defaultContainer']['ownerContact']));
61 * test shared calendar as default
62 * @see 0011986: Default Calender in Preferences restet to personal one after logout/login
64 public function testGetRegistryDataWithSharedDefault()
66 $fe = new Tinebase_Frontend_Json_Container();
67 $container = $fe->addContainer('Calendar', 'testdeletecontacts', Tinebase_Model_Container::TYPE_SHARED, '');
69 Tinebase_Core::set(Tinebase_Core::PREFERENCES, array());
70 Tinebase_Core::getPreference('Calendar')
71 ->setValue(Calendar_Preference::DEFAULTCALENDAR, $container['id']);
73 $registryData = $this->_uit->getRegistryData();
75 $this->assertTrue(is_array($registryData['defaultContainer']['account_grants']));
76 $this->assertFalse(isset($registryData['defaultContainer']['ownerContact']));
78 Tinebase_Core::getPreference('Calendar')
79 ->deleteUserPref(Calendar_Preference::DEFAULTCALENDAR);
85 * @param $now should the current date be used
87 public function testCreateEvent($now = FALSE)
89 $scleverDisplayContainerId = Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::DEFAULTCALENDAR, $this->_getPersona('sclever')->getId());
90 $contentSeqBefore = Tinebase_Container::getInstance()->getContentSequence($scleverDisplayContainerId);
92 $eventData = $this->_getEvent($now)->toArray();
94 $tag = Tinebase_Tags::getInstance()->createTag(new Tinebase_Model_Tag(array(
95 'name' => 'phpunit-' . substr(Tinebase_Record_Abstract::generateUID(), 0, 10),
96 'type' => Tinebase_Model_Tag::TYPE_PERSONAL
98 $eventData['tags'] = array($tag->toArray());
100 $note = new Tinebase_Model_Note(array(
101 'note' => 'very important note!',
102 'note_type_id' => Tinebase_Notes::getInstance()->getNoteTypes()->getFirstRecord()->getId(),
104 $eventData['notes'] = array($note->toArray());
105 $eventData['etag'] = Tinebase_Record_Abstract::generateUID();
107 $persistentEventData = $this->_uit->saveEvent($eventData);
108 $loadedEventData = $this->_uit->getEvent($persistentEventData['id']);
110 $this->_assertJsonEvent($eventData, $loadedEventData, 'failed to create/load event');
111 $this->assertEquals($eventData['etag'], $loadedEventData['etag']);
113 $contentSeqAfter = Tinebase_Container::getInstance()->getContentSequence($scleverDisplayContainerId);
114 $this->assertEquals($contentSeqBefore + 1, $contentSeqAfter,
115 'content sequence of display container should be increased by 1:' . $contentSeqAfter);
116 $this->assertEquals($contentSeqAfter, Tinebase_Container::getInstance()->get($scleverDisplayContainerId)->content_seq);
118 return $loadedEventData;
121 public function testStripWindowsLinebreaks()
123 $e = $this->_getEvent(TRUE);
124 $e->description = 'Hello my friend,' . chr(13) . chr(10) .'bla bla bla.' . chr(13) . chr(10) .'good bye.';
125 $persistentEventData = $this->_uit->saveEvent($e->toArray());
126 $loadedEventData = $this->_uit->getEvent($persistentEventData['id']);
127 $this->assertEquals($loadedEventData['description'], 'Hello my friend,' . chr(10) . 'bla bla bla.' . chr(10) . 'good bye.');
131 * testCreateEventWithNonExistantAttender
133 public function testCreateEventWithNonExistantAttender()
135 $testEmail = 'unittestnotexists@example.org';
136 $eventData = $this->_getEvent(TRUE)->toArray();
137 $eventData['attendee'][] = $this->_getUserTypeAttender($testEmail);
139 $persistentEventData = $this->_uit->saveEvent($eventData);
141 foreach ($persistentEventData['attendee'] as $attender) {
142 if ($attender['user_id']['email'] === $testEmail) {
143 $this->assertEquals($testEmail, $attender['user_id']['n_fn']);
147 $this->assertTrue($found);
151 * get single attendee array
153 * @param string $email
156 protected function _getUserTypeAttender($email = 'unittestnotexists@example.org')
160 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
161 'role' => Calendar_Model_Attender::ROLE_REQUIRED,
166 * test create event with alarm
168 * @todo add testUpdateEventWithAlarm
170 public function testCreateEventWithAlarm()
172 $eventData = $this->_getEventWithAlarm(TRUE)->toArray();
173 $persistentEventData = $this->_uit->saveEvent($eventData);
174 $loadedEventData = $this->_uit->getEvent($persistentEventData['id']);
176 //print_r($loadedEventData);
178 // check if alarms are created / returned
179 $this->assertGreaterThan(0, count($loadedEventData['alarms']));
180 $this->assertEquals('Calendar_Model_Event', $loadedEventData['alarms'][0]['model']);
181 $this->assertEquals(Tinebase_Model_Alarm::STATUS_PENDING, $loadedEventData['alarms'][0]['sent_status']);
182 $this->assertTrue((isset($loadedEventData['alarms'][0]['minutes_before']) || array_key_exists('minutes_before', $loadedEventData['alarms'][0])), 'minutes_before is missing');
184 $scheduler = Tinebase_Core::getScheduler();
185 $scheduler->addTask('Tinebase_Alarm', $this->createTask());
188 // check alarm status
189 $loadedEventData = $this->_uit->getEvent($persistentEventData['id']);
190 $this->assertEquals(Tinebase_Model_Alarm::STATUS_SUCCESS, $loadedEventData['alarms'][0]['sent_status']);
196 public function createTask()
198 $request = new Zend_Controller_Request_Http();
199 $request->setControllerName('Tinebase_Alarm');
200 $request->setActionName('sendPendingAlarms');
201 $request->setParam('eventName', 'Tinebase_Event_Async_Minutely');
203 $task = new Tinebase_Scheduler_Task();
204 $task->setMonths("Jan-Dec");
205 $task->setWeekdays("Sun-Sat");
206 $task->setDays("1-31");
207 $task->setHours("0-23");
208 $task->setMinutes("0/1");
209 $task->setRequest($request);
216 public function testUpdateEvent()
218 $event = new Calendar_Model_Event($this->testCreateEvent(), true);
219 $event->dtstart->addHour(5);
220 $event->dtend->addHour(5);
221 $event->description = 'are you kidding?';
223 $eventData = $event->toArray();
224 foreach ($eventData['attendee'] as $key => $attenderData) {
225 if ($eventData['attendee'][$key]['user_id'] != $this->_getTestUserContact()->getId()) {
226 unset($eventData['attendee'][$key]);
230 $updatedEventData = $this->_uit->saveEvent($eventData);
232 $this->_assertJsonEvent($eventData, $updatedEventData, 'failed to update event');
234 return $updatedEventData;
240 public function testDeleteEvent() {
241 $eventData = $this->testCreateEvent();
243 $this->_uit->deleteEvents(array($eventData['id']));
245 $this->setExpectedException('Tinebase_Exception_NotFound');
246 $this->_uit->getEvent($eventData['id']);
252 public function testSearchEvents()
254 $eventData = $this->testCreateEvent(TRUE);
256 $filter = $this->_getEventFilterArray();
257 $searchResultData = $this->_uit->searchEvents($filter, array());
259 $this->assertTrue(! empty($searchResultData['results']));
260 $resultEventData = $searchResultData['results'][0];
262 $this->_assertJsonEvent($eventData, $resultEventData, 'failed to search event');
263 return $searchResultData;
267 * get filter array with container and period filter
269 * @param string|int $containerId
270 * @return multitype:multitype:string Ambigous <number, multitype:> multitype:string multitype:string
272 protected function _getEventFilterArray($containerId = NULL)
274 $containerId = ($containerId) ? $containerId : $this->_getTestCalendar()->getId();
276 array('field' => 'container_id', 'operator' => 'equals', 'value' => $containerId),
277 array('field' => 'period', 'operator' => 'within', 'value' =>
278 array("from" => '2009-03-20 06:15:00', "until" => Tinebase_DateTime::now()->addDay(1)->toString())
284 * testSearchEvents with period filter
286 * @todo add an event that is in result set of Calendar_Controller_Event::search()
287 * but should be removed in Calendar_Frontend_Json::_multipleRecordsToJson()
289 public function testSearchEventsWithPeriodFilter()
291 $eventData = $this->testCreateRecurEvent();
294 array('field' => 'period', 'operator' => 'within', 'value' => array(
295 'from' => '2009-03-25 00:00:00',
296 'until' => '2009-03-25 23:59:59',
298 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
301 $searchResultData = $this->_uit->searchEvents($filter, array());
303 $this->assertTrue(isset($searchResultData['results'][0]), 'event not found in result: ' . print_r($searchResultData['results'], true));
304 $resultEventData = $searchResultData['results'][0];
306 $this->_assertJsonEvent($eventData, $resultEventData, 'failed to search event');
310 * #7688: Internal Server Error on calendar search
312 * add period filter if none is given
314 * https://forge.tine20.org/mantisbt/view.php?id=7688
316 public function testSearchEventsWithOutPeriodFilter()
318 $eventData = $this->testCreateRecurEvent();
319 $filter = array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()));
321 $searchResultData = $this->_uit->searchEvents($filter, array());
322 $returnedFilter = $searchResultData['filter'];
323 $this->assertEquals(2, count($returnedFilter), 'Two filters shoud have been returned!');
324 $this->assertTrue($returnedFilter[1]['field'] == 'period' || $returnedFilter[0]['field'] == 'period', 'One returned filter shoud be a period filter');
328 * add period filter if none is given / configure from+until
330 * @see 0009688: allow to configure default period filter in json frontend
332 public function testSearchEventsWithOutPeriodFilterConfiguredFromAndUntil()
334 Calendar_Config::getInstance()->set(Calendar_Config::MAX_JSON_DEFAULT_FILTER_PERIOD_FROM, 12);
336 $filter = array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()));
337 $searchResultData = $this->_uit->searchEvents($filter, array());
339 $now = Tinebase_DateTime::now()->setTime(0,0,0);
340 foreach ($searchResultData['filter'] as $filter) {
341 if ($filter['field'] === 'period') {
342 $this->assertEquals($now->getClone()->subYear(1)->toString(), $filter['value']['from']);
343 $this->assertEquals($now->getClone()->addMonth(1)->toString(), $filter['value']['until']);
349 * testSearchEvents with organizer = me filter
351 * @see #6716: default favorite "me" is not resolved properly
353 public function testSearchEventsWithOrganizerMeFilter()
355 $eventData = $this->testCreateEvent(TRUE);
357 $filter = $this->_getEventFilterArray();
358 $filter[] = array('field' => 'organizer', 'operator' => 'equals', 'value' => Addressbook_Model_Contact::CURRENTCONTACT);
360 $searchResultData = $this->_uit->searchEvents($filter, array());
361 $this->assertTrue(! empty($searchResultData['results']));
362 $resultEventData = $searchResultData['results'][0];
363 $this->_assertJsonEvent($eventData, $resultEventData, 'failed to search event');
365 // check organizer filter resolving
366 $organizerfilter = $searchResultData['filter'][2];
367 $this->assertTrue(is_array($organizerfilter['value']), 'organizer should be resolved: ' . print_r($organizerfilter, TRUE));
368 $this->assertEquals(Tinebase_Core::getUser()->contact_id, $organizerfilter['value']['id']);
372 * testSearchEventsWithSharedContainerFilter
374 * @see 0011968: shared calendars filter leads to sql error with pgsql
376 public function testSearchEventsWithSharedContainerFilter()
378 $filter = $this->_getEventFilterArray();
379 $pathFilterValue = array("path" => "/shared");
380 $filter[0]['value'] = $pathFilterValue;
381 $searchResultData = $this->_uit->searchEvents($filter, array());
383 $this->assertEquals($pathFilterValue, $searchResultData['filter'][0]['value'], print_r($searchResultData['filter'], true));
387 * search event with alarm
389 public function testSearchEventsWithAlarm()
391 $eventData = $this->_getEventWithAlarm(TRUE)->toArray();
392 $persistentEventData = $this->_uit->saveEvent($eventData);
394 $searchResultData = $this->_uit->searchEvents($this->_getEventFilterArray(), array());
395 $this->assertTrue(! empty($searchResultData['results']));
396 $resultEventData = $searchResultData['results'][0];
398 $this->_assertJsonEvent($persistentEventData, $resultEventData, 'failed to search event with alarm');
402 * testSetAttenderStatus
404 public function testSetAttenderStatus()
406 $eventData = $this->testCreateEvent();
407 $numAttendee = count($eventData['attendee']);
408 $eventData['attendee'][$numAttendee] = array(
409 'user_id' => $this->_getPersonasContacts('pwulf')->getId(),
412 $updatedEventData = $this->_uit->saveEvent($eventData);
413 $pwulf = $this->_findAttender($updatedEventData['attendee'], 'pwulf');
415 // he he, we don't have his authkey, cause json class sorts it out due to rights restrictions.
416 $attendeeBackend = new Calendar_Backend_Sql_Attendee();
417 $pwulf['status_authkey'] = $attendeeBackend->get($pwulf['id'])->status_authkey;
419 $updatedEventData['container_id'] = $updatedEventData['container_id']['id'];
421 $pwulf['status'] = Calendar_Model_Attender::STATUS_ACCEPTED;
422 $this->_uit->setAttenderStatus($updatedEventData, $pwulf, $pwulf['status_authkey']);
424 $loadedEventData = $this->_uit->getEvent($eventData['id']);
425 $loadedPwulf = $this->_findAttender($loadedEventData['attendee'], 'pwulf');
426 $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $loadedPwulf['status']);
430 * testCreateRecurEvent
432 public function testCreateRecurEvent()
434 $eventData = $this->testCreateEvent();
435 $eventData['rrule'] = array(
441 $updatedEventData = $this->_uit->saveEvent($eventData);
442 $this->assertTrue(is_array($updatedEventData['rrule']));
444 return $updatedEventData;
448 * testCreateRecurEventYearly
450 * @see 0010610: yearly event is not shown in week view
452 public function testCreateRecurEventYearly()
454 $eventData = $this->_getEvent()->toArray();
455 $eventData['is_all_day_event'] = true;
456 $eventData['dtstart'] = '2015-01-04 00:00:00';
457 $eventData['dtend'] = '2015-01-04 23:59:59';
458 $eventData['rrule'] = array(
465 $updatedEventData = $this->_uit->saveEvent($eventData);
466 $this->assertTrue(is_array($updatedEventData['rrule']));
469 array('field' => 'container_id', 'operator' => 'equals', 'value' => $eventData['container_id']),
470 array('field' => 'period', 'operator' => 'within', 'value' =>
471 array("from" => '2014-12-29 00:00:00', "until" => '2015-01-05 00:00:00')
474 $searchResultData = $this->_uit->searchEvents($filter, array());
475 $this->assertEquals(1, $searchResultData['totalcount'], 'event not found');
479 * testCreateRecurEventWithRruleUntil
481 * @see 0008906: rrule_until is saved in usertime
483 public function testCreateRecurEventWithRruleUntil()
485 $eventData = $this->testCreateRecurEvent();
486 $localMidnight = Tinebase_DateTime::now()->setTime(23,59,59)->toString();
487 $eventData['rrule']['until'] = $localMidnight;
488 //$eventData['rrule']['freq'] = 'WEEKLY';
490 $updatedEventData = $this->_uit->saveEvent($eventData);
491 $this->assertGreaterThanOrEqual($localMidnight, $updatedEventData['rrule']['until']);
494 $calbackend = new Calendar_Backend_Sql();
495 $db = $calbackend->getAdapter();
496 $select = $db->select();
497 $select->from(array($calbackend->getTableName() => $calbackend->getTablePrefix() . $calbackend->getTableName()), array('rrule_until', 'rrule'))->limit(1);
498 $select->where($db->quoteIdentifier($calbackend->getTableName() . '.id') . ' = ?', $updatedEventData['id']);
500 $stmt = $db->query($select);
501 $queryResult = $stmt->fetch();
503 // echo Tinebase_Core::getUserTimezone();
504 // echo date_default_timezone_get();
506 $midnightInUTC = new Tinebase_DateTime($queryResult['rrule_until']);
507 $this->assertEquals(Tinebase_DateTime::now()->setTime(23,59,59)->toString(), $midnightInUTC->setTimezone(Tinebase_Core::getUserTimezone(), TRUE)->toString());
511 * testCreateRecurEventWithConstrains
513 public function testCreateRecurEventWithConstrains()
515 /* $conflictEventData = */$this->testCreateEvent();
517 $eventData = $this->testCreateEvent();
518 $eventData['rrule'] = array(
523 $eventData['rrule_constraints'] = array(
524 array('field' => 'container_id', 'operator' => 'in', 'value' => array($eventData['container_id'])),
527 $updatedEventData = $this->_uit->saveEvent($eventData);
529 $this->assertTrue(is_array($updatedEventData['rrule_constraints']));
530 $this->assertEquals('personal',$updatedEventData['rrule_constraints'][0]['value'][0]['type'], 'filter is not resolved');
531 $this->assertEquals(1, count($updatedEventData['exdate']));
532 $this->assertEquals('2009-03-25 06:00:00', $updatedEventData['exdate'][0]);
534 return $updatedEventData;
538 * testSearchRecuringIncludes
540 public function testSearchRecuringIncludes()
542 $recurEvent = $this->testCreateRecurEvent();
544 $from = $recurEvent['dtstart'];
545 $until = new Tinebase_DateTime($from);
546 $until->addWeek(5)->addHour(10);
547 $until = $until->get(Tinebase_Record_Abstract::ISO8601LONG);
550 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
551 array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)),
554 $searchResultData = $this->_uit->searchEvents($filter, array());
556 $this->assertEquals(6, $searchResultData['totalcount']);
558 // test appending tags to recurring instances
559 $this->assertTrue(isset($searchResultData['results'][4]['tags'][0]), 'tags not set: ' . print_r($searchResultData['results'][4], true));
560 $this->assertEquals('phpunit-', substr($searchResultData['results'][4]['tags'][0]['name'], 0, 8));
562 return $searchResultData;
566 * testSearchRecuringIncludesAndSort
568 public function testSearchRecuringIncludesAndSort()
570 $recurEvent = $this->testCreateRecurEvent();
572 $from = $recurEvent['dtstart'];
573 $until = new Tinebase_DateTime($from);
574 $until->addWeek(5)->addHour(10);
575 $until = $until->get(Tinebase_Record_Abstract::ISO8601LONG);
578 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
579 array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)),
582 $searchResultData = $this->_uit->searchEvents($filter, array('sort' => 'dtstart', 'dir' => 'DESC'));
584 $this->assertEquals(6, $searchResultData['totalcount']);
587 $this->assertEquals('2009-04-29 06:00:00', $searchResultData['results'][0]['dtstart']);
588 $this->assertEquals('2009-04-22 06:00:00', $searchResultData['results'][1]['dtstart']);
592 * testCreateRecurException
594 public function testCreateRecurException()
596 $recurSet = Tinebase_Helper::array_value('results', $this->testSearchRecuringIncludes());
598 $persistentException = $recurSet[1];
599 $persistentException['summary'] = 'go sleeping';
601 // create persistent exception
602 $this->_uit->createRecurException($persistentException, FALSE, FALSE);
604 // create exception date
605 $updatedBaseEvent = Calendar_Controller_Event::getInstance()->getRecurBaseEvent(new Calendar_Model_Event($recurSet[2]));
606 $recurSet[2]['last_modified_time'] = $updatedBaseEvent->last_modified_time;
607 $this->_uit->createRecurException($recurSet[2], TRUE, FALSE);
609 // delete all following (including this)
610 $updatedBaseEvent = Calendar_Controller_Event::getInstance()->getRecurBaseEvent(new Calendar_Model_Event($recurSet[4]));
611 $recurSet[4]['last_modified_time'] = $updatedBaseEvent->last_modified_time;
612 $this->_uit->createRecurException($recurSet[4], TRUE, TRUE);
614 $from = $recurSet[0]['dtstart'];
615 $until = new Tinebase_DateTime($from);
616 $until->addWeek(5)->addHour(10);
617 $until = $until->get(Tinebase_Record_Abstract::ISO8601LONG);
620 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
621 array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)),
624 $searchResultData = $this->_uit->searchEvents($filter, array('sort' => 'dtstart'));
626 // we deleted one and cropped
627 $this->assertEquals(3, count($searchResultData['results']));
629 $summaryMap = array();
630 foreach ($searchResultData['results'] as $event) {
631 $summaryMap[$event['dtstart']] = $event['summary'];
633 $this->assertTrue((isset($summaryMap['2009-04-01 06:00:00']) || array_key_exists('2009-04-01 06:00:00', $summaryMap)));
634 $this->assertEquals($persistentException['summary'], $summaryMap['2009-04-01 06:00:00']);
636 return $searchResultData;
640 * testCreateRecurExceptionWithOtherUser
642 * @see 0008172: displaycontainer_id not set when recur exception is created
644 public function testCreateRecurExceptionWithOtherUser()
646 $recurSet = Tinebase_Helper::array_value('results', $this->testSearchRecuringIncludes());
648 // create persistent exception (just status update)
649 $persistentException = $recurSet[1];
650 $scleverAttender = $this->_findAttender($persistentException['attendee'], 'sclever');
651 $attendeeBackend = new Calendar_Backend_Sql_Attendee();
652 $status_authkey = $attendeeBackend->get($scleverAttender['id'])->status_authkey;
653 $scleverAttender['status'] = Calendar_Model_Attender::STATUS_ACCEPTED;
654 $scleverAttender['status_authkey'] = $status_authkey;
655 foreach ($persistentException['attendee'] as $key => $attender) {
656 if ($attender['id'] === $scleverAttender['id']) {
657 $persistentException['attendee'][$key] = $scleverAttender;
662 // sclever has only READ grant
663 Tinebase_Container::getInstance()->setGrants($this->_getTestCalendar(), new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(array(
664 'account_id' => $this->_getTestUser()->getId(),
665 'account_type' => 'user',
666 Tinebase_Model_Grants::GRANT_READ => true,
667 Tinebase_Model_Grants::GRANT_ADD => true,
668 Tinebase_Model_Grants::GRANT_EDIT => true,
669 Tinebase_Model_Grants::GRANT_DELETE => true,
670 Tinebase_Model_Grants::GRANT_PRIVATE => true,
671 Tinebase_Model_Grants::GRANT_ADMIN => true,
672 Tinebase_Model_Grants::GRANT_FREEBUSY => true,
674 'account_id' => $this->_getPersona('sclever')->getId(),
675 'account_type' => 'user',
676 Tinebase_Model_Grants::GRANT_READ => true,
677 Tinebase_Model_Grants::GRANT_FREEBUSY => true,
680 $unittestUser = Tinebase_Core::getUser();
681 Tinebase_Core::set(Tinebase_Core::USER, $this->_getPersona('sclever'));
683 // create persistent exception
684 $createdException = $this->_uit->createRecurException($persistentException, FALSE, FALSE);
685 Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
687 $sclever = $this->_findAttender($createdException['attendee'], 'sclever');
688 $defaultCal = $this->_getPersonasDefaultCals('sclever');
689 $this->assertEquals('Susan Clever', $sclever['user_id']['n_fn']);
690 $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $sclever['status'], 'status mismatch: ' . print_r($sclever, TRUE));
691 $this->assertTrue(is_array($sclever['displaycontainer_id']));
692 $this->assertEquals($defaultCal['id'], $sclever['displaycontainer_id']['id']);
696 * testUpdateRecurSeries
698 public function testUpdateRecurSeries()
700 $recurSet = Tinebase_Helper::array_value('results', $this->testSearchRecuringIncludes());
702 $persistentException = $recurSet[1];
703 $persistentException['summary'] = 'go sleeping';
704 $persistentException['dtstart'] = '2009-04-01 20:00:00';
705 $persistentException['dtend'] = '2009-04-01 20:30:00';
707 // create persistent exception
708 $recurResult = $this->_uit->createRecurException($persistentException, FALSE, FALSE);
710 // update recurseries
711 $someRecurInstance = $recurSet[2];
712 $someRecurInstance['summary'] = 'go fishing';
713 $someRecurInstance['dtstart'] = '2009-04-08 10:00:00';
714 $someRecurInstance['dtend'] = '2009-04-08 12:30:00';
716 $someRecurInstance['seq'] = 3;
717 $this->_uit->updateRecurSeries($someRecurInstance, FALSE, FALSE);
719 $searchResultData = $this->_searchRecurSeries($recurSet[0]);
720 $this->assertEquals(6, count($searchResultData['results']));
722 $summaryMap = array();
723 foreach ($searchResultData['results'] as $event) {
724 $summaryMap[$event['dtstart']] = $event['summary'];
727 $this->assertTrue((isset($summaryMap['2009-04-01 20:00:00']) || array_key_exists('2009-04-01 20:00:00', $summaryMap)));
728 $this->assertEquals('go sleeping', $summaryMap['2009-04-01 20:00:00']);
730 $fishings = array_keys($summaryMap, 'go fishing');
731 $this->assertEquals(5, count($fishings));
732 foreach ($fishings as $dtstart) {
733 $this->assertEquals('10:00:00', substr($dtstart, -8), 'all fishing events should start at 10:00');
738 * testUpdateRecurSeriesRruleWeekly
740 * Changing the weekday for a whole series should change the rrule as well
742 public function testUpdateRecurSeriesRruleWeekly()
744 // dtstart = 2009-03-25 => WE
745 $eventToCreate = $this->_getEvent();
746 $eventToCreate->rrule = 'FREQ=WEEKLY;INTERVAL=1;BYDAY=WE';
747 $createdEvent = $this->_eventController->create($eventToCreate);
749 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
751 $updatedEvent['dtstart'] = '2009-04-02 12:00:00';
752 $updatedEvent['dtend'] = '2009-04-02 13:00:00';
754 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
756 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
758 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
759 $this->assertEquals('TH', $newBaseEvent['rrule']['byday'], 'Rrule should have changed');
763 * testUpdateRecurSeriesRruleMonthly
765 * Changing the weekday for a whole series should change the rrule as well
767 public function testUpdateRecurSeriesRruleMonthly()
769 // dtstart = 2009-03-25 => WE
770 $eventToCreate = $this->_getEvent();
771 $eventToCreate->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYDAY=4WE';
772 $createdEvent = $this->_eventController->create($eventToCreate);
774 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
776 $updatedEvent['dtstart'] = '2009-04-02 12:00:00';
777 $updatedEvent['dtend'] = '2009-04-02 13:00:00';
779 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
781 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
783 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
784 $this->assertEquals('1TH', $newBaseEvent['rrule']['byday'], 'Rrule should have changed');
786 $this->_eventController->delete(array($createdEvent->getId()));
790 * testUpdateRecurSeriesRruleMonthly1
792 * Changing the weekday for a whole series should change the rrule as well
794 public function testUpdateRecurSeriesRruleMonthly1()
796 // dtstart = 2009-03-25 => WE
797 $eventToCreate = $this->_getEvent();
798 $eventToCreate->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYDAY=4WE';
799 $createdEvent = $this->_eventController->create($eventToCreate);
801 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
803 $updatedEvent['dtstart'] = '2009-03-26 12:00:00';
804 $updatedEvent['dtend'] = '2009-03-26 13:00:00';
806 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
808 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
810 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
811 $this->assertEquals('4TH', $newBaseEvent['rrule']['byday'], 'Rrule should have changed');
813 $this->_eventController->delete(array($createdEvent->getId()));
817 * testUpdateRecurSeriesRruleMonthly2
819 * Changing the weekday for a whole series should change the rrule as well
821 public function testUpdateRecurSeriesRruleMonthly2()
823 // dtstart = 2009-03-25 => WE
824 $eventToCreate = $this->_getEvent();
825 $eventToCreate->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYDAY=-1WE';
826 $createdEvent = $this->_eventController->create($eventToCreate);
828 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
830 $updatedEvent['dtstart'] = '2009-03-26 12:00:00';
831 $updatedEvent['dtend'] = '2009-03-26 13:00:00';
833 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
835 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
837 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
838 $this->assertEquals('-1TH', $newBaseEvent['rrule']['byday'], 'Rrule should have changed');
840 $this->_eventController->delete(array($createdEvent->getId()));
844 * testUpdateRecurSeriesRruleMonthly3
846 * Changing the weekday for a whole series should change the rrule as well
848 public function testUpdateRecurSeriesRruleMonthly3()
850 // dtstart = 2009-03-25 => WE
851 $eventToCreate = $this->_getEvent();
852 $eventToCreate->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYDAY=-1WE';
853 $createdEvent = $this->_eventController->create($eventToCreate);
855 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
857 $updatedEvent['dtstart'] = '2009-04-02 12:00:00';
858 $updatedEvent['dtend'] = '2009-04-02 13:00:00';
860 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
862 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
864 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
865 $this->assertEquals('1TH', $newBaseEvent['rrule']['byday'], 'Rrule should have changed');
867 $this->_eventController->delete(array($createdEvent->getId()));
871 * testUpdateRecurSeriesRruleMonthly4
873 * Changing the weekday for a whole series should change the rrule as well
875 public function testUpdateRecurSeriesRruleMonthly4()
877 // dtstart = 2009-03-25 => WE
878 $eventToCreate = $this->_getEvent();
879 $eventToCreate->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYDAY=-1WE';
880 $createdEvent = $this->_eventController->create($eventToCreate);
882 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
884 $updatedEvent['dtstart'] = '2009-03-24 12:00:00';
885 $updatedEvent['dtend'] = '2009-03-24 13:00:00';
887 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
889 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
891 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
892 $this->assertEquals('4TU', $newBaseEvent['rrule']['byday'], 'Rrule should have changed');
894 $this->_eventController->delete(array($createdEvent->getId()));
898 * testUpdateRecurSeriesRruleMonthly5
900 * Changing the weekday for a whole series should change the rrule as well
902 public function testUpdateRecurSeriesRruleMonthly5()
904 // dtstart = 2009-03-25 => WE
905 $eventToCreate = $this->_getEvent();
906 $eventToCreate->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYDAY=-1TU'; // <- this is a mismatch! so nothing should happen
907 $createdEvent = $this->_eventController->create($eventToCreate);
909 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
911 $updatedEvent['dtstart'] = '2009-03-24 12:00:00';
912 $updatedEvent['dtend'] = '2009-03-24 13:00:00';
914 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
916 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
918 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
919 $this->assertEquals('-1TU', $newBaseEvent['rrule']['byday'], 'Rrule should not have changed');
921 $this->_eventController->delete(array($createdEvent->getId()));
925 * testUpdateRecurSeriesRruleMonthly6
927 * Changing the weekday for a whole series should change the rrule as well
929 public function testUpdateRecurSeriesRruleMonthly6()
931 // dtstart = 2009-03-25 => WE
932 $eventToCreate = $this->_getEvent();
933 $eventToCreate->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=25';
934 $createdEvent = $this->_eventController->create($eventToCreate);
936 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
938 $updatedEvent['dtstart'] = '2009-04-02 12:00:00';
939 $updatedEvent['dtend'] = '2009-04-02 13:00:00';
941 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
943 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
945 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
946 $this->assertEquals('2', $newBaseEvent['rrule']['bymonthday'], 'Rrule should have changed');
948 $this->_eventController->delete(array($createdEvent->getId()));
952 * testUpdateRecurSeriesRruleMonthly7
954 * Changing the weekday for a whole series should change the rrule as well
956 public function testUpdateRecurSeriesRruleMonthly7()
958 // dtstart = 2009-03-25 => WE
959 $eventToCreate = $this->_getEvent();
960 $eventToCreate->rrule = 'FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=26'; // this is a mismatch, so nothing should happen
961 $createdEvent = $this->_eventController->create($eventToCreate);
963 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
965 $updatedEvent['dtstart'] = '2009-04-02 12:00:00';
966 $updatedEvent['dtend'] = '2009-04-02 13:00:00';
968 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
970 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
972 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
973 $this->assertEquals('26', $newBaseEvent['rrule']['bymonthday'], 'Rrule should not have changed');
975 $this->_eventController->delete(array($createdEvent->getId()));
979 * testUpdateRecurSeriesRruleYearly
981 * Changing the weekday for a whole series should change the rrule as well
983 public function testUpdateRecurSeriesRruleYearly()
985 // dtstart = 2009-03-25 => WE
986 $eventToCreate = $this->_getEvent();
987 $eventToCreate->rrule = 'FREQ=YEARLY;INTERVAL=1;BYMONTH=3;BYMONTHDAY=26'; // this is a mismatch, so nothing should happen
988 $createdEvent = $this->_eventController->create($eventToCreate);
990 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
992 $updatedEvent['dtstart'] = '2009-04-02 12:00:00';
993 $updatedEvent['dtend'] = '2009-04-02 13:00:00';
995 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
997 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
999 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
1000 $this->assertEquals('26', $newBaseEvent['rrule']['bymonthday'], 'Rrule should not have changed');
1001 $this->assertEquals('3', $newBaseEvent['rrule']['bymonth'], 'Rrule should not have changed');
1003 $this->_eventController->delete(array($createdEvent->getId()));
1007 * testUpdateRecurSeriesRruleYearly1
1009 * Changing the weekday for a whole series should change the rrule as well
1011 public function testUpdateRecurSeriesRruleYearly1()
1013 // dtstart = 2009-03-25 => WE
1014 $eventToCreate = $this->_getEvent();
1015 $eventToCreate->rrule = 'FREQ=YEARLY;INTERVAL=1;BYMONTH=3;BYMONTHDAY=25';
1016 $createdEvent = $this->_eventController->create($eventToCreate);
1018 $updatedEvent = $this->_uit->getEvent($createdEvent->getId());
1019 // change day => 1TH
1020 $updatedEvent['dtstart'] = '2009-04-02 12:00:00';
1021 $updatedEvent['dtend'] = '2009-04-02 13:00:00';
1023 $oldBaseEvent = $this->_uit->getEvent($createdEvent->getId());
1025 $newBaseEvent = $this->_uit->updateRecurSeries($updatedEvent, FALSE);
1027 $this->assertNotEquals($oldBaseEvent['dtstart'], $newBaseEvent['dtstart'], 'dtstart of baseEvent should have changed');
1028 $this->assertEquals('2', $newBaseEvent['rrule']['bymonthday'], 'Rrule should have changed');
1029 $this->assertEquals('4', $newBaseEvent['rrule']['bymonth'], 'Rrule should have changed');
1031 $this->_eventController->delete(array($createdEvent->getId()));
1036 * search updated recur set
1038 * @param array $firstInstance
1041 protected function _searchRecurSeries($firstInstance)
1043 $from = $firstInstance['dtstart'];
1044 $until = new Tinebase_DateTime($from);
1045 $until->addWeek(5)->addHour(10);
1046 $until = $until->get(Tinebase_Record_Abstract::ISO8601LONG);
1049 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
1050 array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)),
1053 return $this->_uit->searchEvents($filter, array());
1057 * testUpdateRecurExceptionsFromSeriesOverDstMove
1061 public function testUpdateRecurExceptionsFromSeriesOverDstMove()
1064 * 1. create recur event 1 day befor dst move
1065 * 2. create an exception and exdate
1066 * 3. move dtstart from 1 over dst boundary
1067 * 4. test recurid and exdate by calculating series
1072 * testDeleteRecurSeries
1074 public function testDeleteRecurSeries()
1076 $recurSet = Tinebase_Helper::array_value('results', $this->testSearchRecuringIncludes());
1078 $persistentException = $recurSet[1];
1079 $persistentException['summary'] = 'go sleeping';
1081 // create persistent exception
1082 $this->_uit->createRecurException($persistentException, FALSE, FALSE);
1084 // delete recurseries
1085 $someRecurInstance = $persistentException = $recurSet[2];
1086 $this->_uit->deleteRecurSeries($someRecurInstance);
1088 $from = $recurSet[0]['dtstart'];
1089 $until = new Tinebase_DateTime($from);
1090 $until->addWeek(5)->addHour(10);
1091 $until = $until->get(Tinebase_Record_Abstract::ISO8601LONG);
1094 array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_getTestCalendar()->getId()),
1095 array('field' => 'period', 'operator' => 'within', 'value' => array('from' => $from, 'until' => $until)),
1098 $searchResultData = $this->_uit->searchEvents($filter, array());
1100 $this->assertEquals(0, count($searchResultData['results']));
1104 * testMeAsAttenderFilter
1106 public function testMeAsAttenderFilter()
1108 $eventData = $this->testCreateEvent(TRUE);
1110 $filter = $this->_getEventFilterArray();
1111 $filter[] = array('field' => 'attender' , 'operator' => 'equals', 'value' => array(
1112 'user_type' => Calendar_Model_Attender::USERTYPE_USER,
1113 'user_id' => Addressbook_Model_Contact::CURRENTCONTACT,
1116 $searchResultData = $this->_uit->searchEvents($filter, array());
1117 $this->assertTrue(! empty($searchResultData['results']));
1118 $resultEventData = $searchResultData['results'][0];
1120 $this->_assertJsonEvent($eventData, $resultEventData, 'failed to filter for me as attender');
1124 * testFreeBusyCleanup
1126 * @return array event data
1128 public function testFreeBusyCleanup()
1130 // give fb grants from sclever
1131 $scleverCal = Tinebase_Container::getInstance()->getContainerById($this->_getPersonasDefaultCals('sclever'));
1132 Tinebase_Container::getInstance()->setGrants($scleverCal->getId(), new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(array(
1133 'account_id' => $this->_getPersona('sclever')->getId(),
1134 'account_type' => 'user',
1135 Tinebase_Model_Grants::GRANT_READ => true,
1136 Tinebase_Model_Grants::GRANT_ADD => true,
1137 Tinebase_Model_Grants::GRANT_EDIT => true,
1138 Tinebase_Model_Grants::GRANT_DELETE => true,
1139 Tinebase_Model_Grants::GRANT_PRIVATE => true,
1140 Tinebase_Model_Grants::GRANT_ADMIN => true,
1141 Tinebase_Model_Grants::GRANT_FREEBUSY => true,
1143 'account_id' => $this->_getTestUser()->getId(),
1144 'account_type' => 'user',
1145 Tinebase_Model_Grants::GRANT_FREEBUSY => true,
1148 Tinebase_Core::set(Tinebase_Core::USER, $this->_getPersona('sclever'));
1149 $eventData = $this->_getEvent()->toArray();
1150 unset($eventData['organizer']);
1151 $eventData['container_id'] = $scleverCal->getId();
1152 $eventData['attendee'] = array(array(
1153 'user_id' => $this->_getPersonasContacts('sclever')->getId()
1155 $eventData['organizer'] = $this->_getPersonasContacts('sclever')->getId();
1156 $eventData = $this->_uit->saveEvent($eventData);
1157 $filter = $this->_getEventFilterArray($this->_getPersonasDefaultCals('sclever')->getId());
1158 $filter[] = array('field' => 'summary', 'operator' => 'equals', 'value' => 'Wakeup');
1159 $searchResultData = $this->_uit->searchEvents($filter, array());
1160 $this->assertTrue(! empty($searchResultData['results']), 'expected event in search result (search by sclever): '
1161 . print_r($eventData, TRUE) . 'search filter: ' . print_r($filter, TRUE));
1163 Tinebase_Core::set(Tinebase_Core::USER, $this->_getTestUser());
1164 $searchResultData = $this->_uit->searchEvents($filter, array());
1165 $this->assertTrue(! empty($searchResultData['results']), 'expected (freebusy cleanup) event in search result: '
1166 . print_r($eventData, TRUE) . 'search filter: ' . print_r($filter, TRUE));
1167 $eventData = $searchResultData['results'][0];
1169 $this->assertFalse((isset($eventData['summary']) || array_key_exists('summary', $eventData)), 'summary not empty: ' . print_r($eventData, TRUE));
1170 $this->assertFalse((isset($eventData['description']) || array_key_exists('description', $eventData)), 'description not empty');
1171 $this->assertFalse((isset($eventData['tags']) || array_key_exists('tags', $eventData)), 'tags not empty');
1172 $this->assertFalse((isset($eventData['notes']) || array_key_exists('notes', $eventData)), 'notes not empty');
1173 $this->assertFalse((isset($eventData['attendee']) || array_key_exists('attendee', $eventData)), 'attendee not empty');
1174 $this->assertFalse((isset($eventData['organizer']) || array_key_exists('organizer', $eventData)), 'organizer not empty');
1175 $this->assertFalse((isset($eventData['alarms']) || array_key_exists('alarms', $eventData)), 'alarms not empty');
1181 * testFreeBusyCleanupOfNotes
1183 * @see 0009918: shared (only free/busy) calendar is showing event details within the history tab.
1185 public function testFreeBusyCleanupOfNotes()
1187 $eventData = $this->testFreeBusyCleanup();
1189 $tinebaseJson = new Tinebase_Frontend_Json();
1190 $filter = array(array(
1191 'field' => "record_model",
1192 'operator' => "equals",
1193 'value' => "Calendar_Model_Event"
1195 'field' => 'record_id',
1196 'operator' => 'equals',
1197 'value' => $eventData['id']
1199 $notes = $tinebaseJson->searchNotes($filter, array());
1201 $this->assertEquals(0, $notes['totalcount'], 'should not find any notes of record');
1202 $this->assertEquals(0, count($notes['results']), 'should not find any notes of record');
1206 * test deleting container and the containing events
1207 * #6704: events do not disappear when shared calendar got deleted
1208 * https://forge.tine20.org/mantisbt/view.php?id=6704
1210 public function testDeleteContainerAndEvents()
1212 $fe = new Tinebase_Frontend_Json_Container();
1213 $container = $fe->addContainer('Calendar', 'testdeletecontacts', Tinebase_Model_Container::TYPE_SHARED, '');
1214 // test if model is set automatically
1215 $this->assertEquals($container['model'], 'Calendar_Model_Event');
1217 $date = new Tinebase_DateTime();
1218 $event = new Calendar_Model_Event(array(
1220 'dtend' => $date->subHour(1),
1221 'summary' => 'bla bla',
1222 'class' => 'PUBLIC',
1223 'transp' => 'OPAQUE',
1224 'container_id' => $container['id'],
1225 'organizer' => Tinebase_Core::getUser()->contact_id
1227 $event = Calendar_Controller_Event::getInstance()->create($event);
1228 $this->assertEquals($container['id'], $event->container_id);
1230 $fe->deleteContainer($container['id']);
1232 $e = new Exception('dummy');
1234 $cb = new Calendar_Backend_Sql();
1235 $deletedEvent = $cb->get($event->getId(), true);
1236 // record should be deleted
1237 $this->assertEquals($deletedEvent->is_deleted, 1);
1240 Calendar_Controller_Event::getInstance()->get($event->getId(), $container['id']);
1241 $this->fail('The expected exception was not thrown');
1242 } catch (Tinebase_Exception_NotFound $e) {
1245 // record should not be found
1246 $this->assertEquals($e->getMessage(), 'Calendar_Model_Event record with id '.$event->getId().' not found!');
1250 * compare expected event data with test event
1252 * @param array $expectedEventData
1253 * @param array $eventData
1254 * @param string $msg
1256 protected function _assertJsonEvent($expectedEventData, $eventData, $msg)
1258 $this->assertEquals($expectedEventData['summary'], $eventData['summary'], $msg . ': failed to create/load event');
1260 // assert effective grants are set
1261 $this->assertEquals((bool) $expectedEventData[Tinebase_Model_Grants::GRANT_EDIT], (bool) $eventData[Tinebase_Model_Grants::GRANT_EDIT], $msg . ': effective grants mismatch');
1262 // container, assert attendee, tags
1263 $this->assertEquals($expectedEventData['dtstart'], $eventData['dtstart'], $msg . ': dtstart mismatch');
1264 $this->assertTrue(is_array($eventData['container_id']), $msg . ': failed to "resolve" container');
1265 $this->assertTrue(is_array($eventData['container_id']['account_grants']), $msg . ': failed to "resolve" container account_grants');
1266 $this->assertGreaterThan(0, count($eventData['attendee']));
1267 $this->assertEquals(count($eventData['attendee']), count($expectedEventData['attendee']), $msg . ': failed to append attendee');
1268 $this->assertTrue(is_array($eventData['attendee'][0]['user_id']), $msg . ': failed to resolve attendee user_id');
1269 // NOTE: due to sorting isshues $eventData['attendee'][0] may be a non resolvable container (due to rights restrictions)
1270 $this->assertTrue(is_array($eventData['attendee'][0]['displaycontainer_id']) || (isset($eventData['attendee'][1]) && is_array($eventData['attendee'][1]['displaycontainer_id'])), $msg . ': failed to resolve attendee displaycontainer_id');
1271 $this->assertEquals(count($expectedEventData['tags']), count($eventData['tags']), $msg . ': failed to append tag');
1272 $this->assertEquals(count($expectedEventData['notes']), count($eventData['notes']), $msg . ': failed to create note or wrong number of notes');
1274 if ((isset($expectedEventData['alarms']) || array_key_exists('alarms', $expectedEventData))) {
1275 $this->assertTrue((isset($eventData['alarms']) || array_key_exists('alarms', $eventData)), ': failed to create alarms');
1276 $this->assertEquals(count($expectedEventData['alarms']), count($eventData['alarms']), $msg . ': failed to create correct number of alarms');
1277 if (count($expectedEventData['alarms']) > 0) {
1278 $this->assertTrue((isset($eventData['alarms'][0]['minutes_before']) || array_key_exists('minutes_before', $eventData['alarms'][0])));
1286 * @param array $attendeeData
1287 * @param string $name
1290 protected function _findAttender($attendeeData, $name) {
1291 $attenderData = false;
1292 $searchedId = $this->_getPersonasContacts($name)->getId();
1294 foreach ($attendeeData as $key => $attender) {
1295 if ($attender['user_type'] == Calendar_Model_Attender::USERTYPE_USER) {
1296 if (is_array($attender['user_id']) && (isset($attender['user_id']['id']) || array_key_exists('id', $attender['user_id']))) {
1297 if ($attender['user_id']['id'] == $searchedId) {
1298 $attenderData = $attendeeData[$key];
1304 return $attenderData;
1308 * test filter with hidden group -> should return empty result
1310 * @see 0006934: setting a group that is hidden from adb as attendee filter throws exception
1312 public function testHiddenGroupFilter()
1314 $hiddenGroup = new Tinebase_Model_Group(array(
1315 'name' => 'hiddengroup',
1316 'description' => 'hidden group',
1317 'visibility' => Tinebase_Model_Group::VISIBILITY_HIDDEN
1319 $hiddenGroup = Admin_Controller_Group::getInstance()->create($hiddenGroup);
1320 $this->_groupIdsToDelete[] = $hiddenGroup->getId();
1322 $filter = array(array(
1323 'field' => 'attender',
1324 'operator' => 'equals',
1326 'user_id' => $hiddenGroup->list_id,
1327 'user_type' => 'group',
1330 $result = $this->_uit->searchEvents($filter, array());
1331 $this->assertEquals(0, $result['totalcount']);
1335 * testExdateDeleteAll
1337 * @see 0007382: allow to edit / delete the whole series / thisandfuture when editing/deleting recur exceptions
1339 public function testExdateDeleteAll()
1341 $events = $this->testCreateRecurException();
1342 $exception = $this->_getException($events);
1343 $this->_uit->deleteEvents(array($exception['id']), Calendar_Model_Event::RANGE_ALL);
1345 $search = $this->_uit->searchEvents($events['filter'], NULL);
1346 $this->assertEquals(0, $search['totalcount'], 'all events should be deleted: ' . print_r($search,TRUE));
1350 * get exception from event resultset
1352 * @param array $events
1353 * @param integer $index (1 = picks first, 2 = picks second, ...)
1354 * @return array|NULL
1356 protected function _getException($events, $index = 1)
1360 foreach ($events['results'] as $event) {
1361 if (! empty($event['recurid'])) {
1363 if ($index === $found) {
1373 * testExdateDeleteThis
1375 * @see 0007382: allow to edit / delete the whole series / thisandfuture when editing/deleting recur exceptions
1377 public function testExdateDeleteThis()
1379 $events = $this->testCreateRecurException();
1380 $exception = $this->_getException($events);
1381 $this->_uit->deleteEvents(array($exception['id']));
1383 $search = $this->_uit->searchEvents($events['filter'], NULL);
1384 $this->assertEquals(2, $search['totalcount'], '2 events should remain: ' . print_r($search,TRUE));
1388 * testExdateDeleteThisAndFuture
1390 * @see 0007382: allow to edit / delete the whole series / thisandfuture when editing/deleting recur exceptions
1392 public function testExdateDeleteThisAndFuture()
1394 $events = $this->testCreateRecurException();
1395 $exception = $this->_getException($events, 1);
1396 $this->_uit->deleteEvents(array($exception['id']), Calendar_Model_Event::RANGE_THISANDFUTURE);
1398 $search = $this->_uit->searchEvents($events['filter'], NULL);
1399 $this->assertEquals(1, $search['totalcount'], '1 event should remain: ' . print_r($search,TRUE));
1403 * assert grant handling
1405 public function testSaveResource($grants = array('readGrant' => true,'editGrant' => true))
1407 $resoureData = array(
1408 'name' => Tinebase_Record_Abstract::generateUID(),
1409 'email' => Tinebase_Record_Abstract::generateUID() . '@unittest.com',
1410 'grants' => array(array_merge($grants, array(
1411 'account_id' => Tinebase_Core::getUser()->getId(),
1412 'account_type' => 'user'
1416 $resoureData = $this->_uit->saveResource($resoureData);
1417 $this->assertTrue(is_array($resoureData['grants']), 'grants are not resolved');
1419 return $resoureData;
1423 * assert only resources with read grant are returned if the user has no manage right
1425 public function testSearchResources()
1427 $readableResoureData = $this->testSaveResource();
1428 $nonReadableResoureData = $this->testSaveResource(array());
1431 array('field' => 'name', 'operator' => 'in', 'value' => array(
1432 $readableResoureData['name'],
1433 $nonReadableResoureData['name'],
1437 $searchResultManager = $this->_uit->searchResources($filer, array());
1438 $this->assertEquals(2, count($searchResultManager['results']), 'with manage grants all records should be found');
1440 // steal manage right and reactivate container checks
1441 $roleManager = Tinebase_Acl_Roles::getInstance();
1442 $roleManager->deleteRoles(array(
1443 $roleManager->getRoleByName('manager role')->getId(),
1444 $roleManager->getRoleByName('admin role')->getId()
1447 Calendar_Controller_Resource::getInstance()->doContainerACLChecks(TRUE);
1449 $searchResult = $this->_uit->searchResources($filer, array());
1450 $this->assertEquals(1, count($searchResult['results']), 'without manage grants only one record should be found');
1454 * assert status authkey with editGrant
1455 * assert stauts can be set with editGrant
1456 * assert stauts can't be set without editGrant
1458 public function testResourceAttendeeGrants()
1460 $editableResoureData = $this->testSaveResource();
1461 $nonEditableResoureData = $this->testSaveResource(array('readGrant'));
1463 $event = $this->_getEvent(TRUE);
1464 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
1466 'user_type' => Calendar_Model_Attender::USERTYPE_RESOURCE,
1467 'user_id' => $editableResoureData['id'],
1468 'status' => Calendar_Model_Attender::STATUS_ACCEPTED
1471 'user_type' => Calendar_Model_Attender::USERTYPE_RESOURCE,
1472 'user_id' => $nonEditableResoureData['id'],
1473 'status' => Calendar_Model_Attender::STATUS_ACCEPTED
1477 $persistentEventData = $this->_uit->saveEvent($event->toArray());
1479 $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $persistentEventData['attendee']);
1480 $this->assertEquals(1, count($attendee->filter('status', Calendar_Model_Attender::STATUS_ACCEPTED)), 'one accepted');
1481 $this->assertEquals(1, count($attendee->filter('status', Calendar_Model_Attender::STATUS_NEEDSACTION)), 'one needs action');
1483 $this->assertEquals(1, count($attendee->filter('status_authkey', '/[a-z0-9]+/', TRUE)), 'one has authkey');
1485 $attendee->status = Calendar_Model_Attender::STATUS_TENTATIVE;
1486 $persistentEventData['attendee'] = $attendee->toArray();
1488 $updatedEventData = $this->_uit->saveEvent($persistentEventData);
1489 $attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $updatedEventData['attendee']);
1490 $this->assertEquals(1, count($attendee->filter('status', Calendar_Model_Attender::STATUS_TENTATIVE)), 'one tentative');
1494 * testExdateUpdateAllSummary
1496 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1498 public function testExdateUpdateAllSummary()
1500 $events = $this->testCreateRecurException();
1501 $exception = $this->_getException($events, 1);
1502 $exception['summary'] = 'new summary';
1504 $event = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL);
1506 $search = $this->_uit->searchEvents($events['filter'], NULL);
1507 foreach ($search['results'] as $event) {
1508 $this->assertEquals('new summary', $event['summary']);
1513 * testExdateUpdateAllDtStart
1515 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1519 public function testExdateUpdateAllDtStart()
1521 $events = $this->testCreateRecurException();
1522 $exception = $this->_getException($events, 1);
1523 $exception['dtstart'] = '2009-04-01 08:00:00';
1524 $exception['dtend'] = '2009-04-01 08:15:00';
1526 $event = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL);
1528 $search = $this->_uit->searchEvents($events['filter'], NULL);
1529 foreach ($search['results'] as $event) {
1530 $this->assertContains('08:00:00', $event['dtstart'], 'wrong dtstart: ' . print_r($event, TRUE));
1531 $this->assertContains('08:15:00', $event['dtend']);
1536 * testExdateUpdateThis
1538 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1540 public function testExdateUpdateThis()
1542 $events = $this->testCreateRecurException();
1543 $exception = $this->_getException($events, 1);
1544 $exception['summary'] = 'exception';
1546 $event = $this->_uit->saveEvent($exception);
1547 $this->assertEquals('exception', $event['summary']);
1549 // check for summary (only changed in one event)
1550 $search = $this->_uit->searchEvents($events['filter'], NULL);
1551 foreach ($search['results'] as $event) {
1552 if (! empty($event['recurid']) && ! preg_match('/^fakeid/', $event['id'])) {
1553 $this->assertEquals('exception', $event['summary'], 'summary not changed in exception: ' . print_r($event, TRUE));
1555 $this->assertEquals('Wakeup', $event['summary']);
1561 * testExdateUpdateThisAndFuture
1563 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1565 public function testExdateUpdateThisAndFuture()
1567 $events = $this->testCreateRecurException();
1568 $exception = $this->_getException($events, 1);
1569 $exception['summary'] = 'new summary';
1571 $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE);
1572 $this->assertEquals('new summary', $updatedEvent['summary'], 'summary not changed in exception: ' . print_r($updatedEvent, TRUE));
1574 $search = $this->_uit->searchEvents($events['filter'], NULL);
1575 foreach ($search['results'] as $event) {
1576 if ($event['dtstart'] >= $updatedEvent['dtstart']) {
1577 $this->assertEquals('new summary', $event['summary'], 'summary not changed in event: ' . print_r($event, TRUE));
1579 $this->assertEquals('Wakeup', $event['summary']);
1585 * testExdateUpdateThisAndFutureWithRruleUntil
1587 * @see 0008244: "rrule until must not be before dtstart" when updating recur exception (THISANDFUTURE)
1589 public function testExdateUpdateThisAndFutureWithRruleUntil()
1591 $events = $this->testCreateRecurException();
1593 $exception = $this->_getException($events, 1);
1594 $exception['dtstart'] = Tinebase_DateTime::now()->toString();
1595 $exception['dtend'] = Tinebase_DateTime::now()->addHour(1)->toString();
1598 $updatedEvent = $this->_uit->saveEvent($exception);
1599 // try to update the whole series
1600 $updatedEvent['summary'] = 'new summary';
1601 $updatedEvent = $this->_uit->saveEvent($updatedEvent, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE);
1603 $this->assertEquals('new summary', $updatedEvent['summary'], 'summary not changed in event: ' . print_r($updatedEvent, TRUE));
1607 * testExdateUpdateThisAndFutureRemoveAttendee
1609 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1611 public function testExdateUpdateThisAndFutureRemoveAttendee()
1613 $events = $this->testCreateRecurException();
1614 $exception = $this->_getException($events, 1);
1615 // remove susan from attendee
1616 unset($exception['attendee'][0]);
1618 $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE);
1619 $this->assertEquals(1, count($updatedEvent['attendee']), 'attender not removed from exception: ' . print_r($updatedEvent, TRUE));
1621 $search = $this->_uit->searchEvents($events['filter'], NULL);
1622 foreach ($search['results'] as $event) {
1623 if ($event['dtstart'] >= $updatedEvent['dtstart']) {
1624 $this->assertEquals(1, count($event['attendee']), 'attendee count mismatch: ' . print_r($event, TRUE));
1626 $this->assertEquals(2, count($event['attendee']), 'attendee count mismatch: ' . print_r($event, TRUE));
1632 * testExdateUpdateAllAddAttendee
1634 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1636 public function testExdateUpdateAllAddAttendee()
1638 $events = $this->testCreateRecurException();
1639 $exception = $this->_getException($events, 1);
1641 $exception['attendee'][] = $this->_getUserTypeAttender();
1643 $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL);
1644 $this->assertEquals(3, count($updatedEvent['attendee']), 'attender not added to exception: ' . print_r($updatedEvent, TRUE));
1646 $search = $this->_uit->searchEvents($events['filter'], NULL);
1647 foreach ($search['results'] as $event) {
1648 $this->assertEquals(3, count($event['attendee']), 'attendee count mismatch: ' . print_r($event, TRUE));
1653 * testExdateUpdateThisAndFutureChangeDtstart
1655 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1657 public function testExdateUpdateThisAndFutureChangeDtstart()
1659 $events = $this->testCreateRecurException();
1660 $exception = $this->_getException($events, 1);
1661 $exception['dtstart'] = '2009-04-01 08:00:00';
1662 $exception['dtend'] = '2009-04-01 08:15:00';
1664 $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_THISANDFUTURE);
1666 $search = $this->_uit->searchEvents($events['filter'], NULL);
1667 foreach ($search['results'] as $event) {
1668 if ($event['dtstart'] >= $updatedEvent['dtstart']) {
1669 $this->assertContains('08:00:00', $event['dtstart'], 'wrong dtstart: ' . print_r($event, TRUE));
1670 $this->assertContains('08:15:00', $event['dtend']);
1672 $this->assertContains('06:00:00', $event['dtstart'], 'wrong dtstart: ' . print_r($event, TRUE));
1673 $this->assertContains('06:15:00', $event['dtend']);
1679 * testExdateUpdateAllWithModlog
1680 * - change base event, then update all
1682 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1683 * @see 0009340: fix Calendar_JsonTests::testExdateUpdateAllWithModlog*
1685 public function testExdateUpdateAllWithModlog()
1687 $this->markTestSkipped('this test is broken: see 0009340: fix Calendar_JsonTests::testExdateUpdateAllWithModlog*');
1689 $events = $this->testCreateRecurException();
1690 $baseEvent = $events['results'][0];
1691 $exception = $this->_getException($events, 1);
1693 $baseEvent['summary'] = 'Get up, lazyboy!';
1694 $baseEvent = $this->_uit->saveEvent($baseEvent);
1697 $exception['summary'] = 'new summary';
1698 $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL);
1700 $search = $this->_uit->searchEvents($events['filter'], NULL);
1701 foreach ($search['results'] as $event) {
1702 if ($event['dtstart'] == $updatedEvent['dtstart']) {
1703 $this->assertEquals('new summary', $event['summary'], 'Recur exception should have the new summary');
1705 $this->assertEquals('Get up, lazyboy!', $event['summary'], 'Wrong summary in base/recur event: ' . print_r($event, TRUE));
1711 * testExdateUpdateAllWithModlogAddAttender
1712 * - change base event, then update all
1714 * @see 0007690: allow to update the whole series / thisandfuture when updating recur exceptions
1715 * @see 0007826: add attendee changes to modlog
1716 * @see 0009340: fix Calendar_JsonTests::testExdateUpdateAllWithModlog*
1718 public function testExdateUpdateAllWithModlogAddAttender()
1720 $this->markTestSkipped('0009340: fix Calendar_JsonTests::testExdateUpdateAllWithModlogAddAttender');
1722 $events = $this->testCreateRecurException();
1723 $baseEvent = $events['results'][0];
1724 $exception = $this->_getException($events, 1);
1727 $baseEvent['attendee'][] = $this->_getUserTypeAttender();
1728 $baseEvent = $this->_uit->saveEvent($baseEvent);
1729 $this->assertEquals(3, count($baseEvent['attendee']), 'Attendee count mismatch in baseEvent: ' . print_r($baseEvent, TRUE));
1732 // check recent changes (needs to contain attendee change)
1733 $exdate = Calendar_Controller_Event::getInstance()->get($exception['id']);
1734 $recentChanges = Tinebase_Timemachine_ModificationLog::getInstance()->getModifications('Calendar', $baseEvent['id'], NULL, 'Sql', $exdate->creation_time);
1735 $changedAttributes = Tinebase_Timemachine_ModificationLog::getModifiedAttributes($recentChanges);
1736 $this->assertGreaterThan(2, count($changedAttributes), 'Did not get all recent changes: ' . print_r($recentChanges->toArray(), TRUE));
1737 $this->assertTrue(in_array('attendee', $changedAttributes), 'Attendee change missing: ' . print_r($recentChanges->toArray(), TRUE));
1739 $exception['attendee'][] = $this->_getUserTypeAttender('unittestnotexists@example.com');
1740 $updatedEvent = $this->_uit->saveEvent($exception, FALSE, Calendar_Model_Event::RANGE_ALL);
1742 $search = $this->_uit->searchEvents($events['filter'], NULL);
1743 foreach ($search['results'] as $event) {
1744 if ($event['dtstart'] == $updatedEvent['dtstart']) {
1745 $this->assertEquals(3, count($event['attendee']), 'Attendee count mismatch in exdate: ' . print_r($event, TRUE));
1747 $this->assertEquals(4, count($event['attendee']), 'Attendee count mismatch: ' . print_r($event, TRUE));
1753 * testConcurrentAttendeeChangeAdd
1755 * @see 0008078: concurrent attendee change should be merged
1757 public function testConcurrentAttendeeChangeAdd()
1759 $eventData = $this->testCreateEvent();
1760 $numAttendee = count($eventData['attendee']);
1761 $eventData['attendee'][$numAttendee] = array(
1762 'user_id' => $this->_getPersonasContacts('pwulf')->getId(),
1764 $this->_uit->saveEvent($eventData);
1766 $eventData['attendee'][$numAttendee] = array(
1767 'user_id' => $this->_getPersonasContacts('jsmith')->getId(),
1769 $event = $this->_uit->saveEvent($eventData);
1771 $this->assertEquals(4, count($event['attendee']), 'both new attendee (pwulf + jsmith) should be added: ' . print_r($event['attendee'], TRUE));
1775 * testConcurrentAttendeeChangeRemove
1777 * @see 0008078: concurrent attendee change should be merged
1779 public function testConcurrentAttendeeChangeRemove()
1781 $eventData = $this->testCreateEvent();
1782 $currentAttendee = $eventData['attendee'];
1783 unset($eventData['attendee'][1]);
1784 $event = $this->_uit->saveEvent($eventData);
1786 $eventData['attendee'] = $currentAttendee;
1787 $numAttendee = count($eventData['attendee']);
1788 $eventData['attendee'][$numAttendee] = array(
1789 'user_id' => $this->_getPersonasContacts('pwulf')->getId(),
1791 $event = $this->_uit->saveEvent($eventData);
1793 $this->assertEquals(2, count($event['attendee']), 'one attendee should added and one removed: ' . print_r($event['attendee'], TRUE));
1797 * testConcurrentAttendeeChangeUpdate
1799 * @see 0008078: concurrent attendee change should be merged
1801 public function testConcurrentAttendeeChangeUpdate()
1803 $eventData = $this->testCreateEvent();
1804 $currentAttendee = $eventData['attendee'];
1805 $adminIndex = ($eventData['attendee'][0]['user_id']['n_fn'] === 'Susan Clever') ? 1 : 0;
1806 $eventData['attendee'][$adminIndex]['status'] = Calendar_Model_Attender::STATUS_TENTATIVE;
1807 $event = $this->_uit->saveEvent($eventData);
1809 $loggedMods = Tinebase_Timemachine_ModificationLog::getInstance()->getModificationsBySeq(
1810 Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
1811 new Calendar_Model_Attender($eventData['attendee'][$adminIndex]), 2);
1812 $this->assertEquals(1, count($loggedMods), 'attender modification has not been logged');
1814 $eventData['attendee'] = $currentAttendee;
1815 $scleverIndex = ($adminIndex === 1) ? 0 : 1;
1816 $attendeeBackend = new Calendar_Backend_Sql_Attendee();
1817 $eventData['attendee'][$scleverIndex]['status_authkey'] = $attendeeBackend->get($eventData['attendee'][$scleverIndex]['id'])->status_authkey;
1818 $eventData['attendee'][$scleverIndex]['status'] = Calendar_Model_Attender::STATUS_TENTATIVE;
1819 $event = $this->_uit->saveEvent($eventData);
1821 foreach ($event['attendee'] as $attender) {
1822 $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $attender['status'], 'both attendee status should be TENTATIVE: ' . print_r($attender, TRUE));
1827 * testFreeBusyCheckForExdates
1829 * @see 0008464: freebusy check does not work when creating recur exception
1831 public function testFreeBusyCheckForExdates()
1833 $events = $this->testCreateRecurException();
1834 $exception = $this->_getException($events, 1);
1836 $anotherEvent = $this->_getEvent(TRUE);
1837 $anotherEvent = $this->_uit->saveEvent($anotherEvent->toArray());
1839 $exception['dtstart'] = $anotherEvent['dtstart'];
1840 $exception['dtend'] = $anotherEvent['dtend'];
1843 $event = $this->_uit->saveEvent($exception, TRUE);
1844 $this->fail('Calendar_Exception_AttendeeBusy expected when saving exception: ' . print_r($exception, TRUE));
1845 } catch (Calendar_Exception_AttendeeBusy $ceab) {
1846 $this->assertEquals('Calendar_Exception_AttendeeBusy', get_class($ceab));
1851 * testAddAttachmentToRecurSeries
1853 * @see 0005024: allow to attach external files to records
1855 public function testAddAttachmentToRecurSeries()
1857 $tempFile = $this->_getTempFile();
1858 $recurSet = Tinebase_Helper::array_value('results', $this->testSearchRecuringIncludes());
1859 // update recurseries
1860 $someRecurInstance = $recurSet[2];
1861 $someRecurInstance['attachments'] = array(array('tempFile' => array('id' => $tempFile->getId())));
1862 $someRecurInstance['seq'] = 2;
1863 $this->_uit->updateRecurSeries($someRecurInstance, FALSE, FALSE);
1865 $searchResultData = $this->_searchRecurSeries($recurSet[0]);
1866 foreach ($searchResultData['results'] as $recurInstance) {
1867 $this->assertTrue(isset($recurInstance['attachments']), 'no attachments found in event: ' . print_r($recurInstance, TRUE));
1868 $this->assertEquals(1, count($recurInstance['attachments']));
1869 $attachment = $recurInstance['attachments'][0];
1870 $this->assertEquals('text/plain', $attachment['contenttype'], print_r($attachment, TRUE));
1875 * checks if manipulated dtend and dtstart gets set to the correct values on creating or updating an event
1877 * @see 0009696: time is not grayed out for all-day events
1879 public function testWholedayEventTimes()
1881 $event = $this->_getEvent(TRUE);
1882 $event->is_all_day_event = TRUE;
1884 $event = Calendar_Controller_Event::getInstance()->create($event);
1885 $event->setTimezone(Tinebase_Core::getUserTimezone());
1887 $this->assertEquals('00:00:00', $event->dtstart->format('H:i:s'));
1888 $this->assertEquals('23:59:59', $event->dtend->format('H:i:s'));
1890 $event->dtstart = Tinebase_DateTime::now();
1891 $event->dtend = Tinebase_DateTime::now()->addHour(1);
1893 $event = Calendar_Controller_Event::getInstance()->update($event);
1894 $event->setTimezone(Tinebase_Core::getUserTimezone());
1896 $this->assertEquals('00:00:00', $event->dtstart->format('H:i:s'));
1897 $this->assertEquals('23:59:59', $event->dtend->format('H:i:s'));
1901 * testAttendeeChangeQuantityToInvalid
1903 * @see 9630: sanitize attender quantity
1905 public function testAttendeeChangeQuantityToInvalid()
1907 $eventData = $this->testCreateEvent();
1908 $currentAttendee = $eventData['attendee'];
1909 $eventData['attendee'][1]['quantity'] = '';
1910 $event = $this->_uit->saveEvent($eventData);
1911 $this->assertEquals(1, $event['attendee'][1]['quantity'], 'The invalid quantity should be saved as 1');
1915 * trigger caldav import by json frontend
1917 * @todo use mock as fallback (if server can not be reached by curl)
1918 * @todo get servername from unittest config / skip or mock if no servername found
1920 public function testCalDAVImport()
1922 // Skip if tine20.com.local could not be resolved
1923 if (gethostbyname('tine20.com.local') == 'tine20.com.local') {
1924 $this->markTestSkipped('Can\'t perform test, because instance is not reachable.');
1927 $this->_testNeedsTransaction();
1929 $event = $this->testCreateEvent(/* $now = */ true);
1931 $fe = new Calendar_Frontend_Json();
1932 $testUserCredentials = TestServer::getInstance()->getTestCredentials();
1933 $fe->importRemoteEvents(
1934 'http://tine20.com.local/calendars/' . Tinebase_Core::getUser()->contact_id . '/' . $event['container_id']['id'],
1935 Tinebase_Model_Import::INTERVAL_DAILY,
1937 'container_id' => 'remote_caldav_calendar',
1938 'sourceType' => 'remote_caldav',
1939 'importFileByScheduler' => false,
1940 'allowDuplicateEvents' => true,
1941 'username' => $testUserCredentials['username'],
1942 'password' => $testUserCredentials['password'],
1945 $importScheduler = Tinebase_Controller_ScheduledImport::getInstance();
1946 $record = $importScheduler->runNextScheduledImport();
1948 $container = Tinebase_Container::getInstance()->getContainerByName('Calendar', 'remote_caldav_calendar', Tinebase_Model_Container::TYPE_PERSONAL, Tinebase_Core::getUser()->getId());
1949 $this->_testCalendars[] = $container;
1950 $this->assertTrue($container instanceof Tinebase_Model_Container, 'Container was not created');
1952 $this->assertNotEquals($record, null, 'The import could not start!');
1954 $filter = $this->_getEventFilterArray($container->getId());
1955 $result = $this->_uit->searchEvents($filter, array());
1956 $this->assertEquals(1, $result['totalcount']);
1962 * @see 0009542: load event relations on demand
1964 public function testGetRelations()
1966 $contact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array(
1967 'n_family' => 'Contact for relations test'
1969 $eventData = $this->_getEvent()->toArray();
1970 $eventData['relations'] = array(
1972 'own_model' => 'Calendar_Model_Event',
1973 'own_backend' => 'Sql',
1975 'related_degree' => Tinebase_Model_Relation::DEGREE_SIBLING,
1977 'related_backend' => 'Sql',
1978 'related_id' => $contact->getId(),
1979 'related_model' => 'Addressbook_Model_Contact',
1982 $event = $this->_uit->saveEvent($eventData);
1984 $tfj = new Tinebase_Frontend_Json();
1985 $relations = $tfj->getRelations('Calendar_Model_Event', $event['id']);
1987 $this->assertEquals(1, $relations['totalcount']);
1988 $this->assertEquals($contact->n_fn, $relations['results'][0]['related_record']['n_family'], print_r($relations['results'], true));
1991 public function testGetFreeBusyInfo()
1993 $event = $this->_getEvent();
1994 $event->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
1995 array('user_id' => $this->_getPersonasContacts('sclever')->getId()),
1996 array('user_id' => $this->_getPersonasContacts('pwulf')->getId())
1998 $persistentEvent = $this->_eventController->create($event);
2000 $period = array(array(
2001 'field' => 'period',
2002 'operator' => 'within',
2004 'from' => $persistentEvent->dtstart,
2005 'until' => $persistentEvent->dtend
2009 $fbinfo = $this->_uit->getFreeBusyInfo($persistentEvent->attendee->toArray(), $period);
2010 $this->assertGreaterThanOrEqual(2, count($fbinfo));