[Commits] (pavlov) Adding support for fetching ItemCollections, storing etags and last-modified dates, verifying etags. fetching will now notice changes on the server and will overwrite local changes with those on the server

commits at osafoundation.org commits at osafoundation.org
Thu Aug 12 11:00:33 PDT 2004


Commit by: pavlov
Modified files:
chandler/parcels/osaf/framework/parcel.xml 1.11 1.12
chandler/parcels/osaf/framework/webdav/.cvsignore None 1.1
chandler/parcels/osaf/framework/webdav/TestDAV.py None 1.1
chandler/parcels/osaf/framework/webdav/DAVItem.py 1.2 1.3
chandler/parcels/osaf/framework/webdav/Dav.py 1.2 1.3
chandler/parcels/osaf/framework/webdav/Export.py 1.3 1.4
chandler/parcels/osaf/framework/webdav/Import.py 1.3 1.4
chandler/parcels/osaf/framework/webdav/parcel.xml 1.2 1.3

Log message:
Adding support for fetching ItemCollections, storing etags and last-modified dates, verifying etags.  fetching will now notice changes on the server and will overwrite local changes with those on the server


ViewCVS links:
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/parcel.xml.diff?r1=text&tr1=1.11&r2=text&tr2=1.12
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/webdav/.cvsignore?rev=1.1&content-type=text/vnd.viewcvs-markup
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/webdav/TestDAV.py?rev=1.1&content-type=text/vnd.viewcvs-markup
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/webdav/DAVItem.py.diff?r1=text&tr1=1.2&r2=text&tr2=1.3
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/webdav/Dav.py.diff?r1=text&tr1=1.2&r2=text&tr2=1.3
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/webdav/Export.py.diff?r1=text&tr1=1.3&r2=text&tr2=1.4
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/webdav/Import.py.diff?r1=text&tr1=1.3&r2=text&tr2=1.4
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/webdav/parcel.xml.diff?r1=text&tr1=1.2&r2=text&tr2=1.3

Index: chandler/parcels/osaf/framework/webdav/DAVItem.py
diff -u chandler/parcels/osaf/framework/webdav/DAVItem.py:1.2 chandler/parcels/osaf/framework/webdav/DAVItem.py:1.3
--- chandler/parcels/osaf/framework/webdav/DAVItem.py:1.2	Mon Jul 19 08:52:22 2004
+++ chandler/parcels/osaf/framework/webdav/DAVItem.py	Thu Aug 12 11:00:32 2004
@@ -24,16 +24,26 @@
         super(DAVItem, self).__init__()
 
         self.dav = dav
-
         self.doc = self._allprop(unicode(dav.url))
 
     def _allprop(self, url, depth = 0):
         """ Fetch all the properties of a resource """
         body = davlib.XML_DOC_HEADER + \
                '<D:propfind xmlns:D="DAV:">' + \
-               '<D:allprop/>' + \
+               '<D:allprop/><D:getetag/><D:getlastmodified/>' + \
                '</D:propfind>'
 
+        # In order to get see if the etag matches something, we need
+        # to first fetch the item, get its uuid and getetag properties.
+        # At that point we can look it up in the itemMap and see if we
+        # already have it, and then match the etag associated with that.
+        #
+        # for now, lets just fetch all the properties and match the etag
+        # later.
+        #
+        # we could also make the get code smarter by allowing you to "get"
+        # an item that was already shared (and hence has a url and an etag
+        # already.  This might be the best solution.
         r = self.dav.newConnection().propfind(url, body, depth)
 
         xmlgoop = r.read()
@@ -59,6 +69,12 @@
         from repository.util.UUID import UUID
         return UUID(value)
 
+    def _getETag(self):
+        return self._getAttribute('getetag', 'DAV:')
+
+    def _getLastModified(self):
+        return self._getAttribute('getlastmodified', 'DAV:')
+
     def getAttribute(self, attr):
         """ takes an Attribute argument """
         attrname = attr.itsName
@@ -85,3 +101,5 @@
 
     itsKind = property(_getKind)
     itsUUID = property(_getUUID)
+    etag = property(_getETag)
+    lastModified = property(_getLastModified)

Index: chandler/parcels/osaf/framework/parcel.xml
diff -u chandler/parcels/osaf/framework/parcel.xml:1.11 chandler/parcels/osaf/framework/parcel.xml:1.12
--- chandler/parcels/osaf/framework/parcel.xml:1.11	Mon Jun 21 17:51:58 2004
+++ chandler/parcels/osaf/framework/parcel.xml	Thu Aug 12 11:00:31 2004
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="iso-8859-1"?>
 
-<!-- $Revision: 1.11 $ -->
-<!-- $Date: 2004/06/22 00:51:58 $ -->
+<!-- $Revision: 1.12 $ -->
+<!-- $Date: 2004/08/12 18:00:31 $ -->
 <!-- Copyright (c) 2003 Open Source Applications Foundation -->
 <!-- License: http://osafoundation.org/Chandler_0.1_license_terms.htm -->
 
@@ -9,7 +9,8 @@
         itsName="framework"
         xmlns="http://osafoundation.org/parcels/core"
         xmlns:app="http://osafoundation.org/parcels/osaf/framework"
-        xmlns:events="http://osafoundation.org/parcels/osaf/framework/notifications/schema">
+        xmlns:events="http://osafoundation.org/parcels/osaf/framework/notifications/schema"
+        xmlns:webdav="http://osafoundation.org/parcels/osaf/framework/webdav">
 
   <displayName>Application Schema</displayName>
   <description>Schemas used by the Chandler Application</description>
@@ -19,4 +20,6 @@
   <events:Event itsName="commit_history"/>
   <events:Event itsName="query_changed"/>
 
+  <webdav:Sharing itsName="GlobalShare"/>
+
 </Parcel>

Index: chandler/parcels/osaf/framework/webdav/Dav.py
diff -u chandler/parcels/osaf/framework/webdav/Dav.py:1.2 chandler/parcels/osaf/framework/webdav/Dav.py:1.3
--- chandler/parcels/osaf/framework/webdav/Dav.py:1.2	Mon Jul 12 12:13:00 2004
+++ chandler/parcels/osaf/framework/webdav/Dav.py	Thu Aug 12 11:00:32 2004
@@ -5,6 +5,12 @@
 
 import Import, Export
 
+"""
+ * If I make ItemCollections use a refcollection under the hood as a real attribute
+   then I can get rid of most of the code in put/getCollection as it will just do
+   the right thing automatically.  Wouldn't that be nice.
+"""
+
 class DAV(object):
     def __init__(self, resourceURL):
         super(DAV, self).__init__()
@@ -26,6 +32,16 @@
 
     def getCollection(self):
         """ gives back a new ItemCollection """
+        collection = self.get()
+
+        # XXX i really don't like duplicating the code in Import.py
+        listXmlGoop = collection._getAttribute('http://www.osafoundation.org/', 'items')
+        nodes = Import.makeAndParse(listXmlGoop)
+
+        for node in nodes:
+            item = DAV(node.content).get()
+            collection.add(item)
+
         # figure out properties of the collection itself.. I should share code
         # with Import.py here..
         # get a listing of all items in the collection... propfind depth 1
@@ -46,21 +62,22 @@
         returns a url to a webdav collection containing
         all of the items in 'itemCollection'
         """
-        # XXX the following code probably belongs in Export.py
+        # just put the item collection as a normal item first
+        self.put(itemCollection)
 
-        # make new dir for the collection, get its base url
-        collectionURL = self.url.join(itemCollection.itsUUID.str16() + '/')
-
-        r = DAV(collectionURL).newConnection().mkcol(collectionURL.path)
-        print collectionURL, collectionURL.path
-        # XXX parse response..
-        # set attributes on the collection
+        # then attatch extra data to it
+        itemList = '<o:items xmlns:o="http://www.osafoundation.org/">'
 
         for item in itemCollection:
-            itemURL = collectionURL.join(item.itsUUID.str16())
+            itemURL = self.url.join(item.itsUUID.str16())
             DAV(itemURL).put(item)
+            itemList = itemList + '<itemref>' + unicode(itemURL) + '</itemref>'
+
+        itemList = itemList + '</o:items>'
+
+        self.newConnection().setprops2(unicode(self.url), itemList)
 
-        return collectionURL
+        return self.url
 
     def sync(self):
         raise NotImplementedError
@@ -72,4 +89,4 @@
         port = url.port or 80
 
         davlib.DAV.__init__(self, host, port)
-        self.setauth('username', 'password')
+        self.setauth('test', 'test')

Index: chandler/parcels/osaf/framework/webdav/Export.py
diff -u chandler/parcels/osaf/framework/webdav/Export.py:1.3 chandler/parcels/osaf/framework/webdav/Export.py:1.4
--- chandler/parcels/osaf/framework/webdav/Export.py:1.3	Mon Jul 19 08:52:22 2004
+++ chandler/parcels/osaf/framework/webdav/Export.py	Thu Aug 12 11:00:32 2004
@@ -3,6 +3,7 @@
 import xml.sax.saxutils
 import libxml2
 
+import application.Globals as Globals
 from repository.item.Item import Item
 
 def parseResponse(response):
@@ -23,13 +24,34 @@
     from Dav import DAV
 
     # hack to avoid infinite recursion
+    # instead of this, we should figure out a way to know if the item has changed since its last
+    # etag.. maybe watch for commits?  if it has, then we should see if the thing on the server
+    # has changed.. if-match? and then try again.  we gotta get rid of this hack....
     if hasattr(item, 'davified'):
         return
     item.davified = True
 
     url = unicode(dav.url)
 
-    r = dav.newConnection().put(url, item.itsKind.itsName, 'text/plain')
+    # need to put an If-Match header here with the item's etag if it exists
+    extraHeaders = {}
+
+    etag = item.getAttributeValue('etag', default=None)
+    if etag:
+        extraHeaders['If-Match'] = etag
+    r = dav.newConnection().put(url, item.itsKind.itsName, 'text/plain', None, extraHeaders)
+
+    # now we need to see if this request failed due to the etags being different
+    
+    # need to handle merging/conflicts here...
+
+    # set them here, even though we have to set them again later
+    item.etag = r.getheader('ETag', default='')
+    item.lastModified = r.getheader('Last-Modified', default='')
+
+    # ew...
+    sharing = Globals.repository.findPath('//parcels/osaf/framework/GlobalShare') 
+    sharing.itemMap[item.itsUUID] = item.itsUUID # add an entry here to say that we're already here
 
     kind = item.itsKind
 
@@ -83,5 +105,11 @@
     print url, r.status, r.reason
     print r.read()
 
+    # argh!! i hate this.. we have to get the etag again here since
+    # some servers *cough*xythos*cough* change the etag when you proppatch
+    r = dav.newConnection().head(url)
+    item.etag = r.getheader('ETag', default='')
+    item.lastModified = r.getheader('Last-Modified', default='')
+
     return url
     #print propstring

Index: chandler/parcels/osaf/framework/webdav/Import.py
diff -u chandler/parcels/osaf/framework/webdav/Import.py:1.3 chandler/parcels/osaf/framework/webdav/Import.py:1.4
--- chandler/parcels/osaf/framework/webdav/Import.py:1.3	Mon Jul 19 08:52:22 2004
+++ chandler/parcels/osaf/framework/webdav/Import.py	Thu Aug 12 11:00:32 2004
@@ -7,8 +7,6 @@
 
 from repository.schema.Kind import Kind
 
-itemMap = {}
-
 def makeAndParse(xml):
     # given a chunk of text that is a flat xml tree like:
     # "<foo/><foo/><foo/>"
@@ -25,25 +23,36 @@
 
 def getItem(dav):
     from Dav import DAV
-    global itemMap
     repository = Globals.repository
 
     # fetch the item
     di = DAVItem.DAVItem(dav)
 
+    # ew...
+    sharing = repository.findPath('//parcels/osaf/framework/GlobalShare') 
+
     # pretend here we don't care if the item has changed..
     try:
         # get the exported item's UUID and see if we have already fetched it
         origUUID = di.itsUUID
-        return repository.findUUID(itemMap[origUUID])
+        newItem = repository.findUUID(sharing.itemMap[origUUID])
+        kind = newItem.itsKind
     except KeyError:
-        pass
+        kind = di.itsKind
+        newItem = kind.newItem(None, repository.findPath('//userdata/contentitems'))
+
+    oldEtag = newItem.getAttributeValue('etag', default=None)
+    if oldEtag == di.etag:
+        print 'no changes to item'
+        return newItem
+    else:
+        print oldEtag, di.etag
 
-    kind = di.itsKind
-    newItem = kind.newItem(None, repository.findPath('//userdata/contentitems'))
+    newItem.etag = di.etag
+    newItem.lastModified = di.lastModified
 
     # XXX hack...
-    itemMap[origUUID] = newItem.itsUUID
+    sharing.itemMap[origUUID] = newItem.itsUUID
 
     for (name, attr) in kind.iterAttributes(True):
 

Index: chandler/parcels/osaf/framework/webdav/parcel.xml
diff -u chandler/parcels/osaf/framework/webdav/parcel.xml:1.2 chandler/parcels/osaf/framework/webdav/parcel.xml:1.3
--- chandler/parcels/osaf/framework/webdav/parcel.xml:1.2	Mon Jul 19 08:52:22 2004
+++ chandler/parcels/osaf/framework/webdav/parcel.xml	Thu Aug 12 11:00:32 2004
@@ -5,4 +5,13 @@
         xmlns="http://osafoundation.org/parcels/core"
         xmlns:dav="http://osafoundation.org/parcels/osaf/framework/webdav">
 
+
+  <Kind itsName="Sharing">
+    <Attribute itsName="itemMap">
+      <cardinality value="dict"/>
+      <type itemref="UUID"/>
+      <initialValue/>
+    </Attribute>
+  </Kind>
+
 </Parcel>



More information about the Commits mailing list