3 commits - docs/SQL lib/ext lib/kolab_sync_backend.php lib/kolab_sync_data_email.php lib/kolab_sync_data.php
Aleksander Machniak
machniak at kolabsys.com
Thu Jan 17 14:01:59 CET 2013
docs/SQL/mysql.initial.sql | 10 ++++++
docs/SQL/mysql/2013011600.sql | 9 ++++++
lib/ext/Syncroton/Command/Sync.php | 2 -
lib/kolab_sync_backend.php | 55 ++++++++++++++++++++++++++++++++++++-
lib/kolab_sync_data.php | 53 +++++++++++++++++++++++------------
lib/kolab_sync_data_email.php | 55 ++++++++++++++++++++++++++++++-------
6 files changed, 154 insertions(+), 30 deletions(-)
New commits:
commit 54c82e3a0e3c2c6d877a78d098cdefca7a3186f1
Author: Aleksander Machniak <alec at alec.pl>
Date: Thu Jan 17 13:59:06 2013 +0100
Fix regression where \Seen flag wasn't set on sent messages
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 6a67b34..9a3c336 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -618,7 +618,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
$sent_folder = kolab_sync::get_instance()->config->get('sent_mbox');
if (strlen($sent_folder) && $this->storage->folder_exists($sent_folder)) {
- return $this->storage->save_message($sent_folder, $message->source());
+ return $this->storage->save_message($sent_folder, $message->source(), '', false, array('SEEN'));
}
}
}
commit 2bec11daf4ffe957a156199d7a3ce279615472c4
Author: Aleksander Machniak <alec at alec.pl>
Date: Thu Jan 17 13:56:01 2013 +0100
Fix detection of changes in email messages (e.g. \Seen flag update) (Bug #1169)
diff --git a/docs/SQL/mysql.initial.sql b/docs/SQL/mysql.initial.sql
index c0581a5..9bae2e1 100644
--- a/docs/SQL/mysql.initial.sql
+++ b/docs/SQL/mysql.initial.sql
@@ -134,3 +134,13 @@ CREATE TABLE IF NOT EXISTS `syncroton_data_folder` (
`parent_id` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
+
+CREATE TABLE IF NOT EXISTS `syncroton_modseq` (
+ `device_id` varchar(40) NOT NULL,
+ `folder_id` varchar(40) NOT NULL,
+ `synctime` varchar(14) NOT NULL,
+ `data` longblob,
+ PRIMARY KEY (`device_id`,`folder_id`,`synctime`),
+ KEY `syncroton_modseq::device_id` (`device_id`),
+ CONSTRAINT `syncroton_modseq::device_id--syncroton_device::id` FOREIGN KEY (`device_id`) REFERENCES `syncroton_device` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
diff --git a/docs/SQL/mysql/2013011600.sql b/docs/SQL/mysql/2013011600.sql
new file mode 100644
index 0000000..914f750
--- /dev/null
+++ b/docs/SQL/mysql/2013011600.sql
@@ -0,0 +1,9 @@
+CREATE TABLE IF NOT EXISTS `syncroton_modseq` (
+ `device_id` varchar(40) NOT NULL,
+ `folder_id` varchar(40) NOT NULL,
+ `synctime` varchar(14) NOT NULL,
+ `data` longblob,
+ PRIMARY KEY (`device_id`,`folder_id`,`synctime`),
+ KEY `syncroton_modseq::device_id` (`device_id`),
+ CONSTRAINT `syncroton_modseq::device_id--syncroton_device::id` FOREIGN KEY (`device_id`) REFERENCES `syncroton_device` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB;
diff --git a/lib/ext/Syncroton/Command/Sync.php b/lib/ext/Syncroton/Command/Sync.php
index b59bae6..c5da42b 100644
--- a/lib/ext/Syncroton/Command/Sync.php
+++ b/lib/ext/Syncroton/Command/Sync.php
@@ -491,7 +491,7 @@ class Syncroton_Command_Sync extends Syncroton_Command_Wbxml
if (($now->getTimestamp() - $collectionData->syncState->lastsync->getTimestamp()) < Syncroton_Registry::getQuietTime()) {
continue;
}
-
+
$dataController = Syncroton_Data_Factory::factory($collectionData->folder->class , $this->_device, $this->_syncTimeStamp);
$hasChanges = $dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState);
diff --git a/lib/kolab_sync_backend.php b/lib/kolab_sync_backend.php
index 25c2fb1..7424a32 100644
--- a/lib/kolab_sync_backend.php
+++ b/lib/kolab_sync_backend.php
@@ -682,7 +682,6 @@ class kolab_sync_backend
}
}
*/
-
// get all folders of specified type
$folderdata = $this->folder_meta();
@@ -710,6 +709,60 @@ class kolab_sync_backend
}
/**
+ */
+ public function modseq_set($deviceid, $folderid, $synctime, $data)
+ {
+ $synctime = $synctime->format('Ymdhis');
+ $rcube = rcube::get_instance();
+ $db = $rcube->get_dbh();
+
+ $this->modseq[$deviceid][$folderid][$synctime] = $data;
+
+ $data = json_encode($data);
+
+ $db->query("UPDATE syncroton_modseq"
+ ." SET data = ?"
+ ." WHERE device_id = ? AND folder_id = ? AND synctime = ?",
+ $data, $deviceid, $folderid, $synctime);
+
+ if (!$db->affected_rows()) {
+ $db->query("INSERT INTO syncroton_modseq (device_id, folder_id, synctime, data)"
+ ." VALUES (?, ?, ?, ?)",
+ $deviceid, $folderid, $synctime, $data);
+ }
+ }
+
+ public function modseq_get($deviceid, $folderid, $synctime)
+ {
+ $synctime = $synctime->format('Ymdhis');
+
+ if (!isset($this->modseq[$deviceid]) || !isset($this->modseq[$deviceid][$folderid])
+ || !isset($this->modseq[$deviceid][$synctime])
+ ) {
+ $rcube = rcube::get_instance();
+ $db = $rcube->get_dbh();
+
+ $db->limitquery("SELECT data, synctime FROM syncroton_modseq"
+ ." WHERE device_id = ? AND folder_id = ? AND synctime <= ?"
+ ." ORDER BY synctime DESC",
+ 0, 2, $deviceid, $folderid, $synctime);
+
+ if ($row = $db->fetch_assoc()) {
+ $this->modseq[$deviceid][$folderid][$synctime] = json_decode($row['data']);
+
+ // Cleanup: remove old records (older than 12 hours from the last one)
+ if ($row = $db->fetch_assoc()) {
+ $db->query("DELETE FROM syncroton_modseq"
+ ." WHERE device_id = ? AND folder_id = ? AND synctime < ?",
+ $deviceid, $folderid, $row['synctime'] - 86400);
+ }
+ }
+ }
+
+ return @$this->modseq[$deviceid][$folderid][$synctime];
+ }
+
+ /**
* Compares two arrays
*
* @param array $array1
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 769d472..6a67b34 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -751,10 +751,12 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
// convert filter into one IMAP search string
foreach ($filter as $idx => $filter_item) {
if (is_array($filter_item)) {
- // @TODO
- // convert 'changed' entries into IMAP search string
- // for now we just return empty result
- return $result_type == self::RESULT_COUNT ? 0 : array();
+ // This is a request for changes since list time
+ // we'll use HIGHESTMODSEQ value from the last Sync
+ if ($filter_item[0] == 'changed' && $filter_item[1] == '>') {
+ $modseq = (array) $this->backend->modseq_get($this->device->id, $folderid, $filter_item[2]);
+ $modseq_data = array();
+ }
}
else {
$filter_str .= ' ' . $filter_item;
@@ -765,15 +767,43 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
// no sorting for best performance
$sort_by = null;
- foreach ($folders as $folderid) {
- $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
+ foreach ($folders as $folder_id) {
+ $foldername = $this->backend->folder_id2name($folder_id, $this->device->deviceid);
if ($foldername === null) {
continue;
}
-// $this->storage->set_folder($foldername);
- $this->storage->folder_sync($foldername);
+ $this->storage->set_folder($foldername);
+
+ // Syncronize folder (if it wasn't synced in this request already)
+ if ($this->lastsync_folder != $foldername
+ || $this->lastsync_time < time() - Syncroton_Registry::getPingTimeout()
+ ) {
+ $this->storage->folder_sync($foldername);
+ }
+
+ $this->lastsync_folder = $foldername;
+ $this->lastsync_time = time();
+
+ // We're in "get changes" mode
+ if (isset($modseq_data)) {
+ $folder_data = $this->storage->folder_data($foldername);
+ if ($folder_data['HIGHESTMODSEQ']) {
+ $modseq_data[$foldername] = $folder_data['HIGHESTMODSEQ'];
+ if ($modseq_data[$foldername] != $modseq[$foldername]) {
+ $modseq_update = true;
+ }
+ }
+
+ // If previous HIGHESTMODSEQ doesn't exist we can't get changes
+ // We can only get folder's HIGHESTMODSEQ value and store it for the next try
+ if (empty($modseq) || empty($modseq[$foldername])) {
+ continue;
+ }
+
+ $filter_str .= " MODSEQ " . ($modseq[$foldername] + 1);
+ }
$search = $this->storage->search_once($foldername, $filter_str);
@@ -789,7 +819,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
case self::RESULT_UID:
if ($uids = $search->get()) {
foreach ($uids as $idx => $uid) {
- $uids[$idx] = $this->createMessageId($folderid, $uid);
+ $uids[$idx] = $this->createMessageId($folder_id, $uid);
}
$result = array_merge($result, $uids);
}
@@ -804,6 +834,11 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
}
}
+ if (!empty($modseq_update)) {
+ $this->backend->modseq_set($this->device->id, $folderid,
+ $this->syncTimeStamp, $modseq_data);
+ }
+
return $result;
}
commit f191c1b177119bfed98a01841e6a01abd38edcd4
Author: Aleksander Machniak <alec at alec.pl>
Date: Tue Jan 15 09:49:32 2013 +0100
Performance improvement in hasChanges()
diff --git a/lib/kolab_sync_data.php b/lib/kolab_sync_data.php
index 1f2e384..82d6547 100644
--- a/lib/kolab_sync_data.php
+++ b/lib/kolab_sync_data.php
@@ -570,7 +570,6 @@ abstract class kolab_sync_data implements Syncroton_Data_IData
return $result;
}
-
/**
* get count of all entries available on the server
*
@@ -587,36 +586,54 @@ abstract class kolab_sync_data implements Syncroton_Data_IData
return $result;
}
-
+ /**
+ * Returns number of changed objects in the backend folder
+ *
+ * @param Syncroton_Backend_IContent $contentBackend
+ * @param Syncroton_Model_IFolder $folder
+ * @param Syncroton_Model_ISyncState $syncState
+ *
+ * @return int
+ */
public function getCountOfChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState)
{
$allClientEntries = $contentBackend->getFolderState($this->device, $folder);
+ $allServerEntries = $this->getServerEntries($folder->serverId, $folder->lastfiltertype);
+ $changedEntries = $this->getChangedEntriesCount($folder->serverId, $syncState->lastsync);
+ $addedEntries = array_diff($allServerEntries, $allClientEntries);
+ $deletedEntries = array_diff($allClientEntries, $allServerEntries);
+
+ return count($addedEntries) + count($deletedEntries) + $changedEntries;
+ }
+
+ /**
+ * Returns true if any data got modified in the backend folder
+ *
+ * @param Syncroton_Backend_IContent $contentBackend
+ * @param Syncroton_Model_IFolder $folder
+ * @param Syncroton_Model_ISyncState $syncState
+ *
+ * @return bool
+ */
+ public function hasChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState)
+ {
+ if ($this->getChangedEntriesCount($folder->serverId, $syncState->lastsync)) {
+ return true;
+ }
+
+ $allClientEntries = $contentBackend->getFolderState($this->device, $folder);
// @TODO: Consider looping over all folders here, not in getServerEntries() and
- // getChangedEntriesCount(). This way we could break the loop and not check all folders (see @TODO below)
+ // getChangedEntriesCount(). This way we could break the loop and not check all folders
// or at least skip redundant cache sync of the same folder
$allServerEntries = $this->getServerEntries($folder->serverId, $folder->lastfiltertype);
$addedEntries = array_diff($allServerEntries, $allClientEntries);
$deletedEntries = array_diff($allClientEntries, $allServerEntries);
- // @TODO: Count is needed only for GetItemEstimate command
- // in Ping command we need only information that anything is changed/added/deleted
- // so in case when count($addedEntries) + count($deletedEntries) != 0 we don't need
- // to count changed entries here. We could also revert the order in such case
- // and execute getChangedEntriesCount() before
- $changedEntries = $this->getChangedEntriesCount($folder->serverId, $syncState->lastsync);
-
- return count($addedEntries) + count($deletedEntries) + $changedEntries;
+ return count($addedEntries) > 0 || count($deletedEntries) > 0;
}
-
- public function hasChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState)
- {
- return !!$this->getCountOfChanges($contentBackend, $folder, $syncState);
- }
-
-
/**
* Fetches the entry from the backend
*/
More information about the commits
mailing list