2009-05-15:

  Add default Namespace
  Add Conduit for Documents
2009-05-26:
  Move a getXMLFromObject to be overriden by specialized conduits
  Create PointFixe in the tests
2009-06-02:
  Split file Subscription.py to create Conflict.py and Signature.py
  Use activities in the test testSyncML
2009-06-17:
  Create a test for synchronize some documents
2009-07-10:
  Change the Final of the synchronisation for the synchronisation
  with datas from the Subscription


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@28229 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent f5f7bb21
......@@ -28,7 +28,7 @@
import smtplib
from Products.ERP5SyncML.SyncCode import SyncCode
from Products.ERP5SyncML.Subscription import Signature
from Products.ERP5SyncML.Signature import Signature
from AccessControl.SecurityManagement import newSecurityManager
from ERP5Diff import ERP5Diff
from zLOG import LOG, INFO
......@@ -55,8 +55,8 @@ class XMLSyncUtilsMixin(SyncCode):
way to set one quickly.
"""
xml = (E.SyncHdr(
E.VerDTD('1.1'),
E.VerProto('SyncML/1.1'),
E.VerDTD('1.2'),
E.VerProto('SyncML/1.2'),
E.SessionID('%s' % session_id),
E.MsgID('%s' % msg_id),
))
......@@ -108,13 +108,15 @@ class XMLSyncUtilsMixin(SyncCode):
return a status bloc with all status corresponding to the syncml
commands in remote_xml
"""
namespace = self.getNamespace(remote_xml.nsmap)
#list of element in the SyncBody bloc
sub_syncbody_element_list = remote_xml.xpath('/SyncML/SyncBody/*')
sub_syncbody_element_list = remote_xml.xpath('/syncml:SyncML/syncml:SyncBody/*')
message_id = self.getMessageIdFromXml(remote_xml)
status_list = []
target_uri = '%s' % remote_xml.xpath('string(/SyncML/SyncHdr/Target/LocURI)')
source_uri = '%s' % remote_xml.xpath('string(/SyncML/SyncHdr/Source/LocURI)')
target_uri = '%s' %\
remote_xml.xpath('string(/syncml:SyncML/syncml:SyncHdr/syncml:Target/syncml:LocURI)')
source_uri = '%s' %\
remote_xml.xpath('string(/syncml:SyncML/syncml:SyncHdr/syncml:Source/syncml:LocURI)')
if data_code != self.AUTH_REQUIRED:
xml = (E.Status(
E.CmdID('%s' % cmd_id),
......@@ -128,34 +130,36 @@ class XMLSyncUtilsMixin(SyncCode):
cmd_id += 1
status_list.append(xml)
for sub_syncbody_element in sub_syncbody_element_list:
if sub_syncbody_element.tag not in ('Status', 'Final', 'Get'):
if sub_syncbody_element.xpath('local-name()') not in ('Status', 'Final', 'Get'):
xml = (E.Status(
E.CmdID('%s' % cmd_id),
E.MsgRef('%s' % message_id),
E.CmdRef('%s' % sub_syncbody_element.xpath('string(.//CmdID)')),
E.Cmd(sub_syncbody_element.tag)
E.CmdRef('%s' %\
sub_syncbody_element.xpath('string(.//syncml:CmdID)')),
E.Cmd('%s' % sub_syncbody_element.xpath('name()'))
))
cmd_id += 1
target_ref = sub_syncbody_element.xpath('string(.//Target/LocURI)')
target_ref = sub_syncbody_element.xpath('string(.//syncml:Target/syncml:LocURI)')
if target_ref:
xml.append(E.TargetRef('%s' % target_ref))
source_ref = sub_syncbody_element.xpath('string(.//Source/LocURI)')
source_ref = sub_syncbody_element.xpath('string(.//syncml:Source/syncml:LocURI)')
if source_ref:
xml.append(E.SourceRef('%s' % source_ref))
if sub_syncbody_element.tag == 'Add':
if sub_syncbody_element.xpath('local-name()') == 'Add':
xml.append(E.Data('%s' % self.ITEM_ADDED))
elif sub_syncbody_element.tag == 'Alert' and \
sub_syncbody_element.xpath('string(.//Data)') == \
elif sub_syncbody_element.xpath('local-name()') == 'Alert' and \
sub_syncbody_element.xpath('string(.//syncml:Data)') == \
str(self.SLOW_SYNC):
xml.append(E.Data('%s' % self.REFRESH_REQUIRED))
elif sub_syncbody_element.tag == 'Alert':
elif sub_syncbody_element.xpath('local-name()') == 'Alert':
xml.append(E.Item(E.Data(E.Anchor(E.Next(next_anchor)))))
else:
xml.append(E.Data('%s' % self.SUCCESS))
status_list.append(xml)
if sub_syncbody_element.tag == 'Get' and subscription is not None:
cmd_ref = '%s' % sub_syncbody_element.xpath('string(.//CmdID)')
#FIXME to do a test for Get
if sub_syncbody_element.xpath('local-name()') == 'Get'\
and subscription is not None:
cmd_ref = '%s' % sub_syncbody_element.xpath('string(.//syncml:CmdID)')
syncml_result = self.SyncMLPut(
cmd_id,
subscription,
......@@ -176,10 +180,12 @@ class XMLSyncUtilsMixin(SyncCode):
synchronized
"""
if remote_xml is not None:
msg_ref = '%s' % remote_xml.xpath("string(/SyncML/SyncHdr/MsgID)")
cmd_ref = '%s' % remote_xml.xpath("string(.//CmdID)")
target_ref = '%s' % remote_xml.xpath("string(.//Target/LocURI)")
source_ref = '%s' % remote_xml.xpath("string(.//Source/LocURI)")
namespace = self.getNamespace(remote_xml.nsmap)
msg_ref = '%s' %\
remote_xml.xpath("string(/syncml:SyncML/syncml:SyncHdr/syncml:MsgID)")
cmd_ref = '%s' % remote_xml.xpath("string(.//syncml:CmdID)")
target_ref = '%s' % remote_xml.xpath("string(.//syncml:Target/syncml:LocURI)")
source_ref = '%s' % remote_xml.xpath("string(.//syncml:Source/syncml:LocURI)")
xml = Element('Status')
if cmd_id:
xml.append(E.CmdID('%s' % cmd_id))
......@@ -296,6 +302,19 @@ class XMLSyncUtilsMixin(SyncCode):
#server.sendmail(fromaddr, "seb@localhost", msg)
server.quit()
def getNamespace(self,nsmap):
"""
return the namespace with prefix of xml
"""
#search urn compatible in the namespaces of nsmap
urns = filter(lambda v: v.upper() in self.URN_LIST, nsmap.values())
if urns :
namespace = etree.FunctionNamespace(urns[0])
namespace.prefix='syncml'
return namespace
else :
raise ValueError, "Sorry, If have a namespace isn't supported"
def addXMLObject(self, cmd_id=0, object=None, xml_string=None,
more_data=0, gid=None, media_type=None):
"""
......@@ -400,63 +419,76 @@ class XMLSyncUtilsMixin(SyncCode):
"""
We will retrieve the session id of the message
"""
return int(xml.xpath('string(/SyncML/SyncHdr/SessionID)'))
namespace = self.getNamespace(xml.nsmap)
return int(xml.xpath('string(/syncml:SyncML/syncml:SyncHdr/syncml:SessionID)'))
def getMessageIdFromXml(self, xml):
"""
We will retrieve the message id of the message
"""
return int(xml.xpath('string(/SyncML/SyncHdr/MsgID)'))
namespace = self.getNamespace(xml.nsmap)
return int(xml.xpath('string(/syncml:SyncML/syncml:SyncHdr/syncml:MsgID)'))
def getTarget(self, xml):
"""
return the target in the SyncHdr section
"""
return '%s' % xml.xpath('string(/SyncML/SyncHdr/Target/LocURI)')
namespace = self.getNamespace(xml.nsmap)
return '%s' % xml.xpath('string(/syncml:SyncML/syncml:SyncHdr/syncml:Target/syncml:LocURI)')
def getAlertLastAnchor(self, xml_stream):
"""
Return the value of the last anchor, in the
alert section of the xml_stream
"""
return '%s' % xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Last)')
namespace = self.getNamespace(xml_stream.nsmap)
return '%s' % xml_stream.xpath('string(.//syncml:Alert/syncml:Item/syncml:Meta/syncml:Anchor/syncml:Last)')
def getAlertNextAnchor(self, xml_stream):
"""
Return the value of the next anchor, in the
alert section of the xml_stream
"""
return '%s' % xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Next)')
namespace = self.getNamespace(xml_stream.nsmap)
return '%s' %\
xml_stream.xpath('string(.//syncml:Alert/syncml:Item/syncml:Meta/syncml:Anchor/syncml:Next)')
def getSourceURI(self, xml):
"""
return the source uri of the data base
"""
return '%s' % xml.xpath('string(//SyncBody/Alert/Item/Source/LocURI)')
namespace = self.getNamespace(xml.nsmap)
return '%s' %\
xml.xpath('string(//syncml:SyncBody/syncml:Alert/syncml:Item/syncml:Source/syncml:LocURI)')
def getTargetURI(self, xml):
"""
return the target uri of the data base
"""
return '%s' % xml.xpath('string(//SyncBody/Alert/Item/Target/LocURI)')
namespace = self.getNamespace(xml.nsmap)
return '%s' %\
xml.xpath('string(//syncml:SyncBody/syncml:Alert/syncml:Item/syncml:Target/syncml:LocURI)')
def getSubscriptionUrlFromXML(self, xml):
"""
return the source URI of the syncml header
"""
return '%s' % xml.xpath('string(//SyncHdr/Source/LocURI)')
namespace = self.getNamespace(xml.nsmap)
return '%s' % xml.xpath('string(//syncml:SyncHdr/syncml:Source/syncml:LocURI)')
def getStatusTarget(self, xml):
"""
Return the value of the alert code inside the xml_stream
"""
return '%s' % xml.xpath('string(TargetRef)')
namespace = self.getNamespace(xml.nsmap)
return '%s' % xml.xpath('string(syncml:TargetRef)')
def getStatusCode(self, xml):
"""
Return the value of the alert code inside the xml_stream
"""
status_code = '%s' % xml.xpath('string(Data)')
namespace = self.getNamespace(xml.nsmap)
status_code = '%s' % xml.xpath('string(syncml:Data)')
if status_code:
return int(status_code)
return None
......@@ -465,18 +497,22 @@ class XMLSyncUtilsMixin(SyncCode):
"""
Return the value of the command inside the xml_stream
"""
namespace = self.getNamespace(xml.nsmap)
cmd = None
if xml.tag == 'Status':
cmd = '%s' % xml.xpath('string(Cmd)')
if xml.xpath('local-name()') == 'Status':
cmd = '%s' % xml.xpath('string(syncml:Cmd)')
return cmd
def getCred(self, xml):
"""
return the credential information : type, format and data
"""
format = '%s' % xml.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Format'])")
type = '%s' % xml.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Type'])")
data = '%s' % xml.xpath('string(/SyncML/SyncHdr/Cred/Data)')
namespace = self.getNamespace(xml.nsmap)
format = '%s' %\
xml.xpath("string(/syncml:SyncML/syncml:SyncHdr/syncml:Cred/syncml:Meta/*[local-name() = 'Format'])")
type = '%s' %\
xml.xpath("string(/syncml:SyncML/syncml:SyncHdr/syncml:Cred/syncml:Meta/*[local-name() = 'Type'])")
data = '%s' % xml.xpath('string(/syncml:SyncML/syncml:SyncHdr/syncml:Cred/syncml:Data)')
return (format, type, data)
......@@ -484,7 +520,8 @@ class XMLSyncUtilsMixin(SyncCode):
"""
Check if there's a Cred section in the xml_stream
"""
return bool(xml_stream.xpath('string(/SyncML/SyncHdr/Cred)'))
namespace = self.getNamespace(xml_stream.nsmap)
return bool(xml_stream.xpath('string(/syncml:SyncML/syncml:SyncHdr/syncml:Cred)'))
def getChal(self, xml):
"""
......@@ -498,31 +535,37 @@ class XMLSyncUtilsMixin(SyncCode):
"""
Check if there's a Chal section in the xml_stream
"""
return bool(xml_stream.xpath('string(/SyncML/SyncBody/Status/Chal)'))
namespace = self.getNamespace(xml_stream.nsmap)
return\
bool(xml_stream.xpath('string(/syncml:SyncML/syncml:SyncBody/syncml:Status/syncml:Chal)'))
def checkMap(self, xml_stream):
"""
Check if there's a Map section in the xml_stream
"""
return bool(xml_stream.xpath('string(/SyncML/SyncBody/Map)'))
namespace = self.getNamespace(xml_stream.nsmap)
return bool(xml_stream.xpath('string(/syncml:SyncML/syncml:SyncBody/syncml:Map)'))
def setRidWithMap(self, xml_stream, subscriber):
"""
get all the local objects of the given target id and set them the rid with
the given source id (in the Map section)
"""
item_list = xml_stream.xpath('/SyncML/SyncBody/Map/MapItem')
namespace = self.getNamespace(xml_stream.nsmap)
item_list = xml_stream.xpath('/syncml:SyncML/syncml:SyncBody/syncml:Map/syncml:MapItem')
for map_item in item_list:
gid = '%s' % map_item.xpath('string(.//Target/LocURI)')
gid = '%s' % map_item.xpath('string(.//syncml:Target/syncml:LocURI)')
signature = subscriber.getSignatureFromGid(gid)
rid = '%s' % map_item.xpath('string(.//Source/LocURI)')
rid = '%s' % map_item.xpath('string(.//syncml:Source/syncml:LocURI)')
signature.setRid(rid)
def getAlertCodeFromXML(self, xml_stream):
"""
Return the value of the alert code inside the full syncml message
"""
alert_code = '%s' % xml_stream.xpath('string(/SyncML/SyncBody/Alert/Data)')
namespace = self.getNamespace(xml_stream.nsmap)
alert_code = '%s' %\
xml_stream.xpath('string(/syncml:SyncML/syncml:SyncBody/syncml:Alert/syncml:Data)')
if alert_code:
return int(alert_code)
else:
......@@ -532,39 +575,52 @@ class XMLSyncUtilsMixin(SyncCode):
"""
Check if there's an Alert section in the xml_stream
"""
return bool(xml_stream.xpath('string(/SyncML/SyncBody/Alert)'))
namespace = self.getNamespace(xml_stream.nsmap)
return bool(xml_stream.xpath('string(/syncml:SyncML/syncml:SyncBody/syncml:Alert)'))
def checkSync(self, xml_stream):
"""
Check if there's an Sync section in the xml_xtream
Check if there's an Sync section in the xml_stream
"""
return bool(xml_stream.xpath('string(/SyncML/SyncBody/Sync)'))
namespace = self.getNamespace(xml_stream.nsmap)
return bool(xml_stream.xpath('string(/syncml:SyncML/syncml:SyncBody/syncml:Sync)'))
def checkFinal(self, xml_stream):
"""
Check if there's an Final section in the xml_stream
The end sections (inizialisation, modification) have this tag
"""
namespace = self.getNamespace(xml_stream.nsmap)
return bool(xml_stream.xpath('/syncml:SyncML/syncml:SyncBody/syncml:Final'))
def checkStatus(self, xml_stream):
"""
Check if there's a Status section in the xml_xtream
Check if there's a Status section in the xml_stream
"""
return bool(xml_stream.xpath('string(/SyncML/SyncBody/Status)'))
namespace = self.getNamespace(xml_stream.nsmap)
return bool(xml_stream.xpath('string(/syncml:SyncML/syncml:SyncBody/syncml:Status)'))
def getSyncActionList(self, xml_stream):
"""
return the list of the action (could be "add", "replace", "delete").
"""
return xml_stream.xpath('//Add|//Delete|//Replace')
namespace = self.getNamespace(xml_stream.nsmap)
return xml_stream.xpath('//syncml:Add|//syncml:Delete|//syncml:Replace')
def getSyncBodyStatusList(self, xml_stream):
"""
return the list of dictionary corredponding to the data of each status bloc
the data are : cmd, code and source
"""
namespace = self.getNamespace(xml_stream.nsmap)
status_list = []
status_node_list = xml_stream.xpath('//Status')
status_node_list = xml_stream.xpath('//syncml:Status')
for status in status_node_list:
tmp_dict = {}
tmp_dict['cmd'] = '%s' % status.xpath('string(./Cmd)')
tmp_dict['code'] = '%s' % status.xpath('string(./Data)')
tmp_dict['source'] = '%s' % status.xpath('string(./SourceRef)')
tmp_dict['target'] = '%s' % status.xpath('string(./TargetRef)')
tmp_dict['cmd'] = '%s' % status.xpath('string(./syncml:Cmd)')
tmp_dict['code'] = '%s' % status.xpath('string(./syncml:Data)')
tmp_dict['source'] = '%s' % status.xpath('string(./syncml:SourceRef)')
tmp_dict['target'] = '%s' % status.xpath('string(./syncml:TargetRef)')
status_list.append(tmp_dict)
return status_list
......@@ -572,13 +628,15 @@ class XMLSyncUtilsMixin(SyncCode):
"""
return the section data in text form, it's usefull for the VCardConduit
"""
return '%s' % action.xpath('string(.//Item/Data)')
namespace = self.getNamespace(action.nsmap)
return '%s' % action.xpath('string(.//syncml:Item/syncml:Data)')
def getDataSubNode(self, action):
"""
Return the node starting with <object....> of the action
"""
object_node_list = action.xpath('.//Item/Data/*[1]')
namespace = self.getNamespace(action.nsmap)
object_node_list = action.xpath('.//syncml:Item/syncml:Data/*[1]')
if object_node_list:
return object_node_list[0]
return None
......@@ -587,22 +645,25 @@ class XMLSyncUtilsMixin(SyncCode):
"""
Return the rid of the object described by the action
"""
id = '%s' % action.xpath('string(.//Item/Source/LocURI)')
namespace = self.getNamespace(action.nsmap)
id = '%s' % action.xpath('string(.//syncml:Item/syncml:Source/syncml:LocURI)')
if not id:
id = '%s' % action.xpath('string(.//Item/Target/LocURI)')
id = '%s' % action.xpath('string(.//syncml:Item/syncml:Target/syncml:LocURI)')
return id
def checkActionMoreData(self, action):
"""
Return the rid of the object described by the action
"""
return bool(action.xpath('.//Item/MoreData'))
namespace = self.getNamespace(action.nsmap)
return bool(action.xpath('.//syncml:Item/syncml:MoreData'))
def getActionType(self, action):
"""
Return the type of the object described by the action
"""
return '%s' % action.xpath('string(.//Meta/Type)')
namespace = self.getNamespace(action.nsmap)
return '%s' % action.xpath('string(.//syncml:Meta/syncml:Type)')
def cutXML(self, xml_string):
"""
......@@ -636,7 +697,7 @@ class XMLSyncUtilsMixin(SyncCode):
if isinstance(remote_xml, (str, unicode)):
remote_xml = etree.XML(remote_xml, parser=parser)
if domain.isOneWayFromServer():
#Do not set object_path_list, subscriber send nothing
#Do not set object_path_list, subscriber send nothing it's a client
subscriber.setRemainingObjectPathList([])
elif subscriber.getRemainingObjectPathList() is None:
object_list = domain.getObjectList()
......@@ -659,7 +720,6 @@ class XMLSyncUtilsMixin(SyncCode):
#LOG('getSyncMLData :', DEBUG,'gid_not_encoded_list:%s, local_gid_list:%s, gid:%s' % (gid_not_encoded_list, local_gid_list, gid))
else:
local_gid_list = [domain.getGidFromObject(x) for x in object_list]
# Objects to remove
#LOG('getSyncMLData remove object to remove ...', DEBUG, '')
for object_gid in subscriber.getGidList():
......@@ -673,7 +733,7 @@ class XMLSyncUtilsMixin(SyncCode):
rid=rid,
cmd_id=cmd_id))
cmd_id += 1
#delete Signature if object does not exist anymore
# Delete Signature if object does not exist anymore
subscriber.delSignature(object_gid)
local_gid_list = []
......@@ -703,18 +763,18 @@ class XMLSyncUtilsMixin(SyncCode):
signature = subscriber.getSignatureFromGid(object_gid)
## Here we first check if the object was modified or not by looking at dates
#if signature is not None:
#LOG('getSyncMLData', DEBUG, 'signature.getStatus: %s' % signature.getStatus())
status = self.SENT
more_data = 0
# For the case it was never synchronized, we have to send everything
if signature is not None and signature.getXMLMapping() is None:
pass
elif signature is None or (signature.getXML() is None and \
signature.getStatus() != self.PARTIAL) or \
elif signature is None or\
(signature.getXML() is None and\
signature.getStatus() != self.PARTIAL) or\
self.getAlertCodeFromXML(remote_xml) == self.SLOW_SYNC:
#LOG('getSyncMLData', DEBUG, 'Current object.getPath: %s' % object.getPath())
xml_string = domain.getXMLFromObject(object)
xml_string = conduit.getXMLFromObjectWithId(object,\
xml_mapping=domain.getXMLMapping())
gid = subscriber.getGidFromObject(object)
signature = Signature(id=gid, object=object).__of__(subscriber)
signature.setTempXML(xml_string)
......@@ -724,7 +784,7 @@ class XMLSyncUtilsMixin(SyncCode):
signature.setPartialXML(rest_string)
status = self.PARTIAL
signature.setAction('Add')
#in fisrt, we try with rid if there is one
#in first, we try with rid if there is one
gid = signature.getRid() or signature.getGid()
syncml_data_list.append(self.addXMLObject(
cmd_id=cmd_id,
......@@ -738,8 +798,9 @@ class XMLSyncUtilsMixin(SyncCode):
subscriber.addSignature(signature)
elif signature.getStatus() in (self.NOT_SYNCHRONIZED,
self.PUB_CONFLICT_MERGE,):
# We don't have synchronized this object yet
xml_object = domain.getXMLFromObject(object)
# We don't have synchronized this object yet but it has a signature
xml_object = conduit.getXMLFromObjectWithId(object,\
xml_mapping=domain.getXMLMapping())
#LOG('getSyncMLData', DEBUG, 'checkMD5: %s' % str(signature.checkMD5(xml_object)))
#LOG('getSyncMLData', DEBUG, 'getStatus: %s' % str(signature.getStatus()))
if signature.getStatus() == self.PUB_CONFLICT_MERGE:
......@@ -751,22 +812,21 @@ class XMLSyncUtilsMixin(SyncCode):
set_synchronized = 1
if not signature.checkMD5(xml_object):
set_synchronized = 0
# This object has changed on this side, we have to generate some xmldiff
if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_XML']:
xml_string = self.getXupdateObject(xml_object, signature.getXML())
else: #if there is no xml, we re-send all the object
xml_string = xml_object
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
# If there is no xml, we re-send all the objects
xml_string = xml_object
elif xml_string.count('\n') > self.MAX_LINES:
# This make comment fails, so we need to replace
xml_string, rest_string = self.cutXML(xml_string)
more_data = 1
signature.setPartialXML(rest_string)
status = self.PARTIAL
signature.setAction('Replace')
signature.setStatus(status)
rid = signature.getRid()#in fisrt, we try with rid if there is one
else:
# This object has changed on this side, we have to generate some xmldiff
xml_string = self.getXupdateObject(xml_object, signature.getXML())
if xml_string.count('\n') > self.MAX_LINES:
# This make comment fails, so we need to replace
xml_string, rest_string = self.cutXML(xml_string)
more_data = 1
signature.setPartialXML(rest_string)
status = self.PARTIAL
signature.setAction('Replace')
signature.setStatus(status)
rid = signature.getRid()#in first, we try with rid if there is
gid = signature.getGid()
syncml_data_list.append(self.replaceXMLObject(
cmd_id=cmd_id, object=object,
......@@ -780,6 +840,8 @@ class XMLSyncUtilsMixin(SyncCode):
subscriber_xupdate = signature.getSubscriberXupdate()
#LOG('getSyncMLData subscriber_xupdate', DEBUG, subscriber_xupdate)
if subscriber_xupdate is not None:
# The modification in the xml from signature is compare and update
# with xml_xupdate from subscriber
old_xml = signature.getXML()
conduit.updateNode(
xml=subscriber_xupdate,
......@@ -787,7 +849,8 @@ class XMLSyncUtilsMixin(SyncCode):
previous_xml=old_xml,
force=(domain.getDomainType() == self.SUB),
simulate=0)
xml_object = domain.getXMLFromObject(object)
xml_object = conduit.getXMLFromObjectWithId(object,\
xml_mapping=domain.getXMLMapping())
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
......@@ -814,7 +877,8 @@ class XMLSyncUtilsMixin(SyncCode):
xml_to_send = Element('Partial')
xml_to_send.text = etree.CDATA(xml_string.decode('utf-8'))
if(subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']):
xml_to_send = domain.getXMLFromObject(object)
xml_to_send = conduit.getXMLFromObjectWithId(object,\
xml_mapping=domain.getXMLMapping())
elif xml_string.count('\n') > self.MAX_LINES:
xml_to_send, rest_string = self.cutXML(xml_string)
more_data = 1
......@@ -859,59 +923,52 @@ class XMLSyncUtilsMixin(SyncCode):
This just look to a list of action to do, then id applies
each action one by one, thanks to a conduit
"""
namespace = self.getNamespace(remote_xml.nsmap)
xml_confirmation_list = []
has_next_action = 0
gid_from_xml_list = []
destination = self.unrestrictedTraverse(domain.getDestinationPath())
#LOG('applyActionList args', DEBUG, 'domain : %s\n subscriber : %s\n cmd_id : %s' % (domain.getPath(), subscriber.getPath(), cmd_id))
#LOG('applyActionList', DEBUG, self.getSyncActionList(remote_xml))
#LOG('applyActionList args', DEBUG, 'domain : %s\n subscriber : %s\n cmd_id: %s'\
#% (domain.getPath(), subscriber.getPath(), cmd_id))
#LOG('XMLSyncUtils applyActionList', DEBUG, self.getSyncActionList(remote_xml))
for action in self.getSyncActionList(remote_xml):
conflict_list = []
status_code = self.SUCCESS
# Thirst we have to check the kind of action it is
partial_data = '%s' % action.xpath('string(.//Item/Data/Partial)')
rid = self.getActionId(action)
if action.tag != 'Delete':
# The rid is the Temporary GUID (SYNCML Protocol). the rid sent by the
# client unlike gid. The rid is in MapItem for each Action Map it's the LocURI in
# the action.
gid = rid = self.getActionId(action)
#The action delete hasn't need a gid and retrieve the gid of conduit for
#object.
if action.xpath('local-name()') != 'Delete':
data_action = self.getDataSubNode(action)
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
#data in unicode
data_action = self.getDataText(action)
if getattr(conduit, 'getGidFromXML', None) is not None and \
conduit.getGidFromXML(self.getDataText(action), gid_from_xml_list):
gid = conduit.getGidFromXML(self.getDataText(action),
gid_from_xml_list)
conduit.getGidFromXML(data_action, namespace, gid_from_xml_list):
gid = conduit.getGidFromXML(data_action, namespace, gid_from_xml_list)
gid_from_xml_list.append(gid)
gid = b16encode(gid)
else:
gid = rid
else:
gid = rid
object_id = domain.generateNewIdWithGenerator(object=destination, gid=gid)
#the rid unlike gid, it's the rid or gid (if rid == gid) will use for
#retrieve object and send response to client
signature = subscriber.getSignatureFromGid(gid)
if signature is not None and rid != gid:
#in this case, the object was created on another subscriber than erp5
# and we should save it's remote id
signature.setRid(rid)
#LOG('gid == rid ?', DEBUG, 'gid=%s, rid=%s' % (gid, rid))
object = subscriber.getObjectFromGid(gid)
if object is None and not domain.getSynchronizeWithERP5Sites():
#if the object is None, that could mean two things :
# - the object to synchronize don't exists
# - the id is not a gid but a rid
#here we try to find an object with the rid
#LOG('applyActionList, try to find an object with rid', DEBUG, '')
object = subscriber.getObjectFromRid(rid)
signature = subscriber.getSignatureFromRid(rid)
if signature is not None:
gid = signature.getId()
#LOG('applyActionList subscriber.getObjectFromGid %s' % gid, DEBUG, object)
if signature is None:
#LOG('applyActionList, signature is None', DEBUG, signature)
if gid == rid:
signature = Signature(id=gid, status=self.NOT_SYNCHRONIZED,
object=object).__of__(subscriber)
else:
signature = Signature(rid=rid, id=gid, status=self.NOT_SYNCHRONIZED,
object_id = domain.generateNewIdWithGenerator(object=destination, gid=gid)
if rid == gid:
#We can't use our own gid and the temporary GUID is useless
rid = None
if signature is not None:
signature.setRid(rid)
else:
signature = Signature(id=gid, rid=rid, status=self.NOT_SYNCHRONIZED,
object=object).__of__(subscriber)
signature.setObjectId(object_id)
subscriber.addSignature(signature)
force = signature.getForce()
partial_data = '%s' % action.xpath('string(.//syncml:Item/syncml:Data/syncml:Partial)')
if not self.checkActionMoreData(action):
data_subnode = None
if partial_data:
......@@ -928,7 +985,7 @@ class XMLSyncUtilsMixin(SyncCode):
data_subnode = self.getDataText(action)
else:
data_subnode = self.getDataSubNode(action)
if action.tag == 'Add':
if action.xpath('local-name()') == 'Add':
# Then store the xml of this new subobject
reset = 0
if object is None:
......@@ -948,7 +1005,9 @@ class XMLSyncUtilsMixin(SyncCode):
if data_subnode is not None:
if not isinstance(data_subnode, str):
xml_string = etree.tostring(data_subnode, encoding='utf-8')
actual_xml = subscriber.getXMLFromObject(object=object, force=1)
#force = 1
actual_xml = conduit.getXMLFromObjectWithId(object,\
xml_mapping=domain.getXMLMapping(force=1))
data_subnode = self.getXupdateObject(xml_string, actual_xml)
conflict_list.extend(conduit.updateNode(
xml=data_subnode,
......@@ -956,7 +1015,8 @@ class XMLSyncUtilsMixin(SyncCode):
previous_xml=signature.getXML(),
force=force,
simulate=simulate))
xml_object = domain.getXMLFromObject(object)
xml_object = conduit.getXMLFromObjectWithId(object,\
xml_mapping=domain.getXMLMapping())
signature.setTempXML(xml_object)
if object is not None:
#LOG('applyActionList', DEBUG, 'addNode, found the object')
......@@ -968,8 +1028,9 @@ class XMLSyncUtilsMixin(SyncCode):
pretty_print=True)
else:
xml_object = data_subnode
else:
xml_object = domain.getXMLFromObject(object)
else:
xml_object = conduit.getXMLFromObjectWithId(object,\
xml_mapping=domain.getXMLMapping())
signature.setStatus(self.SYNCHRONIZED)
#signature.setId(object.getId())
signature.setPath(object.getPhysicalPath())
......@@ -980,13 +1041,11 @@ class XMLSyncUtilsMixin(SyncCode):
sync_code=self.ITEM_ADDED,
remote_xml=action))
cmd_id +=1
elif action.tag == 'Replace':
elif action.xpath('local-name()') == 'Replace':
#LOG('applyActionList', DEBUG, 'object: %s will be updated...' % str(object))
if object is not None:
#LOG('applyActionList', DEBUG, 'object: %s will be updated...' % object.id)
signature = subscriber.getSignatureFromGid(gid)
if signature is None:
signature = subscriber.getSignatureFromRid(gid)
#LOG('applyActionList', DEBUG, 'previous signature: %s' % str(signature))
previous_xml = signature.getXML()
conflict_list += conduit.updateNode(
......@@ -995,7 +1054,8 @@ class XMLSyncUtilsMixin(SyncCode):
previous_xml=signature.getXML(),
force=force,
simulate=simulate)
xml_object = domain.getXMLFromObject(object)
xml_object = conduit.getXMLFromObjectWithId(object,\
xml_mapping=domain.getXMLMapping())
signature.setTempXML(xml_object)
if conflict_list:
status_code = self.CONFLICT
......@@ -1019,7 +1079,8 @@ class XMLSyncUtilsMixin(SyncCode):
#LOG('applyActionList, subscriber_xupdate:', TRACE, data_subnode_string)
signature.setSubscriberXupdate(data_subnode_string)
elif action.tag == 'Delete':
elif action.xpath('local-name()') == 'Delete':
LOG("applyactionlist delete",INFO,"")
object_id = signature.getId()
#LOG('applyActionList Delete on : ', DEBUG, (signature.getId(), subscriber.getObjectFromGid(object_id)))
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
......@@ -1049,7 +1110,7 @@ class XMLSyncUtilsMixin(SyncCode):
#LOG('applyActionList', DEBUG, 'waiting more data for :%s' % signature.getId())
xml_confirmation_list.append(self.SyncMLConfirmation(
cmd_id=cmd_id,
cmd=action.tag,
cmd= "%s" % action.xpath('name()'),
sync_code=self.WAITING_DATA,
remote_xml=action))
if conflict_list and signature is not None:
......@@ -1074,11 +1135,13 @@ class XMLSyncUtilsMixin(SyncCode):
if not object_gid:
object_gid = status['target']
status_code = int(status['code'])
signature = subscriber.getSignatureFromGid(object_gid)
if signature is None and\
not(subscriber.getSynchronizeWithERP5Sites()):
#the client give his id but not the gid
signature = subscriber.getSignatureFromRid(object_gid)
if status_cmd in ('Add', 'Replace',):
has_status_list = 1
signature = subscriber.getSignatureFromGid(object_gid)
if signature is None:
signature = subscriber.getSignatureFromRid(object_gid)
if status_code == self.CHUNK_OK:
destination_waiting_more_data = 1
signature.setStatus(self.PARTIAL)
......@@ -1097,15 +1160,10 @@ class XMLSyncUtilsMixin(SyncCode):
elif status_cmd == 'Delete':
has_status_list = 1
if status_code == self.SUCCESS:
signature = subscriber.getSignatureFromGid(object_gid)
if signature is None and \
not(subscriber.getSynchronizeWithERP5Sites()):
signature = subscriber.getSignatureFromRid(object_gid)
if signature is not None:
subscriber.delSignature(signature.getGid())
return (destination_waiting_more_data, has_status_list)
class XMLSyncUtils(XMLSyncUtilsMixin):
def Sync(self, id, msg=None, RESPONSE=None):
......@@ -1152,7 +1210,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
#LOG('SyncModif', DEBUG, 'Starting... domain: %s' % domain.getId())
# Get informations from the header
xml_header = remote_xml[0]
if xml_header.tag != "SyncHdr":
#FIXME to apply a DTD or schema
if xml_header.xpath('local-name()') != "SyncHdr":
LOG('SyncModif', INFO, 'This is not a SyncML Header')
raise ValueError, "Sorry, This is not a SyncML Header"
......@@ -1210,7 +1269,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
subscriber=subscriber,
remote_xml=remote_xml,
conduit=conduit, simulate=simulate)
xml = Element('SyncML')
nsmap = {None : self.XHTML_NAMESPACE}
xml = Element('SyncML', nsmap=nsmap)
# syncml header
if domain.domain_type == self.PUB:
......@@ -1358,7 +1419,10 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
def sendSyncModif(self, syncml_data_list, cmd_id_before_getsyncmldata,
subscriber, domain, xml_confirmation_list, remote_xml,
xml_tree, has_status_list, has_response):
# XXX the better is a namespace for all
sync_body = xml_tree.find('SyncBody')
if sync_body is None:
sync_body = xml_tree.xpath('syncml:SyncBody')[0]
if syncml_data_list:
sync_node = SubElement(sync_body, 'Sync')
cmd_id_node = SubElement(sync_node, 'CmdID')
......@@ -1372,14 +1436,19 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
for syncml_data in syncml_data_list:
sync_node.append(etree.XML(syncml_data, parser=parser))
for xml_confirmation in xml_confirmation_list:
if not isinstance(xml_confirmation, str):
sync_body.append(xml_confirmation)
else:
sync_body.append(etree.XML(xml_confirmation, parser=parser))
sync_body.append(Element('Final'))
xml_string = etree.tostring(xml_tree, encoding='utf-8', pretty_print=True)
if isinstance(xml_confirmation, str):
xml_confirmation = etree.XML(xml_confirmation, parser=parser)
sync_body.append(xml_confirmation)
self.sync_finished = 0
if domain.domain_type == self.PUB: # We always reply
# When the publication recieved the response Final and the modification
# data is finished so the publication send the tag "Final"
if not self.checkSync(remote_xml) and not xml_confirmation_list\
and not syncml_data_list and self.checkFinal(remote_xml):
sync_body.append(Element('Final'))
self.sync_finished = 1
xml_string = etree.tostring(xml_tree, encoding='utf-8', pretty_print=True)
subscriber.setLastSentMessage(xml_string)
self.sendResponse(
from_url=domain.publication_url,
......@@ -1388,13 +1457,20 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
xml=xml_string,
domain=domain,
content_type=domain.getSyncContentType())
if not syncml_data_list:
if self.sync_finished == 1:
LOG('this is the end of the synchronisation session from PUB !!!', INFO, domain.getId())
subscriber.setAuthenticated(False)
domain.setAuthenticated(False)
has_response = 1
elif domain.domain_type == self.SUB:
if self.checkAlert(remote_xml) or xml_confirmation_list or syncml_data_list:
# the modification data is finished on the subscription so the tag
# "Final" sent to the publication
if not self.checkAlert(remote_xml) and not xml_confirmation_list\
and not syncml_data_list:
sync_body.append(Element('Final'))
self.sync_finished = 1
xml_string = etree.tostring(xml_tree, encoding='utf-8', pretty_print=True)
if not self.sync_finished or not self.checkFinal(remote_xml):
subscriber.setLastSentMessage(xml_string)
self.sendResponse(
from_url=domain.subscription_url,
......@@ -1403,6 +1479,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
xml=xml_string, domain=domain,
content_type=domain.getSyncContentType())
has_response = 1
#When the receive the final element and the sub finished synchronization
else:
if domain.isOneWayFromServer():
self.deleteRemainObjectList(domain, subscriber)
......@@ -1453,17 +1530,22 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
xml_client = self.readResponse(from_url=publication.getPublicationUrl())
#LOG('PubSync', DEBUG, 'Starting... msg: %s' % str(xml_client))
result = None
if xml_client is not None:
if isinstance(xml_client, (str, unicode)):
xml_client = etree.XML(xml_client, parser=parser)
if xml_client.tag != "SyncML":
if len(xml_client.nsmap) ==1:
namespace = xml_client.nsmap[None]
else :
namespace = xml_client.nsmap['syncml']
#FIXME to apply a DTD or schema
if xml_client.xpath('local-name()') != "SyncML":
LOG('PubSync', INFO, 'This is not a SyncML Message')
raise ValueError, "Sorry, This is not a SyncML Message"
alert_code = self.getAlertCodeFromXML(xml_client)
# Get informations from the header
client_header = xml_client[0]
if client_header.tag != "SyncHdr":
#FIXME to apply a DTD or schema
if client_header.xpath('local-name()') != "SyncHdr":
LOG('PubSync', INFO, 'This is not a SyncML Header')
raise ValueError, "Sorry, This is not a SyncML Header"
subscription_url = self.getSubscriptionUrlFromXML(client_header)
......@@ -1560,7 +1642,3 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
else:
return response
def getActivityType(self, domain):
if domain.getActivityEnabled():
return 'SQLQueue'
return 'RAMQueue'
......@@ -35,6 +35,9 @@ from Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
from Products.ERP5SyncML.SyncCode import SyncCode
from zLOG import LOG
from base64 import b64encode, b64decode, b16encode, b16decode
import transaction
from ERP5Diff import ERP5Diff
from lxml import etree
class TestERP5SyncMLMixin:
......@@ -72,10 +75,12 @@ class TestERP5SyncMLMixin:
nb_subscription = 2
nb_publication = 1
nb_synchronization = 3
nb_message_first_synchronization = 8
nb_message_first_synchronization = 10
nb_message_first_sync_max_lines = 10
subscription_url1 = 'file://tmp/sync_client1'
subscription_url2 = 'file://tmp/sync_client2'
publication_url = 'file://tmp/sync_server'
activity_enabled = False
#publication_url = 'server@localhost'
#subscription_url1 = 'client1@localhost'
#subscription_url2 = 'client2@localhost'
......@@ -126,12 +131,12 @@ class TestERP5SyncMLMixin:
uf._doAddUser('syncml', '', ['Manager'], [])
user = uf.getUserById('fab').__of__(uf)
newSecurityManager(None, user)
def populatePersonServer(self, quiet=0, run=0):
def initPersonModule(self, quiet=0, run=0):
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Populate Person Server ')
LOG('Testing... ',0,'populatePersonServer')
ZopeTestCase._print('\nTest Init Person Module')
LOG('Testing... ',0,'initPersonModule')
self.login()
portal = self.getPortal()
if not hasattr(portal,'person_server'):
......@@ -146,13 +151,21 @@ class TestERP5SyncMLMixin:
portal.portal_types.constructContent(type_name = 'Person Module',
container = portal,
id = 'person_client2')
person_id = ''
def populatePersonServer(self, quiet=0, run=0):
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Populate Person Server ')
LOG('Testing... ',0,'populatePersonServer')
self.login()
portal = self.getPortal()
self.initPersonModule(quiet=1, run=1)
person_server = self.getPersonServer()
person1 = person_server.newContent(id=self.id1,portal_type='Person')
person1 = person_server.newContent(id=self.id1, portal_type='Person')
kw = {'first_name':self.first_name1,'last_name':self.last_name1,
'description':self.description1}
person1.edit(**kw)
person2 = person_server.newContent(id=self.id2,portal_type='Person')
person2 = person_server.newContent(id=self.id2, portal_type='Person')
kw = {'first_name':self.first_name2,'last_name':self.last_name2,
'description':self.description2}
person2.edit(**kw)
......@@ -160,6 +173,24 @@ class TestERP5SyncMLMixin:
self.assertEqual(nb_person, 2)
return nb_person
def populatePersonClient1(self, quiet=0, run=0):
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Populate Person Client 1 ')
LOG('Testing... ',0,'populatePersonClient1')
self.login()
portal = self.getPortal()
self.initPersonModule(quiet=1, run=1)
person_client = self.getPersonClient1()
for id in range(1, 60):
person = person_client.newContent(id=id, portal_type='Person')
kw = {'first_name':self.first_name1,'last_name':self.last_name1,
'description':self.description1}
person.edit(**kw)
nb_person = len(person_client.objectValues())
self.assertEqual(nb_person,59)
return nb_person
def synchronize(self, id, run=1):
"""
This just define how we synchronize, we have
......@@ -168,25 +199,31 @@ class TestERP5SyncMLMixin:
portal_sync = self.getSynchronizationTool()
subscription = portal_sync.getSubscription(id)
publication = None
for publication in portal_sync.getPublicationList():
if publication.getPublicationUrl()==subscription.getSubscriptionUrl():
publication = publication
self.failUnless(publication is not None)
for pub in portal_sync.getPublicationList():
if pub.getPublicationUrl()==subscription.getPublicationUrl():
publication = pub
self.assertTrue(publication is not None)
# reset files, because we do sync by files
file = open('/tmp/sync_client1','w')
file = open('/tmp/sync_client1', 'w')
file.write('')
file.close()
file = open('/tmp/sync_client2','w')
file = open('/tmp/sync_client2', 'w')
file.write('')
file.close()
file = open('/tmp/sync','w')
file = open('/tmp/sync_server', 'w')
file.write('')
file.close()
nb_message = 1
result = portal_sync.SubSync(subscription.getPath())
while result['has_response']==1:
portal_sync.PubSync(publication.getPath())
if self.activity_enabled:
transaction.commit()
self.tic()
result = portal_sync.SubSync(subscription.getPath())
if self.activity_enabled:
transaction.commit()
self.tic()
nb_message += 1 + result['has_response']
return nb_message
......@@ -199,18 +236,18 @@ class TestERP5SyncMLMixin:
#portal_sync.email = None # XXX To be removed
subscription = portal_sync.getSubscription(id)
publication = None
for publication in portal_sync.getPublicationList():
if publication.getPublicationUrl()==subscription.getSubscriptionUrl():
publication = publication
self.failUnless(publication is not None)
for pub in portal_sync.getPublicationList():
if pub.getPublicationUrl()==subscription.getPublicationUrl():
publication = pub
self.assertTrue(publication is not None)
# reset files, because we do sync by files
file = open('/tmp/sync_client1','w')
file = open('/tmp/sync_client1', 'w')
file.write('')
file.close()
file = open('/tmp/sync_client2','w')
file = open('/tmp/sync_client2', 'w')
file.write('')
file.close()
file = open('/tmp/sync','w')
file = open('/tmp/sync_server', 'w')
file.write('')
file.close()
nb_message = 1
......@@ -219,11 +256,29 @@ class TestERP5SyncMLMixin:
# We do thing three times, so that we will test
# if we manage well duplicate messages
portal_sync.PubSync(publication.getPath())
if self.activity_enabled:
transaction.commit()
self.tic()
portal_sync.PubSync(publication.getPath())
if self.activity_enabled:
transaction.commit()
self.tic()
portal_sync.PubSync(publication.getPath())
if self.activity_enabled:
transaction.commit()
self.tic()
result = portal_sync.SubSync(subscription.getPath())
if self.activity_enabled:
transaction.commit()
self.tic()
result = portal_sync.SubSync(subscription.getPath())
if self.activity_enabled:
transaction.commit()
self.tic()
result = portal_sync.SubSync(subscription.getPath())
if self.activity_enabled:
transaction.commit()
self.tic()
nb_message += 1 + result['has_response']
return nb_message
......@@ -254,16 +309,6 @@ class TestERP5SyncMLMixin:
for m in sub.getSignatureList():
self.assertEquals(m.getPartialXML(),None)
def verifyFirstNameAndLastNameAreSynchronized(self, first_name,
last_name, person_server, person_client):
"""
verify if the first and last name are synchronized
"""
self.assertEqual(person_server.getFirstName(), first_name)
self.assertEqual(person_server.getLastName(), last_name)
self.assertEqual(person_client.getFirstName(), first_name)
self.assertEqual(person_client.getLastName(), last_name)
def verifyFirstNameAndLastNameAreNotSynchronized(self, first_name,
last_name, person_server, person_client):
"""
......@@ -297,7 +342,54 @@ class TestERP5SyncMLMixin:
self.assertEqual(person2_c.getId(), id)
self.assertEqual(person2_c.getFirstName(), self.first_name1)
self.assertEqual(person2_c.getLastName(), self.last_name1)
def resetSignaturePublicationAndSubscription(self):
portal_sync = self.getSynchronizationTool()
publication = portal_sync.getPublication(self.pub_id)
subscription1 = portal_sync.getSubscription(self.sub_id1)
subscription2 = portal_sync.getSubscription(self.sub_id2)
publication.resetAllSubscribers()
subscription1.resetAllSignatures()
transaction.commit()
self.tic()
def assertXMLViewIsEqual(self, sub_id, object_pub=None, object_sub=None,\
force=0):
"""
Check the equality between two xml objects with gid as id
"""
portal_sync = self.getSynchronizationTool()
subscription = portal_sync.getSubscription(sub_id)
publication = portal_sync.getPublication(self.pub_id)
gid_pub = publication.getGidFromObject(object_pub)
gid_sub = publication.getGidFromObject(object_sub)
self.assertEqual(gid_pub, gid_sub)
conduit = ERP5Conduit()
xml_pub = conduit.getXMLFromObjectWithGid(object=object_pub, gid=gid_pub,\
xml_mapping=publication.getXMLMapping())
#if One Way From Server there is not xml_mapping for subscription
xml_sub = conduit.getXMLFromObjectWithGid(object=object_sub, gid=gid_sub,\
xml_mapping=subscription.getXMLMapping(force))
erp5diff = ERP5Diff()
erp5diff.compare(xml_pub, xml_sub)
result = erp5diff.outputString()
result = etree.XML(result)
if len(result) != 0 :
for update in result:
#XXX edit workflow is not replaced, so discard workflow checking
if update.get('select').find('time') != -1 or\
update.get('select').find('serial') !=1:
continue
else :
self.fail('diff between pub:\n%s \nand sub:\n%s \n => \n%s' %\
(xml_pub, xml_sub, etree.tostring(result, pretty_print=True)))
def deletePublicationAndSubscription(self):
portal_sync = self.getSynchronizationTool()
portal_sync.manage_deletePublication(title=self.pub_id)
portal_sync.manage_deleteSubscription(title=self.sub_id1)
if portal_sync.getSubscription(title=self.sub_id2):
portal_sync.manage_deleteSubscription(title=self.sub_id2)
class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
......@@ -307,6 +399,123 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
"""
return "ERP5 SyncML"
def setupPublicationAndSubscription(self, quiet=0, run=run_all_test):
portal_sync = self.getSynchronizationTool()
person_server = self.getPersonServer()
if person_server is not None:
portal = self.getPortal()
portal._delObject(id='person_server')
portal._delObject(id='person_client1')
portal._delObject(id='person_client2')
self.deletePublicationAndSubscription()
self.test_02_AddPublication(quiet=1,run=1)
self.test_03_AddSubscription1(quiet=1,run=1)
self.test_04_AddSubscription2(quiet=1,run=1)
def setupPublicationAndSubscriptionAndGid(self, quiet=0, run=run_all_test):
self.setupPublicationAndSubscription(quiet=1,run=1)
portal_sync = self.getSynchronizationTool()
sub1 = portal_sync.getSubscription(self.sub_id1)
sub2 = portal_sync.getSubscription(self.sub_id2)
pub = portal_sync.getPublication(self.pub_id)
sub1.setConduit('ERP5ConduitTitleGid')
sub2.setConduit('ERP5ConduitTitleGid')
pub.setConduit('ERP5ConduitTitleGid')
pub.setSynchronizationIdGenerator('_generateNextId')
sub1.setSynchronizationIdGenerator('_generateNextId')
sub2.setSynchronizationIdGenerator('_generateNextId')
def checkSynchronizationStateIsConflict(self, quiet=0, run=1):
portal_sync = self.getSynchronizationTool()
person_server = self.getPersonServer()
for person in person_server.objectValues():
if person.getId()==self.id1:
state_list = portal_sync.getSynchronizationState(person)
for state in state_list:
self.assertEqual(state[1], state[0].CONFLICT)
person_client1 = self.getPersonClient1()
for person in person_client1.objectValues():
if person.getId()==self.id1:
state_list = portal_sync.getSynchronizationState(person)
for state in state_list:
self.assertEqual(state[1], state[0].CONFLICT)
person_client2 = self.getPersonClient2()
for person in person_client2.objectValues():
if person.getId()==self.id1:
state_list = portal_sync.getSynchronizationState(person)
for state in state_list:
self.assertEqual(state[1], state[0].CONFLICT)
# make sure sub object are also in a conflict mode
person = person_client1._getOb(self.id1)
# use a temp_object to create a no persistent object in person
sub_person =\
person.newContent(id=self.id1, portal_type='Person', temp_object=1)
state_list = portal_sync.getSynchronizationState(sub_person)
for state in state_list:
self.assertEqual(state[1], state[0].CONFLICT)
def populatePersonServerWithSubObject(self, quiet=0, run=run_all_test):
"""
Before this method, we need to call populatePersonServer
Then it will give the following tree :
- person_server :
- id1
- id1
- id2
- id1
- id2
"""
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Populate Person Server With Sub Object ')
LOG('Testing... ',0,'populatePersonServerWithSubObject')
person_server = self.getPersonServer()
person1 = person_server._getOb(self.id1)
sub_person1 = person1.newContent(id=self.id1, portal_type='Person')
kw = {'first_name':self.first_name1,'last_name':self.last_name1,
'description':self.description1}
sub_person1.edit(**kw)
sub_sub_person1 = sub_person1.newContent(id=self.id1, portal_type='Person')
kw = {'first_name':self.first_name1,'last_name':self.last_name1,
'description':self.description1, 'default_telephone_text':'0689778308'}
sub_sub_person1.edit(**kw)
sub_sub_person2 = 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_person2.edit(**kw)
# remove ('','portal...','person_server')
len_path = len(sub_sub_person1.getPhysicalPath()) - 3
self.assertEqual(len_path, 3)
len_path = len(sub_sub_person2.getPhysicalPath()) - 3
self.assertEqual(len_path, 3)
def addAuthenticationToPublication(self, publication_id, login, password,
auth_format, auth_type):
"""
add authentication to the publication
"""
portal_sync = self.getSynchronizationTool()
pub = portal_sync.getPublication(publication_id)
pub.setLogin(login)
pub.setPassword(password)
pub.setAuthenticationFormat(auth_format)
pub.setAuthenticationType(auth_type)
def addAuthenticationToSubscription(self, subscription_id, login, password,
auth_format, auth_type):
"""
add authentication to the subscription
"""
portal_sync = self.getSynchronizationTool()
sub = portal_sync.getSubscription(subscription_id)
sub.setAuthenticated(False)
sub.setLogin(login)
sub.setPassword(password)
sub.setAuthenticationFormat(auth_format)
sub.setAuthenticationType(auth_type)
def test_01_HasEverything(self, quiet=0, run=run_all_test):
# Test if portal_synchronizations was created
if not run: return
......@@ -325,6 +534,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
LOG('Testing... ',0,'test_02_AddPublication')
portal_id = self.getPortalName()
portal_sync = self.getSynchronizationTool()
if portal_sync.getPublication(self.pub_id) is not None:
portal_sync.manage_deletePublication(title=self.pub_id)
portal_sync.manage_addPublication(title=self.pub_id,
publication_url=self.publication_url,
destination_path='/%s/person_server' % portal_id,
......@@ -333,7 +544,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
xml_mapping=self.xml_mapping,
conduit='ERP5Conduit',
gpg_key='',
activity_enabled=False,
activity_enabled=self.activity_enabled,
authentication_format='b64',
authentication_type='syncml:auth-basic')
pub = portal_sync.getPublication(self.pub_id)
......@@ -346,6 +557,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
LOG('Testing... ',0,'test_03_AddSubscription1')
portal_id = self.getPortalId()
portal_sync = self.getSynchronizationTool()
if portal_sync.getSubscription(self.sub_id1) is not None:
portal_sync.manage_deleteSubscription(title=self.sub_id1)
portal_sync.manage_addSubscription(title=self.sub_id1,
publication_url=self.publication_url,
subscription_url=self.subscription_url1,
......@@ -356,7 +569,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
xml_mapping=self.xml_mapping,
conduit='ERP5Conduit',
gpg_key='',
activity_enabled=False,
activity_enabled=self.activity_enabled,
login='fab',
password='myPassword')
sub = portal_sync.getSubscription(self.sub_id1)
......@@ -369,6 +582,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
LOG('Testing... ',0,'test_04_AddSubscription2')
portal_id = self.getPortalId()
portal_sync = self.getSynchronizationTool()
if portal_sync.getSubscription(self.sub_id2) is not None:
portal_sync.manage_deleteSubscription(title=self.sub_id2)
portal_sync.manage_addSubscription(title=self.sub_id2,
publication_url=self.publication_url,
subscription_url=self.subscription_url2,
......@@ -379,30 +594,12 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
xml_mapping=self.xml_mapping,
conduit='ERP5Conduit',
gpg_key='',
activity_enabled=False,
activity_enabled=self.activity_enabled,
login='fab',
password='myPassword')
sub = portal_sync.getSubscription(self.sub_id2)
self.failUnless(sub is not None)
def setupPublicationAndSubscription(self, quiet=0, run=run_all_test):
self.test_02_AddPublication(quiet=1,run=1)
self.test_03_AddSubscription1(quiet=1,run=1)
self.test_04_AddSubscription2(quiet=1,run=1)
def setupPublicationAndSubscriptionAndGid(self, quiet=0, run=run_all_test):
self.setupPublicationAndSubscription(quiet=1,run=1)
portal_sync = self.getSynchronizationTool()
sub1 = portal_sync.getSubscription(self.sub_id1)
sub2 = portal_sync.getSubscription(self.sub_id2)
pub = portal_sync.getPublication(self.pub_id)
sub1.setConduit('ERP5ConduitTitleGid')
sub2.setConduit('ERP5ConduitTitleGid')
pub.setConduit('ERP5ConduitTitleGid')
pub.setSynchronizationIdGenerator('_generateNextId')
sub1.setSynchronizationIdGenerator('_generateNextId')
sub2.setSynchronizationIdGenerator('_generateNextId')
def test_05_GetSynchronizationList(self, quiet=0, run=run_all_test):
# This test the getSynchronizationList, ie,
# We want to see if we retrieve both the subscription
......@@ -445,6 +642,12 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
publication.setQuery(query)
object_list = publication.getObjectList()
self.assertEqual(len(object_list), 1)
# Add the query path
portal_id = self.getPortalName()
publication.setDestinationPath('/%s/' % portal_id)
publication.setQuery('person_server/objectValues')
object_list = publication.getObjectList()
self.assertEqual(len(object_list), nb_person)
def test_07_ExportImport(self, quiet=0, run=run_all_test):
"""
......@@ -527,9 +730,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.assertEqual(person1_s.getLastName(), self.last_name1)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
self.assertEqual(person1_c.getId(), self.id1)
self.assertEqual(person1_c.getFirstName(), long_line)
self.assertEqual(person1_c.getLastName(), self.last_name1)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
def test_10_GetObjectFromGid(self, quiet=0, run=run_all_test):
# We will try to get an object from a publication
......@@ -569,33 +770,6 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
# for each subscriber
self.checkSynchronizationStateIsSynchronized()
def checkSynchronizationStateIsConflict(self, quiet=0, run=1):
portal_sync = self.getSynchronizationTool()
person_server = self.getPersonServer()
for person in person_server.objectValues():
if person.getId()==self.id1:
state_list = portal_sync.getSynchronizationState(person)
for state in state_list:
self.assertEqual(state[1], state[0].CONFLICT)
person_client1 = self.getPersonClient1()
for person in person_client1.objectValues():
if person.getId()==self.id1:
state_list = portal_sync.getSynchronizationState(person)
for state in state_list:
self.assertEqual(state[1], state[0].CONFLICT)
person_client2 = self.getPersonClient2()
for person in person_client2.objectValues():
if person.getId()==self.id1:
state_list = portal_sync.getSynchronizationState(person)
for state in state_list:
self.assertEqual(state[1], state[0].CONFLICT)
# make sure sub object are also in a conflict mode
person = person_client1._getOb(self.id1)
sub_person = person.newContent(id=self.id1,portal_type='Person')
state_list = portal_sync.getSynchronizationState(sub_person)
for state in state_list:
self.assertEqual(state[1], state[0].CONFLICT)
def test_12_UpdateSimpleData(self, quiet=0, run=run_all_test):
if not run: return
if not quiet:
......@@ -614,32 +788,29 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
person1_c = person_client1._getOb(self.id1)
self.assertEqual(person1_s.getFirstName(), self.first_name3)
self.assertEqual(person1_s.getLastName(), self.last_name3)
self.assertEqual(person1_c.getFirstName(), self.first_name3)
self.assertEqual(person1_c.getLastName(), self.last_name3)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
# Then we do only modification on a client
kw = {'first_name':self.first_name1,'last_name':self.last_name1}
person1_c.edit(**kw)
#person1_c.setModificationDate(DateTime()+1)
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
#person1_s = person_server._getOb(self.id1)
self.assertEqual(person1_s.getFirstName(), self.first_name1)
self.assertEqual(person1_s.getLastName(), self.last_name1)
self.assertEqual(person1_c.getFirstName(), self.first_name1)
self.assertEqual(person1_c.getLastName(), self.last_name1)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
# Then we do only modification on both the client and the server
# and of course, on the same object
kw = {'first_name':self.first_name3}
person1_s.edit(**kw)
#person1_s.setModificationDate(DateTime()+2)
kw = {'description':self.description3}
person1_c.edit(**kw)
#person1_c.setModificationDate(DateTime()+2)
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
#person1_s = person_server._getOb(self.id1)
self.assertEqual(person1_s.getFirstName(), self.first_name3)
self.assertEqual(person1_s.getDescription(), self.description3)
self.assertEqual(person1_c.getFirstName(), self.first_name3)
self.assertEqual(person1_c.getDescription(), self.description3)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
def test_13_GetConflictList(self, quiet=0, run=run_all_test):
# We will try to generate a conflict and then to get it
......@@ -709,7 +880,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.assertEqual(person1_c.getDescription(), self.description2)
self.assertEqual(person1_s.getDescription(), self.description2)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
conflict_list = portal_sync.getConflictList()
self.assertEqual(len(conflict_list), 0)
......@@ -732,45 +903,10 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.assertEqual(person1_s.getDescription(), self.description3)
self.assertEqual(person1_c.getDescription(), self.description3)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
conflict_list = portal_sync.getConflictList()
self.assertEqual(len(conflict_list), 0)
def populatePersonServerWithSubObject(self, quiet=0, run=run_all_test):
"""
Before this method, we need to call populatePersonServer
Then it will give the following tree :
- person_server :
- id1
- id1
- id2
- id1
- id2
"""
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Populate Person Server With Sub Object ')
LOG('Testing... ',0,'populatePersonServerWithSubObject')
person_server = self.getPersonServer()
person1 = person_server._getOb(self.id1)
sub_person1 = person1.newContent(id=self.id1,portal_type='Person')
kw = {'first_name':self.first_name1,'last_name':self.last_name1,
'description':self.description1}
sub_person1.edit(**kw)
sub_sub_person1 = sub_person1.newContent(id=self.id1,portal_type='Person')
kw = {'first_name':self.first_name1,'last_name':self.last_name1,
'description':self.description1, 'default_telephone_text':'0689778308'}
sub_sub_person1.edit(**kw)
sub_sub_person2 = 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_person2.edit(**kw)
# remove ('','portal...','person_server')
len_path = len(sub_sub_person1.getPhysicalPath()) - 3
self.assertEqual(len_path, 3)
len_path = len(sub_sub_person2.getPhysicalPath()) - 3
self.assertEqual(len_path, 3)
def test_17_AddSubObject(self, quiet=0, run=run_all_test):
"""
In this test, we synchronize, then add sub object on the
......@@ -803,6 +939,10 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.assertEquals(sub_sub_person2.getDescription(), self.description2)
self.assertEquals(sub_sub_person2.getFirstName(), self.first_name2)
self.assertEquals(sub_sub_person2.getLastName(), self.last_name2)
#check two side (client, server)
person_server = self.getPersonServer()
sub_sub_person_s = person_server._getOb(self.id1)._getOb(self.id1)._getOb(self.id1)
self.assertXMLViewIsEqual(self.sub_id1, sub_sub_person_s, sub_sub_person1)
def test_18_UpdateSubObject(self, quiet=0, run=run_all_test):
"""
......@@ -831,8 +971,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.checkSynchronizationStateIsSynchronized()
self.assertEqual(sub_sub_person_c.getDescription(), self.description3)
self.assertEqual(sub_sub_person_c.getFirstName(), self.first_name3)
self.assertEqual(sub_sub_person_s.getDescription(), self.description3)
self.assertEqual(sub_sub_person_s.getFirstName(), self.first_name3)
self.assertXMLViewIsEqual(self.sub_id1, sub_sub_person_s, sub_sub_person_c)
def test_19_DeleteObject(self, quiet=0, run=run_all_test):
"""
......@@ -951,10 +1090,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.checkSynchronizationStateIsSynchronized()
self.assertEqual(sub_object_s.getDescription(), self.description2)
self.assertEqual(sub_object_s.getLanguage(), self.lang2)
self.assertEqual(sub_object_c1.getDescription(), self.description2)
self.assertEqual(sub_object_c1.getLanguage(), self.lang2)
self.assertEqual(sub_object_c2.getDescription(), self.description2)
self.assertEqual(sub_object_c2.getLanguage(), self.lang2)
self.assertXMLViewIsEqual(self.sub_id1, sub_object_s, sub_object_c1)
self.assertXMLViewIsEqual(self.sub_id2, sub_object_s, sub_object_c2)
def test_23_ApplySubscriberDocumentOnSubObject(self, quiet=0, run=run_all_test):
"""
......@@ -981,10 +1118,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.checkSynchronizationStateIsSynchronized()
self.assertEqual(sub_object_s.getDescription(), self.description3)
self.assertEqual(sub_object_s.getLanguage(), self.lang3)
self.assertEqual(sub_object_c1.getDescription(), self.description3)
self.assertEqual(sub_object_c1.getLanguage(), self.lang3)
self.assertEqual(sub_object_c2.getDescription(), self.description3)
self.assertEqual(sub_object_c2.getLanguage(), self.lang3)
self.assertXMLViewIsEqual(self.sub_id1, sub_object_s, sub_object_c1)
self.assertXMLViewIsEqual(self.sub_id2, sub_object_s, sub_object_c2)
def test_24_SynchronizeWithStrangeGid(self, quiet=0, run=run_all_test):
"""
......@@ -1021,6 +1156,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.checkSynchronizationStateIsSynchronized()
self.assertEqual(person_s.getDescription(), self.description3)
self.assertEqual(person_c1.getDescription(), self.description3)
self.assertXMLViewIsEqual(self.sub_id1, person_s, person_c1)
# This will test deleting object
person_server = self.getPersonServer()
person_client1 = self.getPersonClient1()
......@@ -1032,7 +1168,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
person_s = publication.getSubscriber(self.subscription_url1).getObjectFromGid(gid)
person_c1 = subscription1.getObjectFromGid(gid)
self.assertEqual(person_s.getDescription(), self.description3)
self.assertEqual(person_c1.getDescription(), self.description3)
self.assertXMLViewIsEqual(self.sub_id1, person_s, person_c1)
def test_25_MultiNodeConflict(self, quiet=0, run=run_all_test):
"""
......@@ -1066,12 +1202,12 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.assertEqual(len(conflict_list), 6)
# check if we have the state conflict on all clients
self.checkSynchronizationStateIsConflict()
# we will take :
# description on person_server
# language on person_client1
# format on person_client2
for conflict in conflict_list:
for conflict in conflict_list :
subscriber = conflict.getSubscriber()
property = conflict.getPropertyId()
resolve = 0
......@@ -1088,15 +1224,18 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.checkSynchronizationStateIsSynchronized()
self.assertEqual(person1_s.getDescription(), self.description2)
self.assertEqual(person1_c1.getDescription(), self.description2)
self.assertEqual(person1_c2.getDescription(), self.description2)
self.assertEqual(person1_s.getLanguage(), self.lang3)
self.assertEqual(person1_c1.getLanguage(), self.lang3)
self.assertEqual(person1_c2.getLanguage(), self.lang3)
self.assertEqual(person1_s.getFormat(), self.format4)
self.assertEqual(person1_c1.getFormat(), self.format4)
self.assertEqual(person1_c2.getFormat(), self.format4)
self.assertEqual(person1_s.getDescription(), self.description2)
self.assertEqual(person1_s.getLanguage(), self.lang3)
self.assertEqual(person1_s.getFormat(), self.format4)
self.assertXMLViewIsEqual(self.sub_id2, person1_s, person1_c2)
# the workflow has one more "edit_workflow" in person1_c1
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.assertXMLViewIsEqual(self.sub_id2, person1_s, person1_c2)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c1)
def test_26_SynchronizeWorkflowHistory(self, quiet=0, run=run_all_test):
......@@ -1123,6 +1262,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
person1_c.edit(**kw1)
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
self.assertEqual(len(person1_s.workflow_history[self.workflow_id]), len_wf+4)
self.assertEqual(len(person1_c.workflow_history[self.workflow_id]), len_wf+4)
......@@ -1152,6 +1292,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
person2_s.manage_delLocalRoles(['fab'])
self.synchronize(self.sub_id1)
self.synchronize(self.sub_id2)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
self.assertXMLViewIsEqual(self.sub_id2, person2_s, person2_c)
role_1_s = person1_s.get_local_roles()
role_2_s = person2_s.get_local_roles()
role_1_c = person1_c.get_local_roles()
......@@ -1220,14 +1362,12 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
person_server = self.getPersonServer() # We also check we don't
# modify initial ob
person1_s = person_server._getOb(self.id1)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
self.assertEqual(person1_s.getId(), self.id1)
self.assertEqual(person1_s.getFirstName(), self.first_name1)
self.assertEqual(person1_s.getLastName(), self.last_name1)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
self.assertEqual(person1_c.getId(), self.id1)
self.assertEqual(person1_c.getFirstName(), self.first_name1)
self.assertEqual(person1_c.getLastName(), self.last_name1)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
SyncCode.MAX_LINES = previous_max_lines
def test_30_GetSynchronizationType(self, quiet=0, run=run_all_test):
......@@ -1252,9 +1392,11 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
# Then we do only modification on a client
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
kw = {'first_name':self.first_name1,'last_name':self.last_name1}
person1_c.edit(**kw)
self.synchronize(self.sub_id1)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
for sub in portal_sync.getSubscriptionList():
self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
# Then we do only modification on both the client and the server
......@@ -1264,6 +1406,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
kw = {'description':self.description3}
person1_c.edit(**kw)
self.synchronize(self.sub_id1)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
for sub in portal_sync.getSubscriptionList():
self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
......@@ -1295,6 +1438,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
role_2_c = person2_c.get_local_permissions()
self.assertEqual(role_1_s,role_1_c)
self.assertEqual(role_2_s,role_2_c)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
self.assertXMLViewIsEqual(self.sub_id2, person2_s, person2_c)
person1_s.manage_setLocalPermissions('View',['Owner'])
person2_s.manage_setLocalPermissions('View',None)
person2_s.manage_setLocalPermissions('View management screens',())
......@@ -1306,6 +1451,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
role_2_c = person2_c.get_local_permissions()
self.assertEqual(role_1_s,role_1_c)
self.assertEqual(role_2_s,role_2_c)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
self.assertXMLViewIsEqual(self.sub_id2, person2_s, person2_c)
def test_32_AddOneWaySubscription(self, quiet=0, run=run_all_test):
if not run: return
......@@ -1314,6 +1461,8 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
LOG('Testing... ',0,'test_32_AddOneWaySubscription')
portal_id = self.getPortalId()
portal_sync = self.getSynchronizationTool()
if portal_sync.getSubscription(self.sub_id1) is not None:
portal_sync.manage_deleteSubscription(title=self.sub_id1)
portal_sync.manage_addSubscription(title=self.sub_id1,
publication_url=self.publication_url,
subscription_url=self.subscription_url1,
......@@ -1324,7 +1473,7 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
xml_mapping=self.xml_mapping,
conduit='ERP5Conduit',
gpg_key='',
activity_enabled=False,
activity_enabled=self.activity_enabled,
alert_code = SyncCode.ONE_WAY_FROM_SERVER,
login = 'fab',
password = 'myPassword')
......@@ -1342,6 +1491,13 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
if not quiet:
ZopeTestCase._print('\nTest One Way Sync ')
LOG('Testing... ',0,'test_33_OneWaySync')
person_server = self.getPersonServer()
if person_server is not None:
portal = self.getPortal()
portal._delObject(id='person_server')
portal._delObject(id='person_client1')
portal._delObject(id='person_client2')
self.deletePublicationAndSubscription()
self.test_02_AddPublication(quiet=1,run=1)
self.test_32_AddOneWaySubscription(quiet=1,run=1)
......@@ -1359,21 +1515,32 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
person_server = self.getPersonServer() # We also check we don't
# modify initial ob
person1_s = person_server._getOb(self.id1)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
self.assertEqual(person1_s.getId(), self.id1)
self.assertEqual(person1_s.getFirstName(), self.first_name1)
self.assertEqual(person1_s.getLastName(), self.last_name1)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
self.assertEqual(person1_c.getId(), self.id1)
self.assertEqual(person1_c.getFirstName(), self.first_name1)
self.assertEqual(person1_c.getLastName(), self.last_name1)
self.checkSynchronizationStateIsSynchronized()
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c, force=1)
# Then we change things on both sides and we look if there
# is synchronization from only one way
person1_c.setFirstName(self.first_name2)
person1_s.setLastName(self.last_name2)
nb_message1 = self.synchronize(self.sub_id1)
self.assertEquals(person1_c.getLastName(),self.last_name2)
self.assertEquals(person1_s.getFirstName(),self.first_name1)
#In One_From_Server Sync not modify the first_name in client because any
#datas client sent
self.assertEquals(person1_c.getFirstName(), self.first_name2)
self.assertEquals(person1_c.getLastName(), self.last_name2)
self.assertEquals(person1_s.getFirstName(), self.first_name1)
self.assertEquals(person1_s.getLastName(), self.last_name2)
#reset for refresh sync
#after synchronize, the client object retrieve value of server
self.resetSignaturePublicationAndSubscription()
nb_message1 = self.synchronize(self.sub_id1)
self.assertEquals(person1_s.getFirstName(), self.first_name1)
self.assertEquals(person1_s.getLastName(), self.last_name2)
self.checkSynchronizationStateIsSynchronized()
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c, force=1)
def test_34_encoding(self, quiet=0, run=run_all_test):
"""
......@@ -1396,13 +1563,12 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
PQRSTUVWXYZéèçà@^~µ&²0123456789!@#0^&*();:<>,. []{}\xc3\xa7sdf__\
sdf\xc3\xa7\xc3\xa7\xc3\xa7_df___&&\xc3\xa9]]]\xc2\xb0\xc2\xb0\xc2\
\xb0\xc2\xb0\xc2\xb0\xc2\xb0"
#= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZéèçà@^~µ&²012345
#6789!@#0^&*();:<>,. []{}çsdf__sdfççç_df___&&é]]]°°°°°°'"
#= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZéèçà@^~µ&²012345
#6789!@#0^&*();:<>,. []{}çsdf__sdfççç_df___&&é]]]°°°°°°'"
awaited_result_long_string = 'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZH\
awaited_result_long_string = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZH\
SElKS0xNTk9QUVJTVFVWV1hZWsOpw6jDp8OgQF5+wrUmwrIwMTIzNDU2Nzg5IUAjMF4mKigpOzo8Pi\
wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=='
wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=="
#test just b64encode
self.assertEqual(b64encode(python), awaited_result_python)
self.assertEqual(b64encode(""), "")
......@@ -1440,32 +1606,6 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=='
self.failUnless(subscription1.isDecodeEncodeTheSame(
subscription1.encode('b64', ''), '', 'b64'))
def addAuthenticationToPublication(self, publication_id, login, password,
auth_format, auth_type):
"""
add authentication to the publication
"""
portal_sync = self.getSynchronizationTool()
pub = portal_sync.getPublication(publication_id)
pub.setLogin(login)
pub.setPassword(password)
pub.setAuthenticationFormat(auth_format)
pub.setAuthenticationType(auth_type)
def addAuthenticationToSubscription(self, subscription_id, login, password,
auth_format, auth_type):
"""
add authentication to the subscription
"""
portal_sync = self.getSynchronizationTool()
sub = portal_sync.getSubscription(subscription_id)
sub.setAuthenticated(False)
sub.setLogin(login)
sub.setPassword(password)
sub.setAuthenticationFormat(auth_format)
sub.setAuthenticationType(auth_type)
def test_35_authentication(self, quiet=0, run=run_all_test):
"""
we will test
......@@ -1497,8 +1637,9 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=='
self.synchronize(self.sub_id1)
#now it should be synchronize
self.checkSynchronizationStateIsSynchronized()
self.verifyFirstNameAndLastNameAreSynchronized(self.first_name3,
self.last_name3, person1_s, person1_c)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
self.assertEquals(person1_s.getFirstName(), self.first_name3)
self.assertEquals(person1_s.getLastName(), self.last_name3)
#adding authentication :
......@@ -1524,8 +1665,9 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=='
#now it should be correctly synchronize
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.verifyFirstNameAndLastNameAreSynchronized(self.first_name2,
self.last_name2, person1_s, person1_c)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
self.assertEquals(person1_s.getFirstName(), self.first_name2)
self.assertEquals(person1_s.getLastName(), self.last_name2)
#try to synchronize with a bad login and/or password
#test if login is case sensitive (it should be !)
......@@ -1552,8 +1694,9 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=='
#now it should be correctly synchronize
self.synchronize(self.sub_id1)
self.checkSynchronizationStateIsSynchronized()
self.verifyFirstNameAndLastNameAreSynchronized(self.first_name1,
self.last_name1, person1_s, person1_c)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
self.assertEquals(person1_s.getFirstName(), self.first_name1)
self.assertEquals(person1_s.getLastName(), self.last_name1)
#verify that the login and password with utf8 caracters are accecpted
......@@ -1579,9 +1722,43 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA=='
self.addAuthenticationToSubscription(self.sub_id1, '\xc3\xa9pouet', 'ploum',
'b64', 'syncml:auth-basic')
self.synchronize(self.sub_id1)
self.verifyFirstNameAndLastNameAreSynchronized(self.first_name3,
self.last_name3, person1_s, person1_c)
self.assertXMLViewIsEqual(self.sub_id1, person1_s, person1_c)
self.assertEquals(person1_s.getFirstName(), self.first_name3)
self.assertEquals(person1_s.getLastName(), self.last_name3)
self.checkSynchronizationStateIsSynchronized()
def test_36_SynchronizationSubscriptionMaxLines(self, quiet=0, run=run_all_test):
# We will try to populate the folder person_server
# with the data form person_client
# with the data which over max line of messages
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Synchronization Subscription Max Lines')
LOG('Testing... ',0,'test_36_SynchronizationSubscriptionMaxLines')
self.login()
self.setupPublicationAndSubscription(quiet=1, run=1)
nb_person = self.populatePersonClient1(quiet=1, run=1)
portal_sync = self.getSynchronizationTool()
for sub in portal_sync.getSubscriptionList():
self.assertEquals(sub.getSynchronizationType(), SyncCode.SLOW_SYNC)
# Synchronize the first client
# data_Sub1 -> Pub (the data are in sub1 to pub is empty)
nb_message1 = self.synchronize(self.sub_id1)
#Verification number object synchronized
self.assertEqual(nb_message1, self.nb_message_first_sync_max_lines)
# Synchronize the second client
# data_Pub -> data_Sub2 the data are in pub to sub2 is empty so add +2 messages)
nb_message2 = self.synchronize(self.sub_id2)
self.assertEqual(nb_message2, self.nb_message_first_sync_max_lines + 2)
person_server = self.getPersonServer()
person_client1 = self.getPersonClient1()
person_client2 = self.getPersonClient2()
for id in range(1, 60):
person_s = person_server._getOb(str(id))
person_c = person_client1._getOb(str(id))
self.assertXMLViewIsEqual(self.sub_id1, person_s, person_c)
self.assertEqual(nb_person, len(person_server.objectValues()))
self.assertEqual(nb_person, len(person_client2.objectValues()))
def test_suite():
suite = unittest.TestSuite()
......
......@@ -59,6 +59,16 @@ class TestERP5SyncMLVCard(TestERP5SyncMLMixin, ERP5TypeTestCase):
"""
return ('erp5_base','erp5_syncml')
def verifyFirstNameAndLastNameAreSynchronized(self, first_name,
last_name, person_server, person_client):
"""
verify if the first and last name are synchronized
"""
self.assertEqual(person_server.getFirstName(), first_name)
self.assertEqual(person_server.getLastName(), last_name)
self.assertEqual(person_client.getFirstName(), first_name)
self.assertEqual(person_client.getLastName(), last_name)
def getTitle(self):
return 'testERP5SyncMLVCard'
......
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