Commit 01840c37 authored by Sebastien Robin's avatar Sebastien Robin

added new tests (change an object on two different places at the same time,

                 working on subojbects)
added the simulation of conflicts in the synchronization
corrected some bugs


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@387 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 02f2305d
...@@ -103,7 +103,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -103,7 +103,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
security.declareProtected(Permissions.ModifyPortalContent, 'addNode') security.declareProtected(Permissions.ModifyPortalContent, 'addNode')
def addNode(self, xml=None, object=None, previous_xml=None, def addNode(self, xml=None, object=None, previous_xml=None,
object_id=None, force=0, **kw): object_id=None, force=0, simulate=0, **kw):
""" """
A node is added A node is added
...@@ -132,7 +132,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -132,7 +132,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
for element in self.getXupdateElementList(xml): for element in self.getXupdateElementList(xml):
xml = self.getElementFromXupdate(element) xml = self.getElementFromXupdate(element)
conflict_list += self.addNode(xml=xml,object=object, conflict_list += self.addNode(xml=xml,object=object,
previous_xml=previous_xml, force=force, **kw) previous_xml=previous_xml, force=force,
simulate=simulate, **kw)
elif xml.nodeName == 'object': elif xml.nodeName == 'object':
if object_id is None: if object_id is None:
object_id = self.getAttribute(xml,'id') object_id = self.getAttribute(xml,'id')
...@@ -170,7 +171,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -170,7 +171,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
px_tool._modifyProxy(proxy,rpath) px_tool._modifyProxy(proxy,rpath)
subobject = object._getOb(object_id) subobject = object._getOb(object_id)
self.newObject(object=subobject,xml=xml) self.newObject(object=subobject,xml=xml,simulate=simulate)
elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \ elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \
and self.getSubObjectDepth(xml)>=1: and self.getSubObjectDepth(xml)>=1:
sub_object_id = self.getSubObjectId(xml) sub_object_id = self.getSubObjectId(xml)
...@@ -194,7 +195,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -194,7 +195,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
LOG('addNode',0,'sub_xml: %s' % str(sub_xml)) LOG('addNode',0,'sub_xml: %s' % str(sub_xml))
# Then do the udpate # Then do the udpate
conflict_list += self.addNode(xml=sub_xml,object=sub_object, conflict_list += self.addNode(xml=sub_xml,object=sub_object,
previous_xml=sub_previous_xml, force=force) previous_xml=sub_previous_xml, force=force,
simulate=simulate, **kw)
elif xml.nodeName == self.history_tag or self.isHistoryAdd(xml)>0: elif xml.nodeName == self.history_tag or self.isHistoryAdd(xml)>0:
# We want to add a workflow action # We want to add a workflow action
wf_tool = getToolByName(object,'portal_workflow') wf_tool = getToolByName(object,'portal_workflow')
...@@ -211,12 +213,12 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -211,12 +213,12 @@ class ERP5Conduit(XMLSyncUtilsMixin):
status=status,wf_tool=wf_tool, status=status,wf_tool=wf_tool,
xml=xml) xml=xml)
LOG('addNode, workflow_history wf_conflict_list:',0,wf_conflict_list) LOG('addNode, workflow_history wf_conflict_list:',0,wf_conflict_list)
if wf_conflict_list==[] or force: if wf_conflict_list==[] or force and not simulate:
LOG('addNode, setting status:',0,'ok') LOG('addNode, setting status:',0,'ok')
wf_tool.setStatusOf(wf_id,object,status) wf_tool.setStatusOf(wf_id,object,status)
else: else:
conflict_list += wf_conflict_list conflict_list += wf_conflict_list
elif xml.nodeName in self.local_role_list: elif xml.nodeName in self.local_role_list and not simulate:
# We want to add a local role # We want to add a local role
roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens') roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens')
roles = list(roles) # Needed for CPS, or we have a CPS error roles = list(roles) # Needed for CPS, or we have a CPS error
...@@ -224,11 +226,13 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -224,11 +226,13 @@ class ERP5Conduit(XMLSyncUtilsMixin):
roles = roles[1:] roles = roles[1:]
object.manage_setLocalRoles(user,roles) object.manage_setLocalRoles(user,roles)
else: else:
conflict_list += self.updateNode(xml=xml,object=object, force=force, **kw) conflict_list += self.updateNode(xml=xml,object=object, force=force,
simulate=simulate, **kw)
return conflict_list return conflict_list
security.declareProtected(Permissions.ModifyPortalContent, 'deleteNode') security.declareProtected(Permissions.ModifyPortalContent, 'deleteNode')
def deleteNode(self, xml=None, object=None, object_id=None, force=None, **kw): def deleteNode(self, xml=None, object=None, object_id=None, force=None,
simulate=0, **kw):
""" """
A node is deleted A node is deleted
""" """
...@@ -251,7 +255,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -251,7 +255,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
sub_object = object._getOb(sub_object_id) sub_object = object._getOb(sub_object_id)
sub_xml = self.getSubObjectXupdate(xml) sub_xml = self.getSubObjectXupdate(xml)
conflict_list += self.deleteNode(xml=sub_xml,object=sub_object, conflict_list += self.deleteNode(xml=sub_xml,object=sub_object,
force=force) force=force, simulate=simulate, **kw)
except KeyError: except KeyError:
pass pass
else: # We do have an object_id else: # We do have an object_id
...@@ -263,7 +267,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -263,7 +267,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return conflict_list return conflict_list
security.declareProtected(Permissions.ModifyPortalContent, 'updateNode') security.declareProtected(Permissions.ModifyPortalContent, 'updateNode')
def updateNode(self, xml=None, object=None, previous_xml=None, force=0, **kw): def updateNode(self, xml=None, object=None, previous_xml=None, force=0,
simulate=0, **kw):
""" """
A node is updated with some xupdate A node is updated with some xupdate
- xml : the xml corresponding to the update, it should be xupdate - xml : the xml corresponding to the update, it should be xupdate
...@@ -280,7 +285,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -280,7 +285,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
#xupdate_utils = XupdateUtils() #xupdate_utils = XupdateUtils()
xupdate_utils = self xupdate_utils = self
conflict_list += xupdate_utils.applyXupdate(object=object,xupdate=xml,conduit=self, conflict_list += xupdate_utils.applyXupdate(object=object,xupdate=xml,conduit=self,
previous_xml=previous_xml, force=force) previous_xml=previous_xml, force=force, simulate=simulate,
**kw)
# we may have only the part of an xupdate # we may have only the part of an xupdate
else: else:
args = {} args = {}
...@@ -358,7 +364,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -358,7 +364,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
#publisher_value=current_data, # not needed any more #publisher_value=current_data, # not needed any more
#subscriber_value=data)] # not needed any more #subscriber_value=data)] # not needed any more
# We will now apply the argument with the method edit # We will now apply the argument with the method edit
if args != {} and (isConflict==0 or force): if args != {} and (isConflict==0 or force) and (not simulate):
LOG('updateNode',0,'object._edit, args: %s' % str(args)) LOG('updateNode',0,'object._edit, args: %s' % str(args))
object._edit(**args) object._edit(**args)
# It is sometimes required to do something after an edit # It is sometimes required to do something after an edit
...@@ -368,12 +374,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -368,12 +374,14 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if keyword == 'object': if keyword == 'object':
# This is the case where we have to call addNode # This is the case where we have to call addNode
LOG('updateNode',0,'we will add sub-object') LOG('updateNode',0,'we will add sub-object')
conflict_list += self.addNode(xml=subnode,object=object,force=force) conflict_list += self.addNode(xml=subnode,object=object,force=force,
elif keyword == self.history_tag: simulate=simulate, **kw)
elif keyword == self.history_tag and not simulate:
# This is the case where we have to call addNode # This is the case where we have to call addNode
LOG('updateNode',0,'we will add history') LOG('updateNode',0,'we will add history')
conflict_list += self.addNode(xml=subnode,object=object,force=force) conflict_list += self.addNode(xml=subnode,object=object,force=force,
elif keyword == self.local_role_tag: simulate=simulate,**kw)
elif keyword == self.local_role_tag and not simulate:
# This is the case where we have to call addNode # This is the case where we have to call addNode
LOG('updateNode',0,'we will add a local role') LOG('updateNode',0,'we will add a local role')
roles = self.convertXmlValue(data,data_type='tokens') roles = self.convertXmlValue(data,data_type='tokens')
...@@ -403,7 +411,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -403,7 +411,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
LOG('updateNode',0,'sub_xml: %s' % str(sub_xml)) LOG('updateNode',0,'sub_xml: %s' % str(sub_xml))
# Then do the udpate # Then do the udpate
conflict_list += self.updateNode(xml=sub_xml,object=sub_object, force=force, conflict_list += self.updateNode(xml=sub_xml,object=sub_object, force=force,
previous_xml=sub_previous_xml) previous_xml=sub_previous_xml,simulate=simulate, **kw)
return conflict_list return conflict_list
security.declareProtected(Permissions.AccessContentsInformation,'getFormatedArgs') security.declareProtected(Permissions.AccessContentsInformation,'getFormatedArgs')
...@@ -635,7 +643,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -635,7 +643,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
except IndexError: # There is no data except IndexError: # There is no data
data = None data = None
data = self.convertXmlValue(data, data_type=data_type) data = self.convertXmlValue(data, data_type=data_type)
LOG('getObjectProperty',0,'prop; %s, data_type:%s, data: %s' % (property,data_type,data))
return data return data
return None return None
...@@ -699,12 +706,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -699,12 +706,14 @@ class ERP5Conduit(XMLSyncUtilsMixin):
security.declareProtected(Permissions.ModifyPortalContent, 'newObject') security.declareProtected(Permissions.ModifyPortalContent, 'newObject')
def newObject(self, object=None, xml=None): def newObject(self, object=None, xml=None, simulate=0):
""" """
modify the object with datas from modify the object with datas from
the xml (action section) the xml (action section)
""" """
args = {} args = {}
if simulate:
return
# Retrieve the list of users with a role and delete default roles # Retrieve the list of users with a role and delete default roles
user_role_list = map(lambda x:x[0],object.get_local_roles()) user_role_list = map(lambda x:x[0],object.get_local_roles())
object.manage_delLocalRoles(user_role_list) object.manage_delLocalRoles(user_role_list)
...@@ -766,7 +775,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -766,7 +775,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
for subnode in self.getElementNodeList(xml): for subnode in self.getElementNodeList(xml):
if subnode.nodeName in self.XUPDATE_EL: if subnode.nodeName in self.XUPDATE_EL:
e_list += [subnode] e_list += [subnode]
LOG('getXupdateElementList, e_list:%',0,e_list) LOG('getXupdateElementList, e_list:',0,e_list)
return e_list return e_list
security.declareProtected(Permissions.AccessContentsInformation,'getElementFromXupdate') security.declareProtected(Permissions.AccessContentsInformation,'getElementFromXupdate')
...@@ -775,7 +784,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -775,7 +784,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
from a xupdate:element returns the element as xml from a xupdate:element returns the element as xml
""" """
if xml.nodeName in self.XUPDATE_EL: if xml.nodeName in self.XUPDATE_EL:
result = '<' result = u'<'
result += xml.attributes[0].nodeValue result += xml.attributes[0].nodeValue
for subnode in self.getElementNodeList(xml): #getElementNodeList for subnode in self.getElementNodeList(xml): #getElementNodeList
if subnode.nodeName == 'xupdate:attribute': if subnode.nodeName == 'xupdate:attribute':
...@@ -786,11 +795,11 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -786,11 +795,11 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xml_string = StringIO() xml_string = StringIO()
PrettyPrint(xml,xml_string) PrettyPrint(xml,xml_string)
xml_string = xml_string.getvalue() xml_string = xml_string.getvalue()
xml_string = unicode(xml_string,encoding='utf-8')
maxi = max(xml_string.find('>')+1,\ maxi = max(xml_string.find('>')+1,\
xml_string.rfind('</xupdate:attribute>')+len('</xupdate:attribute>')) xml_string.rfind('</xupdate:attribute>')+len('</xupdate:attribute>'))
result += xml_string[maxi:xml_string.find('</xupdate:element>')] result += xml_string[maxi:xml_string.find('</xupdate:element>')]
result += '</' + xml.attributes[0].nodeValue + '>' result += '</' + xml.attributes[0].nodeValue + '>'
LOG('getElementFromXupdate, result:',0,result)
return self.convertToXml(result) return self.convertToXml(result)
return xml return xml
...@@ -856,7 +865,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -856,7 +865,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# XXX is it the right place ? It should be in XupdateUtils, but here we # XXX is it the right place ? It should be in XupdateUtils, but here we
# have some specific things to do # have some specific things to do
security.declareProtected(Permissions.ModifyPortalContent, 'applyXupdate') security.declareProtected(Permissions.ModifyPortalContent, 'applyXupdate')
def applyXupdate(self, object=None, xupdate=None, conduit=None, force=0, **kw): def applyXupdate(self, object=None, xupdate=None, conduit=None, force=0,
simulate=0, **kw):
""" """
Parse the xupdate and then it will call the conduit Parse the xupdate and then it will call the conduit
""" """
...@@ -869,13 +879,13 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -869,13 +879,13 @@ class ERP5Conduit(XMLSyncUtilsMixin):
selection_name = '' selection_name = ''
if subnode.nodeName in self.XUPDATE_INSERT_OR_ADD: if subnode.nodeName in self.XUPDATE_INSERT_OR_ADD:
conflict_list += conduit.addNode(xml=sub_xupdate,object=object, \ conflict_list += conduit.addNode(xml=sub_xupdate,object=object, \
force=force, **kw) force=force, simulate=simulate, **kw)
elif subnode.nodeName in self.XUPDATE_DEL: elif subnode.nodeName in self.XUPDATE_DEL:
conflict_list += conduit.deleteNode(xml=sub_xupdate, object=object, \ conflict_list += conduit.deleteNode(xml=sub_xupdate, object=object, \
force=force, **kw) force=force, simulate=simulate, **kw)
elif subnode.nodeName in self.XUPDATE_UPDATE: elif subnode.nodeName in self.XUPDATE_UPDATE:
conflict_list += conduit.updateNode(xml=sub_xupdate, object=object, \ conflict_list += conduit.updateNode(xml=sub_xupdate, object=object, \
force=force, **kw) force=force, simulate=simulate, **kw)
#elif subnode.nodeName in self.XUPDATE_INSERT: #elif subnode.nodeName in self.XUPDATE_INSERT:
# conflict_list += conduit.addNode(xml=subnode, object=object, force=force, **kw) # conflict_list += conduit.addNode(xml=subnode, object=object, force=force, **kw)
......
...@@ -198,6 +198,8 @@ class Signature(SyncCode): ...@@ -198,6 +198,8 @@ class Signature(SyncCode):
self.resetConflictList() self.resetConflictList()
self.md5_string = None self.md5_string = None
self.force = 0 self.force = 0
self.setSubscriberXupdate(None)
self.setPublisherXupdate(None)
#def __init__(self,object=None, status=None, xml_string=None): #def __init__(self,object=None, status=None, xml_string=None):
# self.uid = object.uid # self.uid = object.uid
...@@ -271,6 +273,30 @@ class Signature(SyncCode): ...@@ -271,6 +273,30 @@ class Signature(SyncCode):
""" """
return self.temp_xml return self.temp_xml
def setSubscriberXupdate(self, xupdate):
"""
set the full temp xupdate
"""
self.subscriber_xupdate = xupdate
def getSubscriberXupdate(self):
"""
get the full temp xupdate
"""
return self.subscriber_xupdate
def setPublisherXupdate(self, xupdate):
"""
set the full temp xupdate
"""
self.publisher_xupdate = xupdate
def getPublisherXupdate(self):
"""
get the full temp xupdate
"""
return self.publisher_xupdate
def setMD5(self, xml): def setMD5(self, xml):
""" """
set the MD5 object of this signature set the MD5 object of this signature
......
...@@ -552,13 +552,23 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -552,13 +552,23 @@ class XMLSyncUtilsMixin(SyncCode):
def getSyncMLData(self, domain=None,remote_xml=None,cmd_id=0, def getSyncMLData(self, domain=None,remote_xml=None,cmd_id=0,
subscriber=None,destination_path=None, subscriber=None,destination_path=None,
xml_confirmation=None): xml_confirmation=None,conduit=None):
""" """
This generate the syncml data message. This returns a string This generate the syncml data message. This returns a string
with all modification made locally (ie replace, add ,delete...) with all modification made locally (ie replace, add ,delete...)
if object is not None, this usually means we want to set the
actual xupdate on the signature.
""" """
local_gid_list = [] local_gid_list = []
syncml_data = '' syncml_data = ''
# store_xupdate = 0
# if object is None:
# object_list = domain.getObjectList()
# else:
# store_xupdate = 1
# object_list = [object]
for object in domain.getObjectList(): for object in domain.getObjectList():
status = self.SENT status = self.SENT
gid_generator = getattr(object,domain.getGidGenerator(),None) gid_generator = getattr(object,domain.getGidGenerator(),None)
...@@ -575,6 +585,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -575,6 +585,7 @@ class XMLSyncUtilsMixin(SyncCode):
LOG('getSyncMLData',0,'hasSignature: %s' % str(subscriber.hasSignature(object_gid))) LOG('getSyncMLData',0,'hasSignature: %s' % str(subscriber.hasSignature(object_gid)))
LOG('getSyncMLData',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC)) LOG('getSyncMLData',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC))
signature = subscriber.getSignature(object_gid) signature = subscriber.getSignature(object_gid)
LOG('getSyncMLData',0,'current object: %s' % str(object.getId()))
if signature is not None: if signature is not None:
LOG('getSyncMLData',0,'signature.status: %s' % str(signature.getStatus())) LOG('getSyncMLData',0,'signature.status: %s' % str(signature.getStatus()))
LOG('getSyncMLData',0,'signature.action: %s' % str(signature.getAction())) LOG('getSyncMLData',0,'signature.action: %s' % str(signature.getAction()))
...@@ -615,7 +626,9 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -615,7 +626,9 @@ class XMLSyncUtilsMixin(SyncCode):
if signature.getStatus()==self.PUB_CONFLICT_MERGE: if signature.getStatus()==self.PUB_CONFLICT_MERGE:
xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id, xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id,
self.CONFLICT_MERGE,'Replace') self.CONFLICT_MERGE,'Replace')
set_synchronized = 1
if not signature.checkMD5(xml_object): if not signature.checkMD5(xml_object):
set_synchronized = 0
# This object has changed on this side, we have to generate some xmldiff # This object has changed on this side, we have to generate some xmldiff
xml_string = self.getXupdateObject(object=object, xml_string = self.getXupdateObject(object=object,
xml_mapping=domain.xml_mapping, xml_mapping=domain.xml_mapping,
...@@ -638,7 +651,18 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -638,7 +651,18 @@ class XMLSyncUtilsMixin(SyncCode):
xml_string=xml_string, more_data=more_data) xml_string=xml_string, more_data=more_data)
cmd_id += 1 cmd_id += 1
signature.setTempXML(xml_object) signature.setTempXML(xml_object)
else: # We should not have this case when we are in CONFLICT_MERGE # Now we can apply the xupdate from the subscriber
subscriber_xupdate = signature.getSubscriberXupdate()
LOG('getSyncMLData subscriber_xupdate',0,subscriber_xupdate)
if subscriber_xupdate is not None:
old_xml = signature.getXML()
conduit.updateNode(xml=subscriber_xupdate, object=object,
previous_xml=old_xml,force=(domain.getDomainType==self.SUB),
simulate=0)
xml_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping)
signature.setTempXML(xml_object)
if set_synchronized: # We have to do that after this previous update
# We should not have this case when we are in CONFLICT_MERGE
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN: elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN:
# We have decided to apply the update # We have decided to apply the update
...@@ -691,7 +715,7 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -691,7 +715,7 @@ class XMLSyncUtilsMixin(SyncCode):
return (syncml_data,xml_confirmation,cmd_id) return (syncml_data,xml_confirmation,cmd_id)
def applyActionList(self, domain=None, subscriber=None,destination_path=None, def applyActionList(self, domain=None, subscriber=None,destination_path=None,
cmd_id=0,remote_xml=None,conduit=None): cmd_id=0,remote_xml=None,conduit=None,simulate=0):
""" """
This just look to a list of action to do, then id applies This just look to a list of action to do, then id applies
each action one by one, thanks to a conduit each action one by one, thanks to a conduit
...@@ -706,8 +730,6 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -706,8 +730,6 @@ class XMLSyncUtilsMixin(SyncCode):
status_code = self.SUCCESS status_code = self.SUCCESS
# Thirst we have to check the kind of action it is # Thirst we have to check the kind of action it is
partial_data = self.getPartialData(next_action) partial_data = self.getPartialData(next_action)
#LOG('XMLSyncUtils',0,'partial_data: %s' % str(partial_data))
#LOG('XMLSyncUtils',0,'checkActionMoreData: %s' % str(self.checkActionMoreData(next_action)))
object_gid = self.getActionId(next_action) object_gid = self.getActionId(next_action)
signature = subscriber.getSignature(object_gid) signature = subscriber.getSignature(object_gid)
if signature == None: if signature == None:
...@@ -749,12 +771,6 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -749,12 +771,6 @@ class XMLSyncUtilsMixin(SyncCode):
self.SyncMLConfirmation(cmd_id,object_gid,self.SUCCESS,'Add') self.SyncMLConfirmation(cmd_id,object_gid,self.SUCCESS,'Add')
cmd_id +=1 cmd_id +=1
elif next_action.nodeName == 'Replace': elif next_action.nodeName == 'Replace':
#object = domain.getObjectFromGid(object=destination_path,gid=object_gid)
# object =None
# try:
# object = destination_path._getOb(self.getActionId(next_action))
# except (AttributeError, KeyError):
# pass
LOG('SyncModif',0,'object: %s will be updated...' % str(object)) LOG('SyncModif',0,'object: %s will be updated...' % str(object))
if object is not None: if object is not None:
LOG('SyncModif',0,'object: %s will be updated...' % object.id) LOG('SyncModif',0,'object: %s will be updated...' % object.id)
...@@ -763,7 +779,8 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -763,7 +779,8 @@ class XMLSyncUtilsMixin(SyncCode):
previous_xml = signature.getXML() previous_xml = signature.getXML()
LOG('SyncModif',0,'previous signature: %i' % len(previous_xml)) LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
conflict_list += conduit.updateNode(xml=data_subnode, object=object, conflict_list += conduit.updateNode(xml=data_subnode, object=object,
previous_xml=signature.getXML(),force=force) previous_xml=signature.getXML(),force=force,
simulate=simulate)
mapping = getattr(object,domain.getXMLMapping(),None) mapping = getattr(object,domain.getXMLMapping(),None)
xml_object = '' xml_object = ''
if mapping is not None: if mapping is not None:
...@@ -778,11 +795,25 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -778,11 +795,25 @@ class XMLSyncUtilsMixin(SyncCode):
PrettyPrint(data_subnode,stream=string_io) PrettyPrint(data_subnode,stream=string_io)
data_subnode_string = string_io.getvalue() data_subnode_string = string_io.getvalue()
signature.setPartialXML(data_subnode_string) signature.setPartialXML(data_subnode_string)
else: elif not simulate:
signature.setStatus(self.SYNCHRONIZED) signature.setStatus(self.SYNCHRONIZED)
xml_confirmation += self.SyncMLConfirmation(cmd_id, xml_confirmation += self.SyncMLConfirmation(cmd_id,
object_gid,status_code,'Replace') object_gid,status_code,'Replace')
cmd_id +=1 cmd_id +=1
if simulate:
# This means we are on the publiher side and we want to store
# the xupdate from the subscriber and we also want to generate
# the current xupdate from the last synchronization
string_io = StringIO()
PrettyPrint(data_subnode,stream=string_io)
data_subnode_string = string_io.getvalue()
LOG('applyActionList, subscriber_xupdate:',0,data_subnode_string)
signature.setSubscriberXupdate(data_subnode_string)
# xml_string = self.getXupdateObject(object=object,
# xml_mapping=domain.xml_mapping,
# old_xml=signature.getXML())
# signature.setPublisherXupdate(xml_string) XXX is it needed ??
elif next_action.nodeName == 'Delete': elif next_action.nodeName == 'Delete':
object_id = object.id object_id = object.id
conduit.deleteNode(xml=self.getDataSubNode(next_action), object=destination_path, conduit.deleteNode(xml=self.getDataSubNode(next_action), object=destination_path,
...@@ -881,7 +912,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -881,7 +912,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
return return
subscriber = domain # If we are the client, this is fine subscriber = domain # If we are the client, this is fine
simulate = 0 # used by applyActionList, should be 0 for client
if domain.domain_type == self.PUB: if domain.domain_type == self.PUB:
simulate = 1
for subnode in xml_header.childNodes: for subnode in xml_header.childNodes:
if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Source": if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Source":
subscription_url = str(subnode.childNodes[0].data) subscription_url = str(subnode.childNodes[0].data)
...@@ -901,7 +934,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -901,7 +934,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
destination_path=destination_path, destination_path=destination_path,
subscriber=subscriber, subscriber=subscriber,
remote_xml=remote_xml, remote_xml=remote_xml,
conduit=conduit) conduit=conduit, simulate=simulate)
LOG('SyncModif, has_next_action:',0,has_next_action) LOG('SyncModif, has_next_action:',0,has_next_action)
xml = "" xml = ""
...@@ -925,7 +958,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin): ...@@ -925,7 +958,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
remote_xml=remote_xml, remote_xml=remote_xml,
subscriber=subscriber, subscriber=subscriber,
destination_path=destination_path, destination_path=destination_path,
cmd_id=cmd_id,xml_confirmation=xml_confirmation) cmd_id=cmd_id,xml_confirmation=xml_confirmation,
conduit=conduit)
# syncml body # syncml body
xml += ' <SyncBody>\n' xml += ' <SyncBody>\n'
......
...@@ -58,10 +58,10 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -58,10 +58,10 @@ class TestERP5SyncML(ERP5TypeTestCase):
description1 = 'description1 $sdfr_sdfsdf_oisfsopf' description1 = 'description1 $sdfr_sdfsdf_oisfsopf'
first_name2 = 'Jean-Paul' first_name2 = 'Jean-Paul'
last_name2 = 'Smets' last_name2 = 'Smets'
description2 = 'descrip ti on2@ $*<<<<>>>></title>&oekd' description2 = 'description2@ $*< <<< >>>></title>&oekd'
first_name3 = 'Yoshinori' first_name3 = 'Yoshinori'
last_name3 = 'Okuji' last_name3 = 'Okuji'
description3 = 'description sdf__sdf_df___&&]]]' description3 = 'description3 sdf__sdf_df___&&]]]'
xml_mapping = 'asXML' xml_mapping = 'asXML'
id1 = '170' id1 = '170'
id2 = '171' id2 = '171'
...@@ -157,11 +157,11 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -157,11 +157,11 @@ class TestERP5SyncML(ERP5TypeTestCase):
user = uf.getUserById('ERP5TypeTestCase').__of__(uf) user = uf.getUserById('ERP5TypeTestCase').__of__(uf)
newSecurityManager(None, user) newSecurityManager(None, user)
def testPopulatePersonServer(self, quiet=0, run=run_all_test): def populatePersonServer(self, quiet=0, run=run_all_test):
if not run: return if not run: return
if not quiet: if not quiet:
ZopeTestCase._print('\nTest Populate Person Server ') ZopeTestCase._print('\nTest Populate Person Server ')
LOG('Testing... ',0,'testPopulatePersonServer') LOG('Testing... ',0,'populatePersonServer')
self.login() self.login()
person_server = self.getPersonServer() person_server = self.getPersonServer()
person_id = '' person_id = ''
...@@ -205,7 +205,7 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -205,7 +205,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
LOG('Testing... ',0,'testGetObjectList') LOG('Testing... ',0,'testGetObjectList')
self.login() self.login()
self.setupPublicationAndSubscription(quiet=1,run=1) self.setupPublicationAndSubscription(quiet=1,run=1)
nb_person = self.testPopulatePersonServer(quiet=1,run=1) nb_person = self.populatePersonServer(quiet=1,run=1)
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
publication_list = portal_sync.getPublicationList() publication_list = portal_sync.getPublicationList()
publication = publication_list[0] publication = publication_list[0]
...@@ -231,7 +231,7 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -231,7 +231,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
ZopeTestCase._print('\nTest Export and Import ') ZopeTestCase._print('\nTest Export and Import ')
LOG('Testing... ',0,'testExportImport') LOG('Testing... ',0,'testExportImport')
self.login() self.login()
self.testPopulatePersonServer(quiet=1,run=1) self.populatePersonServer(quiet=1,run=1)
person_server = self.getPersonServer() person_server = self.getPersonServer()
person_client1 = self.getPersonClient1() person_client1 = self.getPersonClient1()
person = person_server._getOb(self.id1) person = person_server._getOb(self.id1)
...@@ -278,7 +278,7 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -278,7 +278,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
LOG('Testing... ',0,'testFirstSynchronization') LOG('Testing... ',0,'testFirstSynchronization')
self.login() self.login()
self.setupPublicationAndSubscription(quiet=1,run=1) self.setupPublicationAndSubscription(quiet=1,run=1)
nb_person = self.testPopulatePersonServer(quiet=1,run=1) nb_person = self.populatePersonServer(quiet=1,run=1)
# Synchronize the first client # Synchronize the first client
nb_message1 = self.synchronize(self.sub_id1) nb_message1 = self.synchronize(self.sub_id1)
self.failUnless(nb_message1==self.nb_message_first_synchronization) self.failUnless(nb_message1==self.nb_message_first_synchronization)
...@@ -316,7 +316,7 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -316,7 +316,7 @@ class TestERP5SyncML(ERP5TypeTestCase):
LOG('Testing... ',0,'testGetObjectFromGid') LOG('Testing... ',0,'testGetObjectFromGid')
self.login() self.login()
self.setupPublicationAndSubscription(quiet=1,run=1) self.setupPublicationAndSubscription(quiet=1,run=1)
self.testPopulatePersonServer(quiet=1,run=1) self.populatePersonServer(quiet=1,run=1)
# By default we can just give the id # By default we can just give the id
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
publication = portal_sync.getPublication(self.pub_id) publication = portal_sync.getPublication(self.pub_id)
...@@ -400,13 +400,12 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -400,13 +400,12 @@ class TestERP5SyncML(ERP5TypeTestCase):
person1_s.edit(**kw) person1_s.edit(**kw)
kw = {'description':self.description3} kw = {'description':self.description3}
person1_c.edit(**kw) person1_c.edit(**kw)
# XXX Warning XXX This does not works actually, need to be CORRECTED !!!! self.synchronize(self.sub_id1)
# self.synchronize(self.sub_id1) self.checkSynchronizationStateIsSynchronized()
# self.checkSynchronizationStateIsSynchronized() self.failUnless(person1_s.getFirstName()==self.first_name3)
# self.failUnless(person1_s.getFirstName()==self.first_name3) self.failUnless(person1_s.getDescription()==self.description3)
# self.failUnless(person1_s.getDescription()==self.description3) self.failUnless(person1_c.getFirstName()==self.first_name3)
# self.failUnless(person1_c.getFirstName()==self.first_name3) self.failUnless(person1_c.getDescription()==self.description3)
# self.failUnless(person1_c.getDescription()==self.description3)
def testGetConflictList(self, quiet=0, run=run_all_test): def testGetConflictList(self, quiet=0, run=run_all_test):
# We will try to generate a conflict and then to get it # We will try to generate a conflict and then to get it
...@@ -428,6 +427,8 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -428,6 +427,8 @@ class TestERP5SyncML(ERP5TypeTestCase):
conflict_list = portal_sync.getConflictList() conflict_list = portal_sync.getConflictList()
self.failUnless(len(conflict_list)==1) self.failUnless(len(conflict_list)==1)
conflict = conflict_list[0] conflict = conflict_list[0]
self.failUnless(person1_c.getDescription()==self.description3)
self.failUnless(person1_s.getDescription()==self.description2)
self.failUnless(conflict.getPropertyId()=='description') self.failUnless(conflict.getPropertyId()=='description')
self.failUnless(conflict.getPublisherValue()==self.description2) self.failUnless(conflict.getPublisherValue()==self.description2)
self.failUnless(conflict.getSubscriberValue()==self.description3) self.failUnless(conflict.getSubscriberValue()==self.description3)
...@@ -438,10 +439,10 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -438,10 +439,10 @@ class TestERP5SyncML(ERP5TypeTestCase):
# We will try to generate a conflict and then to get it # We will try to generate a conflict and then to get it
# We will also make sure it contains what we want # We will also make sure it contains what we want
if not run: return if not run: return
self.testGetConflictList(quiet=1,run=1)
if not quiet: if not quiet:
ZopeTestCase._print('\nTest Apply Publisher Value ') ZopeTestCase._print('\nTest Apply Publisher Value ')
LOG('Testing... ',0,'testApplyPublisherValue') LOG('Testing... ',0,'testApplyPublisherValue')
self.testGetConflictList(quiet=1,run=1)
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
conflict_list = portal_sync.getConflictList() conflict_list = portal_sync.getConflictList()
conflict = conflict_list[0] conflict = conflict_list[0]
...@@ -452,8 +453,8 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -452,8 +453,8 @@ class TestERP5SyncML(ERP5TypeTestCase):
conflict.applyPublisherValue() conflict.applyPublisherValue()
self.synchronize(self.sub_id1) self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized() self.checkSynchronizationStateIsSynchronized()
self.failUnless(person1_s.getDescription()==self.description2)
self.failUnless(person1_c.getDescription()==self.description2) self.failUnless(person1_c.getDescription()==self.description2)
self.failUnless(person1_s.getDescription()==self.description2)
conflict_list = portal_sync.getConflictList() conflict_list = portal_sync.getConflictList()
self.failUnless(len(conflict_list)==0) self.failUnless(len(conflict_list)==0)
...@@ -461,12 +462,12 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -461,12 +462,12 @@ class TestERP5SyncML(ERP5TypeTestCase):
# We will try to generate a conflict and then to get it # We will try to generate a conflict and then to get it
# We will also make sure it contains what we want # We will also make sure it contains what we want
if not run: return if not run: return
if not quiet:
ZopeTestCase._print('\nTest Apply Subscriber Value ')
LOG('Testing... ',0,'testApplySubscriberValue')
self.testGetConflictList(quiet=1,run=1) self.testGetConflictList(quiet=1,run=1)
portal_sync = self.getSynchronizationTool() portal_sync = self.getSynchronizationTool()
conflict_list = portal_sync.getConflictList() conflict_list = portal_sync.getConflictList()
if not quiet:
ZopeTestCase._print('\nTest Apply Subscriber Value ')
LOG('Testing... ',0,'testApplySubscriberValue')
conflict = conflict_list[0] conflict = conflict_list[0]
person_server = self.getPersonServer() person_server = self.getPersonServer()
person1_s = person_server._getOb(self.id1) person1_s = person_server._getOb(self.id1)
...@@ -480,30 +481,86 @@ class TestERP5SyncML(ERP5TypeTestCase): ...@@ -480,30 +481,86 @@ class TestERP5SyncML(ERP5TypeTestCase):
conflict_list = portal_sync.getConflictList() conflict_list = portal_sync.getConflictList()
self.failUnless(len(conflict_list)==0) self.failUnless(len(conflict_list)==0)
# def testPopulatePersonServerWithSubObject(self, quiet=0, run=1): def populatePersonServerWithSubObject(self, quiet=0, run=run_all_test):
# if not run: return """
# if not quiet: Before this method, we need to call populatePersonServer
# ZopeTestCase._print('\nTest Populate Person Server With Sub Object ') Then it will give the following tree :
# LOG('Testing... ',0,'testPopulatePersonServerWithSubObject') - person_server :
# self.testPopulatePersonServer(quiet=1,run=1) - id1
# person_server = self.getPersonServer() - id1
# person1 = person_server._getOb(self.id1) - id2
# sub_person1 = person1.newContent(id=self.id1,portal_type='Person') - id2
# person2 = person_server.newContent(id=self.id2,portal_type='Person') """
# kw = {'first_name':self.first_name1,'last_name':self.last_name1, if not run: return
# 'description':self.description1} if not quiet:
# person1.edit(**kw) ZopeTestCase._print('\nTest Populate Person Server With Sub Object ')
# kw = {'first_name':self.first_name2,'last_name':self.last_name2, LOG('Testing... ',0,'populatePersonServerWithSubObject')
# 'description':self.description2} person_server = self.getPersonServer()
# person2.edit(**kw) person1 = person_server._getOb(self.id1)
# nb_person = len(person_server.objectValues()) sub_person1 = person1.newContent(id=self.id1,portal_type='Person')
# self.failUnless(nb_person==2) kw = {'first_name':self.first_name1,'last_name':self.last_name1,
# return nb_person 'description':self.description1}
sub_person1.edit(**kw)
# XXX TODO : need to add a test with all kind of strange sign, like &, ...>< sub_sub_person = sub_person1.newContent(id=self.id2,portal_type='Person')
kw = {'first_name':self.first_name2,'last_name':self.last_name2,
'description':self.description2}
sub_sub_person.edit(**kw)
# remove ('','portal...','person_server')
len_path = len(sub_sub_person.getPhysicalPath()) - 3
self.failUnless(len_path==3)
def testAddSubObject(self, quiet=0, run=run_all_test):
"""
In this test, we synchronize, then add sub object on the
server and then see if the next synchronization will also
create sub-objects on the client
"""
if not run: return
self.testFirstSynchronization(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Add Sub Object ')
LOG('Testing... ',0,'testAddSubObject')
self.populatePersonServerWithSubObject(quiet=1,run=1)
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
sub_person1_c = person1_c._getOb(self.id1)
sub_sub_person = sub_person1_c._getOb(self.id2)
# remove ('','portal...','person_server')
len_path = len(sub_sub_person.getPhysicalPath()) - 3
self.failUnless(len_path==3)
self.failUnless(sub_sub_person.getDescription()==self.description2)
self.failUnless(sub_sub_person.getFirstName()==self.first_name2)
self.failUnless(sub_sub_person.getLastName()==self.last_name2)
def testUpdateSubObject(self, quiet=0, run=1):
"""
In this test, we start with a tree of object already
synchronized, then we update a subobject, and we will see
if it is updated correctly
"""
if not run: return
self.testAddSubObject(quiet=1,run=1)
if not quiet:
ZopeTestCase._print('\nTest Update Sub Object ')
LOG('Testing... ',0,'testUpdateSubObject')
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
sub_person1_c = person1_c._getOb(self.id1)
sub_sub_person_c = sub_person1_c._getOb(self.id2)
person_server = self.getPersonServer()
sub_sub_person_s = person_server._getOb(self.id1)._getOb(self.id1)._getOb(self.id2)
kw = {'first_name':self.first_name3}
sub_sub_person_c.edit(**kw)
kw = {'description':self.description3}
sub_sub_person_s.edit(**kw)
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.failUnless(sub_sub_person_c.getDescription()==self.description3)
self.failUnless(sub_sub_person_c.getFirstName()==self.first_name3)
self.failUnless(sub_sub_person_s.getDescription()==self.description3)
self.failUnless(sub_sub_person_s.getFirstName()==self.first_name3)
if __name__ == '__main__': if __name__ == '__main__':
framework() framework()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment