From 71edd1c9387fd647809a072887fc7c164c83f87d Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 3 Nov 2008 07:44:02 +0000 Subject: [PATCH] Big patch from Philip Herbert (Knauber) modifying the SyncML code to no longer use GUIDs containing eGW's install_id, as the information is irrellevant for SyncML and cause doublications of entries if the install_id changes. I plan to have a new rc4 Wednesday or Thursday containing these changes. --- addressbook/csv_import.php | 3 +- .../inc/class.addressbook_vcal.inc.php | 7 ++- calendar/inc/class.calendar_ical.inc.php | 12 +++- infolog/inc/class.infolog_ical.inc.php | 11 ++-- infolog/inc/class.infolog_sif.inc.php | 4 +- phpgwapi/inc/class.contenthistory.inc.php | 16 ++--- phpgwapi/inc/horde/Horde/SyncML/State.php | 5 +- phpgwapi/inc/horde/Horde/SyncML/State_egw.php | 62 ++++++++++++++++++- phpgwapi/inc/horde/Horde/SyncML/Sync.php | 13 ++-- .../SyncML/Sync/RefreshFromServerSync.php | 1 - .../inc/horde/Horde/SyncML/Sync/SlowSync.php | 2 +- .../horde/Horde/SyncML/Sync/TwoWaySync.php | 6 +- phpgwapi/setup/setup.inc.php | 2 +- phpgwapi/setup/tables_current.inc.php | 1 - phpgwapi/setup/tables_update.inc.php | 24 +++++++ 15 files changed, 131 insertions(+), 38 deletions(-) diff --git a/addressbook/csv_import.php b/addressbook/csv_import.php index 9700ee4d94..ce38047db2 100644 --- a/addressbook/csv_import.php +++ b/addressbook/csv_import.php @@ -243,7 +243,8 @@ switch($_POST['action']) 'trans' => $_POST['trans'] )); @set_time_limit(0); - $fp=fopen($csvfile,'rb'); + ini_set('auto_detect_line_endings',true); // to allow to import files created eg. on a mac + $fp=fopen($csvfile,'r'); $csv_fields = fgetcsv($fp,8000,$_POST['fieldsep']); $csv_fields = $GLOBALS['egw']->translation->convert($csv_fields,$_POST['charset']); $csv_fields[] = 'no CSV 1'; // eg. for static assignments diff --git a/addressbook/inc/class.addressbook_vcal.inc.php b/addressbook/inc/class.addressbook_vcal.inc.php index 7f806588a4..83e2259dd6 100644 --- a/addressbook/inc/class.addressbook_vcal.inc.php +++ b/addressbook/inc/class.addressbook_vcal.inc.php @@ -421,9 +421,9 @@ class addressbook_vcal extends addressbook_bo )); $defaultFields[9] = array( // nokia e90 - 'ADR;WORK' => array('','','adr_one_street','adr_one_locality','adr_one_region', + 'ADR;WORK' => array('','adr_one_street2','adr_one_street','adr_one_locality','adr_one_region', 'adr_one_postalcode','adr_one_countryname'), - 'ADR;HOME' => array('','','adr_two_street','adr_two_locality','adr_two_region', + 'ADR;HOME' => array('','adr_two_street2','adr_two_street','adr_two_locality','adr_two_region', 'adr_two_postalcode','adr_two_countryname'), 'BDAY' => array('bday'), 'X-CLASS' => array('private'), @@ -441,7 +441,7 @@ class addressbook_vcal extends addressbook_bo 'TEL;PAGER;WORK' => array('tel_pager'), 'TEL;VOICE;WORK' => array('tel_work'), 'TEL;VOICE;HOME' => array('tel_home'), - 'TITLE' => array('contact_role'), + 'TITLE' => array('title'), 'URL;WORK' => array('url'), 'URL;HOME' => array('url_home'), 'X-ASSISTANT' => array('assistent'), @@ -539,6 +539,7 @@ class addressbook_vcal extends addressbook_bo break; case 'e51': case 'e90': + case 'e71': $this->supportedFields = $defaultFields[9]; break; case '9300': diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 6d3e339ab2..d7f6200a4c 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -391,8 +391,8 @@ class calendar_ical extends calendar_boupdate } } - $modified = $GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'modify'); - $created = $GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'add'); + $modified = $GLOBALS['egw']->contenthistory->getTSforAction('calendar',$event['id'],'modify'); + $created = $GLOBALS['egw']->contenthistory->getTSforAction('calendar',$event['id'],'add'); if (!$created && !$modified) $created = $event['modified']; if ($created) $attributes['CREATED'] = $created; if (!$modified) $modified = $event['modified']; @@ -443,7 +443,8 @@ class calendar_ical extends calendar_boupdate } } - $attributes['UID'] = $force_own_uid ? $eventGUID : $event['uid']; + //$attributes['UID'] = $force_own_uid ? $eventGUID : $event['uid']; + $attributes['UID'] = $event['uid']; foreach($attributes as $key => $value) { foreach(is_array($value)&&$parameters[$key]['VALUE']!='DATE' ? $value : array($value) as $valueID => $valueData) @@ -1166,6 +1167,11 @@ class calendar_ical extends calendar_boupdate case 'e61': $this->supportedFields = $defaultFields['minimal']; break; + case 'e51': + case 'e90': + case 'e71': + $this->supportedFields = $defaultFields['basic']; + break; default: error_log("Unknown Nokia phone '$_productName', assuming E61"); $this->supportedFields = $defaultFields['minimal']; diff --git a/infolog/inc/class.infolog_ical.inc.php b/infolog/inc/class.infolog_ical.inc.php index 8878b554cd..7a14d1b502 100644 --- a/infolog/inc/class.infolog_ical.inc.php +++ b/infolog/inc/class.infolog_ical.inc.php @@ -81,9 +81,10 @@ class infolog_ical extends infolog_bo if($taskData['info_datecompleted']) $vevent->setAttribute('COMPLETED',$taskData['info_datecompleted']); $vevent->setAttribute('DTSTAMP',time()); - $vevent->setAttribute('CREATED',$GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'add')); - $vevent->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'modify')); - $vevent->setAttribute('UID',$force_own_uid ? $taskGUID : $taskData['info_uid']); + $vevent->setAttribute('CREATED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$_taskID,'add')); + $vevent->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$_taskID,'modify')); + //$vevent->setAttribute('UID',$force_own_uid ? $taskGUID : $taskData['info_uid']); + $vevent->setAttribute('UID',$taskData['info_uid']); $vevent->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE'); $vevent->setAttribute('STATUS',$this->status2vtodo($taskData['info_status'])); // we try to preserv the original infolog status as X-INFOLOG-STATUS, so we can restore it, if the user does not modify STATUS @@ -253,8 +254,8 @@ class infolog_ical extends infolog_bo $vnote->setAttribute('BODY',$note['info_des']); if($note['info_startdate']) $vnote->setAttribute('DCREATED',$note['info_startdate']); - $vnote->setAttribute('DCREATED',$GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'add')); - $vnote->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'modify')); + $vnote->setAttribute('DCREATED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_note',$_noteID,'add')); + $vnote->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_note',$_noteID,'modify')); if (!empty($note['info_cat'])) { $cats = $this->get_categories(array($note['info_cat'])); diff --git a/infolog/inc/class.infolog_sif.inc.php b/infolog/inc/class.infolog_sif.inc.php index 3a8902f401..de71f99d52 100644 --- a/infolog/inc/class.infolog_sif.inc.php +++ b/infolog/inc/class.infolog_sif.inc.php @@ -459,8 +459,8 @@ class infolog_sif extends infolog_bo if($taskData['info_enddate']) $vevent->setAttribute('DUE',$taskData['info_enddate']); $vevent->setAttribute('DTSTAMP',time()); - $vevent->setAttribute('CREATED',$GLOBALS['phpgw']->contenthistory->getTSforAction($eventGUID,'add')); - $vevent->setAttribute('LAST-MODIFIED',$GLOBALS['phpgw']->contenthistory->getTSforAction($eventGUID,'modify')); + $vevent->setAttribute('CREATED',$GLOBALS['phpgw']->contenthistory->getTSforAction('infolog_task',$_taskID,'add')); + $vevent->setAttribute('LAST-MODIFIED',$GLOBALS['phpgw']->contenthistory->getTSforAction('infolog_task',$_taskID,'modify')); $vevent->setAttribute('UID',$taskGUID); $vevent->setAttribute('CLASS',(($taskData['info_access'] == 'public')?'PUBLIC':'PRIVATE')); $vevent->setAttribute('STATUS',(($taskData['info_status'] == 'completed')?'COMPLETED':'NEEDS-ACTION')); diff --git a/phpgwapi/inc/class.contenthistory.inc.php b/phpgwapi/inc/class.contenthistory.inc.php index 7b8338af7a..44429c4008 100644 --- a/phpgwapi/inc/class.contenthistory.inc.php +++ b/phpgwapi/inc/class.contenthistory.inc.php @@ -58,7 +58,7 @@ class contenthistory * @param string$_appName the appname example: infolog_notes * @param string $_action can be modify, add or delete * @param string $_ts timestamp where to start searching from - * @return array containing the global UID's + * @return array containing contentIds with changes */ function getHistory($_appName, $_action, $_ts) { @@ -79,13 +79,13 @@ class contenthistory // no valid $_action set return array(); } - $guidList = array(); - foreach($this->db->select(self::TABLE,'sync_guid',$where,__LINE__,__FILE__) as $row) + $idList = array(); + foreach($this->db->select(self::TABLE,'sync_contentid',$where,__LINE__,__FILE__) as $row) { - $guidList[] = $row['sync_guid']; + $idList[] = $row['sync_contentid']; } - return $guidList; + return $idList; } /** @@ -95,7 +95,7 @@ class contenthistory * @param $_action string can be add, delete or modify * @return string the last timestamp */ - function getTSforAction($_guid, $_action) + function getTSforAction($_appName, $_id, $_action) { switch($_action) { @@ -112,7 +112,8 @@ class contenthistory return false; } $where = array ( - 'sync_guid' => $_guid, + 'sync_appname' => $_appName, + 'sync_contentid' => $_id, ); if (($ts = $this->db->select(self::TABLE,$col,$where,__LINE__,__FILE__)->fetchSingle())) @@ -137,7 +138,6 @@ class contenthistory 'sync_appname' => $_appName, 'sync_contentid' => $_id, 'sync_added' => $this->db->to_timestamp($_ts), - 'sync_guid' => $GLOBALS['egw']->common->generate_uid($_appName, $_id), 'sync_changedby' => $GLOBALS['egw_info']['user']['account_id'], ); diff --git a/phpgwapi/inc/horde/Horde/SyncML/State.php b/phpgwapi/inc/horde/Horde/SyncML/State.php index 30a912ed3c..acd4c4578e 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/State.php +++ b/phpgwapi/inc/horde/Horde/SyncML/State.php @@ -779,10 +779,13 @@ class Horde_SyncML_State { break; case 'calendar': - case 'caltasks': return 'calendar'; break; + case 'caltasks': + return 'caltasks'; + break; + # funambol related types case 'sifcalendar': diff --git a/phpgwapi/inc/horde/Horde/SyncML/State_egw.php b/phpgwapi/inc/horde/Horde/SyncML/State_egw.php index 61761a6510..185afda0ec 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/State_egw.php +++ b/phpgwapi/inc/horde/Horde/SyncML/State_egw.php @@ -17,12 +17,67 @@ include_once dirname(__FILE__).'/State.php'; class EGW_SyncML_State extends Horde_SyncML_State { var $table_devinfo = 'egw_syncmldevinfo'; - + /* * store the mappings of egw uids to client uids */ var $uidMappings = array(); + + /** + * get the local content id from a syncid + * + * @param sting $_syncid id used in syncml + * @return int local egw content id + */ + function get_egwID($_syncid) + { + $syncIDParts = explode('-',$_syncid); + array_shift($syncIDParts); + $_id = implode ('', $syncIDParts); + return $_id; + } + + /** + * when got a entry last added/modified/deleted + * + * @param $_syncid containing appName-contentid + * @param $_action string can be add, delete or modify + * @return string the last timestamp + */ + function getSyncTSforAction($_syncid, $_action) + { + $syncIDParts = explode('-',$_syncid); + $_appName = array_shift($syncIDParts); + $_id = implode ('', $syncIDParts); + + $ts = $GLOBALS['egw']->contenthistory->getTSforAction($_appName, $_id, $_action); + + return $ts; + } + + /** + * get the timestamp for action + * + * find which content changed since $_ts for application $_appName + * + * @param string$_appName the appname example: infolog_notes + * @param string $_action can be modify, add or delete + * @param string $_ts timestamp where to start searching from + * @return array containing syncIDs with changes + */ + function getHistory($_appName, $_action, $_ts) + { + $guidList = array (); + $syncIdList = array (); + $idList = $GLOBALS['egw']->contenthistory->getHistory($_appName, $_action, $_ts); + foreach ($idList as $idItem) + { + $syncIdList[] = $_appName . '-' . $idItem; + } + return $syncIdList; + } + /** * Returns the timestamp (if set) of the last change to the * obj:guid, that was caused by the client. This is stored to @@ -257,7 +312,7 @@ class EGW_SyncML_State extends Horde_SyncML_State function removeUID($type, $locid) { $mapID = $this->_locName . $this->_sourceURI . $type; - + $where = array ( 'map_id' => $mapID, 'map_locuid' => $locid @@ -301,6 +356,9 @@ class EGW_SyncML_State extends Horde_SyncML_State $guid = $this->getUIDMapping($_guid); if($guid === false) { + // this message is not really usefull here because setUIDMapping is only called when adding content to the client, + // however setUID is called also when adding content from the client. So in all other conditions this + // message will be logged. Horde::logMessage("SyncML: setUID $type, $locid, $guid something went wrong!!! Mapping not found.", __FILE__, __LINE__, PEAR_LOG_INFO); $guid = $_guid; //return false; diff --git a/phpgwapi/inc/horde/Horde/SyncML/Sync.php b/phpgwapi/inc/horde/Horde/SyncML/Sync.php index 154da61570..182d91fd23 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Sync.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Sync.php @@ -163,7 +163,7 @@ class Horde_SyncML_Sync { array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); if (!is_a($guid, 'PEAR_Error')) { - $ts = $history->getTSforAction($guid, 'add'); + $ts = $state->getSyncTSforAction($guid, 'add'); $state->setUID($type, $syncItem->getLocURI(), $guid, $ts); $state->log("Client-Add"); Horde::logMessage('SyncML: added client entry as ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG); @@ -180,7 +180,7 @@ class Horde_SyncML_Sync { if (!is_a($guid, 'PEAR_Error') && $guid != false) { $registry->call($hordeType . '/delete', array($guid)); - #$ts = $history->getTSforAction($guid, 'delete'); + #$ts = $state->getSyncTSforAction($guid, 'delete'); #$state->setUID($type, $syncItem->getLocURI(), $guid, $ts); $state->log("Client-Delete"); Horde::logMessage('SyncML: deleted entry ' . $guid . ' due to client request', __FILE__, __LINE__, PEAR_LOG_DEBUG); @@ -194,13 +194,14 @@ class Horde_SyncML_Sync { if ($guid) { Horde::logMessage('SyncML: locuri'. $syncItem->getLocURI() . ' guid ' . $guid , __FILE__, __LINE__, PEAR_LOG_ERR); // Entry exists: replace current one. - $ok = $registry->call($hordeType . '/replace', - array($guid, $state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); + $ok = $registry->call($hordeType . '/replace', + array($guid, $state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); if (!is_a($ok, 'PEAR_Error')) { - $ts = $history->getTSforAction($guid, 'modify'); + $ts = $state->getSyncTSforAction($guid, 'modify'); $state->setUID($type, $syncItem->getLocURI(), $guid, $ts); Horde::logMessage('SyncML: replaced entry due to client request guid: ' .$guid. ' ts: ' .$ts, __FILE__, __LINE__, PEAR_LOG_DEBUG); $state->log("Client-Replace"); + error_log("done_replace"); $ok = true; } else { // Entry may have been deleted; try adding it. @@ -214,7 +215,7 @@ class Horde_SyncML_Sync { $guid = $registry->call($hordeType . '/import', array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); if (!is_a($guid, 'PEAR_Error')) { - $ts = $history->getTSforAction($guid, 'add'); + $ts = $state->getSyncTSforAction($guid, 'add'); $state->setUID($type, $syncItem->getLocURI(), $guid, $ts); $state->log("Client-AddReplace"); Horde::logMessage('SyncML: r/ added client entry as ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG); diff --git a/phpgwapi/inc/horde/Horde/SyncML/Sync/RefreshFromServerSync.php b/phpgwapi/inc/horde/Horde/SyncML/Sync/RefreshFromServerSync.php index 35c071f4e7..cf2f7ff507 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Sync/RefreshFromServerSync.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Sync/RefreshFromServerSync.php @@ -19,7 +19,6 @@ class Horde_SyncML_Sync_RefreshFromServerSync extends Horde_SyncML_Sync_TwoWaySy function handleSync($currentCmdID, $hordeType, $syncType, &$output, $refts) { global $registry; - $history = $GLOBALS['egw']->contenthistory; $state = &$_SESSION['SyncML.state']; $adds = &$state->getAddedItems($hordeType); diff --git a/phpgwapi/inc/horde/Horde/SyncML/Sync/SlowSync.php b/phpgwapi/inc/horde/Horde/SyncML/Sync/SlowSync.php index 39f587559c..a05f5b6259 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Sync/SlowSync.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Sync/SlowSync.php @@ -160,7 +160,7 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync { $guid = $registry->call($hordeType . '/import', array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType)); if (!is_a($guid, 'PEAR_Error')) { - $ts = $history->getTSforAction($guid, 'add'); + $ts = $state->getSyncTSforAction($guid, 'add'); $state->setUID($type, $syncItem->getLocURI(), $guid, $ts); $state->log("Client-AddReplace"); Horde::logMessage('SyncML: r/ added client entry as ' . $guid, __FILE__, __LINE__, PEAR_LOG_DEBUG); diff --git a/phpgwapi/inc/horde/Horde/SyncML/Sync/TwoWaySync.php b/phpgwapi/inc/horde/Horde/SyncML/Sync/TwoWaySync.php index 84da16ee42..78f02ab0cf 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/Sync/TwoWaySync.php +++ b/phpgwapi/inc/horde/Horde/SyncML/Sync/TwoWaySync.php @@ -60,7 +60,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync { // handle changes if(is_array($changes)) { while($guid = array_shift($changes)) { - $guid_ts = $history->getTSforAction($guid, 'modify'); + $guid_ts = $state->getSyncTSforAction($guid, 'modify'); $sync_ts = $state->getChangeTS($syncType, $guid); Horde::logMessage("SyncML: timestamp modify guid_ts: $guid_ts sync_ts: $sync_ts", __FILE__, __LINE__, PEAR_LOG_DEBUG); if ($sync_ts && $sync_ts == $guid_ts) { @@ -115,7 +115,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync { // handle deletes if(is_array($deletes)) { while($guid = array_shift($deletes)) { - $guid_ts = $history->getTSforAction($guid, 'delete'); + $guid_ts = $state->getSyncTSforAction($guid, 'delete'); $sync_ts = $state->getChangeTS($syncType, $guid); Horde::logMessage("SyncML: timestamp delete guid_ts: $guid_ts sync_ts: $sync_ts", __FILE__, __LINE__, PEAR_LOG_DEBUG); if ($sync_ts && $sync_ts == $guid_ts) { @@ -163,7 +163,7 @@ class Horde_SyncML_Sync_TwoWaySync extends Horde_SyncML_Sync { if(is_array($adds)) { while($guid = array_shift($adds)) { - $guid_ts = $history->getTSforAction($guid, 'add'); + $guid_ts = $state->getSyncTSforAction($guid, 'add'); $sync_ts = $state->getChangeTS($syncType, $guid); Horde::logMessage("SyncML: timestamp add $guid guid_ts: $guid_ts sync_ts: $sync_ts", __FILE__, __LINE__, PEAR_LOG_DEBUG); if ($sync_ts && $sync_ts == $guid_ts) { diff --git a/phpgwapi/setup/setup.inc.php b/phpgwapi/setup/setup.inc.php index fce0007acf..c5e55fdd3b 100755 --- a/phpgwapi/setup/setup.inc.php +++ b/phpgwapi/setup/setup.inc.php @@ -12,7 +12,7 @@ /* Basic information about this app */ $setup_info['phpgwapi']['name'] = 'phpgwapi'; $setup_info['phpgwapi']['title'] = 'eGroupWare API'; -$setup_info['phpgwapi']['version'] = '1.5.016'; +$setup_info['phpgwapi']['version'] = '1.5.017'; $setup_info['phpgwapi']['versions']['current_header'] = '1.29'; $setup_info['phpgwapi']['enable'] = 3; $setup_info['phpgwapi']['app_order'] = 1; diff --git a/phpgwapi/setup/tables_current.inc.php b/phpgwapi/setup/tables_current.inc.php index 9325d3f048..5297fdaed5 100644 --- a/phpgwapi/setup/tables_current.inc.php +++ b/phpgwapi/setup/tables_current.inc.php @@ -302,7 +302,6 @@ $phpgw_baseline = array( 'sync_modified' => array('type' => 'timestamp'), 'sync_deleted' => array('type' => 'timestamp'), 'sync_id' => array('type' => 'auto','nullable' => False), - 'sync_guid' => array('type' => 'varchar','precision' => '120','nullable' => False), 'sync_changedby' => array('type' => 'int','precision' => '4','nullable' => False) ), 'pk' => array('sync_id'), diff --git a/phpgwapi/setup/tables_update.inc.php b/phpgwapi/setup/tables_update.inc.php index 86bf36ed90..00a3937720 100644 --- a/phpgwapi/setup/tables_update.inc.php +++ b/phpgwapi/setup/tables_update.inc.php @@ -578,3 +578,27 @@ function phpgwapi_upgrade1_5_015() return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.5.016'; } + +/** + * Drop no longer needed egw_api_content_history.sync_guid column + */ +function phpgwapi_upgrade1_5_016() +{ + $GLOBALS['egw_setup']->oProc->DropColumn('egw_api_content_history',array( + 'fd' => array( + 'sync_appname' => array('type' => 'varchar','precision' => '60','nullable' => False), + 'sync_contentid' => array('type' => 'varchar','precision' => '60','nullable' => False), + 'sync_added' => array('type' => 'timestamp'), + 'sync_modified' => array('type' => 'timestamp'), + 'sync_deleted' => array('type' => 'timestamp'), + 'sync_id' => array('type' => 'auto','nullable' => False), + 'sync_changedby' => array('type' => 'int','precision' => '4','nullable' => False) + ), + 'pk' => array('sync_id'), + 'fk' => array(), + 'ix' => array('sync_added','sync_modified','sync_deleted','sync_guid','sync_changedby',array('sync_appname','sync_contentid')), + 'uc' => array() + ),'sync_guid'); + + return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.5.017'; +}