Commit b14f6814 authored by Sebastien Robin's avatar Sebastien Robin

- 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


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@14954 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 6b7e714c
......@@ -139,7 +139,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
#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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
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 @@ class ERP5Conduit(XMLSyncUtilsMixin):
xml_string = buf.getvalue()
buf.close()
return xml_string
......@@ -152,7 +152,7 @@ class Publication(Subscription):
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 @@ class Publication(Subscription):
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()
......
......@@ -110,7 +110,7 @@ class PublicationSynchronization(XMLSyncUtils):
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 @@ class PublicationSynchronization(XMLSyncUtils):
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 @@ class PublicationSynchronization(XMLSyncUtils):
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)
......
......@@ -42,9 +42,10 @@ from zLOG import LOG
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 @@ class Signature(Folder,SyncCode):
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 @@ class Signature(Folder,SyncCode):
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 @@ class Signature(Folder,SyncCode):
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 @@ class Signature(Folder,SyncCode):
"""
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,16 +467,32 @@ class Signature(Folder,SyncCode):
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):
"""
Set the partial string we will have to
......@@ -545,7 +569,7 @@ class Signature(Folder,SyncCode):
"""
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 @@ class Subscription(Folder, SyncCode):
# 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 @@ class Subscription(Folder, SyncCode):
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 @@ class Subscription(Folder, SyncCode):
setter for title
"""
self.title = value
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
# Accessors
def getRemoteId(self, id, path=None):
def getTargetURI(self):
"""
Returns the remote id from a know local id
Returns None if...
path allows to implement recursive sync
getter for the target_uri (the distant Publication we want to synchronize
with)
"""
pass
return getattr(self, 'target_uri', None)
def getSynchronizationType(self, default=None):
"""
......@@ -749,7 +792,7 @@ class Subscription(Folder, SyncCode):
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,12 +810,11 @@ class Subscription(Folder, SyncCode):
"""
self.last_sent_message = xml
def getLocalId(self, rid, path=None):
def getDomainType(self):
"""
Returns the local id from a know remote id
Returns None if...
return the ID
"""
pass
return self.domain_type
def getId(self):
"""
......@@ -780,12 +822,6 @@ class Subscription(Folder, SyncCode):
"""
return self.id
def getDomainType(self):
"""
return the ID
"""
return self.domain_type
def setId(self, id):
"""
set the ID
......@@ -934,13 +970,13 @@ class Subscription(Folder, SyncCode):
"""
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 @@ class Subscription(Folder, SyncCode):
# 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 @@ class Subscription(Folder, SyncCode):
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 @@ class Subscription(Folder, SyncCode):
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,28 +1239,83 @@ class Subscription(Folder, SyncCode):
"""
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):
def getSignatureFromObjectId(self, id):
"""
add a Signature to the subscription
return the signature corresponding to the gid
"""
o = None
# 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
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 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):
"""
add a Signature to the subscription
......@@ -1238,12 +1337,6 @@ class Subscription(Folder, SyncCode):
for id in self.objectIds():
self._delObject(id)
def getGidList(self):
"""
Returns the list of ids from signature
"""
return self.objectIds()
def getConflictList(self):
"""
Return the list of all conflicts from all signatures
......@@ -1280,20 +1373,6 @@ class Subscription(Folder, SyncCode):
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):
"""
Set the status of every object as NOT_SYNCHRONIZED
......
......@@ -49,7 +49,7 @@ class SubscriptionSynchronization(XMLSyncUtils):
# 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 @@ class SubscriptionSynchronization(XMLSyncUtils):
# 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 @@ class SubscriptionSynchronization(XMLSyncUtils):
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 @@ class SubscriptionSynchronization(XMLSyncUtils):
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 @@ class SubscriptionSynchronization(XMLSyncUtils):
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)
......
......@@ -41,6 +41,8 @@ class SyncCode(Persistent):
# 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
......
......@@ -161,7 +161,7 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
"""
reset a subscription
"""
self.SubSync(title)
self.SubSync(self.getSubscription(title))
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
......@@ -371,7 +376,7 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 @@ class SynchronizationTool( SubscriptionSynchronization,
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 )
......@@ -30,7 +30,7 @@ import smtplib
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
......@@ -50,6 +50,11 @@ except ImportError:
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):
def SyncMLHeader(self, session_id, msg_id, target, source, target_name=None,
......@@ -79,8 +84,8 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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,8 +196,8 @@ class XMLSyncUtilsMixin(SyncCode):
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))
......@@ -181,6 +205,87 @@ class XMLSyncUtilsMixin(SyncCode):
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):
"""
Send a message via email
......@@ -213,9 +318,14 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
"""
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
"""
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
status = True
return status
def getNextSyncAction(self, xml_stream, last_action):
def getSyncActionList(self, xml_stream):
"""
It goes throw actions in the Sync section of the SyncML file,
then it returns the next action (could be "add", "replace",
"delete").
return the list of the 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
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 @@ class XMLSyncUtilsMixin(SyncCode):
"""
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
"""
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 @@ class XMLSyncUtilsMixin(SyncCode):
#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 @@ class XMLSyncUtilsMixin(SyncCode):
#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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
#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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
#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 @@ class XMLSyncUtilsMixin(SyncCode):
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 @@ class XMLSyncUtilsMixin(SyncCode):
#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 @@ class XMLSyncUtilsMixin(SyncCode):
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,44 +1139,46 @@ class XMLSyncUtilsMixin(SyncCode):
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):
def Sync(self, id, msg=None, RESPONSE=None):
......@@ -1121,7 +1281,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
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 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
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 @@ class XMLSyncUtils(XMLSyncUtilsMixin):
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,
......
......@@ -67,6 +67,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="destination_path" value="<dtml-var getDestinationPath>" 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" value="<dtml-var getSourceURI>" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
......
......@@ -77,6 +77,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<input type="text" name="destination_path" value="<dtml-var getDestinationPath>" 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" 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">
......
......@@ -63,6 +63,16 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<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">
......
......@@ -73,6 +73,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<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>
<td align="left" valign="top">
<div class="form-label">
......
......@@ -43,9 +43,10 @@ from Products.ERP5SyncML.SyncCode import SyncCode
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 @@ class TestERP5SyncMLMixin:
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 @@ class TestERP5SyncMLMixin:
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 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
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 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
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 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
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 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
# 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 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
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 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
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 @@ class TestERP5SyncML(TestERP5SyncMLMixin, ERP5TypeTestCase):
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)
......
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