Commit 141a752f authored by Sebastien Robin's avatar Sebastien Robin

many bugs corrected thanks to unit test

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@370 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 2cae14a9
......@@ -43,6 +43,7 @@ import pickle
import string
from xml.dom.ext import PrettyPrint
from cStringIO import StringIO
from xml.sax.saxutils import escape, unescape
import re, copy
from zLOG import LOG
......@@ -101,7 +102,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
self.args = {}
security.declareProtected(Permissions.ModifyPortalContent, 'addNode')
def addNode(self, xml=None, object=None, previous_xml=None, force=0, **kw):
def addNode(self, xml=None, object=None, previous_xml=None,
object_id=None, force=0, **kw):
"""
A node is added
......@@ -132,6 +134,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
conflict_list += self.addNode(xml=xml,object=object,
previous_xml=previous_xml, force=force, **kw)
elif xml.nodeName == 'object':
if object_id is None:
object_id = self.getAttribute(xml,'id')
docid = self.getObjectDocid(xml)
LOG('addNode',0,'object_id: %s' % object_id)
......@@ -296,7 +299,6 @@ class ERP5Conduit(XMLSyncUtilsMixin):
new_select_list += (select_item,)
select_list = new_select_list # Something like : ('','object','sid')
keyword = select_list[len(select_list)-1] # this will be 'sid'
data_xml = xml
data = None
LOG('updateNode',0,'keyword: %s' % str(keyword))
......@@ -307,6 +309,8 @@ class ERP5Conduit(XMLSyncUtilsMixin):
if subnode1.nodeName=='name':
keyword = subnode1.nodeValue
data_xml = subnode
if keyword is None: # This is not a selection, directly the property
keyword = xml.nodeName
if len(self.getElementNodeList(data_xml))==0:
try:
data = data_xml.childNodes[0].data
......@@ -832,6 +836,7 @@ class ERP5Conduit(XMLSyncUtilsMixin):
# Encoders.encode_base64(msg)
# msg.set_payload(data)
# data = msg.get_payload(decode=1)
data = unescape(data)
elif data_type in self.pickle_type_list:
data = data.replace('@@@','\n')
msg = MIMEBase('application','octet-stream')
......
......@@ -76,7 +76,7 @@ class Subscriber(Subscription):
"""
class Publication(SyncCode):
class Publication(Subscription):
"""
Publication defined by
......@@ -104,34 +104,12 @@ class Publication(SyncCode):
self.id = id
self.publication_url = publication_url
self.destination_path = destination_path
self.query = query
self.setQuery(query)
self.xml_mapping = xml_mapping
self.list_subscribers = PersistentMapping()
self.domain_type = self.PUB
def getId(self):
"""
return the id
"""
return self.id
def setId(self, id):
"""
set the id
"""
self.id = id
def getQuery(self):
"""
return the query
"""
return self.query
def setQuery(self, query):
"""
set the query
"""
self.query = query
self.setGidGenerator(None)
self.setIdGenerator(None)
def getPublicationUrl(self):
"""
......@@ -152,30 +130,6 @@ class Publication(SyncCode):
"""
self.publication_url = publication_url
def getDestinationPath(self):
"""
return the destination path
"""
return self.destination_path
def setDestinationPath(self, destination_path):
"""
set the destination path
"""
self.destination_path = destination_path
def getXML_Mapping(self):
"""
return the xml mapping
"""
return self.xml_mapping
def setXML_Mapping(self, xml_mapping):
"""
return the xml mapping
"""
self.xml_mapping = xml_mapping
def addSubscriber(self, subscriber):
"""
Add a new subscriber to the publication
......@@ -201,7 +155,7 @@ class Publication(SyncCode):
"""
for f in range(len(self.list_subscribers)):
if self.list_subscribers[f].subscription_url == subscription_url:
return self.list_subscribers[f]
return self.list_subscribers[f].__of__(self)
return None
def getSubscriberList(self):
......@@ -219,7 +173,8 @@ class Publication(SyncCode):
"""
for f in range(len(self.list_subscribers)):
if self.list_subscribers[f].subscription_url == subscription_url:
self.list_subscribers = self.list_subscribers[0:f] + self.list_subscribers[f+1:len(self.list_subscribers)]
self.list_subscribers = self.list_subscribers[0:f] + \
self.list_subscribers[f+1:len(self.list_subscribers)]
def resetAllSubscribers(self):
"""
......
......@@ -134,6 +134,8 @@ class PublicationSynchronization(XMLSyncUtils):
if self.email is None:
file = open('/tmp/sync_client','r')
xml_client = FromXmlStream(file)
file.seek(0)
LOG('PubSync',0,'Starting... msg: %s' % str(file.read()))
file.close()
elif msg is not None:
xml_client = FromXml(msg)
......@@ -164,22 +166,25 @@ class PublicationSynchronization(XMLSyncUtils):
# FIXME: Why can't we use the method addSubscriber ??
self.getPublication(id).addSubscriber(subscriber)
# first synchronization
self.PubSyncInit(self.list_publications[id],xml_client,subscriber=subscriber)
self.PubSyncInit(self.getPublication(id),xml_client,subscriber=subscriber)
elif self.checkAlert(xml_client) and self.getAlertCode(xml_client) in (self.TWO_WAY,self.SLOW_SYNC):
self.PubSyncInit(publication=self.list_publications[id],
self.PubSyncInit(publication=self.getPublication(id),
xml_client=xml_client, subscriber=subscriber)
else:
self.PubSyncModif(self.list_publications[id], xml_client)
self.PubSyncModif(self.getPublication(id), xml_client)
elif subscriber is not None:
# This looks like we are starting a synchronization after
# a conflict resolution by the user
self.PubSyncInit(publication=self.list_publications[id],
self.PubSyncInit(publication=self.getPublication(id),
xml_client=None, subscriber=subscriber)
has_response = 1 #pubsync always replies to messages
if RESPONSE is not None:
RESPONSE.redirect('managePublications')
else:
return 1
def PubSyncModif(self, publication, xml_client):
"""
......
......@@ -180,14 +180,16 @@ class Signature(SyncCode):
md5_object -- An MD5 value of a given document
#uid -- The UID of the document
id -- the ID of the document
gid -- the global id of the document
rid -- the uid of the document on the remote database,
only needed on the server.
xml -- the xml of the object at the time where it was synchronized
"""
# Constructor
def __init__(self,id=None, status=None, xml_string=None):
self.id = id
def __init__(self,gid=None, status=None, xml_string=None):
self.setGid(gid)
self.setId(None)
self.status = status
self.setXML(xml_string)
self.partial_xml = None
......@@ -314,6 +316,18 @@ class Signature(SyncCode):
"""
return self.id
def setGid(self, gid):
"""
set the id
"""
self.gid = gid
def getGid(self):
"""
get the id
"""
return self.gid
def setPartialXML(self, xml):
"""
Set the partial string we will have to
......@@ -398,7 +412,7 @@ class Signature(SyncCode):
else:
self.resetConflictList()
class Subscription(SyncCode):
class Subscription(SyncCode, Implicit):
"""
Subscription hold the definition of a master ODB
from/to which a selection of objects will be synchronised
......@@ -445,7 +459,7 @@ class Subscription(SyncCode):
self.publication_url = (publication_url)
self.subscription_url = str(subscription_url)
self.destination_path = str(destination_path)
self.query = query
self.setQuery(query)
self.xml_mapping = xml_mapping
self.anchor = None
self.session_id = 0
......@@ -453,6 +467,9 @@ class Subscription(SyncCode):
self.last_anchor = '00000000T000000Z'
self.next_anchor = '00000000T000000Z'
self.domain_type = self.SUB
self.setGidGenerator(None)
self.setIdGenerator(None)
#self.signatures = PersitentMapping()
# Accessors
......@@ -496,6 +513,12 @@ class Subscription(SyncCode):
"""
return self.id
def getDomainType(self):
"""
return the ID
"""
return self.domain_type
def setId(self, id):
"""
set the ID
......@@ -512,6 +535,8 @@ class Subscription(SyncCode):
"""
set the query
"""
if query in (None,''):
query = 'objectValues'
self.query = query
def getPublicationUrl(self):
......@@ -532,18 +557,119 @@ class Subscription(SyncCode):
"""
self.publication_url = publication_url
def getXML_Mapping(self):
def getXMLMapping(self):
"""
return the xml mapping
"""
return self.xml_mapping
def setXML_Mapping(self, xml_mapping):
def setXMLMapping(self, xml_mapping):
"""
return the xml mapping
"""
self.xml_mapping = xml_mapping
def setGidGenerator(self, method_id):
"""
This set the method name wich allows to find a gid
from any object
"""
if method_id in (None,''):
method_id = 'getId'
self.gid_generator = method_id
def getGidGenerator(self):
"""
This get the method name wich allows to find a gid
from any object
"""
return self.gid_generator
def getObjectFromGid(self, gid):
"""
This tries to get the object with the given gid
This uses the query if it exist
"""
signature = self.getSignature(gid)
# First look if we do already have the mapping between
# the id and the gid
# query_list = []
# query = self.getQuery()
# if query is type('a'):
# query_method = getattr(object,self.getQuery(),None)
# query_list = query()
# if callable(query):
# query_list = query(self)
object_list = self.getObjectList()
destination = self.getDestination()
if signature is not None:
o_id = signature.getId()
o = None
try:
o = destination._getOb(o_id)
except (AttributeError, KeyError):
pass
if o is not None and o in object_list:
return o
for o in object_list:
LOG('getObjectFromGid',0,'working on : %s' % repr(o))
o_base = aq_base(o)
LOG('getObjectFromGid',0,'gidgenerator : %s' % repr(self.getGidGenerator()))
if hasattr(o_base, self.getGidGenerator()):
LOG('getObjectFromGid',0,'there is the gid generator')
generator = getattr(o, self.getGidGenerator())
o_gid = generator()
LOG('getObjectFromGid',0,'o_gid: %s' % repr(o_gid))
LOG('getObjectFromGid',0,'gid: %s' % repr(gid))
if o_gid == gid:
return o
LOG('getObjectFromGid',0,'returning None')
return None
def getObjectList(self):
"""
This returns the list of sub-object corresponding
to the query
"""
destination = self.getDestination()
LOG('getObjectList',0,'this is a log')
query = self.getQuery()
query_list = []
if type(query) is type('a'):
query_method = getattr(destination,query,None)
if query_method is not None:
query_list = query_method()
if callable(query):
query_list = query(destination)
# if query is not None:
# query_list = query()
return query_list
def generateNewId(self, object=None,gid=None):
"""
This tries to generate a new Id
"""
if self.getIdGenerator() is not None:
o_base = aq_base(object)
if hasattr(aq_base, self.getIdGenerator()):
generator = getattr(o, self.getIdGenerator())
new_id = generator()
return new_id
return None
def setIdGenerator(self, method_id):
"""
This set the method name wich allows to generate
a new id
"""
self.id_generator = method_id
def getIdGenerator(self):
"""
This get the method name wich allows to generate a new id
"""
return self.id_generator
def getSubscriptionUrl(self):
"""
return the subscription url
......@@ -562,6 +688,12 @@ class Subscription(SyncCode):
"""
return self.destination_path
def getDestination(self):
"""
return the destination object itself
"""
return self.unrestrictedTraverse(self.getDestinationPath())
def setDestinationPath(self, destination_path):
"""
set the destination path
......@@ -625,15 +757,15 @@ class Subscription(SyncCode):
"""
add a Signature to the subscription
"""
self.signatures[signature.id] = signature
self.signatures[signature.getGid()] = signature
def delSignature(self, id):
def delSignature(self, gid):
"""
add a Signature to the subscription
"""
del self.signatures[id]
del self.signatures[gid]
def getSignature(self, id):
def getSignature(self, gid):
"""
add a Signature to the subscription
"""
......@@ -642,8 +774,8 @@ class Subscription(SyncCode):
#for key in self.signatures.keys():
# dict[key]=self.signatures[key].getPartialXML()
#LOG('Subscription',0,'dict: %s' % str(dict))
if self.signatures.has_key(id):
return self.signatures[id]
if self.signatures.has_key(gid):
return self.signatures[gid]
return None
def getSignatureList(self):
......@@ -655,12 +787,12 @@ class Subscription(SyncCode):
signature_list += [self.signatures[key]]
return signature_list
def hasSignature(self, id):
def hasSignature(self, gid):
"""
Check if there's a signature with this uid
"""
LOG('Subscription',0,'keys: %s' % str(self.signatures.keys()))
return self.signatures.has_key(id)
return self.signatures.has_key(gid)
def resetAllSignatures(self):
"""
......@@ -668,7 +800,7 @@ class Subscription(SyncCode):
"""
self.signatures = PersistentMapping()
def getIdList(self):
def getGidList(self):
"""
Returns the list of ids from signature
"""
......
......@@ -88,37 +88,42 @@ class SubscriptionSynchronization(XMLSyncUtils):
LOG('SubSync',0,'starting... id: %s' % str(id))
LOG('SubSync',0,'starting... msg: %s' % str(msg))
has_response = 0 #check if subsync replies to this messages
# first synchronization
if self.email is None:
file = open('/tmp/sync','r')
if file.readlines() == []:
self.SubSyncInit(self.list_subscriptions[id])
self.SubSyncInit(self.getSubscription(id))
has_response = 1
else:
file.seek(0)
xml_client = FromXmlStream(file)
self.SubSyncModif(self.list_subscriptions[id],xml_client)
file.seek(0)
LOG('SubSync',0,'starting... msg: %s' % str(file.read()))
has_response = self.SubSyncModif(self.getSubscription(id),xml_client)
file.close()
else:
if msg==None:
self.SubSyncInit(self.list_subscriptions[id])
self.SubSyncInit(self.getSubscription(id))
has_response = 1
else:
xml_client = FromXml(msg)
self.SubSyncModif(self.list_subscriptions[id],xml_client)
# Looks like this is not needed now
# if self.checkAlert(xml_client):
# self.SubSyncModif(self.list_subscriptions[id],xml_client)
# else:
# self.SubLastSync(self.list_subscriptions[id],xml_client)
has_response = self.SubSyncModif(self.getSubscription(id),xml_client)
if RESPONSE is not None:
RESPONSE.redirect('manageSubscriptions')
else:
LOG('SubSync',0,'has_response: %s' % str(has_response))
return has_response
def SubSyncModif(self, subscription, xml_client):
"""
Send the client modification, this happens after the Synchronization
initialization
"""
self.SyncModif(subscription, xml_client)
return self.SyncModif(subscription, xml_client)
def SubLastSync(self, subscription, xml_client=None, RESPONSE=None):
......
......@@ -243,16 +243,18 @@ class SynchronizationTool( UniqueObject, SimpleItem,
self.list_publications = PersistentMapping()
for key in self.list_publications.keys():
LOG('getPublicationList',0,'key: %s, pub:%s' % (key,repr(self.list_publications[key])))
return_list += [self.list_publications[key]]
return_list += [self.list_publications[key].__of__(self)]
return return_list
security.declareProtected(Permissions.AccessContentsInformation,'getPublication')
def getPublication(self, id):
"""
Return a list of publications
Return the publications with this id
"""
#self.list_publications=PersistentMapping()
return self.list_publications[id]
if self.list_publications.has_key(id):
return self.list_publications[id].__of__(self)
return None
security.declareProtected(Permissions.AccessContentsInformation,'getSubscriptionList')
def getSubscriptionList(self):
......@@ -264,19 +266,38 @@ class SynchronizationTool( UniqueObject, SimpleItem,
# SynchronizationTool, XXX To be removed
self.list_subscriptions = PersistentMapping()
for key in self.list_subscriptions.keys():
return_list += [self.list_subscriptions[key]]
return_list += [self.list_subscriptions[key].__of__(self)]
return return_list
def getSubscription(self, id):
"""
Returns the subscription with this id
"""
for subscription in self.getSubscriptionList():
if subscription.getId()==id:
return subscription
return None
security.declareProtected(Permissions.AccessContentsInformation,'')
def getSynchronizationList(self):
"""
Returns the list of subscriptions and publications
getSynchronizationList ? (mon choix)
getSubscriptionOrPublicationList ?
"""
return self.getSubscriptionList() + self.getPublicationList()
security.declareProtected(Permissions.AccessContentsInformation,'')
def getSubscriberList(self):
"""
Returns the list of subscribers and subscriptions
"""
s_list = []
s_list += self.getSubscriptionList()
for publication in self.getPublicationList():
s_list += publication.getSubscriberList()
return s_list
security.declareProtected(Permissions.AccessContentsInformation,'getConflictList')
def getConflictList(self, context=None):
"""
......@@ -292,14 +313,14 @@ class SynchronizationTool( UniqueObject, SimpleItem,
sub_conflict_list = subscriber.getConflictList()
for conflict in sub_conflict_list:
#conflict.setDomain('Publication')
conflict.setDomain(subscriber)
conflict.setSubscriber(subscriber)
#conflict.setDomainId(subscriber.getId())
conflict_list += [conflict.__of__(self)]
for subscription in self.getSubscriptionList():
sub_conflict_list = subscription.getConflictList()
for conflict in sub_conflict_list:
#conflict.setDomain('Subscription')
conflict.setDomain(subscription)
conflict.setSubscriber(subscription)
#conflict.setDomainId(subscription.getId())
conflict_list += [conflict.__of__(self)]
if path is not None: # Retrieve only conflicts for a given path
......@@ -342,7 +363,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
for conflict in conflict_list:
if conflict.getObjectPath() == path:
LOG('getSynchronizationState',0,'found a conflict: %s' % str(conflict))
state_list += [[conflict.getDomain(),self.CONFLICT]]
state_list += [[conflict.getSubscriber(),self.CONFLICT]]
for domain in self.getSynchronizationList():
destination = domain.getDestinationPath()
LOG('getSynchronizationState',0,'destination: %s' % str(destination))
......@@ -356,10 +377,13 @@ class SynchronizationTool( UniqueObject, SimpleItem,
subscriber_list = domain.getSubscriberList()
else:
subscriber_list = [domain]
LOG('getSynchronizationState, subscriber_list:',0,subscriber_list)
for subscriber in subscriber_list:
signature = subscriber.getSignature(o_id)
if signature is not None:
state = signature.getStatus()
LOG('getSynchronizationState:',0,'sub.dest :%s, state: %s' % \
(subscriber.getSubscriptionUrl(),str(state)))
found = None
# Make sure there is not already a conflict giving the state
for state_item in state_list:
......@@ -376,7 +400,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
to keep the local version of an object
"""
object = self.unrestrictedTraverse(conflict.getObjectPath())
subscriber = conflict.getDomain()
subscriber = conflict.getSubscriber()
# get the signature:
LOG('p_sync.setLocalObject, subscriber: ',0,subscriber)
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
......@@ -411,7 +435,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
to keep the local version of an object
"""
object = self.unrestrictedTraverse(conflict.getObjectPath())
subscriber = conflict.getDomain()
subscriber = conflict.getSubscriber()
# get the signature:
LOG('p_sync.setRemoteObject, subscriber: ',0,subscriber)
signature = subscriber.getSignature(object.getId()) # XXX may be change for rid
......@@ -449,7 +473,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if conflict.getKeyword() == keyword:
LOG('manageLocalValue',0,'found the keyword')
if '/'.join(conflict.getObjectPath())==object_path:
if conflict.getDomain().getSubscriptionUrl()==subscription_url:
if conflict.getSubscriber().getSubscriptionUrl()==subscription_url:
conflict.applyPublisherValue()
if RESPONSE is not None:
RESPONSE.redirect('manageConflicts')
......@@ -468,7 +492,7 @@ class SynchronizationTool( UniqueObject, SimpleItem,
if conflict.getKeyword() == keyword:
LOG('manageLocalValue',0,'found the keyword')
if '/'.join(conflict.getObjectPath())==object_path:
if conflict.getDomain().getSubscriptionUrl()==subscription_url:
if conflict.getSubscriber().getSubscriptionUrl()==subscription_url:
conflict.applySubscriberValue()
if RESPONSE is not None:
RESPONSE.redirect('manageConflicts')
......
This diff is collapsed.
......@@ -84,7 +84,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="xml_mapping" value="<dtml-var getXML_Mapping>" size="40" />
<input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
</td>
</tr>
</table>
......
......@@ -94,7 +94,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
</label></div>
</td>
<td align="left" valign="top">
<input type="text" name="xml_mapping" value="<dtml-var getXML_Mapping>" size="40" />
<input type="text" name="xml_mapping" value="<dtml-var getXMLMapping>" size="40" />
</td>
</tr>
</table>
......
......@@ -39,3 +39,5 @@ Setting up Synchronization
XML Mapping : asXML
Then you have to go on the subscription and hit 'Sync'
\ No newline at end of file
......@@ -393,6 +393,25 @@ class TestERP5SyncML(ERP5TypeTestCase):
# self.failUnless(person1_c.getFirstName()==self.first_name3)
# self.failUnless(person1_c.getDescription()==self.description3)
def testGetConflictList(self, quiet=0):
# We will try to generate a conflict and then to get it
if not quiet:
ZopeTestCase._print('\nTest Get Conflict List ')
LOG('Testing... ',0,'testGetConflictList')
self.testFirstSynchronization(quiet=1)
# First we do only modification on server
portal_sync = self.getSynchronizationTool()
person_server = self.getPersonServer()
person1_s = person_server._getOb(self.id1)
person1_s.setDescription(self.description2)
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
person1_c.setDescription(self.description3)
self.synchronize(self.sub_id1)
#self.checkSynchronizationStateIsSynchronized()
if __name__ == '__main__':
framework()
else:
......
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