diff --git a/product/ERP5SyncML/Conduit/ERP5Conduit.py b/product/ERP5SyncML/Conduit/ERP5Conduit.py index 0df7b45c16604d62ac4c27d4de4ddd4ed459d20e..dd8893e96b0ebc74bb3805d77c71808d30b2527c 100644 --- a/product/ERP5SyncML/Conduit/ERP5Conduit.py +++ b/product/ERP5SyncML/Conduit/ERP5Conduit.py @@ -29,7 +29,6 @@ from Products.ERP5SyncML.XMLSyncUtils import XMLSyncUtilsMixin from Products.ERP5SyncML.Subscription import Conflict from Products.CMFCore.utils import getToolByName -from Products.ERP5SyncML.XMLSyncUtils import Parse from DateTime.DateTime import DateTime from email.MIMEBase import MIMEBase from email import Encoders @@ -41,19 +40,9 @@ from cStringIO import StringIO from xml.sax.saxutils import escape, unescape import re import cStringIO +from lxml import etree +from xml.marshal.generic import loads as unmarshaler from zLOG import LOG, INFO, DEBUG -try: - from Ft.Xml.Domlette import Print, PrettyPrint -except ImportError: - LOG('ERP5Conduit', INFO, "Can't import Print and PrettyPrint") - class Print: - def __init__(self, *args, **kw): - raise ImportError, '4Suite-XML is not installed' - - class PrettyPrint: - def __init__(self, *args, **kw): - raise ImportError, '4Suite-XML is not installed' - class ERP5Conduit(XMLSyncUtilsMixin): """ @@ -133,21 +122,21 @@ class ERP5Conduit(XMLSyncUtilsMixin): conflict_list = [] xml = self.convertToXml(xml) if xml is None: - return {'conflict_list':conflict_list, 'object':sub_object} + return {'conflict_list': conflict_list, 'object': sub_object} # In the case where this new node is a object to add - if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and \ + if xml.xpath('name()') in self.XUPDATE_INSERT_OR_ADD and \ self.getSubObjectDepth(xml) == 0: - if self.isHistoryAdd(xml)!=-1: # bad hack XXX to be removed + if self.isHistoryAdd(xml) != -1: # bad hack XXX to be removed for element in self.getXupdateElementList(xml): - xml = self.getElementFromXupdate(element) - conflict_list += self.addNode( - xml=xml, - object=object, - previous_xml=previous_xml, - force=force, - simulate=simulate, - **kw)['conflict_list'] - elif xml.nodeName == 'object': + xml = self.getElementFromXupdate(element) + conflict_list += self.addNode( + xml=xml, + object=object, + previous_xml=previous_xml, + force=force, + simulate=simulate, + **kw)['conflict_list'] + elif xml.tag == 'object': if object_id is None: object_id = self.getAttribute(xml, 'id') if object_id is not None: @@ -158,9 +147,9 @@ class ERP5Conduit(XMLSyncUtilsMixin): sub_object = None if sub_object is None: # If so, it doesn't exist portal_type = '' - if xml.nodeName == 'object': + if xml.tag == 'object': portal_type = self.getObjectType(xml) - elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD: # Deprecated ??? + elif xml.xpath('name()') in self.XUPDATE_INSERT_OR_ADD: # Deprecated ??? portal_type = self.getXupdateObjectType(xml) # Deprecated ??? sub_object, reset_local_roles, reset_workflow = self.constructContent( object, @@ -172,8 +161,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): simulate=simulate, reset_local_roles=reset_local_roles, reset_workflow=reset_workflow) - elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \ - and self.getSubObjectDepth(xml)>=1: + elif xml.xpath('name()') in self.XUPDATE_INSERT_OR_ADD \ + and self.getSubObjectDepth(xml) >= 1: sub_object_id = self.getSubObjectId(xml) if previous_xml is not None and sub_object_id is not None: # Find the previous xml corresponding to this subobject @@ -195,12 +184,12 @@ class ERP5Conduit(XMLSyncUtilsMixin): conflict_list += self.addNode(xml=sub_xml,object=sub_object, previous_xml=sub_previous_xml, force=force, simulate=simulate, **kw)['conflict_list'] - elif xml.nodeName == self.history_tag or self.isHistoryAdd(xml)>0: + elif xml.tag == self.history_tag or self.isHistoryAdd(xml)>0: conflict_list += self.addWorkflowNode(object, xml, simulate) - #elif xml.nodeName in self.local_role_list or self.isLocalRole(xml)>0 and not simulate: - elif xml.nodeName in self.local_role_list: - conflict_list += self.addLocalRoleNode(object, xml) - elif xml.nodeName in self.local_permission_list: + #elif xml.tag in self.local_role_list or self.isLocalRole(xml)>0 and not simulate: + elif xml.tag in self.local_role_list: + self.addLocalRoleNode(object, xml) + elif xml.tag in self.local_permission_list: conflict_list += self.addLocalPermissionNode(object, xml) else: conflict_list += self.updateNode(xml=xml,object=object, force=force, @@ -221,7 +210,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): xml = self.convertToXml(xml) if object_id is None: #LOG('ERP5Conduit.deleteNode', DEBUG, 'deleteNode, SubObjectDepth: %i' % self.getSubObjectDepth(xml)) - if xml.nodeName == self.xml_object_tag: + if xml.tag == self.xml_object_tag: object_id = self.getAttribute(xml,'id') elif self.getSubObjectDepth(xml) == 1: object_id = self.getSubObjectId(xml) @@ -240,17 +229,17 @@ class ERP5Conduit(XMLSyncUtilsMixin): self.deleteObject(object, object_id) # In the case where we have to delete an user role # If we are still there, this means the delete is for this node - elif xml.nodeName in self.XUPDATE_DEL: + elif xml.xpath('name()') in self.XUPDATE_DEL: xml = self.getElementFromXupdate(xml) - if xml.nodeName in self.local_role_list and not simulate: + if xml.tag in self.local_role_list and not simulate: # We want to del a local role user = self.getAttribute(xml,'id') #LOG('ERP5Conduit.deleteNode local_role: ', DEBUG, 'user: %s' % repr(user)) - if xml.nodeName.find(self.local_role_tag)>=0: + if xml.tag.find(self.local_role_tag)>=0: object.manage_delLocalRoles([user]) - elif xml.nodeName.find(self.local_group_tag)>=0: + elif xml.tag.find(self.local_group_tag)>=0: object.manage_delLocalGroupRoles([user]) - if xml.nodeName in self.local_permission_list and not simulate: + if xml.tag in self.local_permission_list and not simulate: permission = self.getAttribute(xml,'id') object.manage_setLocalPermissions(permission) return conflict_list @@ -277,10 +266,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): if xml is None: return {'conflict_list':conflict_list, 'object':object} xml = self.convertToXml(xml) - #LOG('ERP5Conduit.updateNode', DEBUG, 'xml.nodeName: %s' % xml.nodeName) + #LOG('ERP5Conduit.updateNode', DEBUG, 'xml.tag: %s' % xml.tag) #LOG('ERP5Conduit.updateNode, force: ', DEBUG, force) # we have an xupdate xml - if xml.nodeName == 'xupdate:modifications': + if xml.xpath('name()') == 'xupdate:modifications': conflict_list += self.applyXupdate(object=object, xupdate=xml, conduit=self, @@ -290,27 +279,24 @@ class ERP5Conduit(XMLSyncUtilsMixin): # we may have only the part of an xupdate else: args = {} - if self.isProperty(xml) and not(self.isSubObjectModification(xml)): + if self.isProperty(xml): keyword = None - for subnode in self.getAttributeNodeList(xml): - if subnode.nodeName == 'select': - select_list = subnode.nodeValue.split('/') # Something like: - #('','object[1]','sid[1]') - new_select_list = () - for select_item in select_list: - if select_item.find('[')>=0: - select_item = select_item[:select_item.find('[')] - 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 + value = xml.attrib.get('select', None) + if value is not None: + select_list = value.split('/') # Something like: + #('','object[1]','sid[1]') + new_select_list = () + for select_item in select_list: + if select_item.find('[') >= 0: + select_item = select_item[:select_item.find('[')] + 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 = None - if xml.nodeName not in self.XUPDATE_INSERT_OR_ADD: - for subnode in self.getElementNodeList(xml): - if subnode.nodeName == 'xupdate:element': - for subnode1 in subnode.attributes: - if subnode1.nodeName == 'name': - keyword = subnode1.nodeValue + if xml.xpath('name()') not in self.XUPDATE_INSERT_OR_ADD: + for subnode in xml: + if subnode.xpath('name()') in self.XUPDATE_EL: + keyword = subnode.attrib.get('name', None) data_xml = subnode else: #We can call add node @@ -321,7 +307,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): **kw) return conflict_list - if xml.nodeName in self.XUPDATE_DEL: + if xml.xpath('name()') in self.XUPDATE_DEL: conflict_list += self.deleteNode(xml=xml, object=object, force=force, @@ -329,17 +315,12 @@ class ERP5Conduit(XMLSyncUtilsMixin): **kw) return conflict_list 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 - except IndexError: # There is no data - data = None + keyword = xml.tag if not (keyword in self.NOT_EDITABLE_PROPERTY): # We will look for the data to enter data_type = object.getPropertyType(keyword) #LOG('ERP5Conduit.updateNode', DEBUG, 'data_type: %s for keyword: %s' % (str(data_type), keyword)) - data = self.convertXmlValue(data, data_type=data_type) + data = self.convertXmlValue(xml, data_type=data_type) args[keyword] = data args = self.getFormatedArgs(args=args) # This is the place where we should look for conflicts @@ -352,7 +333,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): 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) + data_type=data_type) #current_data = object.getProperty(keyword) current_data = self.getProperty(object, keyword) #LOG('ERP5Conduit.updateNode', DEBUG, 'Conflict data: %s' % str(data)) @@ -367,11 +348,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): if 1: # This is a conflict isConflict = 1 - string_io = StringIO() - PrettyPrint(xml, stream=string_io) + xml_string = etree.tostring(xml, encoding='utf-8') conflict = Conflict(object_path=object.getPhysicalPath(), keyword=keyword) - conflict.setXupdate(string_io.getvalue()) + conflict.setXupdate(xml_string) if not (data_type in self.binary_type_list): conflict.setLocalValue(current_data) conflict.setRemoteValue(data) @@ -392,8 +372,9 @@ class ERP5Conduit(XMLSyncUtilsMixin): **kw)['conflict_list'] elif keyword == self.history_tag and not simulate: # This is the case where we have to call addNode - conflict_list += self.addNode(xml=subnode,object=object,force=force, - simulate=simulate,**kw)['conflict_list'] + conflict_list += self.addNode(xml=subnode, object=object, + force=force, 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('ERP5Conduit.updateNode', DEBUG, 'we will add a local role') @@ -401,20 +382,21 @@ 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 sub_object_id = self.getSubObjectId(xml) #LOG('ERP5Conduit.updateNode', DEBUG,'isSubObjectModification sub_object_id: %s' % sub_object_id) if previous_xml is not None and sub_object_id is not None: - sub_previous_xml = self.getSubObjectXml(sub_object_id,previous_xml) + sub_previous_xml = self.getSubObjectXml(sub_object_id, previous_xml) #LOG('ERP5Conduit.updateNode', DEBUG, 'isSubObjectModification sub_previous_xml: %s' % str(sub_previous_xml)) if sub_previous_xml is not None: sub_object = None try: - sub_object = object[sub_object_id] + sub_object = object._getOb(sub_object_id) except KeyError: pass if sub_object is not None: @@ -424,9 +406,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): sub_xml = self.getSubObjectXupdate(xml) #LOG('ERP5Conduit.updateNode', DEBUG, '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: @@ -465,19 +448,17 @@ class ERP5Conduit(XMLSyncUtilsMixin): new_args = {} for keyword in args.keys(): data = args[keyword] - if type(keyword) is type(u"a"): + if isinstance(keyword, unicode): keyword = keyword.encode(self.getEncoding()) - if type(data) is type([]) or type(data) is type(()): + if isinstance(data, (tuple, list)): new_data = [] for item in data: - if type(item) is type(u"a"): + if isinstance(item, unicode): item = item.encode(self.getEncoding()) - item = item.replace('@@@','\n') - new_data += [item] + new_data.append(item) data = new_data - if type(data) is type(u"a"): + if isinstance(data, unicode): data = data.encode(self.getEncoding()) - data = data.replace('@@@','\n') if keyword == 'binary_data': #LOG('ERP5Conduit.getFormatedArgs', DEBUG, 'binary_data keyword: %s' % str(keyword)) msg = MIMEBase('application','octet-stream') @@ -487,46 +468,44 @@ class ERP5Conduit(XMLSyncUtilsMixin): new_args[keyword] = data return new_args - security.declareProtected(Permissions.AccessContentsInformation,'isProperty') + security.declareProtected(Permissions.AccessContentsInformation, 'isProperty') def isProperty(self, xml): """ Check if it is a simple property - """ - bad_list = (self.sub_object_exp,self.history_exp) - for subnode in self.getAttributeNodeList(xml): - if subnode.nodeName == 'select': - value = subnode.nodeValue - for bad_string in bad_list: - if re.search(bad_string,value) is not None: - return 0 + not an attribute @type it's a metadata + """ + bad_list = (self.sub_object_exp, self.history_exp, self.attribute_type_exp,) + value = xml.attrib.get('select', None) + if value is not None: + for bad_string in bad_list: + if bad_string.search(value) is not None: + return 0 return 1 - security.declareProtected(Permissions.AccessContentsInformation, + 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 = 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) + from copy import deepcopy + xml_copy = deepcopy(xml) + self.changeSubObjectSelect(xml_copy) return xml_copy - security.declareProtected(Permissions.AccessContentsInformation, + security.declareProtected(Permissions.AccessContentsInformation, 'isHistoryAdd') def isHistoryAdd(self, xml): bad_list = (self.history_exp,) - for subnode in self.getAttributeNodeList(xml): - if subnode.nodeName == 'select': - value = subnode.nodeValue - for bad_string in bad_list: - if re.search(bad_string,value) is not None: - if re.search(self.bad_history_exp,value) is None: - return 1 - else: - return -1 + value = xml.attrib.get('select') + if value is not None: + for bad_string in bad_list: + if bad_string.search(value) is not None: + if self.bad_history_exp.search(value) is None: + return 1 + else: + return -1 return 0 security.declareProtected(Permissions.AccessContentsInformation, @@ -536,12 +515,11 @@ class ERP5Conduit(XMLSyncUtilsMixin): Check if it is a modification from an subobject """ good_list = (self.sub_object_exp,) - for subnode in self.getAttributeNodeList(xml) : - if subnode.nodeName == 'select': - value = subnode.nodeValue - for good_string in good_list: - if re.search(good_string,value) is not None: - return 1 + value = xml.attrib.get('select', None) + if value is not None: + for good_string in good_list: + if good_string.search(value) is not None: + return 1 return 0 security.declareProtected(Permissions.AccessContentsInformation, @@ -553,45 +531,44 @@ class ERP5Conduit(XMLSyncUtilsMixin): 1 means it is a subobject 2 means it is more depth than subobject """ - #LOG('getSubObjectDepth',0,'xml.nodeName: %s' % xml.nodeName) - if xml.nodeName in self.XUPDATE_TAG: + #LOG('getSubObjectDepth',0,'xml.tag: %s' % xml.tag) + if xml.xpath('name()') in self.XUPDATE_TAG: i = 0 - if xml.nodeName in self.XUPDATE_INSERT: + if xml.xpath('name()') in self.XUPDATE_INSERT: i = 1 - #LOG('getSubObjectDepth',0,'xml2.nodeName: %s' % xml.nodeName) - for subnode in self.getAttributeNodeList(xml): - #LOG('getSubObjectDepth',0,'subnode.nodeName: %s' % subnode.nodeName) - if subnode.nodeName == 'select': - value = subnode.nodeValue - #LOG('getSubObjectDepth',0,'subnode.nodeValue: %s' % subnode.nodeValue) - if re.search(self.sub_sub_object_exp,value) is not None: - return 2 # This is sure in all cases - elif re.search(self.sub_object_exp,value) is not None: - #new_select = self.getSubObjectSelect(value) # Still needed ??? - #if self.getSubObjectSelect(new_select) != new_select: - # return (2 - i) - #return (1 - i) - return (2 - i) - elif re.search(self.object_exp,value) is not None: - return (1 - i) + #LOG('getSubObjectDepth',0,'xml2.tag: %s' % xml.tag) + value = xml.attrib.get('select', None) + if value is not None: + #LOG('getSubObjectDepth',0,'subnode.nodeValue: %s' % subnode.nodeValue) + if self.sub_sub_object_exp.search(value) is not None: + return 2 # This is sure in all cases + elif self.sub_object_exp.search(value) is not None: + #new_select = self.getSubObjectSelect(value) # Still needed ??? + #if self.getSubObjectSelect(new_select) != new_select: + # return (2 - i) + #return (1 - i) + return (2 - i) + elif self.object_exp.search(value) is not None: + return (1 - i) return 0 - security.declareProtected(Permissions.AccessContentsInformation, - 'getSubObjectSelect') - def getSubObjectSelect(self, select): + security.declareProtected(Permissions.ModifyPortalContent, + 'changeSubObjectSelect') + def changeSubObjectSelect(self, xml): """ Return a string wich is the selection for the subobject ex: for "/object[@id='161']/object[@id='default_address']/street_address" it returns "/object[@id='default_address']/street_address" """ - if re.search(self.object_exp,select) is not None: + select = xml.attrib.get('select') + if self.object_exp.search(select) is not None: s = '/' - if re.search('/.*/',select) is not None: # This means we have more than just object - new_value = select[select.find(s,select.find(s)+1):] + if re.search('/.*/', select) is not None: # This means we have more than just object + new_value = select[select.find(s, select.find(s)+1):] else: new_value = '/' select = new_value - return select + xml.attrib['select'] = select security.declareProtected(Permissions.AccessContentsInformation, 'getSubObjectId') @@ -600,14 +577,13 @@ class ERP5Conduit(XMLSyncUtilsMixin): Return the id of the subobject in an xupdate modification """ object_id = None - for subnode in self.getAttributeNodeList(xml): - if subnode.nodeName == 'select': - value = subnode.nodeValue - if re.search(self.object_exp,value) is not None: - s = "'" - first = value.find(s)+1 - object_id = value[first:value.find(s,first)] - return object_id + value = xml.attrib.get('select', None) + if value is not None: + if self.object_exp.search(value) is not None: + s = "'" + first = value.find(s) + 1 + object_id = value[first:value.find(s, first)] + return object_id return object_id security.declareProtected(Permissions.AccessContentsInformation, @@ -617,15 +593,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): Return the id of the subobject in an xupdate modification """ object_id = None - for subnode in self.getAttributeNodeList(xml): - if subnode.nodeName == 'select': - value = subnode.nodeValue - if re.search(self.history_exp,value) is not None: - s = self.history_tag - object_id = value[value.find(s):] - object_id = object_id[object_id.find("'")+1:] - object_id = object_id[:object_id.find("'")] - return object_id + value = xml.attrib.get('select', None) + if value is not None: + if self.history_exp.search(value) is not None: + s = self.history_tag + object_id = value[value.find(s):] + object_id = object_id[object_id.find("'") + 1:] + object_id = object_id[:object_id.find("'")] + return object_id return object_id security.declareProtected(Permissions.AccessContentsInformation, @@ -635,9 +610,9 @@ class ERP5Conduit(XMLSyncUtilsMixin): Return the xml of the subobject which as the id object_id """ xml = self.convertToXml(xml) - for subnode in self.getElementNodeList(xml): - if subnode.nodeName == self.xml_object_tag: - if object_id == self.getAttribute(subnode,'id'): + for subnode in xml: + if subnode.tag == self.xml_object_tag: + if object_id == self.getAttribute(subnode, 'id'): return subnode return None @@ -646,11 +621,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ Retrieve the given parameter from the xml """ - for attribute in self.getAttributeNodeList(xml): - if attribute.nodeName == param: - data = attribute.value - return self.convertXmlValue(data, data_type='string') - return None + return xml.attrib.get(param, None) security.declareProtected(Permissions.AccessContentsInformation,'getObjectProperty') def getObjectProperty(self, property, xml, data_type=None): @@ -659,34 +630,24 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ xml = self.convertToXml(xml) # document, with childNodes[0] a DocumentType and childNodes[1] the Element Node - for subnode in self.getElementNodeList(xml): - if subnode.nodeName == property: - if data_type is None: - data_type = self.getPropertyType(subnode) - try: - data = subnode.childNodes[0].data - except IndexError: # There is no data - data = None - data = self.convertXmlValue(data, data_type=data_type) - return data + for subnode in xml: + if subnode.tag == property: + return self.convertXmlValue(subnode) return None security.declareProtected(Permissions.AccessContentsInformation,'convertToXml') - def convertToXml(self,xml): + def convertToXml(self, xml): """ if xml is a string, convert it to a node """ - if xml is None: - return - if type(xml) in (type('a'),type(u'a')): - if type(xml) is type(u'a'): + if xml is None: return None + if isinstance(xml, (str, unicode)): + if isinstance(xml, unicode): xml = xml.encode('utf-8') - xml = Parse(xml) - #LOG('Conduit.convertToXml not failed',0,'ok') - xml = xml.childNodes[0] # Because we just created a new xml + xml = etree.XML(xml) # If we have the xml from the node erp5, we just take the subnode - if xml.nodeName == 'erp5': - xml = self.getElementNodeList(xml)[0] + if xml.tag == 'erp5': + xml = xml[0] return xml security.declareProtected(Permissions.AccessContentsInformation,'getObjectType') @@ -694,23 +655,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ Retrieve the portal type from an xml """ - portal_type = None - attr_list = xml.xpath('.//@portal_type[1]') - if attr_list: - portal_type = attr_list[0].value.encode('utf-8') - return portal_type + return '%s' % xml.xpath('string(.//@portal_type)') security.declareProtected(Permissions.AccessContentsInformation,'getPropertyType') def getPropertyType(self, xml): """ Retrieve the portal type from an xml """ - p_type = None - attr_list = xml.xpath('.//@type[1]') - if attr_list: - p_type = attr_list[0].value.encode('utf-8') - p_type = self.convertXmlValue(p_type, data_type='string') - return p_type + return '%s' % xml.xpath('string(.//@type)') security.declareProtected(Permissions.AccessContentsInformation,'getXupdateObjectType') def getXupdateObjectType(self, xml): @@ -718,22 +670,11 @@ class ERP5Conduit(XMLSyncUtilsMixin): Retrieve the portal type from an xupdate XXXX This should not be used any more !!! XXXXXXXXXXX """ - if xml.nodeName.find('xupdate')>=0: - for subnode in self.getElementNodeList(xml): - if subnode.nodeName == 'xupdate:element': - for subnode1 in self.getElementNodeList(subnode): - if subnode1.nodeName == 'xupdate:attribute': - for attribute in subnode1.attributes: - if attribute.nodeName == 'name': - if attribute.nodeValue == 'portal_type': - data = subnode1.childNodes[0].data - data = self.convertXmlValue(data) - return data - return None - + return xml.xpath('string(.//*[name() == "xupdate:attribute"][@name = "portal_type"])') or None security.declareProtected(Permissions.ModifyPortalContent, 'newObject') - def newObject(self, object=None, xml=None, simulate=0, reset_local_roles=1, reset_workflow=1): + def newObject(self, object=None, xml=None, simulate=0, reset_local_roles=1, + reset_workflow=1): """ modify the object with datas from the xml (action section) @@ -747,24 +688,16 @@ class ERP5Conduit(XMLSyncUtilsMixin): object.manage_delLocalRoles(user_role_list) if getattr(object, 'workflow_history', None) is not None and reset_workflow: object.workflow_history = PersistentMapping() - if xml.nodeName.find('xupdate') >= 0: - xml = self.getElementNodeList(xml)[0] - for subnode in self.getElementNodeList(xml): - if subnode.nodeName not in self.NOT_EDITABLE_PROPERTY: + if xml.tag.find('xupdate') >= 0: + xml = xml[0] + for subnode in xml.xpath('*'): + #get only Element nodes (not Comments or Processing instructions) + if subnode.tag not in self.NOT_EDITABLE_PROPERTY: keyword_type = self.getPropertyType(subnode) # This is the case where the property is a list - keyword = str(subnode.nodeName) - data = None - while subnode.hasChildNodes(): # We check that this tag is not empty - subnode = subnode.firstChild - if subnode.nodeType == subnode.TEXT_NODE: - data = subnode.data - args[keyword] = data - #if args.has_key(keyword): - # LOG('newObject',0,'data: %s' % str(args[keyword])) - if args.has_key(keyword): - args[keyword] = self.convertXmlValue(args[keyword], keyword_type) - elif subnode.nodeName in self.ADDABLE_PROPERTY: + keyword = subnode.tag + args[keyword] = self.convertXmlValue(subnode, keyword_type) + elif subnode.tag in self.ADDABLE_PROPERTY + (self.xml_object_tag,): self.addNode(object=object, xml=subnode, force=1) # We should first edit the object args = self.getFormatedArgs(args=args) @@ -775,10 +708,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): object.manage_afterEdit() self.afterNewObject(object) - # Then we may create subobject - for subnode in self.getElementNodeList(xml): - if subnode.nodeName in (self.xml_object_tag,): #,self.history_tag): - self.addNode(object=object, xml=subnode) + ## Then we may create subobject + #for subnode in xml: + #if subnode.tag in (self.xml_object_tag,): #,self.history_tag): + #self.addNode(object=object, xml=subnode) security.declareProtected(Permissions.AccessContentsInformation,'afterNewObject') def afterNewObject(self, object): @@ -790,8 +723,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): Return a worklow status from xml """ status = {} - for subnode in self.getElementNodeList(xml): - keyword = self.convertXmlValue(subnode.nodeName) + for subnode in xml: + keyword = subnode.tag value = self.getObjectProperty(keyword, xml) status[keyword] = value return status @@ -801,45 +734,32 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ Retrieve the list of xupdate:element subnodes """ - e_list = [] - for subnode in self.getElementNodeList(xml): - if subnode.nodeName in self.XUPDATE_EL: - e_list += [subnode] - #LOG('getXupdateElementList, e_list:',0,e_list) - return e_list + return xml.xpath('|'.join(['.//*[name() = "%s"]' % name for name in self.XUPDATE_EL])) security.declareProtected(Permissions.AccessContentsInformation,'getElementFromXupdate') def getElementFromXupdate(self, xml): """ from a xupdate:element returns the element as xml """ - if xml.nodeName in self.XUPDATE_EL: - result = unicode('<', encoding='utf-8') - result += xml.attributes.values()[0].nodeValue - for subnode in self.getElementNodeList(xml): #getElementNodeList - if subnode.nodeName == 'xupdate:attribute': - result += ' %s=' % subnode.attributes.values()[0].nodeValue - result += '"%s"' % subnode.childNodes[0].nodeValue + if xml.xpath('name()') in self.XUPDATE_EL: + result = '<' + tag_name = xml.attrib.get('name') + result += tag_name + for subnode in xml: + if subnode.xpath('name()') == 'xupdate:attribute': + result += ' %s=' % subnode.attrib.get('name') + result += '"%s"' % subnode.text 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 = 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') maxi = max(xml_string.find('>')+1,\ xml_string.rfind('</xupdate:attribute>')+len('</xupdate:attribute>')) result += xml_string[maxi:xml_string.find('</xupdate:element>')] - result += '</%s>' % xml.attributes.values()[0].nodeValue - return self.convertToXml(result.encode('utf-8')) - if xml.nodeName in (self.XUPDATE_UPDATE+self.XUPDATE_DEL): + result += '</%s>' % tag_name + return self.convertToXml(result) + if xml.xpath('name()') in (self.XUPDATE_UPDATE + self.XUPDATE_DEL): result = u'<' - for subnode in self.getAttributeNodeList(xml): - if subnode.nodeName == 'select': - attribute = subnode.nodeValue + attribute = xml.attrib.get('select') s = '[@id=' s_place = attribute.find(s) select_id = None @@ -848,17 +768,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): select_id = select_id[:select_id.find("'",1)+1] else: s_place = len(attribute) - property = attribute[:s_place] - if property.find('/') == 0: - property = property[1:] + property = attribute[:s_place].strip('/') result += property if select_id is not None: result += ' id=%s' % select_id result += '>' 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)] + result += xml_string[maxi:xml_string.find('</%s>' % xml.xpath('name()'))] result += '</%s>' % (property) #LOG('getElementFromXupdate, result:',0,repr(result)) return self.convertToXml(result) @@ -870,49 +787,42 @@ class ERP5Conduit(XMLSyncUtilsMixin): Return the list of workflow actions """ action_list = [] - if xml.nodeName in self.XUPDATE_EL: - action_list += [xml] + if xml.xpath('name()') in self.XUPDATE_EL: + action_list.append(xml) return action_list - for subnode in self.getElementNodeList(xml): - if subnode.nodeName == self.action_tag: - action_list += [subnode] + for subnode in xml: + if subnode.tag == self.action_tag: + action_list.append(subnode) return action_list security.declareProtected(Permissions.AccessContentsInformation,'convertXmlValue') - def convertXmlValue(self, data, data_type=None): + def convertXmlValue(self, node, data_type=None): """ It is possible that the xml change the value, for example there is some too much '\n' and some spaces. We have to do some extra things so that we convert correctly the value XXXNicolas: I'm totally disagree with, so i comment this code """ - if data is None: + if node is None: return None + if data_type is None: + data_type = self.getPropertyType(node) + if data_type == self.none_type: return None + data = node.text + if data is not None and isinstance(data, unicode): + data = data.encode('utf-8') + elif data is None: if data_type in self.list_type_list: data = () - if data_type in self.text_type_list: + elif data_type in self.text_type_list: data = '' return data - #data = data.replace('\n','') - if isinstance(data, unicode): - data = data.encode(self.getEncoding()) - if data == 'None': - return None # We can now convert string in tuple, dict, binary... if data_type in self.list_type_list: - if type(data) is type('a'): - data = tuple(data.split('@@@')) + data = unmarshaler(node.text) elif data_type in self.text_type_list: - data = data.replace('@@@','\n') -# elif data_type in self.binary_type_list: -# data = data.replace('@@@','\n') -# msg = MIMEBase('application','octet-stream') -# 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') + msg = MIMEBase('application', 'octet-stream') Encoders.encode_base64(msg) msg.set_payload(data) data = msg.get_payload(decode=1) @@ -925,7 +835,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): if data == '{}': data = {} else: - dict_list = map(lambda x:x.split(':'),data[1:-1].split(',')) + dict_list = map(lambda x:x.split(':'), data[1:-1].split(',')) data = map(lambda (x,y):(x.replace(' ','').replace("'",''),int(y)),dict_list) data = dict(data) return data @@ -939,15 +849,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): Parse the xupdate and then it will call the conduit """ conflict_list = [] - if type(xupdate) in (type('a'),type(u'a')): - xupdate = Parse(xupdate) + if isinstance(xupdate, (str, unicode)): + xupdate = etree.XML(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 #del all sub_element - sub_node_list = self.getElementNodeList(xupdate) #clean the node - for subnode in sub_node_list: + for subnode in xupdate: #Create one xupdate:modification per update node conflict_list += self.updateNode(xml=subnode, object=object, @@ -955,38 +864,37 @@ class ERP5Conduit(XMLSyncUtilsMixin): simulate=simulate, **kw) return conflict_list - for subnode in self.getElementNodeList(xupdate): + for subnode in xupdate: sub_xupdate = self.getSubObjectXupdate(subnode) selection_name = '' - if subnode.nodeName in self.XUPDATE_INSERT_OR_ADD: - conflict_list += conduit.addNode(xml=sub_xupdate,object=object, \ - force=force, simulate=simulate, **kw)['conflict_list'] - elif subnode.nodeName in self.XUPDATE_DEL: - conflict_list += conduit.deleteNode(xml=sub_xupdate, object=object, \ - force=force, simulate=simulate, **kw) - elif subnode.nodeName in self.XUPDATE_UPDATE: - conflict_list += conduit.updateNode(xml=sub_xupdate, object=object, \ - force=force, simulate=simulate, **kw) - #elif subnode.nodeName in self.XUPDATE_INSERT: - # conflict_list += conduit.addNode(xml=subnode, object=object, force=force, **kw) + if subnode.xpath('name()') in self.XUPDATE_INSERT_OR_ADD: + conflict_list += conduit.addNode(xml=sub_xupdate,object=object, + force=force, simulate=simulate, + **kw)['conflict_list'] + elif subnode.xpath('name()') in self.XUPDATE_DEL: + conflict_list += conduit.deleteNode(xml=sub_xupdate, object=object, + force=force, simulate=simulate, **kw) + elif subnode.xpath('name()') in self.XUPDATE_UPDATE: + conflict_list += conduit.updateNode(xml=sub_xupdate, object=object, + force=force, simulate=simulate, **kw) return conflict_list def isMixedXupdate(self, xml): #If an xupdate:modifications contains modification which concerns different objects - subnode_list = self.getElementNodeList(xml) + subnode_list = xml nb_sub = len(subnode_list) comp = 0 for subnode in subnode_list: value = self.getAttribute(subnode, 'select') - if re.search(self.object_exp, value): + if self.object_exp.search(value) is not None: comp += 1 if nb_sub == comp: return 0 return 1 - def isWorkflowActionAddable(self, object=None,status=None,wf_tool=None, - wf_id=None,xml=None): + def isWorkflowActionAddable(self, object=None, status=None, wf_tool=None, + wf_id=None, xml=None): """ Some checking in order to check if we should add the workfow or not We should not returns a conflict list as we wanted before, we should @@ -1018,7 +926,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): This is really usefull if you want to write your own Conduit. """ - portal_types = getToolByName(object, 'portal_types') #LOG('ERP5Conduit.addNode',0,'portal_type: |%s|' % str(portal_type)) object.newContent(portal_type=portal_type, id=object_id) @@ -1037,7 +944,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): wf_id = self.getAttribute(xml,'id') if wf_id is None: # History added by xupdate wf_id = self.getHistoryIdFromSelect(xml) - xml = self.getElementNodeList(xml)[0] + xml = xml[0] #for action in self.getWorkflowActionFromXml(xml): status = self.getStatusFromXml(xml) #LOG('addNode, status:',0,status) @@ -1067,19 +974,17 @@ class ERP5Conduit(XMLSyncUtilsMixin): This allows to specify how to handle the local role informations. This is really usefull if you want to write your own Conduit. """ - conflict_list = [] # We want to add a local role - roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens') - user = self.getAttribute(xml,'id') + roles = self.convertXmlValue(xml, data_type='tokens') + user = self.getAttribute(xml, 'id') roles = list(roles) # Needed for CPS, or we have a CPS error #LOG('local_role: ',0,'user: %s roles: %s' % (repr(user),repr(roles))) #user = roles[0] #roles = roles[1:] - if xml.nodeName.find(self.local_role_tag)>=0: - object.manage_setLocalRoles(user,roles) - elif xml.nodeName.find(self.local_group_tag)>=0: - object.manage_setLocalGroupRoles(user,roles) - return conflict_list + if xml.tag.find(self.local_role_tag) >= 0: + object.manage_setLocalRoles(user, roles) + elif xml.tag.find(self.local_group_tag) >= 0: + object.manage_setLocalGroupRoles(user, roles) security.declareProtected(Permissions.ModifyPortalContent, 'addLocalPermissionNode') def addLocalPermissionNode(self, object, xml): @@ -1090,17 +995,17 @@ class ERP5Conduit(XMLSyncUtilsMixin): conflict_list = [] # We want to add a local role #LOG('addLocalPermissionNode, xml',0,xml) - if len(xml.childNodes)>0: - roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens') + if len(xml.text): + roles = self.convertXmlValue(xml, data_type='tokens') roles = list(roles) # Needed for CPS, or we have a CPS error else: roles = () - permission = self.getAttribute(xml,'id') + permission = self.getAttribute(xml, 'id') #LOG('local_role: ',0,'permission: %s roles: %s' % (repr(permission),repr(roles))) #user = roles[0] #roles = roles[1:] - if xml.nodeName.find(self.local_permission_tag)>=0: - object.manage_setLocalPermissions(permission,roles) + if xml.tag.find(self.local_permission_tag) >= 0: + object.manage_setLocalPermissions(permission, roles) return conflict_list security.declareProtected(Permissions.ModifyPortalContent, 'editDocument') @@ -1123,11 +1028,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ 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 + return etree.tostring(node, encoding='utf-8') def getGidFromObject(self, object): """ diff --git a/product/ERP5SyncML/Conduit/VCardConduit.py b/product/ERP5SyncML/Conduit/VCardConduit.py index f9be71f01d5174f39d965d7837e8ba5205349774..e441c23bf8204e8b16f1ffde390be8c3bdf4b5e8 100755 --- a/product/ERP5SyncML/Conduit/VCardConduit.py +++ b/product/ERP5SyncML/Conduit/VCardConduit.py @@ -69,8 +69,7 @@ class VCardConduit(ERP5Conduit, SyncCode): portal_type = 'Person' #the VCard can just use Person if sub_object is None: - new_object, reset_local_roles, reset_workflow = ERP5Conduit.constructContent(self, object, object_id, - portal_type) + new_object, reset_local_roles, reset_workflow = ERP5Conduit.constructContent(self, object, object_id, portal_type) else: #if the object exist, it juste must be update new_object = sub_object #LOG('addNode', 0, 'new_object:%s, sub_object:%s' % (new_object, sub_object)) @@ -80,7 +79,7 @@ class VCardConduit(ERP5Conduit, SyncCode): simulate=simulate, **kw) #in a first time, conflict are not used - return {'conflict_list':None, 'object': new_object} + return {'conflict_list':[], 'object': new_object} security.declareProtected(Permissions.ModifyPortalContent, 'deleteNode') def deleteNode(self, xml=None, object=None, object_id=None, force=None, @@ -89,12 +88,11 @@ class VCardConduit(ERP5Conduit, SyncCode): A node is deleted """ #LOG('deleteNode :', 0, 'object:%s, object_id:%s' % (str(object), str(object_id))) - conflict_list = [] try: object._delObject(object_id) except (AttributeError, KeyError): LOG('VCardConduit',0,'deleteNode, Unable to delete: %s' % str(object_id)) - return conflict_list + return [] security.declareProtected(Permissions.ModifyPortalContent, 'updateNode') def updateNode(self, xml=None, object=None, previous_xml=None, force=0, @@ -156,7 +154,7 @@ class VCardConduit(ERP5Conduit, SyncCode): property_value_list_well_incoded.append(property_value) #elif ... put here the other encodings else: - property_value_list_well_incoded=property_value_list + property_value_list_well_incoded = property_value_list return property_value_list_well_incoded @@ -175,7 +173,7 @@ class VCardConduit(ERP5Conduit, SyncCode): for vcard_line in vcard_list: if ':' in vcard_line: property, property_value = vcard_line.split(':') - property_value_list=property_value.split(';') + property_value_list = property_value.split(';') property_parameters_list = [] property_name = '' if ';' in property: @@ -195,28 +193,28 @@ class VCardConduit(ERP5Conduit, SyncCode): property_parameters_list = tmp #now property_parameters_list looks like : # [{'ENCODING':'QUOTED-PRINTABLE'}, {'CHARSET':'UTF-8'}] - + property_value_list = \ - self.changePropertyEncoding(property_parameters_list, - property_value_list) + self.changePropertyEncoding(property_parameters_list, + property_value_list) else: property_name=property - if type(property_name) is type(u'a'): + if isinstance(property_name, unicode): property_name = property_name.encode('utf-8') - tmp=[] + tmp = [] for property_value in property_value_list: - if type(property_value) is type(u'a'): + if isinstance(property_value, unicode): property_value = property_value.encode('utf-8') tmp.append(property_value) - property_value_list=tmp + property_value_list = tmp if property_name in convert_dict.keys(): if property_name == 'N' and len(property_value_list) > 1: - edit_dict[convert_dict['N']]=property_value_list[0] - edit_dict[convert_dict['FN']]=property_value_list[1] + edit_dict[convert_dict['N']] = property_value_list[0] + edit_dict[convert_dict['FN']] = property_value_list[1] else: - edit_dict[convert_dict[property_name]]=property_value_list[0] + edit_dict[convert_dict[property_name]] = property_value_list[0] #LOG('edit_dict =',0,edit_dict) return edit_dict diff --git a/product/ERP5SyncML/PublicationSynchronization.py b/product/ERP5SyncML/PublicationSynchronization.py index 66e15544de4e55f9c443329620a0a02a1afb974c..9cf38c169031c1c0b49da5e6b1874adbd9806f0a 100644 --- a/product/ERP5SyncML/PublicationSynchronization.py +++ b/product/ERP5SyncML/PublicationSynchronization.py @@ -29,7 +29,6 @@ import smtplib # to send emails from Publication import Publication,Subscriber from Subscription import Signature -from XMLSyncUtils import Parse from XMLSyncUtils import XMLSyncUtils from Conduit.ERP5Conduit import ERP5Conduit from Products.CMFCore.utils import getToolByName @@ -54,9 +53,9 @@ class PublicationSynchronization(XMLSyncUtils): """ LOG('PubSyncInit', INFO, 'Starting... publication: %s' % (publication.getPath())) #the session id is set at the same value of those of the client - subscriber.setSessionId(self.getSessionId(xml_client)) + subscriber.setSessionId(self.getSessionIdFromXml(xml_client)) #same for the message id - subscriber.setMessageId(self.getMessageId(xml_client)) + subscriber.setMessageId(self.getMessageIdFromXml(xml_client)) #at the begining of the synchronization the subscriber is not authenticated subscriber.setAuthenticated(False) #the last_message_id is 1 because the message that @@ -137,7 +136,7 @@ class PublicationSynchronization(XMLSyncUtils): if authentication_type == publication.getAuthenticationType(): authentication_format = publication.getAuthenticationFormat() decoded = subscriber.decode(authentication_format, data) - if decoded not in ('', None) and ':' in decoded: + if decoded and ':' in decoded: (login, password) = decoded.split(':') uf = self.getPortalObject().acl_users for plugin_name, plugin in uf._getOb('plugins').listPlugins( diff --git a/product/ERP5SyncML/Subscription.py b/product/ERP5SyncML/Subscription.py index 12bfb1e9e962f1927247c4686436229586faae83..d0f3f30a5f7e2c60dbeb5150c6411842c6feb8dc 100644 --- a/product/ERP5SyncML/Subscription.py +++ b/product/ERP5SyncML/Subscription.py @@ -492,10 +492,6 @@ class Signature(Folder, SyncCode): Set the partial string we will have to deliver in the future """ - if type(xml) is type(u'a'): - xml = xml.encode('utf-8') - if xml is not None: - xml = xml.replace('@-@@-@','--') # need to put back '--' self.partial_xml = xml def getPartialXML(self): diff --git a/product/ERP5SyncML/SubscriptionSynchronization.py b/product/ERP5SyncML/SubscriptionSynchronization.py index eb975b5d9de34607dcdeb61e5d45c369be53de83..1418291a05362a67597746e868a4c1d1693afbeb 100644 --- a/product/ERP5SyncML/SubscriptionSynchronization.py +++ b/product/ERP5SyncML/SubscriptionSynchronization.py @@ -28,7 +28,7 @@ import smtplib # to send emails from Subscription import Subscription,Signature -from XMLSyncUtils import XMLSyncUtils, Parse +from XMLSyncUtils import XMLSyncUtils import commands from Conduit.ERP5Conduit import ERP5Conduit from AccessControl import getSecurityManager diff --git a/product/ERP5SyncML/SyncCode.py b/product/ERP5SyncML/SyncCode.py index d51f12a413bad32458af7891aefff026d80e5ec1..a5fc59bc71b005e4a8c7583236e9cb636afcff33 100644 --- a/product/ERP5SyncML/SyncCode.py +++ b/product/ERP5SyncML/SyncCode.py @@ -28,6 +28,7 @@ from Products.ERP5Type.Accessor.TypeDefinition import list_types from Globals import Persistent +import re class SyncCode(Persistent): """ @@ -87,6 +88,7 @@ class SyncCode(Persistent): tuple(XUPDATE_UPDATE) + tuple(XUPDATE_DEL) text_type_list = ('text','string') list_type_list = list_types + none_type = 'None' force_conflict_list = ('layout_and_schema','ModificationDate') binary_type_list = ('image','file','document','pickle') date_type_list = ('date',) @@ -105,18 +107,12 @@ class SyncCode(Persistent): ADDABLE_PROPERTY = local_role_list + (history_tag,) + local_permission_list NOT_EDITABLE_PROPERTY = ('id','object','uid','xupdate:attribute') \ + XUPDATE_EL + ADDABLE_PROPERTY - sub_object_exp = "/object\[@id='.*'\]/" - object_exp = "/object\[@id='.*'\]" - sub_sub_object_exp = "/object\[@id='.*'\]/object\[@id='.*'\]/" - history_exp = "/%s\[@id='.*'\]" % history_tag - local_role_exp = "/%s\[@id='.*'\]" % local_role_tag - local_group_exp = "/%s\[@id='.*'\]" % local_group_tag - bad_local_role_exp = "/%s\[@id='.*'\]/" % local_role_tag - bad_local_group_exp = "/%s\[@id='.*'\]/" % local_group_tag - bad_history_exp = "/%s\[@id='.*'\]/" % history_tag - local_role_and_group_list = (local_group_exp,local_role_exp) - bad_local_role_and_group_list = (bad_local_group_exp,bad_local_role_exp) - + sub_object_exp = re.compile("/object\[@id='.*'\]/") + object_exp = re.compile("/object\[@id='.*'\]") + attribute_type_exp = re.compile("^.*attribute::type$") + sub_sub_object_exp = re.compile("/object\[@id='.*'\]/object\[@id='.*'\]/") + history_exp = re.compile("/%s\[@id='.*'\]" % history_tag) + bad_history_exp = re.compile("/%s\[@id='.*'\]/" % history_tag) #media types : diff --git a/product/ERP5SyncML/SynchronizationTool.py b/product/ERP5SyncML/SynchronizationTool.py index ac7c7e0083bcd17bcb4f5901f3860966946bc7ed..26b5bdd43879646df19ccc94d9a616f18bad466d 100644 --- a/product/ERP5SyncML/SynchronizationTool.py +++ b/product/ERP5SyncML/SynchronizationTool.py @@ -40,7 +40,6 @@ from Products.ERP5SyncML import Conduit from Publication import Publication, Subscriber from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2 from Subscription import Subscription -from XMLSyncUtils import Parse from Products.ERP5Type import Permissions from PublicationSynchronization import PublicationSynchronization from SubscriptionSynchronization import SubscriptionSynchronization @@ -490,7 +489,7 @@ class SynchronizationTool( SubscriptionSynchronization, for conflict in conflict_list: if conflict.getObjectPath() == path: #LOG('getSynchronizationState', DEBUG, 'found a conflict: %s' % str(conflict)) - state_list += [[conflict.getSubscriber(),self.CONFLICT]] + state_list.append([conflict.getSubscriber(), self.CONFLICT]) for domain in self.getSynchronizationList(): destination = domain.getDestinationPath() #LOG('getSynchronizationState', TRACE, 'destination: %s' % str(destination)) @@ -499,7 +498,6 @@ class SynchronizationTool( SubscriptionSynchronization, if j_path.find(destination)==0: o_id = j_path[len(destination)+1:].split('/')[0] #LOG('getSynchronizationState', TRACE, 'o_id: %s' % o_id) - subscriber_list = [] if domain.domain_type==self.PUB: subscriber_list = domain.getSubscriberList() else: @@ -512,13 +510,13 @@ class SynchronizationTool( SubscriptionSynchronization, state = signature.getStatus() #LOG('getSynchronizationState:', TRACE, 'sub.dest :%s, state: %s' % \ #(subscriber.getSubscriptionUrl(),str(state))) - found = None + found = False # Make sure there is not already a conflict giving the state for state_item in state_list: - if state_item[0]==subscriber: - found = 1 - if found is None: - state_list += [[subscriber,state]] + if state_item[0] == subscriber: + found = True + if not found: + state_list.append([subscriber, state]) return state_list security.declareProtected(Permissions.AccessContentsInformation, diff --git a/product/ERP5SyncML/XMLSyncUtils.py b/product/ERP5SyncML/XMLSyncUtils.py index b33ce0766b0591d9070b489fd2c9d1f6c987c523..0cdff41132760e441b5fb41d958a52b1a74cf66f 100644 --- a/product/ERP5SyncML/XMLSyncUtils.py +++ b/product/ERP5SyncML/XMLSyncUtils.py @@ -30,27 +30,18 @@ import smtplib from Products.ERP5SyncML.SyncCode import SyncCode from Products.ERP5SyncML.Subscription import Signature from AccessControl.SecurityManagement import newSecurityManager -from StringIO import StringIO from ERP5Diff import ERP5Diff from zLOG import LOG, INFO -try: - from Ft.Xml import Parse -except ImportError: - LOG('XMLSyncUtils', INFO, "Can't import Parse") - class Parse: - def __init__(self, *args, **kw): - raise ImportError, '4Suite-XML is not installed' +from lxml import etree +from lxml.etree import Element +from lxml.builder import E +from xml.dom import minidom try: from base64 import b16encode, b16decode except ImportError: from base64 import encodestring as b16encode, decodestring as b16decode -try: - from Ft.Xml.Domlette import PrettyPrint -except ImportError: - from xml.dom.ext import PrettyPrint -from xml.dom import minidom class XMLSyncUtilsMixin(SyncCode): @@ -61,34 +52,30 @@ class XMLSyncUtilsMixin(SyncCode): Since the Header is always almost the same, this is the way to set one quickly. """ - xml_list = [] - xml = xml_list.append - xml(' <SyncHdr>\n') - xml(' <VerDTD>1.1</VerDTD>\n') - xml(' <VerProto>SyncML/1.1</VerProto>\n') - xml(' <SessionID>%s</SessionID>\n' % session_id) - xml(' <MsgID>%s</MsgID>\n' % msg_id) - xml(' <Target>\n') - xml(' <LocURI>%s</LocURI>\n' % target) - if target_name not in (None, ''): - xml(' <LocName>%s</LocName>\n' %target_name) - xml(' </Target>\n') - xml(' <Source>\n') - xml(' <LocURI>%s</LocURI>\n' % source) - if source_name not in (None, ''): - xml(' <LocName>%s</LocName>\n' % source_name) - xml(' </Source>\n') - if dataCred not in (None, ''): - xml(' <Cred>\n') - xml(' <Meta>\n') - xml(" <Format xmlns='syncml:metinf'>%s</Format>\n" % authentication_format) - xml(" <Type xmlns='syncml:metinf'>%s</Type>\n" % authentication_type) - xml(' </Meta>\n') - xml(' <Data>%s</Data>\n' % dataCred) - xml(' </Cred>\n') - xml(' </SyncHdr>\n') - xml_a = ''.join(xml_list) - return xml_a + if source: + if source_name: + source_name = source_name.decode('utf-8') + node_to_append = E.Source(E.LocURI(source), E.LocName(source_name or '')) + else: + if target_name: + target_name = target_name.decode('utf-8') + node_to_append = E.Target(E.LocURI(target), E.LocName(target_name or '')) + + xml = (E.SyncHdr( + E.VerDTD('1.1'), + E.VerProto('SyncML/1.1'), + E.SessionID('%s' % session_id), + E.MsgID('%s' % msg_id), + node_to_append + )) + if dataCred: + xml.append(E.Cred( + E.Meta(E.Format(authentication_format, xmlns='syncml:metinf'), + E.Type(authentication_type, xmlns='syncml:metinf'),), + E.Data(dataCred) + )) + return etree.tostring(xml, encoding='utf-8', + xml_declaration=False, pretty_print=False) def SyncMLAlert(self, cmd_id, sync_code, target, source, last_anchor, next_anchor): @@ -96,28 +83,26 @@ class XMLSyncUtilsMixin(SyncCode): Since the Alert section is always almost the same, this is the way to set one quickly. """ - xml_list = [] - xml = xml_list.append - xml(' <Alert>\n') - xml(' <CmdID>%s</CmdID>\n' % cmd_id) - xml(' <Data>%s</Data>\n' % sync_code) - xml(' <Item>\n') - xml(' <Target>\n') - xml(' <LocURI>%s</LocURI>\n' % target) - xml(' </Target>\n') - xml(' <Source>\n') - xml(' <LocURI>%s</LocURI>\n' % source) - xml(' </Source>\n') - xml(' <Meta>\n') - xml(' <Anchor>\n') - xml(' <Last>%s</Last>\n' % last_anchor) - xml(' <Next>%s</Next>\n' % next_anchor) - xml(' </Anchor>\n') - xml(' </Meta>\n') - xml(' </Item>\n') - xml(' </Alert>\n') - xml_a = ''.join(xml_list) - return xml_a + xml = (E.Alert( + E.CmdID('%s' % cmd_id), + E.Data('%s' % sync_code), + E.Item( + E.Target( + E.LocURI(target) + ), + E.Source( + E.LocURI(source) + ), + E.Meta( + E.Anchor( + E.Last(last_anchor), + E.Next(next_anchor) + ) + ) + ) + )) + return etree.tostring(xml, encoding='utf-8', + xml_declaration=False, pretty_print=False) def SyncMLStatus(self, remote_xml, data_code, cmd_id, next_anchor, subscription=None): @@ -127,56 +112,50 @@ class XMLSyncUtilsMixin(SyncCode): """ #list of element in the SyncBody bloc - syncbody_element_list = remote_xml.xpath('//SyncBody/*') - message_id = self.getMessageId(remote_xml) + sub_syncbody_element_list = remote_xml.xpath('/SyncML/SyncBody/*') + message_id = self.getMessageIdFromXml(remote_xml) xml_list = [] xml = xml_list.append if data_code != self.AUTH_REQUIRED:#because for AUTH_REQUIRED, SyncMLChal is #called # status for SyncHdr - message_id = self.getMessageId(remote_xml) xml(' <Status>\n') xml(' <CmdID>%s</CmdID>\n' % cmd_id) cmd_id += 1 - xml(' <MsgRef>%s</MsgRef>\n' % self.getMessageId(remote_xml)) + xml(' <MsgRef>%s</MsgRef>\n' % message_id) xml(' <CmdRef>0</CmdRef>\n') #to make reference to the SyncHdr, it's 0 xml(' <Cmd>SyncHdr</Cmd>\n') - xml(' <TargetRef>%s</TargetRef>\n' \ - % remote_xml.xpath('string(//SyncHdr/Target/LocURI)').encode('utf-8')) - xml(' <SourceRef>%s</SourceRef>\n' \ - % remote_xml.xpath('string(//SyncHdr/Source/LocURI)').encode('utf-8')) - if isinstance(data_code, int): - data_code = str(data_code) - xml(' <Data>%s</Data>\n' % data_code) + target_uri = remote_xml.xpath('string(/SyncML/SyncHdr/Target/LocURI)') + xml(' <TargetRef>%s</TargetRef>\n' % (target_uri)) + source_uri = remote_xml.xpath('string(/SyncML/SyncHdr/Source/LocURI)') + xml(' <SourceRef>%s</SourceRef>\n' % (source_uri)) + xml(' <Data>%s</Data>\n' % (data_code)) xml(' </Status>\n') #add the status bloc corresponding to the receive command - for syncbody_element in syncbody_element_list: - #LOG('SyncMLStatus : ', DEBUG, "command:%s, subscription:%s" % (str(syncbody_element.nodeName), subscription)) - if str(syncbody_element.nodeName) not in ('Status', 'Final', 'Get'): + for sub_syncbody_element in sub_syncbody_element_list: + #LOG('SyncMLStatus : ', DEBUG, "command:%s, subscription:%s" % (str(syncbody_element.tag), subscription)) + if sub_syncbody_element.tag not in ('Status', 'Final', 'Get'): xml(' <Status>\n') xml(' <CmdID>%s</CmdID>\n' % cmd_id) cmd_id += 1 xml(' <MsgRef>%s</MsgRef>\n' % message_id) xml(' <CmdRef>%s</CmdRef>\n' \ - % syncbody_element.xpath('string(.//CmdID)').encode('utf-8')) - xml(' <Cmd>%s</Cmd>\n' % syncbody_element.nodeName.encode('utf-8')) + % sub_syncbody_element.xpath('string(.//CmdID)')) + xml(' <Cmd>%s</Cmd>\n' % sub_syncbody_element.tag) - target_ref = syncbody_element.xpath('string(.//Target/LocURI)').encode('utf-8') - if target_ref not in (None, ''): + target_ref = sub_syncbody_element.xpath('string(.//Target/LocURI)') + if target_ref: xml(' <TargetRef>%s</TargetRef>\n' % target_ref ) - source_ref = syncbody_element.xpath('string(.//Source/LocURI)').encode('utf-8') - if source_ref not in (None, ''): + source_ref = sub_syncbody_element.xpath('string(.//Source/LocURI)') + if source_ref: xml(' <SourceRef>%s</SourceRef>\n' % source_ref ) - if syncbody_element.nodeName.encode('utf-8') == 'Add': - xml(' <Data>%s</Data>\n' % str(self.ITEM_ADDED)) - elif syncbody_element.nodeName.encode('utf-8') == 'Alert' and \ - syncbody_element.xpath('string(.//Data)').encode('utf-8') == \ + if sub_syncbody_element.tag == 'Add': + xml(' <Data>%s</Data>\n' % self.ITEM_ADDED) + elif sub_syncbody_element.tag == 'Alert' and \ + sub_syncbody_element.xpath('string(.//Data)') == \ str(self.SLOW_SYNC): - xml(' <Data>%s</Data>\n' % str(self.REFRESH_REQUIRED)) - else: - xml(' <Data>%s</Data>\n' % str(self.SUCCESS)) - - if str(syncbody_element.nodeName) == 'Alert': + xml(' <Data>%s</Data>\n' % self.REFRESH_REQUIRED) + elif sub_syncbody_element.tag == 'Alert': xml(' <Item>\n') xml(' <Data>\n') xml(' <Anchor>\n') @@ -184,16 +163,18 @@ class XMLSyncUtilsMixin(SyncCode): xml(' </Anchor>\n') xml(' </Data>\n') xml(' </Item>\n') + else: + xml(' <Data>%s</Data>\n' % self.SUCCESS) xml(' </Status>\n') - if str(syncbody_element.nodeName) == 'Get' and subscription != None: - cmd_ref = syncbody_element.xpath('string(.//CmdID)').encode('utf-8') + if sub_syncbody_element.tag == 'Get' and subscription is not None: + cmd_ref = '%s' % sub_syncbody_element.xpath('string(.//CmdID)') syncml_result = self.SyncMLPut( cmd_id, subscription, markup='Results', cmd_ref=cmd_ref, - message_id=self.getMessageId(remote_xml)) + message_id=message_id) xml(syncml_result) cmd_id += 1 xml_a = ''.join(xml_list) @@ -207,57 +188,51 @@ class XMLSyncUtilsMixin(SyncCode): synchronized """ if remote_xml is not None: - msg_ref=remote_xml.xpath("string(/SyncML/SyncHdr/MsgID)").encode('utf-8') - cmd_ref=remote_xml.xpath("string(.//CmdID)").encode('utf-8') - target_ref=remote_xml.xpath("string(.//Target/LocURI)").encode('utf-8') - source_ref=remote_xml.xpath("string(.//Source/LocURI)").encode('utf-8') - - xml_list = [] - xml = xml_list.append - xml(' <Status>\n') - #here there is a lot of test to keep compatibility with older call - if cmd_id not in (None,'') : - xml(' <CmdID>%s</CmdID>\n' % cmd_id) - if msg_ref not in (None,''): - xml(' <MsgRef>%s</MsgRef>\n' % msg_ref) - if cmd_ref not in (None,''): - xml(' <CmdRef>%s</CmdRef>\n' %cmd_ref) - if cmd not in (None,''): - xml(' <Cmd>%s</Cmd>\n' % cmd) - if target_ref not in (None,''): - xml(' <TargetRef>%s</TargetRef>\n' % target_ref) - if source_ref not in (None,''): - xml(' <SourceRef>%s</SourceRef>\n' % source_ref) - if sync_code not in (None,''): - xml(' <Data>%s</Data>\n' % sync_code) - xml(' </Status>\n') - xml_a = ''.join(xml_list) - return xml_a + msg_ref = '%s' % remote_xml.xpath("string(/SyncML/SyncHdr/MsgID)") + cmd_ref = '%s' % remote_xml.xpath("string(.//CmdID)") + target_ref = '%s' % remote_xml.xpath("string(.//Target/LocURI)") + source_ref = '%s' % remote_xml.xpath("string(.//Source/LocURI)") + xml = (E.Status()) + if cmd_id: + xml.append(E.CmdID('%s' % cmd_id)) + if msg_ref: + xml.append(E.MsgRef(msg_ref)) + if cmd_ref: + xml.append(E.CmdRef(cmd_ref)) + if cmd: + xml.append(E.Cmd(cmd)) + if target_ref: + xml.append(E.TargetRef(target_ref)) + if source_ref: + xml.append(E.SourceRef(source_ref)) + if sync_code: + xml.append(E.Data('%s'% sync_code)) + + return etree.tostring(xml, encoding='utf-8', + xml_declaration=False, pretty_print=False) def SyncMLChal(self, cmd_id, cmd, target_ref, source_ref, auth_format, auth_type, data_code): """ This is used in order to ask crendentials """ - xml_list = [] - xml = xml_list.append - xml(' <Status>\n') - xml(' <CmdID>%s</CmdID>\n' % cmd_id) - xml(' <MsgRef>1</MsgRef>\n') - xml(' <CmdRef>0</CmdRef>\n') - xml(' <Cmd>%s</Cmd>\n' % cmd) - xml(' <TargetRef>%s</TargetRef>\n' % target_ref) - xml(' <SourceRef>%s</SourceRef>\n' % source_ref) - xml(' <Chal>\n') - xml(' <Meta>\n') - xml(" <Format xmlns='syncml:metinf'>%s</Format>\n" % auth_format) - xml(" <Type xmlns='syncml:metinf'>%s</Type>\n" % auth_type) - xml(' </Meta>\n') - xml(' </Chal>\n') - xml(' <Data>%s</Data>\n' % str(data_code)) - xml(' </Status>\n') - xml_a = ''.join(xml_list) - return xml_a + xml = (E.Status( + E.CmdID('%s' % cmd_id), + E.MsgRef('1'), + E.CmdRef('0'), + E.Cmd(cmd), + E.TargetRef(target_ref), + E.SourceRef(source_ref), + E.Chal( + E.Meta( + E.Format(auth_format, xmlns='syncml:metinf'), + E.Type(auth_type, xmlns='syncml:metinf') + ) + ), + E.Data('%s' % data_code) + )) + return etree.tostring(xml, encoding='utf-8', + xml_declaration=False, pretty_print=False) def SyncMLPut(self, cmd_id, subscription, markup='Put', cmd_ref=None, message_id=None): @@ -269,16 +244,16 @@ class XMLSyncUtilsMixin(SyncCode): conduit_name = subscription.getConduit() conduit = self.getConduitByName(conduit_name) #if the conduit support the SyncMLPut : - if hasattr(conduit, 'getCapabilitiesCTTypeList') and \ - hasattr(conduit, 'getCapabilitiesVerCTList') and \ - hasattr(conduit, 'getPreferedCapabilitieVerCT'): + if getattr(conduit, 'getCapabilitiesCTTypeList', None) is not None and \ + getattr(conduit, 'getCapabilitiesVerCTList', None) is not None and \ + getattr(conduit, 'getPreferedCapabilitieVerCT', None) is not None: xml_list = [] xml = xml_list.append xml(' <%s>\n' % markup) xml(' <CmdID>%s</CmdID>\n' % cmd_id) - if message_id not in (None, ''): + if message_id: xml(' <MsgRef>%s</MsgRef>\n' % message_id) - if cmd_ref not in (None, '') : + if cmd_ref: xml(' <CmdRef>%s</CmdRef>\n' % cmd_ref) xml(' <Meta>\n') xml(' <Type>application/vnd.syncml-devinf+xml</Type>\n'); @@ -365,84 +340,80 @@ class XMLSyncUtilsMixin(SyncCode): """ Add an object with the SyncML protocol """ - xml_list = [] - xml = xml_list.append - xml(' <Add>\n') - xml(' <CmdID>%s</CmdID>\n' % cmd_id) - xml(' <Meta>\n') - xml(' <Type>%s</Type>\n' % media_type) - xml(' </Meta>\n') - xml(' <Item>\n') - xml(' <Source>\n') - xml(' <LocURI>%s</LocURI>\n' % gid) - xml(' </Source>\n') - if media_type == self.MEDIA_TYPE['TEXT_XML']: - xml(' <Data>') - xml(xml_string) - xml('</Data>\n') + data_node = Element('Data') + if media_type == self.MEDIA_TYPE['TEXT_XML'] and isinstance(xml_string, str): + data_node.append(etree.XML(xml_string)) + elif media_type == self.MEDIA_TYPE['TEXT_XML'] and \ + not isinstance(xml_string, str): + #xml_string could be Partial element if partial XML + data_node.append(xml_string) else: - xml(' <Data><![CDATA[') - xml(xml_string) - xml('\n]]></Data>\n') - if more_data == 1: - xml(' <MoreData/>\n') - xml(' </Item>\n') - xml(' </Add>\n') - xml_a = ''.join(xml_list) - return xml_a + cdata = etree.CDATA(xml_string.decode('utf-8')) + data_node.text = cdata + xml = (E.Add( + E.CmdID('%s' % cmd_id), + E.Meta( + E.Type(media_type) + ), + E.Item( + E.Source( + E.LocURI(gid) + ), + data_node + ) + )) + if more_data: + item_node = xml.find('Item') + item_node.append(Element('MoreData')) + return etree.tostring(xml, encoding='utf-8', + xml_declaration=False, pretty_print=False) def deleteXMLObject(self, cmd_id=0, object_gid=None, rid=None, xml_object=''): """ Delete an object with the SyncML protocol """ - xml_list = [] - xml = xml_list.append - xml(' <Delete>\n') - xml(' <CmdID>%s</CmdID>\n' % cmd_id) - xml(' <Item>\n') - if rid not in (None, ''): - xml(' <Target>\n') - xml(' <LocURI>%s</LocURI>\n' % rid) - xml(' </Target>\n') + if rid: + elem_to_append = E.Target(E.LocURI('%s' % rid)) else: - xml(' <Source>\n') - xml(' <LocURI>%s</LocURI>\n' % object_gid) - xml(' </Source>\n') - xml(' </Item>\n') - xml(' </Delete>\n') - xml_a = ''.join(xml_list) - return xml_a + elem_to_append = E.Source(E.LocURI('%s' % object_gid)) + xml = (E.Delete( + E.CmdID('%s' % cmd_id), + E.Item( + elem_to_append + ) + )) + return etree.tostring(xml, encoding='utf-8', + xml_declaration=False, pretty_print=False) def replaceXMLObject(self, cmd_id=0, object=None, xml_string=None, more_data=0, gid=None, rid=None, media_type=None): """ Replace an object with the SyncML protocol """ - xml_list = [] - xml = xml_list.append - xml(' <Replace>\n') - xml(' <CmdID>%s</CmdID>\n' % cmd_id) - xml(' <Meta>\n') - xml(' <Type>%s</Type>\n' % media_type) - xml(' </Meta>\n') - xml(' <Item>\n') - if rid is not None: - xml(' <Target>\n') - xml(' <LocURI>%s</LocURI>\n' % str(rid)) - xml(' </Target>\n') + if rid: + elem_to_append = E.Target(E.LocURI('%s' % rid)) else: - xml(' <Source>\n') - xml(' <LocURI>%s</LocURI>\n' % str(gid)) - xml(' </Source>\n') - xml(' <Data>') - xml(xml_string) - xml(' </Data>\n') - if more_data == 1: - xml(' <MoreData/>\n') - xml(' </Item>\n') - xml(' </Replace>\n') - xml_a = ''.join(xml_list) - return xml_a + elem_to_append = E.Source(E.LocURI('%s' % gid)) + data_node = Element('Data') + if not isinstance(xml_string, (str, unicode)): + data_node.append(xml_string) + else: + data_node.append(etree.XML(xml_string)) + xml = (E.Replace( + E.CmdID('%s' % cmd_id), + E.Meta( + E.Type(media_type) + ), + E.Item( + elem_to_append, + data_node + ) + )) + if more_data: + item_node = xml.find('Item') + item_node.append(Element('MoreData')) + return etree.tostring(xml, encoding='utf-8', + xml_declaration=False, pretty_print=False) def getXupdateObject(self, object_xml=None, old_xml=None): """ @@ -467,93 +438,68 @@ class XMLSyncUtilsMixin(SyncCode): xupdate = xupdate[xupdate.find('<xupdate:modifications'):] return xupdate - def getSessionId(self, xml): + def getSessionIdFromXml(self, xml): """ We will retrieve the session id of the message """ - session_id = 0 - session_id = xml.xpath('string(/SyncML/SyncHdr/SessionID)') - session_id = int(session_id) - return session_id + return int(xml.xpath('string(/SyncML/SyncHdr/SessionID)')) - def getMessageId(self, xml): + def getMessageIdFromXml(self, xml): """ We will retrieve the message id of the message """ - message_id = 0 - message_id = xml.xpath('string(/SyncML/SyncHdr/MsgID)') - message_id = int(message_id) - return message_id + return int(xml.xpath('string(/SyncML/SyncHdr/MsgID)')) def getTarget(self, xml): """ return the target in the SyncHdr section """ - url = '' - url = xml.xpath('string(/SyncML/SyncHdr/Target/LocURI)') - url = url.encode('utf-8') - return url + return '%s' % xml.xpath('string(/SyncML/SyncHdr/Target/LocURI)') def getAlertLastAnchor(self, xml_stream): """ Return the value of the last anchor, in the alert section of the xml_stream """ - last_anchor = xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Last)') - last_anchor = last_anchor.encode('utf-8') - return last_anchor + return '%s' % xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Last)') def getAlertNextAnchor(self, xml_stream): """ Return the value of the next anchor, in the alert section of the xml_stream """ - next_anchor = xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Next)') - next_anchor = next_anchor.encode('utf-8') - return next_anchor + return '%s' % xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Next)') def getSourceURI(self, xml): """ return the source uri of the data base """ - source_uri = xml.xpath('string(//SyncBody/Alert/Item/Source/LocURI)') - if isinstance(source_uri, unicode): - source_uri = source_uri.encode('utf-8') - return source_uri + return '%s' % xml.xpath('string(//SyncBody/Alert/Item/Source/LocURI)') def getTargetURI(self, xml): """ return the target uri of the data base """ - target_uri = xml.xpath('string(//SyncBody/Alert/Item/Target/LocURI)') - if isinstance(target_uri, unicode): - target_uri = target_uri.encode('utf-8') - return target_uri + return '%s' % xml.xpath('string(//SyncBody/Alert/Item/Target/LocURI)') def getSubscriptionUrlFromXML(self, xml): """ return the source URI of the syncml header """ - subscription_url = xml.xpath('string(//SyncHdr/Source/LocURI)') - if isinstance(subscription_url, unicode): - subscription_url = subscription_url.encode('utf-8') - return subscription_url + return '%s' % xml.xpath('string(//SyncHdr/Source/LocURI)') def getStatusTarget(self, xml): """ Return the value of the alert code inside the xml_stream """ - status = xml.xpath('string(TargetRef)') - if isinstance(status, unicode): - status = status.encode('utf-8') - return status + return '%s' % xml.xpath('string(TargetRef)') def getStatusCode(self, xml): """ Return the value of the alert code inside the xml_stream """ - status_code = xml.xpath('string(Data)') - if status_code not in ('', None, []): + status_code = '%s' % xml.xpath('string(Data)') + if status_code: return int(status_code) return None @@ -562,81 +508,64 @@ class XMLSyncUtilsMixin(SyncCode): Return the value of the command inside the xml_stream """ cmd = None - if xml.nodeName=='Status': - cmd = xml.xpath('string(//Status/Cmd)') - if isinstance(cmd, unicode): - cmd = cmd.encode('utf-8') + if xml.tag == 'Status': + cmd = '%s' % xml.xpath('string(//Status/Cmd)') return cmd def getCred(self, xml): """ return the credential information : type, format and data """ - format='' - type='' - data='' - - first_node = xml.childNodes[0] - format = first_node.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Format'])") - type = first_node.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Type'])") - data = first_node.xpath('string(/SyncML/SyncHdr/Cred/Data)') + format = '%s' % xml.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Format'])") + type = '%s' % xml.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Type'])") + data = '%s' % xml.xpath('string(/SyncML/SyncHdr/Cred/Data)') - format = format.encode('utf-8') - type = type.encode('utf-8') - data = data.encode('utf-8') return (format, type, data) def checkCred(self, xml_stream): """ Check if there's a Cred section in the xml_stream """ - return xml_stream.xpath('string(SyncML/SyncHdr/Cred)') not in ('', None, []) + return bool(xml_stream.xpath('string(/SyncML/SyncHdr/Cred)')) def getChal(self, xml): """ return the chalenge information : format and type """ - format=None - type=None - - first_node = xml.childNodes[0] - format = first_node.xpath("string(//*[local-name() = 'Format'])") - type = first_node.xpath("string(//*[local-name() = 'Type'])") - - format = format.encode('utf-8') - type = type.encode('utf-8') + format = '%s' % xml.xpath("string(//*[local-name() = 'Format'])") + type = '%s' % xml.xpath("string(//*[local-name() = 'Type'])") return (format, type) def checkChal(self, xml_stream): """ Check if there's a Chal section in the xml_stream """ - return xml_stream.xpath('string(SyncML/SyncBody/Status/Chal)') not in ('', None, []) + return bool(xml_stream.xpath('string(/SyncML/SyncBody/Status/Chal)')) def checkMap(self, xml_stream): """ Check if there's a Map section in the xml_stream """ - return xml_stream.xpath('string(SyncML/SyncBody/Map)') not in ('', None, []) + return bool(xml_stream.xpath('string(/SyncML/SyncBody/Map)')) def setRidWithMap(self, xml_stream, subscriber): """ get all the local objects of the given target id and set them the rid with the given source id (in the Map section) """ - item_list = xml_stream.xpath('SyncML/SyncBody/Map/MapItem') + item_list = xml_stream.xpath('/SyncML/SyncBody/Map/MapItem') for map_item in item_list: - gid = map_item.xpath('string(.//Target/LocURI)').encode('utf-8') + gid = '%s' % map_item.xpath('string(.//Target/LocURI)') signature = subscriber.getSignatureFromGid(gid) - rid = map_item.xpath('string(.//Source/LocURI)').encode('utf-8') + rid = '%s' % map_item.xpath('string(.//Source/LocURI)') signature.setRid(rid) def getAlertCodeFromXML(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, ''): + alert_code = '%s' % xml_stream.xpath('string(/SyncML/SyncBody/Alert/Data)') + if alert_code: return int(alert_code) else: return None @@ -645,19 +574,19 @@ class XMLSyncUtilsMixin(SyncCode): """ Check if there's an Alert section in the xml_stream """ - return xml_stream.xpath('string(SyncML/SyncBody/Alert)') not in ('', None, []) + return bool(xml_stream.xpath('string(/SyncML/SyncBody/Alert)')) def checkSync(self, xml_stream): """ Check if there's an Sync section in the xml_xtream """ - return xml_stream.xpath('string(SyncML/SyncBody/Sync)') not in ('', None, []) + return bool(xml_stream.xpath('string(/SyncML/SyncBody/Sync)')) def checkStatus(self, xml_stream): """ Check if there's a Status section in the xml_xtream """ - return xml_stream.xpath('string(SyncML/SyncBody/Status)') not in ('', None, []) + return bool(xml_stream.xpath('string(/SyncML/SyncBody/Status)')) def getSyncActionList(self, xml_stream): """ @@ -671,13 +600,13 @@ class XMLSyncUtilsMixin(SyncCode): the data are : cmd, code and source """ status_list = [] - xml = xml_stream.xpath('//Status') - for status in xml: + status_node_list = xml_stream.xpath('//Status') + for status in status_node_list: tmp_dict = {} - tmp_dict['cmd'] = status.xpath('string(./Cmd)').encode('utf-8') - tmp_dict['code'] = status.xpath('string(./Data)').encode('utf-8') - tmp_dict['source'] = status.xpath('string(./SourceRef)').encode('utf-8') - tmp_dict['target'] = status.xpath('string(./TargetRef)').encode('utf-8') + tmp_dict['cmd'] = '%s' % status.xpath('string(./Cmd)') + tmp_dict['code'] = '%s' % status.xpath('string(./Data)') + tmp_dict['source'] = '%s' % status.xpath('string(./SourceRef)') + tmp_dict['target'] = '%s' % status.xpath('string(./TargetRef)') status_list.append(tmp_dict) return status_list @@ -685,77 +614,48 @@ class XMLSyncUtilsMixin(SyncCode): """ return the section data in text form, it's usefull for the VCardConduit """ - data = action.xpath('string(Item/Data)') - if isinstance(data, unicode): - data = data.encode('utf-8') - return data + return '%s' % action.xpath('string(.//Item/Data)') def getDataSubNode(self, action): """ Return the node starting with <object....> of the action """ - if action.xpath('.//Item/Data') not in ([], None): - data_node = action.xpath('.//Item/Data')[0] - if data_node.hasChildNodes(): - return data_node.childNodes[0] - return None - - def getPartialData(self, action): - """ - Return the node starting with <object....> of the action - """ - comment_list = action.xpath('.//Item/Data[comment()]') - if comment_list != []: - return comment_list[0].childNodes[0].data.encode('utf-8') + object_node_list = action.xpath('.//Item/Data/*[1]') + if object_node_list: + return object_node_list[0] return None def getActionId(self, action): """ Return the rid of the object described by the action """ - id = action.xpath('string(.//Item/Source/LocURI)') - if id in (None, ''): - id = action.xpath('string(.//Item/Target/LocURI)') - if isinstance(id, unicode): - id = id.encode('utf-8') + id = '%s' % action.xpath('string(.//Item/Source/LocURI)') + if not id: + id = '%s' % action.xpath('string(.//Item/Target/LocURI)') return id def checkActionMoreData(self, action): """ Return the rid of the object described by the action """ - return action.xpath('Item/MoreData') not in ([],None) + return bool(action.xpath('.//Item/MoreData')) def getActionType(self, action): """ Return the type of the object described by the action """ - action_type = action.xpath('string(Meta/Type)') - if isinstance(action_type, unicode): - action_type = action_type.encode('utf-8') - return action_type - - def getElementNodeList(self, node): - """ - Return childNodes that are ElementNode - """ - return node.xpath('*') + return '%s' % action.xpath('string(.//Meta/Type)') - def getTextNodeList(self, node): + def cutXML(self, xml_string): """ - Return childNodes that are ElementNode + Sliced a xml tree a return two fragment """ - subnode_list = [] - for subnode in node.childNodes or []: - if subnode.nodeType == subnode.TEXT_NODE: - subnode_list += [subnode] - return subnode_list - - def getAttributeNodeList(self, node): - """ - Return childNodes that are ElementNode - """ - return node.xpath('@*') + line_list = xml_string.split('\n') + short_string = '\n'.join(line_list[:self.MAX_LINES]) + rest_string = '\n'.join(line_list[self.MAX_LINES:]) + xml_string = etree.Element('Partial') + xml_string.text = etree.CDATA(short_string.decode('utf-8')) + return xml_string, rest_string def getSyncMLData(self, domain=None, remote_xml=None, cmd_id=0, subscriber=None, xml_confirmation=None, conduit=None, @@ -773,8 +673,8 @@ class XMLSyncUtilsMixin(SyncCode): local_gid_list = [] syncml_data = kw.get('syncml_data','') result = {'finished':1} - if isinstance(remote_xml, str) or isinstance(remote_xml, unicode): - remote_xml = Parse(remote_xml) + if isinstance(remote_xml, (str, unicode)): + remote_xml = etree.XML(remote_xml) if domain.isOneWayFromServer(): #Do not set object_path_list, subscriber send nothing subscriber.setRemainingObjectPathList([]) @@ -792,8 +692,8 @@ class XMLSyncUtilsMixin(SyncCode): gid = b16decode(domain.getGidFromObject(object)) if gid in gid_not_encoded_list: number = len([item for item in gid_not_encoded_list if item.startswith(gid)]) - if number > 0: - gid = gid+'__'+str(number+1) + if number: + gid = '%s__%s' % (gid, str(number+1)) gid_not_encoded_list.append(gid) local_gid_list.append(b16encode(gid)) #LOG('getSyncMLData :', DEBUG,'gid_not_encoded_list:%s, local_gid_list:%s, gid:%s' % (gid_not_encoded_list, local_gid_list, gid)) @@ -834,27 +734,27 @@ class XMLSyncUtilsMixin(SyncCode): object = self.unrestrictedTraverse(object_path) status = self.SENT object_gid = domain.getGidFromObject(object) - if object_gid in ('', None): + if not object_gid: continue local_gid_list += [object_gid] force = 0 if syncml_data.count('\n') < self.MAX_LINES and not \ object.id.startswith('.'): # If not we have to cut - #LOG('getSyncMLData', DEBUG, 'object_path: %s' % '/'.join(object_path)) - #LOG('getSyncMLData', DEBUG, 'xml_mapping: %s' % str(domain.getXMLMapping())) - #LOG('getSyncMLData', DEBUG, 'code: %s' % str(self.getAlertCodeFromXML(remote_xml))) - #LOG('getSyncMLData', DEBUG, 'gid_list: %s' % str(local_gid_list)) - #LOG('getSyncMLData', DEBUG, 'subscriber.getGidList: %s' % subscriber.getGidList()) - #LOG('getSyncMLData', DEBUG, 'hasSignature: %s' % str(subscriber.hasSignature(object_gid))) - #LOG('getSyncMLData', DEBUG, 'alert_code == slowsync: %s' % str(self.getAlertCodeFromXML(remote_xml) == self.SLOW_SYNC)) + #LOG('getSyncMLData', 0, 'object_path: %s' % '/'.join(object_path)) + #LOG('getSyncMLData', 0, 'xml_mapping: %s' % str(domain.getXMLMapping())) + #LOG('getSyncMLData', 0, 'code: %s' % str(self.getAlertCodeFromXML(remote_xml))) + #LOG('getSyncMLData', 0, 'gid_list: %s' % str(local_gid_list)) + #LOG('getSyncMLData', 0, 'subscriber.getGidList: %s' % subscriber.getGidList()) + #LOG('getSyncMLData', 0, 'hasSignature: %s' % str(subscriber.hasSignature(object_gid))) + #LOG('getSyncMLData', 0, 'alert_code == slowsync: %s' % str(self.getAlertCodeFromXML(remote_xml) == self.SLOW_SYNC)) signature = subscriber.getSignatureFromGid(object_gid) ## Here we first check if the object was modified or not by looking at dates #if signature is not None: #LOG('getSyncMLData', DEBUG, 'signature.getStatus: %s' % signature.getStatus()) status = self.SENT - more_data=0 + more_data = 0 # For the case it was never synchronized, we have to send everything if signature is not None and signature.getXMLMapping() is None: pass @@ -862,32 +762,18 @@ class XMLSyncUtilsMixin(SyncCode): signature.getStatus() != self.PARTIAL) or \ self.getAlertCodeFromXML(remote_xml) == self.SLOW_SYNC: #LOG('getSyncMLData', DEBUG, 'Current object.getPath: %s' % object.getPath()) - xml_object = domain.getXMLFromObject(object) - xml_string = xml_object - if isinstance(xml_string, unicode): - xml_string = xml_object.encode('utf-8') + xml_string = domain.getXMLFromObject(object) gid = subscriber.getGidFromObject(object) signature = Signature(id=gid, object=object).__of__(subscriber) - signature.setTempXML(xml_object) + signature.setTempXML(xml_string) if xml_string.count('\n') > self.MAX_LINES: - if xml_string.find('--') >= 0: # This make comment fails, so we need to replace - xml_string = xml_string.replace('--','@-@@-@') - more_data=1 - i = 0 - short_string = '' - rest_string = xml_string - while i < self.MAX_LINES: - short_string += rest_string[:rest_string.find('\n')+1] - rest_string = xml_string[len(short_string):] - i += 1 - #LOG('getSyncMLData', DEBUG, 'setPartialXML with: %s' % str(rest_string)) + xml_string, rest_string = self.cutXML(xml_string) + more_data = 1 signature.setPartialXML(rest_string) status = self.PARTIAL signature.setAction('Add') - xml_string = '<!--' + short_string + '-->' - gid = signature.getRid()#in fisrt, we try with rid if there is one - if gid is None: - gid = signature.getGid() + #in fisrt, we try with rid if there is one + gid = signature.getRid() or signature.getGid() syncml_data += self.addXMLObject( cmd_id=cmd_id, object=object, @@ -918,26 +804,16 @@ class XMLSyncUtilsMixin(SyncCode): xml_string = self.getXupdateObject(xml_object, signature.getXML()) else: #if there is no xml, we re-send all the object xml_string = xml_object - if xml_string.count('\n') > self.MAX_LINES: + if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: + xml_string = xml_object + elif xml_string.count('\n') > self.MAX_LINES: # This make comment fails, so we need to replace - if xml_string.find('--') >= 0: - xml_string = xml_string.replace('--', '@-@@-@') - i = 0 + xml_string, rest_string = self.cutXML(xml_string) more_data = 1 - short_string_list = [] - short_string_list_ap = short_string_list.append - rest_string = xml_string - while i < self.MAX_LINES: - short_string_list_ap(rest_string[:rest_string.find('\n')+1]) - rest_string = xml_string[len(''.join(short_string_list)):] - i += 1 signature.setPartialXML(rest_string) status = self.PARTIAL signature.setAction('Replace') - xml_string = '<!--' + ''.join(short_string_list) + '-->' - signature.setStatus(status) - if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: - xml_string = xml_object + signature.setStatus(status) rid = signature.getRid()#in fisrt, we try with rid if there is one gid = signature.getGid() syncml_data += self.replaceXMLObject( @@ -983,25 +859,16 @@ class XMLSyncUtilsMixin(SyncCode): signature.setStatus(self.SYNCHRONIZED) elif signature.getStatus() == self.PARTIAL: xml_string = signature.getPartialXML() - if xml_string.count('\n') > self.MAX_LINES: - i = 0 - more_data=1 - short_string_list = [] - short_string_list_ap = short_string_list.append - rest_string = xml_string - while i < self.MAX_LINES: - short_string_list_ap(rest_string[:rest_string.find('\n')+1]) - rest_string = xml_string[len(''.join(short_string_list)):] - i += 1 + xml_to_send = Element('Partial') + xml_to_send.text = etree.CDATA(xml_string.decode('utf-8')) + if(subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']): + xml_to_send = domain.getXMLFromObject(object) + elif xml_string.count('\n') > self.MAX_LINES: + xml_to_send, rest_string = self.cutXML(xml_string) + more_data = 1 signature.setPartialXML(rest_string) - xml_string = ''.join(short_string_list) status = self.PARTIAL - if xml_string.find('--') >= 0: # This make comment fails, so we need to replace - xml_string = xml_string.replace('--','@-@@-@') - xml_string = '<!--' + xml_string + '-->' signature.setStatus(status) - if(subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']): - xml_string = domain.getXMLFromObject(object) if signature.getAction() == 'Replace': rid = signature.getRid()#in fisrt, we try with rid if there is one gid = signature.getGid() @@ -1010,18 +877,17 @@ class XMLSyncUtilsMixin(SyncCode): object=object, gid=gid, rid=rid, - xml_string=xml_string, + xml_string=xml_to_send, more_data=more_data, media_type=subscriber.getMediaType()) elif signature.getAction() == 'Add': - gid = signature.getRid()#in fisrt, we try with rid if there is one - if gid == None: - gid = signature.getGid() + #in fisrt, we try with rid if there is one + gid = signature.getRid() or signature.getGid() syncml_data += self.addXMLObject( cmd_id=cmd_id, object=object, gid=gid, - xml_string=xml_string, + xml_string=xml_to_send, more_data=more_data, media_type=subscriber.getMediaType()) if not more_data: @@ -1048,25 +914,22 @@ class XMLSyncUtilsMixin(SyncCode): #LOG('applyActionList args', DEBUG, 'domain : %s\n subscriber : %s\n cmd_id : %s' % (domain.getPath(), subscriber.getPath(), cmd_id)) #LOG('applyActionList', DEBUG, self.getSyncActionList(remote_xml)) for action in self.getSyncActionList(remote_xml): - if isinstance(action, unicode): - action = action.encode('utf-8') conflict_list = [] status_code = self.SUCCESS # Thirst we have to check the kind of action it is - partial_data = self.getPartialData(action) + partial_data = '%s' % action.xpath('string(.//Item/Data/Partial)') rid = self.getActionId(action) - if action.nodeName != 'Delete': - if hasattr(conduit, 'getGidFromXML') and \ - conduit.getGidFromXML(self.getDataText(action), - gid_from_xml_list) not in ('', None): + if action.tag != 'Delete': + if getattr(conduit, 'getGidFromXML', None) is not None and \ + conduit.getGidFromXML(self.getDataText(action), gid_from_xml_list): gid = conduit.getGidFromXML(self.getDataText(action), - gid_from_xml_list) + gid_from_xml_list) gid_from_xml_list.append(gid) gid = b16encode(gid) else: - gid=rid + gid = rid else: - gid=rid + gid = rid object_id = domain.generateNewIdWithGenerator(object=destination, gid=gid) signature = subscriber.getSignatureFromGid(gid) if signature is not None and rid != gid: @@ -1083,47 +946,44 @@ class XMLSyncUtilsMixin(SyncCode): #LOG('applyActionList, try to find an object with rid', DEBUG, '') object = subscriber.getObjectFromRid(rid) signature = subscriber.getSignatureFromRid(rid) - if signature not in ('', None): + if signature is not None: gid = signature.getId() #LOG('applyActionList subscriber.getObjectFromGid %s' % gid, DEBUG, object) if signature is None: #LOG('applyActionList, signature is None', DEBUG, signature) if gid == rid: signature = Signature(id=gid, status=self.NOT_SYNCHRONIZED, - object=object).__of__(subscriber) + object=object).__of__(subscriber) else: signature = Signature(rid=rid, id=gid, status=self.NOT_SYNCHRONIZED, - object=object).__of__(subscriber) + object=object).__of__(subscriber) signature.setObjectId(object_id) subscriber.addSignature(signature) force = signature.getForce() - if self.checkActionMoreData(action) == 0: + if not self.checkActionMoreData(action): data_subnode = None - if partial_data != None: + if partial_data: signature_partial_xml = signature.getPartialXML() - if signature_partial_xml is not None: - data_subnode = signature.getPartialXML() + partial_data + if signature_partial_xml: + data_subnode = signature_partial_xml + partial_data else: data_subnode = partial_data #LOG('applyActionList', DEBUG, 'data_subnode: %s' % data_subnode) if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_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 + data_subnode = etree.XML(data_subnode) else: if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: data_subnode = self.getDataText(action) else: data_subnode = self.getDataSubNode(action) - if action.nodeName == 'Add': + if action.tag == 'Add': # Then store the xml of this new subobject reset = 0 if object is None: add_data = conduit.addNode(xml=data_subnode, object=destination, object_id=object_id) - if add_data['conflict_list'] not in ('', None, []): - conflict_list += add_data['conflict_list'] + conflict_list.extend(add_data['conflict_list']) # Retrieve directly the object from addNode object = add_data['object'] if object is not None: @@ -1134,11 +994,9 @@ class XMLSyncUtilsMixin(SyncCode): #Object was retrieve but need to be updated without recreated #usefull when an object is only deleted by workflow. if data_subnode is not None: - if type(data_subnode) != type(''): - string_io = StringIO() - PrettyPrint(data_subnode, stream=string_io) - xml_string = string_io.getvalue() - actual_xml = subscriber.getXMLFromObject(object = object, force=1) + if not isinstance(data_subnode, str): + xml_string = etree.tostring(data_subnode, encoding='utf-8') + actual_xml = subscriber.getXMLFromObject(object=object, force=1) data_subnode = self.getXupdateObject(xml_string, actual_xml) conflict_list += conduit.updateNode( xml=data_subnode, @@ -1153,9 +1011,10 @@ class XMLSyncUtilsMixin(SyncCode): if reset: #After a reset we want copy the LAST XML view on Signature. #this implementation is not sufficient, need to be improved. - string_io = StringIO() - PrettyPrint(data_subnode, stream=string_io) - xml_object = string_io.getvalue() + if not isinstance(data_subnode, str): + xml_object = etree.tostring(data_subnode, encoding='utf-8') + else: + xml_object = data_subnode else: xml_object = domain.getXMLFromObject(object) signature.setStatus(self.SYNCHRONIZED) @@ -1168,7 +1027,7 @@ class XMLSyncUtilsMixin(SyncCode): sync_code=self.ITEM_ADDED, remote_xml=action) cmd_id +=1 - elif action.nodeName == 'Replace': + elif action.tag == 'Replace': #LOG('applyActionList', DEBUG, 'object: %s will be updated...' % str(object)) if object is not None: #LOG('applyActionList', DEBUG, 'object: %s will be updated...' % object.id) @@ -1185,14 +1044,11 @@ class XMLSyncUtilsMixin(SyncCode): simulate=simulate) xml_object = domain.getXMLFromObject(object) signature.setTempXML(xml_object) - if conflict_list != []: + 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.setConflictList(signature.getConflictList()+conflict_list) + data_subnode_string = etree.tostring(data_subnode, encoding='utf-8') signature.setPartialXML(data_subnode_string) elif not simulate: signature.setStatus(self.SYNCHRONIZED) @@ -1206,13 +1062,11 @@ class XMLSyncUtilsMixin(SyncCode): # This means we are on the publisher side and we want to store # the xupdate from the subscriber and we also want to generate # the current xupdate from the last synchronization - string_io = StringIO() - PrettyPrint(data_subnode, stream=string_io) - data_subnode_string = string_io.getvalue() + data_subnode_string = etree.tostring(data_subnode, encoding='utf-8') #LOG('applyActionList, subscriber_xupdate:', TRACE, data_subnode_string) signature.setSubscriberXupdate(data_subnode_string) - elif action.nodeName == 'Delete': + elif action.tag == 'Delete': object_id = signature.getId() #LOG('applyActionList Delete on : ', DEBUG, (signature.getId(), subscriber.getObjectFromGid(object_id))) if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']: @@ -1220,7 +1074,7 @@ class XMLSyncUtilsMixin(SyncCode): else: data_subnode = self.getDataSubNode(action) #LOG('applyActionList, object gid to delete :', 0, subscriber.getObjectFromGid(object_id)) - if subscriber.getObjectFromGid(object_id) not in (None, ''): + if subscriber.getObjectFromGid(object_id) is not None: #if the object exist: conduit.deleteNode( xml=data_subnode, @@ -1242,10 +1096,10 @@ class XMLSyncUtilsMixin(SyncCode): #LOG('applyActionList', DEBUG, 'waiting more data for :%s' % signature.getId()) xml_confirmation += self.SyncMLConfirmation( cmd_id=cmd_id, - cmd=action.nodeName, + cmd=action.tag, sync_code=self.WAITING_DATA, remote_xml=action) - if conflict_list != [] and signature is not None: + if conflict_list and signature is not None: # We had a conflict signature.setStatus(self.CONFLICT) @@ -1259,42 +1113,43 @@ class XMLSyncUtilsMixin(SyncCode): status_list = self.getSyncBodyStatusList(remote_xml) has_status_list = 0 destination_waiting_more_data = 0 - if status_list != []: - for status in status_list: - status_cmd = status['cmd'] - object_gid = status['source'] - if object_gid in ('', None, []): - object_gid = status['target'] - status_code = int(status['code']) - if status_cmd in ('Add','Replace',): - has_status_list = 1 + for status in status_list: + if not status['code']: + continue + status_cmd = status['cmd'] + object_gid = status['source'] + if not object_gid: + object_gid = status['target'] + status_code = int(status['code']) + if status_cmd in ('Add', 'Replace',): + has_status_list = 1 + signature = subscriber.getSignatureFromGid(object_gid) + if signature is None: + signature = subscriber.getSignatureFromRid(object_gid) + if status_code == self.CHUNK_OK: + destination_waiting_more_data = 1 + signature.setStatus(self.PARTIAL) + 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 in (self.SUCCESS, self.ITEM_ADDED): + signature.setStatus(self.SYNCHRONIZED) + elif status_cmd == 'Delete': + has_status_list = 1 + if status_code == self.SUCCESS: signature = subscriber.getSignatureFromGid(object_gid) - if signature is None: + if signature is None and \ + not(subscriber.getSynchronizeWithERP5Sites()): signature = subscriber.getSignatureFromRid(object_gid) - 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 in (self.SUCCESS, self.ITEM_ADDED): - signature.setStatus(self.SYNCHRONIZED) - elif status_cmd == 'Delete': - has_status_list = 1 - if status_code == self.SUCCESS: - signature = subscriber.getSignatureFromGid(object_gid) - if signature is None and \ - not(subscriber.getSynchronizeWithERP5Sites()): - signature = subscriber.getSignatureFromRid(object_gid) - if signature is not None: - subscriber.delSignature(signature.getGid()) + if signature is not None: + subscriber.delSignature(signature.getGid()) return (destination_waiting_more_data, has_status_list) @@ -1342,10 +1197,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin): has_response = 0 #check if syncmodif replies to this messages cmd_id = 1 # specifies a SyncML message-unique command identifier #LOG('SyncModif', DEBUG, 'Starting... domain: %s' % domain.getId()) - first_node = remote_xml.childNodes[0] # Get informations from the header - xml_header = first_node.childNodes[1] - if xml_header.nodeName != "SyncHdr": + xml_header = remote_xml[0] + if xml_header.tag != "SyncHdr": LOG('SyncModif', INFO, 'This is not a SyncML Header') raise ValueError, "Sorry, This is not a SyncML Header" @@ -1358,24 +1212,24 @@ class XMLSyncUtils(XMLSyncUtilsMixin): # We have to check if this message was not already, this can be dangerous # to update two times the same object - message_id = self.getMessageId(remote_xml) + message_id = self.getMessageIdFromXml(remote_xml) correct_message = subscriber.checkCorrectRemoteMessageId(message_id) if not correct_message: # We need to send again the message LOG('SyncModif, no correct message:', INFO, "sending again...") last_xml = subscriber.getLastSentMessage() LOG("SyncModif last_xml :", INFO, last_xml) - string_io = StringIO() - PrettyPrint(remote_xml, stream=string_io) - remote_xml = string_io.getvalue() + remote_xml = etree.tostring(remote_xml, encoding='utf-8', + xml_declaration=True, + pretty_print=True) LOG("SyncModif remote_xml :", INFO, remote_xml) - if last_xml != '': + if last_xml: has_response = 1 if domain.domain_type == self.PUB: # We always reply self.sendResponse( from_url=domain.publication_url, to_url=subscriber.subscription_url, sync_id=domain.getTitle(), - xml=last_xml,domain=domain, + xml=last_xml, domain=domain, content_type=domain.getSyncContentType()) elif domain.domain_type == self.SUB: self.sendResponse( @@ -1435,7 +1289,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin): destination_url = '' # alert message if we want more data - if destination_waiting_more_data == 1: + if destination_waiting_more_data: xml(self.SyncMLAlert( cmd_id, self.WAITING_DATA, @@ -1448,10 +1302,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin): cmd_id = cmd_id+1 if domain.getActivityEnabled(): #use activities to get SyncML data. - if not (isinstance(remote_xml, str) or isinstance(remote_xml, unicode)): - string_io = StringIO() - PrettyPrint(remote_xml,stream=string_io) - remote_xml = string_io.getvalue() + if not isinstance(remote_xml, (str, unicode)): + remote_xml = etree.tostring(remote_xml, encoding='utf-8', + xml_declaration=True, pretty_print=False) domain.activate(activity='SQLQueue', tag=domain.getId(), priority=self.PRIORITY).activateSyncModif( @@ -1518,12 +1371,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin): domain = self.unrestrictedTraverse(kw['domain_relative_url']) subscriber = self.unrestrictedTraverse(kw['subscriber_relative_url']) conduit = subscriber.getConduit() - result = self.getSyncMLData( - domain = domain, - subscriber = subscriber, - conduit = conduit, - max = self.MAX_OBJECTS, - **kw) + result = self.getSyncMLData(domain=domain, subscriber=subscriber, + conduit=conduit, max=self.MAX_OBJECTS, **kw) syncml_data = result['syncml_data'] cmd_id = result['cmd_id'] kw['syncml_data'] = syncml_data @@ -1537,7 +1386,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin): xml_confirmation = result['xml_confirmation'] cmd_id = result['cmd_id'] cmd_id_before_getsyncmldata = kw['cmd_id_before_getsyncmldata'] - remote_xml = Parse(kw['remote_xml']) + remote_xml = etree.XML(kw['remote_xml']) xml_list = kw['xml_list'] has_status_list = kw['has_status_list'] has_response = kw['has_response'] @@ -1555,6 +1404,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin): def sendSyncModif(self, syncml_data, cmd_id_before_getsyncmldata, subscriber, domain, xml_confirmation, remote_xml, xml_list, has_status_list, has_response): + xml = xml_list.append if syncml_data != '': xml(' <Sync>\n') @@ -1584,7 +1434,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin): xml=xml_a, domain=domain, content_type=domain.getSyncContentType()) - if syncml_data == '': + if not syncml_data: LOG('this is the end of the synchronisation session !!!', INFO, domain.getId()) subscriber.setAuthenticated(False) domain.setAuthenticated(False) @@ -1652,17 +1502,15 @@ class XMLSyncUtils(XMLSyncUtilsMixin): result = None if xml_client is not None: - if isinstance(xml_client, str) or isinstance(xml_client, unicode): - xml_client = Parse(xml_client) - first_node = xml_client.childNodes[0] - - if first_node.nodeName != "SyncML": + if isinstance(xml_client, (str, unicode)): + xml_client = etree.XML(xml_client) + if xml_client.tag != "SyncML": LOG('PubSync', INFO, 'This is not a SyncML Message') raise ValueError, "Sorry, This is not a SyncML Message" alert_code = self.getAlertCodeFromXML(xml_client) # Get informations from the header - client_header = first_node.childNodes[1] - if client_header.nodeName != "SyncHdr": + client_header = xml_client[0] + if client_header.tag != "SyncHdr": LOG('PubSync', INFO, 'This is not a SyncML Header') raise ValueError, "Sorry, This is not a SyncML Header" subscription_url = self.getSubscriptionUrlFromXML(client_header) @@ -1717,17 +1565,17 @@ class XMLSyncUtils(XMLSyncUtilsMixin): """ response = None #check if subsync replies to this messages subscription = self.unrestrictedTraverse(subscription_path) - if msg is None and (subscription.getSubscriptionUrl()).find('file')>=0: + if msg is None and (subscription.getSubscriptionUrl()).find('file') >= 0: msg = self.readResponse(sync_id=subscription.getSubscriptionUrl(), - from_url=subscription.getSubscriptionUrl()) + from_url=subscription.getSubscriptionUrl()) if msg is None: response = self.SubSyncInit(subscription) else: xml_client = msg - if isinstance(xml_client, str) or isinstance(xml_client, unicode): - xml_client = Parse(xml_client) + if isinstance(xml_client, (str, unicode)): + xml_client = etree.XML(xml_client) status_list = self.getSyncBodyStatusList(xml_client) - if status_list not in (None, []): + if status_list: status_code_syncHdr = status_list[0]['code'] if status_code_syncHdr.isdigit(): status_code_syncHdr = int(status_code_syncHdr) diff --git a/product/ERP5SyncML/tests/testERP5SyncML.py b/product/ERP5SyncML/tests/testERP5SyncML.py index d530a4a74a251f4aab34d4d9c3401e0f9392955f..378e490779811929c7e6e4c36555802d5e53bf31 100644 --- a/product/ERP5SyncML/tests/testERP5SyncML.py +++ b/product/ERP5SyncML/tests/testERP5SyncML.py @@ -1,5 +1,6 @@ +# -*- coding: utf-8 -*- ############################################################################## -# +# # Copyright (c) 2004 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> # @@ -486,23 +487,23 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase): ZopeTestCase._print('\nTest First Synchronization ') LOG('Testing... ',0,'test_08_FirstSynchronization') self.login() - self.setupPublicationAndSubscription(quiet=1,run=1) - nb_person = self.populatePersonServer(quiet=1,run=1) + self.setupPublicationAndSubscription(quiet=1, run=1) + nb_person = self.populatePersonServer(quiet=1, run=1) portal_sync = self.getSynchronizationTool() for sub in portal_sync.getSubscriptionList(): - self.assertEquals(sub.getSynchronizationType(),SyncCode.SLOW_SYNC) + self.assertEquals(sub.getSynchronizationType(), SyncCode.SLOW_SYNC) # Synchronize the first client nb_message1 = self.synchronize(self.sub_id1) for sub in portal_sync.getSubscriptionList(): if sub.getTitle() == self.sub_id1: - self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY) + self.assertEquals(sub.getSynchronizationType(), SyncCode.TWO_WAY) else: - self.assertEquals(sub.getSynchronizationType(),SyncCode.SLOW_SYNC) + self.assertEquals(sub.getSynchronizationType(), SyncCode.SLOW_SYNC) self.assertEqual(nb_message1, self.nb_message_first_synchronization) # Synchronize the second client nb_message2 = self.synchronize(self.sub_id2) for sub in portal_sync.getSubscriptionList(): - self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY) + self.assertEquals(sub.getSynchronizationType(), SyncCode.TWO_WAY) self.assertEqual(nb_message2, self.nb_message_first_synchronization) self.checkFirstSynchronization(id=self.id1, nb_person=nb_person)