improve default attendee for CalDAV/ActiveSync
[tine20] / tests / tine20 / Calendar / Frontend / WebDAV / EventTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     Calendar
6  * @license     http://www.gnu.org/licenses/agpl.html
7  * @copyright   Copyright (c) 2011-2013 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Lars Kneschke <l.kneschke@metaways.de>
9  */
10
11 /**
12  * Test helper
13  */
14 require_once dirname(dirname(dirname(dirname(__FILE__)))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
15
16 /**
17  * Test class for Calendar_Frontend_WebDAV_Event
18  */
19 class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
20 {
21     /**
22      * @var array test objects
23      */
24     protected $objects = array();
25     
26     /**
27      * Runs the test methods of this class.
28      *
29      * @access public
30      * @static
31      */
32     public static function main()
33     {
34         $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Calendar WebDAV Event Tests');
35         PHPUnit_TextUI_TestRunner::run($suite);
36     }
37
38     /**
39      * Sets up the fixture.
40      * This method is called before a test is executed.
41      */
42     public function setUp()
43     {
44         parent::setUp();
45         
46         $this->objects['initialContainer'] = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
47             'name'              => Tinebase_Record_Abstract::generateUID(),
48             'type'              => Tinebase_Model_Container::TYPE_PERSONAL,
49             'backend'           => 'Sql',
50             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
51         )));
52         $this->objects['sharedContainer'] = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
53             'name'              => Tinebase_Record_Abstract::generateUID(),
54             'type'              => Tinebase_Model_Container::TYPE_SHARED,
55             'backend'           => 'Sql',
56             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
57         )));
58         
59         $prefs = new Calendar_Preference();
60         $prefs->setValue(Calendar_Preference::DEFAULTCALENDAR, $this->objects['initialContainer']->getId());
61         
62         $_SERVER['REQUEST_URI'] = 'lars';
63     }
64
65     /**
66      * tear down tests
67      *
68      */
69     public function tearDown()
70     {
71         parent::tearDown();
72         
73         Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
74     }
75     
76     /**
77      * test create event with internal organizer
78      * 
79      * @return Calendar_Frontend_WebDAV_Event
80      */
81     public function testCreateEventWithInternalOrganizer()
82     {
83         if (!isset($_SERVER['HTTP_USER_AGENT'])) {
84             $_SERVER['HTTP_USER_AGENT'] = 'FooBar User Agent';
85         }
86         
87         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
88         
89         $id = Tinebase_Record_Abstract::generateUID();
90         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
91         
92         $record = $event->getRecord();
93
94         $this->assertEquals('New Event', $record->summary);
95         
96         return $event;
97     }
98     
99     /**
100      * test create event with internal organizer
101      * 
102      * @return Calendar_Frontend_WebDAV_Event
103      */
104     public function testCreateEventWithRRule()
105     {
106         if (!isset($_SERVER['HTTP_USER_AGENT'])) {
107             $_SERVER['HTTP_USER_AGENT'] = 'FooBar User Agent';
108         }
109         
110         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning_repeating_yearly.ics');
111         
112         $id = Tinebase_Record_Abstract::generateUID();
113         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
114         
115         $record = $event->getRecord();
116         
117         $this->assertEquals('New Event', $record->summary);
118         $this->assertEquals($record->rrule['bymonthday'], 25);
119         
120         return $event;
121     }
122     
123     /**
124      * test create event with external organizer
125      * 
126      * @return Calendar_Frontend_WebDAV_Event
127      */
128     public function testCreateEventWithExternalOrganizer()
129     {
130         if (!isset($_SERVER['HTTP_USER_AGENT'])) {
131             $_SERVER['HTTP_USER_AGENT'] = 'FooBar User Agent';
132         }
133         
134         $vcalendar = file_get_contents(dirname(__FILE__) . '/../../Import/files/lightning.ics');
135         
136         $id = Tinebase_Record_Abstract::generateUID();
137         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
138         
139         $record = $event->getRecord();
140
141         $this->assertEquals('New Event', $record->summary);
142         
143         return $event;
144     }
145     
146     /**
147      * create an event which already exists on the server
148      * - this happen when the client moves an event to another calendar -> see testMove*
149      * - or when an client processes an iMIP which is not already loaded by CalDAV
150      *
151      * @return Calendar_Frontend_WebDAV_Event
152      */
153     public function testCreateEventWhichExistsAlready()
154     {
155         if (!empty($_SERVER['HTTP_USER_AGENT'])) {
156             $oldUserAgent = $_SERVER['HTTP_USER_AGENT'];
157         }
158         
159         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
160         
161         $existingEvent = $this->testCreateEventWithInternalOrganizer();
162         $existingRecord = $existingEvent->getRecord();
163         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
164         
165         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], $existingEvent->getRecord()->uid . '.ics', $vcalendar);
166         
167         if (isset($oldUserAgent)) {
168             $_SERVER['HTTP_USER_AGENT'] = $oldUserAgent;
169         }
170         
171         $record = $event->getRecord();
172         
173         $this->assertEquals($existingRecord->getId(), $record->getId(), 'event got duplicated');
174     }
175     
176     /**
177      * test create repeating event
178      *
179      * @return Calendar_Frontend_WebDAV_Event
180      */
181     public function testCreateRepeatingEvent()
182     {
183         if (!isset($_SERVER['HTTP_USER_AGENT'])) {
184             $_SERVER['HTTP_USER_AGENT'] = 'FooBar User Agent';
185         }
186     
187         $vcalendarStream = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning_repeating_daily.ics');
188         
189         $id = Tinebase_Record_Abstract::generateUID();
190         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendarStream);
191         $this->_checkExdate($event);
192         
193         // check rrule_until normalisation
194         $record = $event->getRecord();
195         $this->assertEquals('2011-10-30 22:59:59', $record->rrule_until->toString(), 'rrule_until not normalised');
196         $this->assertEquals('2011-10-30 22:59:59', $record->rrule->until->toString(), 'rrule->until not normalised');
197         
198         return $event;
199     }
200     
201     /**
202      * check event exdate
203      * 
204      * @param Calendar_Frontend_WebDAV_Event $event
205      */
206     protected function _checkExdate(Calendar_Frontend_WebDAV_Event $event)
207     {
208         $record = $event->getRecord();
209         $exdate = $record->exdate[0];
210         $this->assertEquals('New Event', $record->summary);
211         $this->assertEquals(Tinebase_Core::getUser()->contact_id, $exdate->organizer,
212             'organizer mismatch, expected :' . print_r(Addressbook_Controller_Contact::getInstance()->get(Tinebase_Core::getUser()->contact_id)->toArray(), TRUE) .
213             'got: ' . print_r(Addressbook_Controller_Contact::getInstance()->get($exdate->organizer)->toArray(), TRUE));
214         $this->assertTrue(in_array(Tinebase_Core::getUser()->contact_id, $exdate->attendee->user_id), 'user contact id not found in exdate attendee: ' . print_r($exdate->attendee->toArray(), TRUE));
215         foreach ($exdate->attendee as $attender) {
216             $this->assertTrue(! empty($attender->displaycontainer_id), 'displaycontainer_id not set for attender: ' . print_r($attender->toArray(), TRUE));
217         }
218     }
219     
220     /**
221      * testCreateRepeatingEventAndPutExdate
222      * 
223      * @see 0008172: displaycontainer_id not set when recur exception is created
224      */
225     public function testCreateRepeatingEventAndPutExdate()
226     {
227         if (!empty($_SERVER['HTTP_USER_AGENT'])) {
228             $oldUserAgent = $_SERVER['HTTP_USER_AGENT'];
229         }
230         
231         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
232         
233         $vcalendarStream = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning_repeating_weekly.ics');
234         $id = '353de608-4b50-41e6-9f6c-35889584fe8d';
235         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendarStream);
236         $existingEvent = $event->getRecord();
237         
238         // put exception
239         $vcalendarStreamException = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning_repeating_weekly_exception.ics');
240         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $existingEvent);
241         $event->put($vcalendarStreamException);
242         
243         $this->_checkExdate($event);
244         
245         return $event;
246     }
247     
248     /**
249      * #7388: Wrong container id's in calendar (maybe CalDAV related)
250      */
251     public function testCreateEventInviteInternalAttendee()
252     {
253         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
254         
255         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/event_with_persona_attendee.ics');
256         
257         $id = Tinebase_Record_Abstract::generateUID();
258         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
259         
260         $event = Calendar_Controller_Event::getInstance()->get($id);
261         $pwulf = $event->attendee->filter('user_id', $this->_personasContacts['pwulf']->getId())->getFirstRecord();
262         
263         $this->assertTrue($pwulf !== null, 'could not find pwulf in attendee: ' . print_r($event->attendee->toArray(), true));
264         $this->assertEquals($this->_personasDefaultCals['pwulf']->getId(), $pwulf->displaycontainer_id, 'event not in pwulfs personal calendar');
265     }
266     
267     /**
268      * _testEventMissingAttendee helper
269      * 
270      * @param Tinebase_Model_Container $container
271      * @param Calendar_Model_Attender $assertionAttendee
272      * @param boolean $assertMissing
273      */
274     public function _testEventMissingAttendee($container, $assertionAttendee, $assertMissing = false)
275     {
276         $not = $assertMissing ? '' : 'not ';
277         $assertFn = $assertMissing ? 'assertFalse' : 'assertTrue';
278         
279         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
280         
281         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/apple_caldendar_repeating.ics');
282         
283         
284         $id = Tinebase_Record_Abstract::generateUID();
285
286         $event = Calendar_Frontend_WebDAV_Event::create($container, "$id.ics", $vcalendar);
287         
288         $this->$assertFn(!! Calendar_Model_Attender::getAttendee($event->getRecord()->attendee, $assertionAttendee),
289                 "attendee has {$not}been added: " . print_r($event->getRecord()->attendee->toArray(), true));
290         $this->$assertFn(!! Calendar_Model_Attender::getAttendee($event->getRecord()->exdate->getFirstRecord()->attendee, $assertionAttendee),
291                 "attendee has {$not}been added to exdate");
292         
293         // Simulate OSX which updates w.o. fetching first
294         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/apple_caldendar_repeating.ics', 'r');
295         
296         $event = new Calendar_Frontend_WebDAV_Event($container, $event->getRecord()->getId());
297         $event->put($vcalendarStream);
298         
299         $this->$assertFn(!! Calendar_Model_Attender::getAttendee($event->getRecord()->attendee, $assertionAttendee),
300                 "attendee has {$not}been preserved: " . print_r($event->getRecord()->attendee->toArray(), true));
301         $this->$assertFn(!! Calendar_Model_Attender::getAttendee($event->getRecord()->exdate->getFirstRecord()->attendee, $assertionAttendee),
302                 "attendee has {$not}been preserved in exdate");
303         
304         // create new exception from client w.o. fetching first
305         Calendar_Controller_Event::getInstance()->purgeRecords(TRUE);
306         Calendar_Controller_Event::getInstance()->delete($event->getRecord()->exdate->getFirstRecord());
307         Calendar_Controller_Event::getInstance()->purgeRecords(FALSE);
308         
309         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/apple_caldendar_repeating.ics', 'r');
310         
311         $event = new Calendar_Frontend_WebDAV_Event($container, $event->getRecord()->getId());
312         $event->put($vcalendarStream);
313         
314         $this->$assertFn(!! Calendar_Model_Attender::getAttendee($event->getRecord()->attendee, $assertionAttendee),
315                 "attendee has {$not}been created in exdate");
316         $this->$assertFn(!! Calendar_Model_Attender::getAttendee($event->getRecord()->exdate->getFirstRecord()->attendee, $assertionAttendee),
317                 "attendee has {$not}been created in exdate");
318     }
319     
320     /**
321      * testEventMissingAttendeeOwnCalendar
322      *
323      * validate test user is added for event in own container
324      */
325     public function testEventMissingAttendeeOwnCalendar()
326     {
327         $this->_testEventMissingAttendee($this->objects['initialContainer'], new Calendar_Model_Attender(array(
328             'user_type'    => Calendar_Model_Attender::USERTYPE_USER,
329             'user_id'      => Tinebase_Core::getUser()->contact_id,
330         )));
331     }
332     /**
333      * testEventMissingAttendeeOtherCalendar
334      *
335      * validate calendar owner is added for event in other user container
336      */
337     public function testEventMissingAttendeeOtherCalendar()
338     {
339         $egt = new Calendar_Controller_EventGrantsTests();
340         $egt->setup();
341         
342         $pwulfPersonalCal = $this->_personasDefaultCals['sclever'];
343         $pwulf = new Calendar_Model_Attender(array(
344             'user_type'    => Calendar_Model_Attender::USERTYPE_USER,
345             'user_id'      => $this->_personasContacts['sclever']->getId(),
346         ));
347         
348         $this->_testEventMissingAttendee($pwulfPersonalCal, $pwulf);
349     }
350     
351     /**
352      * testEventMissingAttendeeSharedCalendar
353      * 
354      * validate no attendee is implicitly added for shared calendars
355      */
356     public function testEventMissingAttendeeSharedCalendar()
357     {
358         $this->markTestSkipped('not yet active');
359         $this->_testEventMissingAttendee($this->objects['sharedContainer'], new Calendar_Model_Attender(array(
360                 'user_type'    => Calendar_Model_Attender::USERTYPE_USER,
361                 'user_id'      => Tinebase_Core::getUser()->contact_id,
362         )), true);
363     }
364     
365     public function testFilterRepeatingException()
366     {
367         // create event in shared calendar test user is attendee
368         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
369         $vcalendarStream = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning_repeating_daily.ics');
370         $id = Tinebase_Record_Abstract::generateUID();
371         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendarStream);
372         
373         // decline exception -> no implicit fallout as exception is still in initialContainer via displaycal
374         $exception = $event->getRecord()->exdate->filter('is_deleted', 0)->getFirstRecord();
375         $exception->attendee[0]->status = Calendar_Model_Attender::STATUS_DECLINED;
376         $updatedException = Calendar_Controller_Event::getInstance()->update($exception);
377         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $event->getRecord()->getId());
378         $vcalendar = stream_get_contents($event->get());
379         $this->assertContains('DTSTART;TZID=Europe/Berlin:20111008T130000', $vcalendar, 'exception must not be implicitly deleted');
380         
381         // delete attendee from exception -> implicit fallout exception is not longer in displaycal
382         $exception = $event->getRecord()->exdate->filter('is_deleted', 0)->getFirstRecord();
383         $exception->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender');
384         $updatedException = Calendar_Controller_Event::getInstance()->update($exception);
385         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $event->getRecord()->getId());
386         $vcalendar = stream_get_contents($event->get());
387         $this->assertContains('EXDATE:20111008T080000Z', $vcalendar, 'exception must be implicitly deleted from event ' . print_r($event->getRecord()->toArray(), TRUE));
388         
389         // save back event -> implicit delete must not be deleted on server
390         $event->put($vcalendar);
391         $event = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], $event->getRecord()->getId());
392         $vcalendar = stream_get_contents($event->get());
393         $this->assertContains('DTSTART;TZID=Europe/Berlin:20111008T130000', $vcalendar, 'exception must not be implicitly deleted after update');
394     }
395     
396     /**
397      * test get vcard
398      * @depends testCreateEventWithInternalOrganizer
399      */
400     public function testGetVCalendar()
401     {
402         $event = $this->testCreateEventWithInternalOrganizer();
403         
404         $vcalendar = stream_get_contents($event->get());
405         
406         //var_dump($vcalendar);
407         
408         $this->assertContains('SUMMARY:New Event', $vcalendar);
409         $this->assertContains('ORGANIZER;CN=', $vcalendar);
410     }
411     
412     /**
413      * test get vcard
414      */
415     public function testGetRepeatingVCalendar()
416     {
417         $event = $this->testCreateRepeatingEvent();
418     
419         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $event->getRecord()->getId());
420         
421         $vcalendar = stream_get_contents($event->get());
422         #var_dump($vcalendar);
423         $this->assertContains('SUMMARY:New Event', $vcalendar);
424         $this->assertContains('EXDATE:20111005T080000Z', $vcalendar);
425         $this->assertContains('RECURRENCE-ID;TZID=Europe/Berlin:20111008T100000', $vcalendar);
426         $this->assertContains('TRIGGER;VALUE=DURATION:-PT1H30M', $vcalendar); // base alarm
427         $this->assertContains('TRIGGER;VALUE=DURATION:-PT1H15M', $vcalendar); // exception alarm
428     }
429     
430     /**
431      * test updating existing event
432      */
433     public function testPutEventFromThunderbird()
434     {
435         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
436         
437         $event = $this->testCreateEventWithInternalOrganizer();
438         
439         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
440         
441         $event->put($vcalendarStream);
442         
443         $record = $event->getRecord();
444         
445         $this->assertEquals('New Event', $record->summary);
446         $this->assertTrue(! empty($record->attendee[0]["status_authkey"]));
447     }
448     
449     /**
450      * test updating existing event
451      */
452     public function testPutEventWithExternalOrganizer()
453     {
454         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
455         
456         $event = $this->testCreateEventWithExternalOrganizer();
457         
458         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
459         
460         $event->put($vcalendarStream);
461         
462         $record = $event->getRecord();
463         
464         $this->assertEquals('New Event', $record->summary);
465     }
466     
467     /**
468      * test updating existing event
469      */
470     public function testPutEventFromMacOsX()
471     {
472         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
473         
474         $event = $this->testCreateEventWithInternalOrganizer();
475     
476         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
477     
478         $event->put($vcalendarStream);
479     
480         $record = $event->getRecord();
481     
482         $this->assertEquals('New Event', $record->summary);
483     }
484     
485     /**
486      * test deleting attachment from existing event
487      */
488     public function testDeleteAttachment()
489     {
490         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
491         
492         $event = $this->createEventWithAttachment(2);
493         
494         // remove agenda.html
495         $clone = clone $event;
496         $attachments = $clone->getRecord()->attachments;
497         $attachments->removeRecord($attachments->filter('name', 'agenda.html')->getFirstRecord());
498         $event->put($clone->get());
499         
500         // assert agenda2.html exists
501         $record = $event->getRecord();
502         $this->assertEquals(1, $record->attachments->count());
503         $this->assertEquals('agenda2.html', $record->attachments->getFirstRecord()->name);
504     }
505     
506     /**
507      * test updating existing event
508      */
509     public function testPutEventFromGenericClient()
510     {
511         $_SERVER['HTTP_USER_AGENT'] = 'FooBar User Agent';
512         
513         $event = $this->testCreateEventWithInternalOrganizer();
514         
515         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
516         
517         $this->setExpectedException('Sabre\DAV\Exception\Forbidden');
518         
519         $event->put($vcalendarStream);
520         
521         $record = $event->getRecord();
522         
523         $this->assertEquals('New Event', $record->summary);
524     }
525     
526     public function testPutEventMultipleAlarms()
527     {
528         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
529         
530         $event = $this->testCreateEventWithInternalOrganizer();
531         
532         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/event_with_multiple_alarm.ics', 'r');
533         
534         $event->put($vcalendarStream);
535         
536         $record = $event->getRecord();
537         
538         $this->assertEquals('3', count($record->alarms));
539     }
540     
541     /**
542      * test get name of vcard
543      */
544     public function testGetNameOfEvent()
545     {
546         $event = $this->testCreateEventWithInternalOrganizer();
547         
548         $record = $event->getRecord();
549         
550         $this->assertEquals($event->getName(), $record->getId() . '.ics');
551     }
552     
553     /**
554      * move event orig container shared -> personal
555      */
556     public function testMoveOriginEvent()
557     {
558         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
559         
560         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
561         
562         $id = Tinebase_Record_Abstract::generateUID();
563         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
564         
565         // move event (origin container)
566         Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", stream_get_contents($event->get()));
567         $oldEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
568         $oldEvent->delete();
569         
570         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
571         $this->assertEquals($this->objects['initialContainer']->getId(), $loadedEvent->getRecord()->container_id, 'origin container not updated');
572         
573     }
574     
575     /**
576      * (organizer) move originpersonal -> originshared
577      * 
578      * NOTE: this is the hard case, as a attendee reference stays in personal which must not be deleted
579      * 
580      * solution: prohibit deleting from displaycal when origcal != displaycal
581      *  Exception: implicit decline if curruser owner of displaycal && ! organizer 
582      */
583     public function testMoveOriginPersonalToShared()
584     {
585         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
586         
587         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
588         
589         $id = Tinebase_Record_Abstract::generateUID();
590         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
591         
592         // move event origin to shared (origin and display were the same)
593         Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", stream_get_contents($event->get()));
594         $oldEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
595         $oldEvent->delete();
596         
597         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
598         $this->assertEquals($this->objects['sharedContainer']->getId(), $loadedEvent->getRecord()->container_id, 'origin container not updated');
599         
600         $ownAttendee = Calendar_Model_Attender::getOwnAttender($loadedEvent->getRecord()->attendee);
601         $this->assertEquals($this->objects['initialContainer']->getId(), $ownAttendee->displaycontainer_id, 'display container must not change');
602         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $ownAttendee->status, 'event must not be declined: ' . print_r($ownAttendee->toArray(), TRUE));
603     }
604     
605     /**
606      * move event displaycal onedisplaycal -> otherdisplaycal
607      */
608     public function testMoveDisplayEvent()
609     {
610         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
611         
612         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
613         
614         $id = Tinebase_Record_Abstract::generateUID();
615         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
616         $ownAttendee = Calendar_Model_Attender::getOwnAttender($event->getRecord()->attendee);
617         $displayCalendar = Tinebase_Container::getInstance()->getContainerById($ownAttendee->displaycontainer_id);
618         
619         $personalCalendar = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
620             'name'              => Tinebase_Record_Abstract::generateUID(),
621             'type'              => Tinebase_Model_Container::TYPE_PERSONAL,
622             'backend'           => 'Sql',
623             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
624         )));
625         
626         // move event (displaycontainer)
627         $displayCalendarEvent = new Calendar_Frontend_WebDAV_Event($displayCalendar, "$id.ics");
628         Calendar_Frontend_WebDAV_Event::create($personalCalendar, "$id.ics", stream_get_contents($displayCalendarEvent->get()));
629         $oldEvent = new Calendar_Frontend_WebDAV_Event($displayCalendar, "$id.ics");
630         $oldEvent->delete();
631         
632         $loadedEvent = new Calendar_Frontend_WebDAV_Event($displayCalendar, "$id.ics");
633         $this->assertEquals($this->objects['sharedContainer']->getId(), $loadedEvent->getRecord()->container_id, 'origin container must not be updated');
634         
635         $ownAttendee = Calendar_Model_Attender::getOwnAttender($loadedEvent->getRecord()->attendee);
636         $this->assertEquals($personalCalendar->getId(), $ownAttendee->displaycontainer_id, 'display container not changed');
637         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $ownAttendee->status, 'event must not be declined');
638     }
639     
640     public function testGetAlarm()
641     {
642         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
643         
644         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
645         
646         $id = Tinebase_Record_Abstract::generateUID();
647         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
648         
649         $personalEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
650         $sharedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
651         
652         $personalVCalendar = stream_get_contents($personalEvent->get());
653         $sharedVCalendar = stream_get_contents($sharedEvent->get());
654         #var_dump($personalVCalendar);
655         #var_dump($sharedVCalendar);
656         $this->assertNotContains('X-MOZ-LASTACK;VALUE=DATE-TIME:21', $personalVCalendar, $personalVCalendar);
657         $this->assertContains('X-MOZ-LASTACK;VALUE=DATE-TIME:21', $sharedVCalendar, $sharedVCalendar);
658     }
659     
660     public function testGetNoAlarmAsNonAttendee()
661     {
662         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
663         
664         $vcalendar = file_get_contents(dirname(__FILE__) . '/../../Import/files/lightning.ics');
665         
666         $egt = new Calendar_Controller_EventGrantsTests();
667         $egt->setup();
668         
669         $pwulfPersonalCal = $this->_personasDefaultCals['sclever'];
670         $id = Tinebase_Record_Abstract::generateUID();
671         $event = Calendar_Frontend_WebDAV_Event::create($pwulfPersonalCal, "$id.ics", $vcalendar);
672         
673         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
674         $ics = stream_get_contents($loadedEvent->get());
675         
676         $this->assertNotContains('BEGIN:VALARM', $ics, $ics);
677     }
678     
679     public function testDeleteOrignEvent()
680     {
681         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
682         
683         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/event_with_custom_alarm.ics');
684         $vcalendar = preg_replace('#DTSTART;TZID=Europe/Berlin:20120214T100000#', 'DTSTART;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->format('Ymd\THis'), $vcalendar);
685         $vcalendar = preg_replace('#DTEND;TZID=Europe/Berlin:20120214T140000#', 'DTEND;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(1)->format('Ymd\THis'), $vcalendar);
686         
687         $id = Tinebase_Record_Abstract::generateUID();
688         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
689         
690         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
691         $loadedEvent->delete();
692         
693         $this->setExpectedException('Tinebase_Exception_NotFound');
694         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
695         $loadedEvent->getRecord();
696     }
697     
698     public function testDeleteImplicitDecline()
699     {
700         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
701         
702         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/event_with_custom_alarm.ics');
703         $vcalendar = preg_replace('#DTSTART;TZID=Europe/Berlin:20120214T100000#', 'DTSTART;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->format('Ymd\THis'), $vcalendar);
704         $vcalendar = preg_replace('#DTEND;TZID=Europe/Berlin:20120214T140000#', 'DTEND;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(1)->format('Ymd\THis'), $vcalendar);
705         
706         $id = Tinebase_Record_Abstract::generateUID();
707         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
708         
709         $pwulf = new Calendar_Model_Attender(array(
710             'user_type'  => Calendar_Model_Attender::USERTYPE_USER,
711             'user_id'    => array_value('pwulf', Zend_Registry::get('personas'))->contact_id
712         ));
713         
714         $pwulfAttendee = Calendar_Model_Attender::getAttendee($event->getRecord()->attendee, $pwulf);
715         $this->assertTrue($pwulfAttendee !== null, 'could not find pwulf in attendee');
716         $pwulfPersonalCal = Tinebase_Container::getInstance()->getContainerById($pwulfAttendee->displaycontainer_id);
717         $pwulfPersonalEvent = new Calendar_Frontend_WebDAV_Event($pwulfPersonalCal, "$id.ics");
718         $pwulfPersonalEvent->delete();
719         
720         $pwulfPersonalEvent = new Calendar_Frontend_WebDAV_Event($pwulfPersonalCal, "$id.ics");
721         $pwulfAttendee = Calendar_Model_Attender::getAttendee($pwulfPersonalEvent->getRecord()->attendee, $pwulf);
722         $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $pwulfAttendee->status, 'event must be declined for pwulf');
723         $this->assertEquals(0, $pwulfPersonalEvent->getRecord()->is_deleted, 'event must not be deleted');
724     }
725     
726     /**
727      * NOTE: As noted in {@see testMoveOriginPersonalToShared} we can't delete/decline for organizer in his
728      *       personal cal because a move personal->shared would delete/decline the event.
729      *       
730      *       To support intensional delete/declines we allow the delte/decline only if ther is some
731      *       time between this and the last update
732      */
733     public function testDeleteImplicitDeclineOrganizer()
734     {
735         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
736         
737         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
738         $vcalendar = preg_replace('#DTSTART;TZID=Europe/Berlin:20111004T100000#', 'DTSTART;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->format('Ymd\THis'), $vcalendar);
739         $vcalendar = preg_replace('#DTEND;TZID=Europe/Berlin:20111004T120000#', 'DTEND;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(1)->format('Ymd\THis'), $vcalendar);
740         
741         $id = Tinebase_Record_Abstract::generateUID();
742         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
743         
744         // move event origin to shared (origin and display where the same)
745         Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", stream_get_contents($event->get()));
746 //         $oldEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
747 //         $oldEvent->delete();
748         
749         // wait some time
750         $cbs = new Calendar_Backend_Sql();
751         $cbs->updateMultiple(array($id), array(
752             'creation_time'      => Tinebase_DateTime::now()->subMinute(5),
753             'last_modified_time' => Tinebase_DateTime::now()->subMinute(3),
754         ));
755         
756         $personalEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
757         $personalEvent->delete();
758         
759         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
760         $ownAttendee = Calendar_Model_Attender::getOwnAttender($loadedEvent->getRecord()->attendee);
761         $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $ownAttendee->status, 'event must be declined');
762     }
763     
764     public function testDeletePastEvent()
765     {
766         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
767         
768         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
769         
770         $id = Tinebase_Record_Abstract::generateUID();
771         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
772         
773         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
774         $loadedEvent->delete();
775         
776         $notDeletedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
777         $this->assertTrue(!! $notDeletedEvent, 'past event must not be deleted');
778     }
779     
780     /**
781      * validate that users can set alarms for events with external organizers
782      * 
783      */
784     public function testSetAlarmForEventWithExternalOrganizer()
785     {
786         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.21) Gecko/20110831 Lightning/1.0b2 Thunderbird/3.1.13';
787         
788         $event = $this->testCreateEventWithExternalOrganizer();
789         
790         $vcalendar = file_get_contents(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
791         $vcalendar = preg_replace('/PT1H15M/', 'PT1H30M', $vcalendar);
792         
793         $event->put($vcalendar);
794         
795         $record = $event->getRecord();
796         
797         $this->assertEquals('2011-10-04 06:30:00', (string) $record->alarms->getFirstRecord()->alarm_time);
798     }
799     
800     /**
801      * return vcalendar as string and replace organizers email address with emailaddess of current user
802      * 
803      * @param string $_filename  file to open
804      * @return string
805      */
806     public static function getVCalendar($_filename)
807     {
808         $vcalendar = file_get_contents($_filename);
809         
810         $unittestUserEmail = Tinebase_Core::getUser()->accountEmailAddress;
811         $vcalendar = preg_replace(
812             array(
813                 '/l.kneschke@metaway\n s.de/',
814                 '/unittest@\n tine20.org/',
815                 '/un\n ittest@tine20.org/',
816                 '/unittest@tine20.org/',
817                 '/unittest@ti\n ne20.org/',
818                 '/pwulf\n @tine20.org/',
819                 '/sclever@tine20.org/',
820             ), 
821             array(
822                 $unittestUserEmail,
823                 $unittestUserEmail,
824                 $unittestUserEmail,
825                 $unittestUserEmail,
826                 $unittestUserEmail,
827                 array_value('pwulf', Zend_Registry::get('personas'))->accountEmailAddress,
828                 array_value('sclever', Zend_Registry::get('personas'))->accountEmailAddress,
829             ), 
830             $vcalendar
831         );
832         
833         return $vcalendar;
834     }
835
836     /**
837      * testAcceptInvitationForRecurringEventException
838      * 
839      * @see 0009022: can not accept invitation to recurring event exception
840      * @see 0009510: is it allowed to have no main vevent in ics?
841      */
842     public function testAcceptInvitationForRecurringEventException()
843     {
844         Tinebase_Container::getInstance()->setGrants($this->objects['initialContainer'], new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(
845             $this->_getAllCalendarGrants(),
846             array(
847                 'account_id'    => 0,
848                 'account_type'  => 'anyone',
849                 Tinebase_Model_Grants::GRANT_READ     => true,
850                 Tinebase_Model_Grants::GRANT_ADD      => false,
851                 Tinebase_Model_Grants::GRANT_EDIT     => false,
852                 Tinebase_Model_Grants::GRANT_DELETE   => false,
853                 Tinebase_Model_Grants::GRANT_FREEBUSY => true,
854                 Tinebase_Model_Grants::GRANT_ADMIN    => false,
855         ))), true);
856         
857         $persistentEvent = Calendar_Controller_Event::getInstance()->create(new Calendar_Model_Event(array(
858             'container_id' => $this->objects['initialContainer']->getId(),
859             'rrule'   => 'FREQ=WEEKLY;BYDAY=WE',
860             'dtstart' => new Tinebase_DateTime('20131016T120000'),
861             'dtend'   => new Tinebase_DateTime('20131016T130000'),
862             'summary' => 'Meeting',
863             'attendee' => new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
864                 array(
865                     'user_id'   => Tinebase_Core::getUser()->contact_id,
866                     'user_type' => Calendar_Model_Attender::USERTYPE_USER,
867                     'role'      => Calendar_Model_Attender::ROLE_REQUIRED,
868                     'status_authkey' => 'e4546f26cb37f69baf59135e7bd379bf94bba429', // TODO is this needed??
869                 )
870             )),
871             'uid' => '3ef8b44333aea7c01aa5a9308e2cb014807c60b3'
872         )));
873
874         // add pwulf as attender to create exception
875         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
876         $exception = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, new Tinebase_DateTime('20131017T140000'));
877         $exception->attendee->addRecord(new Calendar_Model_Attender(array(
878             'user_type' => Calendar_Model_Attender::USERTYPE_USER,
879             'user_id'   => $this->_personasContacts['pwulf']->getId(),
880         )));
881         $persistentException = Calendar_Controller_Event::getInstance()->createRecurException($exception);
882         $persistentEvent = Calendar_Controller_Event::getInstance()->get($persistentEvent->getId());
883         
884         // pwulf tries to accept invitation
885         Tinebase_Core::set(Tinebase_Core::USER, Tinebase_User::getInstance()->getFullUserByLoginName('pwulf'));
886         
887         $_SERVER['HTTP_USER_AGENT'] = 'Mac OS X/10.8.5 (12F45) CalendarAgent/57';
888         // this ics only has an exdate vevent
889         $vcalendarStream = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/accept_exdate_invite.ics');
890         
891         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $persistentEvent);
892         $event->put($vcalendarStream);
893         
894         $exdateWebDAVEvent = $event->getRecord()->exdate[0];
895         $this->_assertCurrentUserAttender($exdateWebDAVEvent);
896         
897         $exdateCalEvent = Calendar_Controller_Event::getInstance()->get($persistentException->getId());
898         $this->_assertCurrentUserAttender($exdateCalEvent);
899     }
900     
901     /**
902      * assert current user as attender
903      * 
904      * @param Calendar_Model_Event $exdate
905      */
906     protected function _assertCurrentUserAttender($event)
907     {
908         $this->assertTrue($event->attendee instanceof Tinebase_Record_RecordSet, 'attendee is no recordset: ' . print_r($event->toArray(), true));
909         $this->assertEquals(2, $event->attendee->count(), 'exdate should have 2 attendee: ' . print_r($event->toArray(), true));
910         $currentUser = Calendar_Model_Attender::getAttendee($event->attendee, new Calendar_Model_Attender(array(
911             'user_id'   => Tinebase_Core::getUser()->contact_id,
912             'user_type' => Calendar_Model_Attender::USERTYPE_USER,
913         )));
914         $this->assertNotNull($currentUser, 'currentUser not found in attendee');
915         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $currentUser->status, print_r($currentUser->toArray(), true));
916     }
917     
918     /**
919      * create event with attachment
920      *
921      * @return multitype:Ambigous <Calendar_Frontend_WebDAV_Event, Calendar_Frontend_WebDAV_Event> Ambigous <Tinebase_Model_Tree_Node, Tinebase_Record_Interface, Tinebase_Record_Abstract, NULL, unknown>
922      */
923     public function createEventWithAttachment($count=1)
924     {
925         $event = $this->testCreateRepeatingEvent();
926         
927         for ($i=1; $i<=$count; $i++) {
928             $suffix = $i>1 ? $i : '';
929             
930             $agenda = fopen("php://temp", 'r+');
931             fputs($agenda, "HELLO WORLD$suffix");
932             rewind($agenda);
933         
934             $attachmentController = Tinebase_FileSystem_RecordAttachments::getInstance();
935             $attachmentNode = $attachmentController->addRecordAttachment($event->getRecord(), "agenda{$suffix}.html", $agenda);
936         }
937     
938         $event = new Calendar_Frontend_WebDAV_Event($event->getContainer(), $event->getRecord()->getId());
939     
940         return $event;
941     }
942 }