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

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

@@@ -2,18 -2,18 +2,18 @@@
  /**
   * Tine 2.0 - http://www.tine20.org
   * 
 - * @package     ActiveSync
 + * @package     Calendar
   * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
   * @copyright   Copyright (c) 2010-2014 Metaways Infosystems GmbH (http://www.metaways.de)
   * @author      Cornelius Weiss <c.weiss@metaways.de>
   */
  
  /**
 - * Test class for Calendar_Controller_Event
 + * Test class for Calendar_Frontend_ActiveSync
   * 
 - * @package     ActiveSync
 + * @package     Calendar
   */
 -class ActiveSync_Controller_CalendarTests extends ActiveSync_Controller_ControllerTest
 +class Calendar_Frontend_ActiveSyncTest extends ActiveSync_Controller_ControllerTest
  {
      /**
       * name of the application
       */
      protected $_applicationName = 'Calendar';
      
 -    protected $_controllerName = 'ActiveSync_Controller_Calendar';
 +    protected $_controllerName = 'Calendar_Frontend_ActiveSync';
      
      protected $_class = Syncroton_Data_Factory::CLASS_CALENDAR;
      
 -    protected $_prefs;
 -    
 -    protected $_defaultCalendar;
 -    
 -    protected $_defaultHasChanged = false;
 -    
      /**
       * @var array test objects
       */
@@@ -51,7 -57,7 +51,7 @@@
                              <Calendar:BusyStatus>2</Calendar:BusyStatus>
                              <Calendar:DtStamp>20121125T150537Z</Calendar:DtStamp>
                              <Calendar:EndTime>20121123T160000Z</Calendar:EndTime>
 -                            <Calendar:Sensitivity>0</Calendar:Sensitivity>
 +                            <Calendar:Sensitivity>2</Calendar:Sensitivity>
                              <Calendar:Subject>Repeat</Calendar:Subject>
                              <Calendar:StartTime>20121123T130000Z</Calendar:StartTime>
                              <Calendar:UID>6de7cb687964dc6eea109cd81750177979362217</Calendar:UID>
@@@ -503,8 -509,6 +503,8 @@@ Zeile 3</AirSyncBase:Data
      {
          parent::setUp();
          
 +        Calendar_Controller_Event::getInstance()->doContainerACLChecks(true);
 +        
          // replace email to make current user organizer and attendee
          $testConfig = Zend_Registry::get('testConfig');
          $email = ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
              'lars@kneschke.de',
              'unittest@tine20.org',
          ), $email, $this->_testXMLInput);
 -        
 -        $this->_prefs = $prefs = new Calendar_Preference();
 -        $this->_defaultCalendar = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR};
 -    }
 -    
 -    protected  function tearDown()
 -    {
 -        parent::tearDown();
 -        
 -        if ($this->_defaultHasChanged) {
 -            $this->_prefs->setValue(Calendar_Preference::DEFAULTCALENDAR, $this->_defaultCalendar);
 -        }
      }
      
      /**
          $this->assertEquals(2,         $syncrotonEvent->busyStatus);
          $this->assertEquals('Repeat',  $syncrotonEvent->subject);
          $this->assertEquals(15,        $syncrotonEvent->reminder);
 +        $this->assertEquals(2,         $syncrotonEvent->sensitivity);
          $this->assertTrue($syncrotonEvent->endTime instanceof DateTime);
          $this->assertTrue($syncrotonEvent->startTime instanceof DateTime);
          $this->assertEquals($thisYear . '1123T160000Z', $syncrotonEvent->endTime->format('Ymd\THis\Z'));
          #echo '----------------' . PHP_EOL; foreach ($syncrotonEvent as $key => $value) {echo "$key => "; var_dump($value);}
          
          $this->assertEquals(0, $syncrotonEvent->allDayEvent);
 +        $this->assertEquals(0, $syncrotonEvent->sensitivity);
          $this->assertTrue($syncrotonEvent->endTime instanceof DateTime);
          $this->assertTrue($syncrotonEvent->startTime instanceof DateTime);
          $this->assertEquals('20101220T100000Z', $syncrotonEvent->endTime->format('Ymd\THis\Z'));
          list($serverId, $syncrotonEvent) = $this->testCreateEntry($syncrotonFolder);
          
          // transfer event to other user
 -        $rwright = array_value('rwright', $this->_personas = Zend_Registry::get('personas'));
 +        $rwright = Tinebase_Helper::array_value('rwright', $this->_personas = Zend_Registry::get('personas'));
          $eventBackend = new Calendar_Backend_Sql();
          $eventBackend->updateMultiple($eventBackend->getMultipleByProperty($syncrotonEvent->uID, 'uid')->id, array(
              'container_id'  => Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::DEFAULTCALENDAR, $rwright->getId()),
      
          $folderA = $this->testCreateFolder(); // personal of test user
          
 -        $sclever = array_value('sclever', Zend_Registry::get('personas'));
 +        $sclever = Tinebase_Helper::array_value('sclever', Zend_Registry::get('personas'));
          $folderB = Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::DEFAULTCALENDAR, $sclever->getId());
  
          // have syncGerant for sclever
@@@ -1189,7 -1203,8 +1189,7 @@@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
          $syncrotonFolder2 = $this->testCreateFolder();
          
          //make $syncrotonFolder2 the default
 -        $this->_prefs->setValue(Calendar_Preference::DEFAULTCALENDAR, $syncrotonFolder2->serverId);
 -        $this->_defaultHasChanged = true;
 +        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());
          
          
          $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');
      }
  }
@@@ -2,20 -2,20 +2,20 @@@
  /**
   * Tine 2.0
   *
 - * @package     ActiveSync
 - * @subpackage  Controller
 + * @package     Calendar
 + * @subpackage  Frontend
   * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 - * @copyright   Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
 + * @copyright   Copyright (c) 2009-2014 Metaways Infosystems GmbH (http://www.metaways.de)
   * @author      Cornelius Weiss <c.weiss@metaways.de>
   */
  
  /**
 - * controller events class
 + * ActiveSync frontend class
   * 
 - * @package     ActiveSync
 - * @subpackage  Controller
 + * @package     Calendar
 + * @subpackage  Frontend
   */
 -class ActiveSync_Controller_Calendar extends ActiveSync_Controller_Abstract implements Syncroton_Data_IDataCalendar
 +class Calendar_Frontend_ActiveSync extends ActiveSync_Frontend_Abstract implements Syncroton_Data_IDataCalendar
  {
      /**
       * available filters
          'endTime'           => 'dtend',
          'location'          => 'location',
          'reminder'          => 'alarms',
 -        //'Sensitivity'       => 'class',
 +        'sensitivity'       => 'class',
          'subject'           => 'summary',
          'body'              => 'description',
          'startTime'         => 'dtstart',
      
      /**
       * (non-PHPdoc)
 -     * @see ActiveSync_Controller_Abstract::__construct()
 +     * @see ActiveSync_Frontend_Abstract::__construct()
       */
      public function __construct(Syncroton_Model_IDevice $_device, DateTime $_syncTimeStamp)
      {
      
      /**
       * (non-PHPdoc)
 -     * @see ActiveSync_Controller_Abstract::toSyncrotonModel()
 +     * @see ActiveSync_Frontend_Abstract::toSyncrotonModel()
       * @todo handle BusyStatus
       */
      public function toSyncrotonModel($entry, array $options = array())
                      
                      break;
                      
 +                case 'class':
 +                    $syncrotonEvent->$syncrotonProperty = $entry->$tine20Property == Calendar_Model_Event::CLASS_PRIVATE ? 2 : 0;
 +                    
 +                    break;
 +                    
                  case 'description':
                      $syncrotonEvent->$syncrotonProperty = new Syncroton_Model_EmailBody(array(
                          'type' => Syncroton_Model_EmailBody::TYPE_PLAINTEXT,
                          'data' => $entry->$tine20Property
                      ));
 -                
 +                    
                      break;
 -                
 +                    
                  case 'dtend':
                      if($entry->$tine20Property instanceof DateTime) {
                          if ($entry->is_all_day_event == true) {
              Tinebase_Core::get(Tinebase_Core::CACHE)
          );
          
 -        $syncrotonEvent->timezone = $timeZoneConverter->encodeTimezone(Tinebase_Core::get(Tinebase_Core::USERTIMEZONE));
 +        $syncrotonEvent->timezone = $timeZoneConverter->encodeTimezone(Tinebase_Core::getUserTimezone());
          
          $syncrotonEvent->meetingStatus = 1;
 -        $syncrotonEvent->sensitivity = 0;
          $syncrotonEvent->dtStamp = $entry->creation_time;
          $syncrotonEvent->uID = $entry->uid;
          
      
      /**
       * (non-PHPdoc)
 -     * @see ActiveSync_Controller_Abstract::toTineModel()
 +     * @see ActiveSync_Frontend_Abstract::toTineModel()
       */
      public function toTineModel(Syncroton_Model_IEntry $data, $entry = null)
      {
                      Calendar_Model_Attender::emailsToAttendee($event, $newAttendees);
                      
                      break;
 -
 +                    
 +                case 'class':
 +                    $event->$tine20Property = $data->$syncrotonProperty == 2 ? Calendar_Model_Event::CLASS_PRIVATE : Calendar_Model_Event::CLASS_PUBLIC;
 +                    
 +                    break;
 +                    
                  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);
                      }
              try {
                  $timezone = $timeZoneConverter->getTimezone(
                      $data->timezone,
 -                    Tinebase_Core::get(Tinebase_Core::USERTIMEZONE)
 +                    Tinebase_Core::getUserTimezone()
                  );
                  $event->originator_tz = $timezone;
              } catch (ActiveSync_TimezoneNotFoundException $e) {
                  Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . " timezone data not found " . $data->timezone);
 -                $event->originator_tz = Tinebase_Core::get(Tinebase_Core::USERTIMEZONE);
 +                $event->originator_tz = Tinebase_Core::getUserTimezone();
              }
          
              if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
              $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
       *