[Erp5-report] r15529 - in /erp5/trunk/products/ERP5SyncML: ./ dtml/

nobody at svn.erp5.org nobody at svn.erp5.org
Tue Aug 7 16:16:47 CEST 2007


Author: seb
Date: Tue Aug  7 16:16:47 2007
New Revision: 15529

URL: http://svn.erp5.org?rev=15529&view=rev
Log:
- add vcard and mobile phone support
- clean some parts

Modified:
    erp5/trunk/products/ERP5SyncML/Publication.py
    erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py
    erp5/trunk/products/ERP5SyncML/Subscription.py
    erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py
    erp5/trunk/products/ERP5SyncML/SyncCode.py
    erp5/trunk/products/ERP5SyncML/SynchronizationTool.py
    erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py
    erp5/trunk/products/ERP5SyncML/dtml/explainSynchronizationTool.dtml
    erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml
    erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml

Modified: erp5/trunk/products/ERP5SyncML/Publication.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Publication.py?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Publication.py (original)
+++ erp5/trunk/products/ERP5SyncML/Publication.py Tue Aug  7 16:16:47 2007
@@ -84,16 +84,6 @@
       Send ACK for a group of documents
     """
 
-  def getConduit(self):
-    """
-    Return the conduit of the publication
-    """
-    #LOG('Subscriber.getConduit, self.getPhysicalPath()',0,self.getPhysicalPath())
-    #LOG('Subscriber.getConduit, self.getParent().getPhysicalPath()',0,self.aq_parent.getPhysicalPath())
-    #LOG('Subscriber.getConduit, self.getParent()',0,self.getParent())
-    return self.aq_parent.getConduit()
-    #return self.conduit
-
   def SendDocuments(self):
     """
       We send all the updated documents (ie. documents not marked
@@ -155,7 +145,8 @@
   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, authentication_format, 
-      authentication_type, activity_enabled):
+      authentication_type, activity_enabled, synchronize_with_erp5_sites, 
+      sync_content_type):
     """
       constructor
     """
@@ -178,6 +169,8 @@
     self.auth_required = auth_required 
     self.authentication_format = authentication_format
     self.authentication_type = authentication_type
+    self.setSyncContentType(sync_content_type)
+    self.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
 
   def getPublicationUrl(self):
     """
@@ -238,6 +231,7 @@
     """
       Add a new subscriber to the publication
     """
+    LOG('addSubscriber starting ...',0,'')
     # We have to remove the subscriber if it already exist (there were probably a reset on the client)
     self.delSubscriber(subscriber.getSubscriptionUrl())
     new_id = subscriber.getId()

Modified: erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py (original)
+++ erp5/trunk/products/ERP5SyncML/PublicationSynchronization.py Tue Aug  7 16:16:47 2007
@@ -52,7 +52,7 @@
       Read the client xml message
       Send the first XML message from the server
     """
-    #LOG('PubSyncInit',0,'Starting... publication: %s' % str(publication))
+    LOG('PubSyncInit',0,'Starting... publication: %s' % str(publication))
     
     #the session id is set at the same value of those of the client
     subscriber.setSessionId(self.getSessionId(xml_client))
@@ -72,153 +72,145 @@
       alert = self.checkAlert(xml_client)
       alert_code = self.getAlertCode(xml_client)
       cred = self.checkCred(xml_client)
-      #XXX this is in developement, it's just for tests
+
+      #the source and the target of the subscriber are reversed compared 
+      # to those of the publication :
+      subscriber.setSourceURI(self.getTargetURI(xml_client))
+      subscriber.setTargetURI(self.getSourceURI(xml_client))
+
+      # If slow sync, then resend everything
+      if alert_code == self.SLOW_SYNC:
+        LOG('Warning !!!, reseting client synchronization for subscriber:',0,
+            subscriber)
+        subscriber.resetAllSignatures()
+
+      # Check if the last time synchronization is the same as the client one
+      mess='\nsubscriber.getNextAnchor:\t%s\nsubscriber.getLastAnchor:\t%s\
+        \nlast_anchor:\t\t\t%s\nnext_anchor:\t\t\t%s' % \
+        (subscriber.getNextAnchor(), subscriber.getLastAnchor(), last_anchor, \
+        next_anchor)
+      #LOG('PubSyncInit',0,mess)
+      
+      if subscriber.getNextAnchor() != last_anchor:
+        if last_anchor in (None, ''):
+          LOG('PubSyncInit',0,'anchor null')
+          #raise ValueError, "Sorry, the anchor was null"
+        else:
+          message = "bad anchors in PubSyncInit! " + \
+              subscriber.getNextAnchor() + " and " + last_anchor
+          LOG('PubSyncInit',0,message)
+      else:
+        subscriber.setNextAnchor(next_anchor)
+
+      xml_list = []
+      xml = xml_list.append
+      cmd_id = 1 # specifies a SyncML message-unique command identifier      
+      xml('<SyncML>\n')
+      # syncml header
+      xml(self.SyncMLHeader(subscriber.getSessionId(),
+        subscriber.getMessageId(), 
+        subscriber.getSubscriptionUrl(), 
+        publication.getPublicationUrl()))
+      # syncml body
+      xml(' <SyncBody>\n')
+
+
       if publication.isAuthenticationRequired():
+        #at the begining, the code is initialised at UNAUTHORIZED
+        auth_code=self.UNAUTHORIZED
+        LOG('PubSyncInit',0,'authentication required')
         if not cred:
-          #LOG('PubSyncInit',0,'authentication required')
+          auth_code=self.AUTH_REQUIRED
+          LOG("there's no credential !!!",0,'')
 	        # Prepare the xml message for the Sync initialization package
-          cmd_id = 1 # specifies a SyncML message-unique command identifier
-          xml_list = []
-          xml = xml_list.append
-          xml('<SyncML>\n')
-          # syncml header
-          xml(self.SyncMLHeader(subscriber.getSessionId(),
-            subscriber.getMessageId(), subscriber.getSubscriptionUrl(), 
-            publication.getPublicationUrl()))
-          # syncml body
-          xml(' <SyncBody>\n')
-	        # chal message
           xml(self.SyncMLChal(cmd_id, "SyncHdr", 
             publication.getPublicationUrl(), subscriber.getSubscriptionUrl(), 
             publication.getAuthenticationFormat(), 
-            publication.getAuthenticationType(), self.AUTH_REQUIRED))
+            publication.getAuthenticationType(), auth_code))
           cmd_id += 1
-
-          xml(' </SyncBody>\n')
-
-          xml('</SyncML>\n')
-          xml_a = ''.join(xml_list)
-
-          self.sendResponse(from_url=publication.getPublicationUrl(),
-            to_url=subscriber.getSubscriptionUrl(), 
-            sync_id=publication.getTitle(), xml=xml_a, domain=publication)
-
-        else:#(if the subscriber begin the session with a cred) -> to be tested
-          (authentication_format, authentication_type, data) = self.getCred(xml_client)
-          #at the begining, the code is initialised at UNAUTHORIZED
-          auth_code=self.UNAUTHORIZED
-          
-          if authentication_format == publication.getAuthenticationFormat():
-            if authentication_type == publication.getAuthenticationType():
-              decoded = subscriber.decode(authentication_format, data)
-              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(
-                                          IAuthenticationPlugin ):
-                  if plugin.authenticateCredentials(
-                            {'login':login, 'password':password}) is not None:
-                    subscriber.setAuthenticated(True)
-                    auth_code=self.AUTH_ACCEPTED
-                    #here we must log in with the user authenticated :
-                    user = uf.getUserById(login).__of__(uf)
-                    newSecurityManager(None, user)
-                    subscriber.setUser(login)
-                    break
-                  else:
-                    auth_code=self.UNAUTHORIZED
+	        # chal message
+          xml_status, cmd_id = self.SyncMLStatus(xml_client, auth_code,
+              cmd_id, next_anchor, subscription=subscriber).values()
+          xml(xml_status)
+        else:
+          (authentication_format, authentication_type, data) = \
+              self.getCred(xml_client)
+          if authentication_type == publication.getAuthenticationType():
+            authentication_format = publication.getAuthenticationFormat()
+            decoded = subscriber.decode(authentication_format, data)
+            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(
+                                        IAuthenticationPlugin ):
+                if plugin.authenticateCredentials(
+                          {'login':login, 'password':password}) is not None:
+                  subscriber.setAuthenticated(True)
+                  auth_code=self.AUTH_ACCEPTED
+                  #here we must log in with the user authenticated :
+                  user = uf.getUserById(login).__of__(uf)
+                  newSecurityManager(None, user)
+                  subscriber.setUser(login)
+                  break
+                else:
+                  auth_code=self.UNAUTHORIZED
           #in all others cases, the auth_code is set to UNAUTHORIZED  
 
 
           # Prepare the xml message for the Sync initialization package
-          cmd_id = 1 # specifies a SyncML message-unique command identifier
-          xml_list = []
-          xml = xml_list.append
-          xml('<SyncML>\n')
-          # syncml header
-          xml(self.SyncMLHeader(subscriber.getSessionId(),
-            subscriber.getMessageId(), 
-            subscriber.getSubscriptionUrl(), 
-            publication.getPublicationUrl()))
-          # syncml body
-          xml(' <SyncBody>\n')
-          xml(self.SyncMLStatus(cmd_id, subscriber.getSubscriptionUrl(), 
-            publication.getPublicationUrl(), auth_code))
-          cmd_id += 1
           if auth_code == self.AUTH_ACCEPTED:
+            xml_status, cmd_id = self.SyncMLStatus(xml_client, auth_code,
+                cmd_id, next_anchor, subscription=subscriber).values()
+            xml(xml_status)
             # alert message
-            xml(self.SyncMLAlert(cmd_id, sync_type, 
-              subscriber.getSubscriptionUrl(), publication.getPublicationUrl(),
-              subscriber.getLastAnchor(), subscriber.getNextAnchor()))
+            xml(self.SyncMLAlert(cmd_id, sync_type, subscriber.getTargetURI(), 
+              subscriber.getSourceURI(), subscriber.getLastAnchor(), 
+              next_anchor))
             cmd_id += 1
           else:
 	          # chal message
             xml(self.SyncMLChal(cmd_id, "SyncHdr", 
               publication.getPublicationUrl(), subscriber.getSubscriptionUrl(), 
               publication.getAuthenticationFormat(), 
-              publication.getAuthenticationType(), self.AUTH_REQUIRED))
+              publication.getAuthenticationType(), auth_code))
             cmd_id += 1
-          xml(' </SyncBody>\n')
-          xml('</SyncML>\n')
-          xml_a = ''.join(xml_list)
-
-          self.sendResponse(from_url=publication.getPublicationUrl(),
-          to_url=subscriber.getSubscriptionUrl(),
-          sync_id=publication.getTitle(), xml=xml_a, domain=publication)
-
-      else :
-        # If slow sync, then resend everything
-        if alert_code == self.SLOW_SYNC:
-          LOG('Warning !!!, reseting client synchronization for subscriber:',0,
-              subscriber)
-          subscriber.resetAllSignatures()
-
-        # Check if the last time synchronization is the same as the client one
-        mess='\nsubscriber.getNextAnchor:\t%s\nsubscriber.getLastAnchor:\t%s\
-        \nlast_anchor:\t\t\t%s\nnext_anchor:\t\t\t%s' % (subscriber.getNextAnchor(), subscriber.getLastAnchor(), last_anchor, next_anchor)
-        #LOG('PubSyncInit',0,mess)
-        
-        if subscriber.getNextAnchor() != last_anchor:
-          if last_anchor == None:
-            #LOG('PubSyncInit',0,'anchor null')
-            raise ValueError, "Sorry, the anchor was null"
-          else:
-            message = "bad anchors in PubSyncInit! " + \
-                subscriber.getNextAnchor() + " and " + last_anchor
-            #LOG('PubSyncInit',0,message)
-        else:
-	        subscriber.setNextAnchor(next_anchor)
+            xml_status, cmd_id = self.SyncMLStatus(xml_client, 
+                self.AUTH_REQUIRED, cmd_id, next_anchor, 
+                subscription=subscriber).values()
+            xml(xml_status)
+
+      elif alert is not None: #if no identification is required :
+        # syncml header
+        xml_status, cmd_id = self.SyncMLStatus(xml_client, self.AUTH_ACCEPTED, 
+            cmd_id, next_anchor, subscription=subscriber).values()
+        xml(xml_status)
+        # alert message
+        xml(self.SyncMLAlert(cmd_id, sync_type, subscriber.getTargetURI(),
+          subscriber.getSourceURI(), subscriber.getLastAnchor(), next_anchor))
+        cmd_id += 1
+
       # We have to set every object as NOT_SYNCHRONIZED
       subscriber.startSynchronization()
     else:
-      # We have started the sync from the server (may be for a conflict resolution)
-      pass
-
-    if alert is not None and not publication.isAuthenticationRequired():
-      # Prepare the xml message for the Sync initialization package
-      cmd_id = 1 # specifies a SyncML message-unique command identifier
-      xml_list = []
-      xml = xml_list.append
-
-      xml('<SyncML>\n')
-      # syncml header
-      xml(self.SyncMLHeader(subscriber.getSessionId(), 
-        subscriber.incrementMessageId(), subscriber.getSubscriptionUrl(), 
-        publication.getPublicationUrl()))
-      # syncml body
-      xml(' <SyncBody>\n')
-      # alert message
-      xml(self.SyncMLAlert(cmd_id, sync_type, subscriber.getSubscriptionUrl(),
-        publication.getPublicationUrl(), subscriber.getLastAnchor(), 
-        subscriber.getNextAnchor()))
-      cmd_id += 1
-      xml(' </SyncBody>\n')
-      xml('</SyncML>\n')
-      xml_a = ''.join(xml_list)
-
-      self.sendResponse(from_url=publication.getPublicationUrl(),
-        to_url=subscriber.getSubscriptionUrl(), sync_id=publication.getTitle(), 
-        xml=xml_a, domain=publication)
+      # We have started the sync from the server (may be for a conflict 
+      # resolution)
+      raise ValueError, "the syncml message is None. Maybe a synchronisation \
+          has been started from the server (forbiden)"
+      # a synchronisation is always starded from a client and can't be from
+      # a server ! 
+
+    xml('  <Final/>\n')
+    xml(' </SyncBody>\n')
+    xml('</SyncML>\n')
+    xml_a = ''.join(xml_list)
+    
+    if publication.getSyncContentType() == self.CONTENT_TYPE['SYNCML_WBXML']:
+      xml_a = self.xml2wbxml(xml_a)
+    self.sendResponse(from_url=publication.getPublicationUrl(),
+      to_url=subscriber.getSubscriptionUrl(), sync_id=publication.getTitle(), 
+      xml=xml_a, domain=publication, 
+      content_type=publication.getSyncContentType())
+
     return {'has_response':1,'xml':xml_a}
 
 
@@ -226,7 +218,7 @@
     """
       This is the synchronization method for the server
     """
-    #LOG('PubSync',0,'Starting... publication: %s' % str(publication_path))
+    LOG('PubSync',0,'Starting... publication: %s' % str(publication_path))
     # Read the request from the client
     publication = self.unrestrictedTraverse(publication_path)
     xml_client = msg
@@ -244,18 +236,19 @@
         #LOG('PubSync',0,'This is not a SyncML Message')
         raise ValueError, "Sorry, This is not a SyncML Message"
       alert_code = self.getAlertCode(xml_client)
-
+      
       # Get informations from the header
       client_header = first_node.childNodes[1]
       if client_header.nodeName != "SyncHdr":
         #LOG('PubSync',0,'This is not a SyncML Header')
         raise ValueError, "Sorry, This is not a SyncML Header"
-      subscription_url = self.getSourceURI(client_header)
+      subscription_url = self.getSubscriptionUrl(client_header)
       # Get the subscriber or create it if not already in the list
       subscriber = publication.getSubscriber(subscription_url)
       if subscriber == None:
         subscriber = Subscriber(publication.generateNewId(),subscription_url)
         subscriber.setXMLMapping(publication.getXMLMapping())
+        subscriber.setConduit(publication.getConduit())
         publication.addSubscriber(subscriber)
         # first synchronization
         result = self.PubSyncInit(publication,xml_client,subscriber=subscriber,
@@ -266,6 +259,8 @@
             xml_client=xml_client, subscriber=subscriber, sync_type=alert_code)
       else:
         #we log the user authenticated to do the synchronization with him
+        if self.checkMap(xml_client) :
+          self.setRidWithMap(xml_client, subscriber)
         if publication.isAuthenticationRequired():
           if subscriber.isAuthenticated():
               uf = self.getPortalObject().acl_users

Modified: erp5/trunk/products/ERP5SyncML/Subscription.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Subscription.py?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Subscription.py (original)
+++ erp5/trunk/products/ERP5SyncML/Subscription.py Tue Aug  7 16:16:47 2007
@@ -575,6 +575,7 @@
     """
     Add a new Subscribption
     """
+    LOG('addSubscription starting...',0,'')
     o = Subscription( id ,'','','','','','')
     self._setObject( id, o )
     if REQUEST is not None:
@@ -648,7 +649,8 @@
   def __init__(self, id, title, publication_url, subscription_url, 
       destination_path, source_uri, target_uri, query, xml_mapping, 
       conduit, gpg_key, id_generator, gid_generator, media_type, login, 
-      password, activity_enabled, alert_code):
+      password, activity_enabled, alert_code, synchronize_with_erp5_sites, 
+      sync_content_type):
     """
       We need to create a dictionnary of
       signatures of documents which belong to the synchronisation
@@ -679,7 +681,8 @@
     self.setConduit(conduit)
     Folder.__init__(self, id)
     self.title = title
-
+    self.setSyncContentType(sync_content_type)
+    self.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
     #self.signatures = PersitentMapping()
 
   def getAlertCodeList(self):
@@ -727,7 +730,7 @@
 
   def getSourceURI(self):
     """
-    getter for the source_uri (the local path of the subscription)
+    getter for the source_uri (the local path of the subscription data base)
     """
     return getattr(self, 'source_uri', None)
 
@@ -739,10 +742,25 @@
 
   def getTargetURI(self):
     """
-    getter for the target_uri (the distant Publication we want to synchronize 
-    with)
+    getter for the target_uri (the distant Publication data base we want to 
+    synchronize with)
     """
     return getattr(self, 'target_uri', None)
+
+  def setSyncContentType(self, sync_content_type):
+    """
+    content type used by the subscriber
+    """
+    self.sync_content_type=sync_content_type
+    # the varible name is sync_content_type instead of content_type because
+    # content_type seems to be a function name already used
+
+
+  def getSyncContentType(self):
+    """
+    getter of the subscriber sync_content_type
+    """
+    return getattr(self, 'sync_content_type', 'application/vnd.syncml+xml')
 
   def getSynchronizationType(self, default=None):
     """
@@ -770,6 +788,21 @@
       value = None
     self.xml_mapping = value
 
+  def setSynchronizeWithERP5Sites(self, synchronize_with_erp5_sites):
+    """
+    if the synchronisation is made with another ERP5 site, 
+    synchronize_with_erp5_sites is True, False in other case
+    XXX in the future, the method used to sendHttpResponse will be the same
+    in all cases, so this method will be useless
+    """
+    self.synchronize_with_erp5_sites = synchronize_with_erp5_sites
+
+  def getSynchronizeWithERP5Sites(self):
+    """
+    return True if the synchronisation is between two erp5 sites
+    """
+    return getattr(self, 'synchronize_with_erp5_sites', None)
+
   def checkCorrectRemoteSessionId(self, session_id):
     """
     We will see if the last session id was the same
@@ -886,7 +919,7 @@
 
   def setPublicationUrl(self, publication_url):
     """
-      return the publication url
+      set the publication url
     """
     self.publication_url = publication_url
 
@@ -1058,6 +1091,30 @@
         break
     return o
 
+  def getObjectFromRid(self, rid):
+    """
+    return the object corresponding to the id
+    """
+    signature = self.getSignatureFromRid(rid)
+    destination = self.getDestination()
+    o = None
+    if signature is not None and signature.getPath() is not None:
+      try:
+        o = destination.getPortalObject().restrictedTraverse(signature.getPath())
+      except:
+        pass
+    return o 
+
+
+
+#  def setOneWaySyncFromServer(self,value):
+#    """
+#    If this option is enabled, then we will not 
+#    send our own modifications
+#    """
+#    self.one_way_sync_from_server = value
+#
+
   def getObjectList(self, **kw):
     """
     This returns the list of sub-object corresponding

Modified: erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py (original)
+++ erp5/trunk/products/ERP5SyncML/SubscriptionSynchronization.py Tue Aug  7 16:16:47 2007
@@ -75,7 +75,8 @@
 
     self.sendResponse(from_url=subscription.subscription_url,
         to_url=subscription.publication_url, sync_id=subscription.getTitle(), 
-        xml=xml_a,domain=subscription)
+        xml=xml_a,domain=subscription, 
+        content_type=subscription.getSyncContentType()) 
 
     return {'has_response':1,'xml':xml_a}
 
@@ -86,7 +87,7 @@
     response = None #check if subsync replies to this messages
     subscription = self.unrestrictedTraverse(subscription_path)
     if msg==None and (subscription.getSubscriptionUrl()).find('file')>=0:
-      msg = self.readResponse(sync_id=subscription.getSourceURI(), 
+      msg = self.readResponse(sync_id=subscription.getSubscriptionUrl(), 
           from_url=subscription.getSubscriptionUrl())
     if msg==None:
       response = self.SubSyncInit(subscription)
@@ -170,7 +171,8 @@
 
     self.sendResponse(from_url=subscription.subscription_url,
         to_url=subscription.publication_url, sync_id=subscription.getTitle(),
-        xml=xml_a,domain=subscription)
+        xml=xml_a,domain=subscription,
+        content_type=subscription.getSyncContentType())
 
     return {'has_response':1,'xml':xml_a}
 

Modified: erp5/trunk/products/ERP5SyncML/SyncCode.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SyncCode.py?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SyncCode.py (original)
+++ erp5/trunk/products/ERP5SyncML/SyncCode.py Tue Aug  7 16:16:47 2007
@@ -37,8 +37,9 @@
   # SyncML Alert Codes
   TWO_WAY = 200
   SLOW_SYNC = 201 # This means we get the data from the publication
+  ONE_WAY_FROM_SERVER = 204
   WAITING_DATA = 214
-  ONE_WAY_FROM_SERVER = 204
+  REFRESH_REQUIRED = 508
 
   # SyncML Status Codes
   SUCCESS = 200
@@ -121,3 +122,9 @@
   MEDIA_TYPE = {}
   MEDIA_TYPE['TEXT_XML'] = 'text/xml'
   MEDIA_TYPE['TEXT_VCARD'] = 'text/vcard'
+  MEDIA_TYPE['TEXT_XVCARD'] = 'text/x-vcard'
+
+  #content types :
+  CONTENT_TYPE = {}
+  CONTENT_TYPE['SYNCML_XML'] = 'application/vnd.syncml+xml'
+  CONTENT_TYPE['SYNCML_WBXML'] = 'application/vnd.syncml+wbxml'

Modified: erp5/trunk/products/ERP5SyncML/SynchronizationTool.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/SynchronizationTool.py?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/SynchronizationTool.py (original)
+++ erp5/trunk/products/ERP5SyncML/SynchronizationTool.py Tue Aug  7 16:16:47 2007
@@ -176,10 +176,13 @@
   security.declareProtected(Permissions.ModifyPortalContent, 
       'manage_addPublication')
   def manage_addPublication(self, title, publication_url, 
-            destination_path, source_uri, query, xml_mapping, conduit, gpg_key, 
+            destination_path, 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, activity_enabled = False):
+            authentication_type='', RESPONSE=None, activity_enabled = False,
+            sync_content_type='application/vnd.syncml+xml', 
+            synchronize_with_erp5_sites=True):
     """ 
       create a new publication
     """
@@ -192,7 +195,9 @@
                       destination_path, source_uri, query, xml_mapping, 
                       conduit, gpg_key, synchronization_id_generator, 
                       gid_generator, media_type, auth_required, 
-                      authentication_format, authentication_type, activity_enabled)
+                      authentication_format, authentication_type, 
+                      activity_enabled, synchronize_with_erp5_sites, 
+                      sync_content_type)
     folder._setObject( new_id, pub )
     #if len(self.list_publications) == 0:
     #  self.list_publications = PersistentMapping()
@@ -207,7 +212,10 @@
                        xml_mapping, conduit, gpg_key,
                        synchronization_id_generator=None, gid_generator=None,
                        media_type=None, login=None, password=None,
-                       RESPONSE=None, activity_enabled=False, alert_code=SyncCode.TWO_WAY):
+                       RESPONSE=None, activity_enabled=False, 
+                       alert_code=SyncCode.TWO_WAY, 
+                       synchronize_with_erp5_sites = True, 
+                       sync_content_type='application/vnd.syncml+xml'):
     """
       XXX should be renamed as addSubscription
       create a new subscription
@@ -221,7 +229,8 @@
                        destination_path, source_uri, target_uri, query,
                        xml_mapping, conduit, gpg_key,
                        synchronization_id_generator, gid_generator, media_type,
-                       login, password, activity_enabled, alert_code)
+                       login, password, activity_enabled, alert_code, 
+                       synchronize_with_erp5_sites, sync_content_type)
     folder._setObject( new_id, sub )
     #if len(self.list_subscriptions) == 0:
     #  self.list_subscriptions = PersistentMapping()
@@ -236,7 +245,9 @@
                             conduit, gpg_key, synchronization_id_generator,
                             gid_generator,  media_type=None, auth_required=0,
                             authentication_format='', authentication_type='',
-                            RESPONSE=None, activity_enabled=False):
+                            RESPONSE=None, activity_enabled=False,
+                            sync_content_type='application/vnd.syncml+xml',
+                            synchronize_with_erp5_sites=False):
     """
       modify a publication
     """
@@ -256,6 +267,8 @@
     pub.setAuthentication(auth_required)
     pub.setAuthenticationFormat(authentication_format)
     pub.setAuthenticationType(authentication_type)
+    pub.setSyncContentType(sync_content_type)
+    pub.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
 
     if RESPONSE is not None:
       RESPONSE.redirect('managePublications')
@@ -265,7 +278,9 @@
   def manage_editSubscription(self, title, publication_url, subscription_url,
       destination_path, source_uri, target_uri, query, xml_mapping, conduit,
       gpg_key, synchronization_id_generator, gid_generator, media_type=None,
-      login='', password='', RESPONSE=None, activity_enabled=False, alert_code=SyncCode.TWO_WAY):
+      login='', password='', RESPONSE=None, activity_enabled=False, 
+      alert_code=SyncCode.TWO_WAY, synchronize_with_erp5_sites=False, 
+      sync_content_type='application/vnd.syncml+xml'):
     """
       modify a subscription
     """
@@ -286,6 +301,8 @@
     sub.setMediaType(media_type)
     sub.setLogin(login)
     sub.setPassword(password)
+    sub.setSyncContentType(sync_content_type)
+    sub.setSynchronizeWithERP5Sites(synchronize_with_erp5_sites)
     sub.setAlertCode(alert_code)
 
     if RESPONSE is not None:
@@ -833,7 +850,7 @@
 
   security.declarePublic('sendResponse')
   def sendResponse(self, to_url=None, from_url=None, sync_id=None,xml=None, 
-      domain=None, send=1):
+      domain=None, send=1, content_type='application/vnd.syncml+xml'):
     """
     We will look at the url and we will see if we need to send mail, http
     response, or just copy to a file.
@@ -843,6 +860,12 @@
     #LOG('sendResponse, from_url: ',0,from_url)
     #LOG('sendResponse, sync_id: ',0,sync_id)
     #LOG('sendResponse, xml: \n',0,xml)
+
+    if content_type == self.CONTENT_TYPE['SYNCML_WBXML']:
+      xml = self.xml2wbxml(xml)
+      #LOG('sendHttpResponse, xml after wbxml: \n',0,self.hexdump(xml))
+
+
     if isinstance(xml, unicode):
       xml = xml.encode('utf-8')
     if domain is not None:
@@ -878,7 +901,8 @@
           self.activate(activity='RAMQueue').sendHttpResponse(sync_id=sync_id,
                                            to_url=to_url,
                                            xml=xml, 
-                                           domain_path=domain.getPath())
+                                           domain_path=domain.getPath(),
+                                           content_type=content_type)
         elif to_url.find('file://')==0:
           filename = to_url[len('file:/'):]
           stream = file(filename,'w')
@@ -895,10 +919,11 @@
 
   security.declarePrivate('sendHttpResponse')
   def sendHttpResponse(self, to_url=None, sync_id=None, xml=None, 
-      domain_path=None ):
+      domain_path=None, content_type='application/vnd.syncml+xml'):
     domain = self.unrestrictedTraverse(domain_path)
     #LOG('sendHttpResponse, self.getPhysicalPath: ',0,self.getPhysicalPath())
     #LOG('sendHttpResponse, starting with domain:',0,domain)
+
     #LOG('sendHttpResponse, xml:',0,xml)
     if domain is not None:
       if domain.domain_type == self.PUB and not domain.getActivityEnabled():
@@ -920,27 +945,46 @@
     urllib2.install_opener(opener)
     to_encode = {}
     head = '<?xml version="1.0" encoding="UTF-8"?>'
-    to_encode['text'] = head + xml
+
+    if content_type == self.CONTENT_TYPE['SYNCML_WBXML']:
+      #because xml2wbxml add the head to the xml
+      to_encode['text'] = xml
+    else:
+      to_encode['text'] = head + xml
     to_encode['sync_id'] = sync_id
-    headers = {'Content-type': 'application/vnd.syncml+xml'}
-    
+    headers = {'User-Agent':'ERP5SyncML', 'Content-Type':content_type}
+
     #XXX bad hack for synchronization with erp5
-    if to_url.find('readResponse')<0:
-      to_url = to_url + '/portal_synchronizations/readResponse'
-    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)
+    # because at this time, when we call the readResponse method, we must
+    # encode the data with urlencode if we want the readResponse method to 
+    # receive the data's in parameters.
+    # All this should be improved to not use urlencode in all cases.
+    # to do this, perhaps use SOAP :
+    #  - http://en.wikipedia.org/wiki/SOAP
+    #  - http://www.contentmanagementsoftware.info/zope/SOAPSupport
+    #  - http://svn.zope.org/soap/trunk/
+
+    if domain.getSynchronizeWithERP5Sites():
+      LOG('Synchronization with another ERP5 instance ...',0,'')
+      if to_url.find('readResponse')<0:
+        to_url = to_url + '/portal_synchronizations/readResponse'
+      encoded = urllib.urlencode(to_encode)
+      data=encoded
+      request = urllib2.Request(url=to_url, data=data)
+    else:
+    #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()
+      url_file = urllib2.urlopen(request)
+      result = url_file.read()
     except socket.error, msg:
       self.activate(activity='RAMQueue').sendHttpResponse(to_url=to_url, 
-          sync_id=sync_id, xml=xml, domain_path=domain.getPath())
+          sync_id=sync_id, xml=xml, domain_path=domain.getPath(), 
+          content_type=content_type)
       LOG('sendHttpResponse, socket ERROR:',0,msg)
-      LOG('sendHttpResponse, url,data',0,(url, data))
+      #LOG('sendHttpResponse, url,data',0,(url, data))
       return
     except urllib2.URLError, msg:
       LOG("sendHttpResponse, can't open url %s :" % to_url, 0, msg)
@@ -961,7 +1005,9 @@
               #user = uf.getUserById('syncml').__of__(uf)
               #newSecurityManager(None, user)
               #self.activate(activity='RAMQueue').readResponse(sync_id=sync_id,text=result)
+              
               self.readResponse(sync_id=sync_id,text=result)
+    return result
 
   security.declarePublic('sync')
   def sync(self):
@@ -986,6 +1032,8 @@
     response, or just copy to a file.
     """
     #LOG('readResponse, text :', 0, text)
+    #LOG('readResponse, text :', 0, self.hexdump(text))
+
     # Login as a manager to make sure we can create objects
     uf = self.acl_users
     user = uf.getUserById('syncml').__of__(uf)
@@ -1001,10 +1049,12 @@
       for publication in self.getPublicationList():
         if publication.getTitle()==sync_id:
           gpg_key = publication.getGPGKey()
+          domain = publication
       if gpg_key == '':
         for subscription in self.getSubscriptionList():
           if subscription.getTitle()==sync_id:
             gpg_key = subscription.getGPGKey()
+            domain = subscription
       # decrypt the message if needed
       if gpg_key not in (None,''):
         filename = str(random.randrange(1,2147483600)) + '.txt'
@@ -1025,8 +1075,10 @@
       # Get the target and then find the corresponding publication or
       # Subscription
       #LOG('type(text) : ',0,type(text))
+      if domain.getSyncContentType() == self.CONTENT_TYPE['SYNCML_WBXML']:
+        text = self.wbxml2xml(text)
+      #LOG('readResponse, text after wbxml :\n', 0, text)
       xml = Parse(text)
-      #XXX this function is not very optimized and should be improved
       url = self.getTarget(xml)
       for publication in self.getPublicationList():
         if publication.getPublicationUrl()==url and \
@@ -1042,6 +1094,8 @@
             xml = result['xml']
             #must be commented because this method is alredy called
             #xml = self.sendResponse(xml=xml,domain=publication,send=0)
+            if publication.getSyncContentType() == self.CONTENT_TYPE['SYNCML_WBXML']:
+              xml = self.xml2wbxml(xml)
             return xml
       
       for subscription in self.getSubscriptionList():
@@ -1096,4 +1150,41 @@
     conduit_object = getattr(conduit_module, conduit)()
     return conduit_object.addNode(**kw)
 
+  def hexdump(self, raw=''):
+    """
+    this function is used to display the raw in a readable format :
+    it display raw in hexadecimal format and display too the printable 
+    characters (because if not printable characters are printed, it makes 
+    terminal display crash)
+    """
+    buf = ""
+    line = ""
+    start = 0
+    done = False
+    while not done:
+        end = start + 16
+        max = len(str(raw))
+        if end > max:
+            end = max
+            done = True
+        chunk = raw[start:end]
+        for i in xrange(len(chunk)):
+            if i > 0:
+                spacing = " "
+            else:
+                spacing = ""
+            buf += "%s%02x" % (spacing, ord(chunk[i]))
+        if done:
+            for i in xrange(16 - (end % 16)):
+                buf += "   "
+        buf += "  "
+        for c in chunk:
+            val = ord(c)
+            if val >= 33 and val <= 126:
+                buf += c
+            else:
+                buf += "."
+        buf += "\n"
+        start += 16
+    return buf 
 InitializeClass( SynchronizationTool )

Modified: erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py (original)
+++ erp5/trunk/products/ERP5SyncML/XMLSyncUtils.py Tue Aug  7 16:16:47 2007
@@ -112,7 +112,7 @@
     xml('     <LocURI>%s</LocURI>\n' % source)
     xml('    </Source>\n')
     xml('    <Meta>\n')
-    xml('     <Anchor xmlns=\'syncml:metinf\'>\n')
+    xml('     <Anchor>\n')
     xml('      <Last>%s</Last>\n' % last_anchor)
     xml('      <Next>%s</Next>\n' % next_anchor)
     xml('     </Anchor>\n')
@@ -122,30 +122,87 @@
     xml_a = ''.join(xml_list)
     return xml_a
 
-  def SyncMLStatus(self, cmd_id, target_ref, source_ref, sync_code, 
-      next_anchor=None):
-    """
-      Since the Status section is always almost the same, this is the
-      way to set one quickly.
-    """
+  def SyncMLStatus(self, remote_xml, data_code, cmd_id, next_anchor, 
+      subscription=None):
+    """
+    return a status bloc with all status corresponding to the syncml
+    commands in remote_xml
+    """
+
+    #list of element in the SyncBody bloc
+    syncbody_element_list = remote_xml.xpath('//SyncBody/*')
+    
+    message_id = self.getMessageId(remote_xml)
     xml_list = []
     xml = xml_list.append
-    xml('  <Status>\n')
-    xml('   <CmdID>%s</CmdID>\n' % cmd_id)
-    xml('   <TargetRef>%s</TargetRef>\n' % target_ref)
-    xml('   <SourceRef>%s</SourceRef>\n' % source_ref)
-    xml('   <Data>%s</Data>\n' % sync_code)
-    if next_anchor is not None:
-      xml('   <Item>\n')
-      xml('    <Data>\n')
-      xml('     <Anchor xmlns=\'syncml:metinf\'>\n')
-      xml('      <Next>%s</Next>\n' % next_anchor)
-      xml('     </Anchor>\n')
-      xml('    </Data>\n')
-      xml('   </Item>\n')
-    xml('  </Status>\n')
+
+    if data_code != self.AUTH_REQUIRED:#because for AUTH_REQUIRED, SyncMLChal is                                       #called
+      # 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' % self.getMessageId(remote_xml))
+      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'))
+      if isinstance(data_code, int):
+        data_code = str(data_code)
+      xml('   <Data>%s</Data>\n' % data_code)
+      xml('  </Status>\n')
+    #add the status bloc corresponding to the receive command
+    for syncbody_element in syncbody_element_list:
+      
+      #LOG('SyncMLStatus : ',0,"command:%s, subscription:%s" % (str(syncbody_element.nodeName), subscription))
+      if str(syncbody_element.nodeName) not in ('Status', 'Final', 'Get'):
+        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' % str(self.ITEM_ADDED))
+        elif syncbody_element.nodeName.encode('utf-8') == 'Alert' and \
+            syncbody_element.xpath('string(.//Data)').encode('utf-8') == \
+            str(self.SLOW_SYNC):
+          xml('   <Data>%s</Data>\n' % str(self.REFRESH_REQUIRED))
+        else:
+          xml('   <Data>%s</Data>\n' % str(self.SUCCESS))
+
+       # if syncbody_element.xpath('.//Item') not in ([], None, '') and\
+       #     syncbody_element.xpath('.//Item.....'): #contient une ancre Next...
+        if str(syncbody_element.nodeName) == 'Alert':
+          xml('   <Item>\n')
+          xml('    <Data>\n')
+          xml('     <Anchor>\n')
+          xml('      <Next>%s</Next>\n' % next_anchor)
+          xml('     </Anchor>\n')
+          xml('    </Data>\n')
+          xml('   </Item>\n')
+        xml('  </Status>\n')
+
+      if str(syncbody_element.nodeName) == 'Get' and subscription != None:
+        cmd_ref = syncbody_element.xpath('string(.//CmdID)').encode('utf-8')
+        syncml_result = self.SyncMLPut(cmd_id, subscription, markup='Results', 
+            cmd_ref=cmd_ref, message_id=self.getMessageId(remote_xml))
+        xml(syncml_result)
+        cmd_id += 1
     xml_a = ''.join(xml_list)
-    return xml_a
+    return {'xml':xml_a, 'cmd_id':cmd_id}
 
   def SyncMLConfirmation(self, cmd_id=None, target_ref=None, cmd=None, 
       sync_code=None, msg_ref=None, cmd_ref=None, source_ref=None, 
@@ -191,6 +248,8 @@
     xml = xml_list.append
     xml('  <Status>\n')
     xml('   <CmdID>%s</CmdID>\n' % cmd_id)
+    xml('   <MsgRef>1</MsgRef>\n')
+    xml('   <CmdRef>0</CmdRef>\n')
     xml('   <Cmd>%s</Cmd>\n' % cmd)
     xml('   <TargetRef>%s</TargetRef>\n' % target_ref)
     xml('   <SourceRef>%s</SourceRef>\n' % source_ref)
@@ -205,9 +264,12 @@
     xml_a = ''.join(xml_list)
     return xml_a
 
-  def SyncMLPut(self, cmd_id, subscription):
+  def SyncMLPut(self, cmd_id, subscription, markup='Put', cmd_ref=None, 
+      message_id=None):
     """
     this is used to inform the server of the CTType version supported
+    but if the server use it to respond to a Get request, it's a <Result> markup
+    instead of <Put>
     """
     from Products.ERP5SyncML import Conduit
     # Import the conduit and get it
@@ -222,65 +284,76 @@
                                   globals(), locals(), [''])
       conduit = getattr(conduit_module, conduit_name)()
     #if the conduit support the SyncMLPut :
-    if hasattr(conduit, 'getCapabilitiesCTType') and \
+    if hasattr(conduit, 'getCapabilitiesCTTypeList') 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('  <%s>\n' % markup)
+      xml('   <CmdID>%s</CmdID>\n' % cmd_id)
+      if message_id not in (None, ''):
+        xml('   <MsgRef>%s</MsgRef>\n' % message_id)
+      if cmd_ref not in (None, '') :
+        xml('   <CmdRef>%s</CmdRef>\n' % cmd_ref)
+      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>Nexedi</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.getPreferedCapabilitieCTType())
+      xml('        <VerCT>%s</VerCT>\n' % \
+          conduit.getPreferedCapabilitieVerCT())
+      xml('       </Rx-Pref>\n')
+      for type in conduit.getCapabilitiesCTTypeList():
+        if type != self.MEDIA_TYPE['TEXT_XML']:
+          for rx_version in conduit.getCapabilitiesVerCTList(type):
+            xml('       <Rx>\n')
+            xml('        <CTType>%s</CTType>\n' % type)
+            xml('        <VerCT>%s</VerCT>\n' % rx_version)
+            xml('       </Rx>\n')
+
+      xml('       <Tx-Pref>\n')
+      xml('        <CTType>%s</CTType>\n' % \
+          conduit.getPreferedCapabilitieCTType())
+      xml('        <VerCT>%s</VerCT>\n' % \
+          conduit.getPreferedCapabilitieVerCT())
+      xml('       </Tx-Pref>\n')
+      for type in conduit.getCapabilitiesCTTypeList():
+        if type != self.MEDIA_TYPE['TEXT_XML']:
+          for tx_version in conduit.getCapabilitiesVerCTList(type):
+            xml('       <Tx>\n')
+            xml('        <CTType>%s</CTType>\n' % type)
+            xml('        <VerCT>%s</VerCT>\n' % tx_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('  </%s>\n' % markup)
       xml_a = ''.join(xml_list)
       return xml_a
     return ''
@@ -333,7 +406,7 @@
     xml_a = ''.join(xml_list)
     return xml_a
 
-  def deleteXMLObject(self, cmd_id=0, object_gid=None, xml_object=''):
+  def deleteXMLObject(self, cmd_id=0, object_gid=None, rid=None, xml_object=''):
     """
       Delete an object with the SyncML protocol
     """
@@ -342,9 +415,14 @@
     xml('   <Delete>\n')
     xml('    <CmdID>%s</CmdID>\n' % cmd_id)
     xml('    <Item>\n')
-    xml('     <Source>\n')
-    xml('      <LocURI>%s</LocURI>\n' % object_gid)
-    xml('     </Source>\n')
+    if rid not in (None, ''):
+      xml('     <Target>\n')
+      xml('      <LocURI>%s</LocURI>\n' % rid)
+      xml('     </Target>\n')
+    else:
+      xml('     <Source>\n')
+      xml('      <LocURI>%s</LocURI>\n' % object_gid)
+      xml('     </Source>\n')
     #xml('     <Data>\n')  #this 2 lines seems to be useless
     #xml('     </Data>\n')
     xml('    </Item>\n')
@@ -353,7 +431,7 @@
     return xml_a
 
   def replaceXMLObject(self, cmd_id=0, object=None, xml_string=None,
-                       more_data=0, gid=None, media_type=None):
+                       more_data=0, gid=None, rid=None, media_type=None):
     """
       Replace an object with the SyncML protocol
     """
@@ -365,9 +443,14 @@
     xml('     <Type>%s</Type>\n' % media_type)
     xml('    </Meta>\n')
     xml('    <Item>\n')
-    xml('     <Source>\n')
-    xml('      <LocURI>%s</LocURI>\n' % str(gid))
-    xml('     </Source>\n')
+    if rid is not None:
+      xml('     <Target>\n')
+      xml('      <LocURI>%s</LocURI>\n' % str(rid))
+      xml('     </Target>\n')
+    else:
+      xml('     <Source>\n')
+      xml('      <LocURI>%s</LocURI>\n' % str(gid))
+      xml('     </Source>\n')
     xml('     <Data>')
     xml(xml_string)
     xml('     </Data>\n')
@@ -429,11 +512,7 @@
       Return the value of the last anchor, in the
       alert section of the xml_stream
     """
-    first_node = xml_stream.childNodes[0]
-
-    # Get informations from the body
-    client_body = first_node.childNodes[3]
-    last_anchor = client_body.xpath('string(/Alert/Item/Meta/Anchor/Last)')
+    last_anchor = xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Last)')
     last_anchor = last_anchor.encode('utf-8')
     return last_anchor
 
@@ -442,17 +521,29 @@
       Return the value of the next anchor, in the
       alert section of the xml_stream
     """
-    first_node = xml_stream.childNodes[0]
-    if first_node.nodeName != "SyncML":
-      print "This is not a SyncML message"
-
-    # Get informations from the body
-    client_body = first_node.childNodes[3]
-    next_anchor = client_body.xpath('string(/Alert/Item/Meta/Anchor/Next)')
+    next_anchor = xml_stream.xpath('string(.//Alert/Item/Meta/Anchor/Next)')
     next_anchor = next_anchor.encode('utf-8')
     return next_anchor
 
   def getSourceURI(self, xml):
+    """
+    return the source uri of the data base
+    """
+    source_uri = xml.xpath('string(//SyncBody/Alert/Item/Source/LocURI)')
+    if isinstance(source_uri, unicode):
+      source_uri = source_uri.encode('utf-8')
+    return source_uri
+  
+  def getTargetURI(self, xml):
+    """
+    return the target uri of the data base
+    """
+    target_uri = xml.xpath('string(//SyncBody/Alert/Item/Target/LocURI)')
+    if isinstance(target_uri, unicode):
+      target_uri = target_uri.encode('utf-8')
+    return target_uri
+
+  def getSubscriptionUrl(self, xml):
     """
     return the source URI of the syncml header
     """
@@ -540,6 +631,27 @@
         not in ('', None, []):
       return True
     return False
+
+  def checkMap(self, xml_stream):
+    """
+      Check if there's a Map section in the xml_stream
+    """
+    if xml_stream.xpath('string(SyncML/SyncBody/Map)') \
+        not in ('', None, []):
+      return True
+    return False
+
+  def setRidWithMap(self, xml_stream, subscriber):
+    """
+      get all the local objects of the given target id and set them the rid with
+      the given source id (in the Map section)
+    """
+    item_list = xml_stream.xpath('SyncML/SyncBody/Map/MapItem')
+    for map_item in item_list:
+      gid = map_item.xpath('string(.//Target/LocURI)').encode('utf-8')
+      signature = subscriber.getSignatureFromGid(gid)
+      rid = map_item.xpath('string(.//Source/LocURI)').encode('utf-8')
+      signature.setRid(rid)   
 
   def getAlertCode(self, xml_stream):
     """
@@ -688,7 +800,7 @@
     if object is not None, this usually means we want to set the
     actual xupdate on the signature.
     """
-    #LOG('\ngetSyncMLData starting...',0, domain.getId())
+    LOG('getSyncMLData starting...',0,'')
     if isinstance(conduit, str):
       conduit = self.getConduitByName(conduit)
     local_gid_list = []
@@ -714,10 +826,10 @@
             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
+              #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)
+                  or '', object_gid=object_gid, rid=rid,cmd_id=cmd_id)
               cmd_id += 1
       #delete Signature if object does not exist anymore
       for known_gid in subscriber.getGidList():
@@ -726,7 +838,6 @@
     local_gid_list = []
     loop = 0
     for object_path in subscriber.getRemainingObjectPathList():
-      #LOG('getRemainingObject :',0,[[subscriber.getRemainingObjectPathList()[i][3] for i in range(5)],[subscriber.getRemainingObjectPathList()[-i][3] for i in range(5)]])
       if max is not None and loop >= max:
         result['finished'] = 0
         break
@@ -806,9 +917,12 @@
           if not signature.checkMD5(xml_object):
             set_synchronized = 0
             # This object has changed on this side, we have to generate some xmldiff
-            xml_string = self.getXupdateObject(
+            if subscriber.getMediaType() == self.MEDIA_TYPE['TEXT_XML']:
+              xml_string = self.getXupdateObject(
                                       domain.getXMLFromObject(object),
                                       signature.getXML())
+            else: #if there is no xml, we re-send all the object
+              xml_string=domain.getXMLFromObject(object)
             if xml_string.count('\n') > self.MAX_LINES:
               # This make comment fails, so we need to replace
               if xml_string.find('--') >= 0:
@@ -828,11 +942,11 @@
             signature.setStatus(status)
             if subscriber.getMediaType() != self.MEDIA_TYPE['TEXT_XML']:
               xml_string = domain.getXMLFromObject(object)
-            gid = signature.getRid()#in fisrt, we try with rid if there is one
-            if gid == None:
-              gid = signature.getGid()
+            rid = signature.getRid()#in fisrt, we try with rid if there is one
+            gid = signature.getGid()
             syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object,
-                                                 gid=gid, xml_string=xml_string,
+                                                 gid=gid, rid=rid, 
+                                                 xml_string=xml_string,
                                                  more_data=more_data,
                                                  media_type=subscriber.getMediaType())
             cmd_id += 1
@@ -884,11 +998,10 @@
             xml_string = domain.getXMLFromObject(object)
           #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()
+            rid = signature.getRid()#in fisrt, we try with rid if there is one
+            gid = signature.getGid()
             syncml_data += self.replaceXMLObject(cmd_id=cmd_id, object=object,
-                gid=gid, xml_string=xml_string, more_data=more_data,
+                gid=gid, rid=rid, 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
@@ -918,7 +1031,10 @@
     has_next_action = 0
     destination = self.unrestrictedTraverse(domain.getDestinationPath())
     #LOG('applyActionList args',0,'domain : %s\n subscriber : %s\n cmd_id : %s' % (domain, subscriber, cmd_id))
+    LOG('applyActionList', 0, self.getSyncActionList(remote_xml))
     for action in self.getSyncActionList(remote_xml):
+      if isinstance(action, unicode):
+        action = action.encode('utf-8')
       conflict_list = []
       status_code = self.SUCCESS
       # Thirst we have to check the kind of action it is
@@ -939,6 +1055,8 @@
         signature.setRid(rid)
       #LOG('gid == rid ?', 0, 'gid=%s, rid=%s' % (gid, rid))
       object = subscriber.getObjectFromGid(gid)
+      if object == None:
+        object = subscriber.getObjectFromRid(rid)
       #LOG('applyActionList subscriber.getObjectFromGid %s' % gid,0,object)
       if signature is None:
         #LOG('applyActionList, signature is None',0,signature)
@@ -1014,6 +1132,8 @@
           if object is not None:
             #LOG('SyncModif',0,'object: %s will be updated...' % object.id)
             signature = subscriber.getSignatureFromGid(gid)
+            if signature is None:
+              signature = subscriber.getSignatureFromRid(gid)
             #LOG('SyncModif',0,'previous signature: %s' % str(signature))
             previous_xml = signature.getXML()
             #LOG('SyncModif',0,'previous signature: %i' % len(previous_xml))
@@ -1103,6 +1223,8 @@
         #  object_gid = status['target']
         #else:
         object_gid = status['source']
+        if object_gid in ('', None, []):
+          object_gid = status['target']
         status_code = int(status['code'])
         if status_cmd in ('Add','Replace'):
           has_status_list = 1
@@ -1126,7 +1248,8 @@
             signature.setStatus(self.SYNCHRONIZED)
         elif status_cmd == 'Delete':
           if status_code == self.SUCCESS:
-            signature = subscriber.getSignatureFromGid(object_gid) or           subscriber.getSignatureFromRid(object_gid)
+            signature = subscriber.getSignatureFromGid(object_gid) or \
+            subscriber.getSignatureFromRid(object_gid)
             if signature is not None:
               subscriber.delSignature(signature.getGid())
     return (destination_waiting_more_data, has_status_list)
@@ -1177,20 +1300,20 @@
     """
     has_response = 0 #check if syncmodif replies to this messages
     cmd_id = 1 # specifies a SyncML message-unique command identifier
-    #LOG('SyncModif',0,'Starting... domain: %s' % str(domain))
+    LOG('SyncModif',0,'Starting... domain: %s' % str(domain))
 
     first_node = remote_xml.childNodes[0]
     # Get informations from the header
     xml_header = first_node.childNodes[1]
     if xml_header.nodeName != "SyncHdr":
-      #LOG('PubSyncModif',0,'This is not a SyncML Header')
+      LOG('PubSyncModif',0,'This is not a SyncML Header')
       raise ValueError, "Sorry, This is not a SyncML Header"
 
     subscriber = domain # If we are the client, this is fine
     simulate = 0 # used by applyActionList, should be 0 for client
     if domain.domain_type == self.PUB:
       simulate = 1
-      subscription_url = self.getSourceURI(xml_header) 
+      subscription_url = self.getSubscriptionUrl(xml_header) 
       subscriber = domain.getSubscriber(subscription_url)
 
     # We have to check if this message was not already, this can be dangerous
@@ -1198,19 +1321,21 @@
     message_id = self.getMessageId(remote_xml)
     correct_message = subscriber.checkCorrectRemoteMessageId(message_id)
     if not correct_message: # We need to send again the message
-      #LOG('SyncModif, no correct message:',0,"sending again...")
+      LOG('SyncModif, no correct message:',0,"sending again...")
       last_xml = subscriber.getLastSentMessage()
-      #LOG("last_xml :", 0, last_xml)
+      LOG("last_xml :", 0, last_xml)
       if last_xml != '':
         has_response = 1
         if domain.domain_type == self.PUB: # We always reply
           self.sendResponse(from_url=domain.publication_url, 
               to_url=subscriber.subscription_url, sync_id=domain.getTitle(), 
-              xml=last_xml,domain=domain)
+              xml=last_xml,domain=domain, 
+              content_type=domain.getSyncContentType()) 
         elif domain.domain_type == self.SUB:
           self.sendResponse(from_url=domain.subscription_url, 
               to_url=domain.publication_url, sync_id=domain.getTitle(), 
-              xml=last_xml, domain=domain)
+              xml=last_xml, domain=domain,
+              content_type=domain.getSyncContentType())
       return {'has_response':has_response,'xml':last_xml}
     subscriber.setLastSentMessage('')
 
@@ -1250,60 +1375,9 @@
     # 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')
+    xml_status, cmd_id = self.SyncMLStatus(remote_xml, self.SUCCESS, cmd_id, 
+         subscriber.getNextAnchor(), subscription=subscriber).values()
+    xml(xml_status)
 
     destination_url = ''
     # alert message if we want more data
@@ -1382,24 +1456,14 @@
     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')
+      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')
       xml(syncml_data)
       xml('  </Sync>\n')
     xml(xml_confirmation)
@@ -1412,7 +1476,8 @@
       subscriber.setLastSentMessage(xml_a)
       self.sendResponse(from_url=domain.publication_url, 
           to_url=subscriber.subscription_url, sync_id=domain.getTitle(), 
-          xml=xml_a,domain=domain)
+          xml=xml_a,domain=domain,
+          content_type=domain.getSyncContentType())
       has_response = 1
     elif domain.domain_type == self.SUB:
       if self.checkAlert(remote_xml) or \
@@ -1421,6 +1486,37 @@
         subscriber.setLastSentMessage(xml_a)
         self.sendResponse(from_url=domain.subscription_url, 
             to_url=domain.publication_url, sync_id=domain.getTitle(), 
-            xml=xml_a,domain=domain)
+            xml=xml_a,domain=domain,
+            content_type=domain.getSyncContentType())
         has_response = 1
     return {'has_response':has_response,'xml':xml_a}
+
+  def xml2wbxml(self, xml):
+    """
+    convert xml string to wbxml using a temporary file
+    """
+    LOG('xml2wbxml starting ...',0,'')
+    import os
+    f = open('/tmp/xml2wbxml', 'w')
+    f.write(xml)
+    f.close()
+    os.system('/usr/bin/xml2wbxml -o /tmp/xml2wbxml /tmp/xml2wbxml')
+    f = open('/tmp/xml2wbxml', 'r')
+    wbxml = f.read()
+    f.close()
+    return wbxml
+
+  def wbxml2xml(self, wbxml):
+    """
+    convert wbxml string to xml using a temporary file
+    """
+    LOG('wbxml2xml starting ...',0,'')
+    import os
+    f = open('/tmp/wbxml2xml', 'w')
+    f.write(wbxml)
+    f.close()
+    os.system('/usr/bin/wbxml2xml -o /tmp/wbxml2xml /tmp/wbxml2xml')
+    f = open('/tmp/wbxml2xml', 'r')
+    xml = f.read()
+    f.close()
+    return xml

Modified: erp5/trunk/products/ERP5SyncML/dtml/explainSynchronizationTool.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/explainSynchronizationTool.dtml?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/explainSynchronizationTool.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/explainSynchronizationTool.dtml Tue Aug  7 16:16:47 2007
@@ -55,6 +55,12 @@
         <b>Query :</b> the type of objects you want to synchronize</br>
         <b>XML Mapping :</b> the page template used on each object before 
         an export</br>
+        <b>Synchronize with other ERP5 sites :</b> The method currently used 
+        to send data to erp5 and external sites is not the same (it will be 
+        improved soon)</br>
+        <b>Content Type :</b> the type of content use to exchange data
+        (could be 'application/vnd.syncml+wbxml' or 
+        'application/vnd.syncml+xml' for example)</br>
         <b>Conduit :</b> the conduit used to synchronize</br>
         <b>GPG key name :</b>a name of gpg key to use</br>
         <b>Id Generator :</b> This set the method name wich allows to 
@@ -80,6 +86,12 @@
         <b>Query :</b> the type of objects you want to synchronize</br>
         <b>XML Mapping :</b> the page template used on each object before 
         an export</br>
+        <b>Synchronize with other ERP5 sites :</b> The method currently used 
+        to send data to erp5 and external sites is not the same (it will be 
+        improved soon)</br>
+        <b>Content Type :</b> the type of content use to exchange data
+        (could be 'application/vnd.syncml+wbxml' or 
+        'application/vnd.syncml+xml' for example)</br>
         <b>Conduit :</b> the conduit used to synchronize</br>
         <b>GPG key name :</b>a name of gpg key to use</br>
         <b>Id Generator :</b> This set the method name wich allows to 

Modified: erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/managePublications.dtml Tue Aug  7 16:16:47 2007
@@ -111,6 +111,26 @@
       <tr>
         <td align="left" valign="top">
         <div class="form-label">
+        Synchronize with other ERP5 sites
+        </label></div>
+        </td>
+        <td align="left" valign="top">
+          <input type="checkbox" name="synchronize_with_erp5_sites" value="1" <dtml-if expr="getSynchronizeWithERP5Sites()">CHECKED</dtml-if>>
+        </td>
+      </tr>
+      <tr>
+        <td align="left" valign="top">
+        <div class="form-label">
+        Content Type
+        </label></div>
+        </td>
+        <td align="left" valign="top">
+        <input type="text" name="sync_content_type" value="<dtml-var getSyncContentType>" size="40" />
+        </td>
+      </tr>
+      <tr>
+        <td align="left" valign="top">
+        <div class="form-label">
         Conduit
         </label></div>
         </td>

Modified: erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manageSubscriptions.dtml Tue Aug  7 16:16:47 2007
@@ -147,6 +147,26 @@
       <tr>
         <td align="left" valign="top">
         <div class="form-label">
+        Synchronize with other ERP5 sites
+        </label></div>
+        </td>
+        <td align="left" valign="top">
+          <input type="checkbox" name="synchronize_with_erp5_sites" value="1" <dtml-if expr="getSynchronizeWithERP5Sites()">CHECKED</dtml-if>>
+        </td>
+      </tr>
+      <tr>
+        <td align="left" valign="top">
+        <div class="form-label">
+        Content Type
+        </label></div>
+        </td>
+        <td align="left" valign="top">
+        <input type="text" name="sync_content_type" value="<dtml-var getSyncContentType>" size="40" />
+        </td>
+      </tr>
+      <tr>
+        <td align="left" valign="top">
+        <div class="form-label">
         Conduit
         </label></div>
         </td>

Modified: erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manage_addPublication.dtml Tue Aug  7 16:16:47 2007
@@ -106,6 +106,26 @@
   <tr>
     <td align="left" valign="top">
     <div class="form-label">
+    Synchronize with other ERP5 sites
+    </label></div>
+    </td>
+    <td align="left" valign="top">
+      <input type="checkbox" name="synchronize_with_erp5_sites" value="1">
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Content Type
+    </label></div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="sync_content_type" size="40" />
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
     Conduit
     </label></div>
     </td>

Modified: erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml?rev=15529&r1=15528&r2=15529&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml (original)
+++ erp5/trunk/products/ERP5SyncML/dtml/manage_addSubscription.dtml Tue Aug  7 16:16:47 2007
@@ -143,6 +143,26 @@
   <tr>
     <td align="left" valign="top">
     <div class="form-label">
+    Synchronize with other ERP5 sites
+    </label></div>
+    </td>
+    <td align="left" valign="top">
+      <input type="checkbox" name="synchronize_with_erp5_sites" value="1">
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
+    Content Type
+    </label></div>
+    </td>
+    <td align="left" valign="top">
+    <input type="text" name="sync_content_type" size="40" />
+    </td>
+  </tr>
+  <tr>
+    <td align="left" valign="top">
+    <div class="form-label">
     Conduit
     </label></div>
     </td>




More information about the Erp5-report mailing list