Big SyncML patch from Philip Herbert <pherbert(at)knauber.de>:

- change the processing of slowsync, to use the content_map instead of
  trying to build a new one. This caused duplication issues on the
  client if multiple similar records where stored, because only the first
  one found in the server-db was matched, These duplicate entries at client
  side had no entry at serverside, so deleting the wrong one
  on the client (the content with a valid map entry) could cause
  unwanted data loss at server side, because it is impossible for the
  user to see what is a duplicate, and what is not.

see also: 
http://www.nabble.com/again---syncml-duplication-issue-to20333619s3741.html

- reenabled UID from syncml clients, because it was partly used this caused
  issues during SlowSync if the content was changed. 

- infolog, calendar if a uid is found in the provided data, allway try to
  find the corresponding content first   using only the UID, instead of
  using the content-id taken from content_map.

also fixed:

- a few fixes in ./notes
- creating an entry on the client that can not be imported,
  (Example, Nokia E Series Appointment without a Title)
  will no longer create an invalid content-map entry
  However, at client side this is still counted in the Protocol as
  Server-Add
This commit is contained in:
Ralf Becker 2008-11-16 10:42:29 +00:00
parent c8b2d0d5d3
commit 1baa158195
10 changed files with 99 additions and 33 deletions

View File

@ -164,12 +164,15 @@ class addressbook_sif extends addressbook_bo
* @param string $_sifdata
* @return boolean/int/string contact-id or false, if not found
*/
function search($_sifdata)
function search($_sifdata,$contentID=null)
{
if(!$contact = $this->siftoegw($_sifdata))
{
return false;
}
if ($contentID) {
$contact['contact_id'] = $contentID;
}
// patch from Di Guest says: we need to ignore the n_fileas
unset($contact['n_fileas']);
// we probably need to ignore even more as we do in vcaladdressbook

View File

@ -170,12 +170,16 @@ class addressbook_vcal extends addressbook_bo
return $result;
}
function search($_vcard)
function search($_vcard, $contentID=null)
{
if(!($contact = $this->vcardtoegw($_vcard))) {
return false;
}
if ($contentID) {
$contact['contact_id'] = $contentID;
}
unset($contact['private']);
unset($contact['note']);
unset($contact['n_fn']);

View File

@ -169,7 +169,6 @@ class calendar_ical extends calendar_boupdate
$event['start'] = $event['start'] + $DSTCorrection;
$event['end'] = $event['end'] + $DSTCorrection;
*/
$eventGUID = $GLOBALS['egw']->common->generate_uid('calendar',$event['id']);
$vevent = Horde_iCalendar::newComponent('VEVENT',$vcal);
$parameters = $attributes = array();
@ -440,7 +439,7 @@ class calendar_ical extends calendar_boupdate
}
}
$attributes['UID'] = $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)
@ -490,7 +489,7 @@ class calendar_ical extends calendar_boupdate
}
$version = $vcal->getAttribute('VERSION');
if(!is_array($this->supportedFields))
{
$this->setSupportedFields();
@ -1298,7 +1297,6 @@ class calendar_ical extends calendar_boupdate
$vcardData['end'] = $attributes['value'];
break;
case 'DTSTART':
//error_log (" CALENDAR ROBV DTSTART: ". $attributes['value']);
$vcardData['start'] = $attributes['value'];
break;
case 'LOCATION':
@ -1405,7 +1403,13 @@ class calendar_ical extends calendar_boupdate
$event['id'] = $uid_event['id'];
unset($uid_event);
}
break;
// not use weak uids that might come from syncml clients
if (isset($event['uid']) && (strlen($event['uid']) < 20 || is_numeric($event['uid'])))
{
error_log ("unset weak uid");
unset ($event['uid']);
}
break;
case 'TRANSP':
$vcardData['non_blocking'] = $attributes['value'] == 'TRANSPARENT';
break;
@ -1516,17 +1520,25 @@ class calendar_ical extends calendar_boupdate
return false;
}
function search($_vcalData)
function search($_vcalData, $contentID=null)
{
if(!$event = $this->icaltoegw($_vcalData)) {
return false;
}
if ($event['uid'] && ($uidmatch = $this->read($event['uid'])))
{
return $uidmatch['id'];
}
$query = array(
'cal_start='.$this->date2ts($event['start'],true), // true = Server-time
'cal_end='.$this->date2ts($event['end'],true),
);
if ($contentID) {
$query[] = 'egw_cal.cal_id='.(int)$contentID;
}
#foreach(array('title','location','priority','public','non_blocking') as $name) {
foreach(array('title','location','public','non_blocking') as $name) {
if (isset($event[$name])) $query['cal_'.$name] = $event[$name];

View File

@ -205,7 +205,7 @@ class calendar_sif extends calendar_boupdate
return $finalEvent;
}
function search($_sifdata) {
function search($_sifdata, $contentID=null) {
if(!$event = $this->siftoegw($_sifdata)) {
return false;
}
@ -214,7 +214,11 @@ class calendar_sif extends calendar_boupdate
'cal_start='.$this->date2ts($event['start'],true), // true = Server-time
'cal_end='.$this->date2ts($event['end'],true),
);
if ($contentID) {
$query[] = 'egw_cal.cal_id='.(int)$contentID;
}
#foreach(array('title','location','priority','public','non_blocking') as $name) {
foreach(array('title','location','public','non_blocking') as $name) {
if (isset($event[$name])) $query['cal_'.$name] = $event[$name];

View File

@ -128,11 +128,22 @@ class infolog_ical extends infolog_bo
return $this->write($taskData);
}
function searchVTODO($_vcalData)
function searchVTODO($_vcalData, $contentID=null)
{
if(!$egwData = $this->vtodotoegw($_vcalData)) {
return false;
}
$myfilter = array('col_filter' => array('info_uid'=>$egwData['info_uid'])) ;
if ($egwData['info_uid'] && ($found=parent::search($myfilter)) && ($uidmatch = array_shift($found)))
{
return $uidmatch['info_id'];
};
unset($egwData['info_uid']);
if ($contentID) {
$egwData['info_id'] = $contentID;
}
#unset($egwData['info_priority']);
@ -223,6 +234,12 @@ class infolog_ical extends infolog_bo
$taskData['info_id'] = $uid_task['id'];
unset($uid_task);
}
// not use weak uids that might come from syncml clients
if (isset($event['uid']) && (strlen($event['uid']) < 20 || is_numeric($event['uid'])))
{
unset ($event['uid']);
}
break;
case 'PERCENT-COMPLETE':
$taskData['info_percent'] = (int) $attributes['value'];
@ -300,13 +317,16 @@ class infolog_ical extends infolog_bo
return $this->write($note);
}
function searchVNOTE($_vcalData, $_type)
function searchVNOTE($_vcalData, $_type, $contentID=null)
{
if(!$note = $this->vnotetoegw($_vcalData)) {
if(!$note = $this->vnotetoegw($_vcalData,$_type)) {
return false;
}
if ($contentID) {
$note['info_id'] = $contentID;
}
$filter = array('col_filter' => $egwData);
$filter = array('col_filter' => $note);
if($foundItems = $this->search($filter)) {
if(count($foundItems) > 0) {
$itemIDs = array_keys($foundItems);
@ -335,6 +355,8 @@ class infolog_ical extends infolog_bo
}
else
{
// should better be imported as subject, but causes duplicates
// TODO: should be examined
$note['info_des'] = $txt;
}

View File

@ -243,10 +243,13 @@ class infolog_sif extends infolog_bo
}
}
function searchSIF($_sifData, $_sifType) {
function searchSIF($_sifData, $_sifType, $contentID=null) {
if(!$egwData = $this->siftoegw($_sifData, $_sifType)) {
return false;
}
if ($contentID) {
$egwData['info_id'] = $contentID;
}
$filter = array('col_filter' => $egwData);
if($foundItems = $this->search($filter)) {

View File

@ -112,9 +112,9 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
Horde::logMessage("SyncML: Anchor match, TwoWaySync since " . $clientlast, __FILE__, __LINE__, PEAR_LOG_DEBUG);
} else {
Horde::logMessage("SyncML: Anchor mismatch, enforcing SlowSync clientlast $clientlast serverlast ".$this->_metaAnchorLast, __FILE__, __LINE__, PEAR_LOG_DEBUG);
// Mismatch, enforce slow sync.
// Mismatch, enforce slow sync. 508=RESPONSE_REFRESH_REQUIRED 201=ALERT_SLOW_SYNC
$this->_alert = 201;
$code = 508;
$code = 508;
// create new synctype
$sync = &Horde_SyncML_Sync::factory($this->_alert);
$sync->_targetLocURI = $this->_targetLocURI;
@ -122,7 +122,9 @@ class Horde_SyncML_Command_Alert extends Horde_SyncML_Command {
if(isset($this->_targetLocURIParameters))
$sync->_targetLocURIParameters = $this->_targetLocURIParameters;
$state->setSync($this->_targetLocURI, $sync);
$state->removeAllUID($this->_targetLocURI);
// PH : no longer delete entire content_map entries for client before SlowSync,
// use content_map to verify if content is mapped correctly
//$state->removeAllUID($this->_targetLocURI);
}
$status = &new Horde_SyncML_Command_Status($code, 'Alert');

View File

@ -1000,7 +1000,7 @@ class Horde_SyncML_State {
case 'text/x-vcalendar':
case 'text/x-vevent':
case 'text/x-vtodo':
$content = preg_replace('/^UID:.*\n/m', '', $content, 1);
// $content = preg_replace('/^UID:.*\n/m', '', $content, 1);
break;
}
@ -1023,7 +1023,7 @@ class Horde_SyncML_State {
case 'text/x-vcalendar':
case 'text/x-vevent':
case 'text/x-vtodo':
$content = preg_replace('/^UID:.*\n/m', '', $content, 1);
// $content = preg_replace('/^UID:.*\n/m', '', $content, 1);
break;
}

View File

@ -301,6 +301,25 @@ class EGW_SyncML_State extends Horde_SyncML_State
$GLOBALS['egw']->db->delete('egw_contentmap', array('map_id' => $mapID), __LINE__, __FILE__, 'syncml');
return true;
}
/**
* Used in SlowSync
* Removes all locid<->guid mappings for the given type,
* that are older than $ts.
*
* Returns always true.
*/
function removeOldUID($type, $ts)
{
$mapID = $this->_locName . $this->_sourceURI . $type;
$where[] = "map_id = '".$mapID."' AND map_timestamp < '".$GLOBALS['egw']->db->to_timestamp($ts)."'";
Horde::logMessage("SyncML: state->removeOldUID(type=$type)", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$GLOBALS['egw']->db->delete('egw_contentmap', $where, __LINE__, __FILE__, 'syncml');
return true;
}
@ -325,7 +344,7 @@ class EGW_SyncML_State extends Horde_SyncML_State
}
Horde::logMessage("SyncML: state->removeUID(type=$type,locid=$locid) : removing guid:$guid", __FILE__, __LINE__, PEAR_LOG_DEBUG);
$GLOBALS['egw']->db->delete('egw_contentmap', $where, __LINE__, __FILE__, 'syncml');
return $guid;
@ -344,13 +363,7 @@ class EGW_SyncML_State extends Horde_SyncML_State
{
#Horde::logMessage("SyncML: setUID $type, $locid, $guid, $ts ", __FILE__, __LINE__, PEAR_LOG_DEBUG);
#Horde::logMessage("SyncML: setUID ". $this->getUIDMapping($guid), __FILE__, __LINE__, PEAR_LOG_DEBUG);
// fix $guid, it maybe was to long for some devices
// format is appname-id-systemid
#$guidParts = explode('-',$guid);
#if(count($guidParts) == 3) {
# $guid = $GLOBALS['egw']->common->generate_uid($guidParts[0],$guidParts[1]);
#}
// problem: entries created from client, come here with the (long) server guid,
// but getUIDMapping does not know them and can not map server-guid <--> client guid
$guid = $this->getUIDMapping($_guid);

View File

@ -25,10 +25,12 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$history = $GLOBALS['egw']->contenthistory;
$state = &$_SESSION['SyncML.state'];
$adds = &$state->getAddedItems($hordeType);
Horde::logMessage("SyncML: ".count($adds). ' added items found for '.$hordeType , __FILE__, __LINE__, PEAR_LOG_DEBUG);
$serverAnchorNext = $state->getServerAnchorNext($syncType);
// now we remove all UID from contentmap that have not been verified in this slowsync
$state->removeOldUID($syncType, $serverAnchorNext);
$adds = &$state->getAddedItems($hordeType);
Horde::logMessage("SyncML: ".count($adds). ' added items found for '.$hordeType , __FILE__, __LINE__, PEAR_LOG_DEBUG);
$counter = 0;
if(is_array($adds)) {
@ -147,7 +149,7 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$guid = false;
$guid = $registry->call($hordeType . '/search',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType));
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType, $state->getGlobalUID($type, $syncItem->getLocURI()) ));
if ($guid) {
# entry exists in database already. Just update the mapping
@ -156,10 +158,11 @@ class Horde_SyncML_Sync_SlowSync extends Horde_SyncML_Sync_TwoWaySync {
$state->log("Client-Replace");
} else {
# Entry does not exist in database: add a new one.
$state->removeUID($type, $syncItem->getLocURI());
Horde::logMessage('SyncML: try to add contentype ' . $contentType .' to '. $hordeType, __FILE__, __LINE__, PEAR_LOG_DEBUG);
$guid = $registry->call($hordeType . '/import',
array($state->convertClient2Server($syncItem->getContent(), $contentType), $contentType));
if (!is_a($guid, 'PEAR_Error')) {
if (!is_a($guid, 'PEAR_Error') && $guid != false) {
$ts = $state->getSyncTSforAction($guid, 'add');
$state->setUID($type, $syncItem->getLocURI(), $guid, $ts);
$state->log("Client-AddReplace");