47f2aafe50c08287da01027273530745d536f9ca
[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     public function testCreateEventMissingOwnAttendee()
268     {
269         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
270         
271         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/apple_caldendar_repeating.ics');
272         
273         
274         $id = Tinebase_Record_Abstract::generateUID();
275         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
276         
277         $this->assertTrue(!! Calendar_Model_Attender::getOwnAttender($event->getRecord()->attendee), 'own attendee has not been added');
278         $this->assertTrue(!! Calendar_Model_Attender::getOwnAttender($event->getRecord()->exdate->getFirstRecord()->attendee), 'own attendee has not been added to exdate');
279         
280         // Simulate OSX which updates w.o. fetching first
281         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/apple_caldendar_repeating.ics', 'r');
282         
283         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $event->getRecord()->getId());
284         $event->put($vcalendarStream);
285         
286         $this->assertTrue(!! Calendar_Model_Attender::getOwnAttender($event->getRecord()->attendee), 'own attendee has not been preserved');
287         $this->assertTrue(!! Calendar_Model_Attender::getOwnAttender($event->getRecord()->exdate->getFirstRecord()->attendee), 'own attendee has not been preserved in exdate');
288         
289         // create new exception from client w.o. fetching first
290         Calendar_Controller_Event::getInstance()->purgeRecords(TRUE);
291         Calendar_Controller_Event::getInstance()->delete($event->getRecord()->exdate->getFirstRecord());
292         Calendar_Controller_Event::getInstance()->purgeRecords(FALSE);
293         
294         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/apple_caldendar_repeating.ics', 'r');
295         
296         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $event->getRecord()->getId());
297         $event->put($vcalendarStream);
298         
299         $this->assertTrue(!! Calendar_Model_Attender::getOwnAttender($event->getRecord()->attendee), 'own attendee has not been created in exdate');
300         $this->assertTrue(!! Calendar_Model_Attender::getOwnAttender($event->getRecord()->exdate->getFirstRecord()->attendee), 'own attendee has not been created in exdate');
301         
302     }
303     
304     public function testFilterRepeatingException()
305     {
306         // create event in shared calendar test user is attendee
307         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
308         $vcalendarStream = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning_repeating_daily.ics');
309         $id = Tinebase_Record_Abstract::generateUID();
310         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendarStream);
311         
312         // decline exception -> no implicit fallout as exception is still in initialContainer via displaycal
313         $exception = $event->getRecord()->exdate->filter('is_deleted', 0)->getFirstRecord();
314         $exception->attendee[0]->status = Calendar_Model_Attender::STATUS_DECLINED;
315         $updatedException = Calendar_Controller_Event::getInstance()->update($exception);
316         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $event->getRecord()->getId());
317         $vcalendar = stream_get_contents($event->get());
318         $this->assertContains('DTSTART;TZID=Europe/Berlin:20111008T130000', $vcalendar, 'exception must not be implicitly deleted');
319         
320         // delete attendee from exception -> implicit fallout exception is not longer in displaycal
321         $exception = $event->getRecord()->exdate->filter('is_deleted', 0)->getFirstRecord();
322         $exception->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender');
323         $updatedException = Calendar_Controller_Event::getInstance()->update($exception);
324         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $event->getRecord()->getId());
325         $vcalendar = stream_get_contents($event->get());
326         $this->assertContains('EXDATE:20111008T080000Z', $vcalendar, 'exception must be implicitly deleted from event ' . print_r($event->getRecord()->toArray(), TRUE));
327         
328         // save back event -> implicit delete must not be deleted on server
329         $event->put($vcalendar);
330         $event = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], $event->getRecord()->getId());
331         $vcalendar = stream_get_contents($event->get());
332         $this->assertContains('DTSTART;TZID=Europe/Berlin:20111008T130000', $vcalendar, 'exception must not be implicitly deleted after update');
333     }
334     
335     /**
336      * test get vcard
337      * @depends testCreateEventWithInternalOrganizer
338      */
339     public function testGetVCalendar()
340     {
341         $event = $this->testCreateEventWithInternalOrganizer();
342         
343         $vcalendar = stream_get_contents($event->get());
344         
345         //var_dump($vcalendar);
346         
347         $this->assertContains('SUMMARY:New Event', $vcalendar);
348         $this->assertContains('ORGANIZER;CN=', $vcalendar);
349     }
350     
351     /**
352      * test get vcard
353      */
354     public function testGetRepeatingVCalendar()
355     {
356         $event = $this->testCreateRepeatingEvent();
357     
358         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $event->getRecord()->getId());
359         
360         $vcalendar = stream_get_contents($event->get());
361         #var_dump($vcalendar);
362         $this->assertContains('SUMMARY:New Event', $vcalendar);
363         $this->assertContains('EXDATE:20111005T080000Z', $vcalendar);
364         $this->assertContains('RECURRENCE-ID;TZID=Europe/Berlin:20111008T100000', $vcalendar);
365         $this->assertContains('TRIGGER;VALUE=DURATION:-PT1H30M', $vcalendar); // base alarm
366         $this->assertContains('TRIGGER;VALUE=DURATION:-PT1H15M', $vcalendar); // exception alarm
367     }
368     
369     /**
370      * test updating existing event
371      */
372     public function testPutEventFromThunderbird()
373     {
374         $_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';
375         
376         $event = $this->testCreateEventWithInternalOrganizer();
377         
378         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
379         
380         $event->put($vcalendarStream);
381         
382         $record = $event->getRecord();
383         
384         $this->assertEquals('New Event', $record->summary);
385         $this->assertTrue(! empty($record->attendee[0]["status_authkey"]));
386     }
387     
388     /**
389      * test updating existing event
390      */
391     public function testPutEventWithExternalOrganizer()
392     {
393         $_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';
394         
395         $event = $this->testCreateEventWithExternalOrganizer();
396         
397         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
398         
399         $event->put($vcalendarStream);
400         
401         $record = $event->getRecord();
402         
403         $this->assertEquals('New Event', $record->summary);
404     }
405     
406     /**
407      * test updating existing event
408      */
409     public function testPutEventFromMacOsX()
410     {
411         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
412         
413         $event = $this->testCreateEventWithInternalOrganizer();
414     
415         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
416     
417         $event->put($vcalendarStream);
418     
419         $record = $event->getRecord();
420     
421         $this->assertEquals('New Event', $record->summary);
422     }
423     
424     /**
425      * test deleting attachment from existing event
426      */
427     public function testDeleteAttachment()
428     {
429         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
430         
431         $event = $this->createEventWithAttachment(2);
432         
433         // remove agenda.html
434         $clone = clone $event;
435         $attachments = $clone->getRecord()->attachments;
436         $attachments->removeRecord($attachments->filter('name', 'agenda.html')->getFirstRecord());
437         $event->put($clone->get());
438         
439         // assert agenda2.html exists
440         $record = $event->getRecord();
441         $this->assertEquals(1, $record->attachments->count());
442         $this->assertEquals('agenda2.html', $record->attachments->getFirstRecord()->name);
443     }
444     
445     /**
446      * test updating existing event
447      */
448     public function testPutEventFromGenericClient()
449     {
450         $_SERVER['HTTP_USER_AGENT'] = 'FooBar User Agent';
451         
452         $event = $this->testCreateEventWithInternalOrganizer();
453         
454         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
455         
456         $this->setExpectedException('Sabre\DAV\Exception\Forbidden');
457         
458         $event->put($vcalendarStream);
459         
460         $record = $event->getRecord();
461         
462         $this->assertEquals('New Event', $record->summary);
463     }
464     
465     public function testPutEventMultipleAlarms()
466     {
467         $_SERVER['HTTP_USER_AGENT'] = 'CalendarStore/5.0 (1127); iCal/5.0 (1535); Mac OS X/10.7.1 (11B26)';
468         
469         $event = $this->testCreateEventWithInternalOrganizer();
470         
471         $vcalendarStream = fopen(dirname(__FILE__) . '/../../Import/files/event_with_multiple_alarm.ics', 'r');
472         
473         $event->put($vcalendarStream);
474         
475         $record = $event->getRecord();
476         
477         $this->assertEquals('3', count($record->alarms));
478     }
479     
480     /**
481      * test get name of vcard
482      */
483     public function testGetNameOfEvent()
484     {
485         $event = $this->testCreateEventWithInternalOrganizer();
486         
487         $record = $event->getRecord();
488         
489         $this->assertEquals($event->getName(), $record->getId() . '.ics');
490     }
491     
492     /**
493      * move event orig container shared -> personal
494      */
495     public function testMoveOriginEvent()
496     {
497         $_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';
498         
499         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
500         
501         $id = Tinebase_Record_Abstract::generateUID();
502         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
503         
504         // move event (origin container)
505         Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", stream_get_contents($event->get()));
506         $oldEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
507         $oldEvent->delete();
508         
509         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
510         $this->assertEquals($this->objects['initialContainer']->getId(), $loadedEvent->getRecord()->container_id, 'origin container not updated');
511         
512     }
513     
514     /**
515      * (organizer) move originpersonal -> originshared
516      * 
517      * NOTE: this is the hard case, as a attendee reference stays in personal which must not be deleted
518      * 
519      * solution: prohibit deleting from displaycal when origcal != displaycal
520      *  Exception: implicit decline if curruser owner of displaycal && ! organizer 
521      */
522     public function testMoveOriginPersonalToShared()
523     {
524         $_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';
525         
526         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
527         
528         $id = Tinebase_Record_Abstract::generateUID();
529         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
530         
531         // move event origin to shared (origin and display were the same)
532         Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", stream_get_contents($event->get()));
533         $oldEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
534         $oldEvent->delete();
535         
536         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
537         $this->assertEquals($this->objects['sharedContainer']->getId(), $loadedEvent->getRecord()->container_id, 'origin container not updated');
538         
539         $ownAttendee = Calendar_Model_Attender::getOwnAttender($loadedEvent->getRecord()->attendee);
540         $this->assertEquals($this->objects['initialContainer']->getId(), $ownAttendee->displaycontainer_id, 'display container must not change');
541         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $ownAttendee->status, 'event must not be declined: ' . print_r($ownAttendee->toArray(), TRUE));
542     }
543     
544     /**
545      * move event displaycal onedisplaycal -> otherdisplaycal
546      */
547     public function testMoveDisplayEvent()
548     {
549         $_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';
550         
551         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
552         
553         $id = Tinebase_Record_Abstract::generateUID();
554         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
555         $ownAttendee = Calendar_Model_Attender::getOwnAttender($event->getRecord()->attendee);
556         $displayCalendar = Tinebase_Container::getInstance()->getContainerById($ownAttendee->displaycontainer_id);
557         
558         $personalCalendar = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
559             'name'              => Tinebase_Record_Abstract::generateUID(),
560             'type'              => Tinebase_Model_Container::TYPE_PERSONAL,
561             'backend'           => 'Sql',
562             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
563         )));
564         
565         // move event (displaycontainer)
566         $displayCalendarEvent = new Calendar_Frontend_WebDAV_Event($displayCalendar, "$id.ics");
567         Calendar_Frontend_WebDAV_Event::create($personalCalendar, "$id.ics", stream_get_contents($displayCalendarEvent->get()));
568         $oldEvent = new Calendar_Frontend_WebDAV_Event($displayCalendar, "$id.ics");
569         $oldEvent->delete();
570         
571         $loadedEvent = new Calendar_Frontend_WebDAV_Event($displayCalendar, "$id.ics");
572         $this->assertEquals($this->objects['sharedContainer']->getId(), $loadedEvent->getRecord()->container_id, 'origin container must not be updated');
573         
574         $ownAttendee = Calendar_Model_Attender::getOwnAttender($loadedEvent->getRecord()->attendee);
575         $this->assertEquals($personalCalendar->getId(), $ownAttendee->displaycontainer_id, 'display container not changed');
576         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $ownAttendee->status, 'event must not be declined');
577     }
578     
579     public function testGetAlarm()
580     {
581         $_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';
582         
583         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
584         
585         $id = Tinebase_Record_Abstract::generateUID();
586         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
587         
588         $personalEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
589         $sharedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
590         
591         $personalVCalendar = stream_get_contents($personalEvent->get());
592         $sharedVCalendar = stream_get_contents($sharedEvent->get());
593         #var_dump($personalVCalendar);
594         #var_dump($sharedVCalendar);
595         $this->assertNotContains('X-MOZ-LASTACK;VALUE=DATE-TIME:21', $personalVCalendar, $personalVCalendar);
596         $this->assertContains('X-MOZ-LASTACK;VALUE=DATE-TIME:21', $sharedVCalendar, $sharedVCalendar);
597     }
598     
599     public function testGetNoAlarmAsNonAttendee()
600     {
601         $_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';
602         
603         $vcalendar = file_get_contents(dirname(__FILE__) . '/../../Import/files/lightning.ics');
604         
605         $currentCU = Calendar_Controller_MSEventFacade::getInstance()->setCalendarUser(new Calendar_Model_Attender(array(
606             'user_type' => Calendar_Model_Attender::USERTYPE_USER,
607             'user_id'   => 'someoneelse'
608         )));
609         $id = Tinebase_Record_Abstract::generateUID();
610         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
611         Calendar_Controller_MSEventFacade::getInstance()->setCalendarUser($currentCU);
612         
613         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
614         $ics = stream_get_contents($loadedEvent->get());
615         
616         $this->assertNotContains('BEGIN:VALARM', $ics, $ics);
617     }
618     
619     public function testDeleteOrignEvent()
620     {
621         $_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';
622         
623         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/event_with_custom_alarm.ics');
624         $vcalendar = preg_replace('#DTSTART;TZID=Europe/Berlin:20120214T100000#', 'DTSTART;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->format('Ymd\THis'), $vcalendar);
625         $vcalendar = preg_replace('#DTEND;TZID=Europe/Berlin:20120214T140000#', 'DTEND;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(1)->format('Ymd\THis'), $vcalendar);
626         
627         $id = Tinebase_Record_Abstract::generateUID();
628         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
629         
630         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
631         $loadedEvent->delete();
632         
633         $this->setExpectedException('Tinebase_Exception_NotFound');
634         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
635         $loadedEvent->getRecord();
636     }
637     
638     public function testDeleteImplicitDecline()
639     {
640         $_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';
641         
642         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/event_with_custom_alarm.ics');
643         $vcalendar = preg_replace('#DTSTART;TZID=Europe/Berlin:20120214T100000#', 'DTSTART;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->format('Ymd\THis'), $vcalendar);
644         $vcalendar = preg_replace('#DTEND;TZID=Europe/Berlin:20120214T140000#', 'DTEND;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(1)->format('Ymd\THis'), $vcalendar);
645         
646         $id = Tinebase_Record_Abstract::generateUID();
647         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", $vcalendar);
648         
649         $pwulf = new Calendar_Model_Attender(array(
650             'user_type'  => Calendar_Model_Attender::USERTYPE_USER,
651             'user_id'    => array_value('pwulf', Zend_Registry::get('personas'))->contact_id
652         ));
653         
654         $pwulfAttendee = Calendar_Model_Attender::getAttendee($event->getRecord()->attendee, $pwulf);
655         $this->assertTrue($pwulfAttendee !== null, 'could not find pwulf in attendee');
656         $pwulfPersonalCal = Tinebase_Container::getInstance()->getContainerById($pwulfAttendee->displaycontainer_id);
657         $pwulfPersonalEvent = new Calendar_Frontend_WebDAV_Event($pwulfPersonalCal, "$id.ics");
658         $pwulfPersonalEvent->delete();
659         
660         $pwulfPersonalEvent = new Calendar_Frontend_WebDAV_Event($pwulfPersonalCal, "$id.ics");
661         $pwulfAttendee = Calendar_Model_Attender::getAttendee($pwulfPersonalEvent->getRecord()->attendee, $pwulf);
662         $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $pwulfAttendee->status, 'event must be declined for pwulf');
663         $this->assertEquals(0, $pwulfPersonalEvent->getRecord()->is_deleted, 'event must not be deleted');
664     }
665     
666     /**
667      * NOTE: As noted in {@see testMoveOriginPersonalToShared} we can't delete/decline for organizer in his
668      *       personal cal because a move personal->shared would delete/decline the event.
669      *       
670      *       To support intensional delete/declines we allow the delte/decline only if ther is some
671      *       time between this and the last update
672      */
673     public function testDeleteImplicitDeclineOrganizer()
674     {
675         $_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';
676         
677         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
678         $vcalendar = preg_replace('#DTSTART;TZID=Europe/Berlin:20111004T100000#', 'DTSTART;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->format('Ymd\THis'), $vcalendar);
679         $vcalendar = preg_replace('#DTEND;TZID=Europe/Berlin:20111004T120000#', 'DTEND;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(1)->format('Ymd\THis'), $vcalendar);
680         
681         $id = Tinebase_Record_Abstract::generateUID();
682         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
683         
684         // move event origin to shared (origin and display where the same)
685         Calendar_Frontend_WebDAV_Event::create($this->objects['sharedContainer'], "$id.ics", stream_get_contents($event->get()));
686 //         $oldEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
687 //         $oldEvent->delete();
688         
689         // wait some time
690         $cbs = new Calendar_Backend_Sql();
691         $cbs->updateMultiple(array($id), array(
692             'creation_time'      => Tinebase_DateTime::now()->subMinute(5),
693             'last_modified_time' => Tinebase_DateTime::now()->subMinute(3),
694         ));
695         
696         $personalEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
697         $personalEvent->delete();
698         
699         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['sharedContainer'], "$id.ics");
700         $ownAttendee = Calendar_Model_Attender::getOwnAttender($loadedEvent->getRecord()->attendee);
701         $this->assertEquals(Calendar_Model_Attender::STATUS_DECLINED, $ownAttendee->status, 'event must be declined');
702     }
703     
704     public function testDeletePastEvent()
705     {
706         $_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';
707         
708         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/lightning.ics');
709         
710         $id = Tinebase_Record_Abstract::generateUID();
711         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
712         
713         $loadedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
714         $loadedEvent->delete();
715         
716         $notDeletedEvent = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], "$id.ics");
717         $this->assertTrue(!! $notDeletedEvent, 'past event must not be deleted');
718     }
719     
720     /**
721      * validate that users can set alarms for events with external organizers
722      * 
723      */
724     public function testSetAlarmForEventWithExternalOrganizer()
725     {
726         $_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';
727         
728         $event = $this->testCreateEventWithExternalOrganizer();
729         
730         $vcalendar = file_get_contents(dirname(__FILE__) . '/../../Import/files/lightning.ics', 'r');
731         $vcalendar = preg_replace('/PT1H15M/', 'PT1H30M', $vcalendar);
732         
733         $event->put($vcalendar);
734         
735         $record = $event->getRecord();
736         
737         $this->assertEquals('2011-10-04 06:30:00', (string) $record->alarms->getFirstRecord()->alarm_time);
738     }
739     
740     /**
741      * return vcalendar as string and replace organizers email address with emailaddess of current user
742      * 
743      * @param string $_filename  file to open
744      * @return string
745      */
746     public static function getVCalendar($_filename)
747     {
748         $vcalendar = file_get_contents($_filename);
749         
750         $unittestUserEmail = Tinebase_Core::getUser()->accountEmailAddress;
751         $vcalendar = preg_replace(
752             array(
753                 '/l.kneschke@metaway\n s.de/',
754                 '/unittest@\n tine20.org/',
755                 '/un\n ittest@tine20.org/',
756                 '/unittest@tine20.org/',
757                 '/unittest@ti\n ne20.org/',
758                 '/pwulf\n @tine20.org/',
759                 '/sclever@tine20.org/',
760             ), 
761             array(
762                 $unittestUserEmail,
763                 $unittestUserEmail,
764                 $unittestUserEmail,
765                 $unittestUserEmail,
766                 $unittestUserEmail,
767                 array_value('pwulf', Zend_Registry::get('personas'))->accountEmailAddress,
768                 array_value('sclever', Zend_Registry::get('personas'))->accountEmailAddress,
769             ), 
770             $vcalendar
771         );
772         
773         return $vcalendar;
774     }
775
776     /**
777      * testAcceptInvitationForRecurringEventException
778      * 
779      * @see 0009022: can not accept invitation to recurring event exception
780      * @see 0009510: is it allowed to have no main vevent in ics?
781      */
782     public function testAcceptInvitationForRecurringEventException()
783     {
784         Tinebase_Container::getInstance()->setGrants($this->objects['initialContainer'], new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(
785             $this->_getAllCalendarGrants(),
786             array(
787                 'account_id'    => 0,
788                 'account_type'  => 'anyone',
789                 Tinebase_Model_Grants::GRANT_READ     => true,
790                 Tinebase_Model_Grants::GRANT_ADD      => false,
791                 Tinebase_Model_Grants::GRANT_EDIT     => false,
792                 Tinebase_Model_Grants::GRANT_DELETE   => false,
793                 Tinebase_Model_Grants::GRANT_FREEBUSY => true,
794                 Tinebase_Model_Grants::GRANT_ADMIN    => false,
795         ))), true);
796         
797         $persistentEvent = Calendar_Controller_Event::getInstance()->create(new Calendar_Model_Event(array(
798             'container_id' => $this->objects['initialContainer']->getId(),
799             'rrule'   => 'FREQ=WEEKLY;BYDAY=WE',
800             'dtstart' => new Tinebase_DateTime('20131016T120000'),
801             'dtend'   => new Tinebase_DateTime('20131016T130000'),
802             'summary' => 'Meeting',
803             'attendee' => new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
804                 array(
805                     'user_id'   => Tinebase_Core::getUser()->contact_id,
806                     'user_type' => Calendar_Model_Attender::USERTYPE_USER,
807                     'role'      => Calendar_Model_Attender::ROLE_REQUIRED,
808                     'status_authkey' => 'e4546f26cb37f69baf59135e7bd379bf94bba429', // TODO is this needed??
809                 )
810             )),
811             'uid' => '3ef8b44333aea7c01aa5a9308e2cb014807c60b3'
812         )));
813
814         // add pwulf as attender to create exception
815         $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event');
816         $exception = Calendar_Model_Rrule::computeNextOccurrence($persistentEvent, $exceptions, new Tinebase_DateTime('20131017T140000'));
817         $exception->attendee->addRecord(new Calendar_Model_Attender(array(
818             'user_type' => Calendar_Model_Attender::USERTYPE_USER,
819             'user_id'   => $this->_personasContacts['pwulf']->getId(),
820         )));
821         $persistentException = Calendar_Controller_Event::getInstance()->createRecurException($exception);
822         $persistentEvent = Calendar_Controller_Event::getInstance()->get($persistentEvent->getId());
823         
824         // pwulf tries to accept invitation
825         Tinebase_Core::set(Tinebase_Core::USER, Tinebase_User::getInstance()->getFullUserByLoginName('pwulf'));
826         
827         $_SERVER['HTTP_USER_AGENT'] = 'Mac OS X/10.8.5 (12F45) CalendarAgent/57';
828         // this ics only has an exdate vevent
829         $vcalendarStream = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/accept_exdate_invite.ics');
830         
831         $event = new Calendar_Frontend_WebDAV_Event($this->objects['initialContainer'], $persistentEvent);
832         $event->put($vcalendarStream);
833         
834         $exdateWebDAVEvent = $event->getRecord()->exdate[0];
835         $this->_assertCurrentUserAttender($exdateWebDAVEvent);
836         
837         $exdateCalEvent = Calendar_Controller_Event::getInstance()->get($persistentException->getId());
838         $this->_assertCurrentUserAttender($exdateCalEvent);
839     }
840     
841     /**
842      * assert current user as attender
843      * 
844      * @param Calendar_Model_Event $exdate
845      */
846     protected function _assertCurrentUserAttender($event)
847     {
848         $this->assertTrue($event->attendee instanceof Tinebase_Record_RecordSet, 'attendee is no recordset: ' . print_r($event->toArray(), true));
849         $this->assertEquals(2, $event->attendee->count(), 'exdate should have 2 attendee: ' . print_r($event->toArray(), true));
850         $currentUser = Calendar_Model_Attender::getAttendee($event->attendee, new Calendar_Model_Attender(array(
851             'user_id'   => Tinebase_Core::getUser()->contact_id,
852             'user_type' => Calendar_Model_Attender::USERTYPE_USER,
853         )));
854         $this->assertNotNull($currentUser, 'currentUser not found in attendee');
855         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $currentUser->status, print_r($currentUser->toArray(), true));
856     }
857     
858     /**
859      * create event with attachment
860      *
861      * @return multitype:Ambigous <Calendar_Frontend_WebDAV_Event, Calendar_Frontend_WebDAV_Event> Ambigous <Tinebase_Model_Tree_Node, Tinebase_Record_Interface, Tinebase_Record_Abstract, NULL, unknown>
862      */
863     public function createEventWithAttachment($count=1)
864     {
865         $event = $this->testCreateRepeatingEvent();
866         
867         for ($i=1; $i<=$count; $i++) {
868             $suffix = $i>1 ? $i : '';
869             
870             $agenda = fopen("php://temp", 'r+');
871             fputs($agenda, "HELLO WORLD$suffix");
872             rewind($agenda);
873         
874             $attachmentController = Tinebase_FileSystem_RecordAttachments::getInstance();
875             $attachmentNode = $attachmentController->addRecordAttachment($event->getRecord(), "agenda{$suffix}.html", $agenda);
876         }
877     
878         $event = new Calendar_Frontend_WebDAV_Event($event->getContainer(), $event->getRecord()->getId());
879     
880         return $event;
881     }
882 }