[Erp5-report] r14954 - in /erp5/trunk/products/ERP5SyncML: ./ Conduit/ dtml/ tests/
nobody at svn.erp5.org
nobody at svn.erp5.org
Mon Jun 25 17:22:33 CEST 2007
Author: seb
Date: Mon Jun 25 17:22:32 2007
New Revision: 14954
URL: http://svn.erp5.org?rev=14954&view=rev
Log:
- add source_uri and target_uri fields in the dtml files
- change the subscriber property to use id, gid and rid
- encode the gid in base16
- change some parts to make VCard synchronization working
- remove some logs
- change the status implementation to use xpath expressions
- use utf-8 everywhere
Modified:
erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py
erp5/trunk/products/ERP5SyncML/Publication.py
erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py
erp5/trunk/products/ERP5SyncML/Subscription.py
erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py
erp5/trunk/products/ERP5SyncML/SyncCode.py
erp5/trunk/products/ERP5SyncML/SynchronizationTool.py
erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py
erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml
erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml
erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml
erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml
erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py
Modified: erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py (original)
+++ erp5/trunk/products/ERP5SyncML/Conduit/ERP5Conduit.py Mon Jun 25 17:22:32 2007
@@ -139,7 +139,6 @@
xml = self.convertToXml(xml)
if xml is None:
return {'conflict_list':conflict_list, 'object':sub_object}
- #LOG('addNode',0,'xml_reconstitued: %s' % str(xml))
# In the case where this new node is a object to add
if xml.nodeName in self.XUPDATE_INSERT_OR_ADD and \
self.getSubObjectDepth(xml)==0:
@@ -153,7 +152,6 @@
if object_id is None:
object_id = self.getAttribute(xml,'id')
docid = self.getObjectDocid(xml)
- #LOG('addNode',0,'object_id: %s' % object_id)
if object_id is not None:
if sub_object is None:
try:
@@ -181,7 +179,6 @@
elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \
and self.getSubObjectDepth(xml)>=1:
sub_object_id = self.getSubObjectId(xml)
- #LOG('addNode',0,'getSubObjectModification number: %s' % sub_object_id)
if previous_xml is not None and sub_object_id is not None:
#LOG('addNode',0,'previous xml is not none and also sub_object_id')
# Find the previous xml corresponding to this subobject
@@ -242,7 +239,7 @@
sub_object = object._getOb(sub_object_id)
sub_xml = self.getSubObjectXupdate(xml)
conflict_list += self.deleteNode(xml=sub_xml,object=sub_object,
- force=force, simulate=simulate, **kw)
+ force=force, simulate=simulate, **kw)
except (KeyError, AttributeError, TypeError):
#LOG('ERP5Conduit',0,'deleteNode, Unable to delete SubObject: %s' % str(sub_object_id))
pass
@@ -385,7 +382,6 @@
#subscriber_value=data)] # not needed any more
# We will now apply the argument with the method edit
if args != {} and (isConflict==0 or force) and (not simulate):
- #LOG('updateNode',0,'object._edit, args: %s' % str(args))
#object._edit(**args)
self.editDocument(object=object,**args)
# It is sometimes required to do something after an edit
@@ -394,12 +390,10 @@
if keyword == 'object':
# This is the case where we have to call addNode
- #LOG('updateNode',0,'we will add sub-object')
conflict_list += self.addNode(xml=xml, object=object, force=force,
simulate=simulate, **kw)['conflict_list']
elif keyword == self.history_tag and not simulate:
# This is the case where we have to call addNode
- #LOG('updateNode',0,'we will add history')
conflict_list += self.addNode(xml=subnode,object=object,force=force,
simulate=simulate,**kw)['conflict_list']
elif keyword in (self.local_role_tag,self.local_permission_tag) and not simulate:
@@ -433,8 +427,9 @@
sub_xml = self.getSubObjectXupdate(xml)
#LOG('updateNode',0,'sub_xml: %s' % str(sub_xml))
# Then do the udpate
- conflict_list += self.updateNode(xml=sub_xml, object=sub_object, force=force,
- previous_xml=sub_previous_xml, simulate=simulate, **kw)
+ conflict_list += self.updateNode(xml=sub_xml, object=sub_object,
+ force=force, previous_xml=sub_previous_xml,
+ simulate=simulate, **kw)
elif previous_xml is None and xml is not None and sub_object_id is not None:
sub_object = None
try:
@@ -470,7 +465,6 @@
This lookd inside the args dictionnary and then
convert any unicode string to string
"""
- #LOG('ERP5Conduit.getFormatedArgs',0,'args: %s' % str(args))
new_args = {}
for keyword in args.keys():
data = args[keyword]
@@ -646,7 +640,6 @@
xml = self.convertToXml(xml)
for subnode in self.getElementNodeList(xml):
if subnode.nodeName==self.xml_object_tag:
- LOG('getSub0bjectXml: object_id:',0,object_id)
if object_id == self.getAttribute(subnode,'id'):
return subnode
return None
@@ -700,7 +693,6 @@
if xml is None:
return
if type(xml) in (type('a'),type(u'a')):
- #LOG('Conduit.convertToXml xml',0,repr(xml))
if type(xml) is type(u'a'):
xml = xml.encode('utf-8')
xml = Parse(xml)
@@ -981,7 +973,7 @@
force=force, simulate=simulate, **kw)['conflict_list']
elif subnode.nodeName in self.XUPDATE_DEL:
conflict_list += conduit.deleteNode(xml=sub_xupdate, object=object, \
- force=force, simulate=simulate, **kw)
+ force=force, simulate=simulate, **kw)
elif subnode.nodeName in self.XUPDATE_UPDATE:
conflict_list += conduit.updateNode(xml=sub_xupdate, object=object, \
force=force, simulate=simulate, **kw)
@@ -1065,27 +1057,19 @@
This is really usefull if you want to write your own Conduit.
"""
conflict_list = []
- #LOG('addNode, workflow_history isHistoryAdd:',0,self.isHistoryAdd(xml))
# We want to add a workflow action
wf_tool = getToolByName(object,'portal_workflow')
wf_id = self.getAttribute(xml,'id')
if wf_id is None: # History added by xupdate
wf_id = self.getHistoryIdFromSelect(xml)
- #LOG('addNode, workflow_history id:',0,wf_id)
- #LOG('addNode, workflow_history xml:',0,xml.toxml())#toxml isn't in 4Suite
- #LOG('addNode, workflow_history xml.getElmentNodeList:',0,self.getElementNodeList(xml))
xml = self.getElementNodeList(xml)[0]
- #LOG('addNode, workflow_history id:',0,wf_id)
- #LOG('addNode, workflow_history xml:',0,xml)
#for action in self.getWorkflowActionFromXml(xml):
status = self.getStatusFromXml(xml)
#LOG('addNode, status:',0,status)
add_action = self.isWorkflowActionAddable(object=object,
status=status,wf_tool=wf_tool,
wf_id=wf_id,xml=xml)
- #LOG('addNode, workflow_history add_action:',0,add_action)
if add_action and not simulate:
- #LOG('addNode, setting status:',0,'ok')
wf_tool.setStatusOf(wf_id,object,status)
# Specific CPS, try to remove duplicate lines in portal_repository._histories
@@ -1191,4 +1175,3 @@
xml_string = buf.getvalue()
buf.close()
return xml_string
-
Modified: erp5/trunk/products/ERP5SyncML/Publication.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Publication.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Publication.py (original)
+++ erp5/trunk/products/ERP5SyncML/Publication.py Mon Jun 25 17:22:32 2007
@@ -152,7 +152,7 @@
constructors = (addPublication,)
# Constructor
- def __init__(self, id, title, publication_url, destination_path,
+ def __init__(self, id, title, publication_url, destination_path, source_uri,
query, xml_mapping, conduit, gpg_key, id_generator, gid_generator,
media_type, auth_required=False, authentication_format='',
authentication_type=''):
@@ -162,6 +162,7 @@
self.id = id
self.publication_url = publication_url
self.destination_path = destination_path
+ self.setSourceURI(source_uri)
self.setQuery(query)
self.xml_mapping = xml_mapping
#self.list_subscribers = PersistentMapping()
Modified: erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py (original)
+++ erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py Mon Jun 25 17:22:32 2007
@@ -110,7 +110,7 @@
if authentication_format == publication.getAuthenticationFormat():
if authentication_type == publication.getAuthenticationType():
decoded = subscriber.decode(authentication_format, data)
- if decoded not in ('', None) and decoded.__contains__(':'):
+ if decoded not in ('', None) and ':' in decoded:
(login, password) = decoded.split(':')
uf = self.getPortalObject().acl_users
for plugin_name, plugin in uf._getOb('plugins').listPlugins(
@@ -124,6 +124,8 @@
newSecurityManager(None, user)
subscriber.setUser(login)
break
+ else:
+ auth_code=self.UNAUTHORIZED
#in all others cases, the auth_code is set to UNAUTHORIZED
@@ -148,6 +150,13 @@
subscriber.getSubscriptionUrl(), publication.getPublicationUrl(),
subscriber.getLastAnchor(), subscriber.getNextAnchor()))
cmd_id += 1
+ else:
+ # chal message
+ xml(self.SyncMLChal(cmd_id, "SyncHdr",
+ publication.getPublicationUrl(), subscriber.getSubscriptionUrl(),
+ publication.getAuthenticationFormat(),
+ publication.getAuthenticationType(), self.AUTH_REQUIRED))
+ cmd_id += 1
xml(' </SyncBody>\n')
xml('</SyncML>\n')
xml_a = ''.join(xml_list)
Modified: erp5/trunk/products/ERP5SyncML/Subscription.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Subscription.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Subscription.py (original)
+++ erp5/trunk/products/ERP5SyncML/Subscription.py Mon Jun 25 17:22:32 2007
@@ -42,9 +42,10 @@
import md5
try:
- from base64 import b64encode, b64decode
+ from base64 import b64encode, b64decode, b16encode, b16decode
except ImportError:
- from base64 import encodestring as b64encode, decodestring as b64decode
+ from base64 import encodestring as b64encode, decodestring as b64decode, \
+ encodestring as b16encode, decodestring as b16decode
#class Conflict(SyncCode, Implicit):
class Conflict(SyncCode, Base):
@@ -250,13 +251,16 @@
isPortalContent = 0 # Make sure RAD generated accessors at the class level
# Constructor
- def __init__(self,gid=None, id=None, status=None, xml_string=None,object=None):
- self.setGid(gid)
+ def __init__(self, id=None, rid=None, status=None, xml_string=None,
+ object=None):
if object is not None:
self.setPath(object.getPhysicalPath())
+ self.setObjectId(object.getId())
else:
self.setPath(None)
self.setId(id)
+ self.setGid(id)
+ self.setRid(rid)
self.status = status
self.setXML(xml_string)
self.partial_xml = None
@@ -280,7 +284,7 @@
if temp_xml is not None:
# This happens when we have sent the xml
# and we just get the confirmation
- self.setXML(self.getTempXML())
+ self.setXML(temp_xml)
self.setTempXML(None)
self.setPartialXML(None)
self.setSubscriberXupdate(None)
@@ -367,7 +371,7 @@
def getXML(self):
"""
- set the XML corresponding to the object
+ get the XML corresponding to the object
"""
xml = getattr(self,'xml',None)
if xml == '':
@@ -437,18 +441,22 @@
"""
set the rid
"""
+ if rid is type(u'a'):
+ rid = rid.encode('utf-8')
self.rid = rid
def getRid(self):
"""
get the rid
"""
- return self.rid
+ return getattr(self, 'rid', None)
def setId(self, id):
"""
set the id
"""
+ if id is type(u'a'):
+ id = id.encode('utf-8')
self.id = id
def getId(self):
@@ -459,15 +467,31 @@
def setGid(self, gid):
"""
- set the id
- """
+ set the gid
+ """
+ if gid is type(u'a'):
+ gid = gid.encode('utf-8')
self.gid = gid
def getGid(self):
"""
- get the id
+ get the gid
"""
return self.gid
+
+ def setObjectId(self, id):
+ """
+ set the id of the object associated to this signature
+ """
+ if id is type(u'a'):
+ id = id.encode('utf-8')
+ self.object_id = id
+
+ def getObjectId(self):
+ """
+ get the id of the object associated to this signature
+ """
+ return getattr(self, 'object_id', None)
def setPartialXML(self, xml):
"""
@@ -545,7 +569,7 @@
"""
Returns the object corresponding to this signature
"""
- return self.getParentValue().getObjectFromGid(self.getGid())
+ return self.getParentValue().getObjectIdGid(self.getObjectId())
def checkSynchronizationNeeded(self, object):
"""
@@ -643,8 +667,9 @@
# Constructor
def __init__(self, id, title, publication_url, subscription_url,
- destination_path, query, xml_mapping, conduit, gpg_key, id_generator,
- gid_generator, media_type, login, password):
+ destination_path, source_uri, target_uri, query, xml_mapping,
+ conduit, gpg_key, id_generator, gid_generator, media_type, login,
+ password):
"""
We need to create a dictionnary of
signatures of documents which belong to the synchronisation
@@ -654,6 +679,8 @@
self.publication_url = (publication_url)
self.subscription_url = str(subscription_url)
self.destination_path = str(destination_path)
+ self.setSourceURI(source_uri)
+ self.setTargetURI(target_uri)
self.setQuery(query)
self.setXMLMapping(xml_mapping)
self.anchor = None
@@ -685,15 +712,31 @@
setter for title
"""
self.title = value
-
- # Accessors
- def getRemoteId(self, id, path=None):
- """
- Returns the remote id from a know local id
- Returns None if...
- path allows to implement recursive sync
- """
- pass
+
+ def setSourceURI(self, value):
+ """
+ setter for source_uri
+ """
+ self.source_uri = value
+
+ def getSourceURI(self):
+ """
+ getter for the source_uri (the local path of the subscription)
+ """
+ return getattr(self, 'source_uri', None)
+
+ def setTargetURI(self, value):
+ """
+ setter for target_uri
+ """
+ self.target_uri = value
+
+ def getTargetURI(self):
+ """
+ getter for the target_uri (the distant Publication we want to synchronize
+ with)
+ """
+ return getattr(self, 'target_uri', None)
def getSynchronizationType(self, default=None):
"""
@@ -749,7 +792,7 @@
self.last_message_id = message_id
return True
- def initLastMessageId(self, last_message_id=None):
+ def initLastMessageId(self, last_message_id=0):
"""
set the last message id to 0
"""
@@ -767,24 +810,17 @@
"""
self.last_sent_message = xml
- def getLocalId(self, rid, path=None):
- """
- Returns the local id from a know remote id
- Returns None if...
- """
- pass
+ def getDomainType(self):
+ """
+ return the ID
+ """
+ return self.domain_type
def getId(self):
"""
return the ID
"""
return self.id
-
- def getDomainType(self):
- """
- return the ID
- """
- return self.domain_type
def setId(self, id):
"""
@@ -934,13 +970,13 @@
"""
return the format of authentication
"""
- return getattr(self, 'authentication_format', '')
+ return getattr(self, 'authentication_format', 'b64')
def getAuthenticationType(self):
"""
return the type of authentication
"""
- return getattr(self, 'authentication_type', '')
+ return getattr(self, 'authentication_type', 'syncml:auth-basic')
def setAuthenticationFormat(self, authentication_format):
"""
@@ -969,7 +1005,7 @@
# It might be a script python
generator = getattr(object,gid_gen)
o_gid = generator() # XXX - used to be o_gid = generator(object=object) which is redundant
- #LOG('getGidFromObject',0,'o_gid: %s' % repr(o_gid))
+ o_gid = b16encode(o_gid)
return o_gid
def getObjectFromGid(self, gid):
@@ -977,16 +1013,13 @@
This tries to get the object with the given gid
This uses the query if it exist
"""
- signature = self.getSignature(gid)
+ signature = self.getSignatureFromGid(gid)
# First look if we do already have the mapping between
# the id and the gid
object_list = self.getObjectList()
destination = self.getDestination()
- # LOG('getObjectFromGid', 0, 'self: %s' % self)
- # LOG('getObjectFromGid',0,'gid: %s' % repr(gid))
- # LOG('getObjectFromGid oject_list',0,object_list)
- if signature is not None and signature.getId() is not None:
- o_id = signature.getId()
+ if signature is not None and signature.getObjectId() is not None:
+ o_id = signature.getObjectId()
o = None
try:
o = destination._getOb(o_id)
@@ -998,7 +1031,18 @@
o_gid = self.getGidFromObject(o)
if o_gid == gid:
return o
- # LOG('getObjectFromGid',0,'returning None')
+ LOG('getObjectFromGid',0,'returning None')
+ return None
+
+ def getObjectFromId(self, id):
+ """
+ return the object corresponding to the id
+ """
+ object_list = self.getObjectList()
+ #XXX very slow with lot of objects
+ for object in object_list:
+ if object.getId() == id:
+ return object
return None
# def setOneWaySyncFromServer(self,value):
@@ -1195,27 +1239,82 @@
"""
add a Signature to the subscription
"""
- if signature.getGid() in self.objectIds():
- self._delObject(signature.getGid())
- self._setObject(signature.getGid(), aq_base(signature) )
+ if signature.getGid() in self.getGidList():
+ self.delSignature(signature.getGid())
+ self._setObject(signature.getGid(), aq_base(signature))
def delSignature(self, gid):
"""
- add a Signature to the subscription
+ del a Signature of the subscription
"""
#del self.signatures[gid]
self._delObject(gid)
- def getSignature(self, gid):
- """
- add a Signature to the subscription
+ def getSignatureFromObjectId(self, id):
+ """
+ return the signature corresponding to the gid
"""
o = None
- if gid in self.objectIds():
- o = self._getOb(gid)
- #if o is not None:
- # return o.__of__(self)
+ # XXX very slow
+ for signature in self.getSignatureList():
+ if id == signature.getObjectId():
+ o = signature
+ break
return o
+
+ def getSignatureFromGid(self, gid):
+ """
+ return the signature corresponding to the gid
+ """
+ o = None
+ # XXX very slow
+ for signature in self.getSignatureList():
+ if gid == signature.getGid():
+ o = signature
+ break
+ return o
+
+ def getSignatureFromRid(self, rid):
+ """
+ return the signature corresponding to the rid
+ """
+ o = None
+ # XXX very slow
+ for signature in self.getSignatureList():
+ if rid == signature.getRid():
+ o = signature
+ break
+ return o
+
+ def getObjectIdList(self):
+ """
+ Returns the list of gids from signature
+ """
+ object_id_list = []
+ for signature in self.getSignatureList():
+ if signature.getObjectId() is not None:
+ object_id_list.append(signature.getObjectId())
+ return object_id_list
+
+ def getGidList(self):
+ """
+ Returns the list of gids from signature
+ """
+ gid_list = []
+ for signature in self.getSignatureList():
+ if signature.getGid() is not None:
+ gid_list.append(signature.getGid())
+ return gid_list
+
+ def getRidList(self):
+ """
+ Returns the list of rids from signature
+ """
+ rid_list = []
+ for signature in self.getSignatureList():
+ if signature.getRid() is not None:
+ rid_list.append(signature.getRid())
+ return rid_list
def getSignatureList(self):
"""
@@ -1237,12 +1336,6 @@
while len(self.objectIds())>0:
for id in self.objectIds():
self._delObject(id)
-
- def getGidList(self):
- """
- Returns the list of ids from signature
- """
- return self.objectIds()
def getConflictList(self):
"""
@@ -1279,20 +1372,6 @@
if o != object_path:
new_list.append(o)
self.setRemainingObjectPathList(new_list)
-
-# def getCurrentObject(self):
-# """
-# When we send some partial data, then we should
-# always synchronize the same object until it is finished
-# """
-# getattr(self,'current_object',None)
-#
-# def setCurrentObject(self,object):
-# """
-# When we send some partial data, then we should
-# always synchronize the same object until it is finished
-# """
-# setattr(self,'current_object',object)
def startSynchronization(self):
"""
Modified: erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py (original)
+++ erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py Mon Jun 25 17:22:32 2007
@@ -49,7 +49,7 @@
# syncml header
xml(self.SyncMLHeader(subscription.incrementSessionId(),
subscription.incrementMessageId(), subscription.getPublicationUrl(),
- subscription.getSubscriptionUrl()))
+ subscription.getSubscriptionUrl(), source_name=subscription.getLogin()))
# syncml body
xml(' <SyncBody>\n')
@@ -59,16 +59,15 @@
# alert message
xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(),
- subscription.getPublicationUrl(),
- subscription.getDestinationPath(),
+ subscription.getTargetURI(),
+ subscription.getSourceURI(),
subscription.getLastAnchor(),
subscription.getNextAnchor()))
cmd_id += 1
-
- xml(' <Put>\n')
- xml(' <CmdID>%s</CmdID>\n' % cmd_id)
- cmd_id += 1
- xml(' </Put>\n')
+ syncml_put = self.SyncMLPut(cmd_id, subscription)
+ if syncml_put not in ('', None):
+ xml(syncml_put)
+ cmd_id += 1
xml(' </SyncBody>\n')
xml('</SyncML>\n')
xml_a = ''.join(xml_list)
@@ -79,20 +78,17 @@
return {'has_response':1,'xml':xml_a}
- def SubSync(self, id, msg=None, RESPONSE=None):
+ def SubSync(self, subscription, msg=None, RESPONSE=None):
"""
This is the synchronization method for the client
"""
- #LOG('SubSync',0,'starting... id: %s' % str(id))
- #LOG('SubSync',0,'starting... msg: %s' % str(msg))
response = None #check if subsync replies to this messages
- subscription = self.getSubscription(id)
if msg==None and (subscription.getSubscriptionUrl()).find('file')>=0:
- msg = self.readResponse(sync_id=id,
+ msg = self.readResponse(sync_id=subscription.getSourceURI(),
from_url=subscription.getSubscriptionUrl())
if msg==None:
- response = self.SubSyncInit(self.getSubscription(id))
+ response = self.SubSyncInit(subscription)
else:
xml_client = msg
if isinstance(xml_client, str) or isinstance(xml_client, unicode):
@@ -105,21 +101,24 @@
if status_code == self.AUTH_REQUIRED:
if self.checkChal(xml_client):
authentication_format, authentication_type = self.getChal(xml_client)
- subscription.setAuthenticationFormat(authentication_format)
- subscription.setAuthenticationType(authentication_type)
+ #LOG('auth_required :',0, 'format:%s, type:%s' % (authentication_format, authentication_type))
+ if authentication_format is not None and \
+ authentication_type is not None:
+ subscription.setAuthenticationFormat(authentication_format)
+ subscription.setAuthenticationType(authentication_type)
else:
raise ValueError, "Sorry, the server chalenge for an \
authentication, but the authentication format is not find"
#LOG('readResponse', 0, 'Authentication required')
- response = self.SubSyncCred(id, xml_client)
+ response = self.SubSyncCred(subscription, xml_client)
elif status_code == self.UNAUTHORIZED:
- #LOG('readResponse', 0, 'Bad authentication')
+ LOG('readResponse', 0, 'Bad authentication')
return {'has_response':0,'xml':xml_client}
else:
- response = self.SubSyncModif(self.getSubscription(id), xml_client)
+ response = self.SubSyncModif(subscription, xml_client)
else:
- response = self.SubSyncModif(self.getSubscription(id), xml_client)
+ response = self.SubSyncModif(subscription, xml_client)
if RESPONSE is not None:
@@ -127,43 +126,43 @@
else:
return response
- def SubSyncCred (self, id, msg=None, RESPONSE=None):
+ def SubSyncCred (self, subscription, msg=None, RESPONSE=None):
"""
This method send crendentials
"""
-
- #LOG('SubSyncCred',0,'starting... id: %s' % str(id))
- #LOG('SubSyncCred',0,'starting... msg: %s' % str(msg))
-
cmd_id = 1 # specifies a SyncML message-unique command identifier
- subscription = self.getSubscription(id)
xml_list = []
xml = xml_list.append
xml('<SyncML>\n')
# syncml header
data = "%s:%s" % (subscription.getLogin(), subscription.getPassword())
data=subscription.encode(subscription.getAuthenticationFormat(), data)
- xml(self.SyncMLHeader(subscription.getSessionId(),
- subscription.incrementMessageId(), subscription.getPublicationUrl(),
- subscription.getSubscriptionUrl(), dataCred=data,
+ xml(self.SyncMLHeader(
+ subscription.incrementSessionId(),
+ subscription.incrementMessageId(),
+ subscription.getPublicationUrl(),
+ subscription.getSubscriptionUrl(),
+ source_name=subscription.getLogin(),
+ dataCred=data,
authentication_format=subscription.getAuthenticationFormat(),
authentication_type=subscription.getAuthenticationType()))
# syncml body
xml(' <SyncBody>\n')
+ # We have to set every object as NOT_SYNCHRONIZED
+ subscription.startSynchronization()
+
# alert message
xml(self.SyncMLAlert(cmd_id, subscription.getSynchronizationType(),
- subscription.getPublicationUrl(),
- subscription.getDestinationPath(),
+ subscription.getTargetURI(),
+ subscription.getSourceURI(),
subscription.getLastAnchor(),
subscription.getNextAnchor()))
cmd_id += 1
-
- xml(' <Put>\n')
- xml(' <CmdID>%s</CmdID>\n' % cmd_id)
+ xml(self.SyncMLPut(cmd_id, subscription))
cmd_id += 1
- xml(' </Put>\n')
+ xml(' <Final/>\n')
xml(' </SyncBody>\n')
xml('</SyncML>\n')
xml_a = ''.join(xml_list)
Modified: erp5/trunk/products/ERP5SyncML/SyncCode.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SyncCode.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SyncCode.py (original)
+++ erp5/trunk/products/ERP5SyncML/SyncCode.py Mon Jun 25 17:22:32 2007
@@ -41,6 +41,8 @@
# SyncML Status Codes
SUCCESS = 200
+ ITEM_ADDED = 201
+
CHUNK_OK = 214
CONFLICT = 409 # A conflict is detected
CONFLICT_MERGE = 207 # We have merged the two versions, sending
Modified: erp5/trunk/products/ERP5SyncML/SynchronizationTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SynchronizationTool.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SynchronizationTool.py (original)
+++ erp5/trunk/products/ERP5SyncML/SynchronizationTool.py Mon Jun 25 17:22:32 2007
@@ -161,7 +161,7 @@
security.declareProtected(Permissions.ModifyPortalContent,
'manage_addPublication')
def manage_addPublication(self, title, publication_url, destination_path,
- query, xml_mapping, conduit, gpg_key,
+ source_uri, query, xml_mapping, conduit, gpg_key,
synchronization_id_generator=None, gid_generator=None,
media_type=None, auth_required=0, authentication_format='',
authentication_type='', RESPONSE=None):
@@ -174,7 +174,7 @@
folder = self.getObjectContainer()
new_id = self.getPublicationIdFromTitle(title)
pub = Publication(new_id, title, publication_url, destination_path,
- query, xml_mapping, conduit, gpg_key,
+ source_uri, query, xml_mapping, conduit, gpg_key,
synchronization_id_generator, gid_generator, media_type,
auth_required, authentication_format, authentication_type)
folder._setObject( new_id, pub )
@@ -187,7 +187,8 @@
security.declareProtected(Permissions.ModifyPortalContent,
'manage_addSubscription')
def manage_addSubscription(self, title, publication_url, subscription_url,
- destination_path, query, xml_mapping, conduit, gpg_key,
+ destination_path, source_uri, target_uri, query,
+ xml_mapping, conduit, gpg_key,
synchronization_id_generator=None, gid_generator=None,
media_type=None, login=None, password=None,
RESPONSE=None):
@@ -201,7 +202,8 @@
folder = self.getObjectContainer()
new_id = self.getSubscriptionIdFromTitle(title)
sub = Subscription(new_id, title, publication_url, subscription_url,
- destination_path, query, xml_mapping, conduit, gpg_key,
+ destination_path, source_uri, target_uri, query,
+ xml_mapping, conduit, gpg_key,
synchronization_id_generator, gid_generator, media_type,
login, password)
folder._setObject( new_id, sub )
@@ -214,7 +216,7 @@
security.declareProtected(Permissions.ModifyPortalContent,
'manage_editPublication')
def manage_editPublication(self, title, publication_url, destination_path,
- query, xml_mapping, conduit, gpg_key,
+ source_uri, query, xml_mapping, conduit, gpg_key,
synchronization_id_generator, gid_generator,
media_type=None, auth_required=0,
authentication_format='', authentication_type='',
@@ -226,6 +228,7 @@
pub.setTitle(title)
pub.setPublicationUrl(publication_url)
pub.setDestinationPath(destination_path)
+ pub.setSourceURI(source_uri)
pub.setQuery(query)
pub.setConduit(conduit)
pub.setXMLMapping(xml_mapping)
@@ -243,9 +246,9 @@
security.declareProtected(Permissions.ModifyPortalContent,
'manage_editSubscription')
def manage_editSubscription(self, title, publication_url, subscription_url,
- destination_path, query, xml_mapping, conduit, gpg_key,
- synchronization_id_generator, gid_generator, media_type=None,login='',
- password='', RESPONSE=None):
+ destination_path, source_uri, target_uri, query, xml_mapping, conduit,
+ gpg_key, synchronization_id_generator, gid_generator, media_type=None,
+ login='', password='', RESPONSE=None):
"""
modify a subscription
"""
@@ -253,6 +256,8 @@
sub.setTitle(title)
sub.setPublicationUrl(publication_url)
sub.setDestinationPath(destination_path)
+ sub.setSourceURI(source_uri)
+ sub.setTargetURI(target_uri)
sub.setQuery(query)
sub.setConduit(conduit)
sub.setXMLMapping(xml_mapping)
@@ -319,7 +324,7 @@
"""
reset a subscription
"""
- self.SubSync(title)
+ self.SubSync(self.getSubscription(title))
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
@@ -371,7 +376,7 @@
def getSubscription(self, title):
"""
- Returns the subscription with this id
+ Returns the subscription with this title
"""
for s in self.getSubscriptionList():
if s.getTitle() == title:
@@ -489,7 +494,7 @@
subscriber_list = [domain]
#LOG('getSynchronizationState, subscriber_list:',0,subscriber_list)
for subscriber in subscriber_list:
- signature = subscriber.getSignature(o_id)
+ signature = subscriber.getSignatureFromObjectId(o_id)
if signature is not None:
state = signature.getStatus()
#LOG('getSynchronizationState:',0,'sub.dest :%s, state: %s' % \
@@ -514,7 +519,7 @@
subscriber = conflict.getSubscriber()
# get the signature:
#LOG('p_sync.applyPublisherValue, subscriber: ',0,subscriber)
- signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
+ signature = subscriber.getSignatureFromObjectId(object.getId()) # XXX may be change for rid
copy_path = conflict.getCopyPath()
#LOG('p_sync.applyPublisherValue, copy_path: ',0,copy_path)
signature.delConflict(conflict)
@@ -690,7 +695,7 @@
subscriber = conflict.getSubscriber()
# get the signature:
#LOG('p_sync.setRemoteObject, subscriber: ',0,subscriber)
- signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
+ signature = subscriber.getSignatureFromObjectId(object.getId()) # XXX may be change for rid
# Import the conduit and get it
conduit_name = subscriber.getConduit()
conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]),
@@ -885,12 +890,22 @@
opener = urllib2.build_opener(proxy_handler, proxy_auth_handler,
auth_handler, urllib2.HTTPHandler)
urllib2.install_opener(opener)
- to_encode = {'text':xml,'sync_id':sync_id}
- encoded = urllib.urlencode(to_encode)
+ to_encode = {}
+ head = '<?xml version="1.0" encoding="UTF-8"?>'
+ to_encode['text'] = head + xml
+ to_encode['sync_id'] = sync_id
+ headers = {'Content-type': 'application/vnd.syncml+xml'}
+
+ #XXX bad hack for synchronization with erp5
if to_url.find('readResponse')<0:
to_url = to_url + '/portal_synchronizations/readResponse'
- request = urllib2.Request(url=to_url,data=encoded)
- #result = urllib2.urlopen(request).read()
+ encoded = urllib.urlencode(to_encode)
+ data=encoded
+ request = urllib2.Request(url=to_url, data=data)
+
+#XXX only to synchronize with other server than erp5 (must be improved):
+# data=head+xml
+# request = urllib2.Request(to_url, data, headers)
try:
result = urllib2.urlopen(request).read()
except socket.error, msg:
@@ -898,10 +913,12 @@
sync_id=sync_id, xml=xml, domain=domain)
LOG('sendHttpResponse, socket ERROR:',0,msg)
return
-
-
+ except urllib2.URLError, msg:
+ LOG("sendHttpResponse, can't open url %s :" % to_url, 0, msg)
+ return
+
+
#LOG('sendHttpResponse, before result, domain:',0,domain)
- #LOG('sendHttpResponse, result:',0,result)
if domain is not None:
if domain.domain_type == self.SUB:
gpg_key = domain.getGPGKey()
@@ -929,7 +946,7 @@
if len(message_list) == 0:
for subscription in self.getSubscriptionList():
#LOG('sync, subcription:',0,subscription)
- self.activate(activity='RAMQueue').SubSync(subscription.getTitle())
+ self.activate(activity='RAMQueue').SubSync(subscription)
security.declarePublic('readResponse')
def readResponse(self, text=None, sync_id=None, to_url=None, from_url=None):
@@ -937,9 +954,6 @@
We will look at the url and we will see if we need to send mail, http
response, or just copy to a file.
"""
- #LOG('readResponse, ',0,'starting')
- #LOG('readResponse, self.getPhysicalPath: ',0,self.getPhysicalPath())
- #LOG('readResponse, sync_id: ',0,sync_id)
# Login as a manager to make sure we can create objects
uf = self.acl_users
user = uf.getUserById('syncml').__of__(uf)
@@ -994,9 +1008,9 @@
for subscription in self.getSubscriptionList():
if subscription.getSubscriptionUrl()==url and \
subscription.getTitle()==sync_id:
- result = self.activate(activity='RAMQueue').SubSync(sync_id,
- text)
- #result = self.SubSync(sync_id,xml)
+ result = self.activate(activity='RAMQueue').SubSync(\
+ self.getSubscription(sync_id), text)
+ #result = self.SubSync(self.getSubscription(sync_id),xml)
# we use from only if we have a file
elif isinstance(from_url, str):
@@ -1041,24 +1055,4 @@
conduit_object = getattr(conduit_module, conduit)()
return conduit_object.addNode(**kw)
-# security.declarePrivate('notify_sync')
-# def notify_sync(self, event_type, object, infos):
-# """Notification from the event service.
-#
-# # XXX very specific to cps
-#
-# Called when an object is added/deleted/modified.
-# Update the date of sync
-# """
-# from Products.CPSCore.utils import _isinstance
-# from Products.CPSCore.ProxyBase import ProxyBase
-#
-# if event_type in ('sys_modify_object',
-# 'modify_object'):
-# if not(_isinstance(object, ProxyBase)):
-# repotool = getToolByName(self, 'portal_repository')
-# if repotool.isObjectInRepository(object):
-# object_id = object.getId()
-
-
InitializeClass( SynchronizationTool )
Modified: erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py (original)
+++ erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py Mon Jun 25 17:22:32 2007
@@ -30,7 +30,7 @@
from Products.ERP5SyncML.SyncCode import SyncCode
from Products.ERP5SyncML.Subscription import Signature
from DateTime import DateTime
-from cStringIO import StringIO
+from StringIO import StringIO
from xml.dom.ext import PrettyPrint
import random
from zLOG import LOG
@@ -49,6 +49,11 @@
class Parse:
def __init__(self, *args, **kw):
raise ImportError, "Sorry, it was not possible to import Ft library"
+
+try:
+ from base64 import b16encode, b16decode
+except ImportError:
+ from base64 import encodestring as b16encode, decodestring as b16decode
class XMLSyncUtilsMixin(SyncCode):
@@ -79,8 +84,8 @@
if dataCred not in (None, ''):
xml(' <Cred>\n')
xml(' <Meta>\n')
- xml(' <Format>%s</Format>\n' % authentication_format)
- xml(' <Type>%s</Type>\n' % authentication_type)
+ xml(" <Format xmlns='syncml:metinf'>%s</Format>\n" % authentication_format)
+ xml(" <Type xmlns='syncml:metinf'>%s</Type>\n" % authentication_type)
xml(' </Meta>\n')
xml(' <Data>%s</Data>\n' % dataCred)
xml(' </Cred>\n')
@@ -142,18 +147,37 @@
xml_a = ''.join(xml_list)
return xml_a
- def SyncMLConfirmation(self, cmd_id, target_ref, sync_code, cmd):
+ def SyncMLConfirmation(self, cmd_id=None, target_ref=None, cmd=None,
+ sync_code=None, msg_ref=None, cmd_ref=None, source_ref=None,
+ remote_xml=None):
"""
This is used in order to confirm that an object was correctly
synchronized
"""
+ if remote_xml is not None :
+ msg_ref=remote_xml.xpath("string(//MsgID)").encode('utf-8')
+ cmd_ref=remote_xml.xpath("string(.//CmdID)").encode('utf-8')
+ target_ref=remote_xml.xpath("string(.//Target/LocURI)").encode('utf-8')
+ source_ref=remote_xml.xpath("string(.//Source/LocURI)").encode('utf-8')
+
xml_list = []
xml = xml_list.append
xml(' <Status>\n')
- xml(' <CmdID>%s</CmdID>\n' % cmd_id)
- xml(' <TargetRef>%s</TargetRef>\n' % target_ref)
- xml(' <Cmd>%s</Cmd>\n' % cmd)
- xml(' <Data>%s</Data>\n' % sync_code)
+ #here there is a lot of test to keep compatibility with older call
+ if cmd_id not in (None,'') :
+ xml(' <CmdID>%s</CmdID>\n' % cmd_id)
+ if msg_ref not in (None,''):
+ xml(' <MsgRef>%s</MsgRef>\n' % msg_ref)
+ if cmd_ref not in (None,''):
+ xml(' <CmdRef>%s</CmdRef>\n' %cmd_ref)
+ if cmd not in (None,''):
+ xml(' <Cmd>%s</Cmd>\n' % cmd)
+ if target_ref not in (None,''):
+ xml(' <TargetRef>%s</TargetRef>\n' % target_ref)
+ if source_ref not in (None,''):
+ xml(' <SourceRef>%s</SourceRef>\n' % source_ref)
+ if sync_code not in (None,''):
+ xml(' <Data>%s</Data>\n' % sync_code)
xml(' </Status>\n')
xml_a = ''.join(xml_list)
return xml_a
@@ -172,14 +196,95 @@
xml(' <SourceRef>%s</SourceRef>\n' % source_ref)
xml(' <Chal>\n')
xml(' <Meta>\n')
- xml(' <Format>%s</Format>\n' % auth_format)
- xml(' <Type>%s</Type>\n' % auth_type)
+ xml(" <Format xmlns='syncml:metinf'>%s</Format>\n" % auth_format)
+ xml(" <Type xmlns='syncml:metinf'>%s</Type>\n" % auth_type)
xml(' </Meta>\n')
xml(' </Chal>\n')
xml(' <Data>%s</Data>\n' % str(data_code))
xml(' </Status>\n')
xml_a = ''.join(xml_list)
return xml_a
+
+ def SyncMLPut(self, cmd_id, subscription):
+ """
+ this is used to inform the server of the CTType version supported
+ """
+ from Products.ERP5SyncML import Conduit
+ # Import the conduit and get it
+ conduit_name = subscription.getConduit()
+ if conduit_name.startswith('Products'):
+ path = conduit_name
+ conduit_name = conduit_name.split('.')[-1]
+ conduit_module = __import__(path, globals(), locals(), [''])
+ conduit = getattr(conduit_module, conduit_name)()
+ else:
+ conduit_module = __import__('.'.join([Conduit.__name__, conduit_name]),
+ globals(), locals(), [''])
+ conduit = getattr(conduit_module, conduit_name)()
+ #if the conduit support the SyncMLPut :
+ if hasattr(conduit, 'getCapabilitiesCTType') and \
+ hasattr(conduit, 'getCapabilitiesVerCTList') and \
+ hasattr(conduit, 'getPreferedCapabilitieVerCT'):
+ xml_list = []
+ xml = xml_list.append
+ if conduit.getCapabilitiesVerCTList() not in ([], None):
+ xml(' <Put>\n')
+ xml(' <CmdID>%s</CmdID>\n' % cmd_id)
+ xml(' <Meta>\n')
+ xml(' <Type>application/vnd.syncml-devinf+xml</Type>\n');
+ xml(' </Meta>\n')
+ xml(' <Item>\n')
+ xml(' <Source>\n')
+ xml(' <LocURI>./devinf11</LocURI>\n')
+ xml(' </Source>\n')
+ xml(' <Data>\n')
+ xml(' <DevInf>\n')
+ xml(' <VerDTD>1.1</VerDTD>\n')
+ xml(' <Man>Fabien MORIN</Man>\n')
+ xml(' <Mod>ERP5SyncML</Mod>\n')
+ xml(' <OEM>Open Source</OEM>\n')
+ xml(' <SwV>0.1</SwV>\n')
+ xml(' <DevID>%s</DevID>\n' % subscription.getSubscriptionUrl())
+ xml(' <DevTyp>workstation</DevTyp>\n')
+ xml(' <UTC/>\n')
+ xml(' <DataStore>\n')
+ xml(' <SourceRef>%s</SourceRef>\n' % subscription.getSourceURI())
+ xml(' <Rx-Pref>\n')
+ xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
+ xml(' <VerCT>%s</VerCT>\n' % conduit.getPreferedCapabilitieVerCT())
+ xml(' </Rx-Pref>\n')
+ for rx_version in conduit.getCapabilitiesVerCTList():
+ xml(' <Rx>\n')
+ xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
+ xml(' <VerCT>%s</VerCT>\n' % rx_version)
+ xml(' </Rx>\n')
+
+ xml(' <Tx-Pref>\n')
+ xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
+ xml(' <VerCT>%s</VerCT>\n' % conduit.getPreferedCapabilitieVerCT())
+ xml(' </Tx-Pref>\n')
+ for tx_version in conduit.getCapabilitiesVerCTList():
+ xml(' <Tx>\n')
+ xml(' <CTType>%s</CTType>\n' % conduit.getCapabilitiesCTType())
+ xml(' <VerCT>%s</VerCT>\n' % rx_version)
+ xml(' </Tx>\n')
+
+ xml(' <SyncCap>\n')
+ xml(' <SyncType>2</SyncType>\n')
+ xml(' <SyncType>1</SyncType>\n')
+ xml(' <SyncType>4</SyncType>\n')
+ xml(' <SyncType>6</SyncType>\n')
+ xml(' </SyncCap>\n')
+
+ xml(' </DataStore>\n')
+ xml(' </DevInf>\n')
+ xml(' </Data>\n')
+ xml(' </Item>\n')
+ xml(' </Put>\n')
+ xml_a = ''.join(xml_list)
+ return xml_a
+ return ''
+
def sendMail(self, fromaddr, toaddr, id_sync, msg):
"""
@@ -213,9 +318,14 @@
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % gid)
xml(' </Source>\n')
- xml(' <Data>\n')
- xml(xml_string)
- xml(' </Data>\n')
+ if media_type == self.MEDIA_TYPE['TEXT_XML']:
+ xml(' <Data>')
+ xml(xml_string)
+ xml('</Data>\n')
+ else:
+ xml(' <Data><![CDATA[')
+ xml(xml_string)
+ xml('\n]]></Data>\n')
if more_data == 1:
xml(' <MoreData/>\n')
xml(' </Item>\n')
@@ -258,7 +368,7 @@
xml(' <Source>\n')
xml(' <LocURI>%s</LocURI>\n' % str(gid))
xml(' </Source>\n')
- xml(' <Data>\n')
+ xml(' <Data>')
xml(xml_string)
xml(' </Data>\n')
if more_data == 1:
@@ -308,7 +418,6 @@
xml = xml_method()
else:
raise ValueError, "Sorry the script or method was not found"
- #LOG('getXMLObject', 0, 'xml_mapping:%s, xml:%s, object:%s xml_method:%s' % (xml_mapping, xml, object, xml_method))
return xml
def getSessionId(self, xml):
@@ -370,7 +479,10 @@
"""
Return the value of the alert code inside the xml_stream
"""
- return xml.xpath('string(TargetRef)')
+ status = xml.xpath('string(TargetRef)')
+ if isinstance(status, unicode):
+ status = status.encode('utf-8')
+ return status
def getStatusCode(self, xml):
"""
@@ -386,7 +498,10 @@
Return the value of the command inside the xml_stream
"""
if xml.nodeName=='Status':
- return xml.xpath('string(//Status/Cmd)')
+ cmd = xml.xpath('string(//Status/Cmd)')
+ if isinstance(cmd, unicode):
+ cmd = cmd.encode('utf-8')
+ return cmd
else:
return None
@@ -399,8 +514,8 @@
data=''
first_node = xml.childNodes[0]
- format = first_node.xpath('string(/SyncML/SyncHdr/Cred/Meta/Format)')
- type = first_node.xpath('string(/SyncML/SyncHdr/Cred/Meta/Type)')
+ format = first_node.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Format'])")
+ type = first_node.xpath("string(/SyncML/SyncHdr/Cred/Meta/*[local-name() = 'Type'])")
data = first_node.xpath('string(/SyncML/SyncHdr/Cred/Data)')
format = format.encode('utf-8')
@@ -420,12 +535,12 @@
"""
return the chalenge information : format and type
"""
- format=''
- type=''
+ format=None
+ type=None
first_node = xml.childNodes[0]
- format = first_node.xpath('string(/SyncML/SyncBody/Status/Chal/Meta/Format)')
- type = first_node.xpath('string(/SyncML/SyncBody/Status/Chal/Meta/Type)')
+ format = first_node.xpath("string(//*[local-name() = 'Format'])")
+ type = first_node.xpath("string(//*[local-name() = 'Type'])")
format = format.encode('utf-8')
type = type.encode('utf-8')
@@ -468,7 +583,7 @@
sync = True
return sync
- def CheckStatus(self, xml_stream):
+ def checkStatus(self, xml_stream):
"""
Check if there's a Status section in the xml_xtream
"""
@@ -477,43 +592,34 @@
status = True
return status
- def getNextSyncAction(self, xml_stream, last_action):
- """
- It goes throw actions in the Sync section of the SyncML file,
- then it returns the next action (could be "add", "replace",
- "delete").
- """
- first_node = xml_stream.childNodes[0]
- client_body = first_node.childNodes[3]
- if client_body.nodeName != "SyncBody":
- print "This is not a SyncML Body"
- next_action = None
- for subnode in client_body.childNodes:
- if subnode.nodeType == subnode.ELEMENT_NODE and \
- subnode.nodeName == "Sync":
- # if we didn't use this method before
- if last_action == None and len(subnode.childNodes) > 1:
- next_action = subnode.childNodes[1]
- else:
- found = None
- for subnode2 in subnode.childNodes:
- if subnode2.nodeType == subnode.ELEMENT_NODE and \
- subnode2 != last_action and found is None:
- pass
- elif subnode2.nodeType == subnode.ELEMENT_NODE and \
- subnode2 == last_action and found is None:
- found = 1
- elif subnode2.nodeType == subnode.ELEMENT_NODE and \
- found is not None:
- next_action = subnode2
- break
- return next_action
+ def getSyncActionList(self, xml_stream):
+ """
+ return the list of the action (could be "add", "replace", "delete").
+ """
+ return xml_stream.xpath('//Add|//Delete|//Replace')
+
+ def getSyncBodyStatusList(self, xml_stream):
+ """
+ return the list of dictionary corredponding to the data of each status bloc
+ the data are : cmd, code and source
+ """
+ status_list = []
+ xml = xml_stream.xpath('//Status')
+ for status in xml:
+ tmp_dict = {}
+ tmp_dict['cmd'] = status.xpath('string(./Cmd)').encode('utf-8')
+ tmp_dict['code'] = status.xpath('string(./Data)').encode('utf-8')
+ tmp_dict['source'] = status.xpath('string(./SourceRef)').encode('utf-8')
+ tmp_dict['target'] = status.xpath('string(./TargetRef)').encode('utf-8')
+ status_list.append(tmp_dict)
+
+ return status_list
def getNextSyncBodyStatus(self, xml_stream, last_status):
"""
- It goes throw actions in the Sync section of the SyncML file,
- then it returns the next action (could be "add", "replace",
- "delete").
+ It goes throw actions in the Sync section of the SyncML file,
+ then it returns the next action (could be "add", "replace",
+ "delete").
"""
first_node = xml_stream.childNodes[0]
client_body = first_node.childNodes[3]
@@ -539,16 +645,19 @@
"""
return the section data in text form, it's usefull for the VCardConduit
"""
- return action.xpath('string(Item/Data)')
+ data = action.xpath('string(Item/Data)')
+ if isinstance(data, unicode):
+ data = data.encode('utf-8')
+ return data
def getDataSubNode(self, action):
"""
Return the node starting with <object....> of the action
"""
- if action.xpath('Item/Data') not in ([], None):
- data_node = action.xpath('Item/Data')[0]
- if data_node.hasChildNodes() and data_node.childNodes.__len__()>1:
- return data_node.childNodes[1]
+ if action.xpath('.//Item/Data') not in ([], None):
+ data_node = action.xpath('.//Item/Data')[0]
+ if data_node.hasChildNodes():
+ return data_node.childNodes[0]
return None
def getPartialData(self, action):
@@ -575,20 +684,16 @@
return None
- def getActionId(self, action):
+ def getActionId(self, action, action_name):
"""
Return the rid of the object described by the action
"""
- for subnode in action.childNodes:
- if subnode.nodeType == subnode.ELEMENT_NODE and \
- subnode.nodeName == 'Item':
- for subnode2 in subnode.childNodes:
- if subnode2.nodeType == subnode2.ELEMENT_NODE and \
- subnode2.nodeName == 'Source':
- for subnode3 in subnode2.childNodes:
- if subnode3.nodeType == subnode3.ELEMENT_NODE and \
- subnode3.nodeName == 'LocURI':
- return str(subnode3.childNodes[0].data)
+ id = action.xpath('string(.//Item/Source/LocURI)')
+ if id in (None, ''):
+ id = action.xpath('string(.//Item/Target/LocURI)')
+ if isinstance(id, unicode):
+ id = id.encode('utf-8')
+ return id
def checkActionMoreData(self, action):
"""
@@ -602,7 +707,10 @@
"""
Return the type of the object described by the action
"""
- return action.xpath('string(Meta/Type)')
+ action_type = action.xpath('string(Meta/Type)')
+ if isinstance(action_type, unicode):
+ action_type = action_type.encode('utf-8')
+ return action_type
def getElementNodeList(self, node):
"""
@@ -647,18 +755,21 @@
#object_gid = domain.getGidFromObject(object)
local_gid_list = map(lambda x: domain.getGidFromObject(x),object_list)
# Objects to remove
- #for object_id in id_list:
for object_gid in subscriber.getGidList():
if not (object_gid in local_gid_list):
# This is an object to remove
- signature = subscriber.getSignature(object_gid)
+ signature = subscriber.getSignatureFromGid(object_gid)
if signature.getStatus()!=self.PARTIAL: # If partial, then we have a signature
# but no local object
xml_object = signature.getXML()
if xml_object is not None: # This prevent to delete an object that
# we were not able to create
+ rid = signature.getRid()
+ if rid != None:
+ object_gid=rid #to use the remote id if it exist
syncml_data += self.deleteXMLObject(xml_object=signature.getXML()\
or '', object_gid=object_gid,cmd_id=cmd_id)
+ cmd_id += 1
local_gid_list = []
#for object in domain.getObjectList():
@@ -683,9 +794,10 @@
#LOG('getSyncMLData',0,'xml_mapping: %s' % str(domain.xml_mapping))
#LOG('getSyncMLData',0,'code: %s' % str(self.getAlertCode(remote_xml)))
#LOG('getSyncMLData',0,'gid_list: %s' % str(local_gid_list))
+ #LOG('getSyncMLData',0,'subscriber.getGidList: %s' % subscriber.getGidList())
#LOG('getSyncMLData',0,'hasSignature: %s' % str(subscriber.hasSignature(object_gid)))
#LOG('getSyncMLData',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC))
- signature = subscriber.getSignature(object_gid)
+ signature = subscriber.getSignatureFromGid(object_gid)
# Here we first check if the object was modified or not by looking at dates
if signature is not None:
@@ -702,7 +814,10 @@
xml_object = self.getXMLObject(object=object,
xml_mapping=domain.xml_mapping)
xml_string = xml_object
- signature = Signature(gid=object_gid,id=object.getId(),object=object)
+ if isinstance(xml_string, unicode):
+ xml_string = xml_object.encode('utf-8')
+ gid = subscriber.getGidFromObject(object)
+ signature = Signature(id=gid,object=object)
signature.setTempXML(xml_object)
if xml_string.count('\n') > self.MAX_LINES:
if xml_string.find('--') >= 0: # This make comment fails, so we need to replace
@@ -714,16 +829,18 @@
while i < self.MAX_LINES:
short_string += rest_string[:rest_string.find('\n')+1]
rest_string = xml_string[len(short_string):]
- #LOG('XMLSyncUtils',0,'rest_string: %s' % str(rest_string))
i += 1
#LOG('getSyncMLData',0,'setPartialXML with: %s' % str(rest_string))
signature.setPartialXML(rest_string)
- status =self.PARTIAL
+ status = self.PARTIAL
signature.setAction('Add')
xml_string = '<!--' + short_string + '-->'
+ gid = signature.getRid()#in fisrt, we try with rid if there is one
+ if gid == None:
+ gid = signature.getGid()
syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,
- gid=object_gid, xml_string=xml_string, more_data=more_data,
- media_type=subscriber.getMediaType())
+ gid=gid, xml_string=xml_string,
+ more_data=more_data, media_type=subscriber.getMediaType())
cmd_id += 1
signature.setStatus(status)
subscriber.addSignature(signature)
@@ -734,8 +851,9 @@
#LOG('getSyncMLData',0,'checkMD5: %s' % str(signature.checkMD5(xml_object)))
#LOG('getSyncMLData',0,'getStatus: %s' % str(signature.getStatus()))
if signature.getStatus()==self.PUB_CONFLICT_MERGE:
- xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id,
- self.CONFLICT_MERGE,'Replace')
+ xml_confirmation += self.SyncMLConfirmation(cmd_id=cmd_id,
+ source_ref=signature.getGid(), sync_code=self.CONFLICT_MERGE,
+ cmd='Replace')
set_synchronized = 1
if not signature.checkMD5(xml_object):
set_synchronized = 0
@@ -762,9 +880,12 @@
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
xml_string = self.getXMLObject(object=object,
xml_mapping=domain.xml_mapping)
+ gid = signature.getRid()#in fisrt, we try with rid if there is one
+ if gid == None:
+ gid = signature.getGid()
syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object,
- gid=object_gid, xml_string=xml_string, more_data=more_data,
- media_type=subscriber.getMediaType())
+ gid=gid, xml_string=xml_string,
+ more_data=more_data, media_type=subscriber.getMediaType())
cmd_id += 1
signature.setTempXML(xml_object)
# Now we can apply the xupdate from the subscriber
@@ -780,6 +901,7 @@
signature.setTempXML(xml_object)
if set_synchronized: # We have to do that after this previous update
# We should not have this case when we are in CONFLICT_MERGE
+
signature.setStatus(self.SYNCHRONIZED)
elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN:
# We have decided to apply the update
@@ -789,8 +911,9 @@
xml_update = signature.getPartialXML()
conduit.updateNode(xml=signature.getPartialXML(), object=object,
previous_xml=signature.getXML(),force=1)
- xml_confirmation += self.SyncMLConfirmation(cmd_id,object_gid,
- self.CONFLICT_CLIENT_WIN,'Replace')
+ xml_confirmation += self.SyncMLConfirmation(cmd_id=cmd_id,
+ target_ref=object_gid, sync_code=self.CONFLICT_CLIENT_WIN,
+ cmd='Replace')
signature.setStatus(self.SYNCHRONIZED)
elif signature.getStatus()==self.PARTIAL:
xml_string = signature.getPartialXML()
@@ -815,13 +938,19 @@
xml_mapping=domain.xml_mapping)
#LOG('xml_string =', 0, xml_string)
if signature.getAction()=='Replace':
+ gid = signature.getRid()#in fisrt, we try with rid if there is one
+ if gid == None:
+ gid = signature.getGid()
syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object,
- gid=object_gid, xml_string=xml_string, more_data=more_data,
+ gid=gid, xml_string=xml_string, more_data=more_data,
media_type=subscriber.getMediaType())
elif signature.getAction()=='Add':
+ gid = signature.getRid()#in fisrt, we try with rid if there is one
+ if gid == None:
+ gid = signature.getGid()
syncml_data += self.addXMLObject(cmd_id=cmd_id, object=object,
- gid=object_gid, xml_string=xml_string, more_data=more_data,
- media_type=subscriber.getMediaType())
+ gid=gid, xml_string=xml_string,
+ more_data=more_data, media_type=subscriber.getMediaType())
return (syncml_data,xml_confirmation,cmd_id)
def applyActionList(self, domain=None, subscriber=None,destination_path=None,
@@ -830,27 +959,42 @@
This just look to a list of action to do, then id applies
each action one by one, thanks to a conduit
"""
- next_action = self.getNextSyncAction(remote_xml, None)
xml_confirmation = ''
has_next_action = 0
- if next_action is not None:
- has_next_action = 1
- while next_action != None:
+ for action in self.getSyncActionList(remote_xml):
conflict_list = []
status_code = self.SUCCESS
# Thirst we have to check the kind of action it is
- partial_data = self.getPartialData(next_action)
- object_gid = self.getActionId(next_action)
- signature = subscriber.getSignature(object_gid)
- object = domain.getObjectFromGid(object_gid)
+ partial_data = self.getPartialData(action)
+ rid = self.getActionId(action, action.nodeName)
+ if action.nodeName != 'Delete':
+ if hasattr(conduit, 'getGidFromXML'):
+ gid = b16encode(conduit.getGidFromXML(self.getDataText(action)))
+ else:
+ gid=rid
+ else:
+ gid=rid
+ object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=gid)
+ signature = subscriber.getSignatureFromGid(gid)
+ if signature != None and rid != gid:
+ #in this case, the object was created on another subscriber than erp5
+ # and we should save it's remote id
+ signature.setRid(rid)
+ LOG('gid == rid ?', 0, 'gid=%s, rid=%s' % (gid, rid))
+ object = subscriber.getObjectFromGid(gid)
if signature == None:
#LOG('applyActionList, signature is None',0,signature)
- signature = Signature(gid=object_gid, status=self.NOT_SYNCHRONIZED,
- object=object).__of__(subscriber)
+ if gid == rid:
+ signature = Signature(id=gid, status=self.NOT_SYNCHRONIZED,
+ object=object).__of__(subscriber)
+ else:
+ signature = Signature(rid=rid, id=gid, status=self.NOT_SYNCHRONIZED,
+ object=object).__of__(subscriber)
+ signature.setObjectId(object_id)
subscriber.addSignature(signature)
force = signature.getForce()
#LOG('applyActionList',0,'object: %s' % repr(object))
- if self.checkActionMoreData(next_action) == 0:
+ if self.checkActionMoreData(action) == 0:
data_subnode = None
if partial_data != None:
signature_partial_xml = signature.getPartialXML()
@@ -859,20 +1003,19 @@
else:
data_subnode = partial_data
#LOG('SyncModif',0,'data_subnode: %s' % data_subnode)
- #data_subnode = FromXml(data_subnode)
if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_XML']:
data_subnode = Parse(data_subnode)
data_subnode = data_subnode.childNodes[0] # Because we just created a new xml
# document, with childNodes[0] a DocumentType and childNodes[1] the Element Node
else:
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
- data_subnode = self.getDataText(next_action)
+ data_subnode = self.getDataText(action)
else:
- data_subnode = self.getDataSubNode(next_action)
- if next_action.nodeName == 'Add':
+ data_subnode = self.getDataSubNode(action)
+ if action.nodeName == 'Add':
# Then store the xml of this new subobject
if object is None:
- object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=object_gid)
+ object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=gid)
#if object_id is not None:
add_data = conduit.addNode(xml=data_subnode,
object=destination_path, object_id=object_id)
@@ -880,14 +1023,14 @@
conflict_list += add_data['conflict_list']
# Retrieve directly the object from addNode
object = add_data['object']
- #LOG('XMLSyncUtils, in ADD add_data',0,add_data)
+ LOG('XMLSyncUtils, in ADD add_data',0,add_data)
if object is not None:
signature.setPath(object.getPhysicalPath())
- #LOG('applyActionList',0,'object after add: %s' % repr(object))
+ signature.setObjectId(object.getId())
else:
#Object was retrieve but need to be updated without recreated
#usefull when an object is only deleted by workflow.
- object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=object_gid)
+ object_id = domain.generateNewIdWithGenerator(object=destination_path,gid=gid)
add_data = conduit.addNode(xml=data_subnode,
object=destination_path,
object_id=object_id,
@@ -901,17 +1044,20 @@
#if mapping is not None:
# xml_object = mapping()
signature.setStatus(self.SYNCHRONIZED)
- signature.setId(object.getId())
+ #signature.setId(object.getId())
signature.setPath(object.getPhysicalPath())
signature.setXML(xml_object)
- xml_confirmation +=\
- self.SyncMLConfirmation(cmd_id,object_gid,self.SUCCESS,'Add')
+ xml_confirmation += self.SyncMLConfirmation(
+ cmd_id=cmd_id,
+ cmd='Add',
+ sync_code=self.ITEM_ADDED,
+ remote_xml=action)
cmd_id +=1
- elif next_action.nodeName == 'Replace':
+ elif action.nodeName == 'Replace':
#LOG('SyncModif',0,'object: %s will be updated...' % str(object))
if object is not None:
#LOG('SyncModif',0,'object: %s will be updated...' % object.id)
- signature = subscriber.getSignature(object_gid)
+ signature = subscriber.getSignatureFromGid(gid)
#LOG('SyncModif',0,'previous signature: %s' % str(signature))
previous_xml = signature.getXML()
#LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
@@ -934,8 +1080,11 @@
signature.setPartialXML(data_subnode_string)
elif not simulate:
signature.setStatus(self.SYNCHRONIZED)
- xml_confirmation += self.SyncMLConfirmation(cmd_id,
- object_gid,status_code,'Replace')
+ xml_confirmation += self.SyncMLConfirmation(\
+ cmd_id=cmd_id,
+ cmd='Replace',
+ sync_code=status_code,
+ remote_xml=action)
cmd_id +=1
if simulate:
# This means we are on the publiher side and we want to store
@@ -947,17 +1096,22 @@
#LOG('applyActionList, subscriber_xupdate:',0,data_subnode_string)
signature.setSubscriberXupdate(data_subnode_string)
- elif next_action.nodeName == 'Delete':
+ elif action.nodeName == 'Delete':
object_id = signature.getId()
if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
- data_subnode = self.getDataText(next_action)
+ data_subnode = self.getDataText(action)
else:
- data_subnode = self.getDataSubNode(next_action)
- conduit.deleteNode(xml=data_subnode, object=destination_path,
- object_id=object_id)
- subscriber.delSignature(object_gid)
- xml_confirmation += self.SyncMLConfirmation(cmd_id,
- object_gid,status_code,'Delete')
+ data_subnode = self.getDataSubNode(action)
+ if subscriber.getObjectFromGid(object_id) not in (None, ''):
+ #if the object exist:
+ conduit.deleteNode(xml=data_subnode, object=destination_path,
+ object_id=subscriber.getObjectFromGid(object_id).getId())
+ subscriber.delSignature(gid)
+ xml_confirmation += self.SyncMLConfirmation(
+ cmd_id=cmd_id,
+ cmd='Delete',
+ sync_code=status_code,
+ remote_xml=action)
else: # We want to retrieve more data
signature.setStatus(self.PARTIAL)
#LOG('SyncModif',0,'setPartialXML: %s' % str(previous_partial))
@@ -967,13 +1121,17 @@
signature.setPartialXML(previous_partial)
#LOG('SyncModif',0,'previous_partial: %s' % str(previous_partial))
#LOG('SyncModif',0,'waiting more data for :%s' % signature.getId())
- xml_confirmation += self.SyncMLConfirmation(cmd_id, object_gid,
- self.WAITING_DATA, next_action.nodeName)
+ #xml_confirmation += self.SyncMLConfirmation(cmd_id, object_gid,
+ # self.WAITING_DATA, action.nodeName)
+ xml_confirmation += self.SyncMLConfirmation(\
+ cmd_id=cmd_id,
+ cmd=action.nodeName,
+ sync_code=self.WAITING_DATA,
+ remote_xml=action)
if conflict_list != [] and signature is not None:
# We had a conflict
signature.setStatus(self.CONFLICT)
- next_action = self.getNextSyncAction(remote_xml, next_action)
return (xml_confirmation,has_next_action,cmd_id)
def applyStatusList(self, subscriber=None,remote_xml=None):
@@ -981,42 +1139,44 @@
This read a list of status list (ie syncml confirmations).
This method have to change status codes on signatures
"""
- next_status = self.getNextSyncBodyStatus(remote_xml, None)
- #LOG('applyStatusList, next_status',0,next_status)
- # We do not want the first one
- next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
+ status_list = self.getSyncBodyStatusList(remote_xml)
has_status_list = 0
- if next_status is not None:
- has_status_list = 1
destination_waiting_more_data = 0
- while next_status != None:
- object_gid = self.getStatusTarget(next_status)
- status_code = self.getStatusCode(next_status)
- status_cmd = self.getStatusCommand(next_status)
- signature = subscriber.getSignature(object_gid)
- #LOG('SyncModif',0,'next_status: %s' % str(status_code))
- if status_cmd in ('Add','Replace'):
- if status_code == self.CHUNK_OK:
- destination_waiting_more_data = 1
- signature.setStatus(self.PARTIAL)
- elif status_code == self.CONFLICT:
- signature.setStatus(self.CONFLICT)
- elif status_code == self.CONFLICT_MERGE:
- # We will have to apply the update, and we should not care about conflicts
- # so we have to force the update
- signature.setStatus(self.NOT_SYNCHRONIZED)
- signature.setForce(1)
- elif status_code == self.CONFLICT_CLIENT_WIN:
- # The server was agree to apply our updates, nothing to do
- signature.setStatus(self.SYNCHRONIZED)
- elif status_code == self.SUCCESS:
- signature.setStatus(self.SYNCHRONIZED)
- elif status_cmd == 'Delete':
- if status_code == self.SUCCESS:
- subscriber.delSignature(object_gid)
- next_status = self.getNextSyncBodyStatus(remote_xml, next_status)
+ if status_list != []:
+ for status in status_list:
+ status_cmd = status['cmd']
+ #if status_cmd in ('Delete'):
+ # object_gid = status['target']
+ #else:
+ object_gid = status['source']
+ status_code = int(status['code'])
+ if status_cmd in ('Add','Replace'):
+ has_status_list = 1
+ signature = subscriber.getSignatureFromGid(object_gid)
+ if signature == None:
+ signature = subscriber.getSignatureFromRid(object_gid)
+ if status_code == self.CHUNK_OK:
+ destination_waiting_more_data = 1
+ signature.setStatus(self.PARTIAL)
+ elif status_code == self.CONFLICT:
+ signature.setStatus(self.CONFLICT)
+ elif status_code == self.CONFLICT_MERGE:
+ # We will have to apply the update, and we should not care
+ # about conflicts, so we have to force the update
+ signature.setStatus(self.NOT_SYNCHRONIZED)
+ signature.setForce(1)
+ elif status_code == self.CONFLICT_CLIENT_WIN:
+ # The server was agree to apply our updates, nothing to do
+ signature.setStatus(self.SYNCHRONIZED)
+ elif status_code in (self.SUCCESS, self.ITEM_ADDED):
+ signature.setStatus(self.SYNCHRONIZED)
+ elif status_cmd == 'Delete':
+ if status_code == self.SUCCESS:
+ signature = subscriber.getSignatureFromGid(object_gid)
+ if signature == None:
+ signature = subscriber.getSignatureFromRid(object_gid)
+ subscriber.delSignature(signature.getGid())
return (destination_waiting_more_data, has_status_list)
-
class XMLSyncUtils(XMLSyncUtilsMixin):
@@ -1121,7 +1281,7 @@
xml_list = []
xml = xml_list.append
xml('<SyncML>\n')
-
+
# syncml header
if domain.domain_type == self.PUB:
xml(self.SyncMLHeader(subscriber.getSessionId(),
@@ -1130,54 +1290,114 @@
elif domain.domain_type == self.SUB:
xml(self.SyncMLHeader(domain.getSessionId(), domain.incrementMessageId(),
domain.getPublicationUrl(), domain.getSubscriptionUrl()))
-
-
- cmd_id += 1
+
# Add or replace objects
syncml_data = ''
- # Now we have to send our own modifications
- if has_next_action == 0 and not \
- (domain.domain_type==self.SUB and alert_code==self.SLOW_SYNC):
- (syncml_data,xml_confirmation,cmd_id) = self.getSyncMLData(domain=domain,
- remote_xml=remote_xml,
- subscriber=subscriber,
- destination_path=destination_path,
- cmd_id=cmd_id,xml_confirmation=xml_confirmation,
- conduit=conduit)
# syncml body
xml(' <SyncBody>\n')
+
+ # status for SyncHdr
+ message_id = self.getMessageId(remote_xml)
+ xml(' <Status>\n')
+ xml(' <CmdID>%s</CmdID>\n' % cmd_id)
+ cmd_id += 1
+ xml(' <MsgRef>%s</MsgRef>\n' % message_id)
+ xml(' <CmdRef>0</CmdRef>\n') #to make reference to the SyncHdr, it's 0
+ xml(' <Cmd>SyncHdr</Cmd>\n')
+ xml(' <TargetRef>%s</TargetRef>\n' \
+ % remote_xml.xpath('string(//SyncHdr/Target/LocURI)').encode('utf-8'))
+ xml(' <SourceRef>%s</SourceRef>\n' \
+ % remote_xml.xpath('string(//SyncHdr/Source/LocURI)').encode('utf-8'))
+ xml(' <Data>200</Data>\n')
+ xml(' </Status>\n')
+
+ #list of element in the SyncBody bloc
+ syncbody_element_list = remote_xml.xpath('//SyncBody/*')
+
+ #add the status bloc corresponding to the receive command
+ for syncbody_element in syncbody_element_list:
+ if str(syncbody_element.nodeName) not in ('Status', 'Final', 'Replace'):
+ xml(' <Status>\n')
+ xml(' <CmdID>%s</CmdID>\n' % cmd_id)
+ cmd_id += 1
+ xml(' <MsgRef>%s</MsgRef>\n' % message_id)
+ xml(' <CmdRef>%s</CmdRef>\n' \
+ % syncbody_element.xpath('string(.//CmdID)').encode('utf-8'))
+ xml(' <Cmd>%s</Cmd>\n' % syncbody_element.nodeName.encode('utf-8'))
+
+ target_ref = syncbody_element.xpath('string(.//Target/LocURI)').encode('utf-8')
+ if target_ref not in (None, ''):
+ xml(' <TargetRef>%s</TargetRef>\n' % target_ref )
+ source_ref = syncbody_element.xpath('string(.//Source/LocURI)').encode('utf-8')
+ if source_ref not in (None, ''):
+ xml(' <SourceRef>%s</SourceRef>\n' % source_ref )
+
+ #xml(' <Data>%s</Data>\n' % subscriber.getSynchronizationType())
+ if syncbody_element.nodeName.encode('utf-8') == 'Add':
+ xml(' <Data>%s</Data>\n' % '201')
+ else:
+ xml(' <Data>%s</Data>\n' % '200')
+
+ # if syncbody_element.xpath('.//Item') not in ([], None, '') and\
+ # syncbody_element.xpath('.//Item.....'): #contient une ancre Next...
+
+ xml(' <Item>\n')
+ xml(' <Data>\n')
+ xml(" <Anchor xmlns='syncml:metinf'>\n")
+ xml(' <Next>%s</Next>\n' % subscriber.getNextAnchor())
+ xml(' </Anchor>\n')
+ xml(' </Data>\n')
+ xml(' </Item>\n')
+
+ xml(' </Status>\n')
+
+
destination_url = ''
- if domain.domain_type == self.PUB:
- subscriber.NewAnchor()
- destination_url = domain.getPublicationUrl()
- xml(self.SyncMLStatus(cmd_id, subscriber.getSubscriptionUrl(),
- domain.getDestinationPath(),
- subscriber.getSynchronizationType(),
- subscriber.getNextAnchor()))
- elif domain.domain_type == self.SUB:
- destination_url = domain.getPublicationUrl()
- xml(self.SyncMLStatus(cmd_id, domain.getPublicationUrl(),
- subscriber.getDestinationPath(),
- subscriber.getSynchronizationType(),
- subscriber.getNextAnchor()))
# alert message if we want more data
if destination_waiting_more_data == 1:
xml(self.SyncMLAlert(cmd_id, self.WAITING_DATA,
- destination_url,
- domain.getDestinationPath(),
+ subscriber.getTargetURI(),
+ subscriber.getSourceURI(),
subscriber.getLastAnchor(),
subscriber.getNextAnchor()))
# Now we should send confirmations
- xml(xml_confirmation)
+ cmd_id_before_getsyncmldata = cmd_id
+ (syncml_data,xml_confirmation,cmd_id) = self.getSyncMLData(domain=domain,
+ remote_xml=remote_xml,
+ subscriber=subscriber,
+ destination_path=destination_path,
+ cmd_id=cmd_id+1,xml_confirmation=xml_confirmation,
+ conduit=conduit)
if syncml_data != '':
xml(' <Sync>\n')
+ xml(' <CmdID>%s</CmdID>\n' % cmd_id_before_getsyncmldata)
+ if domain.domain_type == self.SUB:
+ if subscriber.getTargetURI() not in ('', None):
+ xml(' <Target>\n')
+ xml(' <LocURI>%s</LocURI>\n' % subscriber.getTargetURI())
+ xml(' </Target>\n')
+ if subscriber.getSourceURI() not in ('', None):
+ xml(' <Source>\n')
+ xml(' <LocURI>%s</LocURI>\n' % subscriber.getSourceURI())
+ xml(' </Source>\n')
+ elif domain.domain_type == self.PUB:
+ if domain.getTargetURI() not in ('', None):
+ xml(' <Target>\n')
+ xml(' <LocURI>%s</LocURI>\n' % domain.getTargetURI())
+ xml(' </Target>\n')
+ if domain.getSourceURI() not in ('', None):
+ xml(' <Source>\n')
+ xml(' <LocURI>%s</LocURI>\n' % domain.getSourceURI())
+ xml(' </Source>\n')
xml(syncml_data)
xml(' </Sync>\n')
+ xml(xml_confirmation)
xml(' <Final/>\n')
xml(' </SyncBody>\n')
xml('</SyncML>\n')
xml_a = ''.join(xml_list)
+
if domain.domain_type == self.PUB: # We always reply
subscriber.setLastSentMessage(xml_a)
self.sendResponse(from_url=domain.publication_url,
@@ -1186,7 +1406,7 @@
has_response = 1
elif domain.domain_type == self.SUB:
if self.checkAlert(remote_xml) or \
- (xml_confirmation,syncml_data)!=('','') or \
+ (xml_confirmation,syncml_data)!=('','') or \
has_status_list:
subscriber.setLastSentMessage(xml_a)
self.sendResponse(from_url=domain.subscription_url,
Modified: erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml Mon Jun 25 17:22:32 2007
@@ -70,6 +70,16 @@
<tr>
<td align="left" valign="top">
<div class="form-label">
+ Source URI
+ </label></div>
+ </td>
+ <td align="left" valign="top">
+ <input type="text" name="source_uri" size="40" value="<dtml-var getSourceURI>" />
+ </td>
+ </tr>
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-label">
Query
</label></div>
</td>
Modified: erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml Mon Jun 25 17:22:32 2007
@@ -80,6 +80,26 @@
<tr>
<td align="left" valign="top">
<div class="form-label">
+ Source URI
+ </label></div>
+ </td>
+ <td align="left" valign="top">
+ <input type="text" name="source_uri" size="40" value="<dtml-var getSourceURI>" />
+ </td>
+ </tr>
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-label">
+ Target URI
+ </label></div>
+ </td>
+ <td align="left" valign="top">
+ <input type="text" name="target_uri" size="40" value="<dtml-var getTargetURI>" />
+ </td>
+ </tr>
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-label">
Query
</label></div>
</td>
Modified: erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml Mon Jun 25 17:22:32 2007
@@ -61,6 +61,16 @@
</td>
<td align="left" valign="top">
<input type="text" name="destination_path" size="40" />
+ </td>
+ </tr>
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-label">
+ Source URI
+ </label></div>
+ </td>
+ <td align="left" valign="top">
+ <input type="text" name="source_uri" size="40" />
</td>
</tr>
<tr>
Modified: erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml Mon Jun 25 17:22:32 2007
@@ -71,6 +71,26 @@
</td>
<td align="left" valign="top">
<input type="text" name="destination_path" size="40" />
+ </td>
+ </tr>
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-label">
+ Source URI
+ </label></div>
+ </td>
+ <td align="left" valign="top">
+ <input type="text" name="source_uri" size="40" />
+ </td>
+ </tr>
+ <tr>
+ <td align="left" valign="top">
+ <div class="form-label">
+ Target URI
+ </label></div>
+ </td>
+ <td align="left" valign="top">
+ <input type="text" name="target_uri" size="40" />
</td>
</tr>
<tr>
Modified: erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py?rev=14954&r1=14953&r2=14954&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py (original)
+++ erp5/trunk/products/ERP5SyncML/tests/testERP5SyncML.py Mon Jun 25 17:22:32 2007
@@ -43,9 +43,10 @@
from zLOG import LOG
try:
- from base64 import b64encode, b64decode
+ from base64 import b64encode, b64decode, b16encode, b16decode
except ImportError:
- from base64 import encodestring as b64encode, decodestring as b64decode
+ from base64 import encodestring as b64encode, decodestring as b64decode, \
+ encodestring as b16encode, decodestring as b16decode
class TestERP5SyncMLMixin:
@@ -195,10 +196,10 @@
file.write('')
file.close()
nb_message = 1
- result = portal_sync.SubSync(subscription.getTitle())
+ result = portal_sync.SubSync(subscription)
while result['has_response']==1:
portal_sync.PubSync(publication.getTitle())
- result = portal_sync.SubSync(subscription.getTitle())
+ result = portal_sync.SubSync(subscription)
nb_message += 1 + result['has_response']
return nb_message
@@ -226,16 +227,16 @@
file.write('')
file.close()
nb_message = 1
- result = portal_sync.SubSync(subscription.getTitle())
+ result = portal_sync.SubSync(subscription)
while result['has_response']==1:
# We do thing three times, so that we will test
# if we manage well duplicate messages
portal_sync.PubSync(publication.getTitle())
portal_sync.PubSync(publication.getTitle())
portal_sync.PubSync(publication.getTitle())
- result = portal_sync.SubSync(subscription.getTitle())
- result = portal_sync.SubSync(subscription.getTitle())
- result = portal_sync.SubSync(subscription.getTitle())
+ result = portal_sync.SubSync(subscription)
+ result = portal_sync.SubSync(subscription)
+ result = portal_sync.SubSync(subscription)
nb_message += 1 + result['has_response']
return nb_message
@@ -337,9 +338,15 @@
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,'objectValues', self.xml_mapping,
- 'ERP5Conduit','')
+ portal_sync.manage_addPublication(title=self.pub_id,
+ publication_url=self.publication_url,
+ destination_path='/%s/person_server' % portal_id,
+ source_uri='Person',
+ query='objectValues',
+ xml_mapping=self.xml_mapping,
+ conduit='ERP5Conduit',
+ gpg_key='',
+ gid_generator='getId')
pub = portal_sync.getPublication(self.pub_id)
self.failUnless(pub is not None)
@@ -350,9 +357,17 @@
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,'objectValues',
- self.xml_mapping,'ERP5Conduit','')
+ portal_sync.manage_addSubscription(title=self.sub_id1,
+ publication_url=self.publication_url,
+ subscription_url=self.subscription_url1,
+ destination_path='/%s/person_client1' % portal_id,
+ source_uri='Person',
+ target_uri='Person',
+ query='objectValues',
+ xml_mapping=self.xml_mapping,
+ conduit='ERP5Conduit',
+ gpg_key='',
+ gid_generator='getId')
sub = portal_sync.getSubscription(self.sub_id1)
self.failUnless(sub is not None)
@@ -363,9 +378,17 @@
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,'objectValues',
- self.xml_mapping,'ERP5Conduit','')
+ portal_sync.manage_addSubscription(title=self.sub_id2,
+ publication_url=self.publication_url,
+ subscription_url=self.subscription_url2,
+ destination_path='/%s/person_client2' % portal_id,
+ source_uri='Person',
+ target_uri='Person',
+ query='objectValues',
+ xml_mapping=self.xml_mapping,
+ conduit='ERP5Conduit',
+ gpg_key='',
+ gid_generator='getId')
sub = portal_sync.getSubscription(self.sub_id2)
self.failUnless(sub is not None)
@@ -530,7 +553,7 @@
# By default we can just give the id
portal_sync = self.getSynchronizationTool()
publication = portal_sync.getPublication(self.pub_id)
- object = publication.getObjectFromGid(self.id1)
+ object = publication.getObjectFromId(self.id1)
self.failUnless(object is not None)
self.failUnless(object.getId()==self.id1)
@@ -994,10 +1017,11 @@
publication = portal_sync.getPublication(self.pub_id)
self.failUnless(len(publication.getObjectList())==nb_person)
gid = self.first_name1 + ' ' + self.last_name1 # ie the title 'Sebastien Robin'
+ gid = b16encode(gid)
person_c1 = subscription1.getObjectFromGid(gid)
id_c1 = person_c1.getId()
self.failUnless(id_c1 in ('1','2')) # id given by the default generateNewId
- person_s = publication.getObjectFromGid(gid)
+ person_s = publication.getSubscriber(self.subscription_url1).getObjectFromGid(gid)
id_s = person_s.getId()
self.failUnless(id_s==self.id1)
# This will test updating object
@@ -1014,7 +1038,7 @@
self.checkSynchronizationStateIsSynchronized()
self.failUnless(len(subscription1.getObjectList())==(nb_person-1))
self.failUnless(len(publication.getObjectList())==(nb_person-1))
- person_s = publication.getObjectFromGid(gid)
+ person_s = publication.getSubscriber(self.subscription_url1).getObjectFromGid(gid)
person_c1 = subscription1.getObjectFromGid(gid)
self.failUnless(person_s.getDescription()==self.description3)
self.failUnless(person_c1.getDescription()==self.description3)
@@ -1299,9 +1323,17 @@
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', '')
+ portal_sync.manage_addSubscription(title=self.sub_id1,
+ publication_url=self.publication_url,
+ subscription_url=self.subscription_url1,
+ destination_path='/%s/person_client1' % portal_id,
+ source_uri='Person',
+ target_uri='Person',
+ query='objectValues',
+ xml_mapping='',
+ conduit='ERP5Conduit',
+ gpg_key='',
+ gid_generator='getId')
sub = portal_sync.getSubscription(self.sub_id1)
self.failUnless(sub is not None)
More information about the Erp5-report
mailing list