Commit 37df290b authored by Sebastien Robin's avatar Sebastien Robin

added workflow synchronization


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@133 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 1541c540
...@@ -37,7 +37,8 @@ from xml.dom.ext.reader.Sax2 import FromXml ...@@ -37,7 +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
import pickle
import string
import re, copy import re, copy
from zLOG import LOG from zLOG import LOG
...@@ -116,11 +117,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -116,11 +117,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
subobject = object._getOb(object_id) subobject = object._getOb(object_id)
except (AttributeError, KeyError): except (AttributeError, KeyError):
subobject = None subobject = None
#subobject = None
#try:
# subobject = object[object_id]
#except KeyError:
# pass
if subobject is None: # If so it does'nt exist yes if subobject is None: # If so it does'nt exist yes
portal_type = '' portal_type = ''
if xml.nodeName == 'object': if xml.nodeName == 'object':
...@@ -141,14 +137,21 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -141,14 +137,21 @@ class ERP5Conduit(XMLSyncUtilsMixin):
proxy_type = 'folder' proxy_type = 'folder'
proxy = px_tool.createEmptyProxy(proxy_type, proxy = px_tool.createEmptyProxy(proxy_type,
object,portal_type,object_id,docid) object,portal_type,object_id,docid)
#px_tool.createRevision(proxy,px_tool.getDefaultLanguage()) # Doesn't works well proxy.isIndexable = 0 # So it will not be reindexed, this prevent errors
# px_tool._addProxy(proxy,None) # Doesn't works well # Calculate rpath
#object.newContent(portal_type=portal_type, id=object_id) # Doesn't works with CPS utool = getToolByName(object, 'portal_url')
#subobject = object[object_id] # Doesn't works with CPS rpath = utool.getRelativeUrl(object)
# portal = getToolByName(object, 'portal_url').getPortalObject()
# root = portal.getPhysicalPath()
# if type(root) is type('a'):
# root = ('',root)
# path = object.getPhysicalPath()
# rpath = path[len(root):]
# rpath = string.join(rpath,'/')
px_tool._modifyProxy(proxy,rpath)
subobject = object._getOb(object_id) subobject = object._getOb(object_id)
# Again for CPS proxy XXX May be not needed
#if docid is not None:
# subobject.proxyChanged()
self.newObject(object=subobject,xml=xml) self.newObject(object=subobject,xml=xml)
elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \ elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \
and self.getSubObjectDepth(xml)==2: and self.getSubObjectDepth(xml)==2:
...@@ -181,6 +184,21 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -181,6 +184,21 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# Then do the udpate # Then do the udpate
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:
# We want to add a workflow action
wf_tool = getToolByName(object,'portal_workflow')
wf_id = self.getObjectId(xml)
if wf_id is None: # History added by xupdate
wf_id = self.getHistoryIdFromSelect(xml)
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 xml:',0,xml)
for action in self.getWorkflowActionFromXml(xml):
status = self.getStatusFromXml(action)
LOG('addNode, status:',0,status)
wf_tool.setStatusOf(wf_id,object,status)
else: else:
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
...@@ -238,8 +256,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -238,8 +256,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
args = {} args = {}
LOG('isSubObjectModification',0,'result: %s' % str(self.isSubObjectModification(xml))) LOG('isSubObjectModification',0,'result: %s' % str(self.isSubObjectModification(xml)))
if self.isProperty(xml) and not(self.isSubObjectModification(xml)): if self.isProperty(xml) and not(self.isSubObjectModification(xml)):
for subnode in xml.attributes: for subnode in self.getAttributeNodeList(xml):
if subnode.nodeType == subnode.ATTRIBUTE_NODE and subnode.nodeName=='select': if subnode.nodeName=='select':
LOG('updateNode',0,'selection: %s' % str(subnode.nodeValue)) LOG('updateNode',0,'selection: %s' % str(subnode.nodeValue))
select_list = subnode.nodeValue.split('/') # Something like: select_list = subnode.nodeValue.split('/') # Something like:
#('','object[1]','sid[1]') #('','object[1]','sid[1]')
...@@ -265,16 +283,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -265,16 +283,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
data = xml.childNodes[0].data data = xml.childNodes[0].data
except IndexError: # There is no data except IndexError: # There is no data
data = None data = None
# XXX may be not needed any more
#else:
# data=()
# for subnode in self.getElementNodeList(xml):
# element_data = subnode.childNodes[0].data
# element_data = self.convertXmlValue(element_data)
# data += (element_data,)
# if len(data) == 1: # This is probably because this is not a list
# but a string XXX may be not good
# data = data[0]
data_type = object.getPropertyType(keyword) data_type = object.getPropertyType(keyword)
LOG('updateNode',0,'data_type: %s' % str(data_type)) LOG('updateNode',0,'data_type: %s' % str(data_type))
data = self.convertXmlValue(data,data_type=data_type) data = self.convertXmlValue(data,data_type=data_type)
...@@ -307,10 +315,16 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -307,10 +315,16 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if args != {} and (isConflict==0 or force): if args != {} and (isConflict==0 or force):
LOG('updateNode',0,'object._edit, args: %s' % str(args)) LOG('updateNode',0,'object._edit, args: %s' % str(args))
object._edit(**args) object._edit(**args)
#if hasattr(object,'getDocid'): # This is a proxy, CPS
if keyword == 'object': if keyword == 'object':
# This is the case where we have to call addNode # This is the case where we have to call addNode
LOG('updateNode',0,'we will add sub-object') LOG('updateNode',0,'we will add sub-object')
conflict_list += self.addNode(xml=subnode,object=object,force=force) conflict_list += self.addNode(xml=subnode,object=object,force=force)
if keyword == self.history_tag:
# This is the case where we have to call addNode
LOG('updateNode',0,'we will add sub-object')
conflict_list += self.addNode(xml=subnode,object=object,force=force)
elif self.isSubObjectModification(xml): elif self.isSubObjectModification(xml):
# We should find the object corresponding to # We should find the object corresponding to
# this update, so we have to look in the previous_xml # this update, so we have to look in the previous_xml
...@@ -372,12 +386,12 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -372,12 +386,12 @@ class ERP5Conduit(XMLSyncUtilsMixin):
""" """
Check if it is a simple property Check if it is a simple property
""" """
bad_list = ('/object[1]/object','/object[1]/workflow_history','/object[1]/security_info') bad_list = (self.sub_object_exp,self.history_exp)
for subnode in xml.attributes: for subnode in xml.attributes:
if subnode.nodeType == subnode.ATTRIBUTE_NODE and subnode.nodeName=='select': if subnode.nodeType == subnode.ATTRIBUTE_NODE and subnode.nodeName=='select':
value = subnode.nodeValue value = subnode.nodeValue
for bad_string in bad_list: for bad_string in bad_list:
if value.find(bad_string)==0: if re.search(bad_string,value) is not None:
return 0 return 0
return 1 return 1
...@@ -402,6 +416,16 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -402,6 +416,16 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return subnode1 return subnode1
return xml return xml
def isHistoryAdd(self, xml):
bad_list = (self.history_exp)
for subnode in xml.attributes:
if subnode.nodeType == subnode.ATTRIBUTE_NODE and subnode.nodeName=='select':
value = subnode.nodeValue
for bad_string in bad_list:
if re.search(bad_string,value) is not None:
return 1
return 0
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
...@@ -482,6 +506,22 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -482,6 +506,22 @@ class ERP5Conduit(XMLSyncUtilsMixin):
return object_id return object_id
return object_id return object_id
def getHistoryIdFromSelect(self, xml):
"""
Return the id of the subobject in an xupdate modification
"""
object_id = None
for subnode in self.getAttributeNodeList(xml):
if subnode.nodeName=='select':
value = subnode.nodeValue
if re.search(self.history_exp,value) is not None:
s = self.history_tag
object_id = value[value.find(s):]
object_id = object_id[object_id.find("'")+1:]
object_id = object_id[:object_id.find("'")]
return object_id
return object_id
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
...@@ -534,11 +574,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -534,11 +574,14 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# document, with childNodes[0] a DocumentType and childNodes[1] the Element Node # document, with childNodes[0] a DocumentType and childNodes[1] the Element Node
for subnode in self.getElementNodeList(xml): for subnode in self.getElementNodeList(xml):
if subnode.nodeName == property: if subnode.nodeName == property:
if data_type is None:
data_type = self.getPropertyType(subnode)
try: try:
data = subnode.childNodes[0].data data = subnode.childNodes[0].data
data = self.convertXmlValue(data, data_type=data_type)
except IndexError: # There is no data except IndexError: # There is no data
data = None data = None
data = self.convertXmlValue(data, data_type=data_type)
LOG('getObjectProperty',0,'prop; %s, data_type:%s, data: %s' % (property,data_type,data))
return data return data
return None return None
...@@ -559,14 +602,25 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -559,14 +602,25 @@ class ERP5Conduit(XMLSyncUtilsMixin):
Retrieve the portal type from an xml Retrieve the portal type from an xml
""" """
portal_type = None portal_type = None
for subnode in xml.attributes: for subnode in self.getAttributeNodeList(xml):
if subnode.nodeType == subnode.ATTRIBUTE_NODE:
if subnode.nodeName=='portal_type': if subnode.nodeName=='portal_type':
portal_type = subnode.nodeValue portal_type = subnode.nodeValue
portal_type = self.convertXmlValue(portal_type) portal_type = self.convertXmlValue(portal_type)
return portal_type return portal_type
return portal_type return portal_type
def getPropertyType(self, xml):
"""
Retrieve the portal type from an xml
"""
p_type = None
for subnode in self.getAttributeNodeList(xml):
if subnode.nodeName=='type':
p_type = subnode.nodeValue
p_type = self.convertXmlValue(p_type,data_type='string')
return p_type
return p_type
def getXupdateObjectType(self, xml): def getXupdateObjectType(self, xml):
""" """
Retrieve the portal type from an xupdate Retrieve the portal type from an xupdate
...@@ -595,12 +649,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -595,12 +649,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xml = self.getElementNodeList(xml)[0] xml = self.getElementNodeList(xml)[0]
for subnode in self.getElementNodeList(xml): for subnode in self.getElementNodeList(xml):
if not(subnode.nodeName in self.NOT_EDITABLE_PROPERTY): if not(subnode.nodeName in self.NOT_EDITABLE_PROPERTY):
keyword_type = None keyword_type = self.getPropertyType(subnode)
for subnode1 in subnode.attributes:
if subnode1.nodeType == subnode1.ATTRIBUTE_NODE:
if subnode1.nodeName=='type':
keyword_type = subnode1.nodeValue
LOG('newObject',0,str(subnode.childNodes)) LOG('newObject',0,str(subnode.childNodes))
# This is the case where the property is a list # This is the case where the property is a list
keyword=str(subnode.nodeName) keyword=str(subnode.nodeName)
...@@ -622,11 +671,53 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -622,11 +671,53 @@ class ERP5Conduit(XMLSyncUtilsMixin):
object._edit(**args) object._edit(**args)
# Then we may create subobject # Then we may create subobject
for subnode in xml.childNodes: for subnode in self.getElementNodeList(xml):
if subnode.nodeType == subnode.ELEMENT_NODE and \ if subnode.nodeName in (self.xml_object_tag,self.history_tag):
subnode.nodeName=='object':
self.addNode(object=object,xml=subnode) self.addNode(object=object,xml=subnode)
def getStatusFromXml(self, xml):
"""
Return a worklow status from xml
"""
status = {}
for subnode in self.getElementNodeList(xml):
keyword = self.convertXmlValue(subnode.nodeName)
value = self.getObjectProperty(keyword,xml)
status[keyword] = value
return status
def getElementFromXupdate(self, xml):
"""
from a xupdate:element returns the element as xml
"""
if xml.nodeName in self.XUPDATE_EL:
result = '<'
result += xml.attributes[0].nodeValue
for subnode in self.getElementNodeList(xml): #getElementNodeList
if subnode.nodeName == 'xupdate:attribute':
result += ' ' + subnode.attributes[0].nodeValue + '='
result += '"' + subnode.childNodes[0].nodeValue + '"'
result += '>'
for text in self.getTextNodeList(xml): # getTextNodeList
result+= text.nodeValue
result += '</' + xml.attributes[0].nodeValue + '>'
LOG('getElementFromXupdate, result:',0,result)
return self.convertToXml(FromXml(result))
return xml
def getWorkflowActionFromXml(self, xml):
"""
Return the list of workflow actions
"""
action_list = []
if xml.nodeName in self.XUPDATE_EL:
action_list += [xml]
return action_list
for subnode in self.getElementNodeList(xml):
if subnode.nodeName == self.action_tag:
action_list += [subnode]
return action_list
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
...@@ -636,6 +727,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -636,6 +727,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if data is None: if data is None:
if data_type in self.list_type_list: if data_type in self.list_type_list:
data = () data = ()
if data_type in self.text_type_list:
data = ''
return data return data
data = data.replace('\n','') data = data.replace('\n','')
if type(data) is type(u"a"): if type(data) is type(u"a"):
...@@ -646,12 +739,19 @@ class ERP5Conduit(XMLSyncUtilsMixin): ...@@ -646,12 +739,19 @@ class ERP5Conduit(XMLSyncUtilsMixin):
data = tuple(data.split('@@@')) data = tuple(data.split('@@@'))
elif data_type in self.text_type_list: elif data_type in self.text_type_list:
data = data.replace('@@@','\n') data = data.replace('@@@','\n')
elif data_type in self.binary_type_list: # elif data_type in self.binary_type_list:
# data = data.replace('@@@','\n')
# msg = MIMEBase('application','octet-stream')
# Encoders.encode_base64(msg)
# msg.set_payload(data)
# data = msg.get_payload(decode=1)
elif data_type in self.pickle_type_list:
data = data.replace('@@@','\n') data = data.replace('@@@','\n')
msg = MIMEBase('application','octet-stream') msg = MIMEBase('application','octet-stream')
Encoders.encode_base64(msg) Encoders.encode_base64(msg)
msg.set_payload(data) msg.set_payload(data)
data = msg.get_payload(decode=1) data = msg.get_payload(decode=1)
data = pickle.loads(data)
elif data_type in self.date_type_list: elif data_type in self.date_type_list:
data = DateTime(data) data = DateTime(data)
elif data_type in self.dict_type_list: elif data_type in self.dict_type_list:
......
...@@ -70,19 +70,24 @@ class SyncCode(Persistent): ...@@ -70,19 +70,24 @@ class SyncCode(Persistent):
#ENCODING='iso-8859-1' #ENCODING='iso-8859-1'
NOT_EDITABLE_PROPERTY = ('id','object','workflow_history','security_info','uid' NOT_EDITABLE_PROPERTY = ('id','object','security_info','uid','workflow_history',
'xupdate:element','xupdate:attribute') 'xupdate:element','xupdate:attribute')
XUPDATE_INSERT = ('xupdate:insert-after','xupdate:insert-before') XUPDATE_INSERT = ('xupdate:insert-after','xupdate:insert-before')
XUPDATE_ADD = ('xupdate:append',) XUPDATE_ADD = ('xupdate:append',)
XUPDATE_DEL = ('xupdate:remove',) XUPDATE_DEL = ('xupdate:remove',)
XUPDATE_UPDATE = ('xupdate:update',) XUPDATE_UPDATE = ('xupdate:update',)
XUPDATE_EL = ('xupdate:element',)
XUPDATE_INSERT_OR_ADD = tuple(XUPDATE_INSERT) + tuple(XUPDATE_ADD) XUPDATE_INSERT_OR_ADD = tuple(XUPDATE_INSERT) + tuple(XUPDATE_ADD)
XUPDATE_TAG = tuple(XUPDATE_INSERT) + tuple(XUPDATE_ADD) + \ XUPDATE_TAG = tuple(XUPDATE_INSERT) + tuple(XUPDATE_ADD) + \
tuple(XUPDATE_UPDATE) + tuple(XUPDATE_DEL) tuple(XUPDATE_UPDATE) + tuple(XUPDATE_DEL)
text_type_list = ('text','string') text_type_list = ('text','string')
list_type_list = list_types list_type_list = list_types
binary_type_list = ('image','file','document') binary_type_list = ('image','file','document','pickle')
date_type_list = ('date',) date_type_list = ('date',)
dict_type_list = ('dict',) dict_type_list = ('dict',)
pickle_type_list = ('pickle',)
xml_object_tag = 'object' xml_object_tag = 'object'
history_tag = 'workflow_history'
action_tag = 'workflow_action'
sub_object_exp = "/object\[@id='.*'\]/object\[@id='.*'\]" sub_object_exp = "/object\[@id='.*'\]/object\[@id='.*'\]"
history_exp = "/object\[@id='.*'\]/%s\[@id='.*'\]" % history_tag
...@@ -528,6 +528,16 @@ class XMLSyncUtilsMixin(SyncCode): ...@@ -528,6 +528,16 @@ class XMLSyncUtilsMixin(SyncCode):
subnode_list += [subnode] subnode_list += [subnode]
return subnode_list return subnode_list
def getTextNodeList(self, node):
"""
Return childNodes that are ElementNode
"""
subnode_list = []
for subnode in node.childNodes:
if subnode.nodeType == subnode.TEXT_NODE:
subnode_list += [subnode]
return subnode_list
def getAttributeNodeList(self, node): def getAttributeNodeList(self, node):
""" """
Return childNodes that are ElementNode Return childNodes that are ElementNode
......
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