Commit fb9162fb authored by Sebastien Robin's avatar Sebastien Robin

modified the syncml tool so that it is a folder. Now ERP5Sycml use

folders instead of dictionnaries


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@1003 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent f3a5bd3b
......@@ -29,6 +29,10 @@
from Globals import Persistent, PersistentMapping
from SyncCode import SyncCode
from Subscription import Subscription
from Products.ERP5Type import Permissions
from Products.ERP5Type.Document.Folder import Folder
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import PropertySheet
class Subscriber(Subscription):
......@@ -44,7 +48,7 @@ class Subscriber(Subscription):
next_anchor - it defines the id of the current synchronisation
"""
def __init__(self, subscription_url):
def __init__(self, id, subscription_url):
"""
constructor
"""
......@@ -53,6 +57,7 @@ class Subscriber(Subscription):
self.next_anchor = '00000000T000000Z'
self.session_id = 0
self.signatures = {}
Folder.__init__(self, id)
def ReceiveDocuments(self):
"""
......@@ -75,6 +80,16 @@ class Subscriber(Subscription):
as conflicting)
"""
def addPublication( self, id, title='', REQUEST=None ):
"""
Add a new Category and generate UID by calling the
ZSQLCatalog
"""
o = Publication( id ,'','','','','')
self._setObject( id, o )
if REQUEST is not None:
return self.manage_main(self, REQUEST, update_menu=1)
return o
class Publication(Subscription):
"""
......@@ -93,11 +108,32 @@ class Publication(Subscription):
list_subscribers -- a list of subsbribtions
"""
# Default Values
list_subscribers = PersistentMapping()
meta_type='ERP5 Publication'
portal_type='Publication' # may be useful in the future...
isPortalContent = 1
isRADContent = 1
icon = None
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.SimpleItem )
allowed_types = ( 'Signatures',)
# Declarative security
security = ClassSecurityInfo()
security.declareProtected(Permissions.ManagePortal,
'manage_editProperties',
'manage_changeProperties',
'manage_propertiesForm',
)
# Declarative constructors
constructors = (addPublication,)
# Constructor
def __init__(self, id, publication_url, destination_path, query, xml_mapping, gpg_key):
def __init__(self, id, title, publication_url, destination_path, query, xml_mapping, gpg_key):
"""
constructor
"""
......@@ -106,11 +142,13 @@ class Publication(Subscription):
self.destination_path = destination_path
self.setQuery(query)
self.xml_mapping = xml_mapping
self.list_subscribers = PersistentMapping()
#self.list_subscribers = PersistentMapping()
self.domain_type = self.PUB
self.gpg_key = gpg_key
self.setGidGenerator(None)
self.setIdGenerator(None)
Folder.__init__(self, id)
self.title = title
def getPublicationUrl(self):
"""
......@@ -137,51 +175,43 @@ class Publication(Subscription):
"""
# We have to remove the subscriber if it already exist (there were probably a reset on the client)
self.delSubscriber(subscriber.getSubscriptionUrl())
if len(self.list_subscribers) == 0:
self.list_subscribers = []
self.list_subscribers = self.list_subscribers + [subscriber]
def searchSubscriber(self, subscription_url):
"""
search if subscriber is in the list or not
"""
for f in range(len(self.list_subscribers)):
if self.list_subscribers[f].subscription_url == subscription_url:
return 1
return None
new_id = str(self.generateNewId())
subscriber.id = new_id
#if len(self.list_subscribers) == 0:
# self.list_subscribers = []
#self.list_subscribers = self.list_subscribers + [subscriber]
self._setObject(new_id,subscriber)
def getSubscriber(self, subscription_url):
"""
return the subscriber corresponding the to subscription_url
"""
for f in range(len(self.list_subscribers)):
if self.list_subscribers[f].subscription_url == subscription_url:
return self.list_subscribers[f].__of__(self)
for o in self.objectValues():
if o.getSubscriptionUrl() == subscription_url:
return o
return None
def getSubscriberList(self):
"""
Get the list of subscribers
"""
list_subscribers = []
for f in range(len(self.list_subscribers)):
list_subscribers += [self.list_subscribers[f]]
return list_subscribers
return self.objectValues()
def delSubscriber(self, subscription_url):
"""
Delete a subscriber for this publication
"""
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)]
for o in self.objectValues():
if o.getSubscriptionUrl() == subscription_url:
self._delObject(o.id)
def resetAllSubscribers(self):
"""
Reset all subscribers
"""
self.list_subscribers = PersistentMapping()
for o in self.objectValues():
self._delObject(o.id)
def getConflictList(self):
"""
......
......@@ -138,7 +138,7 @@ class PublicationSynchronization(XMLSyncUtils):
# Get the subscriber or create it if not already in the list
subscriber = self.getPublication(id).getSubscriber(subscription_url)
if subscriber == None:
subscriber = Subscriber(subscription_url)
subscriber = Subscriber(self.generateNewId(),subscription_url)
subscriber.setXMLMapping(publication.getXMLMapping())
self.getPublication(id).addSubscriber(subscriber)
# first synchronization
......@@ -156,8 +156,6 @@ class PublicationSynchronization(XMLSyncUtils):
result = self.PubSyncInit(publication=self.getPublication(id),
xml_client=None, subscriber=subscriber,sync_type=self.TWO_WAY)
has_response = 1 #pubsync always replies to messages
if RESPONSE is not None:
RESPONSE.redirect('managePublications')
elif result is not None:
......
......@@ -29,13 +29,19 @@
from Globals import PersistentMapping
from time import gmtime,strftime # for anchors
from SyncCode import SyncCode
from AccessControl import ClassSecurityInfo
from Products.CMFCore.utils import getToolByName
from Acquisition import Implicit, aq_base
from Products.ERP5Type.Document.Folder import Folder
from Products.ERP5Type.Base import Base
from Products.ERP5Type import Permissions
from Products.ERP5Type import PropertySheet
from zLOG import LOG
import md5
class Conflict(SyncCode, Implicit):
#class Conflict(SyncCode, Implicit):
class Conflict(SyncCode, Base):
"""
object_path : the path of the obect
keyword : an identifier of the conflict
......@@ -206,7 +212,7 @@ class Conflict(SyncCode, Implicit):
"""
return self.keyword
class Signature(SyncCode):
class Signature(SyncCode,Folder):
"""
status -- SENT, CONFLICT...
md5_object -- An MD5 value of a given document
......@@ -233,12 +239,6 @@ class Signature(SyncCode):
self.setSubscriberXupdate(None)
self.setPublisherXupdate(None)
#def __init__(self,object=None, status=None, xml_string=None):
# self.uid = object.uid
# self.id = object.id
# self.status = status
# self.setXML(xml_string)
def setStatus(self, status):
"""
set the Status (see SyncCode for numbers)
......@@ -397,11 +397,9 @@ class Signature(SyncCode):
Set the partial string we will have to
deliver in the future
"""
#LOG('Subscriber.setPartialXML before',0,'partial_xml: %s' % str(self.partial_xml))
if type(xml) is type(u'a'):
xml = xml.encode('utf-8')
self.partial_xml = xml
#LOG('Subscriber.setPartialXML after',0,'partial_xml: %s' % str(self.partial_xml))
def getPartialXML(self):
"""
......@@ -466,7 +464,19 @@ class Signature(SyncCode):
else:
self.resetConflictList()
class Subscription(SyncCode, Implicit):
def addSubscription( self, id, title='', REQUEST=None ):
"""
Add a new Category and generate UID by calling the
ZSQLCatalog
"""
o = Subscription( id ,'','','','','','')
self._setObject( id, o )
if REQUEST is not None:
return self.manage_main(self, REQUEST, update_menu=1)
return o
#class Subscription(SyncCode, Implicit):
class Subscription(SyncCode, Implicit, Folder):
"""
Subscription hold the definition of a master ODB
from/to which a selection of objects will be synchronised
......@@ -502,10 +512,32 @@ class Subscription(SyncCode, Implicit):
"""
signatures = PersistentMapping()
meta_type='ERP5 Subscription'
portal_type='Subscription' # may be useful in the future...
isPortalContent = 1
isRADContent = 1
icon = None
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.SimpleItem )
allowed_types = ( 'Signatures',)
# Declarative constructors
constructors = (addSubscription,)
# Declarative security
security = ClassSecurityInfo()
security.declareProtected(Permissions.ManagePortal,
'manage_editProperties',
'manage_changeProperties',
'manage_propertiesForm',
)
# Constructor
def __init__(self, id, publication_url, subscription_url, destination_path, query, xml_mapping, gpg_key):
def __init__(self, id, title, publication_url, subscription_url, destination_path, query, xml_mapping, gpg_key):
"""
We need to create a dictionnary of
signatures of documents which belong to the synchronisation
......@@ -519,16 +551,30 @@ class Subscription(SyncCode, Implicit):
self.xml_mapping = xml_mapping
self.anchor = None
self.session_id = 0
self.signatures = PersistentMapping()
#self.signatures = PersistentMapping()
self.last_anchor = '00000000T000000Z'
self.next_anchor = '00000000T000000Z'
self.domain_type = self.SUB
self.gpg_key = gpg_key
self.setGidGenerator(None)
self.setIdGenerator(None)
Folder.__init__(self, id)
self.title = title
#self.signatures = PersitentMapping()
def getTitle(self):
"""
getter for title
"""
return getattr(self,'title',None)
def setTitle(self, value):
"""
setter for title
"""
self.title = value
# Accessors
def getRemoteId(self, id, path=None):
"""
......@@ -544,16 +590,15 @@ class Subscription(SyncCode, Implicit):
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# XXX for debugging only, to be removed
dict_sign = {}
for object_id in self.signatures.keys():
dict_sign[object_id] = self.signatures[object_id].getStatus()
for o in self.objectValues():
dict_sign[o.getId()] = o.getStatus()
LOG('getSignature',0,'signatures_status: %s' % str(dict_sign))
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
code = self.SLOW_SYNC
if len(self.signatures.keys()) > 0:
if len(self.objectValues()) > 0:
code = self.TWO_WAY
if default is not None:
code = default
LOG('Subscription',0,'getSynchronizationType keys: %s' % str(self.signatures.keys()))
LOG('Subscription',0,'getSynchronizationType: %s' % code)
return code
......@@ -620,6 +665,12 @@ class Subscription(SyncCode, Implicit):
"""
return getattr(self,'gpg_key','')
def setGPGKey(self, value):
"""
setter for the gnupg key name
"""
self.gpg_key = value
def setQuery(self, query):
"""
set the query
......@@ -739,7 +790,7 @@ class Subscription(SyncCode, Implicit):
# query_list = query()
# return query_list
def generateNewId(self, object=None,gid=None):
def generateNewIdWithGenerator(self, object=None,gid=None):
"""
This tries to generate a new Id
"""
......@@ -858,54 +909,50 @@ class Subscription(SyncCode, Implicit):
"""
add a Signature to the subscription
"""
self.signatures[signature.getGid()] = signature
self._setObject( signature.getGid(), signature )
def delSignature(self, gid):
"""
add a Signature to the subscription
"""
del self.signatures[gid]
#del self.signatures[gid]
self._delObject(gid)
def getSignature(self, gid):
"""
add a Signature to the subscription
"""
# This is just a test XXX To be removed
#dict = {}
#for key in self.signatures.keys():
# dict[key]=self.signatures[key].getPartialXML()
#LOG('Subscription',0,'dict: %s' % str(dict))
if self.signatures.has_key(gid):
return self.signatures[gid]
return None
o = None
if gid in self.objectIds():
o = self._getOb(gid)
return o
def getSignatureList(self):
"""
add a Signature to the subscription
"""
signature_list = []
for key in self.signatures.keys():
signature_list += [self.signatures[key]]
return signature_list
return self.objectValues()
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(gid)
#return self.signatures.has_key(gid)
return gid in self.objectIds()
def resetAllSignatures(self):
"""
Reset all signatures
"""
self.signatures = PersistentMapping()
#self.signatures = PersistentMapping()
for o in self.objectValues():
self._delObject(o.id)
def getGidList(self):
"""
Returns the list of ids from signature
"""
return self.signatures.keys()
return self.objectIds()
def getConflictList(self):
"""
......@@ -920,17 +967,10 @@ class Subscription(SyncCode, Implicit):
"""
Set the status of every object as NOT_SYNCHRONIZED
"""
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# XXX for debugging only, to be removed
dict_sign = {}
for object_id in self.signatures.keys():
dict_sign[object_id] = self.signatures[object_id].getStatus()
LOG('startSynchronization',0,'signatures_status: %s' % str(dict_sign))
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
for object_id in self.signatures.keys():
for o in self.objectValues():
# Change the status only if we are not in a conflict mode
if not(self.signatures[object_id].getStatus() in (self.CONFLICT,self.PUB_CONFLICT_MERGE,
if not(o.getStatus() in (self.CONFLICT,self.PUB_CONFLICT_MERGE,
self.PUB_CONFLICT_CLIENT_WIN)):
self.signatures[object_id].setStatus(self.NOT_SYNCHRONIZED)
self.signatures[object_id].setPartialXML(None)
self.signatures[object_id].setTempXML(None)
o.setStatus(self.NOT_SYNCHRONIZED)
o.setPartialXML(None)
o.setTempXML(None)
This diff is collapsed.
......@@ -43,7 +43,7 @@ except ImportError:
import commands
from zLOG import LOG
class XMLSyncUtilsMixin(SyncCode, ActiveObject):
class XMLSyncUtilsMixin(SyncCode):
def SyncMLHeader(self, session_id, msg_id, target, source):
"""
......@@ -748,7 +748,7 @@ class XMLSyncUtilsMixin(SyncCode, ActiveObject):
if next_action.nodeName == 'Add':
# Then store the xml of this new subobject
if object is None:
object_id = domain.generateNewId(object=destination_path)
object_id = domain.generateNewIdWithGenerator(object=destination_path)
conflict_list += conduit.addNode(xml=data_subnode, object=destination_path,
object_id=object_id)
object = domain.getObjectFromGid(object_gid)
......
......@@ -40,18 +40,30 @@ document_classes = generateInitFiles(this_module, globals())
# Update ERP5 Globals
from Products.ERP5Type.Utils import initializeProduct, updateGlobals
import Interface, PropertySheet, Permissions, Constraint
updateGlobals( this_module, globals(),
property_sheet_module = PropertySheet,
interface_module = Interface,
permissions_module = Permissions,
constraint_module = Constraint)
# Define object classes and tools
import SynchronizationTool
object_classes = ()
object_classes = (Subscription.Subscription, Publication.Publication,)
portal_tools = (SynchronizationTool.SynchronizationTool,)
content_classes = ()
content_constructors = ()
# Finish installation
def initialize( context ):
import Document
initializeProduct(context, this_module, globals(),
document_module = Document,
document_classes = document_classes,
object_classes = object_classes,
portal_tools = portal_tools,
content_constructors = content_constructors,
content_classes = content_classes)
......@@ -40,11 +40,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" value="<dtml-var getId>" size="40"/>
<input type="text" name="title" value="<dtml-var getTitle>" size="40"/>
</td>
</tr>
<tr>
......@@ -107,12 +107,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<form action="manage_resetPublication" method="POST">
<td align="left" valign="top">
<input type="submit" value=" Reset ">
<input type="hidden" name="id" value="<dtml-var getId>" >
<input type="hidden" name="title" value="<dtml-var getTitle>" >
</form>
<form action="manage_deletePublication" method="POST">
<td align="left" valign="top">
<input type="submit" value=" Delete ">
<input type="hidden" name="id" value="<dtml-var getId>" >
<input type="hidden" name="title" value="<dtml-var getTitle>" >
</form>
</td>
</tr>
......
......@@ -40,11 +40,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" value="<dtml-var getId>" size="40"/>
<input type="text" name="title" value="<dtml-var getTitle>" size="40"/>
</td>
</tr>
<tr>
......@@ -117,17 +117,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<form action="SubSync" method="POST">
<td align="left" valign="top">
<input type="submit" value=" Sync ">
<input type="hidden" name="id" value="<dtml-var getId>" >
<input type="hidden" name="title" value="<dtml-var getTitle>" >
</form>
<form action="manage_resetSubscription" method="POST">
<td align="left" valign="top">
<input type="submit" value=" Reset ">
<input type="hidden" name="id" value="<dtml-var getId>" >
<input type="hidden" name="title" value="<dtml-var getTitle>" >
</form>
<form action="manage_deleteSubscription" method="POST">
<td align="left" valign="top">
<input type="submit" value=" Delete ">
<input type="hidden" name="id" value="<dtml-var getId>" >
<input type="hidden" name="title" value="<dtml-var getTitle>" >
</form>
</td>
</tr>
......
......@@ -36,11 +36,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
......
......@@ -36,11 +36,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
......
......@@ -318,10 +318,10 @@ class TestERP5SyncML(ERP5TypeTestCase):
file.write('')
file.close()
nb_message = 1
result = portal_sync.SubSync(subscription.getId())
result = portal_sync.SubSync(subscription.getTitle())
while result['has_response']==1:
portal_sync.PubSync(publication.getId())
result = portal_sync.SubSync(subscription.getId())
portal_sync.PubSync(publication.getTitle())
result = portal_sync.SubSync(subscription.getTitle())
nb_message += 1 + result['has_response']
return nb_message
......@@ -349,16 +349,16 @@ class TestERP5SyncML(ERP5TypeTestCase):
file.write('')
file.close()
nb_message = 1
result = portal_sync.SubSync(subscription.getId())
result = portal_sync.SubSync(subscription.getTitle())
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.getId())
portal_sync.PubSync(publication.getId())
portal_sync.PubSync(publication.getId())
result = portal_sync.SubSync(subscription.getId())
result = portal_sync.SubSync(subscription.getId())
result = portal_sync.SubSync(subscription.getId())
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())
nb_message += 1 + result['has_response']
return nb_message
......@@ -372,13 +372,22 @@ class TestERP5SyncML(ERP5TypeTestCase):
self.login()
self.setupPublicationAndSubscription(quiet=1,run=1)
nb_person = self.populatePersonServer(quiet=1,run=1)
portal_sync = self.getSynchronizationTool()
for sub in portal_sync.getSubscriptionList():
self.assertEquals(sub.getSynchronizationType(),SyncCode.SLOW_SYNC)
# Synchronize the first client
nb_message1 = self.synchronize(self.sub_id1)
for sub in portal_sync.getSubscriptionList():
if sub.getTitle() == self.sub_id1:
self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
else:
self.assertEquals(sub.getSynchronizationType(),SyncCode.SLOW_SYNC)
self.failUnless(nb_message1==self.nb_message_first_synchronization)
# Synchronize the second client
nb_message2 = self.synchronize(self.sub_id2)
for sub in portal_sync.getSubscriptionList():
self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
self.failUnless(nb_message2==self.nb_message_first_synchronization)
portal_sync = self.getSynchronizationTool()
subscription1 = portal_sync.getSubscription(self.sub_id1)
subscription2 = portal_sync.getSubscription(self.sub_id2)
self.failUnless(len(subscription1.getObjectList())==nb_person)
......@@ -1153,6 +1162,43 @@ class TestERP5SyncML(ERP5TypeTestCase):
self.failUnless(person1_c.getLastName()==self.last_name1)
SyncCode.MAX_LINES = previous_max_lines
def testGetSynchronizationType(self, quiet=0, run=run_all_test):
# We will try to update some simple data, first
# we change on the server side, the on the client side
if not run: return
if not quiet:
ZopeTestCase._print('\nTest Get Synchronization Type ')
LOG('Testing... ',0,'testGetSynchronizationType')
self.testFirstSynchronization(quiet=1,run=1)
# First we do only modification on server
# Check for each subsription that the synchronization type
# is TWO WAY
portal_sync = self.getSynchronizationTool()
for sub in portal_sync.getSubscriptionList():
self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
person_server = self.getPersonServer()
person1_s = person_server._getOb(self.id1)
kw = {'first_name':self.first_name3,'last_name':self.last_name3}
person1_s.edit(**kw)
self.synchronize(self.sub_id1)
# Then we do only modification on a client
person_client1 = self.getPersonClient1()
person1_c = person_client1._getOb(self.id1)
kw = {'first_name':self.first_name1,'last_name':self.last_name1}
person1_c.edit(**kw)
self.synchronize(self.sub_id1)
for sub in portal_sync.getSubscriptionList():
self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
# Then we do only modification on both the client and the server
# and of course, on the same object
kw = {'first_name':self.first_name3}
person1_s.edit(**kw)
kw = {'description':self.description3}
person1_c.edit(**kw)
self.synchronize(self.sub_id1)
for sub in portal_sync.getSubscriptionList():
self.assertEquals(sub.getSynchronizationType(),SyncCode.TWO_WAY)
# We may add a test in order to check if the slow_sync mode works fine, ie
# if we do have both object on the client and server side, we must make sure
# that the server first sends is own data
......
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