Commit 1ef01e23 authored by Sebastien Robin's avatar Sebastien Robin

many updates


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@227 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 3484454e
...@@ -37,6 +37,8 @@ from xml.dom.ext.reader.Sax2 import FromXml ...@@ -37,6 +37,8 @@ from xml.dom.ext.reader.Sax2 import FromXml
from DateTime.DateTime import DateTime from DateTime.DateTime import DateTime
from email.MIMEBase import MIMEBase from email.MIMEBase import MIMEBase
from email import Encoders from email import Encoders
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
import pickle import pickle
import string import string
from xml.dom.ext import PrettyPrint from xml.dom.ext import PrettyPrint
...@@ -84,16 +86,21 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -84,16 +86,21 @@ class ERP5Conduit(XMLSyncUtilsMixin):
""" """
# Declarative security
security = ClassSecurityInfo()
security.declareProtected(Permissions.AccessContentsInformation,'getEncoding')
def getEncoding(self): def getEncoding(self):
""" """
return the string corresponding to the local encoding return the string corresponding to the local encoding
""" """
return "iso-8859-1" return "iso-8859-1"
security.declareProtected(Permissions.ModifyPortalContent, '__init__')
def __init__(self): def __init__(self):
self.args = {} self.args = {}
security.declareProtected(Permissions.ModifyPortalContent, 'addNode')
def addNode(self, xml=None, object=None, previous_xml=None, force=0, **kw): def addNode(self, xml=None, object=None, previous_xml=None, force=0, **kw):
""" """
A node is added A node is added
...@@ -117,11 +124,13 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -117,11 +124,13 @@ class ERP5Conduit(XMLSyncUtilsMixin):
LOG('addNode',0,'object.id: %s' % object.getId()) LOG('addNode',0,'object.id: %s' % object.getId())
LOG('addNode',0,'xml.nodeName: %s' % xml.nodeName) LOG('addNode',0,'xml.nodeName: %s' % xml.nodeName)
LOG('addNode',0,'isSubObjectAdd: %i' % self.getSubObjectDepth(xml)) LOG('addNode',0,'isSubObjectAdd: %i' % self.getSubObjectDepth(xml))
LOG('addNode',0,'isHistoryAdd: %i' % self.isHistoryAdd(xml))
if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and self.getSubObjectDepth(xml)==0: if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and self.getSubObjectDepth(xml)==0:
for element in self.getXupdateElementList(xml): if self.isHistoryAdd(xml)!=-1: # bad hack XXX to be removed
xml = self.getElementFromXupdate(element) for element in self.getXupdateElementList(xml):
conflict_list += self.addNode(xml=xml,object=object, xml = self.getElementFromXupdate(element)
previous_xml=previous_xml, force=force, **kw) conflict_list += self.addNode(xml=xml,object=object,
previous_xml=previous_xml, force=force, **kw)
elif xml.nodeName == 'object': elif xml.nodeName == 'object':
object_id = self.getAttribute(xml,'id') object_id = self.getAttribute(xml,'id')
docid = self.getObjectDocid(xml) docid = self.getObjectDocid(xml)
...@@ -184,25 +193,28 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -184,25 +193,28 @@ class ERP5Conduit(XMLSyncUtilsMixin):
conflict_list += self.addNode(xml=sub_xml,object=sub_object, conflict_list += self.addNode(xml=sub_xml,object=sub_object,
previous_xml=sub_previous_xml, force=force) previous_xml=sub_previous_xml, force=force)
elif xml.nodeName == self.history_tag or self.isHistoryAdd(xml)>0: elif xml.nodeName == self.history_tag or self.isHistoryAdd(xml)>0:
#return conflict_list # XXX to be removed soon
# We want to add a workflow action # We want to add a workflow action
wf_tool = getToolByName(object,'portal_workflow') wf_tool = getToolByName(object,'portal_workflow')
wf_id = self.getAttribute(xml,'id') wf_id = self.getAttribute(xml,'id')
if wf_id is None: # History added by xupdate if wf_id is None: # History added by xupdate
wf_id = self.getHistoryIdFromSelect(xml) wf_id = self.getHistoryIdFromSelect(xml)
xml = self.getElementNodeList(xml)[0] xml = self.getElementNodeList(xml)[0]
# xml = self.getElementFromXupdate(xml) # This doesn't works, because with subnodes,
# not only text as before
LOG('addNode, workflow_history id:',0,wf_id) LOG('addNode, workflow_history id:',0,wf_id)
LOG('addNode, workflow_history xml:',0,xml) LOG('addNode, workflow_history xml:',0,xml)
for action in self.getWorkflowActionFromXml(xml): #for action in self.getWorkflowActionFromXml(xml):
status = self.getStatusFromXml(action) status = self.getStatusFromXml(xml)
LOG('addNode, status:',0,status) LOG('addNode, status:',0,status)
wf_conflict_list = self.isWorkflowActionAddable(object=object,
status=status,wf_tool=wf_tool,
xml=xml)
LOG('addNode, workflow_history wf_conflict_list:',0,wf_conflict_list)
if wf_conflict_list==[] or force:
LOG('addNode, setting status:',0,'ok')
wf_tool.setStatusOf(wf_id,object,status) wf_tool.setStatusOf(wf_id,object,status)
else:
conflict_list += wf_conflict_list
elif xml.nodeName in self.local_role_list: elif xml.nodeName in self.local_role_list:
#return conflict_list # XXX to be removed soon
# We want to add a local role # We want to add a local role
#user = self.getParameter(xml,'user')
roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens') roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens')
roles = list(roles) # Needed for CPS, or we have a CPS error roles = list(roles) # Needed for CPS, or we have a CPS error
user = roles[0] user = roles[0]
...@@ -212,6 +224,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -212,6 +224,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
conflict_list += self.updateNode(xml=xml,object=object, force=force, **kw) conflict_list += self.updateNode(xml=xml,object=object, force=force, **kw)
return conflict_list return conflict_list
security.declareProtected(Permissions.ModifyPortalContent, 'deleteNode')
def deleteNode(self, xml=None, object=None, object_id=None, force=None, **kw): def deleteNode(self, xml=None, object=None, object_id=None, force=None, **kw):
""" """
A node is deleted A node is deleted
...@@ -246,6 +259,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -246,6 +259,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
pass pass
return conflict_list return conflict_list
security.declareProtected(Permissions.ModifyPortalContent, 'updateNode')
def updateNode(self, xml=None, object=None, previous_xml=None, force=0, **kw): def updateNode(self, xml=None, object=None, previous_xml=None, force=0, **kw):
""" """
A node is updated with some xupdate A node is updated with some xupdate
...@@ -257,6 +271,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -257,6 +271,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
conflict_list = [] conflict_list = []
xml = self.convertToXml(xml) xml = self.convertToXml(xml)
LOG('updateNode',0,'xml.nodeName: %s' % xml.nodeName) LOG('updateNode',0,'xml.nodeName: %s' % xml.nodeName)
LOG('updateNode, force: ',0,force)
# we have an xupdate xml # we have an xupdate xml
if xml.nodeName == 'xupdate:modifications': if xml.nodeName == 'xupdate:modifications':
#xupdate_utils = XupdateUtils() #xupdate_utils = XupdateUtils()
...@@ -310,7 +325,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -310,7 +325,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# - old_data : the data from this box but at the time of the last synchronization # - old_data : the data from this box but at the time of the last synchronization
# - current_data : the data actually on this box # - current_data : the data actually on this box
isConflict = 0 isConflict = 0
if previous_xml is not None: # if no previous_xml, no conflict if (previous_xml is not None) and (not force): # if no previous_xml, no conflict
old_data = self.getObjectProperty(keyword,previous_xml,data_type=data_type) old_data = self.getObjectProperty(keyword,previous_xml,data_type=data_type)
current_data = object.getProperty(keyword) current_data = object.getProperty(keyword)
LOG('updateNode',0,'Conflict data: %s' % str(data)) LOG('updateNode',0,'Conflict data: %s' % str(data))
...@@ -326,8 +341,12 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -326,8 +341,12 @@ class ERP5Conduit(XMLSyncUtilsMixin):
isConflict = 1 isConflict = 1
string_io = StringIO() string_io = StringIO()
PrettyPrint(xml,stream=string_io) PrettyPrint(xml,stream=string_io)
conflict = Conflict(object_path=object.getPhysicalPath()) conflict = Conflict(object_path=object.getPhysicalPath(),
keyword=keyword)
conflict.setXupdate(string_io.getvalue()) conflict.setXupdate(string_io.getvalue())
if not (data_type in self.binary_type_list):
conflict.setLocalValue(current_data)
conflict.setRemoteValue(data)
conflict_list += [conflict] conflict_list += [conflict]
#conflict_list += [Conflict(object_path=object.getPhysicalPath(), #conflict_list += [Conflict(object_path=object.getPhysicalPath(),
# keyword=keyword, # keyword=keyword,
...@@ -381,6 +400,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -381,6 +400,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
previous_xml=sub_previous_xml) previous_xml=sub_previous_xml)
return conflict_list return conflict_list
security.declareProtected(Permissions.AccessContentsInformation,'getFormatedArgs')
def getFormatedArgs(self, args=None): def getFormatedArgs(self, args=None):
""" """
This lookd inside the args dictionnary and then This lookd inside the args dictionnary and then
...@@ -412,6 +432,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -412,6 +432,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
new_args[keyword] = data new_args[keyword] = data
return new_args return new_args
security.declareProtected(Permissions.AccessContentsInformation,'isProperty')
def isProperty(self, xml): def isProperty(self, xml):
""" """
Check if it is a simple property Check if it is a simple property
...@@ -425,6 +446,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -425,6 +446,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return 0 return 0
return 1 return 1
security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectXupdate')
def getSubObjectXupdate(self, xml): def getSubObjectXupdate(self, xml):
""" """
This will change the xml in order to change the update This will change the xml in order to change the update
...@@ -436,6 +458,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -436,6 +458,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
subnode.nodeValue = self.getSubObjectSelect(subnode.nodeValue) subnode.nodeValue = self.getSubObjectSelect(subnode.nodeValue)
return xml return xml
security.declareProtected(Permissions.AccessContentsInformation,'isHistoryAdd')
def isHistoryAdd(self, xml): def isHistoryAdd(self, xml):
bad_list = (self.history_exp) bad_list = (self.history_exp)
for subnode in self.getAttributeNodeList(xml): for subnode in self.getAttributeNodeList(xml):
...@@ -443,9 +466,13 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -443,9 +466,13 @@ class ERP5Conduit(XMLSyncUtilsMixin):
value = subnode.nodeValue value = subnode.nodeValue
for bad_string in bad_list: for bad_string in bad_list:
if re.search(bad_string,value) is not None: if re.search(bad_string,value) is not None:
return 1 if re.search(self.bad_history_exp,value) is None:
return 1
else:
return -1
return 0 return 0
security.declareProtected(Permissions.AccessContentsInformation,'isSubObjectModification')
def isSubObjectModification(self, xml): def isSubObjectModification(self, xml):
""" """
Check if it is a modification from an subobject Check if it is a modification from an subobject
...@@ -460,6 +487,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -460,6 +487,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return 1 return 1
return 0 return 0
security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectDepth')
def getSubObjectDepth(self, xml): def getSubObjectDepth(self, xml):
""" """
Give the Depth of a subobject modification Give the Depth of a subobject modification
...@@ -490,6 +518,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -490,6 +518,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return (1 - i) return (1 - i)
return 0 return 0
security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectSelect')
def getSubObjectSelect(self, select): def getSubObjectSelect(self, select):
""" """
Return a string wich is the selection for the subobject Return a string wich is the selection for the subobject
...@@ -505,6 +534,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -505,6 +534,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
select = new_value select = new_value
return select return select
security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectId')
def getSubObjectId(self, xml): def getSubObjectId(self, xml):
""" """
Return the id of the subobject in an xupdate modification Return the id of the subobject in an xupdate modification
...@@ -520,6 +550,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -520,6 +550,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return object_id return object_id
return object_id return object_id
security.declareProtected(Permissions.AccessContentsInformation,'getHistoryIdFromSelect')
def getHistoryIdFromSelect(self, xml): def getHistoryIdFromSelect(self, xml):
""" """
Return the id of the subobject in an xupdate modification Return the id of the subobject in an xupdate modification
...@@ -536,6 +567,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -536,6 +567,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return object_id return object_id
return object_id return object_id
security.declareProtected(Permissions.AccessContentsInformation,'getSubObjectXml')
def getSubObjectXml(self, object_id, xml): def getSubObjectXml(self, object_id, xml):
""" """
Return the xml of the subobject which as the id object_id Return the xml of the subobject which as the id object_id
...@@ -559,6 +591,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -559,6 +591,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# return self.convertXmlValue(data) # return self.convertXmlValue(data)
# return None # return None
security.declareProtected(Permissions.AccessContentsInformation,'getAttribute')
def getAttribute(self, xml, param): def getAttribute(self, xml, param):
""" """
Retrieve the given parameter from the xml Retrieve the given parameter from the xml
...@@ -569,6 +602,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -569,6 +602,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return self.convertXmlValue(data,data_type='string') return self.convertXmlValue(data,data_type='string')
return None return None
security.declareProtected(Permissions.AccessContentsInformation,'getObjectDocid')
def getObjectDocid(self, xml): def getObjectDocid(self, xml):
""" """
Retrieve the docid Retrieve the docid
...@@ -579,6 +613,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -579,6 +613,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return self.convertXmlValue(data) return self.convertXmlValue(data)
return None return None
security.declareProtected(Permissions.AccessContentsInformation,'getObjectProperty')
def getObjectProperty(self, property, xml, data_type=None): def getObjectProperty(self, property, xml, data_type=None):
""" """
Retrieve the given property Retrieve the given property
...@@ -598,6 +633,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -598,6 +633,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return data return data
return None 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 a string, convert it to a node
...@@ -610,6 +646,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -610,6 +646,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xml = self.getElementNodeList(xml)[0] xml = self.getElementNodeList(xml)[0]
return xml return xml
security.declareProtected(Permissions.AccessContentsInformation,'getObjectType')
def getObjectType(self, xml): def getObjectType(self, xml):
""" """
Retrieve the portal type from an xml Retrieve the portal type from an xml
...@@ -622,6 +659,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -622,6 +659,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return portal_type return portal_type
return portal_type return portal_type
security.declareProtected(Permissions.AccessContentsInformation,'getPropertyType')
def getPropertyType(self, xml): def getPropertyType(self, xml):
""" """
Retrieve the portal type from an xml Retrieve the portal type from an xml
...@@ -634,6 +672,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -634,6 +672,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return p_type return p_type
return p_type return p_type
security.declareProtected(Permissions.AccessContentsInformation,'getXupdateObjectType')
def getXupdateObjectType(self, xml): def getXupdateObjectType(self, xml):
""" """
Retrieve the portal type from an xupdate Retrieve the portal type from an xupdate
...@@ -653,6 +692,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -653,6 +692,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return None return None
security.declareProtected(Permissions.ModifyPortalContent, 'newObject')
def newObject(self, object=None, xml=None): def newObject(self, object=None, xml=None):
""" """
modify the object with datas from modify the object with datas from
...@@ -662,6 +702,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -662,6 +702,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# Retrieve the list of users with a role and delete default roles # Retrieve the list of users with a role and delete default roles
user_role_list = map(lambda x:x[0],object.get_local_roles()) user_role_list = map(lambda x:x[0],object.get_local_roles())
object.manage_delLocalRoles(user_role_list) object.manage_delLocalRoles(user_role_list)
if hasattr(object,'workflow_history'):
object.workflow_history = {}
if xml.nodeName.find('xupdate')>= 0: if xml.nodeName.find('xupdate')>= 0:
xml = self.getElementNodeList(xml)[0] xml = self.getElementNodeList(xml)[0]
for subnode in self.getElementNodeList(xml): for subnode in self.getElementNodeList(xml):
...@@ -680,7 +722,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -680,7 +722,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if args.has_key(keyword): if args.has_key(keyword):
args[keyword] = self.convertXmlValue(args[keyword],keyword_type) args[keyword] = self.convertXmlValue(args[keyword],keyword_type)
elif subnode.nodeName in self.ADDABLE_PROPERTY: elif subnode.nodeName in self.ADDABLE_PROPERTY:
self.addNode(object=object,xml=subnode) self.addNode(object=object,xml=subnode, force=1)
# We should first edit the object # We should first edit the object
args = self.getFormatedArgs(args=args) args = self.getFormatedArgs(args=args)
LOG('newObject',0,"object.getpath: %s" % str(object.getPath())) LOG('newObject',0,"object.getpath: %s" % str(object.getPath()))
...@@ -691,9 +733,10 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -691,9 +733,10 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# Then we may create subobject # Then we may create subobject
for subnode in self.getElementNodeList(xml): for subnode in self.getElementNodeList(xml):
if subnode.nodeName in (self.xml_object_tag,self.history_tag): if subnode.nodeName in (self.xml_object_tag,): #,self.history_tag):
self.addNode(object=object,xml=subnode) self.addNode(object=object,xml=subnode)
security.declareProtected(Permissions.AccessContentsInformation,'getStatusFromXml')
def getStatusFromXml(self, xml): def getStatusFromXml(self, xml):
""" """
Return a worklow status from xml Return a worklow status from xml
...@@ -705,6 +748,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -705,6 +748,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
status[keyword] = value status[keyword] = value
return status return status
security.declareProtected(Permissions.AccessContentsInformation,'getXupdateElementList')
def getXupdateElementList(self, xml): def getXupdateElementList(self, xml):
""" """
Retrieve the list of xupdate:element subnodes Retrieve the list of xupdate:element subnodes
...@@ -716,6 +760,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -716,6 +760,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
LOG('getXupdateElementList, e_list:%',0,e_list) LOG('getXupdateElementList, e_list:%',0,e_list)
return e_list return e_list
security.declareProtected(Permissions.AccessContentsInformation,'getElementFromXupdate')
def getElementFromXupdate(self, xml): def getElementFromXupdate(self, xml):
""" """
from a xupdate:element returns the element as xml from a xupdate:element returns the element as xml
...@@ -740,6 +785,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -740,6 +785,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return self.convertToXml(result) return self.convertToXml(result)
return xml return xml
security.declareProtected(Permissions.AccessContentsInformation,'getWorkflowActionFromXml')
def getWorkflowActionFromXml(self, xml): def getWorkflowActionFromXml(self, xml):
""" """
Return the list of workflow actions Return the list of workflow actions
...@@ -753,6 +799,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -753,6 +799,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
action_list += [subnode] action_list += [subnode]
return action_list return action_list
security.declareProtected(Permissions.AccessContentsInformation,'convertXmlValue')
def convertXmlValue(self, data, data_type=None): def convertXmlValue(self, data, data_type=None):
""" """
It is possible that the xml change the value, for example It is possible that the xml change the value, for example
...@@ -798,6 +845,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -798,6 +845,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# XXX is it the right place ? It should be in XupdateUtils, but here we # XXX is it the right place ? It should be in XupdateUtils, but here we
# have some specific things to do # have some specific things to do
security.declareProtected(Permissions.ModifyPortalContent, 'applyXupdate')
def applyXupdate(self, object=None, xupdate=None, conduit=None, force=0, **kw): def applyXupdate(self, object=None, xupdate=None, conduit=None, force=0, **kw):
""" """
Parse the xupdate and then it will call the conduit Parse the xupdate and then it will call the conduit
...@@ -823,3 +871,25 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -823,3 +871,25 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return conflict_list return conflict_list
def isWorkflowActionAddable(self, object=None,status=None,wf_tool=None,xml=None):
"""
Some checking in order to check if we should add the workfow or not
"""
conflict_list = []
action_name = status['action']
authorized = 0
authorized_actions = wf_tool.getActionsFor(object)
LOG('isWorkflowActionAddable, status:',0,status)
LOG('isWorkflowActionAddable, authorized_actions:',0,authorized_actions)
for action in authorized_actions:
if action['id']==action_name:
authorized = 1
if not authorized:
string_io = StringIO()
PrettyPrint(xml,stream=string_io)
conflict = Conflict(object_path=object.getPhysicalPath(),
keyword=self.history_tag)
conflict.setXupdate(string_io.getvalue())
conflict.setRemoteValue(status)
conflict_list += [conflict]
return conflict_list
...@@ -29,11 +29,13 @@ ...@@ -29,11 +29,13 @@
from Globals import PersistentMapping from Globals import PersistentMapping
from time import gmtime,strftime # for anchors from time import gmtime,strftime # for anchors
from SyncCode import SyncCode from SyncCode import SyncCode
from Products.CMFCore.utils import getToolByName
from Acquisition import Implicit, aq_base
from zLOG import LOG from zLOG import LOG
import md5 import md5
class Conflict(SyncCode): class Conflict(SyncCode, Implicit):
""" """
object_path : the path of the obect object_path : the path of the obect
keyword : an identifier of the conflict keyword : an identifier of the conflict
...@@ -118,6 +120,21 @@ class Conflict(SyncCode): ...@@ -118,6 +120,21 @@ class Conflict(SyncCode):
except TypeError: # It happens when we try to store StringIO except TypeError: # It happens when we try to store StringIO
self.remote_value = None self.remote_value = None
def applyLocalValue(self):
"""
after a conflict resolution, we have decided
to keep the local version of this object
"""
p_sync = getToolByName(self,'portal_synchronizations')
p_sync.applyLocalValue(self)
def applyRemoteValue(self):
"""
get the domain
"""
p_sync = getToolByName(self,'portal_synchronizations')
p_sync.applyRemoteValue(self)
def setDomain(self, domain): def setDomain(self, domain):
""" """
set the domain set the domain
...@@ -148,18 +165,6 @@ class Conflict(SyncCode): ...@@ -148,18 +165,6 @@ class Conflict(SyncCode):
""" """
self.domain_id = domain_id self.domain_id = domain_id
def applyRemoteValue():
"""
We will take the remote value for this conflict
"""
pass
def applyLocalValue():
"""
We will take the local value for this conflict
"""
pass
class Signature(SyncCode): class Signature(SyncCode):
""" """
status -- SENT, CONFLICT... status -- SENT, CONFLICT...
...@@ -179,7 +184,6 @@ class Signature(SyncCode): ...@@ -179,7 +184,6 @@ class Signature(SyncCode):
self.partial_xml = None self.partial_xml = None
self.action = None self.action = None
self.setTempXML(None) self.setTempXML(None)
self.setTempXML(None)
self.resetConflictList() self.resetConflictList()
self.md5_string = None self.md5_string = None
self.force = 0 self.force = 0
...@@ -351,23 +355,39 @@ class Signature(SyncCode): ...@@ -351,23 +355,39 @@ class Signature(SyncCode):
Return the actual action for a partial synchronization Return the actual action for a partial synchronization
""" """
LOG('setConflictList, list',0,conflict_list) LOG('setConflictList, list',0,conflict_list)
if conflict_list is None: if conflict_list is None or conflict_list==[]:
self.resetConflictList() self.resetConflictList()
else: else:
new_conflict_list = [] #new_conflict_list = []
# If two conflicts are on the same objects, then # If two conflicts are on the same objects, then
# we join them, so we have a conflict with many xupdate # we join them, so we have a conflict with many xupdate
for conflict in conflict_list: # for conflict in conflict_list:
found = None # found = None
for n_conflict in new_conflict_list: # for n_conflict in new_conflict_list:
if n_conflict.getObjectPath() == conflict.getObjectPath(): # if n_conflict.getObjectPath() == conflict.getObjectPath():
found = n_conflict # found = n_conflict
LOG('setConflictList, found',0,found) # LOG('setConflictList, found',0,found)
if found == None: # if found == None:
new_conflict_list += [conflict] # new_conflict_list += [conflict]
else: # else:
n_conflict.setXupdate(conflict.getXupdateList()) # n_conflict.setXupdate(conflict.getXupdateList())
self.conflict_list = new_conflict_list #self.conflict_list = new_conflict_list
self.conflict_list = conflict_list
def delConflict(self, conflict):
"""
Return the actual action for a partial synchronization
"""
LOG('delConflict, conflict',0,conflict)
conflict_list = []
for c in self.getConflictList():
LOG('delConflict, c==conflict',0,c==aq_base(conflict))
if c != aq_base(conflict):
conflict_list += [c]
if conflict_list != []:
self.setConflictList(conflict_list)
else:
self.resetConflictList()
class Subscription(SyncCode): class Subscription(SyncCode):
""" """
......
...@@ -70,7 +70,8 @@ class SyncCode(Persistent): ...@@ -70,7 +70,8 @@ class SyncCode(Persistent):
#ENCODING='iso-8859-1' #ENCODING='iso-8859-1'
NOT_EDITABLE_PROPERTY = ('id','object','uid','xupdate:element','workflow_history', action_tag = 'workflow_action'
NOT_EDITABLE_PROPERTY = ('id','object','uid','xupdate:element',action_tag,
'xupdate:attribute','local_role') 'xupdate:attribute','local_role')
XUPDATE_INSERT = ('xupdate:insert-after','xupdate:insert-before') XUPDATE_INSERT = ('xupdate:insert-after','xupdate:insert-before')
XUPDATE_ADD = ('xupdate:append',) XUPDATE_ADD = ('xupdate:append',)
...@@ -87,12 +88,13 @@ class SyncCode(Persistent): ...@@ -87,12 +88,13 @@ class SyncCode(Persistent):
dict_type_list = ('dict',) dict_type_list = ('dict',)
pickle_type_list = ('pickle',) pickle_type_list = ('pickle',)
xml_object_tag = 'object' xml_object_tag = 'object'
history_tag = 'workflow_history' #history_tag = 'workflow_history'
history_tag = 'workflow_action'
local_role_tag = 'local_role' local_role_tag = 'local_role'
local_role_list = (local_role_tag,'/'+local_role_tag) local_role_list = (local_role_tag,'/'+local_role_tag)
action_tag = 'workflow_action'
ADDABLE_PROPERTY = (local_role_tag,history_tag) ADDABLE_PROPERTY = (local_role_tag,history_tag)
sub_object_exp = "/object\[@id='.*'\]/object\[@id='.*'\]" sub_object_exp = "/object\[@id='.*'\]/object\[@id='.*'\]"
object_exp = "/object\[@id='.*'\]" object_exp = "/object\[@id='.*'\]"
sub_sub_object_exp = "/object\[@id='.*'\]/object\[@id='.*'\]/object\[@id='.*'\]" sub_sub_object_exp = "/object\[@id='.*'\]/object\[@id='.*'\]/object\[@id='.*'\]"
history_exp = "/object\[@id='.*'\]/%s\[@id='.*'\]" % history_tag history_exp = "/%s\[@id='.*'\]" % history_tag
bad_history_exp = "/%s\[@id='.*'\]/" % history_tag
...@@ -101,7 +101,7 @@ Details ...@@ -101,7 +101,7 @@ Details
and the realm of attributes we synchronise (ie. the mapping) and the realm of attributes we synchronise (ie. the mapping)
2 The slave sends the "initialisation package" (SyncML specification), in this package, 2 The slave sends the "initialisation package" (SyncML specification), in this package,
appears several elements : appears several elements :
- the SynchHdr element with several informations about the protocol used. We will - the SynchHdr element with several informations about the protocol used. We will
...@@ -128,7 +128,7 @@ Details ...@@ -128,7 +128,7 @@ Details
4 The master sends the "Initialisation package" to the client with : 4 The master sends the "Initialisation package" to the client with :
- the SynchHdr element with several informations about the protocol used, - the SynchHdr element with several informations about the protocol used,
and also authentification informations. and also authentification informations.
- the Status element, in order to respond to the alert command sent by the client. - the Status element, in order to respond to the alert command sent by the client.
...@@ -141,12 +141,8 @@ Details ...@@ -141,12 +141,8 @@ Details
for us it will be 201 wich specifies a client-initiated, two-way slow-synchronisation. for us it will be 201 wich specifies a client-initiated, two-way slow-synchronisation.
This just means that both the server and the client sends all their data. Each This just means that both the server and the client sends all their data. Each
data must specify the DTD used. data must specify the DTD used.
//4 The master updates is database with the objects from the client. Then
//the master generate an xml file of differences between the two databases (tricky if
//at the beginning the client don't have any data), differences only all objects
//corresponding to the query.
At this step, we may eventually record the subscriber in the publication At this step, we may eventually record the subscriber in the publication
of the master database for... (OPTION). This is like of the master database for... (OPTION). This is like
the subscribtion is becoming member of mail list to be informed of the subscribtion is becoming member of mail list to be informed of
...@@ -190,7 +186,7 @@ Details ...@@ -190,7 +186,7 @@ Details
1 update the slave S(tn) <- S(tn) + DM(tn) + C(tn) 1 update the slave S(tn) <- S(tn) + DM(tn) + C(tn)
Detail implementation Detail implementation
blabla blabla
...@@ -199,5 +195,5 @@ References ...@@ -199,5 +195,5 @@ References
XMLDiff - http://www.garshol.priv.no/download/xmltools/prod/xmldiff.html XMLDiff - http://www.garshol.priv.no/download/xmltools/prod/xmldiff.html
SyncML - http://www.syncml.org SyncML - http://www.syncml.org
ZSyncer - http://www.zope.org/Members/andym/ZSyncer ZSyncer - http://www.zope.org/Members/andym/ZSyncer
...@@ -39,6 +39,7 @@ from Publication import Publication,Subscriber ...@@ -39,6 +39,7 @@ from Publication import Publication,Subscriber
from Subscription import Subscription,Signature from Subscription import Subscription,Signature
from xml.dom.ext.reader.Sax2 import FromXmlStream, FromXml from xml.dom.ext.reader.Sax2 import FromXmlStream, FromXml
from XMLSyncUtils import * from XMLSyncUtils import *
from Products.ERP5Type import Permissions
from PublicationSynchronization import PublicationSynchronization from PublicationSynchronization import PublicationSynchronization
from SubscriptionSynchronization import SubscriptionSynchronization from SubscriptionSynchronization import SubscriptionSynchronization
#import sys #import sys
...@@ -140,6 +141,7 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -140,6 +141,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
+ '?manage_tabs_message=Tool+updated.' + '?manage_tabs_message=Tool+updated.'
) )
security.declareProtected(Permissions.ModifyPortalContent, 'addPublications')
def addPublications(self, id, publication_url, destination_path, def addPublications(self, id, publication_url, destination_path,
query, xml_mapping, RESPONSE=None): query, xml_mapping, RESPONSE=None):
""" """
...@@ -153,9 +155,11 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -153,9 +155,11 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('managePublications') RESPONSE.redirect('managePublications')
security.declareProtected(Permissions.ModifyPortalContent, 'addSubscriptions')
def addSubscriptions(self, id, publication_url, subscription_url, def addSubscriptions(self, id, publication_url, subscription_url,
destination_path, query, xml_mapping, RESPONSE=None): destination_path, query, xml_mapping, RESPONSE=None):
""" """
XXX should be renamed as addSubscription
create a new subscription create a new subscription
""" """
sub = Subscription(id, publication_url, subscription_url, sub = Subscription(id, publication_url, subscription_url,
...@@ -166,6 +170,7 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -166,6 +170,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions') RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.ModifyPortalContent, 'editPublications')
def editPublications(self, id, publication_url, destination_path, def editPublications(self, id, publication_url, destination_path,
query, xml_mapping, RESPONSE=None): query, xml_mapping, RESPONSE=None):
""" """
...@@ -177,6 +182,7 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -177,6 +182,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('managePublications') RESPONSE.redirect('managePublications')
security.declareProtected(Permissions.ModifyPortalContent, 'editSubscriptions')
def editSubscriptions(self, id, publication_url, subscription_url, def editSubscriptions(self, id, publication_url, subscription_url,
destination_path, query, xml_mapping, RESPONSE=None): destination_path, query, xml_mapping, RESPONSE=None):
""" """
...@@ -188,6 +194,7 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -188,6 +194,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions') RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.ModifyPortalContent, 'deletePublications')
def deletePublications(self, id, RESPONSE=None): def deletePublications(self, id, RESPONSE=None):
""" """
delete a publication delete a publication
...@@ -196,6 +203,7 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -196,6 +203,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('managePublications') RESPONSE.redirect('managePublications')
security.declareProtected(Permissions.ModifyPortalContent, 'deleteSubscriptions')
def deleteSubscriptions(self, id, RESPONSE=None): def deleteSubscriptions(self, id, RESPONSE=None):
""" """
delete a subscription delete a subscription
...@@ -204,6 +212,7 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -204,6 +212,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions') RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.ModifyPortalContent, 'ResetPublications')
def ResetPublications(self, id, RESPONSE=None): def ResetPublications(self, id, RESPONSE=None):
""" """
reset a publication reset a publication
...@@ -212,15 +221,18 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -212,15 +221,18 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('managePublications') RESPONSE.redirect('managePublications')
security.declareProtected(Permissions.ModifyPortalContent, 'ResetSubscriptions')
def ResetSubscriptions(self, id, RESPONSE=None): def ResetSubscriptions(self, id, RESPONSE=None):
""" """
reset a subscription reset a subscription
XXX R -> r
""" """
self.list_subscriptions[id].resetAllSignatures() self.list_subscriptions[id].resetAllSignatures()
self.list_subscriptions[id].resetAnchors() self.list_subscriptions[id].resetAnchors()
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions') RESPONSE.redirect('manageSubscriptions')
security.declareProtected(Permissions.AccessContentsInformation,'getPublicationList')
def getPublicationList(self): def getPublicationList(self):
""" """
Return a list of publications Return a list of publications
...@@ -233,6 +245,7 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -233,6 +245,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
return_list += [self.list_publications[key]] return_list += [self.list_publications[key]]
return return_list return return_list
security.declareProtected(Permissions.AccessContentsInformation,'getSubscriptionList')
def getSubscriptionList(self): def getSubscriptionList(self):
""" """
Return a list of publications Return a list of publications
...@@ -245,12 +258,17 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -245,12 +258,17 @@ class SynchronizationTool( UniqueObject, SimpleItem,
return_list += [self.list_subscriptions[key]] return_list += [self.list_subscriptions[key]]
return return_list return return_list
security.declareProtected(Permissions.AccessContentsInformation,'getDomainList')
def getDomainList(self): def getDomainList(self):
""" """
Returns the list of subscriptions and publications Returns the list of subscriptions and publications
getSynchronizationList ? (mon choix)
getSubscriptionOrPublicationList ?
""" """
return self.getSubscriptionList() + self.getPublicationList() return self.getSubscriptionList() + self.getPublicationList()
security.declareProtected(Permissions.AccessContentsInformation,'getConflictList')
def getConflictList(self, path=None): def getConflictList(self, path=None):
""" """
Retrieve the list of all conflicts Retrieve the list of all conflicts
...@@ -260,27 +278,29 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -260,27 +278,29 @@ class SynchronizationTool( UniqueObject, SimpleItem,
""" """
conflict_list = [] conflict_list = []
for publication in self.getPublicationList(): for publication in self.getPublicationList():
pub_conflict_list = publication.getConflictList() for subscriber in publication.getSubscriberList():
for conflict in pub_conflict_list: sub_conflict_list = subscriber.getConflictList()
#conflict.setDomain('Publication') for conflict in sub_conflict_list:
conflict.setDomain(publication) #conflict.setDomain('Publication')
conflict.setDomainId(publication.getId()) conflict.setDomain(subscriber)
conflict_list += [conflict] #conflict.setDomainId(subscriber.getId())
conflict_list += [conflict.__of__(self)]
for subscription in self.getSubscriptionList(): for subscription in self.getSubscriptionList():
sub_conflict_list = subscription.getConflictList() sub_conflict_list = subscription.getConflictList()
for conflict in sub_conflict_list: for conflict in sub_conflict_list:
#conflict.setDomain('Subscription') #conflict.setDomain('Subscription')
conflict.setDomain(subscription) conflict.setDomain(subscription)
conflict.setDomainId(subscription.getId()) #conflict.setDomainId(subscription.getId())
conflict_list += [conflict] conflict_list += [conflict.__of__(self)]
if path is not None: # Retrieve only conflicts for a given path if path is not None: # Retrieve only conflicts for a given path
new_list = [] new_list = []
for conflict in conflict_list: for conflict in conflict_list:
if conflict.getObjectPath() == path: if conflict.getObjectPath() == path:
new_list += [conflict] new_list += [conflict.__of__(self)]
return new_list return new_list
return conflict_list return conflict_list
security.declareProtected(Permissions.AccessContentsInformation,'getSynchronizationState')
def getSynchronizationState(self, path): def getSynchronizationState(self, path):
""" """
context : the context on which we are looking for state context : the context on which we are looking for state
...@@ -290,6 +310,11 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -290,6 +310,11 @@ class SynchronizationTool( UniqueObject, SimpleItem,
then we have to check on a publication/subscription. then we have to check on a publication/subscription.
This method returns a mapping between subscription and states This method returns a mapping between subscription and states
JPS suggestion:
path -> object, document, context, etc.
type -> '/titi/toto' or ('','titi', 'toto') or <Base instance 1562567>
object = self.resolveContext(context) (method to add)
""" """
conflict_list = self.getConflictList() conflict_list = self.getConflictList()
state_list= [] state_list= []
...@@ -324,71 +349,95 @@ class SynchronizationTool( UniqueObject, SimpleItem, ...@@ -324,71 +349,95 @@ class SynchronizationTool( UniqueObject, SimpleItem,
state_list += [[subscriber,state]] state_list += [[subscriber,state]]
return state_list return state_list
def manageLocalValue(self, domain, domain_id, object_path, RESPONSE=None): security.declareProtected(Permissions.ModifyPortalContent, 'applyLocalValue')
def applyLocalValue(self, conflict):
"""
after a conflict resolution, we have decided
to keep the local version of an object
XXXC Local ? Remote ?
applyPublisherValue ? (JPS 1)
applySubscriberValue ?
applyPublicationValue ? (JPS 2)
applySubscriptionValue ?
applyPublishedValue ? (JPS 3)
applySubscribedValue ?
"""
object = self.unrestrictedTraverse(conflict.getObjectPath())
subscriber = conflict.getDomain()
# get the signature:
LOG('p_sync.setLocalObject, subscriber: ',0,subscriber)
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
signature.delConflict(conflict)
if signature.getConflictList() == []:
signature.setStatus(self.PUB_CONFLICT_MERGE)
security.declareProtected(Permissions.ModifyPortalContent, 'applyRemoteValue')
def applyRemoteValue(self, conflict):
"""
after a conflict resolution, we have decided
to keep the local version of an object
"""
object = self.unrestrictedTraverse(conflict.getObjectPath())
subscriber = conflict.getDomain()
# get the signature:
LOG('p_sync.setRemoteObject, subscriber: ',0,subscriber)
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
conduit = ERP5Conduit()
for xupdate in conflict.getXupdateList():
conduit.updateNode(xml=xupdate,object=object,force=1)
signature.delConflict(conflict)
if signature.getConflictList() == []:
signature.setStatus(self.PUB_CONFLICT_MERGE)
security.declareProtected(Permissions.ModifyPortalContent, 'manageLocalValue')
def manageLocalValue(self, subscription_url, keyword, object_path, RESPONSE=None):
""" """
Do whatever needed in order to store the local value on Do whatever needed in order to store the local value on
the remote server the remote server
Suggestion:
manage_applyLocalValue XXX
Suggestion:
add global apply (not conflict per conflict) XXX
Suggestion (API)
add method to view document with applied xupdate
of a given subscriber XX (ex. viewSubscriberDocument?path=ddd&subscriber_id=dddd)
Version=Version CPS
""" """
# Retrieve the conflict object # Retrieve the conflict object
conflict=None LOG('manageLocalValue',0,'%s %s %s' % (str(subscription_url),
if type(object_path) is type(''): str(keyword),
object_path = tuple(object_path.split('/')) str(object_path)))
for item in self.getConflictList(): for conflict in self.getConflictList():
if item.getDomain() == domain and item.getDomainId()==domain_id \ LOG('manageLocalValue, conflict:',0,conflict)
and item.getObjectPath()==object_path: if conflict.getKeyword() == keyword:
conflict=item LOG('manageLocalValue',0,'found the keyword')
break if '/'.join(conflict.getObjectPath())==object_path:
publication = subscriber = None if conflict.getDomain().getSubscriptionUrl()==subscription_url:
if conflict.getDomain()=='Publication': # may be we do not need the case 'subscription' conflict.applyLocalValue()
for publication_item in self.getPublicationList():
if conflict in publication_item.getConflictList():
publication = publication_item
for subscriber_item in publication.getSubscriberList():
if conflict in subscriber_item.getConflictList():
subscriber = subscriber_item
if subscriber is not None and publication is not None:
# Retrieve the signature and change the status
publication_path = tuple(publication.getDestinationPath().split('/'))
# Get 167 in /nexedi/server/167/default_message
signature_id = object_path[len(publication_path)]
signature = subscriber.getSignature(signature_id)
signature.setStatus(signature.PUB_CONFLICT_MERGE)
# Then launch the synchronization (wich will only be upate for conflict
self.PubSync(publication.getId(),subscriber=subscriber)
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('manageConflicts') RESPONSE.redirect('manageConflicts')
security.declareProtected(Permissions.ModifyPortalContent, 'manageRemoteValue')
def manageRemoteValue(self, domain, domain_id, object_path, RESPONSE=None): def manageRemoteValue(self, subscription_url, keyword, object_path, RESPONSE=None):
""" """
Do whatever needed in order to store the remote value locally Do whatever needed in order to store the remote value locally
and confirmed that the remote box should keep it's value and confirmed that the remote box should keep it's value
""" """
conflict=None LOG('manageLocalValue',0,'%s %s %s' % (str(subscription_url),
if type(object_path) is type(''): str(keyword),
object_path = tuple(object_path.split('/')) str(object_path)))
for item in self.getConflictList(): for conflict in self.getConflictList():
if item.getDomain() == domain and item.getDomainId()==domain_id \ LOG('manageLocalValue, conflict:',0,conflict)
and item.getObjectPath()==object_path: if conflict.getKeyword() == keyword:
conflict=item LOG('manageLocalValue',0,'found the keyword')
break if '/'.join(conflict.getObjectPath())==object_path:
publication = subscriber = None if conflict.getDomain().getSubscriptionUrl()==subscription_url:
if conflict.getDomain()=='Publication': # may be we do not need the case 'subscription' conflict.applyRemoteValue()
for publication_item in self.getPublicationList():
if conflict in publication_item.getConflictList():
publication = publication_item
for subscriber_item in publication.getSubscriberList():
if conflict in subscriber_item.getConflictList():
subscriber = subscriber_item
if subscriber is not None and publication is not None:
# Retrieve the signature and change the status
publication_path = tuple(publication.getDestinationPath().split('/'))
# Get 167 in /nexedi/server/167/default_message
signature_id = object_path[len(publication_path)]
signature = subscriber.getSignature(signature_id)
signature.setStatus(signature.PUB_CONFLICT_CLIENT_WIN)
# Then launch the synchronization (wich will only be upate for conflict
self.PubSync(publication.getId(),subscriber=subscriber)
if RESPONSE is not None: if RESPONSE is not None:
RESPONSE.redirect('manageConflicts') RESPONSE.redirect('manageConflicts')
......
...@@ -73,23 +73,27 @@ Conflict management with n clients, n >= 2 ...@@ -73,23 +73,27 @@ Conflict management with n clients, n >= 2
I guess the best way is to just solve conflict one by one, then we are still I guess the best way is to just solve conflict one by one, then we are still
free to make another method wich solve for all versions by the same time. free to make another method wich solve for all versions by the same time.
- So we have to do : - So we have to do :
Conflict2.manageRemoteObject() Conflict2.setRemoteObject()
Conflict1.manageLocalObject() # wich is the version of D because of the previous call Conflict1.setLocalObject() # wich is the version of D because of the previous call
Conflict3.manageLocalObject() Conflict3.setLocalObject()
Conflict4.manageLocalObject() Conflict4.setLocalObject()
- May be we can do a global method, like : - May be we can do a global method, like :
Conflict2.manageGlobalRemoteObject() wich implicitly call Conflict2.setGlobalRemoteObject() wich implicitly call
Conflict1.manageLocalObject() Conflict1.setLocalObject()
and Conflict3.manageGlobalLocalObject() wich implicitly call and Conflict3.setGlobalLocalObject() wich implicitly call
Conflict4.manageLocalObject() Conflict4.setLocalObject()
- Conflict2.manageRemoteObject() have to apply all xupdate strings stored - Conflict2.setRemoteObject() have to apply all xupdate strings stored
in Conflict2 in Conflict2. Then it have to set the status as CONFLICT_CLIENT_WIN.
- Conflict3.manageLocalObject() have to set the status as SYNCHRONIZED and then - Conflict3.setLocalObject() have to set the status as CONFLICT_MERGE. How ??
it has to delete itself (Conflict3) -> How ?? - Probably the best way is to call the synchronizationTool wich know everything
Probably the best way is to call the synchronizationTool wich know everything
about subscription and subscriber. about subscription and subscriber.
- synchronizationtool.setLocalObject should have as parameter: the conflict (wich
store the subscriber), that's all
- then we can look at the signature of the object, delete the corresponding
Conflict, and if there is no conflict left, then we can set the signature
as CONFLICT_MERGE
- At this state, we do have the /w/x/truc of D, and the /w/x/machin of B, and - At this state, we do have the /w/x/truc of D, and the /w/x/machin of B, and
there is no conflict left, at least on the server side. there is no conflict left, at least on the server side.
- at the time of the next synchronization, the server should send is new version #- at the time of the next synchronization, the server should send is new version
of /w/x/truc and /w/x/machin to B, C and D, so that everyone is synchronized of /w/x/truc and /w/x/machin to B, C and D, so that everyone is synchronized
without conflict. without conflict.
\ No newline at end of file
...@@ -40,11 +40,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ...@@ -40,11 +40,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<th align="left" valign="top">Local Value</th> <th align="left" valign="top">Local Value</th>
<th align="left" valign="top">Remote Value</th> <th align="left" valign="top">Remote Value</th>
</tr> </tr>
<dtml-in getConflictList> <dtml-in prefix="loop" expr="getConflictList()">
<tr> <tr>
<td align="left" valign="top"><a href="manageLocalValue?domain=<dtml-var domain>&domain_id=<dtml-var domain_id>&object_path=<dtml-var "'/'.join(object_path)">">Local</a> <a href="manageRemoteValue?domain=<dtml-var domain>&domain_id=<dtml-var domain_id>&object_path=<dtml-var "'/'.join(object_path)">">Remote</a></td> <td align="left" valign="top"><a href="manageLocalValue?subscription_url=<dtml-var expr="loop_item.getDomain().getSubscriptionUrl()">&keyword=<dtml-var keyword>&object_path=<dtml-var "'/'.join(object_path)">">Local</a> <a href="manageRemoteValue?subscription_url=<dtml-var expr="loop_item.getDomain().getSubscriptionUrl()">&keyword=<dtml-var keyword>&object_path=<dtml-var "'/'.join(object_path)">">Remote</a></td>
<td align="left" valign="top"><dtml-var expr="loop_item.getDomain().getSubscriptionUrl()"></td>
<td align="left" valign="top"><dtml-var domain></td> <td align="left" valign="top"><dtml-var domain></td>
<td align="left" valign="top"><dtml-var domain_id></td>
<td align="left" valign="top"><dtml-var "'/'.join(object_path)"></td> <td align="left" valign="top"><dtml-var "'/'.join(object_path)"></td>
<td align="left" valign="top"><dtml-var keyword></td> <td align="left" valign="top"><dtml-var keyword></td>
<td align="left" valign="top"><dtml-var local_value></td> <td align="left" valign="top"><dtml-var local_value></td>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment