[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