Merge branch '2013.10' into 2014.11
authorPhilipp Schüle <p.schuele@metaways.de>
Fri, 15 Jan 2016 13:01:35 +0000 (14:01 +0100)
committerPhilipp Schüle <p.schuele@metaways.de>
Fri, 15 Jan 2016 13:01:35 +0000 (14:01 +0100)
Conflicts:
tests/tine20/Calendar/Frontend/ActiveSyncTest.php

Change-Id: I55a18ae2aae8d982fe02688eb5920652b1a91303

tests/tine20/Calendar/Frontend/ActiveSyncTest.php
tine20/Calendar/Frontend/ActiveSync.php

index 4c5b491..1a38e8d 100644 (file)
@@ -1220,51 +1220,81 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAMA
         
         $this->assertNotEmpty($syncrotonEvent->attendees, 'Events attendees not found in default calendar');
     }
-    
+
     public function testUpdateEntriesIPhone()
     {
+        // create event
         $syncrotonFolder = $this->testCreateFolder();
-        
         $syncrotonFolder2 = $this->testCreateFolder();
         
         //make $syncrotonFolder2 the default
         Tinebase_Core::getPreference('Calendar')->setValue(Calendar_Preference::DEFAULTCALENDAR, $syncrotonFolder2->serverId);
         
         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
-    
         list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder);
-        $event = Calendar_Controller_Event::getInstance()->get($serverId);
-    
-        unset($syncrotonEvent->recurrence);
-        unset($syncrotonEvent->exceptions);
-        unset($syncrotonEvent->attendees);
 
-        // need to create new controller to set new sync timestamp for concurrency handling
+        // update event
+        $syncrotonEventtoUpdate = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
+        $syncrotonEventtoUpdate->exceptions[0]->subject = 'update';
+        $syncrotonEventtoUpdate->exceptions[0]->startTime->addHour(1);
+        $syncrotonEventtoUpdate->exceptions[0]->endTime->addHour(1);
+
         $syncTimestamp = Calendar_Controller_Event::getInstance()->get($serverId)->last_modified_time;
         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), $syncTimestamp);
-        $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEvent);
-        
-        $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
-        $updatedEvent = Calendar_Controller_Event::getInstance()->get($serverId);
+        $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEventtoUpdate);
 
-        $this->assertEmpty($syncrotonEvent->attendees, 'Events attendees found in folder which is not the default calendar');
-        $this->assertNotEmpty($updatedEvent->attendee, 'attendee must be preserved');
-        $this->assertEquals($event->attendee[0]->getId(), $updatedEvent->attendee[0]->getId(), 'attendee must be perserved');
+        // assert update
+        $updatedSyncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
+        $this->assertEquals('update', $updatedSyncrotonEvent->exceptions[0]->subject);
+        $this->assertNotEquals($syncrotonEvent->exceptions[0]->startTime->toString(), $updatedSyncrotonEvent->exceptions[0]->startTime->toString());
+        $this->assertEquals($syncrotonEventtoUpdate->exceptions[0]->startTime->toString(), $updatedSyncrotonEvent->exceptions[0]->startTime->toString());
 
-        return;
-        list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder2);
+        // assert attendee are preserved
+        $updatedEvent = Calendar_Controller_MSEventFacade::getInstance()->get($serverId);
+        $this->assertNotEmpty($updatedEvent->attendee, 'attendee must be preserved during update');
+    }
 
-        unset($syncrotonEvent->recurrence);
-        unset($syncrotonEvent->exceptions);
-        $syncrotonEvent->attendees[0]->name = Tinebase_Record_Abstract::generateUID();
+    /**
+     * testUpdateEntriesIPhoneNonDefaultFolder
+     *
+     * for iOS devices we need to suppress attendee during sync for non default folder (see foldertype in foldersync)
+     * iOS can only have one default folder
+     *
+     * NOTE: our implementation in not correct at the moment as we only append attendee if the event is in the default folder.
+     *       correct implementation would be to append attendee when default folder is synced
+     */
+    public function testUpdateEntriesIPhoneNonDefaultFolder()
+    {
+        // create event in folder1
+        $syncrotonFolder = $this->testCreateFolder();
+        $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), Tinebase_DateTime::now());
+        list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder);
+
+        // create new default folder2
+        $syncrotonFolder2 = $this->testCreateFolder();
+        Tinebase_Core::getPreference('Calendar')->setValue(Calendar_Preference::DEFAULTCALENDAR, $syncrotonFolder2->serverId);
+
+        // load event in non default folder2
+        $syncrotonEventtoUpdate = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder->serverId)), $serverId);
+        $this->assertEmpty($syncrotonEventtoUpdate->attendees, 'attendee in non default folders must be removed for ios');
+
+        // update event in non default folder
+        $syncrotonEventtoUpdate->exceptions[0]->subject = 'update';
+        $syncrotonEventtoUpdate->exceptions[0]->startTime->addHour(1);
+        $syncrotonEventtoUpdate->exceptions[0]->endTime->addHour(1);
 
-        // need to create new controller to set new sync timestamp for concurrency handling
         $syncTimestamp = Calendar_Controller_Event::getInstance()->get($serverId)->last_modified_time;
         $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), $syncTimestamp);
-        $serverId = $controller->updateEntry($syncrotonFolder2->serverId, $serverId, $syncrotonEvent);
+        $serverId = $controller->updateEntry($syncrotonFolder->serverId, $serverId, $syncrotonEventtoUpdate);
 
-        $syncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder2->serverId)), $serverId);
+        // assert update from non default folder
+        $updatedSyncrotonEvent = $controller->getEntry(new Syncroton_Model_SyncCollection(array('collectionId' => $syncrotonFolder2->serverId)), $serverId);
+        $this->assertEquals('update', $updatedSyncrotonEvent->exceptions[0]->subject);
+        $this->assertNotEquals($syncrotonEvent->exceptions[0]->startTime->toString(), $updatedSyncrotonEvent->exceptions[0]->startTime->toString());
+        $this->assertEquals($syncrotonEventtoUpdate->exceptions[0]->startTime->toString(), $updatedSyncrotonEvent->exceptions[0]->startTime->toString());
 
-        $this->assertNotEmpty($syncrotonEvent->attendees, 'Events attendees not found in default calendar');
+        // assert attendee are preserved
+        $updatedEvent = Calendar_Controller_MSEventFacade::getInstance()->get($serverId);
+        $this->assertNotEmpty($updatedEvent->attendee, 'attendee must be preserved during update');
     }
 }
index 69359d0..b3a48f1 100644 (file)
@@ -618,20 +618,17 @@ class Calendar_Frontend_ActiveSync extends ActiveSync_Frontend_Abstract implemen
                 case 'exdate':
                     // handle exceptions from recurrence
                     $exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event');
+                    $oldExdates = $event->exdate instanceof Tinebase_Record_RecordSet ? $event->exdate : new Tinebase_Record_RecordSet('Calendar_Model_Event');
                     
                     foreach ($data->$syncrotonProperty as $exception) {
+                        $eventException = $this->_getRecurException($oldExdates, $exception);
+
                         if ($exception->deleted == 0) {
-                            $eventException = $this->toTineModel($exception);
+                            $eventException = $this->toTineModel($exception, $eventException);
                             $eventException->last_modified_time = new Tinebase_DateTime($this->_syncTimeStamp);
-                            $eventException->recurid            = new Tinebase_DateTime($exception->exceptionStartTime);
-                            $eventException->is_deleted         = false;
-                        } else {
-                            $eventException = new Calendar_Model_Event(array(
-                                'recurid'    => new Tinebase_DateTime($exception->exceptionStartTime),
-                                'is_deleted' => true
-                            ));
                         }
-                        
+
+                        $eventException->is_deleted = (bool) $exception->deleted;
                         $eventException->seq = $entry['seq'];
                         $exdates->addRecord($eventException);
                     }
@@ -817,7 +814,30 @@ class Calendar_Frontend_ActiveSync extends ActiveSync_Frontend_Abstract implemen
             $event->alarms->removeRecord($currentAlarm);
         }
     }
-    
+
+    /**
+     * find a matching exdate or return an empty event record
+     *
+     * @param  Tinebase_Record_RecordSet        $oldExdates
+     * @param  Syncroton_Model_EventException   $sevent
+     * @return Calendar_Model_Event
+     */
+    protected function _getRecurException(Tinebase_Record_RecordSet $oldExdates, Syncroton_Model_EventException $sevent)
+    {
+        // we need to use the user timezone here if this is a DATE (like this: RECURRENCE-ID;VALUE=DATE:20140429)
+        $originalDtStart = new Tinebase_DateTime($sevent->exceptionStartTime);
+
+        foreach ($oldExdates as $id => $oldExdate) {
+            if ($originalDtStart == $oldExdate->getOriginalDtStart()) {
+                return $oldExdate;
+            }
+        }
+
+        return new Calendar_Model_Event(array(
+            'recurid'    => $originalDtStart,
+        ));
+    }
+
     /**
      * append organizer name and email
      *