Merge branch 'master' of http://git.tine20.org/git/Syncope
authorLars Kneschke <l.kneschke@metaways.de>
Wed, 22 Feb 2012 15:24:32 +0000 (16:24 +0100)
committerLars Kneschke <l.kneschke@metaways.de>
Wed, 22 Feb 2012 15:24:32 +0000 (16:24 +0100)
1  2 
tine20/library/Syncope/lib/Syncope/Backend/Content.php
tine20/library/Syncope/lib/Syncope/Backend/SyncState.php
tine20/library/Syncope/lib/Syncope/Command/Sync.php
tine20/library/Syncope/lib/Syncope/Data/Contacts.php
tine20/library/Syncope/lib/Syncope/Model/IContent.php
tine20/library/Syncope/tests/Syncope/Backend/ContentTests.php
tine20/library/Syncope/tests/Syncope/Command/GetItemEstimateTests.php
tine20/library/Syncope/tests/Syncope/Command/SyncTests.php
tine20/library/Syncope/tests/bootstrap.php

@@@ -47,12 -47,13 +47,13 @@@ class Syncope_Backend_Content implement
          $folderId = $_state->folder_id instanceof Syncope_Model_IFolder ? $_state->folder_id->id : $_state->folder_id;
          
          $this->_db->insert($this->_tablePrefix . 'content', array(
-             'id'            => $id, 
-             'device_id'     => $deviceId,
-             'folder_id'     => $folderId,
-             'contentid'     => $_state->contentid,
-             'creation_time' => $_state->creation_time->format('Y-m-d H:i:s'),
-             'is_deleted'    => isset($_state->is_deleted) ? (int)!!$_state->is_deleted : 0
+             'id'               => $id, 
+             'device_id'        => $deviceId,
+             'folder_id'        => $folderId,
+             'contentid'        => $_state->contentid,
+             'creation_time'    => $_state->creation_time->format('Y-m-d H:i:s'),
+             'creation_synckey' => $_state->creation_synckey,
+             'is_deleted'       => isset($_state->is_deleted) ? (int)!!$_state->is_deleted : 0
          ));
          
          return $this->get($id);
@@@ -223,16 -223,17 +223,17 @@@ class Syncope_Backend_SyncState impleme
              $this->_db->update($this->_tablePrefix . 'content', array(
                  'is_deleted'  => 0,
              ), array(
-                 'device_id = ?'  => $deviceId,
-                 'folder_id = ?'  => $folderId,
-                 'is_deleted = ?' => 1
+                 'device_id = ?'        => $deviceId,
+                 'folder_id = ?'        => $folderId,
+                 'creation_synckey = ?' => $state->counter,
+                 'is_deleted = ?'       => 1
              ));
              
              // remove entries added during latest sync in syncope_content table
              $this->_db->delete($this->_tablePrefix . 'content', array(
-                 'device_id = ?'     => $deviceId,
-                 'folder_id = ?'     => $folderId,
-                 'creation_time > ?' => $state->lastsync->format('Y-m-d H:i:s'),
+                 'device_id = ?'        => $deviceId,
+                 'folder_id = ?'        => $folderId,
+                 'creation_synckey > ?' => $state->counter,
              ));
              
          } else {
@@@ -246,11 -246,11 +246,11 @@@ class Syncope_Command_Sync extends Sync
                              'serverId'     => $serverId,
                              'status'       => self::STATUS_SUCCESS,
                              'contentState' => $this->_contentStateBackend->create(new Syncope_Model_Content(array(
-                                 'device_id'     => $this->_device,
-                                 'folder_id'     => $collectionData['folder'],
-                                 'contentid'     => $serverId,
-                                 'creation_time' => $this->_syncTimeStamp
-                             
+                                 'device_id'        => $this->_device,
+                                 'folder_id'        => $collectionData['folder'],
+                                 'contentid'        => $serverId,
+                                 'creation_time'    => $this->_syncTimeStamp,
+                                 'creation_synckey' => $collectionData['syncKey'] + 1
                              )))
                          );
                          
                          break;
                      }
                      
-                     /**
-                      * somewhere is a problem in the logic for handling moreAvailable
-                      * 
-                      * it can happen, that we have a contentstate (which means we sent the entry to the client
-                      * and that this entry is yet in $collectionData['syncState']->pendingdata['serverAdds']
-                      * I have no idea how this can happen, but the next lines of code work around this problem
-                      */
-                     try {
-                         $this->_contentStateBackend->getContentState($this->_device, $collectionData['folder'], $serverId);
-                     
-                         if ($this->_logger instanceof Zend_Log) 
-                             $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped an entry($serverId) which is already on the client");
-                         
-                         unset($serverAdds[$id]);
-                         continue;
-                         
-                     } catch (Syncope_Exception_NotFound $senf) {
-                         // do nothing => content state should not exist yet
-                     }
+                     #/**
+                     # * somewhere is a problem in the logic for handling moreAvailable
+                     # * 
+                     # * it can happen, that we have a contentstate (which means we sent the entry to the client
+                     # * and that this entry is yet in $collectionData['syncState']->pendingdata['serverAdds']
+                     # * I have no idea how this can happen, but the next lines of code work around this problem
+                     # */
+                     #try {
+                     #    $this->_contentStateBackend->getContentState($this->_device, $collectionData['folder'], $serverId);
+                     # 
+                     #    if ($this->_logger instanceof Zend_Log) 
+                     #        $this->_logger->info(__METHOD__ . '::' . __LINE__ . " skipped an entry($serverId) which is already on the client");
+                     #    
+                     #    unset($serverAdds[$id]);
+                     #    continue;
+                     #    
+                     #} catch (Syncope_Exception_NotFound $senf) {
+                     #    // do nothing => content state should not exist yet
+                     #}
                      
                      try {
                          $add = $this->_outputDom->createElementNS('uri:AirSync', 'Add');
                      
                      // mark as send to the client, even the conversion to xml might have failed 
                      $newContentStates[] = new Syncope_Model_Content(array(
-                         'device_id'     => $this->_device,
-                         'folder_id'     => $collectionData['folder'],
-                         'contentid'     => $serverId,
-                         'creation_time' => $this->_syncTimeStamp
+                         'device_id'        => $this->_device,
+                         'folder_id'        => $collectionData['folder'],
+                         'contentid'        => $serverId,
+                         'creation_time'    => $this->_syncTimeStamp,
+                         'creation_synckey' => $collectionData['syncState']->counter
                      ));
                      unset($serverAdds[$id]);    
                  }
@@@ -86,16 -86,48 +86,48 @@@ class Syncope_Data_Contacts extends Syn
           */
          if (!isset(Syncope_Data_AData::$entries[get_class($this)])) {
              Syncope_Data_AData::$entries[get_class($this)] = array(
-                       'addressbookFolderId' => array(
-                         'contact1' => array(
-                               'FirstName' => 'Lars', 
-                               'LastName'  => 'Kneschke'
-                       ),
-                         'contact2' => array(
-                               'FirstName' => 'Cornelius', 
-                               'LastName'  => 'Weiß'
-                           )
-                   )
+                 'addressbookFolderId' => array(
+                     'contact1' => array(
+                         'FirstName' => 'Lars', 
+                         'LastName'  => 'Kneschke'
+                     ),
+                     'contact2' => array(
+                         'FirstName' => 'Cornelius', 
+                         'LastName'  => 'Weiß'
+                     ),
+                     'contact3' => array(
+                         'FirstName' => 'Lars', 
+                         'LastName'  => 'Kneschke'
+                     ),
+                     'contact4' => array(
+                         'FirstName' => 'Cornelius', 
+                         'LastName'  => 'Weiß'
+                     ),
+                     'contact5' => array(
+                         'FirstName' => 'Lars', 
+                         'LastName'  => 'Kneschke'
+                     ),
+                     'contact6' => array(
+                         'FirstName' => 'Cornelius', 
+                         'LastName'  => 'Weiß'
+                     ),
+                     'contact7' => array(
+                         'FirstName' => 'Lars', 
+                         'LastName'  => 'Kneschke'
+                     ),
+                     'contact8' => array(
+                         'FirstName' => 'Cornelius', 
+                         'LastName'  => 'Weiß'
+                     ),
+                     'contact9' => array(
+                         'FirstName' => 'Lars', 
+                         'LastName'  => 'Kneschke'
+                     ),
+                     'contact10' => array(
+                         'FirstName' => 'Cornelius', 
+                         'LastName'  => 'Weiß'
+                     )
+                 )
              );
          }
      }
@@@ -18,6 -18,7 +18,7 @@@
   * @property    string    folder_id
   * @property    string    contentid
   * @property    DateTime  creation_time
+  * @property    string    creation_synckey
   * @property    string    is_deleted
   */
  
@@@ -162,10 -162,11 +162,11 @@@ class Syncope_Backend_ContentTests exte
      public static function getTestContent(Syncope_Model_IDevice $_device, Syncope_Model_IFolder $_folder)
      {
          return new Syncope_Model_Content(array(
-             'device_id'     => $_device,
-             'folder_id'     => $_folder,
-             'contentid'     => 'abc1234',
-             'creation_time' => new DateTime(null, new DateTimeZone('utc'))
+             'device_id'        => $_device,
+             'folder_id'        => $_folder,
+             'contentid'        => 'abc1234',
+             'creation_time'    => new DateTime(null, new DateTimeZone('utc')),
+             'creation_synckey' => 1
          ));
      }
  }
@@@ -91,7 -91,7 +91,7 @@@ class Syncope_Command_GetItemEstimateTe
                  
          $nodes = $xpath->query('//ItemEstimate:GetItemEstimate/ItemEstimate:Response/ItemEstimate:Collection/ItemEstimate:Estimate');
          $this->assertEquals(1, $nodes->length, $responseDoc->saveXML());
-         $this->assertEquals(2, $nodes->item(0)->nodeValue, $responseDoc->saveXML());
+         $this->assertEquals(10, $nodes->item(0)->nodeValue, $responseDoc->saveXML());
          
      }    
  }
@@@ -139,7 -139,7 +139,7 @@@ class Syncope_Command_SyncTests extend
          $doc = new DOMDocument();
          $doc->loadXML('<?xml version="1.0" encoding="utf-8"?>
              <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
-             <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections><Collection><Class>Contacts</Class><SyncKey>0</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>100</WindowSize><Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options></Collection></Collections></Sync>'
+             <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections><Collection><Class>Contacts</Class><SyncKey>0</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>2</WindowSize><Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options></Collection></Collections></Sync>'
          );
          
          $sync = new Syncope_Command_Sync($doc, $this->_device, $this->_device->policykey);
          $doc = new DOMDocument();
          $doc->loadXML('<?xml version="1.0" encoding="utf-8"?>
              <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
-             <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections><Collection><Class>Contacts</Class><SyncKey>1</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>100</WindowSize><Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options></Collection></Collections></Sync>'
+             <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections><Collection><Class>Contacts</Class><SyncKey>1</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>2</WindowSize><Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options></Collection></Collections></Sync>'
          );
          
          $sync = new Syncope_Command_Sync($doc, $this->_device, $this->_device->policykey);
          $this->assertEquals(1, $nodes->length, $syncDoc->saveXML());
          $this->assertEquals(Syncope_Command_Sync::STATUS_SUCCESS, $nodes->item(0)->nodeValue, $syncDoc->saveXML());
          
+         $nodes = $xpath->query('//AirSync:Sync/AirSync:Collections/AirSync:Collection/AirSync:MoreAvailable');
+         $this->assertEquals(1, $nodes->length, $syncDoc->saveXML());
+         
          $nodes = $xpath->query('//AirSync:Sync/AirSync:Collections/AirSync:Collection/AirSync:Commands');
          $this->assertEquals(1, $nodes->length, $syncDoc->saveXML());
          #$this->assertEquals(Syncope_Command_Sync::STATUS_SUCCESS, $nodes->item(0)->nodeValue, $syncDoc->saveXML());
          $this->assertEquals(0, $nodes->length, $syncDoc->saveXML());
                  
          $this->assertEquals("uri:Contacts", $syncDoc->lookupNamespaceURI('Contacts'), $syncDoc->saveXML());
+         
+         // try with previous synckey again
+         $doc = new DOMDocument();
+         $doc->loadXML('<?xml version="1.0" encoding="utf-8"?>
+             <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+             <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections><Collection><Class>Contacts</Class><SyncKey>1</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>2</WindowSize><Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options></Collection></Collections></Sync>'
+         );
+         
+         $sync = new Syncope_Command_Sync($doc, $this->_device, $this->_device->policykey);
+         
+         $sync->handle();
+         
+         $syncDoc = $sync->getResponse();
+         #$syncDoc->formatOutput = true; echo $syncDoc->saveXML();
+         
+         
+         // and now fetch the rest
+         $doc = new DOMDocument();
+         $doc->loadXML('<?xml version="1.0" encoding="utf-8"?>
+             <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+             <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections><Collection><Class>Contacts</Class><SyncKey>2</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>20</WindowSize><Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options></Collection></Collections></Sync>'
+         );
+         
+         $sync = new Syncope_Command_Sync($doc, $this->_device, $this->_device->policykey);
+         
+         $sync->handle();
+         
+         $syncDoc = $sync->getResponse();
+         #$syncDoc->formatOutput = true; echo $syncDoc->saveXML();
+         
+         $xpath = new DomXPath($syncDoc);
+         $xpath->registerNamespace('AirSync', 'uri:AirSync');
+         
+         $nodes = $xpath->query('//AirSync:Sync/AirSync:Collections/AirSync:Collection/AirSync:MoreAvailable');
+         $this->assertEquals(0, $nodes->length, $syncDoc->saveXML());
      }
          
      /**
              <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
              <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase"><Collections>
                  <Collection>
-                     <Class>Contacts</Class><SyncKey>2</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>100</WindowSize>
+                     <Class>Contacts</Class><SyncKey>3</SyncKey><CollectionId>addressbookFolderId</CollectionId><DeletesAsMoves/><GetChanges/><WindowSize>100</WindowSize>
                      <Options><AirSyncBase:BodyPreference><AirSyncBase:Type>1</AirSyncBase:Type><AirSyncBase:TruncationSize>5120</AirSyncBase:TruncationSize></AirSyncBase:BodyPreference><Conflict>1</Conflict></Options>
                  </Collection>
              </Collections></Sync>'
          
          $nodes = $xpath->query('//AirSync:Sync/AirSync:Collections/AirSync:Collection/AirSync:SyncKey');
          $this->assertEquals(1, $nodes->length, $syncDoc->saveXML());
-         $this->assertEquals(2, $nodes->item(0)->nodeValue, $syncDoc->saveXML());
+         $this->assertEquals(3, $nodes->item(0)->nodeValue, $syncDoc->saveXML());
          
          $nodes = $xpath->query('//AirSync:Sync/AirSync:Collections/AirSync:Collection/AirSync:Status');
          $this->assertEquals(1, $nodes->length, $syncDoc->saveXML());
@@@ -74,6 -74,7 +74,7 @@@ function getTestDatabase(
          `folder_id` varchar(40) NOT NULL,
          `contentid` varchar(64) NOT NULL,
          `creation_time` datetime NOT NULL,
+         `creation_synckey` int(11) NOT NULL,
          `is_deleted` int(11) DEFAULT '0',
          PRIMARY KEY (`id`),
          UNIQUE (`device_id`,`folder_id`,`contentid`)