From be3df2e3db0d3b92fc05dd5a4c037e77ce62375c Mon Sep 17 00:00:00 2001 From: Sebastien Robin <seb@nexedi.com> Date: Fri, 15 Oct 2004 14:50:43 +0000 Subject: [PATCH] added one way synchronization git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@1860 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5SyncML/Conduit/ERP5Conduit.py | 3 +- .../Conduit/ERP5ShopOrderConduit.py | 226 +++++++++------ product/ERP5SyncML/Publication.py | 14 +- product/ERP5SyncML/Subscription.py | 56 +++- product/ERP5SyncML/SynchronizationTool.py | 47 ++-- product/ERP5SyncML/XMLSyncUtils.py | 55 ++-- .../ERP5SyncML/dtml/managePublications.dtml | 10 + .../ERP5SyncML/dtml/manageSubscriptions.dtml | 10 + .../dtml/manage_addPublication.dtml | 10 + .../dtml/manage_addSubscription.dtml | 10 + product/ERP5SyncML/tests/testERP5SyncML.py | 257 +++++++++++------- 11 files changed, 452 insertions(+), 246 deletions(-) diff --git a/product/ERP5SyncML/Conduit/ERP5Conduit.py b/product/ERP5SyncML/Conduit/ERP5Conduit.py index 7f45cdb697..ee3ac60214 100755 --- a/product/ERP5SyncML/Conduit/ERP5Conduit.py +++ b/product/ERP5SyncML/Conduit/ERP5Conduit.py @@ -97,7 +97,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): """ return the string corresponding to the local encoding """ - return "iso-8859-1" + #return "iso-8859-1" + return "utf-8" security.declareProtected(Permissions.ModifyPortalContent, '__init__') def __init__(self): diff --git a/product/ERP5SyncML/Conduit/ERP5ShopOrderConduit.py b/product/ERP5SyncML/Conduit/ERP5ShopOrderConduit.py index ee16c3fb3c..4bfd618ce2 100755 --- a/product/ERP5SyncML/Conduit/ERP5ShopOrderConduit.py +++ b/product/ERP5SyncML/Conduit/ERP5ShopOrderConduit.py @@ -47,6 +47,8 @@ class ERP5ShopOrderConduit(ERP5Conduit): """ This conduit is used in the synchronisation process of Storever and ERP5 to convert a Storever Shop Order to a ERP5 Sale Order. + Don't forget to add this base categories in portal_category : + 'hd_size', 'memory_size', 'optical_drive', 'keyboad_layout', 'cpu_type' """ @@ -72,17 +74,17 @@ class ERP5ShopOrderConduit(ERP5Conduit): if portal_type == 'Shop Order': # The random part of the id can be removed. It's only used for the developpement new_object_id = 'storever-' + object_id + '-' + str(random.randint(1000, 9999)) - object.newContent ( portal_type = 'Sale Order' - , id = new_object_id) + subobject = object.newContent( portal_type = 'Sale Order' + , id = new_object_id) if portal_type == 'Order Line': last_line_num = self.getLastOrderLineNumber(object) new_object_id = "storever-" + str(last_line_num + 1) + "-" + object_id - object.newContent ( portal_type = 'Sale Order Line' - , id = new_object_id) - subobject = object._getOb(new_object_id) + subobject = object.newContent( portal_type = 'Sale Order Line' + , id = new_object_id) return subobject + # # Not needed yet # security.declareProtected(Permissions.ModifyPortalContent, 'addWorkflowNode') # def addWorkflowNode(self, object, xml, simulate): @@ -178,9 +180,8 @@ class ERP5ShopOrderConduit(ERP5Conduit): if product_id == erp5_product_id: return erp5_site.restrictedTraverse(erp5_site_path + '/product/' + erp5_product_id) # We have to create a new product - product_folder.newContent ( portal_type = 'Product' - , id = erp5_product_id) - return product_folder._getOb(erp5_product_id) + return product_folder.newContent( portal_type = 'Product' + , id = erp5_product_id) @@ -190,18 +191,25 @@ class ERP5ShopOrderConduit(ERP5Conduit): This function set the validation workflow to indicate if a product is discontinued (workflow state = invalidate) or not (workflow state = validate) """ + #!!!!!!!!!!!!!!!!!!!!!!!!!! +# return + #!!!!!!!!!!!!!!!!!!!!!!!!!! action = None if hasattr(product_object, 'workflow_history'): + LOG('Info needed from portal_workflow >>>>>>>>> ', 0, '') workflow_state = product_object.portal_workflow.getInfoFor(product_object, 'validation_state') + LOG('workflow_state is >>>>>>>>> ', 0, repr(workflow_state)) if product_title.lower().find('discontinued') != -1: if workflow_state != 'invalidated': action = 'invalidate_action' elif workflow_state in ('draft', 'invalidated'): action = 'validate_action' + LOG('action is >>>>>>>>> ', 0, repr(action)) if action != None: product_object.portal_workflow.doActionFor( product_object , action , wf_id = 'validation_workflow') + LOG('end of workflow action >>>>>>>>> ', 0, repr(action)) @@ -248,6 +256,43 @@ class ERP5ShopOrderConduit(ERP5Conduit): + security.declarePrivate('updateObjProperty') + def updateObjProperty(self, object, property, kw, key): + """ + This function update the property of an object with a given value stored in a dictionnary. This function help the Conduit to make decision about the synchronisation of values. + + Example of call : self.updateObjProperty(person_object, 'DefaultAddressStreetAddress', kw, 'address') + + Solution (d'apres seb) : + * machin = getattr (object, methos) + * machin() + """ + if kw.has_key(key): + new_value = kw[key] + if new_value != None: + if type(new_value) is type('s'): + new_value = new_value.title() + + current_value = eval('object.get' + property + '()') + LOG("I have to run this >>>>>>>> ", 0, 'object.get' + property + '()') + LOG("current_value >>>>>>>> ", 0, repr(current_value)) + + # The current property value is not consistent + if current_value == None or len(current_value) == 0: + # Erase the current value with the new one + + LOG("I have to run this to set the property >>>>>>>> " + 'object.set' + str(property) + '(' + str(new_value) + ')' + str(current_value), 0, '') + + # A previous consistent value exist + elif current_value.strip().lower() != new_value.strip().lower(): + # TODO : We need to choose if we replace it or not, or mix the current with the new one + LOG('We have to make the fusion of previous address with the current one >>>>>>>', 0, '') + return False + return True + return False + + + security.declareProtected(Permissions.ModifyPortalContent, 'editDocument') def editDocument(self, object=None, **kw): """ @@ -258,6 +303,10 @@ class ERP5ShopOrderConduit(ERP5Conduit): LOG('KW >>>>>>>> ', 0, kw) + # This list contain a list of object to check to know if their workflow need to be mofified + # We store these objects into a list and we will apply modification at the end to avoid mysql lock problem + workflow_joblist = [] + # Get the ERP5 root object portal_types = getToolByName(object, 'portal_types') erp5_site = portal_types.getPortalObject() @@ -265,12 +314,11 @@ class ERP5ShopOrderConduit(ERP5Conduit): # The object is a ShopOrder if kw.has_key('country'): - # Find the organisation and the person folder person_path = erp5_site_path + '/person' person_folder = erp5_site.restrictedTraverse(person_path) organisation_path = erp5_site_path + '/organisation' - organisation_folder = erp5_site.restrictedTraverse(organisation_path) + org_folder = erp5_site.restrictedTraverse(organisation_path) # Find the service folder service_path = erp5_site_path + '/service' service_folder = erp5_site.restrictedTraverse(service_path) @@ -284,23 +332,23 @@ class ERP5ShopOrderConduit(ERP5Conduit): # Try to find the identity created for a previous ShopOrder of the same Storever member account person_object = None - organisation_object = None + org_object = None for person_id in person_folder.objectIds(): if person_id == owner_id: person_object = erp5_site.restrictedTraverse(erp5_site_path + '/person/' + person_id) LOG("Previous person found ! >>>>>>>>",0,repr(person_object)) break - for organisation_id in organisation_folder.objectIds(): + for organisation_id in org_folder.objectIds(): if organisation_id == owner_id: - organisation_object = erp5_site.restrictedTraverse(erp5_site_path + '/organisation/' + organisation_id) - LOG("Previous organisation found ! >>>>>>>>",0,repr(organisation_object)) + org_object = erp5_site.restrictedTraverse(erp5_site_path + '/organisation/' + organisation_id) + LOG("Previous organisation found ! >>>>>>>>",0,repr(org_object)) break # Define the previous customer structure previous_owner_type = '' if person_object != None: previous_owner_type += 'p' - if organisation_object != None: + if org_object != None: previous_owner_type += 'o' if len(previous_owner_type) == 0: previous_owner_type = None @@ -324,27 +372,25 @@ class ERP5ShopOrderConduit(ERP5Conduit): if previous_owner_type != owner_type: # There is difference between the two (previous and current) representation of the customer # We have to manage the differences to create a unique customer representation - LOG("There is difference between previous and current >>>>>>>>",0,None) + LOG("There is difference between previous and current >>>>>>>>",0,'') if previous_owner_type == None: # No previous customer found, create one if owner_type.find('o') != -1: - organisation_folder.newContent ( portal_type = 'Organisation' - , id = owner_id) - organisation_object = organisation_folder._getOb(owner_id) - LOG("new organisation created >>>>>>>>",0,repr(organisation_object)) + org_object = org_folder.newContent( portal_type = 'Organisation' + , id = owner_id) + LOG("new organisation created >>>>>>>>",0,repr(org_object)) if owner_type.find('p') != -1: - person_folder.newContent ( portal_type = 'Person' - , id = owner_id) - person_object = person_folder._getOb(owner_id) + person_object = person_folder.newContent( portal_type = 'Person' + , id = owner_id) LOG("new person created >>>>>>>>",0,repr(person_object)) else: if owner_type == None: # Use the previous Structure owner_type = previous_owner_type - LOG("Use the previous Structure >>>>>>>>",0,None) + LOG("Use the previous Structure >>>>>>>>",0,'') else: - LOG("We have to convert the structure >>>>>>>>",0,None) -# # XXX Be aware of that problem: the invoice for a sale order must be the same + LOG("We have to convert the structure >>>>>>>>",0,'') +# # XXX Be aware of that problem: the invoice for a sale order must look the same (I mean when we generate the pdf version) # Case to process : # previous current # o --> p @@ -364,9 +410,8 @@ class ERP5ShopOrderConduit(ERP5Conduit): if owner_type.find('p') != -1 and owner_type.find('o') != -1: # Create a new organisation # # TODO : factorise this code with the same above - organisation_folder.newContent ( portal_type = 'Organisation' - , id = owner_id) - organisation_object = organisation_folder._getOb(owner_id) + org_object = org_folder.newContent( portal_type = 'Organisation' + , id = owner_id) else: # # TODO : Transform a person to an organisation ? Is it a good idea ? pass @@ -390,19 +435,18 @@ class ERP5ShopOrderConduit(ERP5Conduit): # a person and there is no previous record # By default, we consider the customer as a person, so we have to force to create one owner_type = 'p' - person_folder.newContent ( portal_type = 'Person' - , id = owner_id) - person_object = person_folder._getOb(owner_id) + person_object = person_folder.newContent( portal_type = 'Person' + , id = owner_id) LOG("Create a person by default >>>>>>>>",0,repr(person_object)) else: # The structure is the same # We only need to be aware of data fusion between the previous and the current representation # So we don't need to do something because the information fusion process take place below - LOG("The structure is the same. don't do anything >>>>>>>>",0,None) + LOG("The structure is the same. don't do anything >>>>>>>>",0,'') pass LOG("Person object >>>>>>>>",0,repr(person_object)) - LOG("Organisation object >>>>>>>>",0,repr(organisation_object)) + LOG("Organisation object >>>>>>>>",0,repr(org_object)) # Copy informations related to the customer in the ERP5 representation of the customer # Be carefull because all informations from the storever ShopOrder are optionnals @@ -415,12 +459,20 @@ class ERP5ShopOrderConduit(ERP5Conduit): # # TODO : before doing something working well in every case, copy the previou value in the comment field to traceback the modification and let me evaluate the solidity of my algorithm # # TODO : perhaps it's possible to factorize the code using a generic function # Synchronise the street address - if kw.has_key('address'): - previous_address = person_object.getDefaultAddressStreetAddress() - if len(previous_address) == 0: - person_object.setDefaultAddressStreetAddress(kw['address'].title()) - elif previous_address.strip().lower() != kw['address'].strip().lower(): - LOG('We have to make the fusion of previous address with the current one >>>>>>>', 0, None) + + # Solution (d'apres seb) + # machin = getattr (object, methos) + # method(machin) + + machin = self.updateObjProperty(person_object, 'DefaultAddressStreetAddress', kw, 'address') + LOG("My new updateObjProperty() return >>>>>>>>",0,repr(machin)) + +# if kw.has_key('address') and kw['address'] != None: +# previous_address = person_object.getDefaultAddressStreetAddress() +# if len(previous_address) == 0: +# person_object.setDefaultAddressStreetAddress(kw['address'].title()) +# elif previous_address.strip().lower() != kw['address'].strip().lower(): +# LOG('We have to make the fusion of previous address with the current one >>>>>>>', 0, '') person_object.setDefaultAddressCity(kw['city'].title()) person_object.setDefaultAddressZipCode(kw['zipcode']) @@ -432,8 +484,10 @@ class ERP5ShopOrderConduit(ERP5Conduit): person_object.setDefaultAddressRegion(region_path) # else: # # TODO : Ask the user to select an appropriate region - person_object.setDefaultEmailText(kw['email']) - person_object.setDefaultTelephoneText(kw['phone']) + if kw.has_key('email') and kw['email'] != None: + person_object.setDefaultEmailText(kw['email']) + if kw.has_key('phone') and kw['phone'] != None: + person_object.setDefaultTelephoneText(kw['phone']) # # TODO : Don't work # person_object.setDefaultCareerRole("client") # Split the name to give at least a required LastName @@ -450,39 +504,44 @@ class ERP5ShopOrderConduit(ERP5Conduit): if owner_type.find('o') != -1: # # TODO : fix this # person_object.setSubordination("organisation/" + owner_id) - organisation_object.setTitle(kw['organisation'].title()) - organisation_object.setCorporateName(kw['organisation'].title()) - organisation_object.setRole("client") - if kw.has_key('eu_vat'): - organisation_object.setEuVatCode(kw['eu_vat']) + if kw.has_key('organisation') and kw['organisation'] != None: + org_object.setTitle(kw['organisation'].title()) + if kw.has_key('eu_vat') and kw['eu_vat'] != None: + org_object.setEuVatCode(kw['eu_vat']) + # Test for debug + if (not (kw.has_key('organisation')) or (kw.has_key('organisation') and kw['organisation'] != None)) and (not (kw.has_key('eu_vat')) or (kw.has_key('eu_vat') and kw['eu_vat'] != None)): + LOG("AARRGG ! Big conflict detected : this organisation has no title or eu_vat. These properties are primary key to deduced that the storever member account was an organisation >>>>>>>>>>", 0, '') + org_object.setCorporateName(kw['organisation'].title()) + org_object.setRole("client") + # The customer is not a person or a person of an organisation, so the customer is an organisation... else: # Link the customer with the Sale Order object.setDestination("organisation/" + owner_id) object.setDestinationDecision("organisation/" + owner_id) # All informations describe the organisation - organisation_object.setTitle(kw['organisation'].title()) - organisation_object.setCorporateName(kw['organisation'].title()) - organisation_object.setRole("client") - organisation_object.setEuVatCode(kw['eu_vat']) - organisation_object.setDefaultAddressStreetAddress(kw['address'].title()) - organisation_object.setDefaultAddressCity(kw['city'].title()) - organisation_object.setDefaultAddressZipCode(kw['zipcode']) + org_object.setTitle(kw['organisation'].title()) + org_object.setCorporateName(kw['organisation'].title()) + org_object.setRole("client") + org_object.setEuVatCode(kw['eu_vat']) + org_object.setDefaultAddressStreetAddress(kw['address'].title()) + org_object.setDefaultAddressCity(kw['city'].title()) + org_object.setDefaultAddressZipCode(kw['zipcode']) # Search the country in the region category if kw['country'] != None: region_path = self.countrySearch(erp5_site, None, kw['country']) if region_path != None: - organisation_object.setDefaultAddressRegion(region_path) + org_object.setDefaultAddressRegion(region_path) # else: # # TODO : Ask the user to select an appropriate region - organisation_object.setDefaultEmailText(kw['email']) - organisation_object.setDefaultTelephoneText(kw['phone']) + org_object.setDefaultEmailText(kw['email']) + org_object.setDefaultTelephoneText(kw['phone']) # Save the billing address in the description, because there is no dedicated place for it if kw.has_key('billing_address') and len(kw['billing_address']) > 0: object.setDescription("Send the bill to : " + kw['billing_address']) # Set the Title because its required - object.setTitle("Storever Order " + kw['order_id']) + object.setTitle("Storever Order " + str(kw['order_id'])) # # ONLY for information (will be used in the future) object.setDescription(str(object.getDescription()) + "\n\nTotal Price (with transport fees) :" + str(kw['total_price'])) @@ -531,9 +590,8 @@ class ERP5ShopOrderConduit(ERP5Conduit): # Create a new order line in this order to represent the shipment service ship_order_line_id = "storever-" + shipment_id - object.newContent( portal_type = 'Sale Order Line' - , id = ship_order_line_id) - ship_order_object = object._getOb(ship_order_line_id) + ship_order_object = object.newContent( portal_type = 'Sale Order Line' + , id = ship_order_line_id) ship_order_object.setQuantity(1.0) ship_order_object.setPrice(kw['send_fee']) ship_order_object.setQuantityUnit('Unit') @@ -579,15 +637,13 @@ class ERP5ShopOrderConduit(ERP5Conduit): product_object.setSourceBasePriceValidity(kw['product_expiration_date']) product_object.setBasePrice(kw['product_price']) product_object.setQuantityUnit('Unit') - - # Set the worflow status - self.setProductWorkflow(product_object, kw['product_title']) - + # Save the worflow status for later modification + workflow_joblist.append((product_object, kw['product_title'])) # In storever, every option are set as string in the title of the OrderLine # This part of code create a list of all options choosen by the customer for this product splitted_title = kw['title'].strip().split(":") option_list = (":".join(splitted_title[1:])).split("/") - LOG('Customer option list: ', 0, repr(option_list)) + LOG('Customer option list >>>>>> ', 0, repr(option_list)) # Now, we will find the price of each option option_classes = [ kw['product_disk_price'] @@ -603,7 +659,7 @@ class ERP5ShopOrderConduit(ERP5Conduit): if option == option_key.strip(): priced_list[option] = option_class[option_key] # # TODO : there is no default options in the final priced_list. Is the option 'default' important ? - LOG('Customer option priced list: ', 0, repr(priced_list)) + LOG('Customer option priced list >>>>>>>>> ', 0, repr(priced_list)) # In ERP5, we have decided to represent some options as variation of a product # and some options as new order line of product @@ -621,8 +677,8 @@ class ERP5ShopOrderConduit(ERP5Conduit): keyboard_options[option_key] = options_prices[option_key] elif option_key.lower().find("cd") != -1 or option_key.lower().find("dvd") != -1: optical_options[option_key] = options_prices[option_key] - LOG('Product keyboard layout priced list: ', 0, repr(keyboard_options)) - LOG('Product optical drive priced list: ', 0, repr(optical_options)) + LOG('Product keyboard layout priced list >>>>>>>>> ', 0, repr(keyboard_options)) + LOG('Product optical drive priced list >>>>>>>>> ', 0, repr(optical_options)) # Create a data structure containing all allowed variations variant_category_list = [ ('hd_size', kw['product_disk_price']) @@ -644,7 +700,7 @@ class ERP5ShopOrderConduit(ERP5Conduit): cat_base_object = portal_cat._getOb(cat_base) cat_base_object.newContent ( portal_type = 'Category' , id = cat_id) - LOG("New category '", 0, cat_path + "' created") + LOG("New created category >>>>>>>>>>> ", 0, cat_path) # Set the base variation of the product product_object.setVariationBaseCategoryList(base_cat_list) @@ -661,6 +717,7 @@ class ERP5ShopOrderConduit(ERP5Conduit): for option in priced_list: option_is_variant = None for (cat_base, cat_data) in variant_category_list: + LOG('editDocument, cat_base',0,cat_base) base_cat_object = portal_cat.resolveCategory(cat_base) cat_list = base_cat_object.getCategoryChildIdItemList() for (category, category_bis) in cat_list: @@ -675,7 +732,7 @@ class ERP5ShopOrderConduit(ERP5Conduit): if option_is_variant == None: customer_product_option_list[option] = priced_list[option] if len(customer_product_option_list) + len(customer_product_variation_list) != len(priced_list): - LOG('Wrong repartition of the customer priced list', 200) + LOG('>>>>>>> Wrong repartition of the customer priced list', 200) LOG('>>>>>> Customer product option priced list: ', 0, repr(customer_product_option_list)) LOG('>>>>>> Customer product variation priced list: ', 0, repr(customer_product_variation_list)) LOG('>>>>>> Customer product base variation list: ', 0, repr(customer_product_base_variation_list)) @@ -700,20 +757,18 @@ class ERP5ShopOrderConduit(ERP5Conduit): opt_prod_object.setTitle(opt_prod_title.title()) opt_prod_object.setBasePrice(opt_prod_price) opt_prod_object.setQuantityUnit('Unit') - # Set the workflow state of the optionnal product - self.setProductWorkflow(opt_prod_object, opt_prod_key) + # Save the workflow state changing for later modification + workflow_joblist.append((opt_prod_object, opt_prod_key)) # Get the last number of order lines # This process is needed to distinguish the same option created for two different product - # and avoid problem when a new Order line is created for a option product already used + # and avoid problem when a new Order line is created for an option product already used # inside the same Sale Order last_line_num = self.getLastOrderLineNumber(parent_order_object) opt_prod_line_id = "storever-" + str(last_line_num) + "-" + opt_prod_key # Create an order line for the product - parent_order_object.newContent ( portal_type = 'Sale Order Line' - , id = opt_prod_line_id) - opt_order_line_object = parent_order_object._getOb(opt_prod_line_id) - + opt_order_line_object = parent_order_object.newContent( portal_type = 'Sale Order Line' + , id = opt_prod_line_id) # Set several properties of the new orderLine opt_order_line_object.setQuantityUnit('Unit') opt_order_line_object.setPrice(opt_prod_price) @@ -725,13 +780,6 @@ class ERP5ShopOrderConduit(ERP5Conduit): # Calcul the sum of option prices options_price_sum += float(opt_prod_price) - - - - - - - # # TODO: don't forget to manage the VAT values # TODO: # Try to find a previous OrderLine to update @@ -761,4 +809,10 @@ class ERP5ShopOrderConduit(ERP5Conduit): # # TODO : fix this # object.setVariationCategoryList(category_list) - return \ No newline at end of file + # Do all workflow change at the end + LOG("enter workflow loop >>>>>>>>",0,repr(workflow_joblist)) + for (object, object_title) in workflow_joblist: + LOG("Workflow to change :: >>>>>>>>",0,repr(object, object_title)) + self.setProductWorkflow(object, object_title) + + return diff --git a/product/ERP5SyncML/Publication.py b/product/ERP5SyncML/Publication.py index 4dfb9bfdaa..a1a2dfd715 100755 --- a/product/ERP5SyncML/Publication.py +++ b/product/ERP5SyncML/Publication.py @@ -33,6 +33,7 @@ from Products.ERP5Type import Permissions from Products.ERP5Type.Document.Folder import Folder from AccessControl import ClassSecurityInfo from Products.ERP5Type import PropertySheet +from zLOG import LOG def addSubscriber( self, id, title='', REQUEST=None ): """ @@ -83,6 +84,16 @@ class Subscriber(Subscription): Send ACK for a group of documents """ + def getConduit(self): + """ + Return the conduit of the publication + """ + #LOG('Subscriber.getConduit, self.getPhysicalPath()',0,self.getPhysicalPath()) + #LOG('Subscriber.getConduit, self.getParent().getPhysicalPath()',0,self.aq_parent.getPhysicalPath()) + #LOG('Subscriber.getConduit, self.getParent()',0,self.getParent()) + return self.aq_parent.getConduit() + #return self.conduit + def SendDocuments(self): """ We send all the updated documents (ie. documents not marked @@ -142,7 +153,7 @@ class Publication(Subscription): constructors = (addPublication,) # Constructor - def __init__(self, id, title, publication_url, destination_path, query, xml_mapping, gpg_key): + def __init__(self, id, title, publication_url, destination_path, query, xml_mapping, conduit, gpg_key): """ constructor """ @@ -156,6 +167,7 @@ class Publication(Subscription): self.gpg_key = gpg_key self.setGidGenerator(None) self.setIdGenerator(None) + self.setConduit(conduit) Folder.__init__(self, id) self.title = title diff --git a/product/ERP5SyncML/Subscription.py b/product/ERP5SyncML/Subscription.py index 244922f614..10450aab0b 100755 --- a/product/ERP5SyncML/Subscription.py +++ b/product/ERP5SyncML/Subscription.py @@ -342,7 +342,10 @@ class Signature(Folder,SyncCode): """ set the XML corresponding to the object """ - return self.xml + xml = getattr(self,'xml',None) + if xml == '': + xml = None + return xml def setTempXML(self, xml): """ @@ -610,7 +613,7 @@ class Subscription(Folder, SyncCode): ) # Constructor - def __init__(self, id, title, publication_url, subscription_url, destination_path, query, xml_mapping, gpg_key): + def __init__(self, id, title, publication_url, subscription_url, destination_path, query, xml_mapping, conduit, gpg_key): """ We need to create a dictionnary of signatures of documents which belong to the synchronisation @@ -621,7 +624,7 @@ class Subscription(Folder, SyncCode): self.subscription_url = str(subscription_url) self.destination_path = str(destination_path) self.setQuery(query) - self.xml_mapping = xml_mapping + self.setXMLMapping(xml_mapping) self.anchor = None self.session_id = 0 #self.signatures = PersistentMapping() @@ -631,6 +634,7 @@ class Subscription(Folder, SyncCode): self.gpg_key = gpg_key self.setGidGenerator(None) self.setIdGenerator(None) + self.setConduit(conduit) Folder.__init__(self, id) self.title = title @@ -675,6 +679,14 @@ class Subscription(Folder, SyncCode): LOG('Subscription',0,'getSynchronizationType: %s' % code) return code + def setXMLMapping(self, value): + """ + this the name of the method used in order to get the xml + """ + if value == '': + value = None + self.xml_mapping = value + def checkCorrectRemoteSessionId(self, session_id): """ We will see if the last session id was the same @@ -726,6 +738,19 @@ class Subscription(Folder, SyncCode): """ self.id = id + def setConduit(self, value): + """ + set the Conduit + """ + self.conduit = value + + def getConduit(self): + """ + get the Conduit + + """ + return getattr(self,'conduit',None) + def getQuery(self): """ return the query @@ -748,8 +773,8 @@ class Subscription(Folder, SyncCode): """ set the query """ - if query in (None,''): - query = 'objectValues' + if query == '': + query = None self.query = query def getPublicationUrl(self): @@ -777,11 +802,17 @@ class Subscription(Folder, SyncCode): xml_mapping = getattr(self,'xml_mapping','asXML') return xml_mapping - def setXMLMapping(self, xml_mapping): + def getXMLFromObject(self,object): """ return the xml mapping """ - self.xml_mapping = xml_mapping + xml_mapping = self.getXMLMapping() + xml = '' + if xml_mapping is not None: + func = getattr(object,xml_mapping,None) + if func is not None: + xml = func() + return xml def setGidGenerator(self, method): """ @@ -843,6 +874,15 @@ class Subscription(Folder, SyncCode): LOG('getObjectFromGid',0,'returning None') return None +# def setOneWaySyncFromServer(self,value): +# """ +# If this option is enabled, then we will not +# send our own modifications +# """ +# self.one_way_sync_from_server = value +# + + def getObjectList(self): """ This returns the list of sub-object corresponding @@ -851,6 +891,8 @@ class Subscription(Folder, SyncCode): destination = self.getDestination() query = self.getQuery() query_list = [] + if query is None: + return query_list if type(query) is type('a'): query_method = getattr(destination,query,None) if query_method is not None: diff --git a/product/ERP5SyncML/SynchronizationTool.py b/product/ERP5SyncML/SynchronizationTool.py index eda0e354d7..cc4aa96f3c 100755 --- a/product/ERP5SyncML/SynchronizationTool.py +++ b/product/ERP5SyncML/SynchronizationTool.py @@ -36,6 +36,7 @@ from Globals import InitializeClass, DTMLFile, PersistentMapping, Persistent from AccessControl import ClassSecurityInfo, getSecurityManager from Products.CMFCore import CMFCorePermissions from Products.ERP5SyncML import _dtmldir +from Products.ERP5SyncML import Conduit from Publication import Publication,Subscriber from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2 from Subscription import Subscription,Signature @@ -158,7 +159,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati security.declareProtected(Permissions.ModifyPortalContent, 'manage_addPublication') def manage_addPublication(self, title, publication_url, destination_path, - query, xml_mapping, gpg_key, RESPONSE=None): + query, xml_mapping, conduit, gpg_key, RESPONSE=None): """ create a new publication """ @@ -168,7 +169,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati folder = self.getObjectContainer() new_id = self.getPublicationIdFromTitle(title) pub = Publication(new_id, title, publication_url, destination_path, - query, xml_mapping, gpg_key) + query, xml_mapping, conduit, gpg_key) folder._setObject( new_id, pub ) #if len(self.list_publications) == 0: # self.list_publications = PersistentMapping() @@ -178,7 +179,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati security.declareProtected(Permissions.ModifyPortalContent, 'manage_addSubscription') def manage_addSubscription(self, title, publication_url, subscription_url, - destination_path, query, xml_mapping, gpg_key, RESPONSE=None): + destination_path, query, xml_mapping, conduit, gpg_key, RESPONSE=None): """ XXX should be renamed as addSubscription create a new subscription @@ -189,7 +190,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati folder = self.getObjectContainer() new_id = self.getSubscriptionIdFromTitle(title) sub = Subscription(new_id, title, publication_url, subscription_url, - destination_path, query, xml_mapping, gpg_key) + destination_path, query, xml_mapping, conduit, gpg_key) folder._setObject( new_id, sub ) #if len(self.list_subscriptions) == 0: # self.list_subscriptions = PersistentMapping() @@ -199,7 +200,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati security.declareProtected(Permissions.ModifyPortalContent, 'manage_editPublication') def manage_editPublication(self, title, publication_url, destination_path, - query, xml_mapping, gpg_key, RESPONSE=None): + query, xml_mapping, conduit, gpg_key, RESPONSE=None): """ modify a publication """ @@ -208,6 +209,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati pub.setPublicationUrl(publication_url) pub.setDestinationPath(destination_path) pub.setQuery(query) + pub.setConduit(conduit) pub.setXMLMapping(xml_mapping) pub.setGPGKey(gpg_key) if RESPONSE is not None: @@ -215,7 +217,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati security.declareProtected(Permissions.ModifyPortalContent, 'manage_editSubscription') def manage_editSubscription(self, title, publication_url, subscription_url, - destination_path, query, xml_mapping, gpg_key, RESPONSE=None): + destination_path, query, xml_mapping, conduit, gpg_key, RESPONSE=None): """ modify a subscription """ @@ -224,6 +226,7 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati sub.setPublicationUrl(publication_url) sub.setDestinationPath(destination_path) sub.setQuery(query) + sub.setConduit(conduit) sub.setXMLMapping(xml_mapping) sub.setGPGKey(gpg_key) sub.setSubscriptionUrl(subscription_url) @@ -370,20 +373,22 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati #conflict.setDomain('Publication') conflict.setSubscriber(subscriber) #conflict.setDomainId(subscriber.getId()) - conflict_list += [conflict.__of__(self)] + if path is None or conflict.getObjectPath() == path: + conflict_list += [conflict.__of__(subscriber)] for subscription in self.getSubscriptionList(): sub_conflict_list = subscription.getConflictList() for conflict in sub_conflict_list: #conflict.setDomain('Subscription') conflict.setSubscriber(subscription) #conflict.setDomainId(subscription.getId()) - conflict_list += [conflict.__of__(self)] - if path is not None: # Retrieve only conflicts for a given path - new_list = [] - for conflict in conflict_list: - if conflict.getObjectPath() == path: - new_list += [conflict.__of__(self)] - return new_list + if path is None or conflict.getObjectPath() == path: + conflict_list += [conflict.__of__(subscription)] + #if path is not None: # Retrieve only conflicts for a given path + # new_list = [] + # for conflict in conflict_list: + # if conflict.getObjectPath() == path: + # new_list += [conflict.__of__(self)] + # return new_list return conflict_list security.declareProtected(Permissions.AccessContentsInformation,'getDocumentConflictList') @@ -530,7 +535,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati object_id = docid if object_id in directory.objectIds(): directory._delObject(object_id) - conduit = ERP5Conduit() + #conduit = ERP5Conduit() + conduit_name = subscriber.getConduit() + conduit = getattr(getattr(Conduit,conduit_name),conduit_name)() conduit.addNode(xml=publisher_xml,object=directory,object_id=object_id) subscriber_document = directory._getOb(object_id) for c in self.getConflictList(conflict.getObjectPath()): @@ -565,7 +572,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati publisher_xml = self.getXMLObject(object=publisher_object,xml_mapping = subscriber.getXMLMapping()) directory = publisher_object.aq_inner.aq_parent object_id = self._getCopyId(publisher_object) - conduit = ERP5Conduit() + #conduit = ERP5Conduit() + conduit_name = subscriber.getConduit() + conduit = getattr(getattr(Conduit,conduit_name),conduit_name)() conduit.addNode(xml=publisher_xml,object=directory,object_id=object_id) subscriber_document = directory._getOb(object_id) subscriber_document._conflict_resolution = 1 @@ -576,7 +585,6 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati conflict.setCopyPath(copy_path) return copy_path - security.declareProtected(Permissions.AccessContentsInformation, 'getSubscriberDocument') def getSubscriberDocument(self, conflict): """ @@ -613,7 +621,9 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati # get the signature: LOG('p_sync.setRemoteObject, subscriber: ',0,subscriber) signature = subscriber.getSignature(object.getId()) # XXX may be change for rid - conduit = ERP5Conduit() + #conduit = ERP5Conduit() + conduit_name = subscriber.getConduit() + conduit = getattr(getattr(Conduit,conduit_name),conduit_name)() for xupdate in conflict.getXupdateList(): conduit.updateNode(xml=xupdate,object=object,force=1) if solve_conflict: @@ -632,7 +642,6 @@ class SynchronizationTool( SubscriptionSynchronization, PublicationSynchronizati directory._delObject(copy_id) signature.setStatus(self.PUB_CONFLICT_MERGE) - security.declareProtected(Permissions.ModifyPortalContent, 'manageLocalValue') def managePublisherValue(self, subscription_url, property_id, object_path, RESPONSE=None): """ diff --git a/product/ERP5SyncML/XMLSyncUtils.py b/product/ERP5SyncML/XMLSyncUtils.py index ead5ec4072..e8d0c766ca 100755 --- a/product/ERP5SyncML/XMLSyncUtils.py +++ b/product/ERP5SyncML/XMLSyncUtils.py @@ -218,12 +218,13 @@ class XMLSyncUtilsMixin(SyncCode): """ xml_method = None xml = "" - if hasattr(object,xml_mapping): - xml_method = getattr(object,xml_mapping) - elif hasattr(object,'manage_FTPget'): - xml_method = getattr(object,'manage_FTPget') - if xml_method is not None: - xml = xml_method() + if xml_mapping is not None: + if hasattr(object,xml_mapping): + xml_method = getattr(object,xml_mapping) + elif hasattr(object,'manage_FTPget'): + xml_method = getattr(object,'manage_FTPget') + if xml_method is not None: + xml = xml_method() return xml def getSessionId(self, xml): @@ -601,23 +602,12 @@ class XMLSyncUtilsMixin(SyncCode): # Here we first check if the object was modified or not by looking at dates if signature is not None: signature.checkSynchronizationNeeded(object) -# LOG('getSyncMLData',0,'signature.status: %s' % str(signature.getStatus())) -# LOG('getSyncMLData',0,'signature.action: %s' % str(signature.getAction())) -# last_modification = DateTime(object.ModificationDate()) -# LOG('getSyncMLData object.ModificationDate()',0,object.ModificationDate()) -# last_synchronization = signature.getLastSynchronizationDate() -# parent = object.aq_parent -# # XXX CPS Specific -# #if parent.id == 'portal_repository': -# if 1: -# if last_synchronization is not None and last_modification is not None: -# if last_synchronization > last_modification: -# LOG('getSyncMLData, no modification on: ',0,object.id) -# signature.setStatus(self.SYNCHRONIZED) status = self.SENT more_data=0 # For the case it was never synchronized, we have to send everything - if signature==None or (signature.getXML()==None and signature.getStatus()!=self.PARTIAL) or \ + if signature is not None and signature.getXMLMapping()==None: + pass + elif signature==None or (signature.getXML()==None and signature.getStatus()!=self.PARTIAL) or \ self.getAlertCode(remote_xml)==self.SLOW_SYNC: #LOG('PubSyncModif',0,'Current object.getPath: %s' % object.getPath()) LOG('getSyncMLData',0,'no signature for gid: %s' % object_gid) @@ -781,10 +771,10 @@ class XMLSyncUtilsMixin(SyncCode): LOG('applyActionList',0,'object after add: %s' % repr(object)) if object is not None: LOG('SyncModif',0,'addNode, found the object') - mapping = getattr(object,domain.getXMLMapping(),None) - xml_object = '' - if mapping is not None: - xml_object = mapping() + #mapping = getattr(object,domain.getXMLMapping(),None) + xml_object = domain.getXMLFromObject(object) + #if mapping is not None: + # xml_object = mapping() signature.setStatus(self.SYNCHRONIZED) signature.setId(object.getId()) signature.setXML(xml_object) @@ -798,14 +788,14 @@ class XMLSyncUtilsMixin(SyncCode): signature = subscriber.getSignature(object_gid) LOG('SyncModif',0,'previous signature: %s' % str(signature)) previous_xml = signature.getXML() - LOG('SyncModif',0,'previous signature: %i' % len(previous_xml)) + #LOG('SyncModif',0,'previous signature: %i' % len(previous_xml)) conflict_list += conduit.updateNode(xml=data_subnode, object=object, previous_xml=signature.getXML(),force=force, simulate=simulate) - mapping = getattr(object,domain.getXMLMapping(),None) - xml_object = '' - if mapping is not None: - xml_object = mapping() + #mapping = getattr(object,domain.getXMLMapping(),None) + xml_object = domain.getXMLFromObject(object) + #if mapping is not None: + # xml_object = mapping() signature.setTempXML(xml_object) if conflict_list != []: status_code = self.CONFLICT @@ -922,7 +912,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin): Send the server modification, this happens after the Synchronization initialization """ - from Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit + #from Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit + from Products.ERP5SyncML import Conduit has_response = 0 #check if syncmodif replies to this messages cmd_id = 1 # specifies a SyncML message-unique command identifier LOG('SyncModif',0,'Starting... domain: %s' % str(domain)) @@ -968,7 +959,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin): remote_xml=remote_xml) alert_code = self.getAlertCode(remote_xml) - conduit = ERP5Conduit() + #conduit = ERP5Conduit() + conduit_name = subscriber.getConduit() + conduit = getattr(getattr(Conduit,conduit_name),conduit_name)() LOG('SyncModif, subscriber: ',0,subscriber) # Then apply the list of actions (xml_confirmation,has_next_action,cmd_id) = self.applyActionList(cmd_id=cmd_id, diff --git a/product/ERP5SyncML/dtml/managePublications.dtml b/product/ERP5SyncML/dtml/managePublications.dtml index 09c91325d4..f54746cd61 100755 --- a/product/ERP5SyncML/dtml/managePublications.dtml +++ b/product/ERP5SyncML/dtml/managePublications.dtml @@ -87,6 +87,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. <input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" /> </td> </tr> + <tr> + <td align="left" valign="top"> + <div class="form-label"> + Conduit + </label></div> + </td> + <td align="left" valign="top"> + <input type="text" name="conduit" value="<dtml-var getConduit>" size="40" /> + </td> + </tr> <tr> <td align="left" valign="top"> <div class="form-label"> diff --git a/product/ERP5SyncML/dtml/manageSubscriptions.dtml b/product/ERP5SyncML/dtml/manageSubscriptions.dtml index ee9cb6f68d..0b2170f134 100755 --- a/product/ERP5SyncML/dtml/manageSubscriptions.dtml +++ b/product/ERP5SyncML/dtml/manageSubscriptions.dtml @@ -97,6 +97,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. <input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" /> </td> </tr> + <tr> + <td align="left" valign="top"> + <div class="form-label"> + Conduit + </label></div> + </td> + <td align="left" valign="top"> + <input type="text" name="conduit" value="<dtml-var getConduit>" size="40" /> + </td> + </tr> <tr> <td align="left" valign="top"> <div class="form-label"> diff --git a/product/ERP5SyncML/dtml/manage_addPublication.dtml b/product/ERP5SyncML/dtml/manage_addPublication.dtml index c898ab29a1..f1a24a7d5c 100755 --- a/product/ERP5SyncML/dtml/manage_addPublication.dtml +++ b/product/ERP5SyncML/dtml/manage_addPublication.dtml @@ -83,6 +83,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. <input type="text" name="xml_mapping" size="40" /> </td> </tr> + <tr> + <td align="left" valign="top"> + <div class="form-label"> + Conduit + </label></div> + </td> + <td align="left" valign="top"> + <input type="text" name="conduit" size="40" /> + </td> + </tr> <tr> <td align="left" valign="top"> <div class="form-label"> diff --git a/product/ERP5SyncML/dtml/manage_addSubscription.dtml b/product/ERP5SyncML/dtml/manage_addSubscription.dtml index 506c47f63f..6c0c0ac2af 100755 --- a/product/ERP5SyncML/dtml/manage_addSubscription.dtml +++ b/product/ERP5SyncML/dtml/manage_addSubscription.dtml @@ -93,6 +93,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. <input type="text" name="xml_mapping" size="40" /> </td> </tr> + <tr> + <td align="left" valign="top"> + <div class="form-label"> + Conduit + </label></div> + </td> + <td align="left" valign="top"> + <input type="text" name="conduit" size="40" /> + </td> + </tr> <tr> <td align="left" valign="top"> <div class="form-label"> diff --git a/product/ERP5SyncML/tests/testERP5SyncML.py b/product/ERP5SyncML/tests/testERP5SyncML.py index 36eef8fec7..dc103a4447 100755 --- a/product/ERP5SyncML/tests/testERP5SyncML.py +++ b/product/ERP5SyncML/tests/testERP5SyncML.py @@ -107,7 +107,7 @@ class TestERP5SyncML(ERP5TypeTestCase): /person_client2 : empty """ #return ('sync_crm',) - return ('erp5_core',) + return () def getSynchronizationTool(self): return getattr(self.getPortal(), 'portal_synchronizations', None) @@ -124,57 +124,57 @@ class TestERP5SyncML(ERP5TypeTestCase): def getPortalId(self): return self.getPortal().getId() - def testHasEverything(self, quiet=0, run=run_all_test): + def test_01_HasEverything(self, quiet=0, run=run_all_test): # Test if portal_synchronizations was created if not run: return if not quiet: ZopeTestCase._print('\nTest Has Everything ') - LOG('Testing... ',0,'testHasEverything') + LOG('Testing... ',0,'test_01_HasEverything') self.failUnless(self.getSynchronizationTool()!=None) #self.failUnless(self.getPersonServer()!=None) #self.failUnless(self.getPersonClient1()!=None) #self.failUnless(self.getPersonClient2()!=None) - def testAddPublication(self, quiet=0, run=run_all_test): + def test_02_AddPublication(self, quiet=0, run=run_all_test): if not run: return if not quiet: ZopeTestCase._print('\nTest Add a Publication ') - LOG('Testing... ',0,'testAddPublication') + LOG('Testing... ',0,'test_02_AddPublication') portal_id = self.getPortalName() portal_sync = self.getSynchronizationTool() portal_sync.manage_addPublication(self.pub_id,self.publication_url, - '/%s/person_server' % portal_id,'', - self.xml_mapping,'') + '/%s/person_server' % portal_id,'objectValues', + self.xml_mapping,'ERP5Conduit','') pub = portal_sync.getPublication(self.pub_id) self.failUnless(pub is not None) - def testAddSubscription1(self, quiet=0, run=run_all_test): + def test_03_AddSubscription1(self, quiet=0, run=run_all_test): if not run: return if not quiet: ZopeTestCase._print('\nTest Add First Subscription ') - LOG('Testing... ',0,'testAddSubscription1') + LOG('Testing... ',0,'test_03_AddSubscription1') portal_id = self.getPortalId() portal_sync = self.getSynchronizationTool() portal_sync.manage_addSubscription(self.sub_id1,self.publication_url, - self.subscription_url1,'/%s/person_client1' % portal_id,'', - self.xml_mapping,'') + self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues', + self.xml_mapping,'ERP5Conduit','') sub = portal_sync.getSubscription(self.sub_id1) self.failUnless(sub is not None) - def testAddSubscription2(self, quiet=0, run=run_all_test): + def test_04_AddSubscription2(self, quiet=0, run=run_all_test): if not run: return if not quiet: ZopeTestCase._print('\nTest Add Second Subscription ') - LOG('Testing... ',0,'testAddSubscription2') + LOG('Testing... ',0,'test_04_AddSubscription2') portal_id = self.getPortalId() portal_sync = self.getSynchronizationTool() portal_sync.manage_addSubscription(self.sub_id2,self.publication_url, - self.subscription_url2,'/%s/person_client2' % portal_id,'', - self.xml_mapping,'') + self.subscription_url2,'/%s/person_client2' % portal_id,'objectValues', + self.xml_mapping,'ERP5Conduit','') sub = portal_sync.getSubscription(self.sub_id2) self.failUnless(sub is not None) - def login(self, quiet=0, run=run_all_test): + def login(self, quiet=0): uf = self.getPortal().acl_users uf._doAddUser('seb', '', ['Manager'], []) user = uf.getUserById('seb').__of__(uf) @@ -213,12 +213,12 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(nb_person==2) return nb_person - def setupPublicationAndSubscription(self, quiet=0, run=run_all_test): - self.testAddPublication(quiet=1,run=1) - self.testAddSubscription1(quiet=1,run=1) - self.testAddSubscription2(quiet=1,run=1) + def setupPublicationAndSubscription(self, quiet=0, run=1): + self.test_02_AddPublication(quiet=1,run=1) + self.test_03_AddSubscription1(quiet=1,run=1) + self.test_04_AddSubscription2(quiet=1,run=1) - def setupPublicationAndSubscriptionAndGid(self, quiet=0, run=run_all_test): + def setupPublicationAndSubscriptionAndGid(self, quiet=0, run=1): self.setupPublicationAndSubscription(quiet=1,run=1) def getGid(object): return object.getTitle() @@ -233,20 +233,20 @@ class TestERP5SyncML(ERP5TypeTestCase): sub1.setIdGenerator('generateNewId') sub2.setIdGenerator('generateNewId') - def testGetSynchronizationList(self, quiet=0, run=run_all_test): + def test_05_GetSynchronizationList(self, quiet=0, run=run_all_test): # This test the getSynchronizationList, ie, # We want to see if we retrieve both the subscription # and the publication if not run: return if not quiet: ZopeTestCase._print('\nTest getSynchronizationList ') - LOG('Testing... ',0,'testGetSynchronizationList') + LOG('Testing... ',0,'test_05_GetSynchronizationList') self.setupPublicationAndSubscription(quiet=1,run=1) portal_sync = self.getSynchronizationTool() synchronization_list = portal_sync.getSynchronizationList() self.failUnless(len(synchronization_list)==self.nb_synchronization) - def testGetObjectList(self, quiet=0, run=run_all_test): + def test_06_GetObjectList(self, quiet=0, run=run_all_test): """ This test the default getObjectList, ie, when the query is 'objectValues', and this also test if we enter @@ -255,7 +255,7 @@ class TestERP5SyncML(ERP5TypeTestCase): if not run: return if not quiet: ZopeTestCase._print('\nTest getObjectList ') - LOG('Testing... ',0,'testGetObjectList') + LOG('Testing... ',0,'test_06_GetObjectList') self.login() self.setupPublicationAndSubscription(quiet=1,run=1) nb_person = self.populatePersonServer(quiet=1,run=1) @@ -276,7 +276,7 @@ class TestERP5SyncML(ERP5TypeTestCase): object_list = publication.getObjectList() self.failUnless(len(object_list)==1) - def testExportImport(self, quiet=0, run=run_all_test): + def test_07_ExportImport(self, quiet=0, run=run_all_test): """ We will try to export a person with asXML And then try to add it to another folder with a conduit @@ -284,7 +284,7 @@ class TestERP5SyncML(ERP5TypeTestCase): if not run: return if not quiet: ZopeTestCase._print('\nTest Export and Import ') - LOG('Testing... ',0,'testExportImport') + LOG('Testing... ',0,'test_07_ExportImport') self.login() self.populatePersonServer(quiet=1,run=1) person_server = self.getPersonServer() @@ -303,7 +303,7 @@ class TestERP5SyncML(ERP5TypeTestCase): c_local_role = person_client1.get_local_roles() self.assertEqual(s_local_role,c_local_role) - def synchronize(self, id, run=run_all_test): + def synchronize(self, id, run=1): """ This just define how we synchronize, we have to define it here because it is specific to the unit testing @@ -334,7 +334,7 @@ class TestERP5SyncML(ERP5TypeTestCase): nb_message += 1 + result['has_response'] return nb_message - def synchronizeWithBrokenMessage(self, id, run=run_all_test): + def synchronizeWithBrokenMessage(self, id, run=1): """ This just define how we synchronize, we have to define it here because it is specific to the unit testing @@ -371,13 +371,13 @@ class TestERP5SyncML(ERP5TypeTestCase): nb_message += 1 + result['has_response'] return nb_message - def testFirstSynchronization(self, quiet=0, run=run_all_test): + def test_08_FirstSynchronization(self, quiet=0, run=run_all_test): # We will try to populate the folder person_client1 # with the data form person_server if not run: return if not quiet: ZopeTestCase._print('\nTest First Synchronization ') - LOG('Testing... ',0,'testFirstSynchronization') + LOG('Testing... ',0,'test_08_FirstSynchronization') self.login() self.setupPublicationAndSubscription(quiet=1,run=1) nb_person = self.populatePersonServer(quiet=1,run=1) @@ -418,13 +418,13 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(person2_c.getFirstName()==self.first_name1) self.failUnless(person2_c.getLastName()==self.last_name1) - def testFirstSynchronizationWithLongLines(self, quiet=0, run=run_all_test): + def test_09_FirstSynchronizationWithLongLines(self, quiet=0, run=run_all_test): # We will try to populate the folder person_client1 # with the data form person_server if not run: return if not quiet: ZopeTestCase._print('\nTest First Synchronization With Long Lines ') - LOG('Testing... ',0,'testFirstSynchronizationWithLongLines') + LOG('Testing... ',0,'test_09_FirstSynchronizationWithLongLines') self.login() self.setupPublicationAndSubscription(quiet=1,run=1) nb_person = self.populatePersonServer(quiet=1,run=1) @@ -448,13 +448,13 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(person1_c.getFirstName()==long_line) self.failUnless(person1_c.getLastName()==self.last_name1) - def testGetObjectFromGid(self, quiet=0, run=run_all_test): + def test_10_GetObjectFromGid(self, quiet=0, run=run_all_test): # We will try to get an object from a publication # just by givin the gid if not run: return if not quiet: ZopeTestCase._print('\nTest getObjectFromGid ') - LOG('Testing... ',0,'testGetObjectFromGid') + LOG('Testing... ',0,'test_10_GetObjectFromGid') self.login() self.setupPublicationAndSubscription(quiet=1,run=1) self.populatePersonServer(quiet=1,run=1) @@ -465,14 +465,14 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(object is not None) self.failUnless(object.getId()==self.id1) - def testGetSynchronizationState(self, quiet=0, run=run_all_test): + def test_11_GetSynchronizationState(self, quiet=0, run=run_all_test): # We will try to get the state of objects # that are just synchronized, if not run: return if not quiet: ZopeTestCase._print('\nTest getSynchronizationState ') - LOG('Testing... ',0,'testGetSynchronizationState') - self.testFirstSynchronization(quiet=1,run=1) + LOG('Testing... ',0,'test_11_GetSynchronizationState') + self.test_08_FirstSynchronization(quiet=1,run=1) portal_sync = self.getSynchronizationTool() person_server = self.getPersonServer() person1_s = person_server._getOb(self.id1) @@ -486,7 +486,7 @@ class TestERP5SyncML(ERP5TypeTestCase): # for each subscriber self.checkSynchronizationStateIsSynchronized() - def checkSynchronizationStateIsSynchronized(self, quiet=0, run=run_all_test): + def checkSynchronizationStateIsSynchronized(self, quiet=0, run=1): portal_sync = self.getSynchronizationTool() person_server = self.getPersonServer() for person in person_server.objectValues(): @@ -513,7 +513,7 @@ class TestERP5SyncML(ERP5TypeTestCase): for m in sub.getSignatureList(): self.assertEquals(m.getPartialXML(),None) - def checkSynchronizationStateIsConflict(self, quiet=0, run=run_all_test): + def checkSynchronizationStateIsConflict(self, quiet=0, run=1): portal_sync = self.getSynchronizationTool() person_server = self.getPersonServer() for person in person_server.objectValues(): @@ -540,12 +540,12 @@ class TestERP5SyncML(ERP5TypeTestCase): for state in state_list: self.failUnless(state[1]==state[0].CONFLICT) - def testUpdateSimpleData(self, quiet=0, run=run_all_test): + def test_12_UpdateSimpleData(self, quiet=0, run=run_all_test): if not run: return if not quiet: ZopeTestCase._print('\nTest Update Simple Data ') - LOG('Testing... ',0,'testUpdateSimpleData') - self.testFirstSynchronization(quiet=1,run=1) + LOG('Testing... ',0,'test_12_UpdateSimpleData') + self.test_08_FirstSynchronization(quiet=1,run=1) # First we do only modification on server portal_sync = self.getSynchronizationTool() person_server = self.getPersonServer() @@ -585,14 +585,14 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(person1_c.getFirstName()==self.first_name3) self.failUnless(person1_c.getDescription()==self.description3) - def testGetConflictList(self, quiet=0, run=run_all_test): + def test_13_GetConflictList(self, quiet=0, run=run_all_test): # We will try to generate a conflict and then to get it # We will also make sure it contains what we want if not run: return if not quiet: ZopeTestCase._print('\nTest Get Conflict List ') - LOG('Testing... ',0,'testGetConflictList') - self.testFirstSynchronization(quiet=1,run=1) + LOG('Testing... ',0,'test_13_GetConflictList') + self.test_08_FirstSynchronization(quiet=1,run=1) # First we do only modification on server portal_sync = self.getSynchronizationTool() person_server = self.getPersonServer() @@ -613,14 +613,14 @@ class TestERP5SyncML(ERP5TypeTestCase): subscriber = conflict.getSubscriber() self.failUnless(subscriber.getSubscriptionUrl()==self.subscription_url1) - def testGetPublisherAndSubscriberDocument(self, quiet=0, run=run_all_test): + def test_14_GetPublisherAndSubscriberDocument(self, quiet=0, run=run_all_test): # We will try to generate a conflict and then to get it # We will also make sure it contains what we want if not run: return if not quiet: ZopeTestCase._print('\nTest Get Publisher And Subscriber Document ') - LOG('Testing... ',0,'testGetPublisherAndSubscriberDocument') - self.testGetConflictList(quiet=1,run=1) + LOG('Testing... ',0,'test_14_GetPublisherAndSubscriberDocument') + self.test_13_GetConflictList(quiet=1,run=1) # First we do only modification on server portal_sync = self.getSynchronizationTool() person_server = self.getPersonServer() @@ -634,14 +634,14 @@ class TestERP5SyncML(ERP5TypeTestCase): subscriber_document = conflict.getSubscriberDocument() self.failUnless(subscriber_document.getDescription()==self.description3) - def testApplyPublisherValue(self, quiet=0, run=run_all_test): + def test_15_ApplyPublisherValue(self, quiet=0, run=run_all_test): # We will try to generate a conflict and then to get it # We will also make sure it contains what we want if not run: return - self.testGetConflictList(quiet=1,run=1) + self.test_13_GetConflictList(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Apply Publisher Value ') - LOG('Testing... ',0,'testApplyPublisherValue') + LOG('Testing... ',0,'test_15_ApplyPublisherValue') portal_sync = self.getSynchronizationTool() conflict_list = portal_sync.getConflictList() conflict = conflict_list[0] @@ -657,16 +657,16 @@ class TestERP5SyncML(ERP5TypeTestCase): conflict_list = portal_sync.getConflictList() self.failUnless(len(conflict_list)==0) - def testApplySubscriberValue(self, quiet=0, run=run_all_test): + def test_16_ApplySubscriberValue(self, quiet=0, run=run_all_test): # We will try to generate a conflict and then to get it # We will also make sure it contains what we want if not run: return - self.testGetConflictList(quiet=1,run=1) + self.test_13_GetConflictList(quiet=1,run=1) portal_sync = self.getSynchronizationTool() conflict_list = portal_sync.getConflictList() if not quiet: ZopeTestCase._print('\nTest Apply Subscriber Value ') - LOG('Testing... ',0,'testApplySubscriberValue') + LOG('Testing... ',0,'test_16_ApplySubscriberValue') conflict = conflict_list[0] person_server = self.getPersonServer() person1_s = person_server._getOb(self.id1) @@ -715,17 +715,17 @@ class TestERP5SyncML(ERP5TypeTestCase): len_path = len(sub_sub_person2.getPhysicalPath()) - 3 self.failUnless(len_path==3) - def testAddSubObject(self, quiet=0, run=run_all_test): + def test_17_AddSubObject(self, quiet=0, run=run_all_test): """ In this test, we synchronize, then add sub object on the server and then see if the next synchronization will also create sub-objects on the client """ if not run: return - self.testFirstSynchronization(quiet=1,run=1) + self.test_08_FirstSynchronization(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Add Sub Object ') - LOG('Testing... ',0,'testAddSubObject') + LOG('Testing... ',0,'test_17_AddSubObject') self.populatePersonServerWithSubObject(quiet=1,run=1) self.synchronize(self.sub_id1) self.synchronize(self.sub_id2) @@ -747,7 +747,7 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(sub_sub_person2.getFirstName()==self.first_name2) self.failUnless(sub_sub_person2.getLastName()==self.last_name2) - def testUpdateSubObject(self, quiet=0, run=run_all_test): + def test_18_UpdateSubObject(self, quiet=0, run=run_all_test): """ In this test, we start with a tree of object already synchronized, then we update a subobject, and we will see @@ -756,10 +756,10 @@ class TestERP5SyncML(ERP5TypeTestCase): the client and the server by the same time """ if not run: return - self.testAddSubObject(quiet=1,run=1) + self.test_17_AddSubObject(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Update Sub Object ') - LOG('Testing... ',0,'testUpdateSubObject') + LOG('Testing... ',0,'test_18_UpdateSubObject') person_client1 = self.getPersonClient1() person1_c = person_client1._getOb(self.id1) sub_person1_c = person1_c._getOb(self.id1) @@ -777,17 +777,17 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(sub_sub_person_s.getDescription()==self.description3) self.failUnless(sub_sub_person_s.getFirstName()==self.first_name3) - def testDeleteObject(self, quiet=0, run=run_all_test): + def test_19_DeleteObject(self, quiet=0, run=run_all_test): """ We will do a first synchronization, then delete an object on both sides, and we will see if nothing is left on the server and also on the two clients """ if not run: return - self.testFirstSynchronization(quiet=1,run=1) + self.test_08_FirstSynchronization(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Delete Object ') - LOG('Testing... ',0,'testDeleteObject') + LOG('Testing... ',0,'test_19_DeleteObject') person_server = self.getPersonServer() person_server.manage_delObjects(self.id1) person_client1 = self.getPersonClient1() @@ -803,7 +803,7 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(len(subscription1.getObjectList())==0) self.failUnless(len(subscription2.getObjectList())==0) - def testDeleteSubObject(self, quiet=0, run=run_all_test): + def test_20_DeleteSubObject(self, quiet=0, run=run_all_test): """ We will do a first synchronization, then delete a sub-object on both sides, and we will see if nothing is left on the server and also @@ -816,10 +816,10 @@ class TestERP5SyncML(ERP5TypeTestCase): - id2 """ if not run: return - self.testAddSubObject(quiet=1,run=1) + self.test_17_AddSubObject(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Delete Sub Object ') - LOG('Testing... ',0,'testDeleteSubObject') + LOG('Testing... ',0,'test_20_DeleteSubObject') person_server = self.getPersonServer() sub_object_s = person_server._getOb(self.id1)._getOb(self.id1) sub_object_s.manage_delObjects(self.id1) @@ -836,16 +836,16 @@ class TestERP5SyncML(ERP5TypeTestCase): len_c2 = len(sub_object_c2.objectValues()) self.failUnless(len_s==len_c1==len_c2==0) - def testGetConflictListOnSubObject(self, quiet=0, run=run_all_test): + def test_21_GetConflictListOnSubObject(self, quiet=0, run=run_all_test): """ We will change several attributes on a sub object on both the server and a client, then we will see if we have correctly the conflict list """ if not run: return - self.testAddSubObject(quiet=1,run=1) + self.test_17_AddSubObject(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Get Conflict List On Sub Object ') - LOG('Testing... ',0,'testGetConflictListOnSubObject') + LOG('Testing... ',0,'test_21_GetConflictListOnSubObject') person_server = self.getPersonServer() object_s = person_server._getOb(self.id1) sub_object_s = object_s._getOb(self.id1) @@ -869,16 +869,16 @@ class TestERP5SyncML(ERP5TypeTestCase): conflict_list = portal_sync.getConflictList(sub_object_s) self.failUnless(len(conflict_list)==2) - def testApplyPublisherDocumentOnSubObject(self, quiet=0, run=run_all_test): + def test_22_ApplyPublisherDocumentOnSubObject(self, quiet=0, run=run_all_test): """ there's several conflict on a sub object, we will see if we can correctly have the publisher version of this document """ if not run: return - self.testGetConflictListOnSubObject(quiet=1,run=1) + self.test_21_GetConflictListOnSubObject(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Apply Publisher Document On Sub Object ') - LOG('Testing... ',0,'testApplyPublisherDocumentOnSubObject') + LOG('Testing... ',0,'test_22_ApplyPublisherDocumentOnSubObject') portal_sync = self.getSynchronizationTool() conflict_list = portal_sync.getConflictList() conflict = conflict_list[0] @@ -899,16 +899,16 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(sub_object_c2.getDescription()==self.description2) self.failUnless(sub_object_c2.getLanguage()==self.lang2) - def testApplySubscriberDocumentOnSubObject(self, quiet=0, run=run_all_test): + def test_23_ApplySubscriberDocumentOnSubObject(self, quiet=0, run=run_all_test): """ there's several conflict on a sub object, we will see if we can correctly have the subscriber version of this document """ if not run: return - self.testGetConflictListOnSubObject(quiet=1,run=1) + self.test_21_GetConflictListOnSubObject(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Apply Subscriber Document On Sub Object ') - LOG('Testing... ',0,'testApplySubscriberDocumentOnSubObject') + LOG('Testing... ',0,'test_23_ApplySubscriberDocumentOnSubObject') portal_sync = self.getSynchronizationTool() conflict_list = portal_sync.getConflictList() conflict = conflict_list[0] @@ -929,7 +929,7 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(sub_object_c2.getDescription()==self.description3) self.failUnless(sub_object_c2.getLanguage()==self.lang3) - def testSynchronizeWithStrangeGid(self, quiet=0, run=run_all_test): + def test_24_SynchronizeWithStrangeGid(self, quiet=0, run=run_all_test): """ By default, the synchronization process use the id in order to recognize objects (because by default, getGid==getId. Here, we will see @@ -938,7 +938,7 @@ class TestERP5SyncML(ERP5TypeTestCase): if not run: return if not quiet: ZopeTestCase._print('\nTest Synchronize With Strange Gid ') - LOG('Testing... ',0,'testSynchronizeWithStrangeGid') + LOG('Testing... ',0,'test_24_SynchronizeWithStrangeGid') self.login() self.setupPublicationAndSubscriptionAndGid(quiet=1,run=1) nb_person = self.populatePersonServer(quiet=1,run=1) @@ -976,16 +976,16 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(person_s.getDescription()==self.description3) self.failUnless(person_c1.getDescription()==self.description3) - def testMultiNodeConflict(self, quiet=0, run=run_all_test): + def test_25_MultiNodeConflict(self, quiet=0, run=run_all_test): """ We will create conflicts with 3 differents nodes, and we will solve it by taking one full version of documents. """ if not run: return - self.testFirstSynchronization(quiet=1,run=1) + self.test_08_FirstSynchronization(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Multi Node Conflict ') - LOG('Testing... ',0,'testMultiNodeConflict') + LOG('Testing... ',0,'test_25_MultiNodeConflict') portal_sync = self.getSynchronizationTool() person_server = self.getPersonServer() person1_s = person_server._getOb(self.id1) @@ -1041,17 +1041,17 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(person1_c2.getFormat()==self.format4) - def testSynchronizeWorkflowHistory(self, quiet=0, run=run_all_test): + def test_26_SynchronizeWorkflowHistory(self, quiet=0, run=run_all_test): """ We will do a synchronization, then we will edit two times the object on the server, then two times the object on the client, and see if the global history as 4 more actions. """ if not run: return - self.testFirstSynchronization(quiet=1,run=1) + self.test_08_FirstSynchronization(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Synchronize WorkflowHistory ') - LOG('Testing... ',0,'testSynchronizeWorkflowHistory') + LOG('Testing... ',0,'test_26_SynchronizeWorkflowHistory') person_server = self.getPersonServer() person1_s = person_server._getOb(self.id1) person_client1 = self.getPersonClient1() @@ -1068,16 +1068,16 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(len(person1_s.workflow_history[self.workflow_id])==len_wf+4) self.failUnless(len(person1_c.workflow_history[self.workflow_id])==len_wf+4) - def testUpdateLocalRole(self, quiet=0, run=run_all_test): + def test_27_UpdateLocalRole(self, quiet=0, run=run_all_test): """ We will do a first synchronization, then modify, add and delete an user role and see if it is correctly synchronized """ if not run: return - self.testFirstSynchronization(quiet=1,run=1) + self.test_08_FirstSynchronization(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Update Local Role ') - LOG('Testing... ',0,'testUpdateLocalRole') + LOG('Testing... ',0,'test_27_UpdateLocalRole') # First, Create a new user uf = self.getPortal().acl_users uf._doAddUser('jp', '', ['Manager'], []) @@ -1101,17 +1101,17 @@ class TestERP5SyncML(ERP5TypeTestCase): self.assertEqual(role_1_s,role_1_c) self.assertEqual(role_2_s,role_2_c) - def testPartialData(self, quiet=0, run=run_all_test): + def test_28_PartialData(self, quiet=0, run=run_all_test): """ We will do a first synchronization, then we will do a change, then we will modify the SyncCode max_line value so it it will generate many messages """ if not run: return - self.testFirstSynchronization(quiet=1,run=1) + self.test_08_FirstSynchronization(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Partial Data ') - LOG('Testing... ',0,'testPartialData') + LOG('Testing... ',0,'test_28_PartialData') previous_max_lines = SyncCode.MAX_LINES SyncCode.MAX_LINES = 10 self.populatePersonServerWithSubObject(quiet=1,run=1) @@ -1136,7 +1136,7 @@ class TestERP5SyncML(ERP5TypeTestCase): self.assertEquals(sub_sub_person2.getLastName(),self.last_name2) SyncCode.MAX_LINES = previous_max_lines - def testBrokenMessage(self, quiet=0, run=run_all_test): + def test_29_BrokenMessage(self, quiet=0, run=run_all_test): """ With http synchronization, when a message is not well received, then we send message again, we want to @@ -1148,7 +1148,7 @@ class TestERP5SyncML(ERP5TypeTestCase): if not run: return if not quiet: ZopeTestCase._print('\nTest Broken Message ') - LOG('Testing... ',0,'testBrokenMessage') + LOG('Testing... ',0,'test_29_BrokenMessage') previous_max_lines = SyncCode.MAX_LINES SyncCode.MAX_LINES = 10 self.setupPublicationAndSubscription(quiet=1,run=1) @@ -1172,14 +1172,14 @@ class TestERP5SyncML(ERP5TypeTestCase): self.failUnless(person1_c.getLastName()==self.last_name1) SyncCode.MAX_LINES = previous_max_lines - def testGetSynchronizationType(self, quiet=0, run=run_all_test): + def test_30_GetSynchronizationType(self, quiet=0, run=run_all_test): # We will try to update some simple data, first # we change on the server side, the on the client side if not run: return if not quiet: ZopeTestCase._print('\nTest Get Synchronization Type ') - LOG('Testing... ',0,'testGetSynchronizationType') - self.testFirstSynchronization(quiet=1,run=1) + LOG('Testing... ',0,'test_30_GetSynchronizationType') + self.test_08_FirstSynchronization(quiet=1,run=1) # First we do only modification on server # Check for each subsription that the synchronization type # is TWO WAY @@ -1209,16 +1209,16 @@ class TestERP5SyncML(ERP5TypeTestCase): for sub in portal_sync.getSubscriptionList(): self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY) - def testUpdateLocalPermission(self, quiet=0, run=run_all_test): + def test_31_UpdateLocalPermission(self, quiet=0, run=run_all_test): """ We will do a first synchronization, then modify, add and delete an user role and see if it is correctly synchronized """ if not run: return - self.testFirstSynchronization(quiet=1,run=1) + self.test_08_FirstSynchronization(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Update Local Permission ') - LOG('Testing... ',0,'testUpdateLocalPermission') + LOG('Testing... ',0,'test_31_UpdateLocalPermission') # then create roles person_server = self.getPersonServer() person1_s = person_server._getOb(self.id1) @@ -1249,9 +1249,64 @@ class TestERP5SyncML(ERP5TypeTestCase): self.assertEqual(role_1_s,role_1_c) self.assertEqual(role_2_s,role_2_c) - # We may add a test in order to check if the slow_sync mode works fine, ie - # if we do have both object on the client and server side, we must make sure - # that the server first sends is own data + def test_32_AddOneWaySubscription(self, quiet=0, run=1): + if not run: return + if not quiet: + ZopeTestCase._print('\nTest Add One Way Subscription ') + LOG('Testing... ',0,'test_32_AddOneWaySubscription') + portal_id = self.getPortalId() + portal_sync = self.getSynchronizationTool() + portal_sync.manage_addSubscription(self.sub_id1,self.publication_url, + self.subscription_url1,'/%s/person_client1' % portal_id,'objectValues', + '','ERP5Conduit','') + sub = portal_sync.getSubscription(self.sub_id1) + #sub.setOneWaySyncFromServer(1) + self.failUnless(sub is not None) + + def test_33_OneWaySync(self, quiet=0, run=1): + """ + We will test if we can synchronize only from to server to the client. + We want to make sure in this case that all modifications on the client + will not be taken into account. + """ + if not run: return + if not quiet: + ZopeTestCase._print('\nTest One Way Sync ') + LOG('Testing... ',0,'test_33_OneWaySync') + self.test_02_AddPublication(quiet=1,run=1) + self.test_32_AddOneWaySubscription(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) + # First do the sync from the server to the client + nb_message1 = self.synchronize(self.sub_id1) + for sub in portal_sync.getSubscriptionList(): + self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY) + self.assertEquals(nb_message1,self.nb_message_first_synchronization) + subscription1 = portal_sync.getSubscription(self.sub_id1) + self.assertEquals(len(subscription1.getObjectList()),nb_person) + person_server = self.getPersonServer() # We also check we don't + # modify initial ob + person1_s = person_server._getOb(self.id1) + self.failUnless(person1_s.getId()==self.id1) + self.failUnless(person1_s.getFirstName()==self.first_name1) + self.failUnless(person1_s.getLastName()==self.last_name1) + person_client1 = self.getPersonClient1() + person1_c = person_client1._getOb(self.id1) + self.failUnless(person1_c.getId()==self.id1) + self.failUnless(person1_c.getFirstName()==self.first_name1) + self.failUnless(person1_c.getLastName()==self.last_name1) + # Then we change things on both sides and we look if there + # is synchronization from only one way + person1_c.setFirstName(self.first_name2) + person1_s.setLastName(self.last_name2) + nb_message1 = self.synchronize(self.sub_id1) + self.assertEquals(person1_c.getLastName(),self.last_name2) + self.assertEquals(person1_s.getFirstName(),self.first_name1) + + if __name__ == '__main__': framework() -- 2.30.9