[Checkins] SVN: z3c.davapp.zopeappfile/ Initial import of
z3c.davapp.zopeappfile.
Michael Kerrin
michael.kerrin at openapp.ie
Tue May 8 14:02:47 EDT 2007
Log message for revision 75627:
Initial import of z3c.davapp.zopeappfile.
This package defines and implements the WebDAV data model for
all zope.app.file.file.File content objects.
Changed:
A z3c.davapp.zopeappfile/
A z3c.davapp.zopeappfile/trunk/
A z3c.davapp.zopeappfile/trunk/buildout.cfg
A z3c.davapp.zopeappfile/trunk/setup.py
A z3c.davapp.zopeappfile/trunk/src/
A z3c.davapp.zopeappfile/trunk/src/z3c/
A z3c.davapp.zopeappfile/trunk/src/z3c/__init__.py
A z3c.davapp.zopeappfile/trunk/src/z3c/davapp/
A z3c.davapp.zopeappfile/trunk/src/z3c/davapp/__init__.py
A z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/
A z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/__init__.py
A z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/configure.zcml
A z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/ftesting.zcml
A z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/properties.txt
A z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/tests.py
-=-
Added: z3c.davapp.zopeappfile/trunk/buildout.cfg
===================================================================
--- z3c.davapp.zopeappfile/trunk/buildout.cfg 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/buildout.cfg 2007-05-08 18:02:46 UTC (rev 75627)
@@ -0,0 +1,10 @@
+[buildout]
+develop = . z3c.dav z3c.etree
+parts = test
+
+[test]
+recipe = zc.recipe.testrunner
+working-directory = .
+defaults = ["--tests-pattern", "^f?tests$"]
+eggs = z3c.dav [test]
+ z3c.davapp.zopeappfile [test]
Added: z3c.davapp.zopeappfile/trunk/setup.py
===================================================================
--- z3c.davapp.zopeappfile/trunk/setup.py 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/setup.py 2007-05-08 18:02:46 UTC (rev 75627)
@@ -0,0 +1,22 @@
+from setuptools import setup, find_packages
+
+setup(name = "z3c.davapp.zopeappfile",
+ version = "0.1",
+ author = "Michael Kerrin",
+ author_email = "michael.kerrin at openapp.ie",
+ url = "http://svn.zope.org/",
+ description = "WebDAV support for zope.app.file content objects",
+ license = "ZPL",
+
+ packages = find_packages("src"),
+ package_dir = {"": "src"},
+ namespace_packages = ["z3c", "z3c.davapp"],
+ install_requires = ["setuptools",
+ "z3c.dav",
+ "zope.app.file",
+ ],
+
+ extras_require = dict(test = ["cElementTree"]),
+
+ include_package_data = True,
+ zip_safe = False)
Added: z3c.davapp.zopeappfile/trunk/src/z3c/__init__.py
===================================================================
--- z3c.davapp.zopeappfile/trunk/src/z3c/__init__.py 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/src/z3c/__init__.py 2007-05-08 18:02:46 UTC (rev 75627)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
Added: z3c.davapp.zopeappfile/trunk/src/z3c/davapp/__init__.py
===================================================================
--- z3c.davapp.zopeappfile/trunk/src/z3c/davapp/__init__.py 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/src/z3c/davapp/__init__.py 2007-05-08 18:02:46 UTC (rev 75627)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
Added: z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/__init__.py
===================================================================
--- z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/__init__.py 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/__init__.py 2007-05-08 18:02:46 UTC (rev 75627)
@@ -0,0 +1,43 @@
+import zope.component
+import zope.interface
+import zope.publisher.interfaces.http
+import zope.app.file.interfaces
+
+import z3c.dav.coreproperties
+
+class DAVFileGetSchema(object):
+ """
+ Storage adapter for the `{DAV:}getcontentlength` and `{DAV:}getcontenttype`
+ WebDAV properties for a content object implementing
+ zope.app.file.interfaces.IFile interface.
+
+ >>> from zope.app.file.file import File
+ >>> from zope.interface.verify import verifyObject
+ >>> file = File('some data for the file', 'text/plain')
+ >>> adapter = DAVFileGetSchema(file, None)
+ >>> verifyObject(z3c.dav.coreproperties.IDAVGetcontentlength, adapter)
+ True
+ >>> verifyObject(z3c.dav.coreproperties.IDAVGetcontenttype, adapter)
+ True
+
+ >>> adapter.getcontentlength
+ 22
+ >>> adapter.getcontenttype
+ 'text/plain'
+
+ """
+ zope.interface.implements(z3c.dav.coreproperties.IDAVGetcontentlength,
+ z3c.dav.coreproperties.IDAVGetcontenttype)
+ zope.component.adapts(zope.app.file.interfaces.IFile,
+ zope.publisher.interfaces.http.IHTTPRequest)
+
+ def __init__(self, context, request):
+ self.context = context
+
+ @property
+ def getcontentlength(self):
+ return self.context.getSize()
+
+ @property
+ def getcontenttype(self):
+ return self.context.contentType
Added: z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/configure.zcml
===================================================================
--- z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/configure.zcml 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/configure.zcml 2007-05-08 18:02:46 UTC (rev 75627)
@@ -0,0 +1,25 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <adapter
+ for="zope.app.file.interfaces.IFile
+ zope.publisher.interfaces.http.IHTTPRequest"
+ factory="z3c.dav.adapters.DAVDublinCore"
+ />
+
+ <adapter
+ for="zope.app.file.interfaces.IFile"
+ factory="z3c.dav.adapters.OpaqueProperties"
+ trusted="1"
+ />
+
+ <adapter
+ factory=".DAVFileGetSchema"
+ provides="z3c.dav.coreproperties.IDAVGetcontentlength"
+ />
+
+ <adapter
+ factory=".DAVFileGetSchema"
+ provides="z3c.dav.coreproperties.IDAVGetcontenttype"
+ />
+
+</configure>
Added: z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/ftesting.zcml
===================================================================
--- z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/ftesting.zcml 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/ftesting.zcml 2007-05-08 18:02:46 UTC (rev 75627)
@@ -0,0 +1,8 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <include package="z3c.dav" file="ftesting.zcml" />
+ <include package="zope.app.file" />
+
+ <include package="z3c.davapp.zopeappfile" />
+
+</configure>
Added: z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/properties.txt
===================================================================
--- z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/properties.txt 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/properties.txt 2007-05-08 18:02:46 UTC (rev 75627)
@@ -0,0 +1,357 @@
+================================
+zope.app.file webdav integration
+================================
+
+ >>> import z3c.etree
+ >>> from zope.app.file.file import File
+
+ >>> etree = z3c.etree.getEngine()
+
+Create a content object called `textfile.txt` in the root folder.
+
+ >>> f = File('%s\n' %('x' * 10) * 5, 'text/plain')
+ >>> 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.zopeappfile/trunk/src/z3c/davapp/zopeappfile/properties.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/tests.py
===================================================================
--- z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/tests.py 2007-05-08 16:04:10 UTC (rev 75626)
+++ z3c.davapp.zopeappfile/trunk/src/z3c/davapp/zopeappfile/tests.py 2007-05-08 18:02:46 UTC (rev 75627)
@@ -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__))
+ZAFDavLayer = z3c.dav.testing.WebDAVLayerClass(
+ os.path.join(here, "ftesting.zcml"), __name__, "ZAFDavLayer")
+
+
+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 = ZAFDavLayer
+ return unittest.TestSuite((
+ doctest.DocTestSuite("z3c.davapp.zopeappfile"),
+ properties,
+ ))
More information about the Checkins
mailing list