added missing pieces to detect folder changes
authorLars Kneschke <l.kneschke@metaways.de>
Sat, 9 Feb 2013 07:36:45 +0000 (08:36 +0100)
committerLars Kneschke <l.kneschke@metaways.de>
Sat, 9 Feb 2013 15:22:06 +0000 (16:22 +0100)
we now detect if folder name has changed for example

Change-Id: I2f03e53432b36a9dcc014e994295ac91c38b60d5

docs/syncroton.sql
lib/Syncroton/Command/FolderSync.php
lib/Syncroton/Data/AData.php
lib/Syncroton/Data/IData.php
tests/Syncroton/Command/FolderSyncTests.php
tests/bootstrap.php

index aca77fc..0e46952 100644 (file)
@@ -128,9 +128,11 @@ CREATE TABLE IF NOT EXISTS `syncroton_data` (
 
 CREATE TABLE IF NOT EXISTS `syncroton_data_folder` (
     `id` varchar(40) NOT NULL,
+    `owner_id` varchar(40) NOT NULL,
     `type` int(11) NOT NULL,
     `name` varchar(255) NOT NULL,
-    `owner_id` varchar(40) NOT NULL,
     `parent_id` varchar(40) DEFAULT NULL,
-    PRIMARY KEY (`id`)
+    `creation_time` datetime NOT NULL,
+    `last_modified_time` datetime DEFAULT NULL,
+    PRIMARY KEY (`id`, `owner_id`)
 );
index 84e761e..87d9d3f 100644 (file)
@@ -121,7 +121,8 @@ class Syncroton_Command_FolderSync extends Syncroton_Command_Wbxml
             $this->_headers = array_merge($this->_headers, $optionsCommand->getHeaders());
         }
         
-        $adds = array();
+        $adds    = array();
+        $updates = array();
         $deletes = array();
 
         foreach($this->_classes as $class) {
@@ -138,6 +139,13 @@ class Syncroton_Command_FolderSync extends Syncroton_Command_Wbxml
                 // retrieve all folders available in data backend
                 $serverFolders = $dataController->getAllFolders();
 
+                if ($this->_syncState->counter > 0) {
+                    // retrieve all folders changed since last sync
+                    $changedFolders = $dataController->getChangedFolders($this->_syncState->lastsync, $this->_syncTimeStamp);
+                } else {
+                    $changedFolders = array();
+                }
+                
                 // retrieve all folders sent to client
                 $clientFolders = $this->_folderBackend->getFolderState($this->_device, $class);
                 
@@ -186,6 +194,17 @@ class Syncroton_Command_FolderSync extends Syncroton_Command_Wbxml
                 $adds[] = $add;
             }
 
+            // calculate changed entries
+            foreach ($changedFolders as $changedFolder) {
+                $change = $clientFolders[$changedFolder->serverId];
+                
+                $change->displayName = $changedFolder->displayName;
+                $change->parentId    = $changedFolder->parentId;
+                $change->type        = $changedFolder->type;
+                
+                $updates[] = $change;
+            }
+            
             // calculate deleted entries
             $serverDiff = array_diff($clientFoldersIds, $serverFoldersIds);
             foreach ($serverDiff as $serverFolderId) {
@@ -195,7 +214,7 @@ class Syncroton_Command_FolderSync extends Syncroton_Command_Wbxml
 
         $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', self::STATUS_SUCCESS));
 
-        $count = count($adds) + /*count($changes) + */count($deletes);
+        $count = count($adds) + count($updates) + count($deletes);
         if($count > 0) {
             $this->_syncState->counter++;
             $this->_syncState->lastsync = $this->_syncTimeStamp;
@@ -218,6 +237,14 @@ class Syncroton_Command_FolderSync extends Syncroton_Command_Wbxml
             }
         }
         
+        foreach($updates as $folder) {
+            $update = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Update'));
+            
+            $folder->appendXML($update, $this->_device);
+            
+            $this->_folderBackend->update($folder);
+        }
+        
         foreach($deletes as $folder) {
             $delete = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Delete'));
             $delete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ServerId', $folder->serverId));
index 506a094..16cafa8 100644 (file)
@@ -64,11 +64,12 @@ abstract class Syncroton_Data_AData implements Syncroton_Data_IData
         $id = !empty($folder->serverId) ? $folder->serverId : sha1(mt_rand(). microtime());
         
         $this->_db->insert($this->_tablePrefix . 'data_folder', array(\r
-            'id'        => $id,\r
-            'type'      => $folder->type,\r
-            'name'      => $folder->displayName,
-            'owner_id'  => $this->_ownerId,
-            'parent_id' => $folder->parentId\r
+            'id'            => $id,\r
+            'type'          => $folder->type,\r
+            'name'          => $folder->displayName,
+            'owner_id'      => $this->_ownerId,
+            'parent_id'     => $folder->parentId,
+            'creation_time' => $this->_timestamp->format('Y-m-d H:i:s')\r
         ));\r
         
         return $this->getFolder($id);
@@ -151,6 +152,39 @@ abstract class Syncroton_Data_AData implements Syncroton_Data_IData
     }
     
     /**
+     * retrieve folders which were modified since last sync
+     * 
+     * @param DateTime $startTimeStamp
+     * @param DateTime $endTimeStamp
+     */
+    public function getChangedFolders(DateTime $startTimeStamp, DateTime $endTimeStamp)
+    {
+        $select = $this->_db->select()
+            ->from($this->_tablePrefix . 'data_folder')
+            ->where('type IN (?)', $this->_supportedFolderTypes)
+            ->where('owner_id = ?', $this->_ownerId)
+            ->where('last_modified_time > ?', $startTimeStamp->format('Y-m-d H:i:s'))
+            ->where('last_modified_time <= ?', $endTimeStamp->format('Y-m-d H:i:s'));
+        
+        $stmt    = $this->_db->query($select);
+        $folders = $stmt->fetchAll();
+        $stmt = null; # see https://bugs.php.net/bug.php?id=44081
+        
+        $result = array();
+        
+        foreach ((array) $folders as $folder) {
+            $result[$folder['id']] =  new Syncroton_Model_Folder(array(
+                'serverId'    => $folder['id'],
+                'displayName' => $folder['name'],
+                'type'        => $folder['type'],
+                'parentId'    => $folder['parent_id']
+            ));
+        }
+        
+        return $result;
+    }
+    
+    /**
      * @param  Syncroton_Model_IFolder|string  $_folderId
      * @param  string                        $_filter
      * @return array
@@ -243,11 +277,15 @@ abstract class Syncroton_Data_AData implements Syncroton_Data_IData
     public function updateFolder(Syncroton_Model_IFolder $folder)\r
     {\r
         $this->_db->update($this->_tablePrefix . 'data_folder', array(
-            'name'      => $folder->displayName,
-            'parent_id' => $folder->parentId
+            'name'               => $folder->displayName,
+            'parent_id'          => $folder->parentId,
+            'last_modified_time' => $this->_timestamp->format('Y-m-d H:i:s')
         ), array(
-            'id = ?' => $folder->serverId
+            'id = ?'       => $folder->serverId,
+            'owner_id = ?' => $this->_ownerId
         ));
+        
+        return $this->getFolder($folder->serverId);
     }\r
 }
 
index 6f5ae10..92849e8 100644 (file)
@@ -65,6 +65,14 @@ interface Syncroton_Data_IData
     
     public function getChangedEntries($folderId, DateTime $startTimeStamp, DateTime $endTimeStamp = NULL, $filterType = NULL);
     
+    /**
+     * retrieve folders which were modified since last sync
+     * 
+     * @param DateTime $startTimeStamp
+     * @param DateTime $endTimeStamp
+     */
+    public function getChangedFolders(DateTime $startTimeStamp, DateTime $endTimeStamp);
+    
     public function getCountOfChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState);
     
     /**
index 723e095..93abbc3 100644 (file)
@@ -76,7 +76,12 @@ class Syncroton_Command_FolderSyncTests extends Syncroton_Command_ATestCase
     {
         $this->testGetFoldersSyncKey0();
         
-        Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_CONTACTS, $this->_device, new DateTime('now'))->createFolder(
+        // rewind back last sync timestamp
+        $syncState = Syncroton_Registry::getSyncStateBackend()->getSyncState($this->_device, 'FolderSync');
+        $syncState->lastsync = new DateTime('2013-02-09 10:40:36', new DateTimeZone('utc'));
+        $syncState = Syncroton_Registry::getSyncStateBackend()->update($syncState);
+        
+        Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_CONTACTS, $this->_device, new DateTime(null, new DateTimeZone('utc')))->createFolder(
             new Syncroton_Model_Folder(array(
                 'serverId'    => 'addressbookFolderId2',
                 'parentId'    => null,
@@ -85,6 +90,15 @@ class Syncroton_Command_FolderSyncTests extends Syncroton_Command_ATestCase
             ))
         );
         
+        Syncroton_Data_Factory::factory(Syncroton_Data_Factory::CLASS_CONTACTS, $this->_device, new DateTime(null, new DateTimeZone('utc')))->updateFolder(
+            new Syncroton_Model_Folder(array(
+                'serverId'    => 'anotherAddressbookFolderId',
+                'parentId'    => null,
+                'displayName' => 'User updated Contacts Folder',
+                'type'        => Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT_USER_CREATED
+            ))
+        );
+        
         $doc = new DOMDocument();
         $doc->loadXML('<?xml version="1.0" encoding="utf-8"?>
             <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
@@ -111,6 +125,16 @@ class Syncroton_Command_FolderSyncTests extends Syncroton_Command_ATestCase
 
         $nodes = $xpath->query('//FolderHierarchy:FolderSync/FolderHierarchy:Changes/FolderHierarchy:Add');
         $this->assertGreaterThanOrEqual(1, $nodes->length, $responseDoc->saveXML());
+        
+        $nodes = $xpath->query('//FolderHierarchy:FolderSync/FolderHierarchy:Changes/FolderHierarchy:Add/FolderHierarchy:DisplayName');
+        $this->assertGreaterThanOrEqual('User created Contacts Folder', $nodes->item(0)->nodeValue, $responseDoc->saveXML());
+        
+        $nodes = $xpath->query('//FolderHierarchy:FolderSync/FolderHierarchy:Changes/FolderHierarchy:Update');
+        $this->assertGreaterThanOrEqual(1, $nodes->length, $responseDoc->saveXML());
+        
+        $nodes = $xpath->query('//FolderHierarchy:FolderSync/FolderHierarchy:Changes/FolderHierarchy:Update/FolderHierarchy:DisplayName');
+        $this->assertGreaterThanOrEqual('User updated Contacts Folder', $nodes->item(0)->nodeValue, $responseDoc->saveXML());
+        
     }
     
     /**
index 20a82a0..955f9b6 100644 (file)
@@ -63,49 +63,57 @@ function getTestDatabase()
         }
     }
     
+    $creationTime = new DateTime(null, new DateTimeZone('utc'));
+    
     // create test folders
     $folders = array(
         array(
-            'id'        => 'addressbookFolderId',
-            'type'      => Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT,
-            'name'      => 'Default Contacts Folder',
-            'owner_id'  => '1234',
-            'parent_id' => '0'
+            'id'            => 'addressbookFolderId',
+            'type'          => Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT,
+            'name'          => 'Default Contacts Folder',
+            'owner_id'      => '1234',
+            'parent_id'     => '0',
+            'creation_time' => $creationTime->format('Y-m-d H:i:s')
         ),
         array(
             'id'        => 'anotherAddressbookFolderId',
             'type'      => Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT_USER_CREATED,
             'name'      => 'Another Contacts Folder',
             'owner_id'  => '1234',
-            'parent_id' => '0'
+            'parent_id' => '0',
+            'creation_time' => $creationTime->format('Y-m-d H:i:s')
         ),
         array(
             'id'        => 'calendarFolderId',
             'type'      => Syncroton_Command_FolderSync::FOLDERTYPE_CALENDAR,
             'name'      => 'Default Contacts Folder',
             'owner_id'  => '1234',
-            'parent_id' => '0'
+            'parent_id' => '0',
+            'creation_time' => $creationTime->format('Y-m-d H:i:s')
         ),
         array(
             'id'        => 'tasksFolderId',
             'type'      => Syncroton_Command_FolderSync::FOLDERTYPE_TASK,
             'name'      => 'Default Tasks Folder',
             'owner_id'  => '1234',
-            'parent_id' => '0'
+            'parent_id' => '0',
+            'creation_time' => $creationTime->format('Y-m-d H:i:s')
         ),
         array(
             'id'        => 'emailInboxFolderId',
             'type'      => Syncroton_Command_FolderSync::FOLDERTYPE_INBOX,
             'name'      => 'Inbox',
             'owner_id'  => '1234',
-            'parent_id' => '0'
+            'parent_id' => '0',
+            'creation_time' => $creationTime->format('Y-m-d H:i:s')
         ),
         array(
             'id'        => 'emailSentFolderId',
             'type'      => Syncroton_Command_FolderSync::FOLDERTYPE_SENTMAIL,
             'name'      => 'Sent',
             'owner_id'  => '1234',
-            'parent_id' => '0'
+            'parent_id' => '0',
+            'creation_time' => $creationTime->format('Y-m-d H:i:s')
         )
     );