Commit 1ec30f8b authored by Arnaud Fontaine's avatar Arnaud Fontaine

Generate unique ID when exporting workflow history.

This fixes ERP5SyncML and ERP5DocumentSyncML tests failures as erp5diff
doesn't support merging of XML with nodes with the same ID.
parent 7494518e
...@@ -52,6 +52,8 @@ from base64 import standard_b64decode ...@@ -52,6 +52,8 @@ from base64 import standard_b64decode
from zope.interface import implements from zope.interface import implements
from copy import deepcopy from copy import deepcopy
import sha
from Products.ERP5SyncML.SyncMLConstant import XUPDATE_ELEMENT,\ from Products.ERP5SyncML.SyncMLConstant import XUPDATE_ELEMENT,\
XUPDATE_INSERT_OR_ADD_LIST, XUPDATE_DEL, XUPDATE_UPDATE, XUPDATE_INSERT_LIST XUPDATE_INSERT_OR_ADD_LIST, XUPDATE_DEL, XUPDATE_UPDATE, XUPDATE_INSERT_LIST
# Constant # Constant
...@@ -84,6 +86,10 @@ EXTRACT_ID_FROM_XPATH = re.compile( ...@@ -84,6 +86,10 @@ EXTRACT_ID_FROM_XPATH = re.compile(
"(?P<object_block>(?P<property>[^/]+)\[@"\ "(?P<object_block>(?P<property>[^/]+)\[@"\
"(?P<id_of_id>id|gid)='(?P<object_id>[^']+)'\])") "(?P<id_of_id>id|gid)='(?P<object_id>[^']+)'\])")
WORKFLOW_ACTION_NOT_ADDABLE = 0
WORKFLOW_ACTION_ADDABLE = 1
WORKFLOW_ACTION_INSERTABLE = 2
class ERP5Conduit(XMLSyncUtilsMixin): class ERP5Conduit(XMLSyncUtilsMixin):
""" """
A conduit is a piece of code in charge of A conduit is a piece of code in charge of
...@@ -214,9 +220,23 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -214,9 +220,23 @@ class ERP5Conduit(XMLSyncUtilsMixin):
#LOG('ERP5Conduit xpath_expression', INFO, xpath_expression) #LOG('ERP5Conduit xpath_expression', INFO, xpath_expression)
context_to_delete = self.getContextFromXpath(object, xpath_expression) context_to_delete = self.getContextFromXpath(object, xpath_expression)
if 'workflow_action' in xpath_expression: if 'workflow_action' in xpath_expression:
# Something like /erp5/object[@gid='313730']/object[@id='170']/workflow_action[@id='edit_workflow'][2] # /erp5/object[@gid='313730']/../workflow_action[@id=SHA(TIME + ACTOR)]
# we can not edit or erase workflow history wf_action_id = EXTRACT_ID_FROM_XPATH.findall(xpath_expression)[-1][-1]
pass def deleteWorkflowNode():
for wf_id, wf_history_tuple in object.workflow_history.iteritems():
for wf_history_index, wf_history in enumerate(wf_history_tuple):
if sha.new(wf_id + str(wf_history['time']) +
wf_history['actor']).hexdigest() == wf_action_id:
object.workflow_history[wf_id] = (
object.workflow_history[wf_id][:wf_history_index] +
object.workflow_history[wf_id][wf_history_index + 1:])
return True
return False
deleteWorkflowNode()
elif context_to_delete != object: elif context_to_delete != object:
self._deleteContent(object=context_to_delete.getParentValue(), self._deleteContent(object=context_to_delete.getParentValue(),
object_id=context_to_delete.getId(), object_id=context_to_delete.getId(),
...@@ -890,20 +910,20 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -890,20 +910,20 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if wf_id in wf_history: if wf_id in wf_history:
action_list = wf_history[wf_id] action_list = wf_history[wf_id]
else: else:
return True # addable return WORKFLOW_ACTION_ADDABLE
addable = True addable = WORKFLOW_ACTION_ADDABLE
time = status.get('time') time = status.get('time')
for action in action_list: for action in action_list:
this_one = True this_one = WORKFLOW_ACTION_ADDABLE
if time <= action.get('time'): if time <= action.get('time'):
# action in the past are not append # action in the past are not append
addable = False addable = WORKFLOW_ACTION_INSERTABLE
for key in action.keys(): for key in action.keys():
if status[key] != action[key]: if status[key] != action[key]:
this_one = False this_one = WORKFLOW_ACTION_NOT_ADDABLE
break break
if this_one: if this_one:
addable = False addable = WORKFLOW_ACTION_NOT_ADDABLE
break break
return addable return addable
...@@ -927,7 +947,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -927,7 +947,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
conflict_list = [] conflict_list = []
# We want to add a workflow action # We want to add a workflow action
wf_tool = getToolByName(object.getPortalObject(), 'portal_workflow') wf_tool = getToolByName(object.getPortalObject(), 'portal_workflow')
wf_id = xml.get('id') wf_id = xml.get('workflow_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 = xml[0] xml = xml[0]
...@@ -937,8 +957,18 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -937,8 +957,18 @@ class ERP5Conduit(XMLSyncUtilsMixin):
add_action = self.isWorkflowActionAddable(object=object, add_action = self.isWorkflowActionAddable(object=object,
status=status,wf_tool=wf_tool, status=status,wf_tool=wf_tool,
wf_id=wf_id,xml=xml) wf_id=wf_id,xml=xml)
if add_action and not simulate:
if not simulate:
if add_action == WORKFLOW_ACTION_ADDABLE:
wf_tool.setStatusOf(wf_id, object, status) wf_tool.setStatusOf(wf_id, object, status)
elif add_action == WORKFLOW_ACTION_INSERTABLE:
wf_history_list = list(object.workflow_history[wf_id])
for wf_history_index, wf_history in enumerate(wf_history_list):
if wf_history['time'] > status['time']:
wf_history_list.insert(wf_history_index, status)
break
object.workflow_history[wf_id] = tuple(wf_history_list)
return conflict_list return conflict_list
......
...@@ -39,6 +39,8 @@ from OFS.Image import Pdata ...@@ -39,6 +39,8 @@ from OFS.Image import Pdata
from zLOG import LOG from zLOG import LOG
from base64 import standard_b64encode from base64 import standard_b64encode
import sha
MARSHALLER_NAMESPACE_URI = 'http://www.erp5.org/namespaces/marshaller' MARSHALLER_NAMESPACE_URI = 'http://www.erp5.org/namespaces/marshaller'
marshaller = Marshaller(namespace_uri=MARSHALLER_NAMESPACE_URI, marshaller = Marshaller(namespace_uri=MARSHALLER_NAMESPACE_URI,
as_tree=True).dumps as_tree=True).dumps
...@@ -129,7 +131,7 @@ def Base_asXML(object, root=None): ...@@ -129,7 +131,7 @@ def Base_asXML(object, root=None):
for workflow_id in workflow_list_keys: for workflow_id in workflow_list_keys:
for workflow_action in workflow_list[workflow_id]: for workflow_action in workflow_list[workflow_id]:
workflow_node = SubElement(object, 'workflow_action', workflow_node = SubElement(object, 'workflow_action',
attrib=dict(id=workflow_id)) attrib=dict(workflow_id=workflow_id))
workflow_variable_list = workflow_action.keys() workflow_variable_list = workflow_action.keys()
workflow_variable_list.sort() workflow_variable_list.sort()
for workflow_variable in workflow_variable_list: for workflow_variable in workflow_variable_list:
...@@ -146,6 +148,14 @@ def Base_asXML(object, root=None): ...@@ -146,6 +148,14 @@ def Base_asXML(object, root=None):
variable_node_text = str(workflow_action[workflow_variable]) variable_node_text = str(workflow_action[workflow_variable])
variable_node.text = unicode(variable_node_text, 'utf-8') variable_node.text = unicode(variable_node_text, 'utf-8')
if workflow_variable == 'time':
time = variable_node.text
elif workflow_variable == 'actor':
actor = variable_node.text
workflow_node.attrib['id'] = sha.new(workflow_id + time +
str(actor.encode('utf-8'))).hexdigest()
# We should now describe security settings # We should now describe security settings
for user_role in self.get_local_roles(): for user_role in self.get_local_roles():
local_role_node = SubElement(object, 'local_role', local_role_node = SubElement(object, 'local_role',
......
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