[Checkins] SVN: z3c.davapp.zopefile/ This package defines the WebDAV data model for all zope.file.file.File

Michael Kerrin michael.kerrin at openapp.ie
Sun May 13 15:13:52 EDT 2007


Log message for revision 75725:
  This package defines the WebDAV data model for all zope.file.file.File
  content objects.
  

Changed:
  A   z3c.davapp.zopefile/
  A   z3c.davapp.zopefile/trunk/
  A   z3c.davapp.zopefile/trunk/buildout.cfg
  A   z3c.davapp.zopefile/trunk/setup.py
  A   z3c.davapp.zopefile/trunk/src/
  A   z3c.davapp.zopefile/trunk/src/z3c/
  A   z3c.davapp.zopefile/trunk/src/z3c/__init__.py
  A   z3c.davapp.zopefile/trunk/src/z3c/davapp/
  A   z3c.davapp.zopefile/trunk/src/z3c/davapp/__init__.py
  A   z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/
  A   z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/__init__.py
  A   z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/configure.zcml
  A   z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/ftesting.zcml
  A   z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/properties.txt
  A   z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/tests.py

-=-
Added: z3c.davapp.zopefile/trunk/buildout.cfg
===================================================================
--- z3c.davapp.zopefile/trunk/buildout.cfg	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/buildout.cfg	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1,10 @@
+[buildout]
+develop = .
+parts = test
+
+[test]
+recipe = zc.recipe.testrunner
+working-directory = .
+defaults = ["--tests-pattern", "^f?tests$"]
+eggs = z3c.dav [test]
+       z3c.davapp.zopefile [test]

Added: z3c.davapp.zopefile/trunk/setup.py
===================================================================
--- z3c.davapp.zopefile/trunk/setup.py	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/setup.py	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1,21 @@
+from setuptools import setup, find_packages
+
+setup(name = "z3c.davapp.zopefile",
+      version = "0.1",
+      author = "Michael Kerrin",
+      author_email = "michael.kerrin at openapp.ie",
+      url = "http://svn.zope.org/",
+      description = "WebDAV support for zope.file content objects",
+      license = "ZPL",
+
+      packages = find_packages("src"),
+      package_dir = {"": "src"},
+      namespace_packages = ["z3c", "z3c.davapp"],
+      install_requires = ["setuptools",
+                          "z3c.dav",
+                          "zope.file",
+                          ],
+      extras_require = dict(test = ["cElementTree"]),
+
+      include_package_data = True,
+      zip_safe = False)

Added: z3c.davapp.zopefile/trunk/src/z3c/__init__.py
===================================================================
--- z3c.davapp.zopefile/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/src/z3c/__init__.py	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)

Added: z3c.davapp.zopefile/trunk/src/z3c/davapp/__init__.py
===================================================================
--- z3c.davapp.zopefile/trunk/src/z3c/davapp/__init__.py	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/src/z3c/davapp/__init__.py	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)

Added: z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/__init__.py
===================================================================
--- z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/__init__.py	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/__init__.py	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1,44 @@
+import zope.interface
+import zope.component
+import zope.file.interfaces
+
+import z3c.dav.coreproperties
+
+
+class FileDAVSchema(object):
+    """
+      >>> from zope.file.file import File
+      >>> from zope.interface.verify import verifyObject
+
+      >>> f = File('text/plain', {'charset': 'ascii'})
+      >>> fp = f.open('w')
+      >>> fp.write('y' * 20)
+      >>> fp.close()
+
+      >>> adapter = FileDAVSchema(f, None) # request not needed
+      >>> verifyObject(z3c.dav.coreproperties.IDAVGetcontentlength, adapter)
+      True
+      >>> verifyObject(z3c.dav.coreproperties.IDAVGetcontenttype, adapter)
+      True
+
+      >>> adapter.getcontentlength
+      20
+      >>> adapter.getcontenttype
+      'text/plain'
+
+    """
+    zope.interface.implements(z3c.dav.coreproperties.IDAVGetcontentlength,
+                              z3c.dav.coreproperties.IDAVGetcontenttype)
+    zope.component.adapts(zope.file.interfaces.IFile,
+                          zope.publisher.interfaces.http.IHTTPRequest)
+
+    def __init__(self, context, request):
+        self.context = context
+
+    @property
+    def getcontentlength(self):
+        return self.context.size
+
+    @property
+    def getcontenttype(self):
+        return self.context.mimeType

Added: z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/configure.zcml
===================================================================
--- z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/configure.zcml	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/configure.zcml	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1,41 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:browser="http://namespaces.zope.org/browser">
+
+  <adapter
+      for="zope.file.interfaces.IFile
+           zope.publisher.interfaces.http.IHTTPRequest"
+      factory="z3c.dav.adapters.DAVDublinCore"
+      />
+
+  <adapter
+      for="zope.file.interfaces.IFile"
+      factory="z3c.dav.adapters.OpaqueProperties"
+      trusted="1"
+      />
+
+  <adapter
+      for="zope.file.interfaces.IFile
+           zope.publisher.interfaces.http.IHTTPRequest"
+      provides="z3c.dav.coreproperties.IDAVGetcontenttype"
+      factory=".FileDAVSchema"
+      />
+
+  <adapter
+      for="zope.file.interfaces.IFile
+           zope.publisher.interfaces.http.IHTTPRequest"
+      provides="z3c.dav.coreproperties.IDAVGetcontentlength"
+      factory=".FileDAVSchema"
+      />
+
+  <!--
+      Default view
+  -->
+  <browser:view
+      for="zope.file.interfaces.IFile"
+      name="index.html"
+      class="zope.file.download.Display"
+      permission="zope.View"
+      layer="zope.publisher.interfaces.browser.IBrowserRequest"
+      />
+
+</configure>

Added: z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/ftesting.zcml
===================================================================
--- z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/ftesting.zcml	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/ftesting.zcml	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1,11 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <include package="z3c.dav" file="ftesting.zcml" />
+
+  <include package="zope.mimetype" file="meta.zcml"/>
+  <include package="zope.mimetype"/>
+  <include package="zope.file" />
+
+  <include package="z3c.davapp.zopefile" />
+
+</configure>

Added: z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/properties.txt
===================================================================
--- z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/properties.txt	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/properties.txt	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1,361 @@
+============================
+zope.file webdav integration
+============================
+
+  >>> import z3c.etree
+  >>> from zope.file.file import File
+
+  >>> etree = z3c.etree.getEngine()
+
+Create a content object called `textfile.txt` in the root folder.
+
+  >>> f = File('text/plain', {'charset': 'ascii'})
+  >>> fp = f.open('w')
+  >>> fp.write('%s\n' %('x' * 10) * 5)
+  >>> fp.close()
+
+  >>> getRootFolder()['testfile.txt'] = f
+
+GET
+===
+
+The default view for the content object needs to be setup in order for WebDAV
+clients to be able to get the data for the DAV compliant resource.
+
+  >>> resp = http("""
+  ... GET /testfile.txt HTTP/1.1
+  ... """, handle_errors = False)
+  >>> resp.getStatus()
+  200
+  >>> resp.getHeader('content-type')
+  'text/plain'
+  >>> resp.getHeader('content-length')
+  '55'
+  >>> print resp.getBody()
+  xxxxxxxxxx
+  xxxxxxxxxx
+  xxxxxxxxxx
+  xxxxxxxxxx
+  xxxxxxxxxx
+
+PUT
+===
+
+???
+
+PROPFIND
+========
+
+Query the WebDAV data model for the file object. The following properties
+currently make up the data model for zope.file:
+
++ {DAV:}creationdate
+
++ {DAV:}displayname
+
++ {DAV:}getcontentlength
+
++ {DAV:}getcontenttype
+
++ {DAV:}getlastmodified
+
++ {DAV:}resourcetype
+
+Query all properties on our test file.
+
+  >>> resp = webdav("""
+  ... PROPFIND /testfile.txt HTTP/1.1
+  ... """, handle_errors = False)
+
+  >>> print resp.getBody() #doctest:+XMLDATA
+  <multistatus xmlns="DAV:">
+    <response>
+      <href>http://localhost/testfile.txt</href>
+      <propstat>
+        <prop>
+          <creationdate />
+          <displayname />
+          <getlastmodified />
+          <getcontenttype>text/plain</getcontenttype>
+          <getcontentlength>55</getcontentlength>
+          <resourcetype />
+        </prop>
+        <status>HTTP/1.1 200 OK</status>
+      </propstat>
+    </response>
+  </multistatus>
+
+The `{DAV:}getetag` and `{DAV:}getcontentlanguage` properties are not defined
+(yet) on a file object.
+
+  >>> resp.getMSProperty('http://localhost/testfile.txt', '{DAV:}getetag')
+  Traceback (most recent call last):
+  ...
+  KeyError: "'{DAV:}getetag' property not found for resource http://localhost/testfile.txt (200)"
+  >>> resp.getMSProperty(
+  ...    'http://localhost/testfile.txt', '{DAV:}getcontentlanguage')
+  Traceback (most recent call last):
+  ...
+  KeyError: "'{DAV:}getcontentlanguage' property not found for resource http://localhost/testfile.txt (200)"
+
+PROPPATCH
+=========
+
+We need to be logged in to update a property.
+
+  >>> reqbody = """<propertyupdate xmlns="DAV:">
+  ...  <set>
+  ...   <prop>
+  ...    <displayname>Test title</displayname>
+  ...   </prop>
+  ...  </set>
+  ... </propertyupdate>"""
+  >>> resp = webdav("""
+  ... PROPPATCH /testfile.txt HTTP/1.1
+  ... Content-Type: application/xml
+  ... Content-Length: %d
+  ...
+  ... %s""" %(len(reqbody), reqbody))
+  >>> resp.getStatus()
+  401
+
+Now update the title of the file.
+
+  >>> resp = webdav("""
+  ... PROPPATCH /testfile.txt HTTP/1.1
+  ... Authorization: Basic mgr:mgrpw
+  ... Content-Type: application/xml
+  ... Content-Length: %d
+  ...
+  ... %s""" %(len(reqbody), reqbody), handle_errors = False)
+  >>> resp.getStatus()
+  207
+  >>> resp.getHeader('content-type')
+  'application/xml'
+  >>> print resp.getBody() #doctest:+XMLDATA
+  <multistatus xmlns="DAV:">
+    <response>
+      <href>http://localhost/testfile.txt</href>
+      <propstat>
+        <prop>
+          <displayname />
+        </prop>
+        <status>HTTP/1.1 200 OK</status>
+      </propstat>
+    </response>
+  </multistatus>
+
+Retrieve the displayname property using PROPFIND and notice the difference.
+
+  >>> reqbody = """<propfind xmlns="DAV:">
+  ...   <prop>
+  ...     <displayname />
+  ...   </prop>
+  ... </propfind>
+  ... """
+  >>> resp = webdav("""
+  ... PROPFIND /testfile.txt HTTP/1.1
+  ... Content-type: application/xml
+  ... Content-Length: %d
+  ...
+  ... %s""" %(len(reqbody), reqbody), handle_errors = False)
+  >>> resp.getStatus()
+  207
+  >>> resp.getHeader('content-type')
+  'application/xml'
+  >>> print resp.getBody() #doctest:+XMLDATA
+  <multistatus xmlns="DAV:">
+    <response>
+      <href>http://localhost/testfile.txt</href>
+      <propstat>
+        <prop>
+          <displayname>Test title</displayname>
+        </prop>
+        <status>HTTP/1.1 200 OK</status>
+      </propstat>
+    </response>
+  </multistatus>
+
+The `{DAV:}getcontentlength` property is a live property, and as such we
+cannot modify it.
+
+  >>> reqbody = """<propertyupdate xmlns="DAV:">
+  ...   <set>
+  ...     <prop>
+  ...       <getcontentlength>24</getcontentlength>
+  ...     </prop>
+  ...   </set>
+  ... </propertyupdate>"""
+  >>> resp = webdav("""
+  ... PROPPATCH /testfile.txt HTTP/1.1
+  ... Authorization: Basic mgr:mgrpw
+  ... Content-type: application/xml 
+  ... Content-length: %d
+  ...
+  ... %s""" %(len(reqbody), reqbody))
+  >>> resp.getStatus()
+  207
+  >>> resp.getHeader('content-type')
+  'application/xml'
+  >>> print resp.getBody() #doctest:+XMLDATA
+  <multistatus xmlns="DAV:">
+    <response>
+      <href>http://localhost/testfile.txt</href>
+      <propstat>
+        <prop>
+          <getcontentlength />
+        </prop>
+        <status>HTTP/1.1 403 Forbidden</status>
+      </propstat>
+    </response>
+  </multistatus>
+
+Opaque properties
+=================
+
+Set two dead properties on our resource.
+
+  >>> reqbody = """<propertyupdate xmlns="DAV:">
+  ...  <set>
+  ...    <prop>
+  ...      <E:prop1 xmlns:E="examplens:">PROPERTY 1</E:prop1>
+  ...      <E:prop2 xmlns:E="examplens:">PROPERTY 2</E:prop2>
+  ...    </prop>
+  ...  </set>
+  ... </propertyupdate>
+  ... """
+  >>> resp = webdav("""
+  ... PROPPATCH /testfile.txt HTTP/1.1
+  ... Authorization: Basic mgr:mgrpw
+  ... Content-type: application/xml
+  ... Content-length: %d
+  ...
+  ... %s""" %(len(reqbody), reqbody), handle_errors = True)
+
+  >>> resp.getStatus()
+  207
+  >>> resp.getHeader('content-type')
+  'application/xml'
+  >>> print resp.getBody() #doctest:+XMLDATA
+  <multistatus xmlns="DAV:">
+    <response>
+      <href>http://localhost/testfile.txt</href>
+      <propstat>
+        <prop>
+          <ns1:prop1 xmlns:ns1="examplens:" />
+          <ns1:prop2 xmlns:ns1="examplens:" />
+        </prop>
+        <status>HTTP/1.1 200 OK</status>
+      </propstat>
+    </response>
+  </multistatus>
+
+Query these new properties.
+
+  >>> reqbody = """<propfind xmlns="DAV:">
+  ...  <prop xmlns:E="examplens:" >
+  ...    <E:prop1 />
+  ...    <E:prop2 />
+  ...  </prop>
+  ... </propfind>
+  ... """
+  >>> resp = webdav("""
+  ... PROPFIND /testfile.txt HTTP/1.1
+  ... Content-type: application/xml
+  ... Content-length: %d
+  ...
+  ... %s""" %(len(reqbody), reqbody))
+  >>> resp.getStatus()
+  207
+  >>> resp.getHeader('content-type')
+  'application/xml'
+  >>> print resp.getBody() #doctest:+XMLDATA
+  <multistatus xmlns="DAV:">
+    <response>
+      <href>http://localhost/testfile.txt</href>
+      <propstat>
+        <prop>
+          <E:prop1 xmlns:E="examplens:">PROPERTY 1</E:prop1>
+          <E:prop2 xmlns:E="examplens:">PROPERTY 2</E:prop2>
+        </prop>
+        <status>HTTP/1.1 200 OK</status>
+      </propstat>
+    </response>
+  </multistatus>
+
+Update the first property and remove the second.
+
+  >>> reqbody = """<propertyupdate xmlns="DAV:">
+  ...  <set>
+  ...    <prop>
+  ...      <E:prop1 xmlns:E="examplens:">Only opaque property</E:prop1>
+  ...    </prop>
+  ...  </set>
+  ...  <remove>
+  ...    <prop>
+  ...      <E:prop2 xmlns:E="examplens:" />
+  ...    </prop>
+  ...  </remove>
+  ... </propertyupdate>"""
+  >>> resp = webdav("""
+  ... PROPPATCH /testfile.txt HTTP/1.1
+  ... Authorization: Basic mgr:mgrpw
+  ... Content-type: application/xml
+  ... Content-length: %d
+  ...
+  ... %s""" %(len(reqbody), reqbody))
+  >>> resp.getStatus()
+  207
+  >>> resp.getHeader('content-type')
+  'application/xml'
+  >>> print resp.getBody() #doctest:+XMLDATA
+  <multistatus xmlns="DAV:">
+    <response>
+      <href>http://localhost/testfile.txt</href>
+      <propstat>
+        <prop>
+          <E:prop1 xmlns:E="examplens:" />
+          <E:prop2 xmlns:E="examplens:" />
+        </prop>
+        <status>HTTP/1.1 200 OK</status>
+      </propstat>
+    </response>
+  </multistatus>
+
+Now check that the opaque properties were updated successfully.
+
+  >>> reqbody = """<propfind xmlns="DAV:">
+  ...  <prop xmlns:E="examplens:" >
+  ...    <E:prop1 />
+  ...    <E:prop2 />
+  ...  </prop>
+  ... </propfind>
+  ... """
+  >>> resp = webdav("""
+  ... PROPFIND /testfile.txt HTTP/1.1
+  ... Content-type: application/xml
+  ... Content-length: %d
+  ...
+  ... %s""" %(len(reqbody), reqbody))
+  >>> resp.getStatus()
+  207
+  >>> resp.getHeader('content-type')
+  'application/xml'
+  >>> print resp.getBody() #doctest:+XMLDATA
+  <multistatus xmlns="DAV:">
+    <response>
+      <href>http://localhost/testfile.txt</href>
+      <propstat>
+        <prop>
+          <ns1:prop1 xmlns:ns1="examplens:">Only opaque property</ns1:prop1>
+        </prop>
+        <status>HTTP/1.1 200 OK</status>
+      </propstat>
+      <propstat>
+        <prop>
+          <ns1:prop2 xmlns:ns1="examplens:" />
+        </prop>
+        <status>HTTP/1.1 404 Not Found</status>
+      </propstat>
+    </response>
+  </multistatus>


Property changes on: z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/properties.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/tests.py
===================================================================
--- z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/tests.py	                        (rev 0)
+++ z3c.davapp.zopefile/trunk/src/z3c/davapp/zopefile/tests.py	2007-05-13 19:13:52 UTC (rev 75725)
@@ -0,0 +1,25 @@
+import os
+import unittest
+from zope.testing import doctest
+
+import z3c.etree.testing
+import z3c.dav.testing
+
+here = os.path.dirname(os.path.realpath(__file__))
+ZopeFileDAVLayer = z3c.dav.testing.WebDAVLayerClass(
+    os.path.join(here, "ftesting.zcml"), __name__, "ZopeFileDAVLayer")
+
+
+def test_suite():
+    properties = doctest.DocFileSuite(
+        "properties.txt",
+        setUp = z3c.dav.testing.functionalSetUp,
+        tearDown = z3c.dav.testing.functionalTearDown,
+        checker = z3c.etree.testing.xmlOutputChecker,
+        optionflags = doctest.REPORT_NDIFF | doctest.NORMALIZE_WHITESPACE)
+    properties.layer = ZopeFileDAVLayer
+                                      
+    return unittest.TestSuite((
+        doctest.DocTestSuite("z3c.davapp.zopefile"),
+        properties,
+        ))



More information about the Checkins mailing list