diff --git a/product/ERP5SyncML/Conduit/ERP5Conduit.py b/product/ERP5SyncML/Conduit/ERP5Conduit.py index 3f52fcabdc23ca7d9e6df3a3d352be214e167c78..4d3bd619087b0bf19b5a9d848502169dc5a64874 100644 --- a/product/ERP5SyncML/Conduit/ERP5Conduit.py +++ b/product/ERP5SyncML/Conduit/ERP5Conduit.py @@ -33,8 +33,7 @@ from Products.CMFCore.utils import getToolByName from Products.ERP5SyncML.XupdateUtils import XupdateUtils from Products.ERP5Type.Utils import convertToUpperCase from Products.ERP5Type.Accessor.TypeDefinition import list_types -from xml.dom.ext.reader.Sax2 import FromXml -from xml.dom.minidom import parse, parseString +from Ft.Xml import Parse from DateTime.DateTime import DateTime from email.MIMEBase import MIMEBase from email import Encoders @@ -44,10 +43,12 @@ from Products.ERP5Type import Permissions from Globals import PersistentMapping import pickle import string -from xml.dom.ext import PrettyPrint from cStringIO import StringIO from xml.sax.saxutils import escape, unescape import re, copy +import cStringIO +from Ft.Xml.Domlette import Print, PrettyPrint + from zLOG import LOG @@ -132,7 +133,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): return {'conflict_list':conflict_list, 'object':sub_object} LOG('addNode',0,'xml_reconstitued: %s' % str(xml)) # In the case where this new node is a object to add - if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and self.getSubObjectDepth(xml)==0: + if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and \ + self.getSubObjectDepth(xml)==0: if self.isHistoryAdd(xml)!=-1: # bad hack XXX to be removed for element in self.getXupdateElementList(xml): xml = self.getElementFromXupdate(element) @@ -160,7 +162,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): portal_type = self.getObjectType(xml) elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD: # Deprecated ??? portal_type = self.getXupdateObjectType(xml) # Deprecated ??? - sub_object = self.constructContent(object, object_id, docid, portal_type) + sub_object = self.constructContent(object, object_id, docid, + portal_type) self.newObject( object=sub_object, xml=xml, @@ -274,9 +277,9 @@ class ERP5Conduit(XMLSyncUtilsMixin): LOG('updateNode, force: ',0,force) # we have an xupdate xml if xml.nodeName == 'xupdate:modifications': - conflict_list += self.applyXupdate(object=object,xupdate=xml,conduit=self, - previous_xml=previous_xml, force=force, simulate=simulate, - **kw) + conflict_list += self.applyXupdate(object=object, xupdate=xml, + conduit=self,previous_xml=previous_xml, force=force, + simulate=simulate, **kw) # we may have only the part of an xupdate else: args = {} @@ -342,7 +345,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): # - current_data : the data actually on this box isConflict = 0 if (previous_xml is not None) and (not force): # if no previous_xml, no conflict - old_data = self.getObjectProperty(keyword,previous_xml,data_type=data_type) + old_data = self.getObjectProperty(keyword, previous_xml, + data_type=data_type) #current_data = object.getProperty(keyword) current_data = self.getProperty(object, keyword) LOG('updateNode',0,'Conflict data: %s' % str(data)) @@ -383,13 +387,13 @@ class ERP5Conduit(XMLSyncUtilsMixin): if keyword == 'object': # This is the case where we have to call addNode LOG('updateNode',0,'we will add sub-object') - conflict_list += self.addNode(xml=xml,object=object,force=force, - simulate=simulate, **kw)['conflict_list'] + conflict_list += self.addNode(xml=xml, object=object, force=force, + simulate=simulate, **kw)['conflict_list'] elif keyword == self.history_tag and not simulate: # This is the case where we have to call addNode LOG('updateNode',0,'we will add history') conflict_list += self.addNode(xml=subnode,object=object,force=force, - simulate=simulate,**kw)['conflict_list'] + simulate=simulate,**kw)['conflict_list'] elif keyword in (self.local_role_tag,self.local_permission_tag) and not simulate: # This is the case where we have to update Roles or update permission LOG('updateNode',0,'we will add a local role') @@ -397,8 +401,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): #roles = self.convertXmlValue(data,data_type='tokens') #object.manage_setLocalRoles(user,roles) xml = self.getElementFromXupdate(xml) - conflict_list += self.addNode(xml=xml,object=object,force=force, - simulate=simulate,**kw)['conflict_list'] + conflict_list += self.addNode(xml=xml, object=object, force=force, + simulate=simulate,**kw)['conflict_list'] elif self.isSubObjectModification(xml): # We should find the object corresponding to # this update, so we have to look in the previous_xml @@ -421,8 +425,21 @@ class ERP5Conduit(XMLSyncUtilsMixin): sub_xml = self.getSubObjectXupdate(xml) LOG('updateNode',0,'sub_xml: %s' % str(sub_xml)) # Then do the udpate - conflict_list += self.updateNode(xml=sub_xml,object=sub_object, force=force, - previous_xml=sub_previous_xml,simulate=simulate, **kw) + conflict_list += self.updateNode(xml=sub_xml, object=sub_object, force=force, + previous_xml=sub_previous_xml, simulate=simulate, **kw) + elif previous_xml is None and xml is not None and sub_object_id is not None: + sub_object = None + try: + sub_object = object[sub_object_id] + except KeyError: + pass + if sub_object is not None: + sub_xml = self.getSubObjectXupdate(xml) + conflict_list += self.updateNode(xml=sub_xml, + object=sub_object, + force=force, + simulate=simulate, + **kw) elif previous_xml is None and xml is not None and sub_object_id is not None: sub_object = None try: @@ -438,7 +455,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): **kw) return conflict_list - security.declareProtected(Permissions.AccessContentsInformation,'getFormatedArgs') + security.declareProtected(Permissions.AccessContentsInformation, + 'getFormatedArgs') def getFormatedArgs(self, args=None): """ This lookd inside the args dictionnary and then @@ -484,19 +502,21 @@ class ERP5Conduit(XMLSyncUtilsMixin): return 0 return 1 - security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectXupdate') + security.declareProtected(Permissions.AccessContentsInformation, + 'getSubObjectXupdate') def getSubObjectXupdate(self, xml): """ This will change the xml in order to change the update from the object to the subobject """ - xml = copy.deepcopy(xml) - for subnode in self.getAttributeNodeList(xml): + xml_copy = xml.cloneNode(True) #make a deepcopy of the node xml + for subnode in self.getAttributeNodeList(xml_copy): if subnode.nodeName=='select': subnode.nodeValue = self.getSubObjectSelect(subnode.nodeValue) - return xml + return xml_copy - security.declareProtected(Permissions.AccessContentsInformation,'isHistoryAdd') + security.declareProtected(Permissions.AccessContentsInformation, + 'isHistoryAdd') def isHistoryAdd(self, xml): bad_list = (self.history_exp,) for subnode in self.getAttributeNodeList(xml): @@ -510,7 +530,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): return -1 return 0 - security.declareProtected(Permissions.AccessContentsInformation,'isSubObjectModification') + security.declareProtected(Permissions.AccessContentsInformation, + 'isSubObjectModification') def isSubObjectModification(self, xml): """ Check if it is a modification from an subobject @@ -524,7 +545,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): return 1 return 0 - security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectDepth') + security.declareProtected(Permissions.AccessContentsInformation, + 'getSubObjectDepth') def getSubObjectDepth(self, xml): """ Give the Depth of a subobject modification @@ -555,7 +577,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): return (1 - i) return 0 - security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectSelect') + security.declareProtected(Permissions.AccessContentsInformation, + 'getSubObjectSelect') def getSubObjectSelect(self, select): """ Return a string wich is the selection for the subobject @@ -571,7 +594,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): select = new_value return select - security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectId') + security.declareProtected(Permissions.AccessContentsInformation, + 'getSubObjectId') def getSubObjectId(self, xml): """ Return the id of the subobject in an xupdate modification @@ -587,7 +611,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): return object_id return object_id - security.declareProtected(Permissions.AccessContentsInformation,'getHistoryIdFromSelect') + security.declareProtected(Permissions.AccessContentsInformation, + 'getHistoryIdFromSelect') def getHistoryIdFromSelect(self, xml): """ Return the id of the subobject in an xupdate modification @@ -604,7 +629,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): return object_id return object_id - security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectXml') + security.declareProtected(Permissions.AccessContentsInformation, + 'getSubObjectXml') def getSubObjectXml(self, object_id, xml): """ Return the xml of the subobject which as the id object_id @@ -624,7 +650,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ for attribute in self.getAttributeNodeList(xml): if attribute.nodeName == param: - data = attribute.childNodes[0].data + data = attribute.value return self.convertXmlValue(data,data_type='string') return None @@ -635,7 +661,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ for subnode in self.getElementNodeList(xml): if subnode.nodeName == 'docid': - data = subnode.childNodes[0].data + data = subnode.childNodes[0].value return self.convertXmlValue(data) return None @@ -669,7 +695,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): LOG('Conduit.convertToXml xml',0,repr(xml)) if type(xml) is type(u'a'): xml = xml.encode('utf-8') - xml = parseString(xml) + xml = Parse(xml) LOG('Conduit.convertToXml not failed',0,'ok') xml = xml.childNodes[0] # Because we just created a new xml # If we have the xml from the node erp5, we just take the subnode @@ -811,7 +837,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): #PrettyPrint(xml,xml_string) #xml_string = xml_string.getvalue() #xml_string = unicode(xml_string,encoding='utf-8') - xml_string = xml.toxml(encoding='utf-8') + xml_string = self.nodeToString(xml) xml_string = unicode(xml_string,encoding='utf-8') #if type(xml_string) is type (u'a'): # xml_string = xml_string.encode('utf-8') @@ -840,12 +866,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): if select_id is not None: result += ' id=%s' % select_id result += '>' - # Then dumps the xml and remove what we does'nt want - #xml_string = StringIO() - #PrettyPrint(xml,xml_string) - #xml_string = xml_string.getvalue() - #xml_string = unicode(xml_string,encoding='utf-8') - xml_string = xml.toxml(encoding='utf-8') + xml_string = self.nodeToString(xml) xml_string = unicode(xml_string,encoding='utf-8') maxi = xml_string.find('>')+1 result += xml_string[maxi:xml_string.find('</%s>' % xml.nodeName)] @@ -929,7 +950,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ conflict_list = [] if type(xupdate) in (type('a'),type(u'a')): - xupdate = parseString(xupdate) + xupdate = Parse(xupdate) #When xupdate mix different object, (like object and his subobject) we need to treat them separatly if self.isMixedXupdate(xupdate): #return to updateNode with only one line @@ -967,7 +988,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): nb_sub = len(subnode_list) comp = 0 for subnode in subnode_list: - value = subnode.getAttribute('select') + value = self.getAttribute(subnode, 'select') if re.search(self.object_exp, value): comp += 1 if nb_sub == comp: @@ -1043,7 +1064,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): if wf_id is None: # History added by xupdate wf_id = self.getHistoryIdFromSelect(xml) LOG('addNode, workflow_history id:',0,wf_id) - LOG('addNode, workflow_history xml:',0,xml.toxml()) + #LOG('addNode, workflow_history xml:',0,xml.toxml())#toxml isn't in 4Suite LOG('addNode, workflow_history xml.getElmentNodeList:',0,self.getElementNodeList(xml)) xml = self.getElementNodeList(xml)[0] LOG('addNode, workflow_history id:',0,wf_id) @@ -1152,3 +1173,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): # conflict.setRemoteValue(status) # conflict_list += [conflict] # return conflict_list + + def nodeToString(self, node): + """ + return an xml string corresponding to the node + """ + buf = cStringIO.StringIO() + Print(node, stream=buf, encoding='utf-8') + xml_string = buf.getvalue() + buf.close() + return xml_string + diff --git a/product/ERP5SyncML/Publication.py b/product/ERP5SyncML/Publication.py index 4ba43575c994c5adbb4eec9140f93eed6dd9e180..57657883762914372cd089bb619aad0f9ff214c8 100644 --- a/product/ERP5SyncML/Publication.py +++ b/product/ERP5SyncML/Publication.py @@ -153,8 +153,9 @@ class Publication(Subscription): # Constructor def __init__(self, id, title, publication_url, destination_path, - query, xml_mapping, conduit, gpg_key, auth_required=False, - authentication_format='', authentication_type=''): + query, xml_mapping, conduit, gpg_key, id_generator, gid_generator, + flow_type, auth_required=False, authentication_format='', + authentication_type=''): """ constructor """ @@ -166,8 +167,9 @@ class Publication(Subscription): #self.list_subscribers = PersistentMapping() self.domain_type = self.PUB self.gpg_key = gpg_key - self.setGidGenerator(None) - self.setIdGenerator(None) + self.setGidGenerator(gid_generator) + self.setFlowType(flow_type) + self.setSynchronizationIdGenerator(id_generator) self.setConduit(conduit) Folder.__init__(self, id) self.title = title diff --git a/product/ERP5SyncML/PublicationSynchronization.py b/product/ERP5SyncML/PublicationSynchronization.py index 444d4200319c97171060144f71f2c2dfc65fa43b..60cb1e274de3ff941f614c82f74a37330fc79f7a 100644 --- a/product/ERP5SyncML/PublicationSynchronization.py +++ b/product/ERP5SyncML/PublicationSynchronization.py @@ -29,8 +29,7 @@ import smtplib # to send emails from Publication import Publication,Subscriber from Subscription import Signature -from xml.dom.ext.reader.Sax2 import FromXmlStream, FromXml -from xml.dom.minidom import parse, parseString +from Ft.Xml import Parse from XMLSyncUtils import XMLSyncUtils from Conduit.ERP5Conduit import ERP5Conduit from Products.CMFCore.utils import getToolByName @@ -142,6 +141,13 @@ class PublicationSynchronization(XMLSyncUtils): xml(' <SyncBody>\n') xml(self.SyncMLStatus(cmd_id, subscriber.getSubscriptionUrl(), publication.getPublicationUrl(), auth_code)) + cmd_id += 1 + if auth_code == self.AUTH_ACCEPTED: + # alert message + xml(self.SyncMLAlert(cmd_id, sync_type, + subscriber.getSubscriptionUrl(), publication.getPublicationUrl(), + subscriber.getLastAnchor(), subscriber.getNextAnchor())) + cmd_id += 1 xml(' </SyncBody>\n') xml('</SyncML>\n') xml_a = ''.join(xml_list) @@ -221,7 +227,7 @@ class PublicationSynchronization(XMLSyncUtils): if xml_client is not None: if isinstance(xml_client, str) or isinstance(xml_client, unicode): - xml_client = parseString(xml_client) + xml_client = Parse(xml_client) first_node = xml_client.childNodes[0] if first_node.nodeName != "SyncML": diff --git a/product/ERP5SyncML/Subscription.py b/product/ERP5SyncML/Subscription.py index 5cae6b5435b08065c3cdcdfb39e017550f23ad99..fb2a94da8f12fe51f32de7708d45c8df81aa8363 100644 --- a/product/ERP5SyncML/Subscription.py +++ b/product/ERP5SyncML/Subscription.py @@ -642,8 +642,9 @@ class Subscription(Folder, SyncCode): ) # Constructor - def __init__(self, id, title, publication_url, subscription_url, destination_path, query, xml_mapping, conduit, gpg_key, login, password, - authentication_format='', authentication_type=''): + def __init__(self, id, title, publication_url, subscription_url, + destination_path, query, xml_mapping, conduit, gpg_key, id_generator, + gid_generator, flow_type, login, password): """ We need to create a dictionnary of signatures of documents which belong to the synchronisation @@ -660,14 +661,13 @@ class Subscription(Folder, SyncCode): #self.signatures = PersistentMapping() self.last_anchor = '00000000T000000Z' self.next_anchor = '00000000T000000Z' - self.login=login + self.flow_type = flow_type + self.login = login self.password=password - self.authentication_format=authentication_format - self.authentication_type=authentication_type self.domain_type = self.SUB self.gpg_key = gpg_key - self.setGidGenerator(None) - self.setIdGenerator(None) + self.setGidGenerator(gid_generator) + self.setSynchronizationIdGenerator(id_generator) self.setConduit(conduit) Folder.__init__(self, id) self.title = title @@ -715,7 +715,7 @@ class Subscription(Folder, SyncCode): def setXMLMapping(self, value): """ - this the name of the method used in order to get the xml + this the name of the method used in order to set the xml """ if value == '': value = None @@ -884,6 +884,20 @@ class Subscription(Folder, SyncCode): """ return self.gid_generator + def getFlowType(self): + """ + This method return the type of the data within the xml message : + - text for VCard's + - xml for others + """ + return getattr(self, 'flow_type', 'xml') + + def setFlowType(self, flow_type): + """ + set the flow type (xml or text) + """ + self.flow_type=flow_type + def getLogin(self): """ This method return the login of this subscription @@ -954,7 +968,7 @@ class Subscription(Folder, SyncCode): # It might be a script python generator = getattr(object,gid_gen) o_gid = generator() # XXX - used to be o_gid = generator(object=object) which is redundant - # LOG('getGidFromObject',0,'o_gid: %s' % repr(o_gid)) + LOG('getGidFromObject',0,'o_gid: %s' % repr(o_gid)) return o_gid def getObjectFromGid(self, gid): @@ -1018,10 +1032,7 @@ class Subscription(Folder, SyncCode): """ This tries to generate a new Id """ - # LOG('generateNewId, object: ',0,object.getPhysicalPath()) - id_generator = self.getIdGenerator() - # LOG('generateNewId, id_generator: ',0,id_generator) - # LOG('generateNewId, portal_object: ',0,object.getPortalObject()) + id_generator = self.getSynchronizationIdGenerator() if id_generator is not None: o_base = aq_base(object) new_id = None @@ -1038,20 +1049,20 @@ class Subscription(Folder, SyncCode): return new_id return None - def setIdGenerator(self, method): + def setSynchronizationIdGenerator(self, method): """ This set the method name wich allows to generate a new id """ if method in ('','None'): method = None - self.id_generator = method + self.synchronization_id_generator = method - def getIdGenerator(self): + def getSynchronizationIdGenerator(self): """ This get the method name wich allows to generate a new id """ - return self.id_generator + return getattr(self, 'synchronization_id_generator', None) def getSubscriptionUrl(self): """ @@ -1185,7 +1196,7 @@ class Subscription(Folder, SyncCode): """ if signature.getGid() in self.objectIds(): self._delObject(signature.getGid()) - self._setObject( signature.getGid(), aq_base(signature) ) + self._setObject(signature.getGid(), aq_base(signature) ) def delSignature(self, gid): """ @@ -1321,7 +1332,7 @@ class Subscription(Folder, SyncCode): #elif format is .... put here the other formats else:#if there is no format corresponding with format, raise an error LOG('encode : unknown or not implemented format :', 0, format) - raise ValueError, "Sorry, the format %s is unknow or not implemented" % format + raise ValueError, "Sorry, the server ask for the format %s but it's unknow or not implemented" % format def decode(self, format, string_to_decode): """ diff --git a/product/ERP5SyncML/SubscriptionSynchronization.py b/product/ERP5SyncML/SubscriptionSynchronization.py index ae05fc9c99a502c665254f6a6db8d136f086678a..dbeec952a471b33c066b0e335ef57f358d4dcd98 100644 --- a/product/ERP5SyncML/SubscriptionSynchronization.py +++ b/product/ERP5SyncML/SubscriptionSynchronization.py @@ -28,9 +28,7 @@ import smtplib # to send emails from Subscription import Subscription,Signature -from xml.dom.ext.reader.Sax2 import FromXmlStream, FromXml -from xml.dom.minidom import parse, parseString -from xml.dom.ext import PrettyPrint +from Ft.Xml import Parse from XMLSyncUtils import XMLSyncUtils import commands from Conduit.ERP5Conduit import ERP5Conduit @@ -99,13 +97,21 @@ class SubscriptionSynchronization(XMLSyncUtils): else: xml_client = msg if isinstance(xml_client, str) or isinstance(xml_client, unicode): - xml_client = parseString(xml_client) + xml_client = Parse(xml_client) next_status = self.getNextSyncBodyStatus(xml_client, None) #LOG('readResponse, next status :',0,next_status) if next_status is not None: status_code = self.getStatusCode(next_status) #LOG('readResponse status code :',0,status_code) if status_code == self.AUTH_REQUIRED: + if self.checkChal(xml_client): + authentication_format, authentication_type = self.getChal(xml_client) + subscription.setAuthenticationFormat(authentication_format) + subscription.setAuthenticationType(authentication_type) + else: + raise ValueError, "Sorry, the server chalenge for an \ + authentication, but the authentication format is not find" + #LOG('readResponse', 0, 'Authentication required') response = self.SubSyncCred(id, xml_client) elif status_code == self.UNAUTHORIZED: diff --git a/product/ERP5SyncML/SynchronizationTool.py b/product/ERP5SyncML/SynchronizationTool.py index 6bb230a57d5e5bdb8534d2412c67fb9f7627022e..1cf90548054f67b6aa49cfc79a171cc7d96a87e8 100644 --- a/product/ERP5SyncML/SynchronizationTool.py +++ b/product/ERP5SyncML/SynchronizationTool.py @@ -40,8 +40,7 @@ from Products.ERP5SyncML import Conduit from Publication import Publication,Subscriber from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2 from Subscription import Subscription,Signature -from xml.dom.ext.reader.Sax2 import FromXmlStream, FromXml -from xml.dom.minidom import parse, parseString +from Ft.Xml import Parse from Products.ERP5Type import Permissions from PublicationSynchronization import PublicationSynchronization from SubscriptionSynchronization import SubscriptionSynchronization @@ -50,7 +49,6 @@ from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import noSecurityManager from AccessControl.User import UnrestrictedUser from Acquisition import aq_base -from xml.parsers.expat import ExpatError # parseString error import urllib import urllib2 import socket @@ -162,9 +160,11 @@ class SynchronizationTool( SubscriptionSynchronization, security.declareProtected(Permissions.ModifyPortalContent, 'manage_addPublication') def manage_addPublication(self, title, publication_url, destination_path, - query, xml_mapping, conduit, gpg_key, auth_required=0, - authentication_format='', authentication_type='', RESPONSE=None): - """ + query, xml_mapping, conduit, gpg_key, + synchronization_id_generator=None, gid_generator=None, + flow_type='xml', auth_required=0, authentication_format='', + authentication_type='', RESPONSE=None): + """ create a new publication """ #if not('publications' in self.objectIds()): @@ -173,8 +173,9 @@ class SynchronizationTool( SubscriptionSynchronization, folder = self.getObjectContainer() new_id = self.getPublicationIdFromTitle(title) pub = Publication(new_id, title, publication_url, destination_path, - query, xml_mapping, conduit, gpg_key, auth_required, - authentication_format, authentication_type) + query, xml_mapping, conduit, gpg_key, + synchronization_id_generator, gid_generator, flow_type, + auth_required, authentication_format, authentication_type) folder._setObject( new_id, pub ) #if len(self.list_publications) == 0: # self.list_publications = PersistentMapping() @@ -186,8 +187,9 @@ class SynchronizationTool( SubscriptionSynchronization, 'manage_addSubscription') def manage_addSubscription(self, title, publication_url, subscription_url, destination_path, query, xml_mapping, conduit, gpg_key, - login=None, password=None, authentication_format='', - authentication_type='',RESPONSE=None): + synchronization_id_generator=None, gid_generator=None, + flow_type='xml', login=None, password=None, + RESPONSE=None): """ XXX should be renamed as addSubscription create a new subscription @@ -199,8 +201,8 @@ class SynchronizationTool( SubscriptionSynchronization, new_id = self.getSubscriptionIdFromTitle(title) sub = Subscription(new_id, title, publication_url, subscription_url, destination_path, query, xml_mapping, conduit, gpg_key, - login, password, authentication_format, - authentication_type) + synchronization_id_generator, gid_generator, flow_type, + login, password) folder._setObject( new_id, sub ) #if len(self.list_subscriptions) == 0: # self.list_subscriptions = PersistentMapping() @@ -211,9 +213,11 @@ class SynchronizationTool( SubscriptionSynchronization, security.declareProtected(Permissions.ModifyPortalContent, 'manage_editPublication') def manage_editPublication(self, title, publication_url, destination_path, - query, xml_mapping, conduit, gpg_key, id_generator, - gid_generator, auth_required=0, authentication_format='', - authentication_type='', RESPONSE=None): + query, xml_mapping, conduit, gpg_key, + synchronization_id_generator, gid_generator, + flow_type='xml', auth_required=0, + authentication_format='', authentication_type='', + RESPONSE=None): """ modify a publication """ @@ -225,8 +229,9 @@ class SynchronizationTool( SubscriptionSynchronization, pub.setConduit(conduit) pub.setXMLMapping(xml_mapping) pub.setGPGKey(gpg_key) - pub.setIdGenerator(id_generator) + pub.setSynchronizationIdGenerator(synchronization_id_generator) pub.setGidGenerator(gid_generator) + pub.setFlowType(flow_type) pub.setAuthentication(auth_required) pub.setAuthenticationFormat(authentication_format) pub.setAuthenticationType(authentication_type) @@ -237,9 +242,9 @@ class SynchronizationTool( SubscriptionSynchronization, security.declareProtected(Permissions.ModifyPortalContent, 'manage_editSubscription') def manage_editSubscription(self, title, publication_url, subscription_url, - destination_path, query, xml_mapping, conduit, gpg_key, id_generator, - gid_generator,login='', password='', authentication_format='', - authentication_type='', RESPONSE=None): + destination_path, query, xml_mapping, conduit, gpg_key, + synchronization_id_generator, gid_generator, flow_type='xml', login='', + password='', RESPONSE=None): """ modify a subscription """ @@ -252,12 +257,11 @@ class SynchronizationTool( SubscriptionSynchronization, sub.setXMLMapping(xml_mapping) sub.setGPGKey(gpg_key) sub.setSubscriptionUrl(subscription_url) - sub.setIdGenerator(id_generator) + sub.setSynchronizationIdGenerator(synchronization_id_generator) sub.setGidGenerator(gid_generator) + sub.setFlowType(flow_type) sub.setLogin(login) sub.setPassword(password) - sub.setAuthenticationFormat(authentication_format) - sub.setAuthenticationType(authentication_type) if RESPONSE is not None: RESPONSE.redirect('manageSubscriptions') @@ -613,6 +617,7 @@ class SynchronizationTool( SubscriptionSynchronization, security.declareProtected(Permissions.AccessContentsInformation, 'getSubscriberDocumentPath') + def getSubscriberDocumentPath(self, conflict): """ apply the publisher value for all conflict of the given document @@ -974,7 +979,7 @@ class SynchronizationTool( SubscriptionSynchronization, commands.getstatusoutput('rm -f /tmp/%s.gz.gpg' % filename) # Get the target and then find the corresponding publication or # Subscription - xml = parseString(text) + xml = Parse(text) #XXX this function is not very optimized and should be improved url = self.getTarget(xml) for publication in self.getPublicationList(): diff --git a/product/ERP5SyncML/XMLSyncUtils.py b/product/ERP5SyncML/XMLSyncUtils.py index bad3c0ff677b3170084aa173c8fa6db725b48645..7a0b3ce52004ec65471b68658b6a20281531499a 100644 --- a/product/ERP5SyncML/XMLSyncUtils.py +++ b/product/ERP5SyncML/XMLSyncUtils.py @@ -29,8 +29,7 @@ import smtplib from Products.ERP5SyncML.SyncCode import SyncCode from Products.ERP5SyncML.Subscription import Signature -from xml.dom.ext.reader.Sax2 import FromXml -from xml.dom.minidom import parse, parseString +from Ft.Xml import Parse from DateTime import DateTime from cStringIO import StringIO from xml.dom.ext import PrettyPrint @@ -200,7 +199,9 @@ class XMLSyncUtilsMixin(SyncCode): xml = xml_list.append xml(' <Add>\n') xml(' <CmdID>%s</CmdID>\n' % cmd_id) - xml(' <Meta><Type>%s</Type></Meta>\n' % object.portal_type) + xml(' <Meta>\n') + xml(' <Type>%s</Type>\n' % object.portal_type) + xml(' </Meta>\n') xml(' <Item>\n') xml(' <Source>\n') xml(' <LocURI>%s</LocURI>\n' % gid) @@ -227,8 +228,8 @@ class XMLSyncUtilsMixin(SyncCode): xml(' <Source>\n') xml(' <LocURI>%s</LocURI>\n' % object_gid) xml(' </Source>\n') - xml(' <Data>\n') - xml(' </Data>\n') + #xml(' <Data>\n') #this 2 lines seems to be useless + #xml(' </Data>\n') xml(' </Item>\n') xml(' </Delete>\n') xml_a = ''.join(xml_list) @@ -243,7 +244,9 @@ class XMLSyncUtilsMixin(SyncCode): xml = xml_list.append xml(' <Replace>\n') xml(' <CmdID>%s</CmdID>\n' % cmd_id) - xml(' <Meta><Type>%s</Type></Meta>\n' % object.portal_type) + xml(' <Meta>\n') + xml(' <Type>%s</Type>\n' % object.portal_type) + xml(' </Meta>\n') xml(' <Item>\n') xml(' <Source>\n') xml(' <LocURI>%s</LocURI>\n' % str(gid)) @@ -290,10 +293,13 @@ class XMLSyncUtilsMixin(SyncCode): if xml_mapping is not None: if hasattr(object,xml_mapping): xml_method = getattr(object,xml_mapping) - elif hasattr(object,'manage_FTPget'): - xml_method = getattr(object,'manage_FTPget') + #elif hasattr(object,'manage_FTPget'): + # xml_method = getattr(object,'manage_FTPget') if xml_method is not None: xml = xml_method() + else: + raise ValueError, "Sorry the script or method was not found" + LOG('getXMLObject', 0, 'xml_mapping:%s, xml:%s, object:%s xml_method:%s' % (xml_mapping, xml, object, xml_method)) return xml def getSessionId(self, xml): @@ -301,28 +307,17 @@ class XMLSyncUtilsMixin(SyncCode): We will retrieve the session id of the message """ session_id = 0 - for subnode in self.getElementNodeList(xml): - if subnode.nodeName == 'SyncML': - for subnode1 in self.getElementNodeList(subnode): - if subnode1.nodeName == 'SyncHdr': - for subnode2 in self.getElementNodeList(subnode1): - if subnode2.nodeName == 'SessionID': - session_id = int(subnode2.childNodes[0].data) + session_id = xml.xpath('string(/SyncML/SyncHdr/SessionID)') + session_id = int(session_id) return session_id - def getMessageId(self, xml): """ We will retrieve the message id of the message """ message_id = 0 - for subnode in self.getElementNodeList(xml): - if subnode.nodeName == 'SyncML': - for subnode1 in self.getElementNodeList(subnode): - if subnode1.nodeName == 'SyncHdr': - for subnode2 in self.getElementNodeList(subnode1): - if subnode2.nodeName == 'MsgID': - message_id = int(subnode2.childNodes[0].data) + message_id = xml.xpath('string(/SyncML/SyncHdr/MsgID)') + message_id = int(message_id) return message_id def getTarget(self, xml): @@ -330,18 +325,10 @@ class XMLSyncUtilsMixin(SyncCode): return the target in the SyncHdr section """ url = '' - for subnode in self.getElementNodeList(xml): - if subnode.nodeName == 'SyncML': - for subnode1 in self.getElementNodeList(subnode): - if subnode1.nodeName == 'SyncHdr': - for subnode2 in self.getElementNodeList(subnode1): - if subnode2.nodeName == 'Target': - for subnode3 in self.getElementNodeList(subnode2): - if subnode3.nodeName == 'LocURI': - url = subnode3.childNodes[0].data + url = xml.xpath('string(/SyncML/SyncHdr/Target/LocURI)') + url = url.encode('utf-8') return url - def getAlertLastAnchor(self, xml_stream): """ Return the value of the last anchor, in the @@ -351,23 +338,8 @@ class XMLSyncUtilsMixin(SyncCode): # Get informations from the body client_body = first_node.childNodes[3] - for subnode in client_body.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and \ - subnode.nodeName == "Alert": - for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and \ - subnode2.nodeName == "Item": - for subnode3 in subnode2.childNodes: - if subnode3.nodeType == subnode3.ELEMENT_NODE and \ - subnode3.nodeName == "Meta": - for subnode4 in subnode3.childNodes: - if subnode4.nodeType == subnode4.ELEMENT_NODE and \ - subnode4.nodeName == "Anchor": - for subnode5 in subnode4.childNodes: - # Get the last time we had a synchronization - if subnode5.nodeType == subnode5.ELEMENT_NODE and \ - subnode5.nodeName == "Last": - last_anchor = subnode5.childNodes[0].data + last_anchor = client_body.xpath('string(/Alert/Item/Meta/Anchor/Last)') + last_anchor = last_anchor.encode('utf-8') return last_anchor def getAlertNextAnchor(self, xml_stream): @@ -381,61 +353,33 @@ class XMLSyncUtilsMixin(SyncCode): # Get informations from the body client_body = first_node.childNodes[3] - if client_body.nodeName != "SyncBody": - print "This is not a SyncML Body" - for subnode in client_body.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and \ - subnode.nodeName == "Alert": - for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and \ - subnode2.nodeName == "Item": - for subnode3 in subnode2.childNodes: - if subnode3.nodeType == subnode3.ELEMENT_NODE and \ - subnode3.nodeName == "Meta": - for subnode4 in subnode3.childNodes: - if subnode4.nodeType == subnode4.ELEMENT_NODE and \ - subnode4.nodeName == "Anchor": - for subnode5 in subnode4.childNodes: - # Get the last time we had a synchronization - if subnode5.nodeType == subnode5.ELEMENT_NODE and \ - subnode5.nodeName == "Next": - next_anchor = subnode5.childNodes[0].data - return next_anchor + next_anchor = client_body.xpath('string(/Alert/Item/Meta/Anchor/Next)') + next_anchor = next_anchor.encode('utf-8') + return next_anchor def getStatusTarget(self, xml): """ Return the value of the alert code inside the xml_stream """ - # Get informations from the body - if xml.nodeName=='Status': - for subnode in xml.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and \ - subnode.nodeName == 'TargetRef': - return subnode.childNodes[0].data - return None + return xml.xpath('string(TargetRef)') def getStatusCode(self, xml): """ Return the value of the alert code inside the xml_stream """ - # Get informations from the body - if xml.nodeName=='Status': - for subnode in xml.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and \ - subnode.nodeName == 'Data': - return int(subnode.childNodes[0].data) + status_code = xml.xpath('string(Data)') + if status_code not in ('', None, []): + return int(status_code) return None def getStatusCommand(self, xml): """ Return the value of the command inside the xml_stream """ - # Get informations from the body if xml.nodeName=='Status': - for subnode in xml.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Cmd': - return subnode.childNodes[0].data - return None + return xml.xpath('string(//Status/Cmd)') + else: + return None def getCred(self, xml): """ @@ -444,111 +388,84 @@ class XMLSyncUtilsMixin(SyncCode): format='' type='' data='' - first_node = xml.childNodes[0] - if first_node.nodeName != "SyncML": - print "This is not a SyncML message" - # 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') - raise ValueError, "Sorry, This is not a SyncML Header" + format = first_node.xpath('string(/SyncML/SyncHdr/Cred/Meta/Format)') + type = first_node.xpath('string(/SyncML/SyncHdr/Cred/Meta/Type)') + data = first_node.xpath('string(/SyncML/SyncHdr/Cred/Data)') - for subnode in xml_header.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName=='Cred': - for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and \ - subnode2.nodeName == 'Meta': - for subnode3 in subnode2.childNodes: - if subnode3.nodeType == subnode3.ELEMENT_NODE and \ - subnode3.nodeName == 'Format': - if len(subnode3.childNodes) > 0: - format=subnode3.childNodes[0].data - if subnode3.nodeType == subnode3.ELEMENT_NODE and \ - subnode3.nodeName == 'Type': - if len(subnode3.childNodes) > 0: - type=subnode3.childNodes[0].data - if subnode2.nodeType == subnode2.ELEMENT_NODE and \ - subnode2.nodeName == 'Data': - if len(subnode2.childNodes) > 0: - data=subnode2.childNodes[0].data + format = format.encode('utf-8') + type = type.encode('utf-8') + data = data.encode('utf-8') return (format, type, data) - def getAlertCode(self, xml_stream): + def checkCred(self, xml_stream): """ - Return the value of the alert code inside the full syncml message + Check if there's a Cred section in the xml_stream """ - # Get informations from the body - first_node = xml_stream.childNodes[0] - client_body = first_node.childNodes[3] - if client_body.nodeName != "SyncBody": - LOG('XMLSyncUtils.getAlertCode',0,"This is not a SyncML Body") - raise ValueError, "Sorry, This is not a SyncML Body" - alert = 0 - for subnode in client_body.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName=='Alert': - for subnode1 in subnode.childNodes: - if subnode1.nodeType == subnode1.ELEMENT_NODE and subnode1.nodeName == 'Data': - return int(subnode1.childNodes[0].data) - return None + if xml_stream.xpath('string(SyncML/SyncHdr/Cred)') not in ('', None, []): + return True + return False - def checkCred(self, xml_stream): + def getChal(self, xml): """ - Check if there's a Cred section in the xml_stream + return the chalenge information : format and type + """ + format='' + type='' + + first_node = xml.childNodes[0] + format = first_node.xpath('string(/SyncML/SyncBody/Status/Chal/Meta/Format)') + type = first_node.xpath('string(/SyncML/SyncBody/Status/Chal/Meta/Type)') + + format = format.encode('utf-8') + type = type.encode('utf-8') + return (format, type) + + def checkChal(self, xml_stream): """ - first_node = xml_stream.childNodes[0] - # 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') - raise ValueError, "Sorry, This is not a SyncML Header" - cred = 0 - for subnode in xml_header.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Cred": - cred=1 - return cred + Check if there's a Chal section in the xml_stream + """ + if xml_stream.xpath('string(SyncML/SyncBody/Status/Chal)') \ + not in ('', None, []): + return True + return False + + def getAlertCode(self, xml_stream): + """ + Return the value of the alert code inside the full syncml message + """ + alert_code = xml_stream.xpath('string(SyncML/SyncBody/Alert/Data)') + if alert_code not in (None, ''): + return int(alert_code) + else: + return None def checkAlert(self, xml_stream): """ Check if there's an Alert section in the xml_stream """ - first_node = xml_stream.childNodes[0] - client_body = first_node.childNodes[3] - if client_body.nodeName != "SyncBody": - print "This is not a SyncML Body" - alert = 0 - for subnode in client_body.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Alert": - alert = 1 + alert = False + if xml_stream.xpath('string(SyncML/SyncBody/Alert)') not in ('', None, []): + alert = True return alert def checkSync(self, xml_stream): """ Check if there's an Sync section in the xml_xtream """ - first_node = xml_stream.childNodes[0] - client_body = first_node.childNodes[3] - if client_body.nodeName != "SyncBody": - LOG('checkSync',0,"This is not a SyncML Body") - raise ValueError, "Sorry, This is not a SyncML Body" - for subnode in client_body.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Sync": - return 1 - return 0 + sync = False + if xml_stream.xpath('string(SyncML/SyncBody/Sync)') not in ('', None, []): + sync = True + return sync def CheckStatus(self, xml_stream): """ Check if there's a Status section in the xml_xtream """ - first_node = xml_stream.childNodes[0] - client_body = first_node.childNodes[3] - if client_body.nodeName != "SyncBody": - print "This is not a SyncML Body" - status = None - for subnode in client_body.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Status": - status = "1" + status = False + if xml_stream.xpath('string(SyncML/SyncBody/Status)') not in ('', None, []): + status = True return status def getNextSyncAction(self, xml_stream, last_action): @@ -563,18 +480,22 @@ class XMLSyncUtilsMixin(SyncCode): print "This is not a SyncML Body" next_action = None for subnode in client_body.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Sync": + if subnode.nodeType == subnode.ELEMENT_NODE and \ + subnode.nodeName == "Sync": # if we didn't use this method before if last_action == None and len(subnode.childNodes) > 1: next_action = subnode.childNodes[1] else: found = None for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode.ELEMENT_NODE and subnode2 != last_action and found is None: + if subnode2.nodeType == subnode.ELEMENT_NODE and \ + subnode2 != last_action and found is None: pass - elif subnode2.nodeType == subnode.ELEMENT_NODE and subnode2 == last_action and found is None: + elif subnode2.nodeType == subnode.ELEMENT_NODE and \ + subnode2 == last_action and found is None: found = 1 - elif subnode2.nodeType == subnode.ELEMENT_NODE and found is not None: + elif subnode2.nodeType == subnode.ELEMENT_NODE and \ + found is not None: next_action = subnode2 break return next_action @@ -593,7 +514,8 @@ class XMLSyncUtilsMixin(SyncCode): next_status = None found = None for subnode in client_body.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Status": + if subnode.nodeType == subnode.ELEMENT_NODE and \ + subnode.nodeName == "Status": # if we didn't use this method before if last_status == None: next_status = subnode @@ -604,27 +526,32 @@ class XMLSyncUtilsMixin(SyncCode): return subnode return next_status + def getDataText(self, action): + """ + return the section data in text form, it's usefull for the VCardConduit + """ + return action.xpath('string(Item/Data)') + def getDataSubNode(self, action): """ Return the node starting with <object....> of the action """ - for subnode in action.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Item': - for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and subnode2.nodeName == 'Data': - for subnode3 in subnode2.childNodes: - #if subnode3.nodeType == subnode3.ELEMENT_NODE and subnode3.nodeName == 'object': - if subnode3.nodeType == subnode3.ELEMENT_NODE: - return subnode3 + if action.xpath('Item/Data') not in ([], None): + data_node = action.xpath('Item/Data')[0] + if data_node.hasChildNodes() and data_node.childNodes.__len__()>1: + return data_node.childNodes[1] + return None def getPartialData(self, action): """ Return the node starting with <object....> of the action """ for subnode in action.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Item': + if subnode.nodeType == subnode.ELEMENT_NODE and \ + subnode.nodeName == 'Item': for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and subnode2.nodeName == 'Data': + if subnode2.nodeType == subnode2.ELEMENT_NODE and \ + subnode2.nodeName == 'Data': for subnode3 in subnode2.childNodes: #if subnode3.nodeType == subnode3.ELEMENT_NODE and subnode3.nodeName == 'object': if subnode3.nodeType == subnode3.COMMENT_NODE: @@ -639,60 +566,40 @@ class XMLSyncUtilsMixin(SyncCode): return None - - def getAttributeNodeList(self, node): - """ - Return attributesNodes that are ElementNode XXX may be not needed at all - """ - subnode_list = [] - for subnode in node.attributes: - if subnode.nodeType == subnode.ATTRIBUTE_NODE: - subnode_list += [subnode] - return subnode_list - def getActionId(self, action): """ Return the rid of the object described by the action """ for subnode in action.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Item': + if subnode.nodeType == subnode.ELEMENT_NODE and \ + subnode.nodeName == 'Item': for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and subnode2.nodeName == 'Source': + if subnode2.nodeType == subnode2.ELEMENT_NODE and \ + subnode2.nodeName == 'Source': for subnode3 in subnode2.childNodes: - if subnode3.nodeType == subnode3.ELEMENT_NODE and subnode3.nodeName == 'LocURI': + if subnode3.nodeType == subnode3.ELEMENT_NODE and \ + subnode3.nodeName == 'LocURI': return str(subnode3.childNodes[0].data) def checkActionMoreData(self, action): """ Return the rid of the object described by the action """ - for subnode in action.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Item': - for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and subnode2.nodeName == 'MoreData': - return 1 - return 0 + if action.xpath('Item/MoreData') not in ([],None) : + return True + return False def getActionType(self, action): """ Return the type of the object described by the action """ - for subnode in action.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == 'Meta': - for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and subnode2.nodeName == 'Type': - return str(subnode2.childNodes[0].data) + return action.xpath('string(Meta/Type)') def getElementNodeList(self, node): """ Return childNodes that are ElementNode """ - #return node.getElementsByTagName('*') - subnode_list = [] - for subnode in node.childNodes or []: - if subnode.nodeType == subnode.ELEMENT_NODE: - subnode_list += [subnode] - return subnode_list + return node.xpath('*') def getTextNodeList(self, node): """ @@ -703,16 +610,12 @@ class XMLSyncUtilsMixin(SyncCode): if subnode.nodeType == subnode.TEXT_NODE: subnode_list += [subnode] return subnode_list - + def getAttributeNodeList(self, node): """ Return childNodes that are ElementNode """ - attribute_list = [] - for subnode in node.attributes.values() or []: - if subnode.nodeType == subnode.ATTRIBUTE_NODE: - attribute_list += [subnode] - return attribute_list + return node.xpath('@*') def getSyncMLData(self, domain=None,remote_xml=None,cmd_id=0, subscriber=None,destination_path=None, @@ -730,7 +633,6 @@ class XMLSyncUtilsMixin(SyncCode): if subscriber.getRemainingObjectPathList() is None: object_list = domain.getObjectList() object_path_list = map(lambda x: x.getPhysicalPath(),object_list) - LOG('getSyncMLData, object_path_list',0,object_path_list) subscriber.setRemainingObjectPathList(object_path_list) #object_gid = domain.getGidFromObject(object) @@ -744,10 +646,10 @@ class XMLSyncUtilsMixin(SyncCode): 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_gid=object_gid,cmd_id=cmd_id) + 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_gid=object_gid,cmd_id=cmd_id) local_gid_list = [] #for object in domain.getObjectList(): @@ -765,14 +667,16 @@ class XMLSyncUtilsMixin(SyncCode): #if gid_generator is not None: # object_gid = gid_generator() force = 0 - if syncml_data.count('\n') < self.MAX_LINES and not object.id.startswith('.'): # If not we have to cut + if syncml_data.count('\n') < self.MAX_LINES and not \ + object.id.startswith('.'): # If not we have to cut #LOG('getSyncMLData',0,'xml_mapping: %s' % str(domain.xml_mapping)) + #LOG('getSyncMLData', 0, 'FlowType: %s' % str(subscriber.getFlowType())) #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) - #LOG('getSyncMLData',0,'current object: %s' % str(object.getId())) + # Here we first check if the object was modified or not by looking at dates if signature is not None: signature.checkSynchronizationNeeded(object) @@ -781,11 +685,12 @@ class XMLSyncUtilsMixin(SyncCode): # For the case it was never synchronized, we have to send everything if signature is not None and signature.getXMLMapping()==None: pass - elif signature==None or (signature.getXML()==None and signature.getStatus()!=self.PARTIAL) or \ - self.getAlertCode(remote_xml)==self.SLOW_SYNC: + elif 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_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping) + xml_object = self.getXMLObject(object=object, + xml_mapping=domain.xml_mapping) xml_string = xml_object signature = Signature(gid=object_gid,id=object.getId(),object=object) signature.setTempXML(xml_object) @@ -806,14 +711,15 @@ class XMLSyncUtilsMixin(SyncCode): status =self.PARTIAL signature.setAction('Add') xml_string = '<!--' + short_string + '-->' - syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,gid=object_gid, - xml_string=xml_string, more_data=more_data) + 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 - xml_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping) + xml_object = self.getXMLObject(object=object, + xml_mapping=domain.xml_mapping) #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: @@ -842,8 +748,11 @@ class XMLSyncUtilsMixin(SyncCode): signature.setAction('Replace') xml_string = '<!--' + short_string + '-->' signature.setStatus(status) - syncml_data += self.replaceXMLObject(cmd_id=cmd_id,object=object,gid=object_gid, - xml_string=xml_string, more_data=more_data) + if(subscriber.getFlowType() == 'text'): + xml_string = self.getXMLObject(object=object, + xml_mapping=domain.xml_mapping) + 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) # Now we can apply the xupdate from the subscriber @@ -851,17 +760,17 @@ class XMLSyncUtilsMixin(SyncCode): #LOG('getSyncMLData subscriber_xupdate',0,subscriber_xupdate) if subscriber_xupdate is not None: old_xml = signature.getXML() - conduit.updateNode(xml=subscriber_xupdate, object=object, - previous_xml=old_xml,force=(domain.getDomainType==self.SUB), - simulate=0) - xml_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping) + conduit.updateNode(xml=subscriber_xupdate, object=object, + previous_xml=old_xml, force=(domain.getDomainType==self.SUB), + simulate=0) + xml_object = self.getXMLObject(object=object, + xml_mapping=domain.xml_mapping) signature.setTempXML(xml_object) if set_synchronized: # We have to do that after this previous update # We should not have this case when we are in CONFLICT_MERGE signature.setStatus(self.SYNCHRONIZED) elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN: # We have decided to apply the update - 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 @@ -889,12 +798,16 @@ class XMLSyncUtilsMixin(SyncCode): xml_string = xml_string.replace('--','@-@@-@') xml_string = '<!--' + xml_string + '-->' signature.setStatus(status) + if(subscriber.getFlowType() == 'text'): + xml_string = self.getXMLObject(object=object, + xml_mapping=domain.xml_mapping) + LOG('xml_string =', 0, xml_string) if signature.getAction()=='Replace': - syncml_data += self.replaceXMLObject(cmd_id=cmd_id,object=object,gid=object_gid, - xml_string=xml_string, more_data=more_data) + 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,gid=object_gid, - xml_string=xml_string, more_data=more_data) + syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object, + gid=object_gid, xml_string=xml_string, more_data=more_data) return (syncml_data,xml_confirmation,cmd_id) def applyActionList(self, domain=None, subscriber=None,destination_path=None, @@ -918,7 +831,8 @@ class XMLSyncUtilsMixin(SyncCode): object = domain.getObjectFromGid(object_gid) if signature == None: LOG('applyActionList, signature is None',0,signature) - signature = Signature(gid=object_gid,status=self.NOT_SYNCHRONIZED,object=object).__of__(subscriber) + signature = Signature(gid=object_gid, status=self.NOT_SYNCHRONIZED, + object=object).__of__(subscriber) subscriber.addSignature(signature) force = signature.getForce() LOG('applyActionList',0,'object: %s' % repr(object)) @@ -932,22 +846,28 @@ class XMLSyncUtilsMixin(SyncCode): data_subnode = partial_data LOG('SyncModif',0,'data_subnode: %s' % data_subnode) #data_subnode = FromXml(data_subnode) - data_subnode = parseString(data_subnode) - data_subnode = data_subnode.childNodes[0] # Because we just created a new xml + if subscriber.getFlowType() == 'xml': + data_subnode = Parse(data_subnode) + data_subnode = data_subnode.childNodes[0] # 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 subscriber.getFlowType() == 'text': + data_subnode = self.getDataText(next_action) + else: + data_subnode = self.getDataSubNode(next_action) if next_action.nodeName == 'Add': # Then store the xml of this new subobject if object is None: object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=object_gid) #if object_id is not None: - add_data = conduit.addNode(xml=data_subnode, object=destination_path, - object_id=object_id) - conflict_list += add_data['conflict_list'] + add_data = conduit.addNode(xml=data_subnode, + object=destination_path, object_id=object_id) + if add_data['conflict_list'] not in ('', None, []): + conflict_list += add_data['conflict_list'] # Retrieve directly the object from addNode object = add_data['object'] LOG('XMLSyncUtils, in ADD add_data',0,add_data) + signature.setPath(object.getPhysicalPath()) LOG('applyActionList',0,'object after add: %s' % repr(object)) else: #Object was retrieve but need to be updated without recreated @@ -957,7 +877,8 @@ class XMLSyncUtilsMixin(SyncCode): object=destination_path, object_id=object_id, sub_object=object) - conflict_list += add_data['conflict_list'] + if add_data['conflict_list'] not in ('', None, []): + conflict_list += add_data['conflict_list'] if object is not None: LOG('SyncModif',0,'addNode, found the object') #mapping = getattr(object,domain.getXMLMapping(),None) @@ -990,7 +911,8 @@ class XMLSyncUtilsMixin(SyncCode): if conflict_list != []: status_code = self.CONFLICT signature.setStatus(self.CONFLICT) - signature.setConflictList(signature.getConflictList()+conflict_list) + signature.setConflictList(signature.getConflictList() \ + + conflict_list) string_io = StringIO() PrettyPrint(data_subnode,stream=string_io) data_subnode_string = string_io.getvalue() @@ -1012,11 +934,15 @@ class XMLSyncUtilsMixin(SyncCode): elif next_action.nodeName == 'Delete': object_id = signature.getId() - conduit.deleteNode(xml=self.getDataSubNode(next_action), object=destination_path, - object_id=object_id) + if subscriber.getFlowType == 'text': + data_subnode = self.getDataText(next_action) + else: + data_subnode = self.getDataSubNode(next_action) + conduit.deleteNode(xml=data_subnode, object=destination_path, + object_id=object_id) subscriber.delSignature(object_gid) - xml_confirmation += self.SyncMLConfirmation(cmd_id, - object_gid,status_code,'Delete') + xml_confirmation += self.SyncMLConfirmation(cmd_id, + object_gid,status_code,'Delete') else: # We want to retrieve more data signature.setStatus(self.PARTIAL) #LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial)) @@ -1026,8 +952,8 @@ class XMLSyncUtilsMixin(SyncCode): 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) + 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) @@ -1121,11 +1047,13 @@ class XMLSyncUtils(XMLSyncUtilsMixin): if domain.domain_type == self.PUB: simulate = 1 for subnode in xml_header.childNodes: - if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Source": - for subnode2 in subnode.childNodes: - if subnode2.nodeType == subnode2.ELEMENT_NODE and subnode2.nodeName == 'LocURI': - subscription_url = str(subnode2.childNodes[0].data) - subscriber = domain.getSubscriber(subscription_url) + if subnode.nodeType == subnode.ELEMENT_NODE and \ + subnode.nodeName == "Source": + for subnode2 in subnode.childNodes: + if subnode2.nodeType == subnode2.ELEMENT_NODE and \ + subnode2.nodeName == 'LocURI': + subscription_url = str(subnode2.childNodes[0].data) + subscriber = domain.getSubscriber(subscription_url) # We have to check if this message was not already, this can be dangerous # to update two times the same object @@ -1134,6 +1062,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin): if not correct_message: # We need to send again the message LOG('SyncModif, no correct message:',0,"sending again...") last_xml = subscriber.getLastSentMessage() + LOG("last_xml :", 0, last_xml) if last_xml != '': has_response = 1 if domain.domain_type == self.PUB: # We always reply @@ -1142,7 +1071,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin): xml=last_xml,domain=domain) elif domain.domain_type == self.SUB: self.sendResponse(from_url=domain.subscription_url, - to_url=domain.publication_url, sync_id=domain.getTitle(), xml=last_xml,domain=domain) + to_url=domain.publication_url, sync_id=domain.getTitle(), + xml=last_xml, domain=domain) return {'has_response':has_response,'xml':last_xml} subscriber.setLastSentMessage('') @@ -1164,7 +1094,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin): globals(), locals(), ['']) conduit = getattr(conduit_module, conduit_name)() # Then apply the list of actions - (xml_confirmation,has_next_action,cmd_id) = self.applyActionList(cmd_id=cmd_id, + (xml_confirmation,has_next_action,cmd_id) = self.applyActionList( + cmd_id=cmd_id, domain=domain, destination_path=destination_path, subscriber=subscriber, @@ -1178,8 +1109,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin): # syncml header if domain.domain_type == self.PUB: - xml(self.SyncMLHeader(subscriber.getSessionId(), subscriber.incrementMessageId(), - subscriber.getSubscriptionUrl(), domain.getPublicationUrl())) + xml(self.SyncMLHeader(subscriber.getSessionId(), + subscriber.incrementMessageId(), subscriber.getSubscriptionUrl(), + domain.getPublicationUrl())) elif domain.domain_type == self.SUB: xml(self.SyncMLHeader(domain.getSessionId(), domain.incrementMessageId(), domain.getPublicationUrl(), domain.getSubscriptionUrl())) @@ -1192,11 +1124,11 @@ class XMLSyncUtils(XMLSyncUtilsMixin): if has_next_action == 0 and not \ (domain.domain_type==self.SUB and alert_code==self.SLOW_SYNC): (syncml_data,xml_confirmation,cmd_id) = self.getSyncMLData(domain=domain, - remote_xml=remote_xml, - subscriber=subscriber, - destination_path=destination_path, - cmd_id=cmd_id,xml_confirmation=xml_confirmation, - conduit=conduit) + remote_xml=remote_xml, + subscriber=subscriber, + destination_path=destination_path, + cmd_id=cmd_id,xml_confirmation=xml_confirmation, + conduit=conduit) # syncml body xml(' <SyncBody>\n') diff --git a/product/ERP5SyncML/dtml/managePublications.dtml b/product/ERP5SyncML/dtml/managePublications.dtml index afd939bebfde039f44163327958bfaf5572046f3..091dee809310f57aaa018b3eda60d5165f97cc08 100644 --- a/product/ERP5SyncML/dtml/managePublications.dtml +++ b/product/ERP5SyncML/dtml/managePublications.dtml @@ -114,7 +114,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="id_generator" value="<dtml-var getIdGenerator>" size="40" /> + <input type="text" name="synchronization_id_generator" value="<dtml-var getSynchronizationIdGenerator>" size="40" /> </td> </tr> <tr> @@ -127,6 +127,24 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. <input type="text" name="gid_generator" value="<dtml-var getGidGenerator>" size="40" /> </td> </tr> + <tr> + <td align="left" valign="top"> + <div class="form-label"> + Flow Type + </label></div> + </td> + <td align="left" valign="top"> + <SELECT name="flow_type" size="1"> + <dtml-if expr="getFlowType() != 'text'"> + <OPTION value="xml" selected>XML</option> + <OPTION value="text">text</option> + <dtml-else> + <OPTION value="xml">XML</option> + <OPTION value="text" selected>text</option> + </dtml-if> + </SELECT> + </td> + </tr> <tr> <td align="left" valign="top"> <div class="form-label"> diff --git a/product/ERP5SyncML/dtml/manageSubscriptions.dtml b/product/ERP5SyncML/dtml/manageSubscriptions.dtml index fb7bd87a3f4570ab66b3af7e1a64feb99324962b..f82c3c98d3e2036509e5373183cef6a622b736b3 100644 --- a/product/ERP5SyncML/dtml/manageSubscriptions.dtml +++ b/product/ERP5SyncML/dtml/manageSubscriptions.dtml @@ -124,7 +124,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="id_generator" value="<dtml-var getIdGenerator>" size="40" /> + <input type="text" name="synchronization_id_generator" value="<dtml-var getSynchronizationIdGenerator>" size="40" /> </td> </tr> <tr> @@ -140,41 +140,39 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. <tr> <td align="left" valign="top"> <div class="form-label"> - Login - </label></div> - </td> - <td align="left" valign="top"> - <input type="text" name="login" value="<dtml-var getLogin>" size="40" /> - </td> - </tr> - <tr> - <td align="left" valign="top"> - <div class="form-label"> - Password + Flow Type </label></div> </td> <td align="left" valign="top"> - <input type="password" name="password" value="<dtml-var getPassword>" size="40" /> + <SELECT name="flow_type" size="1"> + <dtml-if expr="getFlowType() != 'text'"> + <OPTION value="xml" selected>XML</option> + <OPTION value="text">text</option> + <dtml-else> + <OPTION value="xml">XML</option> + <OPTION value="text" selected>text</option> + </dtml-if> + </SELECT> </td> </tr> <tr> <td align="left" valign="top"> <div class="form-label"> - Format authentication + Login </label></div> </td> <td align="left" valign="top"> - <input type="text" name="authentication_format" value="<dtml-var getAuthenticationFormat>" size="40" /> + <input type="text" name="login" value="<dtml-var getLogin>" size="40" /> </td> </tr> <tr> <td align="left" valign="top"> <div class="form-label"> - Type authentication + Password </label></div> </td> <td align="left" valign="top"> - <input type="text" name="authentication_type" value="<dtml-var getAuthenticationType>" size="40" /> + <input type="password" name="password" value="<dtml-var getPassword>" size="40" /> </td> </tr> </table> diff --git a/product/ERP5SyncML/dtml/manage_addPublication.dtml b/product/ERP5SyncML/dtml/manage_addPublication.dtml index 8b59f8ee75b287a7add2e014e187d21fc5c2a160..ce095663a206b718bddfebd51917e9cfa43393f1 100644 --- a/product/ERP5SyncML/dtml/manage_addPublication.dtml +++ b/product/ERP5SyncML/dtml/manage_addPublication.dtml @@ -110,7 +110,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="id_generator" size="40" /> + <input type="text" name="synchronization_id_generator" size="40" /> </td> </tr> <tr> @@ -123,6 +123,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. <input type="text" name="gid_generator" size="40" /> </td> </tr> + <tr> + <td align="left" valign="top"> + <div class="form-label"> + Flow Type + </label></div> + </td> + <td align="left" valign="top"> + <SELECT name="flow_type" size="1"> + <OPTION value="xml" selected>XML</option> + <OPTION value="text">text</option> + </SELECT> + </td> + </tr> <tr> <td align="left" valign="top"> <div class="form-label"> diff --git a/product/ERP5SyncML/dtml/manage_addSubscription.dtml b/product/ERP5SyncML/dtml/manage_addSubscription.dtml index 50a5a2df644b7de8bc77e2d71a73487e5ea10ef5..9d93125120045f925363f2ea72f523ad88e81781 100644 --- a/product/ERP5SyncML/dtml/manage_addSubscription.dtml +++ b/product/ERP5SyncML/dtml/manage_addSubscription.dtml @@ -120,7 +120,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="id_generator" size="40" /> + <input type="text" name="synchronization_id_generator" size="40" /> </td> </tr> <tr> @@ -136,39 +136,33 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. <tr> <td align="left" valign="top"> <div class="form-label"> - Login + Flow Type </label></div> </td> <td align="left" valign="top"> - <input type="text" name="login" size="40" /> </td> - </tr> - <tr> - <td align="left" valign="top"> - <div class="form-label"> - Password - </label></div> - </td> - <td align="left" valign="top"> - <input type="password" name="password" size="40" /> + <SELECT name="flow_type" size="1"> + <OPTION value="xml" selected>XML</option> + <OPTION value="text">text</option> + </SELECT> </td> </tr> <tr> <td align="left" valign="top"> <div class="form-label"> - Format authentication + Login </label></div> </td> <td align="left" valign="top"> - <input type="text" name="authentication_format" size="40" /> - </td> - </tr> + <input type="text" name="login" size="40" /> </td> + </tr> + <tr> <td align="left" valign="top"> <div class="form-label"> - Type authentication + Password </label></div> </td> <td align="left" valign="top"> - <input type="text" name="authentication_type" size="40" /> + <input type="password" name="password" size="40" /> </td> </tr> <tr> diff --git a/product/ERP5SyncML/tests/testERP5SyncML.py b/product/ERP5SyncML/tests/testERP5SyncML.py index bc5a8ca912d88296f9eb2fae947349301212ec51..77e5eb4c267ed0b6f2825c0278f236dad00558f8 100644 --- a/product/ERP5SyncML/tests/testERP5SyncML.py +++ b/product/ERP5SyncML/tests/testERP5SyncML.py @@ -46,10 +46,11 @@ try: from base64 import b64encode, b64decode except ImportError: from base64 import encodestring as b64encode, decodestring as b64decode -class TestERP5SyncML(ERP5TypeTestCase): + + +class TestERP5SyncMLMixin: # Different variables used for this test - run_all_test = 1 workflow_id = 'edit_workflow' first_name1 = 'Sebastien' last_name1 = 'Robin' @@ -91,10 +92,6 @@ class TestERP5SyncML(ERP5TypeTestCase): #subscription_url1 = 'client1@localhost' #subscription_url2 = 'client2@localhost' - def getTitle(self): - """ - """ - return "ERP5 SyncML" def afterSetUp(self): """Setup.""" @@ -117,7 +114,7 @@ class TestERP5SyncML(ERP5TypeTestCase): /person_client1 : empty /person_client2 : empty """ - return ('erp5_base',) + return ('erp5_base','fabien_bt') def getSynchronizationTool(self): return getattr(self.getPortal(), 'portal_synchronizations', None) @@ -134,6 +131,194 @@ class TestERP5SyncML(ERP5TypeTestCase): def getPortalId(self): return self.getPortal().getId() + def login(self, quiet=0): + uf = self.getPortal().acl_users + uf._doAddUser('fab', 'myPassword', ['Manager'], []) + uf._doAddUser('ERP5TypeTestCase', '', ['Manager'], []) + uf._doAddUser('syncml', '', ['Manager'], []) + user = uf.getUserById('fab').__of__(uf) + newSecurityManager(None, user) + + 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() + if not hasattr(portal,'person_server'): + portal.portal_types.constructContent(type_name = 'Person Module', + container = portal, + id = 'person_server') + if not hasattr(portal,'person_client1'): + portal.portal_types.constructContent(type_name = 'Person Module', + container = portal, + id = 'person_client1') + if not hasattr(portal,'person_client2'): + portal.portal_types.constructContent(type_name = 'Person Module', + container = portal, + id = 'person_client2') + person_id = '' + person_server = self.getPersonServer() + 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') + kw = {'first_name':self.first_name2,'last_name':self.last_name2, + 'description':self.description2} + person2.edit(**kw) + nb_person = len(person_server.objectValues()) + self.failUnless(nb_person==2) + return nb_person + + def synchronize(self, id, run=1): + """ + This just define how we synchronize, we have + to define it here because it is specific to the unit testing + """ + 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) + # reset files, because we do sync by files + file = open('/tmp/sync_client1','w') + file.write('') + file.close() + file = open('/tmp/sync_client2','w') + file.write('') + file.close() + file = open('/tmp/sync','w') + file.write('') + file.close() + nb_message = 1 + result = portal_sync.SubSync(subscription.getTitle()) + while result['has_response']==1: + portal_sync.PubSync(publication.getTitle()) + result = portal_sync.SubSync(subscription.getTitle()) + nb_message += 1 + result['has_response'] + return nb_message + + def synchronizeWithBrokenMessage(self, id, run=1): + """ + This just define how we synchronize, we have + to define it here because it is specific to the unit testing + """ + portal_sync = self.getSynchronizationTool() + #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) + # reset files, because we do sync by files + file = open('/tmp/sync_client1','w') + file.write('') + file.close() + file = open('/tmp/sync_client2','w') + file.write('') + file.close() + file = open('/tmp/sync','w') + file.write('') + file.close() + nb_message = 1 + result = portal_sync.SubSync(subscription.getTitle()) + while result['has_response']==1: + # We do thing three times, so that we will test + # if we manage well duplicate messages + portal_sync.PubSync(publication.getTitle()) + portal_sync.PubSync(publication.getTitle()) + portal_sync.PubSync(publication.getTitle()) + result = portal_sync.SubSync(subscription.getTitle()) + result = portal_sync.SubSync(subscription.getTitle()) + result = portal_sync.SubSync(subscription.getTitle()) + nb_message += 1 + result['has_response'] + return nb_message + + def checkSynchronizationStateIsSynchronized(self, quiet=0, run=1): + portal_sync = self.getSynchronizationTool() + person_server = self.getPersonServer() + for person in person_server.objectValues(): + state_list = portal_sync.getSynchronizationState(person) + for state in state_list: + self.failUnless(state[1]==state[0].SYNCHRONIZED) + person_client1 = self.getPersonClient1() + for person in person_client1.objectValues(): + state_list = portal_sync.getSynchronizationState(person) + for state in state_list: + self.failUnless(state[1]==state[0].SYNCHRONIZED) + person_client2 = self.getPersonClient2() + for person in person_client2.objectValues(): + state_list = portal_sync.getSynchronizationState(person) + for state in state_list: + self.failUnless(state[1]==state[0].SYNCHRONIZED) + # Check for each signature that the tempXML is None + for sub in portal_sync.getSubscriptionList(): + for m in sub.getSignatureList(): + self.assertEquals(m.getTempXML(),None) + self.assertEquals(m.getPartialXML(),None) + for pub in portal_sync.getPublicationList(): + for sub in pub.getSubscriberList(): + 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.failUnless(person_server.getFirstName()==first_name) + self.failUnless(person_server.getLastName()==last_name) + self.failUnless(person_client.getFirstName()==first_name) + self.failUnless(person_client.getLastName()==last_name) + + def verifyFirstNameAndLastNameAreNotSynchronized(self, first_name, + last_name, person_server, person_client): + """ + verify that the first and last name are NOT synchronized + """ + self.failUnless(person_server.getFirstName()!=first_name) + self.failUnless(person_server.getLastName()!=last_name) + self.failUnless(person_client.getFirstName()==first_name) + self.failUnless(person_client.getLastName()==last_name) + + def checkFirstSynchronization(self, id=None, nb_person=0): + + portal_sync = self.getSynchronizationTool() + subscription1 = portal_sync.getSubscription(self.sub_id1) + subscription2 = portal_sync.getSubscription(self.sub_id2) + self.failUnless(len(subscription1.getObjectList())==nb_person) + person_server = self.getPersonServer() # We also check we don't + # modify initial ob + person1_s = person_server._getOb(self.id1) + self.failUnless(person1_s.getId()==self.id1) + self.failUnless(person1_s.getFirstName()==self.first_name1) + self.failUnless(person1_s.getLastName()==self.last_name1) + person_client1 = self.getPersonClient1() + person1_c = person_client1._getOb(id) + self.failUnless(person1_c.getId()==id) + self.failUnless(person1_c.getFirstName()==self.first_name1) + self.failUnless(person1_c.getLastName()==self.last_name1) + self.failUnless(len(subscription2.getObjectList())==nb_person) + person_client2 = self.getPersonClient2() + person2_c = person_client2._getOb(id) + self.failUnless(person2_c.getId()==id) + self.failUnless(person2_c.getFirstName()==self.first_name1) + self.failUnless(person2_c.getLastName()==self.last_name1) + + +class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): + + run_all_test = True + def getTitle(self): + """ + """ + return "ERP5 SyncML" + def test_01_HasEverything(self, quiet=0, run=run_all_test): # Test if portal_synchronizations was created if not run: return @@ -152,9 +337,9 @@ class TestERP5SyncML(ERP5TypeTestCase): LOG('Testing... ',0,'test_02_AddPublication') portal_id = self.getPortalName() portal_sync = self.getSynchronizationTool() - portal_sync.manage_addPublication(self.pub_id,self.publication_url, - '/%s/person_server' % portal_id,'objectValues', - self.xml_mapping,'ERP5Conduit','') + portal_sync.manage_addPublication(self.pub_id,self.publication_url, + '/%s/person_server' % portal_id,'objectValues', self.xml_mapping, + 'ERP5Conduit','') pub = portal_sync.getPublication(self.pub_id) self.failUnless(pub is not None) @@ -165,9 +350,9 @@ class TestERP5SyncML(ERP5TypeTestCase): LOG('Testing... ',0,'test_03_AddSubscription1') portal_id = self.getPortalId() portal_sync = self.getSynchronizationTool() - portal_sync.manage_addSubscription(self.sub_id1,self.publication_url, - self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues', - self.xml_mapping,'ERP5Conduit','') + portal_sync.manage_addSubscription(self.sub_id1, self.publication_url, + self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues', + self.xml_mapping,'ERP5Conduit','') sub = portal_sync.getSubscription(self.sub_id1) self.failUnless(sub is not None) @@ -178,59 +363,18 @@ class TestERP5SyncML(ERP5TypeTestCase): LOG('Testing... ',0,'test_04_AddSubscription2') portal_id = self.getPortalId() portal_sync = self.getSynchronizationTool() - portal_sync.manage_addSubscription(self.sub_id2,self.publication_url, - self.subscription_url2,'/%s/person_client2' % portal_id,'objectValues', - self.xml_mapping,'ERP5Conduit','') + portal_sync.manage_addSubscription(self.sub_id2,self.publication_url, + self.subscription_url2,'/%s/person_client2' % portal_id,'objectValues', + self.xml_mapping,'ERP5Conduit','') sub = portal_sync.getSubscription(self.sub_id2) self.failUnless(sub is not None) - def login(self, quiet=0): - uf = self.getPortal().acl_users - uf._doAddUser('fab', 'myPassword', ['Manager'], []) - uf._doAddUser('ERP5TypeTestCase', '', ['Manager'], []) - uf._doAddUser('syncml', '', ['Manager'], []) - user = uf.getUserById('fab').__of__(uf) - newSecurityManager(None, user) - - def populatePersonServer(self, quiet=0, run=run_all_test): - if not run: return - if not quiet: - ZopeTestCase._print('\nTest Populate Person Server ') - LOG('Testing... ',0,'populatePersonServer') - self.login() - portal = self.getPortal() - if not hasattr(portal,'person_server'): - portal.portal_types.constructContent(type_name = 'Person Module', - container = portal, - id = 'person_server') - if not hasattr(portal,'person_client1'): - portal.portal_types.constructContent(type_name = 'Person Module', - container = portal, - id = 'person_client1') - if not hasattr(portal,'person_client2'): - portal.portal_types.constructContent(type_name = 'Person Module', - container = portal, - id = 'person_client2') - person_id = '' - person_server = self.getPersonServer() - 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') - kw = {'first_name':self.first_name2,'last_name':self.last_name2, - 'description':self.description2} - person2.edit(**kw) - nb_person = len(person_server.objectValues()) - self.failUnless(nb_person==2) - return nb_person - - def setupPublicationAndSubscription(self, quiet=0, run=1): + 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=1): + def setupPublicationAndSubscriptionAndGid(self, quiet=0, run=run_all_test): self.setupPublicationAndSubscription(quiet=1,run=1) def getGid(object): return object.getTitle() @@ -241,9 +385,9 @@ class TestERP5SyncML(ERP5TypeTestCase): pub.setGidGenerator(getGid) sub1.setGidGenerator(getGid) sub2.setGidGenerator(getGid) - pub.setIdGenerator('_generateNextId') - sub1.setIdGenerator('_generateNextId') - sub2.setIdGenerator('_generateNextId') + 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, @@ -315,73 +459,6 @@ class TestERP5SyncML(ERP5TypeTestCase): c_local_role = person_client1.get_local_roles() self.assertEqual(s_local_role,c_local_role) - def synchronize(self, id, run=1): - """ - This just define how we synchronize, we have - to define it here because it is specific to the unit testing - """ - 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) - # reset files, because we do sync by files - file = open('/tmp/sync_client1','w') - file.write('') - file.close() - file = open('/tmp/sync_client2','w') - file.write('') - file.close() - file = open('/tmp/sync','w') - file.write('') - file.close() - nb_message = 1 - result = portal_sync.SubSync(subscription.getTitle()) - while result['has_response']==1: - portal_sync.PubSync(publication.getTitle()) - result = portal_sync.SubSync(subscription.getTitle()) - nb_message += 1 + result['has_response'] - return nb_message - - def synchronizeWithBrokenMessage(self, id, run=1): - """ - This just define how we synchronize, we have - to define it here because it is specific to the unit testing - """ - portal_sync = self.getSynchronizationTool() - #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) - # reset files, because we do sync by files - file = open('/tmp/sync_client1','w') - file.write('') - file.close() - file = open('/tmp/sync_client2','w') - file.write('') - file.close() - file = open('/tmp/sync','w') - file.write('') - file.close() - nb_message = 1 - result = portal_sync.SubSync(subscription.getTitle()) - while result['has_response']==1: - # We do thing three times, so that we will test - # if we manage well duplicate messages - portal_sync.PubSync(publication.getTitle()) - portal_sync.PubSync(publication.getTitle()) - portal_sync.PubSync(publication.getTitle()) - result = portal_sync.SubSync(subscription.getTitle()) - result = portal_sync.SubSync(subscription.getTitle()) - result = portal_sync.SubSync(subscription.getTitle()) - nb_message += 1 + result['has_response'] - return nb_message - def test_08_FirstSynchronization(self, quiet=0, run=run_all_test): # We will try to populate the folder person_client1 # with the data form person_server @@ -408,26 +485,7 @@ class TestERP5SyncML(ERP5TypeTestCase): for sub in portal_sync.getSubscriptionList(): self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY) self.failUnless(nb_message2==self.nb_message_first_synchronization) - subscription1 = portal_sync.getSubscription(self.sub_id1) - subscription2 = portal_sync.getSubscription(self.sub_id2) - self.failUnless(len(subscription1.getObjectList())==nb_person) - person_server = self.getPersonServer() # We also check we don't - # modify initial ob - person1_s = person_server._getOb(self.id1) - self.failUnless(person1_s.getId()==self.id1) - self.failUnless(person1_s.getFirstName()==self.first_name1) - self.failUnless(person1_s.getLastName()==self.last_name1) - person_client1 = self.getPersonClient1() - person1_c = person_client1._getOb(self.id1) - self.failUnless(person1_c.getId()==self.id1) - self.failUnless(person1_c.getFirstName()==self.first_name1) - self.failUnless(person1_c.getLastName()==self.last_name1) - self.failUnless(len(subscription2.getObjectList())==nb_person) - person_client2 = self.getPersonClient2() - person2_c = person_client2._getOb(self.id1) - self.failUnless(person2_c.getId()==self.id1) - self.failUnless(person2_c.getFirstName()==self.first_name1) - self.failUnless(person2_c.getLastName()==self.last_name1) + self.checkFirstSynchronization(id=self.id1, nb_person=nb_person) def test_09_FirstSynchronizationWithLongLines(self, quiet=0, run=run_all_test): # We will try to populate the folder person_client1 @@ -497,33 +555,6 @@ class TestERP5SyncML(ERP5TypeTestCase): # for each subscriber self.checkSynchronizationStateIsSynchronized() - def checkSynchronizationStateIsSynchronized(self, quiet=0, run=1): - portal_sync = self.getSynchronizationTool() - person_server = self.getPersonServer() - for person in person_server.objectValues(): - state_list = portal_sync.getSynchronizationState(person) - for state in state_list: - self.failUnless(state[1]==state[0].SYNCHRONIZED) - person_client1 = self.getPersonClient1() - for person in person_client1.objectValues(): - state_list = portal_sync.getSynchronizationState(person) - for state in state_list: - self.failUnless(state[1]==state[0].SYNCHRONIZED) - person_client2 = self.getPersonClient2() - for person in person_client2.objectValues(): - state_list = portal_sync.getSynchronizationState(person) - for state in state_list: - self.failUnless(state[1]==state[0].SYNCHRONIZED) - # Check for each signature that the tempXML is None - for sub in portal_sync.getSubscriptionList(): - for m in sub.getSignatureList(): - self.assertEquals(m.getTempXML(),None) - self.assertEquals(m.getPartialXML(),None) - for pub in portal_sync.getPublicationList(): - for sub in pub.getSubscriberList(): - for m in sub.getSignatureList(): - self.assertEquals(m.getPartialXML(),None) - def checkSynchronizationStateIsConflict(self, quiet=0, run=1): portal_sync = self.getSynchronizationTool() person_server = self.getPersonServer() @@ -987,7 +1018,7 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(person_s.getDescription()==self.description3) self.failUnless(person_c1.getDescription()==self.description3) - def test_25_MultiNodeConflict(self, quiet=0, run=1): + def test_25_MultiNodeConflict(self, quiet=0, run=run_all_test): """ We will create conflicts with 3 differents nodes, and we will solve it by taking one full version of documents. @@ -1410,27 +1441,7 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA==' sub.setAuthenticationFormat(auth_format) sub.setAuthenticationType(auth_type) - def verifyFirstNameAndLastNameAreSynchronized(self, first_name, - last_name, person_server, person_client): - """ - verify if the first and last name are synchronized - """ - self.failUnless(person_server.getFirstName()==first_name) - self.failUnless(person_server.getLastName()==last_name) - self.failUnless(person_client.getFirstName()==first_name) - self.failUnless(person_client.getLastName()==last_name) - - def verifyFirstNameAndLastNameAreNotSynchronized(self, first_name, - last_name, person_server, person_client): - """ - verify that the first and last name are NOT synchronized - """ - self.failUnless(person_server.getFirstName()!=first_name) - self.failUnless(person_server.getLastName()!=last_name) - self.failUnless(person_client.getFirstName()==first_name) - self.failUnless(person_client.getLastName()==last_name) - - def test_35_authentication(self, quiet=0, run=1): + def test_35_authentication(self, quiet=0, run=run_all_test): """ we will test - if we can't synchronize without good authentication for an @@ -1507,7 +1518,6 @@ wuIFtde33Dp3NkZl9fc2Rmw6fDp8OnX2RmX19fJibDqV1dXcKwwrDCsMKwwrDCsA==' self.synchronize(self.sub_id1) self.verifyFirstNameAndLastNameAreNotSynchronized(self.first_name1, self.last_name1, person1_s, person1_c) - #with the good password self.addAuthenticationToSubscription(self.sub_id1, 'fab', 'myPassword',