Commit 141a752f authored by Sebastien Robin's avatar Sebastien Robin

many bugs corrected thanks to unit test


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@370 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 2cae14a9
......@@ -43,6 +43,7 @@ import pickle
import string
from xml.dom.ext import PrettyPrint
from cStringIO import StringIO
from xml.sax.saxutils import escape, unescape
import re, copy
from zLOG import LOG
......@@ -101,7 +102,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
self.args = {}
security.declareProtected(Permissions.ModifyPortalContent, 'addNode')
def addNode(self, xml=None, object=None, previous_xml=None, force=0, **kw):
def addNode(self, xml=None, object=None, previous_xml=None,
object_id=None, force=0, **kw):
"""
A node is added
......@@ -132,6 +134,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
conflict_list += self.addNode(xml=xml,object=object,
previous_xml=previous_xml, force=force, **kw)
elif xml.nodeName == 'object':
if object_id is None:
object_id = self.getAttribute(xml,'id')
docid = self.getObjectDocid(xml)
LOG('addNode',0,'object_id: %s' % object_id)
......@@ -296,7 +299,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
new_select_list += (select_item,)
select_list = new_select_list # Something like : ('','object','sid')
keyword = select_list[len(select_list)-1] # this will be 'sid'
data_xml = xml
data = None
LOG('updateNode',0,'keyword: %s' % str(keyword))
......@@ -307,6 +309,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if subnode1.nodeName=='name':
keyword = subnode1.nodeValue
data_xml = subnode
if keyword is None: # This is not a selection, directly the property
keyword = xml.nodeName
if len(self.getElementNodeList(data_xml))==0:
try:
data = data_xml.childNodes[0].data
......@@ -832,6 +836,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# Encoders.encode_base64(msg)
# msg.set_payload(data)
# data = msg.get_payload(decode=1)
data = unescape(data)
elif data_type in self.pickle_type_list:
data = data.replace('@@@','\n')
msg = MIMEBase('application','octet-stream')
......
......@@ -76,7 +76,7 @@ class Subscriber(Subscription):
"""
class Publication(SyncCode):
class Publication(Subscription):
"""
Publication defined by
......@@ -104,34 +104,12 @@ class Publication(SyncCode):
self.id = id
self.publication_url = publication_url
self.destination_path = destination_path
self.query = query
self.setQuery(query)
self.xml_mapping = xml_mapping
self.list_subscribers = PersistentMapping()
self.domain_type = self.PUB
def getId(self):
"""
return the id
"""
return self.id
def setId(self, id):
"""
set the id
"""
self.id = id
def getQuery(self):
"""
return the query
"""
return self.query
def setQuery(self, query):
"""
set the query
"""
self.query = query
self.setGidGenerator(None)
self.setIdGenerator(None)
def getPublicationUrl(self):
"""
......@@ -152,30 +130,6 @@ class Publication(SyncCode):
"""
self.publication_url = publication_url
def getDestinationPath(self):
"""
return the destination path
"""
return self.destination_path
def setDestinationPath(self, destination_path):
"""
set the destination path
"""
self.destination_path = destination_path
def getXML_Mapping(self):
"""
return the xml mapping
"""
return self.xml_mapping
def setXML_Mapping(self, xml_mapping):
"""
return the xml mapping
"""
self.xml_mapping = xml_mapping
def addSubscriber(self, subscriber):
"""
Add a new subscriber to the publication
......@@ -201,7 +155,7 @@ class Publication(SyncCode):
"""
for f in range(len(self.list_subscribers)):
if self.list_subscribers[f].subscription_url == subscription_url:
return self.list_subscribers[f]
return self.list_subscribers[f].__of__(self)
return None
def getSubscriberList(self):
......@@ -219,7 +173,8 @@ class Publication(SyncCode):
"""
for f in range(len(self.list_subscribers)):
if self.list_subscribers[f].subscription_url == subscription_url:
self.list_subscribers = self.list_subscribers[0:f] + self.list_subscribers[f+1:len(self.list_subscribers)]
self.list_subscribers = self.list_subscribers[0:f] + \
self.list_subscribers[f+1:len(self.list_subscribers)]
def resetAllSubscribers(self):
"""
......
......@@ -134,6 +134,8 @@ class PublicationSynchronization(XMLSyncUtils):
if self.email is None:
file = open('/tmp/sync_client','r')
xml_client = FromXmlStream(file)
file.seek(0)
LOG('PubSync',0,'Starting... msg: %s' % str(file.read()))
file.close()
elif msg is not None:
xml_client = FromXml(msg)
......@@ -164,22 +166,25 @@ class PublicationSynchronization(XMLSyncUtils):
# FIXME: Why can't we use the method addSubscriber ??
self.getPublication(id).addSubscriber(subscriber)
# first synchronization
self.PubSyncInit(self.list_publications[id],xml_client,subscriber=subscriber)
self.PubSyncInit(self.getPublication(id),xml_client,subscriber=subscriber)
elif self.checkAlert(xml_client) and self.getAlertCode(xml_client) in (self.TWO_WAY,self.SLOW_SYNC):
self.PubSyncInit(publication=self.list_publications[id],
self.PubSyncInit(publication=self.getPublication(id),
xml_client=xml_client, subscriber=subscriber)
else:
self.PubSyncModif(self.list_publications[id], xml_client)
self.PubSyncModif(self.getPublication(id), xml_client)
elif subscriber is not None:
# This looks like we are starting a synchronization after
# a conflict resolution by the user
self.PubSyncInit(publication=self.list_publications[id],
self.PubSyncInit(publication=self.getPublication(id),
xml_client=None, subscriber=subscriber)
has_response = 1 #pubsync always replies to messages
if RESPONSE is not None:
RESPONSE.redirect('managePublications')
else:
return 1
def PubSyncModif(self, publication, xml_client):
"""
......
......@@ -180,14 +180,16 @@ class Signature(SyncCode):
md5_object -- An MD5 value of a given document
#uid -- The UID of the document
id -- the ID of the document
gid -- the global id of the document
rid -- the uid of the document on the remote database,
only needed on the server.
xml -- the xml of the object at the time where it was synchronized
"""
# Constructor
def __init__(self,id=None, status=None, xml_string=None):
self.id = id
def __init__(self,gid=None, status=None, xml_string=None):
self.setGid(gid)
self.setId(None)
self.status = status
self.setXML(xml_string)
self.partial_xml = None
......@@ -314,6 +316,18 @@ class Signature(SyncCode):
"""
return self.id
def setGid(self, gid):
"""
set the id
"""
self.gid = gid
def getGid(self):
"""
get the id
"""
return self.gid
def setPartialXML(self, xml):
"""
Set the partial string we will have to
......@@ -398,7 +412,7 @@ class Signature(SyncCode):
else:
self.resetConflictList()
class Subscription(SyncCode):
class Subscription(SyncCode, Implicit):
"""
Subscription hold the definition of a master ODB
from/to which a selection of objects will be synchronised
......@@ -445,7 +459,7 @@ class Subscription(SyncCode):
self.publication_url = (publication_url)
self.subscription_url = str(subscription_url)
self.destination_path = str(destination_path)
self.query = query
self.setQuery(query)
self.xml_mapping = xml_mapping
self.anchor = None
self.session_id = 0
......@@ -453,6 +467,9 @@ class Subscription(SyncCode):
self.last_anchor = '00000000T000000Z'
self.next_anchor = '00000000T000000Z'
self.domain_type = self.SUB
self.setGidGenerator(None)
self.setIdGenerator(None)
#self.signatures = PersitentMapping()
# Accessors
......@@ -496,6 +513,12 @@ class Subscription(SyncCode):
"""
return self.id
def getDomainType(self):
"""
return the ID
"""
return self.domain_type
def setId(self, id):
"""
set the ID
......@@ -512,6 +535,8 @@ class Subscription(SyncCode):
"""
set the query
"""
if query in (None,''):
query = 'objectValues'
self.query = query
def getPublicationUrl(self):
......@@ -532,18 +557,119 @@ class Subscription(SyncCode):
"""
self.publication_url = publication_url
def getXML_Mapping(self):
def getXMLMapping(self):
"""
return the xml mapping
"""
return self.xml_mapping
def setXML_Mapping(self, xml_mapping):
def setXMLMapping(self, xml_mapping):
"""
return the xml mapping
"""
self.xml_mapping = xml_mapping
def setGidGenerator(self, method_id):
"""
This set the method name wich allows to find a gid
from any object
"""
if method_id in (None,''):
method_id = 'getId'
self.gid_generator = method_id
def getGidGenerator(self):
"""
This get the method name wich allows to find a gid
from any object
"""
return self.gid_generator
def getObjectFromGid(self, gid):
"""
This tries to get the object with the given gid
This uses the query if it exist
"""
signature = self.getSignature(gid)
# First look if we do already have the mapping between
# the id and the gid
# query_list = []
# query = self.getQuery()
# if query is type('a'):
# query_method = getattr(object,self.getQuery(),None)
# query_list = query()
# if callable(query):
# query_list = query(self)
object_list = self.getObjectList()
destination = self.getDestination()
if signature is not None:
o_id = signature.getId()
o = None
try:
o = destination._getOb(o_id)
except (AttributeError, KeyError):
pass
if o is not None and o in object_list:
return o
for o in object_list:
LOG('getObjectFromGid',0,'working on : %s' % repr(o))
o_base = aq_base(o)
LOG('getObjectFromGid',0,'gidgenerator : %s' % repr(self.getGidGenerator()))
if hasattr(o_base, self.getGidGenerator()):
LOG('getObjectFromGid',0,'there is the gid generator')
generator = getattr(o, self.getGidGenerator())
o_gid = generator()
LOG('getObjectFromGid',0,'o_gid: %s' % repr(o_gid))
LOG('getObjectFromGid',0,'gid: %s' % repr(gid))
if o_gid == gid:
return o
LOG('getObjectFromGid',0,'returning None')
return None
def getObjectList(self):
"""
This returns the list of sub-object corresponding
to the query
"""
destination = self.getDestination()
LOG('getObjectList',0,'this is a log')
query = self.getQuery()
query_list = []
if type(query) is type('a'):
query_method = getattr(destination,query,None)
if query_method is not None:
query_list = query_method()
if callable(query):
query_list = query(destination)
# if query is not None:
# query_list = query()
return query_list
def generateNewId(self, object=None,gid=None):
"""
This tries to generate a new Id
"""
if self.getIdGenerator() is not None:
o_base = aq_base(object)
if hasattr(aq_base, self.getIdGenerator()):
generator = getattr(o, self.getIdGenerator())
new_id = generator()
return new_id
return None
def setIdGenerator(self, method_id):
"""
This set the method name wich allows to generate
a new id
"""
self.id_generator = method_id
def getIdGenerator(self):
"""
This get the method name wich allows to generate a new id
"""
return self.id_generator
def getSubscriptionUrl(self):
"""
return the subscription url
......@@ -562,6 +688,12 @@ class Subscription(SyncCode):
"""
return self.destination_path
def getDestination(self):
"""
return the destination object itself
"""
return self.unrestrictedTraverse(self.getDestinationPath())
def setDestinationPath(self, destination_path):
"""
set the destination path
......@@ -625,15 +757,15 @@ class Subscription(SyncCode):
"""
add a Signature to the subscription
"""
self.signatures[signature.id] = signature
self.signatures[signature.getGid()] = signature
def delSignature(self, id):
def delSignature(self, gid):
"""
add a Signature to the subscription
"""
del self.signatures[id]
del self.signatures[gid]
def getSignature(self, id):
def getSignature(self, gid):
"""
add a Signature to the subscription
"""
......@@ -642,8 +774,8 @@ class Subscription(SyncCode):
#for key in self.signatures.keys():
# dict[key]=self.signatures[key].getPartialXML()
#LOG('Subscription',0,'dict: %s' % str(dict))
if self.signatures.has_key(id):
return self.signatures[id]
if self.signatures.has_key(gid):
return self.signatures[gid]
return None
def getSignatureList(self):
......@@ -655,12 +787,12 @@ class Subscription(SyncCode):
signature_list += [self.signatures[key]]
return signature_list
def hasSignature(self, id):
def hasSignature(self, gid):
"""
Check if there's a signature with this uid
"""
LOG('Subscription',0,'keys: %s' % str(self.signatures.keys()))
return self.signatures.has_key(id)
return self.signatures.has_key(gid)
def resetAllSignatures(self):
"""
......@@ -668,7 +800,7 @@ class Subscription(SyncCode):
"""
self.signatures = PersistentMapping()
def getIdList(self):
def getGidList(self):
"""
Returns the list of ids from signature
"""
......
......@@ -88,37 +88,42 @@ class SubscriptionSynchronization(XMLSyncUtils):
LOG('SubSync',0,'starting... id: %s' % str(id))
LOG('SubSync',0,'starting... msg: %s' % str(msg))
has_response = 0 #check if subsync replies to this messages
# first synchronization
if self.email is None:
file = open('/tmp/sync','r')
if file.readlines() == []:
self.SubSyncInit(self.list_subscriptions[id])
self.SubSyncInit(self.getSubscription(id))
has_response = 1
else:
file.seek(0)
xml_client = FromXmlStream(file)
self.SubSyncModif(self.list_subscriptions[id],xml_client)
file.seek(0)
LOG('SubSync',0,'starting... msg: %s' % str(file.read()))
has_response = self.SubSyncModif(self.getSubscription(id),xml_client)
file.close()
else:
if msg==None:
self.SubSyncInit(self.list_subscriptions[id])
self.SubSyncInit(self.getSubscription(id))
has_response = 1
else:
xml_client = FromXml(msg)
self.SubSyncModif(self.list_subscriptions[id],xml_client)
# Looks like this is not needed now
# if self.checkAlert(xml_client):
# self.SubSyncModif(self.list_subscriptions[id],xml_client)
# else:
# self.SubLastSync(self.list_subscriptions[id],xml_client)
has_response = self.SubSyncModif(self.getSubscription(id),xml_client)
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
else:
LOG('SubSync',0,'has_response: %s' % str(has_response))
return has_response
def SubSyncModif(self, subscription, xml_client):
"""
Send the client modification, this happens after the Synchronization
initialization
"""
self.SyncModif(subscription, xml_client)
return self.SyncModif(subscription, xml_client)
def SubLastSync(self, subscription, xml_client=None, RESPONSE=None):
......
......@@ -243,16 +243,18 @@ class SynchronizationTool( UniqueObject, SimpleItem,
self.list_publications = PersistentMapping()
for key in self.list_publications.keys():
LOG('getPublicationList',0,'key: %s, pub:%s' % (key,repr(self.list_publications[key])))
return_list += [self.list_publications[key]]
return_list += [self.list_publications[key].__of__(self)]
return return_list
security.declareProtected(Permissions.AccessContentsInformation,'getPublication')
def getPublication(self, id):
"""
Return a list of publications
Return the publications with this id
"""
#self.list_publications=PersistentMapping()
return self.list_publications[id]
if self.list_publications.has_key(id):
return self.list_publications[id].__of__(self)
return None
security.declareProtected(Permissions.AccessContentsInformation,'getSubscriptionList')
def getSubscriptionList(self):
......@@ -264,19 +266,38 @@ class SynchronizationTool( UniqueObject, SimpleItem,
# SynchronizationTool, XXX To be removed
self.list_subscriptions = PersistentMapping()
for key in self.list_subscriptions.keys():
return_list += [self.list_subscriptions[key]]
return_list += [self.list_subscriptions[key].__of__(self)]
return return_list
def getSubscription(self, id):
"""
Returns the subscription with this id
"""
for subscription in self.getSubscriptionList():
if subscription.getId()==id:
return subscription
return None
security.declareProtected(Permissions.AccessContentsInformation,'')
def getSynchronizationList(self):
"""
Returns the list of subscriptions and publications
getSynchronizationList ? (mon choix)
getSubscriptionOrPublicationList ?
"""
return self.getSubscriptionList() + self.getPublicationList()
security.declareProtected(Permissions.AccessContentsInformation,'')
def getSubscriberList(self):
"""
Returns the list of subscribers and subscriptions
"""
s_list = []
s_list += self.getSubscriptionList()
for publication in self.getPublicationList():
s_list += publication.getSubscriberList()
return s_list
security.declareProtected(Permissions.AccessContentsInformation,'getConflictList')
def getConflictList(self, context=None):
"""
......@@ -292,14 +313,14 @@ class SynchronizationTool( UniqueObject, SimpleItem,
sub_conflict_list = subscriber.getConflictList()
for conflict in sub_conflict_list:
#conflict.setDomain('Publication')
conflict.setDomain(subscriber)
conflict.setSubscriber(subscriber)
#conflict.setDomainId(subscriber.getId())
conflict_list += [conflict.__of__(self)]
for subscription in self.getSubscriptionList():
sub_conflict_list = subscription.getConflictList()
for conflict in sub_conflict_list:
#conflict.setDomain('Subscription')
conflict.setDomain(subscription)
conflict.setSubscriber(subscription)
#conflict.setDomainId(subscription.getId())
conflict_list += [conflict.__of__(self)]
if path is not None: # Retrieve only conflicts for a given path
......@@ -342,7 +363,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
for conflict in conflict_list:
if conflict.getObjectPath() == path:
LOG('getSynchronizationState',0,'found a conflict: %s' % str(conflict))
state_list += [[conflict.getDomain(),self.CONFLICT]]
state_list += [[conflict.getSubscriber(),self.CONFLICT]]
for domain in self.getSynchronizationList():
destination = domain.getDestinationPath()
LOG('getSynchronizationState',0,'destination: %s' % str(destination))
......@@ -356,10 +377,13 @@ class SynchronizationTool( UniqueObject, SimpleItem,
subscriber_list = domain.getSubscriberList()
else:
subscriber_list = [domain]
LOG('getSynchronizationState, subscriber_list:',0,subscriber_list)
for subscriber in subscriber_list:
signature = subscriber.getSignature(o_id)
if signature is not None:
state = signature.getStatus()
LOG('getSynchronizationState:',0,'sub.dest :%s, state: %s' % \
(subscriber.getSubscriptionUrl(),str(state)))
found = None
# Make sure there is not already a conflict giving the state
for state_item in state_list:
......@@ -376,7 +400,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
to keep the local version of an object
"""
object = self.unrestrictedTraverse(conflict.getObjectPath())
subscriber = conflict.getDomain()
subscriber = conflict.getSubscriber()
# get the signature:
LOG('p_sync.setLocalObject, subscriber: ',0,subscriber)
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
......@@ -411,7 +435,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
to keep the local version of an object
"""
object = self.unrestrictedTraverse(conflict.getObjectPath())
subscriber = conflict.getDomain()
subscriber = conflict.getSubscriber()
# get the signature:
LOG('p_sync.setRemoteObject, subscriber: ',0,subscriber)
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
......@@ -449,7 +473,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if conflict.getKeyword() == keyword:
LOG('manageLocalValue',0,'found the keyword')
if '/'.join(conflict.getObjectPath())==object_path:
if conflict.getDomain().getSubscriptionUrl()==subscription_url:
if conflict.getSubscriber().getSubscriptionUrl()==subscription_url:
conflict.applyPublisherValue()
if RESPONSE is not None:
RESPONSE.redirect('manageConflicts')
......@@ -468,7 +492,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if conflict.getKeyword() == keyword:
LOG('manageLocalValue',0,'found the keyword')
if '/'.join(conflict.getObjectPath())==object_path:
if conflict.getDomain().getSubscriptionUrl()==subscription_url:
if conflict.getSubscriber().getSubscriptionUrl()==subscription_url:
conflict.applySubscriberValue()
if RESPONSE is not None:
RESPONSE.redirect('manageConflicts')
......
......@@ -127,7 +127,8 @@ class XMLSyncUtilsMixin(SyncCode):
server.sendmail(fromaddr, "seb@localhost", msg)
server.quit()
def addXMLObject(self, cmd_id=0, object=None, xml_string=None,more_data=0):
def addXMLObject(self, cmd_id=0, object=None, xml_string=None,
more_data=0,gid=None):
"""
Add an object with the SyncML protocol
"""
......@@ -136,7 +137,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml += ' <CmdID>%s</CmdID>\n' % cmd_id
xml += ' <Meta><Type>%s</Type></Meta>\n' % object.portal_type
xml += ' <Item>\n'
xml += ' <Source><LocURI>%s</LocURI></Source>\n' % object.id
xml += ' <Source><LocURI>%s</LocURI></Source>\n' % gid
xml += ' <Data>\n'
xml += xml_string
xml += ' </Data>\n'
......@@ -146,7 +147,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml += ' </Add>\n'
return xml
def deleteXMLObject(self, cmd_id=0, object_id=None, xml_object=''):
def deleteXMLObject(self, cmd_id=0, object_gid=None, xml_object=''):
"""
Add an object with the SyncML protocol
"""
......@@ -155,7 +156,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml += ' <CmdID>%s</CmdID>\n' % cmd_id
#xml += ' <Meta><Type>%s</Type></Meta>\n' % object.portal_type
xml += ' <Item>\n'
xml += ' <Source><LocURI>%s</LocURI></Source>\n' % object_id
xml += ' <Source><LocURI>%s</LocURI></Source>\n' % object_gid
xml += ' <Data>\n'
#xml += xml_object # We have to send the data, because it allows to
# wich object to delete (it could be clever things
......@@ -164,7 +165,8 @@ class XMLSyncUtilsMixin(SyncCode):
xml += ' </Delete>\n'
return xml
def replaceXMLObject(self, cmd_id=0, object=None, xml_string=None, more_data=0):
def replaceXMLObject(self, cmd_id=0, object=None, xml_string=None,
more_data=0,gid=None):
"""
Add an object with the SyncML protocol
"""
......@@ -173,7 +175,7 @@ class XMLSyncUtilsMixin(SyncCode):
xml += ' <CmdID>%s</CmdID>\n' % cmd_id
xml += ' <Meta><Type>%s</Type></Meta>\n' % object.portal_type
xml += ' <Item>\n'
xml += ' <Source><LocURI>%s</LocURI></Source>\n' % object.id
xml += ' <Source><LocURI>%s</LocURI></Source>\n' % str(gid)
xml += ' <Data>\n'
xml += xml_string
xml += ' </Data>\n'
......@@ -548,214 +550,42 @@ class XMLSyncUtilsMixin(SyncCode):
attribute_list += [subnode]
return attribute_list
class XMLSyncUtils(XMLSyncUtilsMixin):
def Sync(self, id, msg=None, RESPONSE=None):
"""
This is the main method for synchronization
"""
pass
def SyncInit(self, domain):
"""
Initialization of a synchronization, this is
used for the first message of every synchronization
"""
pass
def SyncModif(self, domain, remote_xml):
"""
Modification Message, this is used after the first
message in order to send modifications.
def getSyncMLData(self, domain=None,remote_xml=None,cmd_id=0,
subscriber=None,destination_path=None):
"""
This generate the syncml data message. This returns a string
with all modification made locally (ie replace, add ,delete...)
"""
Send the server modification, this happens after the Synchronization
initialization
"""
from Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
cmd_id = 1 # specifies a SyncML message-unique command identifier
LOG('SyncModif',0,'Starting... domain: %s' % str(domain))
# Get the destination folder
destination_path = self.unrestrictedTraverse(domain.getDestinationPath())
first_node = remote_xml.childNodes[1]
# Get informations from the header
xml_header = first_node.childNodes[1]
if xml_header.nodeName != "SyncHdr":
LOG('PubSyncModif',0,'This is not a SyncML Header')
return
subscriber = domain # If we are the client, this is fine
if domain.domain_type == self.PUB:
for subnode in xml_header.childNodes:
if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Source":
subscription_url = str(subnode.childNodes[0].data)
subscriber = domain.getSubscriber(subscription_url)
next_status = self.getNextSyncBodyStatus(remote_xml, None)
next_status = self.getNextSyncBodyStatus(remote_xml, next_status) # We do not want the first one
destination_waiting_more_data = 0
while next_status != None:
object_id = self.getStatusTarget(next_status)
status_code = self.getStatusCode(next_status)
signature = subscriber.getSignature(object_id)
LOG('SyncModif',0,'next_status: %s' % str(status_code))
if status_code == self.CHUNK_OK:
destination_waiting_more_data = 1
signature.setStatus(self.PARTIAL)
elif status_code == self.CONFLICT:
signature.setStatus(self.CONFLICT)
elif status_code == self.CONFLICT_MERGE:
# We will have to apply the update, and we should not care about conflicts
# so we have to force the update
signature.setStatus(self.NOT_SYNCHRONIZED)
signature.setForce(1)
elif status_code == self.CONFLICT_CLIENT_WIN:
# The server was agree to apply our updates, nothing to do
signature.setStatus(self.SYNCHRONIZED)
elif status_code == self.SUCCESS:
signature.setStatus(self.SYNCHRONIZED)
next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
alert_code = self.getAlertCode(remote_xml)
next_action = self.getNextSyncAction(remote_xml, None)
has_next_action = 0
if next_action is not None:
has_next_action = 1
LOG('SyncModif, has_next_action:',0,has_next_action)
conduit = ERP5Conduit()
xml_confirmation = ''
while next_action != None:
conflict_list = []
status_code = self.SUCCESS
# Thirst we have to check the kind of action it is
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_id = self.getActionId(next_action)
signature = subscriber.getSignature(object_id)
if signature == None:
signature = Signature(id=object_id,status=self.NOT_SYNCHRONIZED)
subscriber.addSignature(signature)
force = signature.getForce()
if self.checkActionMoreData(next_action) == 0:
data_subnode = None
if partial_data != None:
data_subnode = signature.getPartialXML() + partial_data
LOG('SyncModif',0,'data_subnode: %s' % data_subnode)
data_subnode = FromXml(data_subnode)
data_subnode = data_subnode.childNodes[1] # Because we just created a new xml
# document, with childNodes[0] a DocumentType and childNodes[1] the Element Node
else:
data_subnode = self.getDataSubNode(next_action)
if next_action.nodeName == 'Add':
conflict_list += conduit.addNode(xml=data_subnode, object=destination_path)
# Then store the xml of this new subobject
object =None
LOG('SyncModif',0,'addNode, getActionId: %s' % self.getActionId(next_action))
try:
object = destination_path._getOb(self.getActionId(next_action))
except (AttributeError, KeyError):
pass
if object is not None:
LOG('SyncModif',0,'addNode, found the object')
xml_object = object.asXML()
signature.setStatus(self.SYNCHRONIZED)
signature.setXML(xml_object)
#has_sign = subscriber.hasSignature(object.id)
#LOG('SyncModif',0,'has_sign: %i' % has_sign)
xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id,self.SUCCESS,'Add')
cmd_id +=1
elif next_action.nodeName == 'Replace':
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))
if object is not None:
LOG('SyncModif',0,'object: %s will be updated...' % object.id)
signature = subscriber.getSignature(object.id)
LOG('SyncModif',0,'previous signature: %s' % str(signature))
previous_xml = signature.getXML()
LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
conflict_list += conduit.updateNode(xml=data_subnode, object=object,
previous_xml=signature.getXML(),force=force)
xml_object = object.asXML()
signature.setTempXML(xml_object)
if conflict_list != []:
status_code = self.CONFLICT
signature.setStatus(self.CONFLICT)
signature.setConflictList(signature.getConflictList()+conflict_list)
string_io = StringIO()
PrettyPrint(data_subnode,stream=string_io)
data_subnode_string = string_io.getvalue()
signature.setPartialXML(data_subnode_string)
else:
signature.setStatus(self.SYNCHRONIZED)
xml_confirmation += self.SyncMLConfirmation(cmd_id,
object.id,status_code,'Replace')
cmd_id +=1
elif next_action.nodeName == 'Delete':
conduit.deleteNode(xml=self.getDataSubNode(next_action), object=destination_path,
object_id=self.getActionId(next_action))
subscriber.delSignature(self.getActionId(next_action))
else: # We want to retrieve more data
signature.setStatus(self.PARTIAL)
#LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial))
previous_partial = signature.getPartialXML() or ''
previous_partial += partial_data
signature.setPartialXML(previous_partial)
#LOG('SyncModif',0,'previous_partial: %s' % str(previous_partial))
LOG('SyncModif',0,'waiting more data for :%s' % signature.getId())
xml_confirmation += self.SyncMLConfirmation(cmd_id,object_id,
self.WAITING_DATA,next_action.nodeName)
if conflict_list != [] and signature is not None:
# We had a conflict
signature.setStatus(self.CONFLICT)
next_action = self.getNextSyncAction(remote_xml, next_action)
xml = ""
xml += '<SyncML>\n'
# syncml header
if domain.domain_type == self.PUB:
xml += self.SyncMLHeader(subscriber.getSessionId(), "1",
subscriber.getSubscriptionUrl(), domain.getPublicationUrl())
elif domain.domain_type == self.SUB:
xml += self.SyncMLHeader(domain.getSessionId(), "1",
domain.getPublicationUrl(), domain.getSubscriptionUrl())
cmd_id += 1
# Add or replace objects
local_id_list = []
local_gid_list = []
syncml_data = ''
if has_next_action == 0:
for object in destination_path.objectValues():
for object in domain.getObjectList():
status = self.SENT
local_id_list += [object.id]
gid_generator = getattr(object,domain.getGidGenerator(),None)
object_gid = None
if gid_generator is not None:
object_gid = gid_generator()
local_gid_list += [object_gid]
force = 0
if syncml_data.count('\n') < self.MAX_LINES and (object.id.find('.')!=0): # If not we have to cut
xml_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping)
LOG('SyncModif',0,'xml_mapping: %s' % str(domain.xml_mapping))
LOG('SyncModif',0,'code: %s' % str(self.getAlertCode(remote_xml)))
LOG('XMLSyncModif',0,'id_list: %s' % str(local_id_list))
LOG('XMLSyncModif',0,'hasSinature: %s' % str(subscriber.hasSignature(object.id)))
LOG('XMLSyncModif',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC))
signature = subscriber.getSignature(object.id)
LOG('getSyncMLData',0,'xml_mapping: %s' % str(domain.xml_mapping))
LOG('getSyncMLData',0,'code: %s' % str(self.getAlertCode(remote_xml)))
LOG('getSyncMLData',0,'gid_list: %s' % str(local_gid_list))
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))
signature = subscriber.getSignature(object_gid)
if signature is not None:
LOG('getSyncMLData',0,'signature.status: %s' % str(signature.getStatus()))
LOG('getSyncMLData',0,'signature.action: %s' % str(signature.getAction()))
status = self.SENT
more_data=0
# For the case it was never synchronized, we have to send everything
if signature==None or (signature.getXML()==None and signature.getStatus()!=self.PARTIAL) or \
self.getAlertCode(remote_xml)==self.SLOW_SYNC:
#LOG('PubSyncModif',0,'Current object.getPath: %s' % object.getPath())
LOG('getSyncMLData',0,'no signature for gid: %s' % object_gid)
xml_string = xml_object
signature = Signature(id=object.id)
signature = Signature(gid=object_gid)
signature.setTempXML(xml_object)
if xml_string.count('\n') > self.MAX_LINES:
more_data=1
......@@ -767,26 +597,27 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
rest_string = xml_string[len(short_string):]
#LOG('XMLSyncUtils',0,'rest_string: %s' % str(rest_string))
i += 1
LOG('SyncModif',0,'setPartialXML with: %s' % str(rest_string))
LOG('getSyncMLData',0,'setPartialXML with: %s' % str(rest_string))
signature.setPartialXML(rest_string)
status =self.PARTIAL
signature.setAction('Add')
xml_string = '<!--' + short_string + '-->'
syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,
syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,gid=object_gid,
xml_string=xml_string, more_data=more_data)
cmd_id += 1
signature.setStatus(status)
subscriber.addSignature(signature)
elif signature.getStatus()==self.NOT_SYNCHRONIZED \
or signature.getStatus()==self.PUB_CONFLICT_MERGE: # We don't have synchronized this object yet
LOG('SyncModif',0,'checkMD5: %s' % str(signature.checkMD5(xml_object)))
LOG('SyncModif',0,'getStatus: %s' % str(signature.getStatus()))
LOG('getSyncMLData',0,'checkMD5: %s' % str(signature.checkMD5(xml_object)))
LOG('getSyncMLData',0,'getStatus: %s' % str(signature.getStatus()))
if signature.getStatus()==self.PUB_CONFLICT_MERGE:
xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id,
self.CONFLICT_MERGE,'Replace')
if not signature.checkMD5(xml_object):
# This object has changed on this side, we have to generate some xmldiff
xml_string = self.getXupdateObject(object=object,xml_mapping=domain.xml_mapping,
xml_string = self.getXupdateObject(object=object,
xml_mapping=domain.xml_mapping,
old_xml=signature.getXML())
if xml_string.count('\n') > self.MAX_LINES:
i = 0
......@@ -802,7 +633,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
signature.setAction('Replace')
xml_string = '<!--' + short_string + '-->'
signature.setStatus(status)
syncml_data += self.replaceXMLObject(cmd_id=cmd_id,object=object,
syncml_data += self.replaceXMLObject(cmd_id=cmd_id,object=object,gid=object_gid,
xml_string=xml_string, more_data=more_data)
cmd_id += 1
signature.setTempXML(xml_object)
......@@ -810,12 +641,13 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
signature.setStatus(self.SYNCHRONIZED)
elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN:
# We have decided to apply the update
LOG('SyncModif',0,'signature.getTempXML(): %s' % str(signature.getTempXML()))
LOG('getSyncMLData',0,'signature.getTempXML(): %s' % str(signature.getTempXML()))
# XXX previous_xml will be getXML instead of getTempXML because
# some modification was already made and the update may not apply correctly
# some modification was already made and the update
# may not apply correctly
conduit.updateNode(xml=signature.getPartialXML(), object=object,
previous_xml=signature.getXML(),force=1)
xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id,
xml_confirmation += self.SyncMLConfirmation(cmd_id,object_gid,
self.CONFLICT_CLIENT_WIN,'Replace')
signature.setStatus(self.SYNCHRONIZED)
elif signature.getStatus()==self.PARTIAL:
......@@ -835,31 +667,270 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
xml_string = '<!--' + xml_string + '-->'
signature.setStatus(status)
if signature.getAction()=='Replace':
syncml_data += self.replaceXMLObject(cmd_id=cmd_id,object=object,
syncml_data += self.replaceXMLObject(cmd_id=cmd_id,object=object,gid=object_gid,
xml_string=xml_string, more_data=more_data)
elif signature.getAction()=='Add':
syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,
syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,gid=object_gid,
xml_string=xml_string, more_data=more_data)
# Objects to remove
#for object_id in id_list:
for object_id in subscriber.getIdList():
if not (object_id in local_id_list):
for object_gid in subscriber.getGidList():
if not (object_gid in local_gid_list): # @@@@@
# This is an object to remove
signature = subscriber.getSignature(object_id)
signature = subscriber.getSignature(object_gid)
if signature.getStatus()!=self.PARTIAL: # If partial, then we have a signature
# but no local object
xml_object = signature.getXML()
if xml_object is not None: # This prevent to delete an object that we
# were not able to create
syncml_data += self.deleteXMLObject(xml_object=signature.getXML() or '',
object_id=object_id,cmd_id=cmd_id)
subscriber.delSignature(object_id)
object_gid=object_gid,cmd_id=cmd_id)
subscriber.delSignature(object_gid)
return (syncml_data,cmd_id)
def applyActionList(self, domain=None, subscriber=None,destination_path=None,
cmd_id=0,remote_xml=None,conduit=None):
"""
This just look to a list of action to do, then id applies
each action one by one, thanks to a conduit
"""
next_action = self.getNextSyncAction(remote_xml, None)
xml_confirmation = ''
has_next_action = 0
if next_action is not None:
has_next_action = 1
while next_action != None:
conflict_list = []
status_code = self.SUCCESS
# Thirst we have to check the kind of action it is
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)
signature = subscriber.getSignature(object_gid)
if signature == None:
signature = Signature(gid=object_gid,status=self.NOT_SYNCHRONIZED)
subscriber.addSignature(signature)
force = signature.getForce()
object = domain.getObjectFromGid(object_gid)
LOG('applyActionList',0,'object: %s' % repr(object))
if self.checkActionMoreData(next_action) == 0:
data_subnode = None
if partial_data != None:
data_subnode = signature.getPartialXML() + partial_data
LOG('SyncModif',0,'data_subnode: %s' % data_subnode)
data_subnode = FromXml(data_subnode)
data_subnode = data_subnode.childNodes[1] # Because we just created a new xml
# document, with childNodes[0] a DocumentType and childNodes[1] the Element Node
else:
data_subnode = self.getDataSubNode(next_action)
if next_action.nodeName == 'Add':
# Then store the xml of this new subobject
#object = domain.getObjectFromGid(object=destination_path,gid=object_gid)
if object is None:
object_id = domain.generateNewId(object=destination_path)
conflict_list += conduit.addNode(xml=data_subnode, object=destination_path,
object_id=object_id)
object = domain.getObjectFromGid(object_gid)
LOG('applyActionList',0,'object after add: %s' % repr(object)) #LOG('SyncModif',0,'addNode, getActionId: %s' % self.getActionId(next_action))
# try:
# object = destination_path._getOb(self.getActionId(next_action))
# except (AttributeError, KeyError):
# pass
if object is not None:
LOG('SyncModif',0,'addNode, found the object')
mapping = getattr(object,domain.getXMLMapping(),None)
xml_object = ''
if mapping is not None:
xml_object = mapping()
#xml_object = object.asXML()
signature.setStatus(self.SYNCHRONIZED)
signature.setId(object.getId())
signature.setXML(xml_object)
xml_confirmation +=\
self.SyncMLConfirmation(cmd_id,object_gid,self.SUCCESS,'Add')
cmd_id +=1
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))
if object is not None:
LOG('SyncModif',0,'object: %s will be updated...' % object.id)
signature = subscriber.getSignature(object_gid)
LOG('SyncModif',0,'previous signature: %s' % str(signature))
previous_xml = signature.getXML()
LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
conflict_list += conduit.updateNode(xml=data_subnode, object=object,
previous_xml=signature.getXML(),force=force)
mapping = getattr(object,domain.getXMLMapping(),None)
xml_object = ''
if mapping is not None:
xml_object = mapping()
#xml_object = object.asXML()
signature.setTempXML(xml_object)
if conflict_list != []:
status_code = self.CONFLICT
signature.setStatus(self.CONFLICT)
signature.setConflictList(signature.getConflictList()+conflict_list)
string_io = StringIO()
PrettyPrint(data_subnode,stream=string_io)
data_subnode_string = string_io.getvalue()
signature.setPartialXML(data_subnode_string)
else:
signature.setStatus(self.SYNCHRONIZED)
xml_confirmation += self.SyncMLConfirmation(cmd_id,
object_gid,status_code,'Replace')
cmd_id +=1
elif next_action.nodeName == 'Delete':
object_id = object.id
conduit.deleteNode(xml=self.getDataSubNode(next_action), object=destination_path,
object_id=self.getActionId(next_action))
subscriber.delSignature(object_gid)
else: # We want to retrieve more data
signature.setStatus(self.PARTIAL)
#LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial))
previous_partial = signature.getPartialXML() or ''
previous_partial += partial_data
signature.setPartialXML(previous_partial)
#LOG('SyncModif',0,'previous_partial: %s' % str(previous_partial))
LOG('SyncModif',0,'waiting more data for :%s' % signature.getId())
xml_confirmation += self.SyncMLConfirmation(cmd_id,object_gid,
self.WAITING_DATA,next_action.nodeName)
if conflict_list != [] and signature is not None:
# We had a conflict
signature.setStatus(self.CONFLICT)
next_action = self.getNextSyncAction(remote_xml, next_action)
return (xml_confirmation,has_next_action,cmd_id)
def applyStatusList(self, subscriber=None,remote_xml=None):
"""
This read a list of status list (ie syncml confirmations).
This method have to change status codes on signatures
"""
next_status = self.getNextSyncBodyStatus(remote_xml, None)
# We do not want the first one
next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
has_status_list = 0
if next_status is not None:
has_status_list = 1
destination_waiting_more_data = 0
while next_status != None:
object_gid = self.getStatusTarget(next_status)
status_code = self.getStatusCode(next_status)
signature = subscriber.getSignature(object_gid)
LOG('SyncModif',0,'next_status: %s' % str(status_code))
if status_code == self.CHUNK_OK:
destination_waiting_more_data = 1
signature.setStatus(self.PARTIAL)
elif status_code == self.CONFLICT:
signature.setStatus(self.CONFLICT)
elif status_code == self.CONFLICT_MERGE:
# We will have to apply the update, and we should not care about conflicts
# so we have to force the update
signature.setStatus(self.NOT_SYNCHRONIZED)
signature.setForce(1)
elif status_code == self.CONFLICT_CLIENT_WIN:
# The server was agree to apply our updates, nothing to do
signature.setStatus(self.SYNCHRONIZED)
elif status_code == self.SUCCESS:
signature.setStatus(self.SYNCHRONIZED)
next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
return (destination_waiting_more_data, has_status_list)
class XMLSyncUtils(XMLSyncUtilsMixin):
def Sync(self, id, msg=None, RESPONSE=None):
"""
This is the main method for synchronization
"""
pass
def SyncInit(self, domain):
"""
Initialization of a synchronization, this is
used for the first message of every synchronization
"""
pass
def SyncModif(self, domain, remote_xml):
"""
Modification Message, this is used after the first
message in order to send modifications.
"""
"""
Send the server modification, this happens after the Synchronization
initialization
"""
from Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
has_response = 0 #check if submodif replies to this messages
cmd_id = 1 # specifies a SyncML message-unique command identifier
LOG('SyncModif',0,'Starting... domain: %s' % str(domain))
# Get the destination folder
destination_path = self.unrestrictedTraverse(domain.getDestinationPath())
first_node = remote_xml.childNodes[1]
# Get informations from the header
xml_header = first_node.childNodes[1]
if xml_header.nodeName != "SyncHdr":
LOG('PubSyncModif',0,'This is not a SyncML Header')
return
subscriber = domain # If we are the client, this is fine
if domain.domain_type == self.PUB:
for subnode in xml_header.childNodes:
if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Source":
subscription_url = str(subnode.childNodes[0].data)
subscriber = domain.getSubscriber(subscription_url)
# First apply the list of status codes
(destination_waiting_more_data,has_status_list) = self.applyStatusList(
subscriber=subscriber,
remote_xml=remote_xml)
alert_code = self.getAlertCode(remote_xml)
conduit = ERP5Conduit()
LOG('SyncModif, subscriber: ',0,subscriber)
# Then apply the list of actions
(xml_confirmation,has_next_action,cmd_id) = self.applyActionList(cmd_id=cmd_id,
domain=domain,
destination_path=destination_path,
subscriber=subscriber,
remote_xml=remote_xml,
conduit=conduit)
LOG('SyncModif, has_next_action:',0,has_next_action)
xml = ""
xml += '<SyncML>\n'
# syncml header
if domain.domain_type == self.PUB:
xml += self.SyncMLHeader(subscriber.getSessionId(), "1",
subscriber.getSubscriptionUrl(), domain.getPublicationUrl())
elif domain.domain_type == self.SUB:
xml += self.SyncMLHeader(domain.getSessionId(), "1",
domain.getPublicationUrl(), domain.getSubscriptionUrl())
cmd_id += 1
# Add or replace objects
syncml_data = ''
# Now we have to send our own modifications
if has_next_action == 0:
(syncml_data,cmd_id) = self.getSyncMLData(domain=domain,remote_xml=remote_xml,
subscriber=subscriber,
destination_path=destination_path,
cmd_id=cmd_id)
# syncml body
xml += ' <SyncBody>\n'
# For a slow sync, we have to send everything
#if alert_code == subscriber.SLOW_SYNC:
destination_url = ''
if domain.domain_type == self.PUB:
subscriber.NewAnchor()
......@@ -891,18 +962,32 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
xml += '</SyncML>\n'
if self.email is None:
# We do not want to use email
if domain.domain_type == self.PUB: # We always reply
#if (xml_confirmation,syncml_data)!=('','') or has_status_list:
if 1:
file = open('/tmp/sync','w')
file.write(xml)
file.close()
has_response = 1
elif domain.domain_type == self.SUB :
if self.checkAlert(remote_xml) or \
(xml_confirmation,syncml_data)!=('','') or \
has_status_list:
file = open('/tmp/sync_client','w')
has_response = 1
file.write(xml)
file.close()
else:
# We use email
if domain.domain_type == self.PUB:
if (xml_confirmation,syncml_data)!=('',''):
if domain.domain_type == self.PUB: # We always reply
#if (xml_confirmation,syncml_data)!=('','') or has_status_list:
if 1:
self.sendMail(domain.publication_url, subscriber.subscription_url,
domain.id, xml)
elif domain.domain_type == self.SUB:
if self.checkAlert(remote_xml) or \
(xml_confirmation,syncml_data)!=('',''):
(xml_confirmation,syncml_data)!=('','') or \
has_status_list:
self.sendMail(domain.subscription_url, domain.publication_url,
domain.id, xml)
return has_response
......@@ -84,7 +84,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="xml_mapping" value="<dtml-var getXML_Mapping>" size="40" />
<input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
</td>
</tr>
</table>
......
......@@ -94,7 +94,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="xml_mapping" value="<dtml-var getXML_Mapping>" size="40" />
<input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
</td>
</tr>
</table>
......
......@@ -39,3 +39,5 @@ Setting up Synchronization
XML Mapping : asXML
Then you have to go on the subscription and hit 'Sync'
\ No newline at end of file
......@@ -393,6 +393,25 @@ class TestERP5SyncML(ERP5TypeTestCase):
# self.failUnless(person1_c.getFirstName()==self.first_name3)
# self.failUnless(person1_c.getDescription()==self.description3)
def testGetConflictList(self, quiet=0):
# We will try to generate a conflict and then to get it
if not quiet:
ZopeTestCase._print('\nTest Get Conflict List ')
LOG('Testing... ',0,'testGetConflictList')
self.testFirstSynchronization(quiet=1)
# First we do only modification on server
portal_sync = self.getSynchronizationTool()
person_server = self.getPersonServer()
person1_s = person_server._getOb(self.id1)
person1_s.setDescription(self.description2)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
person1_c.setDescription(self.description3)
self.synchronize(self.sub_id1)
#self.checkSynchronizationStateIsSynchronized()
if __name__ == '__main__':
framework()
else:
......
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