9890: Improve imip invitation
authorPhilipp Schüle <p.schuele@metaways.de>
Wed, 23 Jul 2014 09:40:24 +0000 (11:40 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Thu, 4 Sep 2014 09:27:43 +0000 (11:27 +0200)
* moves personal imip events in a shared shadow container
* adds external_seq col to allow responses with the right sequence
* allow multiple internal attendee
* show current event in UI when appropriate

https://forge.tine20.org/mantisbt/view.php?id=9890

Change-Id: I79802d19765083609fc01a971208dee83b80e8aa
Reviewed-on: http://gerrit.tine20.com/customers/1090
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
19 files changed:
tests/tine20/Calendar/Controller/MSEventFacadeTest.php
tests/tine20/Calendar/Frontend/WebDAV/EventTest.php
tests/tine20/Calendar/Frontend/files/invitation_request_external.ics
tests/tine20/Calendar/Frontend/iMIPTest.php
tests/tine20/Calendar/JsonTests.php
tests/tine20/Calendar/Model/AttenderTests.php
tests/tine20/Calendar/Setup/DemoDataTests.php
tests/tine20/Calendar/TestCase.php
tests/tine20/Phone/JsonTest.php
tine20/Calendar/Controller.php
tine20/Calendar/Controller/Event.php
tine20/Calendar/Controller/EventNotifications.php
tine20/Calendar/Convert/Event/VCalendar/Abstract.php
tine20/Calendar/Frontend/iMIP.php
tine20/Calendar/Model/Event.php
tine20/Calendar/Setup/Update/Release8.php
tine20/Calendar/Setup/setup.xml
tine20/Calendar/js/iMIPDetailsPanel.js
tine20/Tinebase/Container.php

index 82323d5..bbc67ed 100644 (file)
@@ -310,6 +310,7 @@ class Calendar_Controller_MSEventFacadeTest extends Calendar_TestCase
         $updatedEvent = $this->_uit->update($event);
         $updatedAlarm = $updatedEvent->exdate[0]->alarms->getFirstRecord();
         
+        $this->assertNotNull($persistentAlarm);
         $diff = $persistentAlarm->diff($updatedAlarm);
         $this->assertTrue($diff->isEmpty(), 'no diff');
         $this->assertTrue(Calendar_Controller_Alarm::getAcknowledgeTime($updatedEvent->alarms->getFirstRecord()) instanceof Tinebase_DateTime, 'ack time missing');
index 047de41..914e9a6 100644 (file)
@@ -4,16 +4,11 @@
  * 
  * @package     Calendar
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2011-2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2011-2014 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
 /**
- * Test helper
- */
-require_once dirname(dirname(dirname(dirname(__FILE__)))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
-
-/**
  * Test class for Calendar_Frontend_WebDAV_Event
  */
 class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
@@ -63,17 +58,6 @@ class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
     }
 
     /**
-     * tear down tests
-     *
-     */
-    public function tearDown()
-    {
-        parent::tearDown();
-        
-        Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
-    }
-    
-    /**
      * test create event with internal organizer
      * 
      * @return Calendar_Frontend_WebDAV_Event
@@ -137,8 +121,14 @@ class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
         $event = Calendar_Frontend_WebDAV_Event::create($this->objects['initialContainer'], "$id.ics", $vcalendar);
         
         $record = $event->getRecord();
-
+        $container = Tinebase_Container::getInstance()->getContainerById($record->container_id);
+        $ownAttendee = Calendar_Model_Attender::getOwnAttender($record->attendee);
+        
         $this->assertEquals('New Event', $record->summary);
+        $this->assertEquals('l.kneschke@metaways.de', $container->name, 'event no in invitation calendar');
+        $this->assertTrue(!! $ownAttendee, 'own attendee missing');
+        $this->assertEquals(1, $record->seq, 'tine20 seq starts with 1');
+        $this->assertEquals(0, $record->external_seq, 'external seq not set -> 0');
         
         return $event;
     }
@@ -280,7 +270,6 @@ class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
         
         $vcalendar = self::getVCalendar(dirname(__FILE__) . '/../../Import/files/apple_caldendar_repeating.ics');
         
-        
         $id = Tinebase_Record_Abstract::generateUID();
 
         $event = Calendar_Frontend_WebDAV_Event::create($container, "$id.ics", $vcalendar);
@@ -462,6 +451,8 @@ class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
         $record = $event->getRecord();
         
         $this->assertEquals('New Event', $record->summary);
+        $this->assertEquals(2, $record->seq, 'tine20 seq should have increased');
+        $this->assertEquals(0, $record->external_seq, 'external seq must not have increased');
     }
     
     /**
@@ -779,7 +770,6 @@ class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
     
     /**
      * validate that users can set alarms for events with external organizers
-     * 
      */
     public function testSetAlarmForEventWithExternalOrganizer()
     {
@@ -811,19 +801,13 @@ class Calendar_Frontend_WebDAV_EventTest extends Calendar_TestCase
         $vcalendar = preg_replace(
             array(
                 '/l.kneschke@metaway\n s.de/',
-                '/unittest@\n tine20.org/',
-                '/un\n ittest@tine20.org/',
-                '/unittest@tine20.org/',
-                '/unittest@ti\n ne20.org/',
-                '/pwulf\n @tine20.org/',
+                '/un[\r\n ]{0,3}ittest@[\r\n ]{0,3}ti[\r\n ]{0,3}ne20.org/',
+                '/pwulf(\n )?@tine20.org/',
                 '/sclever@tine20.org/',
             ), 
             array(
                 $unittestUserEmail,
                 $unittestUserEmail,
-                $unittestUserEmail,
-                $unittestUserEmail,
-                $unittestUserEmail,
                 array_value('pwulf', Zend_Registry::get('personas'))->accountEmailAddress,
                 array_value('sclever', Zend_Registry::get('personas'))->accountEmailAddress,
             ), 
index 5efda9e..7f8b87a 100644 (file)
@@ -32,9 +32,12 @@ ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;EMAIL=l.kneschke@ca
  ke@caldav.org\r
 ATTENDEE;CN="Löw, Jogi";CUTYPE=INDIVIDUAL;EMAIL=j.loew@caldav.org;PARTSTAT\r
  =NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=FALSE:mailto:j.loew@caldav.org\r
-ATTENDEE;CN="External, Caldav";CUTYPE=INDIVIDUAL;EMAIL=tine20admin@caldav.n\r
- et;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=FALSE:mailto:tine20admi\r
- n@caldav.net\r
+ATTENDEE;CN="Admin Account, Tine 2.0";CUTYPE=INDIVIDUAL;EMAIL="unittest@\r
+ tine20.org";PARTSTAT=NEEDS-ACTION:mailto:unittest@tine20.org\r
+ATTENDEE;CN="Clever, Susan";CUTYPE=INDIVIDUAL;EMAIL="sclever@tine20.org"\r
+ ;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT:mailto:sclever@tine20.org\r
+ATTENDEE;CN="Paul, Wulf";CUTYPE=INDIVIDUAL;EMAIL="pwulf@tine20.org"\r
+ ;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT:mailto:pwulf@tine20.org\r
 CLASS:PUBLIC\r
 SUMMARY:test mit extern\r
 TRANSP:OPAQUE\r
index 4b45d94..02048fe 100644 (file)
@@ -6,6 +6,8 @@
  * @license     http://www.gnu.org/licenses/agpl.html
  * @copyright   Copyright (c) 2011-2014 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schüle <p.schuele@metaways.de>
+ * 
+ * @todo        add tests testInvitationCancel and testOrganizerSendBy
  */
 
 /**
@@ -63,6 +65,8 @@ class Calendar_Frontend_iMIPTest extends TestCase
     {
         Calendar_Controller_Event::getInstance()->sendNotifications(true);
         
+        Calendar_Config::getInstance()->set(Calendar_Config::DISABLE_EXTERNAL_IMIP, false);
+        
         $this->_iMIPFrontend = new Calendar_Frontend_iMIP();
         $this->_iMIPFrontendMock = new Calendar_Frontend_iMIPMock();
         
@@ -98,7 +102,7 @@ class Calendar_Frontend_iMIPTest extends TestCase
      */
     public function testExternalInvitationRequestAutoProcess()
     {
-        $ics = file_get_contents(dirname(__FILE__) . '/files/invitation_request_external.ics' );
+        $ics = Calendar_Frontend_WebDAV_EventTest::getVCalendar(dirname(__FILE__) . '/files/invitation_request_external.ics' );
         $iMIP = new Calendar_Model_iMIP(array(
             'id'             => Tinebase_Record_Abstract::generateUID(),
             'ics'            => $ics,
@@ -108,9 +112,13 @@ class Calendar_Frontend_iMIPTest extends TestCase
         
         $this->_iMIPFrontend->autoProcess($iMIP);
         $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
-
-        $this->assertEquals(3, count($prepared->event->attendee));
+        
+        $this->assertEmpty($prepared->existing_event, 'there should be no existing event');
+        $this->assertEmpty($prepared->preconditions, 'no preconditions should be raised');
+        $this->assertEquals(5, count($prepared->event->attendee));
         $this->assertEquals('test mit extern', $prepared->event->summary);
+        
+        return $iMIP;
     }
 
     /**
@@ -136,19 +144,33 @@ class Calendar_Frontend_iMIPTest extends TestCase
      */
     protected function _getiMIP($_method, $_addEventToiMIP = FALSE, $_testEmptyMethod = FALSE)
     {
+        $testConfig = Zend_Registry::get('testConfig');
+        $email = ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
+        
         $event = $this->_getEvent();
         $event = Calendar_Controller_Event::getInstance()->create($event);
         $this->_eventIdsToDelete[] = $event->getId();
         
+        if ($_method == 'REPLY') {
+            $personas = Zend_Registry::get('personas');
+            $sclever = $personas['sclever'];
+            
+            $scleverAttendee = $event->attendee
+                ->filter('status', Calendar_Model_Attender::STATUS_NEEDSACTION)
+                ->getFirstRecord();
+            
+            $scleverAttendee->status = Calendar_Model_Attender::STATUS_ACCEPTED;
+            Calendar_Controller_Event::getInstance()->attenderStatusUpdate($event, $scleverAttendee, $scleverAttendee->status_authkey);
+            $event = Calendar_Controller_Event::getInstance()->get($event->getId());
+            $email = $sclever->accountEmailAddress;
+        }
+        
         // get iMIP invitation for event
         $converter = Calendar_Convert_Event_VCalendar_Factory::factory(Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC);
         $vevent = $converter->fromTine20Model($event);
         $vevent->METHOD = $_method;
         $ics = $vevent->serialize();
         
-        $testConfig = Zend_Registry::get('testConfig');
-        $email = ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
-        
         $iMIP = new Calendar_Model_iMIP(array(
             'id'             => Tinebase_Record_Abstract::generateUID(),
             'ics'            => $ics,
@@ -184,13 +206,26 @@ class Calendar_Frontend_iMIPTest extends TestCase
     public function testInternalInvitationRequestPreconditionOwnStatusAlreadySet()
     {
         $iMIP = $this->_getiMIP('REQUEST', TRUE);
+        
         // set own status
         $ownAttender = Calendar_Model_Attender::getOwnAttender($iMIP->getEvent()->attendee);
-        $ownAttender->status = Calendar_Model_Attender::STATUS_ACCEPTED;
+        $ownAttender->status = Calendar_Model_Attender::STATUS_TENTATIVE;
         Calendar_Controller_Event::getInstance()->attenderStatusUpdate($iMIP->getEvent(), $ownAttender, $ownAttender->status_authkey);
         
         $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
-        $this->assertFalse(empty($prepared->preconditions));
+        $this->assertTrue(empty($prepared->preconditions), "it's ok to reanswer without reschedule!");
+        
+        // reschedule
+        $event = Calendar_Controller_Event::getInstance()->get($prepared->existing_event->getId());
+        $event->dtstart->addHour(2);
+        $event->dtend->addHour(2);
+        Calendar_Controller_Event::getInstance()->update($event, false);
+        
+        $iMIP->getExistingEvent(true);
+        $iMIP->preconditionsChecked = false;
+        $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
+        
+        $this->assertFalse(empty($prepared->preconditions), 'do not accept this iMIP after reshedule');
         $this->assertTrue((isset($prepared->preconditions[Calendar_Model_iMIP::PRECONDITION_RECENT]) || array_key_exists(Calendar_Model_iMIP::PRECONDITION_RECENT, $prepared->preconditions)));
     }
     
@@ -227,6 +262,7 @@ class Calendar_Frontend_iMIPTest extends TestCase
                 'user_id'        => Tinebase_Core::getUser()->contact_id,
                 'user_type'      => Calendar_Model_Attender::USERTYPE_USER,
                 'role'           => Calendar_Model_Attender::ROLE_REQUIRED,
+                'status'         => Calendar_Model_Attender::STATUS_ACCEPTED,
                 'status_authkey' => Tinebase_Record_Abstract::generateUID(),
             ),
             array(
@@ -240,18 +276,23 @@ class Calendar_Frontend_iMIPTest extends TestCase
     
     /**
      * testExternalInvitationRequestProcess
-     * - uses felamimail to cache external invitation message
-     * 
-     * -> external invitation requests are not supported atm
      */
     public function testExternalInvitationRequestProcess()
     {
-        $complete = $this->_addImipMessageToEmailCache();
+        $ics = Calendar_Frontend_WebDAV_EventTest::getVCalendar(dirname(__FILE__) . '/files/invitation_request_external.ics' );
+        $ics = preg_replace('#DTSTART;VALUE=DATE-TIME;TZID=Europe/Berlin:20111121T130000#', 'DTSTART;VALUE=DATE-TIME;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(1)->format('Ymd\THis'), $ics);
+        $ics = preg_replace('#DTEND;VALUE=DATE-TIME;TZID=Europe/Berlin:20111121T140000#', 'DTEND;VALUE=DATE-TIME;TZID=Europe/Berlin:' . Tinebase_DateTime::now()->addHour(2)->format('Ymd\THis'), $ics);
         
-        $iMIP = $complete->preparedParts->getFirstRecord()->preparedData;
+        $iMIP = new Calendar_Model_iMIP(array(
+                'id'             => Tinebase_Record_Abstract::generateUID(),
+                'ics'            => $ics,
+                'method'         => 'REQUEST',
+                'originator'     => 'l.kneschke@caldav.org',
+        ));
         
         Calendar_Controller_EventNotificationsTests::flushMailer();
-        $result = $this->_iMIPFrontend->process($iMIP, Calendar_Model_Attender::STATUS_ACCEPTED);
+        $result = $this->_iMIPFrontendMock->process($iMIP, Calendar_Model_Attender::STATUS_ACCEPTED);
+        
         $this->_iMIPFrontend->prepareComponent($iMIP);
         $this->_eventIdsToDelete[] = $iMIP->event->getId();
         
@@ -260,16 +301,18 @@ class Calendar_Frontend_iMIPTest extends TestCase
         $this->assertTrue(empty($iMIP->event->organizer->account_id), 'organizer must not have an account');
         
         // assert attendee
-        $this->assertEquals(1, count($iMIP->event->attendee), 'all attendee but curruser must be whiped');
-        $this->assertEquals($this->_getEmailAddress(), $iMIP->event->attendee->getFirstRecord()->user_id->email, 'wrong attendee mail');
-        $this->assertEquals(Tinebase_Core::getUser()->getId(), $iMIP->event->attendee->getFirstRecord()->user_id->account_id, 'wrong attendee');
-        $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $iMIP->event->attendee->getFirstRecord()->status);
+        $ownAttendee = Calendar_Model_Attender::getOwnAttender($iMIP->event->attendee);
+        $this->assertTrue(!! $ownAttendee, 'own attendee missing');
+        $this->assertEquals(5, count($iMIP->event->attendee), 'all attendee must be keeped');
+        $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $ownAttendee->status, 'must be ACCEPTED');
         
-        // assert REPLY message
+        // assert REPLY message to organizer only
         $messages = Calendar_Controller_EventNotificationsTests::getMessages();
-        $this->assertEquals(1, count($messages), 'only one mails should be send');
+        $this->assertEquals(1, count($messages), 'exactly one mail should be send');
         $this->assertTrue(in_array('l.kneschke@caldav.org', $messages[0]->getRecipients()), 'organizer is not a receipient');
+        $this->assertContains('accepted', $messages[0]->getSubject(), 'wrong subject');
         $this->assertContains('METHOD:REPLY', var_export($messages[0], TRUE), 'method missing');
+        $this->assertContains('SEQUENCE:0', var_export($messages[0], TRUE), 'external sequence has not been keepted');
     }
     
     /**
@@ -358,9 +401,11 @@ class Calendar_Frontend_iMIPTest extends TestCase
     }
     
     /**
-     * testInvitationInternalReplyPreconditions
+     * testInternalInvitationReplyPreconditions
+     * 
+     * an internal reply does not need to be processed of course
      */
-    public function testInvitationInternalReplyPreconditions()
+    public function testInternalInvitationReplyPreconditions()
     {
         $iMIP = $this->_getiMIP('REPLY');
         $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
@@ -370,10 +415,11 @@ class Calendar_Frontend_iMIPTest extends TestCase
     }
     
     /**
-     * test no seq update
-     * test no notifications
-     *
-    public function testInvitationInternalReplyAutoProcess()
+     * testInternalInvitationReplyAutoProcess
+     * 
+     * an internal reply does not need to be processed of course
+     */
+    public function testInternalInvitationReplyAutoProcess()
     {
         // flush mailer
         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
@@ -384,14 +430,15 @@ class Calendar_Frontend_iMIPTest extends TestCase
         $iMIP = $this->_getiMIP('REPLY', TRUE);
         $event = $iMIP->getEvent();
         
-        print_r($event->getId());
         try {
             $this->_iMIPFrontend->autoProcess($iMIP);
         } catch (Exception $e) {
-            $this->fail('autoProcess throwed Exception');
+            $this->assertContains('TOPROCESS', $e->getMessage());
+            return;
         }
+        
+        $this->fail("autoProcess did not throw TOPROCESS Exception $e");
     }
-    */
     
     /**
      * testInvitationExternalReply
index b1cb11f..b1bc51f 100644 (file)
@@ -548,7 +548,7 @@ class Calendar_JsonTests extends Calendar_TestCase
         
         // sclever has only READ grant
         Tinebase_Container::getInstance()->setGrants($this->_testCalendar, new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(array(
-            'account_id'    => $this->_testUser->getId(),
+            'account_id'    => $this->_originalTestUser->getId(),
             'account_type'  => 'user',
             Tinebase_Model_Grants::GRANT_READ     => true,
             Tinebase_Model_Grants::GRANT_ADD      => true,
@@ -564,12 +564,11 @@ class Calendar_JsonTests extends Calendar_TestCase
             Tinebase_Model_Grants::GRANT_FREEBUSY => true,
         ))), TRUE);
         
-        $unittestUser = Tinebase_Core::getUser();
         Tinebase_Core::set(Tinebase_Core::USER, $this->_personas['sclever']);
         
         // create persistent exception
         $createdException = $this->_uit->createRecurException($persistentException, FALSE, FALSE);
-        Tinebase_Core::set(Tinebase_Core::USER, $unittestUser);
+        Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
         
         $sclever = $this->_findAttender($createdException['attendee'], 'sclever');
         $this->assertEquals('Susan Clever', $sclever['user_id']['n_fn']);
@@ -728,7 +727,7 @@ class Calendar_JsonTests extends Calendar_TestCase
             Tinebase_Model_Grants::GRANT_ADMIN    => true,
             Tinebase_Model_Grants::GRANT_FREEBUSY => true,
         ), array(
-            'account_id'    => $this->_testUser->getId(),
+            'account_id'    => $this->_originalTestUser->getId(),
             'account_type'  => 'user',
             Tinebase_Model_Grants::GRANT_FREEBUSY => true,
         ))), TRUE);
@@ -748,7 +747,7 @@ class Calendar_JsonTests extends Calendar_TestCase
         $this->assertTrue(! empty($searchResultData['results']), 'expected event in search result (search by sclever): ' 
             . print_r($eventData, TRUE) . 'search filter: ' . print_r($filter, TRUE));
         
-        Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
+        Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
         $searchResultData = $this->_uit->searchEvents($filter, array());
         $this->assertTrue(! empty($searchResultData['results']), 'expected (freebusy cleanup) event in search result: ' 
             . print_r($eventData, TRUE) . 'search filter: ' . print_r($filter, TRUE));
index eb6909a..9167b2c 100644 (file)
@@ -58,11 +58,11 @@ class Calendar_Model_AttenderTests extends Calendar_TestCase
         $newAttendees = array(
             array(
                 'userType'    => Calendar_Model_Attender::USERTYPE_USER,
-                'firstName'   => $this->_testUser->accountFirstName,
-                'lastName'    => $this->_testUser->accountLastName,
+                'firstName'   => $this->_originalTestUser->accountFirstName,
+                'lastName'    => $this->_originalTestUser->accountLastName,
                 'partStat'    => Calendar_Model_Attender::STATUS_ACCEPTED,
                 'role'        => Calendar_Model_Attender::ROLE_REQUIRED,
-                'email'       => $this->_testUser->accountEmailAddress
+                'email'       => $this->_originalTestUser->accountEmailAddress
             ),
             array(
                 'userType'    => Calendar_Model_Attender::USERTYPE_USER,
@@ -103,11 +103,11 @@ class Calendar_Model_AttenderTests extends Calendar_TestCase
         $newAttendees = array(
             array(
                 'userType'    => Calendar_Model_Attender::USERTYPE_USER,
-                'firstName'   => $this->_testUser->accountFirstName,
-                'lastName'    => $this->_testUser->accountLastName,
+                'firstName'   => $this->_originalTestUser->accountFirstName,
+                'lastName'    => $this->_originalTestUser->accountLastName,
                 'partStat'    => Calendar_Model_Attender::STATUS_TENTATIVE,
                 'role'        => Calendar_Model_Attender::ROLE_REQUIRED,
-                'email'       => $this->_testUser->accountEmailAddress
+                'email'       => $this->_originalTestUser->accountEmailAddress
             ),
             array(
                 'userType'    => Calendar_Model_Attender::USERTYPE_GROUP,
@@ -150,7 +150,7 @@ class Calendar_Model_AttenderTests extends Calendar_TestCase
         // current account must not be a groupmember
         $this->assertFalse(!! Calendar_Model_Attender::getAttendee($persistentEvent->attendee, new Calendar_Model_Attender(array(
             'user_type'    => Calendar_Model_Attender::USERTYPE_GROUPMEMBER,
-            'user_id'      => $this->_testUser->contact_id
+            'user_id'      => $this->_originalTestUser->contact_id
         ))), 'found user as groupmember');
         
         $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, Calendar_Model_Attender::getOwnAttender($persistentEvent->attendee)->status);
index 56754e7..cdc009b 100644 (file)
@@ -14,21 +14,10 @@ require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHe
  * 
  * @package     Calendar
  */
-class Calendar_Setup_DemoDataTests extends PHPUnit_Framework_TestCase
+class Calendar_Setup_DemoDataTests extends TestCase
 {
-    
-    public function setUp()
+    public function testCreateDemoCalendars()
     {
-         Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
-    }
-
-    public function tearDown()
-    {
-         Tinebase_TransactionManager::getInstance()->rollBack();
-    }
-    
-    public function testCreateDemoCalendars() {
-        
         ob_start();
         Calendar_Setup_DemoData::getInstance()->createDemoData(array('locale' => 'en'));
         ob_end_clean();
@@ -48,7 +37,5 @@ class Calendar_Setup_DemoDataTests extends PHPUnit_Framework_TestCase
         
         $this->assertEquals($businessEvents->count(), 1);
         $this->assertEquals($sharedEvents->count(), 10);
-        
-        
     }
 }
index 77c7c43..bb9eb8d 100644 (file)
@@ -31,11 +31,6 @@ abstract class Calendar_TestCase extends TestCase
     protected $_testCalendars;
     
     /**
-     * @var Tinebase_Model_FullUser
-     */
-    protected $_testUser;
-    
-    /**
      * @var Addressbook_Model_Contact
      */
     protected $_testUserContact;
@@ -76,8 +71,7 @@ abstract class Calendar_TestCase extends TestCase
             $this->_personasDefaultCals[$loginName] = Tinebase_Container::getInstance()->getContainerById($defaultCalendarId);
         }
         
-        $this->_testUser = Tinebase_Core::getUser();
-        $this->_testUserContact = Addressbook_Controller_Contact::getInstance()->getContactByUserId($this->_testUser->getId());
+        $this->_testUserContact = Addressbook_Controller_Contact::getInstance()->getContactByUserId($this->_originalTestUser->getId());
         $this->_testCalendar = $this->_getTestContainer('Calendar');
         
         $this->_testCalendars = new Tinebase_Record_RecordSet('Tinebase_Model_Container');
@@ -111,12 +105,6 @@ abstract class Calendar_TestCase extends TestCase
                 Tinebase_Container::getInstance()->deleteContainer($cal, true);
             }
         }
-        
-        if ($this->_testUser->getId() !== Tinebase_Core::getUser()->getId()) {
-            // reset test user
-            Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
-        }
-        
     }
     
     /**
index df0ba86..b122e0b 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 /**
- * Test class for Tinebase_Group
+ * Test class for Phone_JsonTest
  */
 class Phone_JsonTest extends TestCase
 {
index 1a2e574..1550b96 100644 (file)
@@ -94,6 +94,46 @@ class Calendar_Controller extends Tinebase_Controller_Event implements Tinebase_
     }
     
     /**
+     * Get/Create Calendar for external organizer
+     * 
+     * @param  Addressbook_Model_Contact $organizer organizer id
+     * @return Tinebase_Model_Container  container id
+     */
+    public function getInvitationContainer($organizer)
+    {
+        $containerName = $organizer->getPreferedEmailAddress();
+        
+        try {
+            $container = Tinebase_Container::getInstance()->getContainerByName('Calendar', $containerName, Tinebase_Model_Container::TYPE_SHARED);
+        } catch (Tinebase_Exception_NotFound $tenf) {
+            if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+                . ' No invitation container found. Creating a new one for organizer ' . $containerName);
+            
+            $container = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
+                'name'              => $containerName,
+                'color'             => '#333399',
+                'type'              => Tinebase_Model_Container::TYPE_SHARED,
+                'backend'           => Tinebase_User::SQL,
+                'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
+                'model'             => 'Calendar_Model_Event'
+            )), NULL, TRUE);
+            
+            $grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(
+                array(
+                    'account_id'      => '0',
+                    'account_type'    => Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE,
+                    Tinebase_Model_Grants::GRANT_ADD         => true,
+                    Tinebase_Model_Grants::GRANT_EDIT        => true,
+                    Tinebase_Model_Grants::GRANT_DELETE      => true,
+                )
+            ));
+            Tinebase_Container::getInstance()->setGrants($container->getId(), $grants, true, false);
+        }
+         
+        return $container;
+    }
+    
+    /**
      * creates the initial folder for new accounts
      *
      * @param mixed[int|Tinebase_Model_User] $_account   the accountd object
@@ -109,7 +149,7 @@ class Calendar_Controller extends Tinebase_Controller_Event implements Tinebase_
             'name'              => sprintf($translation->_("%s's personal calendar"), $account->accountFullName),
             'type'              => Tinebase_Model_Container::TYPE_PERSONAL,
             'owner_id'          => $_account,
-            'backend'           => 'Sql',
+            'backend'           => Tinebase_User::SQL,
             'color'             => '#FF6600',
             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Calendar')->getId(),
             'model'             => static::$_defaultModel
index 1d33da1..156379c 100644 (file)
@@ -1408,10 +1408,34 @@ class Calendar_Controller_Event extends Tinebase_Controller_Record_Abstract impl
         $_record->organizer = $_record->organizer ? $_record->organizer : Tinebase_Core::getUser()->contact_id;
         $_record->transp = $_record->transp ? $_record->transp : Calendar_Model_Event::TRANSP_OPAQUE;
         
-        // external organizer (iTIP)
-        if (! $_record->resolveOrganizer()->account_id && count($_record->attendee) > 1) {
-            $ownAttendee = Calendar_Model_Attender::getOwnAttender($_record->attendee);
-            $_record->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender', $ownAttendee ? array($ownAttendee) : array());
+        if ($_record->hasExternalOrganizer()) {
+            // assert calendarUser as attendee. This is important to keep the event in the loop via its displaycontianer(s)
+            try {
+                $container = Tinebase_Container::getInstance()->getContainerById($_record->container_id);
+                $owner = $container->getOwner();
+                $calendarUserId = Addressbook_Controller_Contact::getInstance()->getContactByUserId($owner, true)->getId();
+            } catch (Exception $e) {
+                $container = NULL;
+                $calendarUserId = Tinebase_Core::getUser()->contact_id;
+            }
+            
+            $attendee = $_record->assertAttendee(new Calendar_Model_Attender(array(
+                'user_type'    => Calendar_Model_Attender::USERTYPE_USER,
+                'user_id'      => $calendarUserId
+            )), false, false, true);
+            
+            if ($attendee && $container instanceof Tinebase_Model_Container) {
+                $attendee->displaycontainer_id = $container->getId();
+            }
+            
+            if (! $container instanceof Tinebase_Model_Container || $container->type == Tinebase_Model_Container::TYPE_PERSONAL) {
+                // move into spechial (external users) container
+                $container = Calendar_Controller::getInstance()->getInvitationContainer($_record->resolveOrganizer());
+                if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+                    . ' Setting container_id to ' . $container->getId() . ' for external organizer ' . $_record->organizer->email);
+                $_record->container_id = $container->getId();
+            }
+            
         }
         
         if ($_record->is_all_day_event) {
@@ -1517,10 +1541,12 @@ class Calendar_Controller_Event extends Tinebase_Controller_Record_Abstract impl
             // admin grant includes all others (only if class is PUBLIC)
             ||  (! empty($this->class) && $this->class === Calendar_Model_Event::CLASS_PUBLIC 
                 && $_record->container_id && Tinebase_Core::getUser()->hasGrant($_record->container_id, Tinebase_Model_Grants::GRANT_ADMIN))
+            // external invitations are in a spechial invitaion calendar. only attendee can see it via displaycal
+            ||  $_record->hasExternalOrganizer()
         ) {
-            return TRUE;
+            return true;
         }
-
+        
         switch ($_action) {
             case 'get':
                 // NOTE: free/busy is not a read grant!
@@ -1555,7 +1581,8 @@ class Calendar_Controller_Event extends Tinebase_Controller_Record_Abstract impl
             if ($_throw) {
                 throw new Tinebase_Exception_AccessDenied($_errorMessage);
             } else {
-                if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . 'No permissions to ' . $_action . ' in container ' . $_record->container_id);
+                if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+                    . ' No permissions to ' . $_action . ' in container ' . $_record->container_id);
             }
         }
         
index c6c2449..56d4217 100644 (file)
             
             // check if user wants this notification NOTE: organizer gets mails unless she set notificationlevel to NONE
             // NOTE prefUser is organzier for external notifications
-            if (($attendeeAccountId == $_updater->getId() && ! $sendOnOwnActions) || ($sendLevel < $_notificationLevel && ($attendeeAccountId != $organizerAccountId || $sendLevel == self::NOTIFICATION_LEVEL_NONE))) {
+            if (($attendeeAccountId == $_updater->getId() && ! $sendOnOwnActions) 
+                || ($sendLevel < $_notificationLevel && (
+                        $attendee->getPreferedEmailAddress() != $organizer->getPreferedEmailAddress() 
+                        || $sendLevel == self::NOTIFICATION_LEVEL_NONE)
+                   )
+                ) {
                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
                     . " Preferred notification level not reached -> skipping notification for {$_attender->getEmail()}");
                 return;
             }
             
-            $method = NULL;
+            $method = NULL; // NOTE $method gets set in _getSubject as referenced param
             $messageSubject = $this->_getSubject($_event, $_notificationLevel, $_action, $_updates, $timezone, $locale, $translate, $method);
             
             // we don't send iMIP parts to external attendee if config is active
      * get notification attachments
      * 
      * @param string $method
-     * @param Calendar_Model_Event $_event
+     * @param Calendar_Model_Event $event
      * @param string $_action
-     * @param Tinebase_Model_FullAccount $_updater
+     * @param Tinebase_Model_FullAccount $updater
      * @param Zend_Mime_Part $calendarPart
      * @return array
      */
-    protected function _getAttachments($method, $_event, $_action, $_updater, &$calendarPart)
+    protected function _getAttachments($method, $event, $_action, $updater, &$calendarPart)
     {
         if ($method === NULL) {
             return array();
         }
         
-        $converter = Calendar_Convert_Event_VCalendar_Factory::factory(Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC);
-        $converter->setMethod($method);
-        $vcalendar = $converter->fromTine20Model($_event);
-
-        // in Tine 2.0 non organizers might be given the grant to update events
-        // @see rfc6047 section 2.2.1 & rfc5545 section 3.2.18
-        if ($method != Calendar_Model_iMIP::METHOD_REPLY && $_event->organizer !== $_updater->contact_id) {
-            foreach ($vcalendar->children() as $component) {
-                if ($component->name == 'VEVENT') {
-                    if (isset($component->{'ORGANIZER'})) {
-                        $component->{'ORGANIZER'}->add('SENT-BY', 'mailto:' . $_updater->accountEmailAddress);
-                    }
-                }
-            }
-        }
-        
-        // @TODO in Tine 2.0 status updater might not be updater
-        if ($method == Calendar_Model_iMIP::METHOD_REPLY) {
-            foreach ($vcalendar->children() as $component) {
-                if ($component->name == 'VEVENT') {
-                    $component->{'REQUEST-STATUS'} = '2.0;Success';
-                }
-            }
-        }
+        $vcalendar = $this->_createVCalendar($event, $method, $updater);
         
         $calendarPart           = new Zend_Mime_Part($vcalendar->serialize());
         $calendarPart->charset  = 'UTF-8';
         
         // add other attachments (only on invitation)
         if ($_action == 'created') {
-            $eventAttachments = $this->_getEventAttachments($_event);
+            $eventAttachments = $this->_getEventAttachments($event);
             $attachments = array_merge($attachments, $eventAttachments);
         }
         
     }
     
     /**
+     * create iMIP VCALENDAR
+     * 
+     * @param Calendar_Model_Event $event
+     * @param string $method
+     * @param Tinebase_Model_FullAccount $updater
+     * @return Sabre\VObject\Component
+     */
+    protected function _createVCalendar($event, $method, $updater)
+    {
+        $converter = Calendar_Convert_Event_VCalendar_Factory::factory(Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC);
+        $converter->setMethod($method);
+        $vcalendar = $converter->fromTine20Model($event);
+        
+        foreach ($vcalendar->children() as $component) {
+            if ($component->name == 'VEVENT') {
+                if ($method != Calendar_Model_iMIP::METHOD_REPLY && $event->organizer !== $updater->contact_id) {
+                    if (isset($component->{'ORGANIZER'})) {
+                        // in Tine 2.0 non organizers might be given the grant to update events
+                        // @see rfc6047 section 2.2.1 & rfc5545 section 3.2.18
+                        $component->{'ORGANIZER'}->add('SENT-BY', 'mailto:' . $updater->accountEmailAddress);
+                    }
+                } else if ($method == Calendar_Model_iMIP::METHOD_REPLY) {
+                    // TODO in Tine 2.0 status updater might not be updater
+                    $component->{'REQUEST-STATUS'} = '2.0;Success';
+                }
+            }
+        }
+        
+        return $vcalendar;
+    }
+    
+    /**
      * get event attachments
      * 
      * @param Calendar_Model_Event $_event
index 14b1199..fc1ee17 100644 (file)
@@ -122,9 +122,10 @@ class Calendar_Convert_Event_VCalendar_Abstract extends Tinebase_Convert_VCalend
             'LAST-MODIFIED' => $lastModifiedDateTime->getClone()->setTimezone('UTC'),
             'DTSTAMP'       => Tinebase_DateTime::now(),
             'UID'           => $event->uid,
-            'SEQUENCE'      => $event->seq
         ));
-
+        
+        $vevent->add('SEQUENCE', $event->hasExternalOrganizer() ? $event->external_seq : $event->seq);
+        
         if ($event->isRecurException()) {
             $originalDtStart = $_event->getOriginalDtStart()->setTimezone($_event->originator_tz);
             
@@ -734,6 +735,8 @@ class Calendar_Convert_Event_VCalendar_Abstract extends Tinebase_Convert_VCalend
                     if (! isset($options[self::OPTION_USE_SERVER_MODLOG]) || $options[self::OPTION_USE_SERVER_MODLOG] !== true) {
                         $event->seq = $property->getValue();
                     }
+                    // iMIP only
+                    $event->external_seq = $property->getValue();
                     break;
                     
                 case 'DESCRIPTION':
index 6b6b152..1fa6466 100644 (file)
@@ -6,7 +6,7 @@
  * @subpackage  Frontend
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Cornelius Weiss <c.weiss@metaways.de>
- * @copyright   Copyright (c) 2011-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2011-2014 Metaways Infosystems GmbH (http://www.metaways.de)
  */
 
 /**
@@ -190,9 +190,23 @@ class Calendar_Frontend_iMIP
         $result &= $this->_assertOrganizer($_iMIP, TRUE, TRUE);
         
         $existingEvent = $_iMIP->getExistingEvent();
-        if ($existingEvent && $_iMIP->getEvent()->isObsoletedBy($existingEvent)) {
-            $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_RECENT, "old iMIP message");
-            $result = FALSE;
+        if ($existingEvent) {
+            $iMIPEvent = $_iMIP->getEvent();
+            $isObsoleted = false;
+            
+            if (! $existingEvent->hasExternalOrganizer() && $iMIPEvent->isObsoletedBy($existingEvent)) {
+                $isObsoleted = true;
+            }
+            
+            else if ($iMIPEvent->external_seq < $existingEvent->external_seq) {
+                $isObsoleted = true;
+            }
+            
+            // allow if not rescheduled
+            if ($isObsoleted && $existingEvent->isRescheduled($iMIPEvent)) {
+                $_iMIP->addFailedPrecondition(Calendar_Model_iMIP::PRECONDITION_RECENT, "old iMIP message");
+                $result = FALSE;
+            }
         }
         
         return $result;
@@ -252,17 +266,17 @@ class Calendar_Frontend_iMIP
     }
     
     /**
-    * returns and optionally asserts own attendee record
-    *
-    * @param  Calendar_Model_iMIP   $_iMIP
-    * @param  bool                  $_assertExistence
-    * @param  bool                  $_assertOriginator
-    * @param  bool                  $_assertAccount
-    * @return Addressbook_Model_Contact
-    * @throws Calendar_Exception_iMIP
-    * 
-    * @todo this needs to be splitted into assertExternalOrganizer / assertInternalOrganizer
-    */
+     * 
+     *
+     * @param  Calendar_Model_iMIP   $_iMIP
+     * @param  bool                  $_assertExistence
+     * @param  bool                  $_assertOriginator
+     * @param  bool                  $_assertAccount
+     * @return Addressbook_Model_Contact
+     * @throws Calendar_Exception_iMIP
+     
+     * @todo this needs to be splitted into assertExternalOrganizer / assertInternalOrganizer
+     */
     protected function _assertOrganizer($_iMIP, $_assertExistence, $_assertOriginator, $_assertAccount = false)
     {
         $result = TRUE;
@@ -303,10 +317,6 @@ class Calendar_Frontend_iMIP
      * 
      * @param  Calendar_Model_iMIP   $_iMIP
      * @param  string                $_status
-     * @throws Tinebase_Exception_NotImplemented
-     * 
-     * @todo handle external organizers
-     * @todo create event in the organizers context
      */
     protected function _processRequest($_iMIP, $_status)
     {
@@ -336,34 +346,39 @@ class Calendar_Frontend_iMIP
         
         // external organizer:
         else {
-            if ($ownAttender && $_status) {
-                $ownAttender->status = $_status;
-            }
-            
+            $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(false);
             if (! $existingEvent) {
                 $event = $_iMIP->getEvent();
                 if (! $event->container_id) {
                     $event->container_id = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR};
                 }
                 
-                $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event);
+                $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event);
             } else {
-                $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($existingEvent);
+                $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($existingEvent);
             }
             
-            //  - send reply to organizer
+            Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications);
+            
+            $ownAttender = Calendar_Model_Attender::getOwnAttender($event->attendee);
+            
+            // NOTE: we do the status update in a separate call to trigger the right notifications
+            if ($ownAttender && $_status) {
+                $ownAttender->status = $_status;
+                $a = Calendar_Controller_Event::getInstance()->attenderStatusUpdate($event, $ownAttender, $ownAttender->status_authkey);
+            }
         }
     }
     
     /**
-    * reply precondition
-    *
-    * @TODO an internal reply should trigge a RECENT precondition
-    * @TODO distinguish RECENT and PROCESSED preconditions?
-    * 
-    * @param  Calendar_Model_iMIP   $_iMIP
-    * @return boolean
-    */
+     * reply precondition
+     *
+     * @TODO an internal reply should trigger a RECENT precondition
+     * @TODO distinguish RECENT and PROCESSED preconditions?
+     
+     * @param  Calendar_Model_iMIP   $_iMIP
+     * @return boolean
+     */
     protected function _checkReplyPreconditions($_iMIP)
     {
         $result = TRUE;
index 8bbb97e..a841801 100644 (file)
@@ -84,6 +84,7 @@ class Calendar_Model_Event extends Tinebase_Record_Abstract
         'deleted_by'           => array(Zend_Filter_Input::ALLOW_EMPTY => true          ),
         'seq'                  => array(Zend_Filter_Input::ALLOW_EMPTY => true,  'Int'  ),
         // calendar only fields
+        'external_seq'         => array(Zend_Filter_Input::ALLOW_EMPTY => true,  'Int'  ), // external seq for caldav / imip update handling
         'dtend'                => array(Zend_Filter_Input::ALLOW_EMPTY => true          ),
         'transp'               => array(
             Zend_Filter_Input::ALLOW_EMPTY => true,
@@ -265,10 +266,11 @@ class Calendar_Model_Event extends Tinebase_Record_Abstract
     /**
      * add given attendee if not present under given conditions
      * 
-     * @param Calendar_Model_Attender $attendee
-     * @param bool                    $ifOrganizer        only add attendee if he's organizer
-     * @param bool                    $ifNoOtherAttendee  only add attendee if no other attendee are present
-     * @param bool                    $personalOnly       only for personal containers
+     * @param Calendar_Model_Attender  $attendee
+     * @param bool                     $ifOrganizer        only add attendee if he's organizer
+     * @param bool                     $ifNoOtherAttendee  only add attendee if no other attendee are present
+     * @param bool                     $personalOnly       only for personal containers
+     * @return Calendar_Model_Attender asserted attendee
      */
     public function assertAttendee($attendee, $ifOrganizer = true, $ifNoOtherAttendee = false, $personalOnly = false)
     {
@@ -304,7 +306,7 @@ class Calendar_Model_Event extends Tinebase_Record_Abstract
                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
                     __METHOD__ . '::' . __LINE__ . " adding attendee.");
                 
-                $newAttender = new Calendar_Model_Attender(array(
+                $assertionAttendee = new Calendar_Model_Attender(array(
                     'user_id'   => $attendee->user_id,
                     'user_type' => $attendee->user_type,
                     'status'    => Calendar_Model_Attender::STATUS_ACCEPTED,
@@ -314,9 +316,11 @@ class Calendar_Model_Event extends Tinebase_Record_Abstract
                 if (! $this->attendee instanceof Tinebase_Record_RecordSet) {
                     $this->attendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender');
                 }
-                $this->attendee->addRecord($newAttender);
+                $this->attendee->addRecord($assertionAttendee);
             }
         }
+        
+        return $assertionAttendee;
     }
     
     /**
@@ -667,4 +671,16 @@ class Calendar_Model_Event extends Tinebase_Record_Abstract
         
         return $organizerContactId == ($this->organizer instanceof Tinebase_Record_Abstract ? $this->organizer->getId() : $this->organizer);
     }
+    
+    /**
+     * returns true if organizer is external
+     * 
+     * @return boolean
+     */
+    public function hasExternalOrganizer()
+    {
+        $organizer = $this->resolveOrganizer();
+        
+        return $organizer instanceof Addressbook_Model_Contact && ! $organizer->account_id;
+    }
 }
index 98edcdc..59d7bcf 100644 (file)
@@ -187,8 +187,28 @@ class Calendar_Setup_Update_Release8 extends Setup_Update_Abstract
                 </field>
             </index>');
         $this->_backend->addIndex('cal_events', $declaration);
-        
         $this->setTableVersion('cal_events', 7);
         $this->setApplicationVersion('Calendar', '8.4');
     }
+
+    /**
+     * adds external_seq col
+     * 
+     * @see 0009890: improve external event invitation support
+     */
+    public function update_4()
+    {
+        $seqCol = '<field>
+            <name>external_seq</name>
+            <type>integer</type>
+            <notnull>true</notnull>
+            <default>0</default>
+        </field>';
+        
+        $declaration = new Setup_Backend_Schema_Field_Xml($seqCol);
+        $this->_backend->addCol('cal_events', $declaration);
+        
+        $this->setTableVersion('cal_events', 8);
+        $this->setApplicationVersion('Calendar', '8.5');
+    }
 }
index 3d8388f..aae0e10 100644 (file)
@@ -2,14 +2,14 @@
 <application>
     <name>Calendar</name>
     <!-- gettext('Calendar') -->   
-    <version>8.4</version>
+    <version>8.5</version>
     <order>15</order>
     <status>enabled</status>
     <tables>
         <!-- events -->
         <table>
             <name>cal_events</name>
-            <version>7</version>
+            <version>8</version>
             <declaration>
                 <field>
                     <name>id</name>
                     <default>0</default>
                 </field>
                 <field>
+                    <name>external_seq</name>
+                    <type>integer</type>
+                    <notnull>true</notnull>
+                    <default>0</default>
+                </field>
+                <field>
                     <name>dtend</name>
                     <type>datetime</type>
                     <notnull>true</notnull>
index 45f99c5..a9a1764 100644 (file)
@@ -238,7 +238,7 @@ Tine.Calendar.iMIPDetailsPanel = Ext.extend(Tine.Calendar.EventDetailsPanel, {
                     this.iMIPclause.setText(this.app.i18n._('An invited attendee responded to the invitation.'));
                     break;
                     
-                default:            
+                default:
                     this.iMIPclause.setText(this.app.i18n._("Unsupported method"));
                     break;
             }
@@ -249,7 +249,7 @@ Tine.Calendar.iMIPDetailsPanel = Ext.extend(Tine.Calendar.EventDetailsPanel, {
         singleRecordPanel.setHeight(150);
         
         
-        this.record = event;
-        singleRecordPanel.loadRecord(event);
+        this.record = existingEvent && ! preconditions ? existingEvent : event;
+        singleRecordPanel.loadRecord(this.record);
     }
 });
index a764035..08fc6cc 100644 (file)
@@ -480,7 +480,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      */
     public function getContainerByName($appName, $containerName, $type, $ownerId = NULL)
     {
-        if ($type !== Tinebase_Model_Container::TYPE_PERSONAL && $type !== Tinebase_Model_Container::TYPE_SHARED) {
+        if (! in_array($type, array(Tinebase_Model_Container::TYPE_PERSONAL, Tinebase_Model_Container::TYPE_SHARED))) {
             throw new Tinebase_Exception_UnexpectedValue ("Invalid type $type supplied.");
         }
         
@@ -570,7 +570,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         $stmt = $this->_db->query($select);
         $containersData = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
         
-        // if no containers where found,  maybe something went wrong when creating the initial folder
+        // if no containers where found, maybe something went wrong when creating the initial folder
         // let's check if the controller of the application has a function to create the needed folders
         if (empty($containersData) and $accountId === $ownerId) {
             $application = Tinebase_Core::getApplicationInstance($application->name);