[Zope3-checkins] SVN: Zope3/branches/mkerrin-webdav/src/zope/app/dav/ This is my first implementation of the namespace manager has

Michael Kerrin michael.kerrin at openapp.biz
Fri Feb 17 07:35:03 EST 2006


Log message for revision 41639:
  This is my first implementation of the namespace manager has
  proposed at http://www.zope.org/Members/mkerrin/WebDAVProposal
  
  This includes a new way of configuring WebDAV namespaces has
  outlined in the proposal.
  
  Hooked up the namespace manager to the PROPFIND method only. The
  other DAV methods will follow.
  
  Added new experimental WebDAV request object to handle WebDAV
  specific traversal.
  
  This breaks dead properties support and the OPTIONS method but
  I will get around to fixing these at a later date.
  

Changed:
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/DEPENDENCIES.cfg
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/adapter.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/common.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/configure.zcml
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/dav.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/test_locking.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/test_propfind.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/interfaces.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/locking.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/meta.zcml
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/metaconfigure.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/metadirectives.py
  A   Zope3/branches/mkerrin-webdav/src/zope/app/dav/namespaces.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/propfind.py
  A   Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davduplicateproperty.zcml
  A   Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davnamespace.zcml
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_locking.py
  A   Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_namespace.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_propfind.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_widget.py
  A   Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/xmldiff.py
  U   Zope3/branches/mkerrin-webdav/src/zope/app/dav/widget.py

-=-
Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/DEPENDENCIES.cfg
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/DEPENDENCIES.cfg	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/DEPENDENCIES.cfg	2006-02-17 12:35:02 UTC (rev 41639)
@@ -2,6 +2,7 @@
 transaction
 zope.app
 zope.app.locking
+zope.app.copypastemove
 zope.component
 zope.configuration
 zope.interface

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/adapter.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/adapter.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/adapter.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -22,7 +22,8 @@
 from zope.interface import Interface, implements
 
 from zope.app import zapi
-from zope.app.dav.interfaces import IDAVSchema, IDAVActiveLock, IDAVLockEntry
+from zope.app.dav.interfaces import IDAVSchema, IActiveLock, ILockEntry, \
+     IDAVLockSchema, IDAVResourceSchema
 from zope.app.dublincore.interfaces import IDCTimes
 from zope.app.filerepresentation.interfaces import IReadDirectory
 from zope.app.size.interfaces import ISized
@@ -30,6 +31,37 @@
 from zope.app.locking.interfaces import ILockable
 
 
+class DAVLockSchemaAdapter(object):
+    implements(IDAVLockSchema)
+
+    def __init__(self, context):
+        self.context = context
+
+    def supportedlock(self):
+        return ILockEntry(self.context, None)
+    supportedlock = property(supportedlock)
+
+    def lockdiscovery(self):
+        lockable = ILockable(self.context, None)
+        if lockable is None or not lockable.locked():
+            return None
+        return IActiveLock(self.context, None)
+    lockdiscovery = property(lockdiscovery)
+
+
+class DAVResourceAdapter(object):
+    """Defines a standard default resourcetype property value of None.
+
+    This is the only mandatory property defined with the DAV: namespace.
+    """
+    implements(IDAVResourceSchema)
+
+    def __init__(self, context):
+        self.context = context
+
+    resourcetype = None
+
+
 class DAVSchemaAdapter(object):
     """An adapter for all content objects that provides the basic DAV
     schema/namespace."""
@@ -88,27 +120,25 @@
 
     def getlastmodified(self):
         dc = IDCTimes(self.context, None)
-        if dc is None or dc.created is None:
+        if dc is None or dc.modified is None:
             return None
         return dc.modified
     getlastmodified = property(getlastmodified)
 
     def supportedlock(self):
-        return IDAVLockEntry(self.context, None)
+        return ILockEntry(self.context, None)
     supportedlock = property(supportedlock)
 
     def lockdiscovery(self):
         lockable = ILockable(self.context, None)
         if lockable is None or not lockable.locked():
             return None
-        return IDAVActiveLock(self.context, None)
+        return IActiveLock(self.context, None)
     lockdiscovery = property(lockdiscovery)
 
-    source = None
 
-
 class LockEntry(object):
-    implements(IDAVLockEntry)
+    implements(ILockEntry)
 
     def __init__(self, context):
         pass
@@ -122,7 +152,7 @@
     """represent a locked object that is the context of this adapter should
     be locked otherwise a TypeError is raised
     """
-    implements(IDAVActiveLock)
+    implements(IActiveLock)
 
     def __init__(self, context):
         self.context = context

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/common.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/common.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/common.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -24,6 +24,7 @@
 from zope.security.proxy import removeSecurityProxy
 
 from zope.app import zapi
+from zope import component
 from zope.app.container.interfaces import IReadContainer
 
 
@@ -155,3 +156,91 @@
         if ns_prefix is not None:
             el.setAttributeNS(ns, 'xmlns', ns_prefix)
         return el
+
+################################################################################
+#
+# WebDAV Request and Request Factory
+#
+################################################################################
+
+from zope.publisher.http import HTTPResponse, HTTPRequest
+from zope.app.publication.http import HTTPPublication
+from zope.app.publication.interfaces import IRequestPublicationFactory
+from interfaces import IWebDAVRequest, IWebDAVRequestFactory
+
+
+class WebDAVResponse(HTTPResponse):
+    """
+    """
+    pass
+
+
+class WebDAVRequest(HTTPRequest):
+    implements(IWebDAVRequest)
+
+    def _createResponse(self):
+        """Create a specific WebDAV response object."""
+        return WebDAVResponse()
+
+
+class WebDAVPublication(HTTPPublication):
+    pass
+
+
+class WebDAVRequestFactory(object):
+    implements(IRequestPublicationFactory)
+
+    def canHandle(self, environment):
+        return True
+
+    def __call__(self):
+        request_class = component.queryUtility(
+            IWebDAVRequestFactory, default = WebDAVRequest)
+        return request_class, WebDAVPublication
+
+################################################################################
+#
+# WebDAV specific traversal. Needed in order to generate null resources.
+#
+################################################################################
+
+from zope.app.http import traversal
+from zope.app.http.put import NullResource
+from zope.publisher.interfaces import NotFound
+from zope.app.http.interfaces import INullResource
+from zope.app.container.interfaces import IContained
+from zope.app.container.contained import Contained
+from persistent import Persistent
+
+
+class LockNullResource(Persistent, Contained):
+    implements(INullResource, IContained)
+
+    def __init__(self, container, name):
+        # container is __parent__
+        # name is __name__
+        pass
+
+    @property
+    def container(self):
+        return self.__parent__
+
+    @property
+    def name(self):
+        return self.__name__
+
+
+class ContainerTraverser(traversal.ContainerTraverser):
+
+    def nullResource(self, request, name):
+        if request.getTraversalStack():
+            raise NotFound(self.context, name, request)
+
+        if request.method == 'MKCOL':
+            return NullResource(self.context, name)
+        elif request.method == 'LOCK':
+            lnr = LockNullResource(self.context, name)
+            self.context[name] = lnr
+            return lnr
+
+        raise NotFound(self.context, name, request)

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/configure.zcml
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/configure.zcml	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/configure.zcml	2006-02-17 12:35:02 UTC (rev 41639)
@@ -2,10 +2,33 @@
    xmlns="http://namespaces.zope.org/zope"
    xmlns:dav="http://namespaces.zope.org/dav">
 
+  <publisher
+     name="WEBDAV"
+     factory=".common.WebDAVRequestFactory"
+     methods="PROPFIND PROPPATCH MKCOL LOCK UNLOCK COPY MOVE"
+     priority="30"
+     />
+
+  <content class=".common.LockNullResource">
+    <allow interface="zope.app.http.interfaces.INullResource" />
+  </content>
+
   <view
+     for="zope.app.container.interfaces.ISimpleReadContainer"
+     type=".interfaces.IWebDAVRequest"
+     provides="zope.publisher.interfaces.IPublishTraverse"
+     factory=".common.ContainerTraverser"
+     permission="zope.Public"
+     allowed_interface="zope.publisher.interfaces.IPublishTraverse"
+     />
+
+  <!--
+      WebDAV method definitions.
+    -->
+  <view
       for="*"
       name="PROPFIND"
-      type="zope.publisher.interfaces.http.IHTTPRequest"
+      type=".interfaces.IWebDAVRequest"
       factory=".propfind.PROPFIND"
       permission="zope.ManageContent"
       allowed_attributes="PROPFIND setDepth getDepth" />
@@ -13,7 +36,7 @@
   <view
       for="*"
       name="PROPPATCH"
-      type="zope.publisher.interfaces.http.IHTTPRequest"
+      type=".interfaces.IWebDAVRequest"
       factory=".proppatch.PROPPATCH"
       permission="zope.ManageContent"
       allowed_attributes="PROPPATCH" />
@@ -21,7 +44,7 @@
   <view
       for="zope.app.http.interfaces.INullResource"
       name="MKCOL"
-      type="zope.publisher.interfaces.http.IHTTPRequest"
+      type=".interfaces.IWebDAVRequest"
       factory=".mkcol.NullResource"
       permission="zope.ManageContent"
       allowed_attributes="MKCOL" />
@@ -29,7 +52,7 @@
   <view
       for="*"
       name="MKCOL"
-      type="zope.publisher.interfaces.http.IHTTPRequest"
+      type=".interfaces.IWebDAVRequest"
       factory=".mkcol.MKCOL"
       permission="zope.ManageContent"
       allowed_attributes="MKCOL" />
@@ -40,7 +63,7 @@
   <view
      for="zope.interface.Interface"
      name="LOCK"
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      factory=".locking.LOCKMethodFactory"
      permission="zope.ManageContent"
      allowed_attributes="LOCK"
@@ -49,7 +72,7 @@
   <view
      for="zope.interface.Interface"
      name="UNLOCK"
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      factory=".locking.UNLOCKMethodFactory"
      permission="zope.ManageContent"
      allowed_attributes="UNLOCK"
@@ -68,28 +91,42 @@
   <view
      for="zope.interface.Interface"
      name="COPY"
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      factory=".copy.COPY"
      permission="zope.ManageContent"
      allowed_attributes="COPY"
      />
 
   <adapter
-     provides="zope.app.dav.interfaces.IDAVSchema"
+     for="zope.app.http.interfaces.INullResource"
+     provides="zope.app.dav.interfaces.IDAVLockSchema"
+     permission="zope.Public"
+     factory=".adapter.DAVLockSchemaAdapter"
+     />
+
+  <adapter
      for="zope.interface.Interface"
+     provides="zope.app.dav.interfaces.IDAVResourceSchema"
      permission="zope.Public"
+     factory=".adapter.DAVResourceAdapter"
+     />
+
+  <adapter
+     provides="zope.app.dav.interfaces.IDAVSchema"
+     for="zope.app.annotation.interfaces.IAnnotatable"
+     permission="zope.Public"
      factory=".adapter.DAVSchemaAdapter"
      />
 
   <adapter
-     provides="zope.app.dav.interfaces.IDAVActiveLock"
+     provides="zope.app.dav.interfaces.IActiveLock"
      for="zope.interface.Interface"
      permission="zope.Public"
      factory=".adapter.ActiveLock"
      />
 
   <adapter
-     provides="zope.app.dav.interfaces.IDAVLockEntry"
+     provides="zope.app.dav.interfaces.ILockEntry"
      for="zope.interface.Interface"
      permission="zope.Public"
      factory=".adapter.LockEntry"
@@ -123,7 +160,7 @@
       Define all the DAV widgets.
     -->
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.schema.interfaces.IText"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.TextDAVWidget"
@@ -131,23 +168,31 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
-     for="zope.schema.interfaces.IBytes"
+     type=".interfaces.IWebDAVRequest"
+     for="zope.schema.interfaces.ITextLine"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.TextDAVWidget"
      permission="zope.Public"
      />
-  
+
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.schema.interfaces.IInt"
      provides="zope.app.dav.interfaces.IDAVWidget"
-     factory="zope.app.dav.widget.TextDAVWidget"
+     factory="zope.app.dav.widget.IntDAVWidget"
      permission="zope.Public"
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
+     for="zope.schema.interfaces.IBytes"
+     provides="zope.app.dav.interfaces.IDAVWidget"
+     factory="zope.app.dav.widget.TextDAVWidget"
+     permission="zope.Public"
+     />
+  
+  <view
+     type=".interfaces.IWebDAVRequest"
      for="zope.schema.interfaces.IFloat"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.TextDAVWidget"
@@ -155,7 +200,7 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.schema.interfaces.IDatetime"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.DatetimeDAVWidget"
@@ -163,7 +208,7 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.schema.interfaces.IDate"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.DateDAVWidget"
@@ -171,7 +216,7 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.schema.interfaces.ITuple"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.TupleDAVWidget"
@@ -179,7 +224,7 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.schema.interfaces.ISequence"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.SequenceDAVWidget"
@@ -187,7 +232,7 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.schema.interfaces.IList"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.ListDAVWidget"
@@ -195,7 +240,7 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.app.dav.fields.IXMLEmptyElementList"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.XMLEmptyElementListDAVWidget"
@@ -203,7 +248,7 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.app.dav.fields.IDAVXMLSubProperty"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.DAVXMLSubPropertyWidget"
@@ -211,7 +256,7 @@
      />
 
   <view
-     type="zope.publisher.interfaces.http.IHTTPRequest"
+     type=".interfaces.IWebDAVRequest"
      for="zope.app.dav.fields.DAVOpaqueField"
      provides="zope.app.dav.interfaces.IDAVWidget"
      factory="zope.app.dav.widget.DAVOpaqueWidget"
@@ -226,4 +271,28 @@
      for="DAV:"
      interface="zope.app.dav.interfaces.IDAVSchema" />
 
+  <dav:namespace
+     namespace="http://purl.org/dc/1.1"
+     schemas="zope.app.dublincore.interfaces.IZopeDublinCore"
+     interfaceType=".interfaces.IDCDAVNamespaceType"
+     />
+
+  <dav:namespace
+     namespace="DAV:"
+     interfaceType=".interfaces.IDAVNamespaceType"
+     schemas=".interfaces.IDAVResourceSchema
+              .interfaces.IDAVCreationDate
+              .interfaces.IDAVDisplayName
+              .interfaces.IGETDependentDAVSchema
+              .interfaces.IDAVLockSchema
+              .interfaces.IOptionalDAVSchema
+              ">
+
+    <widget
+       propname="creationdate"
+       class=".widget.ISO8601DateDAVWidget"
+       />
+
+  </dav:namespace>
+
 </configure>

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/dav.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/dav.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/dav.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -18,7 +18,10 @@
 from persistent import Persistent
 import transaction
 from zope.interface import implements
-from zope.app.testing.functional import HTTPTestCase
+from zope.app.testing.functional import HTTPTestCase, FunctionalTestSetup
+from zope.publisher.http import HTTPRequest
+from zope.app.publication.http import HTTPPublication
+from zope.app.dav.common import WebDAVRequest, WebDAVPublication
 
 from zope.app.folder import Folder
 from zope.app.annotation.interfaces import IAttributeAnnotatable
@@ -53,3 +56,31 @@
         page = Page()
         page.source = content
         self.createObject(path, page)
+
+    def makeRequest(self, path = '', basic = None, form = None, env = {},
+                    instream = None):
+        method = env.get('REQUEST_METHOD', '')
+
+        if instream is None:
+            instream = ''
+
+        environment = {"HTTP_HOST": 'localhost',
+                       "HTTP_REFERER": 'localhost'}
+        environment.update(env)
+
+        app = FunctionalTestSetup().getApplication()
+        if method in ('PROPFIND', 'PROPPATCH', 'MKCOL', 'LOCK', 'UNLOCK',
+                      'COPY', 'MOVE'):
+            request = app._request(path, instream,
+                                   environment = environment,
+                                   basic = basic, form = form,
+                                   request = WebDAVRequest,
+                                   publication = WebDAVPublication)
+        else:
+            request = app._request(path, instream,
+                                   environment=environment,
+                                   basic=basic, form=form,
+                                   request=HTTPRequest,
+                                   publication=HTTPPublication)
+
+        return request

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/test_locking.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/test_locking.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/test_locking.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -23,7 +23,7 @@
 
 from zope.interface import Interface
 from zope.app import zapi
-from zope.app.testing import setup, ztapi, functional
+from zope.app.testing import setup, ztapi
 from zope.app.locking.interfaces import ILockStorage, ILockable
 from zope.app.locking.storage import PersistentLockStorage
 from zope.app.locking.adapter import LockingAdapterFactory, LockingPathAdapter
@@ -32,6 +32,7 @@
 
 from zope.app.security.adapter import LocatingTrustedAdapterFactory
 
+from dav import DAVTestCase
 
 def makeLockBody(data):
     body = '''<?xml version="1.0" encoding="utf-8"?>
@@ -44,7 +45,7 @@
     return body
 
 
-class TestAllowBefore(functional.HTTPTestCase):
+class TestAllowBefore(DAVTestCase):
     # Test for the LOCK and UNLOCK in the Allow header. I use both a OPTIONS
     # request and a 'FROGS' (undefined) request to test for this. The reason
     # for two tests is that I ran into problems getting this to work with the
@@ -108,7 +109,7 @@
 class TestLOCK(TestAllowBefore):
 
     def setUp(self):
-        functional.HTTPTestCase.setUp(self)
+        super(TestLOCK, self).setUp()
         sm = zapi.getSiteManager(self.getRootFolder())
 
         self.storage = storage = PersistentLockStorage()
@@ -126,12 +127,12 @@
         del self.storage
 
     def test_allow_options(self):
-        super(TestLOCK, self)._test_allow_options(True)
+        self._test_allow_options(True)
 
     def test_allow_publish(self):
         ## XXX - the LOCK, and UNLOCK methods should show up in the allow header
         ## for this test but I can't get this work.
-        super(TestLOCK, self)._test_allow_publish(True)
+        self._test_allow_publish(True)
 
     def test_lock_file_simple(self):
         file = File('some content', 'text/plain')

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/test_propfind.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/test_propfind.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/ftests/test_propfind.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -63,15 +63,15 @@
         self.verifyPropOK(path='/pt', ns='http://purl.org/dc/1.1',
                           prop='subjects', expect=expect, basic='mgr:mgrpw')
         
-    def test_opaque(self):
-        self.addPage('/pt', u'<span />')
-        pt = traverse(self.getRootFolder(), '/pt')
-        adapted = IDAVOpaqueNamespaces(pt)
-        adapted[u'uri://bar'] = {u'foo': '<foo>spam</foo>'}
-        transaction.commit()
-        expect = 'spam'
-        self.verifyPropOK(path='/pt', ns='uri://bar',
-                          prop='foo', expect=expect, basic='mgr:mgrpw')
+##     def test_opaque(self):
+##         self.addPage('/pt', u'<span />')
+##         pt = traverse(self.getRootFolder(), '/pt')
+##         adapted = IDAVOpaqueNamespaces(pt)
+##         adapted[u'uri://bar'] = {u'foo': '<foo>spam</foo>'}
+##         transaction.commit()
+##         expect = 'spam'
+##         self.verifyPropOK(path='/pt', ns='uri://bar',
+##                           prop='foo', expect=expect, basic='mgr:mgrpw')
 
     def verifyPropOK(self, path, ns, prop, expect, basic):
         body = """<?xml version="1.0" ?>

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/interfaces.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/interfaces.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/interfaces.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -17,11 +17,16 @@
 """
 __docformat__ = 'restructuredtext'
 
-from zope.interface import Interface
+from zope.interface import Interface, Attribute
+from zope.interface.interfaces import IInterface
+from zope.interface.common.mapping import IEnumerableMapping
 from zope.schema import Int, Text, TextLine, Dict, Datetime, Date, List, Bool
 from zope.schema.interfaces import IText, IList
 from zope.app.form.interfaces import IWidget
 
+from zope.publisher.interfaces.http import IHTTPRequest
+from zope.app.publication.interfaces import IRequestFactory
+
 from fields import IXMLEmptyElementList, XMLEmptyElementList
 from fields import IDAVXMLSubProperty, DAVXMLSubProperty, DAVOpaqueField
 
@@ -33,6 +38,42 @@
     """
 
 
+class IDAVNamespaceType(IInterface):
+    """This interface represents the WebDAV namespace schema defined
+    in RFC2518, RFC3744, ...
+
+    If an **interface** provides this interface type, then all adapters
+    providing the **interfaace** are considered WebDAV data adapters.
+    """
+
+
+class IDCDAVNamespaceType(IInterface):
+    """This interface represents the Dublin Core namespace.
+
+    If an **interface** provides this interface type, then all adapters
+    providing the **interface** are considered data adapters for the
+    DC properties.
+    """
+
+
+class IDAVResourceSchema(Interface):
+    """DAV properties required for Level 1 compliance"""
+
+    resourcetype = XMLEmptyElementList(
+                           title = u'''Specifies the nature of the resource''',
+
+                           description = u'''\
+                                 The resourcetype property MUST be
+                                 defined on all DAV compliant
+                                 resources.  The default value is
+                                 empty.''',
+
+                           readonly = True,
+
+                           value_type = TextLine(title = u'resource type')
+                           )
+
+
 class IDAVCreationDate(Interface):
 
     creationdate = Datetime(title=u'''Records the time and date the resource\
@@ -83,7 +124,7 @@
                                       asserts no policy on ordering.''')
 
 
-class IOptionalDAVSchema(IDAVCreationDate, IDAVDisplayName, IDAVSource):
+class IOptionalDAVSchema(IDAVCreationDate, IDAVDisplayName):
     """DAV properties that SHOULD be present but are not required"""
 
 
@@ -153,24 +194,7 @@
                                readonly = True)
 
 
-class IDAVResourceSchema(Interface):
-    """DAV properties required for Level 1 compliance"""
-
-    resourcetype = XMLEmptyElementList(
-                           title = u'''Specifies the nature of the resource''',
-
-                           description = u'''\
-                                 The resourcetype property MUST be
-                                 defined on all DAV compliant
-                                 resources.  The default value is
-                                 empty.''',
-
-                           readonly = True,
-
-                           value_type = TextLine(title = u'resource type')
-                           )
-
-class IDAVLockEntry(Interface):
+class ILockEntry(Interface):
     """A DAV Sub property of the supportedlock property.
     """
     lockscope = XMLEmptyElementList(title = u'''\
@@ -180,8 +204,6 @@
                             Specifies whether a lock is an exclusive lock, or a
                             shared lock.''',
 
-                                    readonly = True,
-
                                     value_type = TextLine(title = u''))
 
     locktype = XMLEmptyElementList(title = u'''\
@@ -192,33 +214,12 @@
                             this specification only defines one lock type, the
                             write lock.''',
 
-                                   readonly = True,
-
                                    value_type = TextLine(title = u''))
 
 
-class IDAVActiveLock(Interface):
+class IActiveLock(ILockEntry):
     """A DAV Sub property of the lockdiscovery property.
     """
-    lockscope = XMLEmptyElementList(title = u'''\
-                            Describes the exclusivity of a lock''',
-
-                                    description = u'''\
-                            Specifies whether a lock is an exclusive lock, or a
-                            shared lock.''',
-
-                                    value_type = TextLine(title = u''))
-
-    locktype = XMLEmptyElementList(title = u'''\
-                            Describes the access type of the lock''',
-
-                                   description = u'''\
-                            Specifies the access type of a lock. At present,
-                            this specification only defines one lock type, the
-                            write lock.''',
-
-                                   value_type = TextLine(title = u''))
-
     depth = Text(title = u'Depth',
                  description = u'The value of the Depth header.')
 
@@ -265,7 +266,7 @@
 
                                       prop_name = 'activelock',
 
-                                      schema = IDAVActiveLock)
+                                      schema = IActiveLock)
 
     supportedlock = DAVXMLSubProperty(title = u'''\
                                            To provide a listing of the lock\
@@ -286,7 +287,7 @@
 
                                       readonly = True,
 
-                                      schema = IDAVLockEntry,
+                                      schema = ILockEntry,
 
                                       prop_name = 'lockentry')
 
@@ -362,12 +363,18 @@
         based on the field constraints.
         """
 
+    ## this shouldn't be used ???
     def setRenderedValue(value):
         """Set the value of the field associated with this widget.
 
         This value must validate to the type expected by the associated field.
         """
 
+    def setNamespace(ns, ns_prefix):
+        """Set the namespace and the namespace prefix. To be used when rendering
+        this widget.
+        """
+
     def setProperty(propel):
         """Parse the DOM element ``propel`` and store the extracted value in
         the widget.
@@ -375,8 +382,12 @@
         The extracted value must validate against the associated field.
         """
 
-    def renderProperty(ns, ns_prefix):
-        """Render a property has a DOM elements.
+    def renderProperty():
+        """Render a property has a Python XML DOM element.
+
+        Use the setNamespace method to set the namespace and namespace prefix
+        to be used when rendering this property if it belongs to a different
+        WebDAV namespace other then the default DAV: namespace.
         """
 
 ##     def removeProperty(self, ns, prop):
@@ -392,3 +403,100 @@
         """Return True / False wheather the current context and request
         matches the the IF HTTP header has specified in RFC 2518
         """
+
+
+################################################################################
+#
+# Namespace Management.
+#
+################################################################################
+class INamespaceManager(Interface):
+    """One instance of this utility exists for each namespace known by the
+    system.
+
+    This utility is used to manage all the properties within a namespace. This
+    includes which properties are defined for an object, accessing these
+    properties, and finding what widgets are used to render these properties.
+    """
+
+    #
+    # Registration methods
+    #
+
+    def registerSchema(schema, restricted_properties = ()):
+        """Register the interface, schema, with the namespace manager. Each
+        field defined in the interface will define a property within the
+        namespace.
+
+        restricted_properties is a list of field names defined in schema
+        which whose corresponding property should not be rendered in
+        response to a PROPFIND request for all properties.
+        """
+
+    def registerWidget(propname, widget):
+        """Register a custom widget for the property named propname.
+
+        The widget is a callable taking the current bound field representing
+        the property named propname and the current request, On being called
+        widget must return an object implementing IDAVWidget.
+        """
+
+    #
+    # namespace management methods
+    #
+
+    def hasProperty(object, propname):
+        """This namespace has a property called propname defined for object.
+        """
+
+    def getProperty(object, propname):
+        """Get the field, propname, for this object, is it exists.
+
+        The returned value will implement zope.schema.interfaces.IField and will
+        be bound to the adapter defining the property propname.
+
+        Raises a TypeError / KeyError exception if the property named propname
+        is not defined for object.
+        """
+
+    def getWidget(object, request, propname, ns_prefix):
+        """Get the WebDAV widget to be used to render / store the property
+        propname.
+
+        The returned value is a WebDAV Widget and so will implement IDAVWidget.
+        By calling the renderProperty and setProperty on this object we will
+        be able to render the value of the property or set a value for this
+        property.
+
+        Raises a KeyError if the property named propname is not defined for
+        object.
+        """
+
+    def getAllPropertyNames(object, restricted = False):
+        """Return a list of all the property names that are defined on object.
+
+        If restricted is True then any defined property for object that is
+        registered has a restricted property is not returned in the generated
+        list of names.
+        """
+
+    def getAllProperties(object, restricted = False):
+        """Return all the properties defined within this namespace for this
+        object.
+
+        If restricted is True then don't return any property that is
+        registered has a restricted property.
+        """
+
+    def isRestrictedProperty(object, propname):
+        """Is the named property a restricted property for this object.
+        """
+
+
+class IWebDAVRequest(IHTTPRequest):
+    """
+    """
+
+
+class IWebDAVRequestFactory(IRequestFactory):
+    """WebDAV request factory."""

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/locking.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/locking.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/locking.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -30,12 +30,14 @@
 from zope.app.form.utility import setUpWidget
 from zope.app.locking.interfaces import ILockable, ILockStorage, LockingError
 from zope.app.container.interfaces import IReadContainer
+from zope.app.http.interfaces import INullResource
 
-from interfaces import IDAVWidget, IDAVActiveLock, IDAVLockEntry, \
-     IDAVLockSchema, IIfHeader
+from interfaces import IDAVWidget, IActiveLock, ILockEntry, \
+     IDAVLockSchema, IIfHeader, IWebDAVRequest
 from common import MultiStatus
 
 MAXTIMEOUT = (2L**32)-1
+DEFAULTTIMEOUT = 12 * 60L
 
 _randGen = random.Random(time.time())
 
@@ -69,10 +71,13 @@
     stauts = 422
 
 
- at adapter(Interface, IHTTPRequest)
+ at adapter(Interface, IWebDAVRequest)
 @implementer(Interface)
 def LOCKMethodFactory(context, request):
-    lockable = ILockable(context, None)
+    try:
+        lockable = ILockable(context, None)
+    except:
+        return None
     if lockable is None:
         return None
     return LOCK(context, request)
@@ -92,11 +97,10 @@
         else:
             self.content_type = ct.lower()
             self.content_type_params = None
+
         self.default_ns = 'DAV:'
         self.default_ns_prefix = None
 
-        self.defaulttimeout = 12 * 60L
-
         self.errorBody = None
 
     def generateLockToken(self):
@@ -113,12 +117,12 @@
         timeoutheader = timeoutheader.strip().lower()
         t = str(timeoutheader).split('-')[-1]
         if t == 'infinite' or t == 'infinity':
-            timeout = self.defaulttimeout
+            timeout = DEFAULTTIMEOUT
         else:
             timeout = long(t)
 
         if timeout > MAXTIMEOUT:
-            timeout = self.defaulttimeout
+            timeout = DEFAULTTIMEOUT
 
         return timeout
 
@@ -199,14 +203,14 @@
         lockinfo['locktoken'] = \
                               '<locktoken><href>%s</href></locktoken>' % token
 
-        adapted = IDAVActiveLock(object)
+        adapted = IActiveLock(object)
         for node in xmldoc.childNodes:
             if node.nodeType != node.ELEMENT_NODE:
                 continue
 
             name = node.localName
 
-            field = IDAVActiveLock[name]
+            field = IActiveLock[name]
 
             setUpWidget(self, name, field, IDAVWidget, value = None,
                         ignoreStickyValues = False)
@@ -266,7 +270,7 @@
                     ignoreStickyValues = False)
         widget = getattr(self, fieldname + '_widget', None)
         assert widget is not None
-        el = widget.renderProperty(ns, ns_prefix)
+        el = widget.renderProperty()
         prop.appendChild(el)
 
         body = resp.toxml('utf-8')
@@ -283,7 +287,10 @@
 @adapter(Interface, IHTTPRequest)
 @implementer(Interface)
 def UNLOCKMethodFactory(context, request):
-    lockable = ILockable(context, None)
+    try:
+        lockable = ILockable(context, None)
+    except:
+        return None
     if lockable is None:
         return None
     return UNLOCK(context, request)
@@ -324,6 +331,8 @@
                                           "lock tokens are not equal")
 
         lockable.unlock()
+        if INullResource.providedBy(object):
+            del object.container[object.name]
 
         # recurise into subfolders if we are a folder.
         if IReadContainer.providedBy(object):

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/meta.zcml
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/meta.zcml	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/meta.zcml	2006-02-17 12:35:02 UTC (rev 41639)
@@ -2,11 +2,52 @@
     xmlns="http://namespaces.zope.org/zope"
     xmlns:meta="http://namespaces.zope.org/meta">
 
-  <meta:directive
-      namespace="http://namespaces.zope.org/dav"
-      name="provideInterface"
-      schema=".metadirectives.IProvideInterfaceDirective"
-      handler=".metaconfigure.interface"
-      />
+  <meta:directives namespace="http://namespaces.zope.org/dav">
 
+    <!--
+        old directive that will disappear later
+      -->
+    <meta:directive
+       name="provideInterface"
+       schema=".metadirectives.IProvideInterfaceDirective"
+       handler=".metaconfigure.interface"
+       />
+
+    <!--
+        new directives
+      -->
+    <meta:complexDirective
+       name="namespace"
+       schema=".metadirectives.INamespaceDirective"
+       handler=".metaconfigure.namespace">
+
+      <meta:subdirective
+         name="widget"
+         schema=".metadirectives.IWidgetSubDirective"
+         />
+
+    </meta:complexDirective>
+
+    <meta:complexDirective
+       name="schemas"
+       schema=".metadirectives.ISchemaDirective"
+       handler=".metaconfigure.schemas">
+
+      <meta:subdirective
+         name="widget"
+         schema=".metadirectives.IWidgetSubDirective"
+         />
+
+    </meta:complexDirective>
+
+    <meta:directive
+       name="widget"
+       schema=".metadirectives.IWidgetDirective"
+       handler=".metaconfigure.widget"
+       />
+
+  </meta:directives>
+
+  <meta:provides feature="webdav" />
+
 </configure>

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/metaconfigure.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/metaconfigure.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/metaconfigure.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -17,10 +17,147 @@
 """
 __docformat__ = 'restructuredtext'
 
+from zope import component
+from zope.schema import getFieldNamesInOrder
 from zope.app.component.metaconfigure import utility
+from zope.app.component.metaconfigure import adapter
+from zope.app.component.interface import provideInterface
 from zope.interface import directlyProvides
 from interfaces import IDAVNamespace
 
+from interfaces import INamespaceManager
+from namespaces import NamespaceManager
+
 def interface(_context, for_, interface):
     directlyProvides(interface, IDAVNamespace)
     utility(_context, IDAVNamespace, interface, name=for_)
+from zope.deprecation import deprecated
+deprecated(interface,
+           'provideInterface is no more - use namespace, schemas directive')
+
+################################################################################
+#
+# New WebDAV registration.
+#
+################################################################################
+
+def _addWidgetToRegistry(registry, propertyname, class_):
+    registry.registerWidget(propertyname, class_)
+
+
+class namespace(object):
+    def __init__(self, _context, namespace, schemas = [],
+                 restricted_properties = [], interfaceType = None):
+        self._context = _context
+        self.namespace = namespace
+        self.schemas = schemas
+        self.restricted_properties = restricted_properties
+        self.interfaceType = interfaceType
+
+        self.widgets = {}
+
+    def widget(self, _context, propname, class_):
+        self.widgets[propname] = class_
+
+    def __call__(self):
+        registry = NamespaceManager(self.namespace, self.schemas,
+                                    self.restricted_properties,
+                                    self.interfaceType)
+
+        #
+        # Create a new INamespaceManager Utility
+        #
+        utility(self._context, INamespaceManager, registry,
+                name = self.namespace)
+
+        #
+        # Register all the widgets if they are specified
+        #
+        for prop, widgetclass in self.widgets.items():
+            registry.registerWidget(prop, widgetclass)
+
+        #
+        # Declare all schemas to implement the interfaceType if it is not None
+        #
+        if self.interfaceType is not None:
+            interfaceType = self.interfaceType
+            path = interfaceType.__module__ + '.' + interfaceType.__name__
+
+            self._context.action(
+                discriminator = None,
+                callable = provideInterface,
+                args = (path, interfaceType),
+                )
+
+            for schema in self.schemas:
+                self._context.action(
+                    discriminator = None,
+                    callable = provideInterface,
+                    args = ('', schema, interfaceType),
+                    )
+
+
+class schemas(object):
+    def __init__(self, _context, namespace, schemas,
+                 restricted_properties = ()):
+        self._context = _context
+        self.namespace = namespace
+        self.schemas = schemas
+        self.restricted_properties = restricted_properties
+        self.widgets = {}
+
+    def widget(self, _context, propname, class_):
+        self.widgets[propname] = class_
+
+    def callaction(self):
+        registry = component.getUtility(INamespaceManager, self.namespace)
+        #
+        # Register all the schemas
+        #
+        for schema in self.schemas:
+            registry.registerSchema(schema, self.restricted_properties)
+
+        #
+        # Register all the widgets
+        #
+        for prop, widgetclass in self.widgets.items():
+            registry.registerWidget(prop, widgetclass)
+
+        #
+        # Declare all interface to implement interfaceType since we want
+        # these interfaces to show up in the APIDOC tool.
+        #
+        if registry.interfaceType is not None:
+            for schema in self.schemas:
+                provideInterface('', schema, registry.interfaceType)
+
+    def __call__(self):
+        #
+        # Need to make self.schemas hashable for the discriminator to work
+        # correctly - this could be done a lot nicer I am guesing.
+        #
+        discriminator = ('dav:schemas', self.namespace)
+        for schema in self.schemas:
+            discriminator += ('%s.%s' %(schema.__module__, schema.__name__),)
+        self._context.action(
+            discriminator = discriminator,
+            callable = self.callaction,
+            args = (),
+            )
+
+
+def widgetHandler(namespace, propname, class_):
+    registry = component.getUtility(INamespaceManager, namespace)
+    registry.registerWidget(propname, class_)
+
+
+def widget(_context, namespace, propname, class_):
+    #
+    # We can't just ask for the namespace here since it is unlikely to
+    # have being created yet.
+    #
+    _context.action(
+        discriminator = ('dav.namepsacewidget', namespace, propname),
+        callable = widgetHandler,
+        args = (namespace, propname, class_),
+        )

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/metadirectives.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/metadirectives.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/metadirectives.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -19,7 +19,7 @@
 
 from zope.configuration.fields import GlobalInterface
 from zope.interface import Interface
-from zope.schema import URI
+from zope.schema import URI, TextLine
 
 class IProvideInterfaceDirective(Interface):
     """This directive assigns a new interface to a component. This interface
@@ -35,3 +35,84 @@
         title=u"Interface",
         description=u"Specifies an interface/schema for DAV.",
         required=True)
+
+#
+# new WebDAV configuration.
+#
+
+from zope.configuration.fields import Tokens
+from zope.configuration.fields import GlobalObject
+from zope.configuration.fields import GlobalInterface
+
+
+class IBaseSchemaDirective(Interface):
+
+    namespace = URI(
+        title = u'Namespace',
+        description = u'Namespace under which this interface will be available'\
+                      u' via DAV.',
+        required = True)
+
+    restricted_properties = Tokens(
+        title = u'Restricted Properties',
+        description = u'A list of property names that should not be rendered' \
+                      u' has part of the allprop PROPFIND request',
+        required = False,
+        value_type = TextLine())
+
+class ISchemaDirective(IBaseSchemaDirective):
+    """Register a schema for a specified namespace
+    """
+
+    schemas = Tokens(
+        title = u'Schemas',
+        description = u'List of specific schema containing all the' \
+                      u' properties to display',
+        required = True,
+        value_type = GlobalInterface())
+
+
+class INamespaceDirective(IBaseSchemaDirective):
+    """Registration new namespace with Zope.
+    """
+
+    schemas = Tokens(
+        title = u'Schemas',
+        description = u'List of specific schema containing all the' \
+                      u' properties to display',
+        required = False,
+        value_type = GlobalInterface())
+
+    interfaceType = GlobalInterface(
+        title = u'Interface Type',
+        description = u'',
+        required = False)
+
+
+class IWidgetSubDirective(Interface):
+    """Register Custom WebDAV Widgets for a protocol.
+    """
+
+    propname = TextLine(
+        title = u'Property Name',
+        description = u"""
+        The name of the property / field for which this widget will be used.
+        """,
+        required = True)
+
+    class_ = GlobalObject(
+        title = u'WebDAV Widget Class',
+        description = u'The class that will create the widget',
+        required = True)
+
+
+class IWidgetDirective(IWidgetSubDirective):
+    """Register a custom IDAVWidget widget for a specific property within
+    the given namespace.
+    """
+
+    namespace = URI(
+        title = u'Namespace',
+        description = u'Namespace under which this custom widget will be' \
+                      u' available.',
+        required = True)

Added: Zope3/branches/mkerrin-webdav/src/zope/app/dav/namespaces.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/namespaces.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/namespaces.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -0,0 +1,134 @@
+from zope.interface import Interface, implements, Attribute
+from zope import component
+from zope.schema import getFieldNamesInOrder, getFieldNames
+
+from interfaces import IDAVNamespaceType, IDAVWidget
+from interfaces import INamespaceManager
+
+
+class NamespaceManager(object):
+    implements(INamespaceManager)
+
+    def __init__(self, namespace, schemas = [], restricted_properties = [],
+                 interfaceType = None):
+        self.namespace = namespace
+        # property name -> schema lookup, schema values should be the
+        # smallest possible schema containing the property.
+        self.properties = {}
+        # a list of property names which should not be listed and rendered
+        # in response to a allprop PROPFIND request.
+        self.restricted_properties = restricted_properties
+        # property name -> widget class to use when rendering a property.
+        self.widgets = {}
+        # optional interface to use with the APIDOC - all schemas will be
+        # declared to implement this interface type. Hence they will show
+        # up in the interface types section of the APIDOC tool.
+        self.interfaceType = interfaceType
+
+        for schema in schemas:
+            self.registerSchema(schema)
+
+    def registerSchema(self, schema, restricted_properties = []):
+        props = {}
+        for baseschema in (schema,) + schema.getBases():
+            for fieldname in getFieldNames(baseschema):
+                if props.has_key(fieldname):
+                    oldschema = props[fieldname]
+                    if oldschema.isOrExtends(baseschema):
+                        pass
+                    elif baseschema.isOrExtends(oldschema):
+                        props[fieldname] = baseschema
+                    else:
+                        raise TypeError, "duplicate property in %s" % schema
+                props[fieldname] = baseschema
+
+        for propname in props:
+            if self.properties.has_key(propname):
+                oldschema = self.properties[propname]
+                newschema = props[propname]
+                if oldschema.isOrExtends(newschema):
+                    pass
+                elif newschema.isOrExtends(oldschema):
+                    self.properties[propname] = newschema
+                else:
+                    raise TypeError, "duplicate property %s %s %s" %(
+                        propname, schema, oldschema)
+            else:
+                self.properties[propname] = props[propname]
+
+        for propname in restricted_properties:
+            if not self.restricted_properties.has_key(propname):
+                self.restricted_properties.append(propname)
+
+    def registerWidget(self, propname, widget):
+        if not self.properties.has_key(propname):
+            raise TypeError, \
+                "There must exist a property for the widget you are registering"
+        self.widgets[propname] = widget
+
+    def _getAdapter(self, object, propname):
+        schema = self.properties.get(propname, None)
+        if schema is not None:
+            return component.queryAdapter(object, schema, default = None)
+
+        return None
+
+    def hasProperty(self, object, propname):
+        adapter = self._getAdapter(object, propname)
+        if adapter is None:
+            return False
+        return True
+
+    def getProperty(self, object, name):
+        adapter = self._getAdapter(object, name)
+        if adapter is None:
+            raise TypeError, "no property found"
+
+        field = self.properties[name][name]
+        field = field.bind(adapter)
+
+        return field
+
+    def getWidget(self, object, request, name, ns_prefix):
+        adapter = self._getAdapter(object, name)
+        if adapter is None:
+            raise TypeError, "no property found"
+
+        field = self.properties[name][name]
+        field = field.bind(adapter)
+
+        value = field.get(adapter)
+
+        if self.widgets.has_key(name):
+            widget = self.widgets[name](field, request)
+        else:
+            widget = component.getMultiAdapter((field, request), IDAVWidget)
+
+        widget.setRenderedValue(value)
+        widget.setNamespace(self.namespace, ns_prefix)
+
+        return widget
+
+    def getAllPropertyNames(self, object, restricted = False):
+        for propname, schema in self.properties.items():
+            adapter = component.queryAdapter(object, schema, default = None)
+            if adapter is not None and \
+               (restricted is False or \
+                propname not in self.restricted_properties):
+                yield propname
+
+    def getAllProperties(self, object, restricted = False):
+        for propname, schema in self.properties.items():
+            adapter = component.queryAdapter(object, schema, default = None)
+            if adapter is not None:
+                if restricted is False or \
+                       propname not in self.restricted_properties:
+                    field = schema[propname]
+                    field = field.bind(adapter)
+                    yield field
+
+    def isRestrictedProperty(self, object, name):
+        if self.hasProperty(object, name):
+            if name in self.restricted_properties:
+                return True
+        return False


Property changes on: Zope3/branches/mkerrin-webdav/src/zope/app/dav/namespaces.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/propfind.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/propfind.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/propfind.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -21,11 +21,11 @@
 from zope.schema import getFieldNamesInOrder, getFields
 from zope.publisher.http import status_reasons
 
-from zope.app import zapi
+from zope import component
 from zope.app.container.interfaces import IReadContainer
 from zope.app.form.utility import setUpWidget
 
-from interfaces import IDAVWidget, IDAVNamespace
+from interfaces import IDAVWidget, IDAVNamespace, INamespaceManager
 from opaquenamespaces import IDAVOpaqueNamespaces
 from common import MultiStatus
 
@@ -46,18 +46,6 @@
             self.content_type_params = None
         self.default_ns = 'DAV:'
 
-        self.oprops = IDAVOpaqueNamespaces(self.context, None)
-        _avail_props = {}
-        # List all *registered* DAV interface namespaces and their properties
-        for ns, iface in zapi.getUtilitiesFor(IDAVNamespace):
-            _avail_props[ns] = getFieldNamesInOrder(iface)
-
-        # List all opaque DAV namespaces and the properties we know of
-        if self.oprops:
-            for ns, oprops in self.oprops.items():
-                _avail_props[ns] = list(oprops.keys())
-        self.avail_props = _avail_props
-
         self.responsedoc = MultiStatus()
 
     def getDepth(self):
@@ -66,7 +54,7 @@
     def setDepth(self, depth):
         self._depth = depth.lower()
 
-    def PROPFIND(self, xmldoc = None):
+    def PROPFIND(self):
         if self.content_type not in ('text/xml', 'application/xml'):
             self.request.response.setStatus(400)
             return ''
@@ -74,11 +62,10 @@
             self.request.response.setStatus(400)
             return ''
 
-        if xmldoc is None:
-            try:
-                xmldoc = minidom.parse(self.request.bodyStream)
-            except expat.ExpatError:
-                pass
+        try:
+            xmldoc = minidom.parse(self.request.bodyStream)
+        except expat.ExpatError:
+            xmldoc = None # request body is empty ???
 
         self.handlePropfind(xmldoc)
 
@@ -97,12 +84,17 @@
             propname = xmldoc.getElementsByTagNameNS(
                 self.default_ns, 'propname')
             if propname:
-                self._handlePropname(resp)
+                self._renderPropnameResponse(resp)
             else:
                 source = xmldoc.getElementsByTagNameNS(self.default_ns, 'prop')
-                self._handlePropvalues(source)
+                if len(source) == 0:
+                    self._renderAllProperties(resp)
+                elif len(source) == 1:
+                    self._renderSelectedProperties(resp, source[0])
+                else:
+                    raise Exception, "something has gone wrong here"
         else:
-            self._handlePropvalues(None)
+            self._renderAllProperties(resp)
 
         self._depthRecurse(xmldoc)
 
@@ -122,125 +114,66 @@
             responses = subrespdoc.getElementsByTagNameNS(self.default_ns,
                                                           'response')
             for r in responses:
-                ## print "obj: %s, %s" %(zapi.getPath(obj), r.toxml('utf-8'))
                 self.responsedoc.appendResponse(r)
 
-    def _handleProp(self, source):
-        props = {}
-        source = source[0]
-
-        for node in source.childNodes:
-            if node.nodeType != node.ELEMENT_NODE:
-                continue
-
-            ns = node.namespaceURI
-            iface = zapi.queryUtility(IDAVNamespace, ns)
-            value = props.get(ns, {'iface': iface, 'props': []})
-            value['props'].append(node.localName)
-            props[ns] = value
-
-        return props
-
-    def _handleAllprop(self):
-        props = {}
-
-        for ns, properties in self.avail_props.items():
-            iface = zapi.queryUtility(IDAVNamespace, ns)
-            props[ns] = {'iface': iface, 'props': properties}
-
-        return props
-
-    def _handlePropname(self, resp):
+    def _renderAllProperties(self, response):
         count = 0
-        for ns, props in self.avail_props.items():
-            attr_name = 'a%s' % count
+
+        for namespace, nsmanager in \
+                component.getUtilitiesFor(INamespaceManager):
             ns_prefix = None
-            if ns is not None and ns != self.default_ns:
+            if namespace != self.default_ns:
+                ns_prefix = 'a%s' % count
                 count += 1
-                ns_prefix = attr_name
-            for p in props:
-                el = resp.createEmptyElement(ns, ns_prefix, p)
-                resp.addPropertyByStatus(ns, ns_prefix, el)
+            for propname in nsmanager.getAllPropertyNames(self.context):
+                widget = nsmanager.getWidget(self.context, self.request,
+                                             propname, ns_prefix)
+                el = widget.renderProperty()
+                response.addPropertyByStatus(namespace, ns_prefix, el, 200)
 
-    def _handlePropvalues(self, source):
-        if not source:
-            _props = self._handleAllprop()
-        else:
-            _props = self._handleProp(source)
-
-        self._renderResponse(self.resp, _props)
-
-    def _renderResponse(self, re, _props):
+    def _renderSelectedProperties(self, response, source):
         count = 0
-        # ns - the full namespace for this object.
-        for ns, ifaceprops in _props.items():
-            attr_name = 'a%s' % count
-            ns_prefix = None
-            if ns is not None and ns != self.default_ns:
-                count += 1
-                ns_prefix = attr_name
+        renderedns = {}
 
-            iface = ifaceprops['iface']
-            props = ifaceprops['props']
-
-            # adapted - the current view through which all properties are
-            # reterived this should be moved to using the widget framework.
-            if not iface:
-                for name in props:
-                    if self.oprops:
-                        status = 200
-                        el = self.oprops.renderProperty(ns, ns_prefix, name)
-                        if el is None:
-                            # We can't add a None property in the MultiStatus
-                            # utility so add an empty property registered has
-                            # a 404 stats not found property.
-                            status = 404
-                            el = re.createEmptyElement(ns, ns_prefix, name)
-                        re.addPropertyByStatus(ns, ns_prefix, el, status)
-                    else:
-                        el = re.createEmptyElement(ns, ns_prefix, name)
-                        re.addPropertyByStatus(ns, ns_prefix, el, 404)
+        for node in source.childNodes:
+            if node.nodeType != node.ELEMENT_NODE:
                 continue
 
-            adapted = iface(self.context, None)
-            if adapted is None:
-                # XXX - maybe these properties are unavailable for a reason.
-                # render unavailable properties
-                for propname in props:
-                    el = re.createEmptyElement(ns, ns_prefix, propname)
-                    re.addPropertyByStatus(ns, ns_prefix, el, 404)
-                    continue
+            namespace = node.namespaceURI
+            propname = node.localName
+            status = 200
 
-            for propname in props:
-                status = 200
-                el = None # property DOM fragment
-                field = None
+            ns_prefix = None
+            if not renderedns.has_key(namespace) and \
+                   namespace != self.default_ns:
+                ns_prefix = 'a%s' % count
+                count += 1
+            elif namespace != self.default_ns:
+                ns_prefix = renderedns[namespace]
 
-                try:
-                    field = iface[propname]
-                except KeyError:
-                    # A widget wasn't generated for this property
-                    # because the attribute was missing on the adapted
-                    # object, which actually means that the adapter
-                    # didn't fully implement the interface ;(
-                    el = re.createEmptyElement(ns, ns_prefix, propname)
-                    status = 404
+            nsmanager = component.queryUtility(INamespaceManager, namespace,
+                                               default = None)
 
-                    re.addPropertyByStatus(ns, ns_prefix, el, status)
-                    continue
+            if nsmanager is not None:
+                if nsmanager.hasProperty(self.context, propname):
+                    widget = nsmanager.getWidget(self.context, self.request,
+                                                 propname, ns_prefix)
+                    el = widget.renderProperty()
+            else:
+                el = response.createEmptyElement(namespace, ns_prefix,
+                                                 propname)
+                status = 404
 
-                try:
-                    setUpWidget(self, propname, field, IDAVWidget,
-                                value = field.get(adapted),
-                                ignoreStickyValues = False)
-                    widget = getattr(self, propname + '_widget', None)
-                    assert widget is not None
-                    el = widget.renderProperty(ns, ns_prefix)
-                    if widget.getErrors():
-                        status = 500
-                except:
-                    # Internal Server Error - status 500
-                    el = re.createEmptyElement(ns, ns_prefix, propname)
-                    status = 500
+            response.addPropertyByStatus(namespace, ns_prefix, el, status)
 
-                re.addPropertyByStatus(ns, ns_prefix, el, status)
+    def _renderPropnameResponse(self, response):
+        count = 0
+        for namespace, manager in component.getUtilitiesFor(INamespaceManager):
+            if namespace != self.default_ns:
+                ns_prefix = 'a%s' % count
+                count += 1
+            else:
+                ns_prefix = None
+            for propname in manager.getAllPropertyNames(self.context):
+                el = response.createEmptyElement(namespace, ns_prefix, propname)
+                response.addPropertyByStatus(namespace, ns_prefix, el, 200)

Added: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davduplicateproperty.zcml
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davduplicateproperty.zcml	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davduplicateproperty.zcml	2006-02-17 12:35:02 UTC (rev 41639)
@@ -0,0 +1,11 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:dav="http://namespaces.zope.org/dav">
+
+  <include package="zope.app.dav" file="meta.zcml"/>
+
+  <dav:schemas
+     namespace="http://examplenamespace.org/dav/schema"
+     schemas=".test_namespace.IExampleDuplicateSchema"
+     />
+
+</configure>


Property changes on: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davduplicateproperty.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davnamespace.zcml
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davnamespace.zcml	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davnamespace.zcml	2006-02-17 12:35:02 UTC (rev 41639)
@@ -0,0 +1,26 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:dav="http://namespaces.zope.org/dav">
+
+  <include package="zope.app.dav" file="meta.zcml"/>
+
+  <dav:namespace
+     namespace="http://examplenamespace.org/dav/schema"
+     interfaceType=".test_namespace.IExampleNamespaceType"
+     />
+ 
+  <dav:schemas
+     namespace="http://examplenamespace.org/dav/schema"
+     schemas=".test_namespace.IExampleSchema"
+     />
+
+  <dav:schemas
+     namespace="http://examplenamespace.org/dav/schema"
+     schemas=".test_namespace.IExampleSchemaExtended"
+     />
+
+  <dav:schemas
+     namespace="http://examplenamespace.org/dav/schema"
+     schemas=".test_namespace.IExampleContactSchema"
+     />
+
+</configure>


Property changes on: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/davnamespace.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_locking.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_locking.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_locking.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -119,7 +119,7 @@
         ztapi.provideUtility(ILockStorage, storage)
         ztapi.provideUtility(ILockTracker, storage)
 
-        ztapi.provideAdapter(Interface, interfaces.IDAVActiveLock,
+        ztapi.provideAdapter(Interface, interfaces.IActiveLock,
                              ActiveLock)
         ztapi.provideAdapter(Interface, interfaces.IDAVLockSchema,
                              DAVSchemaAdapter)

Added: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_namespace.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_namespace.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_namespace.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -0,0 +1,269 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test the Zope WebDAV namespace registry.
+
+$Id:$
+"""
+import unittest
+from cStringIO import StringIO
+
+from zope.configuration import xmlconfig
+from zope.configuration.config import ConfigurationExecutionError
+from zope.interface.interfaces import IInterface
+from zope.interface.verify import verifyObject
+from zope.interface import Interface, implements
+import zope.app.dav.tests
+from zope.app.component.testing import PlacefulSetup
+
+from zope import component
+from zope.interface.declarations import directlyProvides
+from zope.schema import Int, TextLine
+from zope.schema.interfaces import IInt, ITextLine
+from zope.app.dav.namespaces import NamespaceManager
+from zope.app.dav.interfaces import INamespaceManager, IWebDAVRequest, \
+     IDAVWidget
+from zope.app.dav.common import WebDAVRequest
+from zope.app.dav.widget import IntDAVWidget, TextDAVWidget
+
+namespace = 'http://examplenamespace.org/dav/schema'
+
+#
+# Some Schema's to test for properties.
+#
+
+class IExampleNamespaceType(IInterface):
+    """ """
+
+class IExampleSchema(Interface):
+    """ """
+    age = Int(title = u'Age')
+
+    name = TextLine(title = u'Name')
+
+class IExampleExtendedSchema(IExampleSchema):
+    """ """
+    job = TextLine(title = u'Job Title')
+
+    company = TextLine(title = u'Place of employment')
+
+class IExampleContactSchema(Interface):
+    """ """
+    phoneNo = TextLine(title = u'Phone Number')
+
+class IExampleDuplicateSchema(Interface):
+    """ """
+    age = TextLine(title = u'Duplicate age property')
+
+#
+# Some interfaces and content objects and adapters to test the schemas against.
+#
+
+class IExampleContent(Interface):
+    """Marker interface for content objects...
+    """
+
+
+class IExampleExtendedContent(IExampleContent):
+    """Marker interface for content objects that will have the IExampleExtended
+    properties defined on them.
+    """
+
+
+class IExampleContactContent(Interface):
+    """Marker interface for content objects that have the IExampleContactSchema
+    properties defined on them.
+    """
+
+
+class Content(object):
+    implements(IExampleContent)
+
+    def __init__(self, parent, name):
+        self.__parent__ = parent
+        self.__name__   = name
+
+
+class ExampleAdapter(object):
+    implements(IExampleSchema)
+
+    def __init__(self, context):
+        self.context = context
+
+    @property
+    def age(self):
+        return 15
+
+    @property
+    def name(self):
+        return 'The Other Michael Kerrin'
+
+class ExampleExtendedAdapter(object):
+    implements(IExampleExtendedSchema)
+
+    def __init__(self, context):
+        self.context = context
+
+    @property
+    def age(self):
+        return 10
+
+    @property
+    def name(self):
+        return 'Michael Kerrin'
+
+    @property
+    def job(self):
+        return 'Programmer'
+
+    @property
+    def company(self):
+        return 'OpenApp'
+
+
+class ExampleContactAdapter(object):
+    implements(IExampleContactSchema)
+
+    def __init__(self, context):
+        self.context = context
+
+    @property
+    def phoneNo(self):
+        return '01 1234567'
+
+
+class TestNamespaceDirectives(unittest.TestCase):
+
+    def test_namespace_directives(self):
+        self.assertEqual(
+            component.queryUtility(INamespaceManager, namespace), None)
+        xmlconfig.XMLConfig("davnamespace.zcml", zope.app.dav.tests)()
+        nmanager = component.getUtility(INamespaceManager, namespace)
+        verifyObject(INamespaceManager, nmanager)
+        # quick check to see that schemas work
+        self.assert_(len(nmanager.properties) > 0)
+        # check that we correctly catch duplicate declarations of properties
+        self.assertRaises(ConfigurationExecutionError,
+                          xmlconfig.XMLConfig("davduplicateproperty.zcml",
+                                              zope.app.dav.tests))
+
+
+class TestNamespaceRegistry(unittest.TestCase):
+
+    def setUp(self):
+        davnamespace = NamespaceManager(namespace,
+                                        schemas = (IExampleSchema,
+                                                   IExampleExtendedSchema,
+                                                   IExampleContactSchema))
+        component.provideUtility(davnamespace, INamespaceManager, namespace)
+
+        # all objects have the properties defined in IExampleSchema defined.
+        component.provideAdapter(ExampleAdapter, (IExampleContent,),
+                                 IExampleSchema)
+        component.provideAdapter(ExampleExtendedAdapter,
+                                 (IExampleExtendedContent,),
+                                 IExampleExtendedSchema)
+        component.provideAdapter(ExampleContactAdapter,
+                                 (IExampleContactContent,),
+                                 IExampleContactSchema)
+
+        # setup for widget adapters.
+        component.provideAdapter(IntDAVWidget, (IInt, IWebDAVRequest),
+                                 IDAVWidget)
+        component.provideAdapter(TextDAVWidget, (ITextLine, IWebDAVRequest),
+                                 IDAVWidget)
+
+    def test_correct_properties(self):
+        nr = component.getUtility(INamespaceManager, namespace)
+        expected = ['age', 'name', 'job', 'company', 'phoneNo']
+        expected.sort()
+        props = nr.properties.keys()
+        props.sort()
+        self.assertEqual(props, expected)
+
+    def test_correct_schema(self):
+        nr = component.getUtility(INamespaceManager, namespace)
+        ageschema = nr.properties['age']
+        jobschema = nr.properties['job']
+        self.assertEqual(ageschema, IExampleSchema)
+        self.assertEqual(jobschema, IExampleExtendedSchema)
+
+    def test_defined_properties(self):
+        # should be missing the phoneNo since no adapter exists.
+        nr = component.getUtility(INamespaceManager, namespace)
+        context = Content(None, 'contenttype')
+        names = list(nr.getAllPropertyNames(context))
+        names.sort()
+        self.assertEquals(names, ['age', 'name'])
+        # now extend the what the context object implements
+        directlyProvides(context, (IExampleExtendedContent,
+                                   IExampleContactContent))
+        names = list(nr.getAllPropertyNames(context))
+        names.sort()
+        self.assertEquals(names, ['age', 'company', 'job', 'name', 'phoneNo'])
+
+    def test_properties(self):
+        nr = component.getUtility(INamespaceManager, namespace)
+        context = Content(None, 'contenttype')
+        directlyProvides(context, (IExampleExtendedContent,
+                                   IExampleContactContent))
+        fields = list(nr.getAllProperties(context))
+
+        names  = [field.getName() for field in fields]
+        names.sort()
+        self.assertEquals(names, ['age', 'company', 'job', 'name', 'phoneNo'])
+
+        #
+        # Assert that the adapters found via the namespace manager matches
+        # what we expect.
+        #
+        adapters = {'age': ExampleExtendedAdapter,
+                    'company': ExampleExtendedAdapter,
+                    'job': ExampleExtendedAdapter,
+                    'name': ExampleExtendedAdapter,
+                    'phoneNo': ExampleContactAdapter,
+                    }
+        for name in names:
+            field = nr.getProperty(context, name)
+            self.assert_(isinstance(field.context, adapters[field.getName()]))
+
+    def test_widget(self):
+        nr = component.getUtility(INamespaceManager, namespace)
+        context = Content(None, 'contenttype')
+        instream = StringIO('')
+        request  = WebDAVRequest(instream, {})
+
+        agewidget = nr.getWidget(context, request, 'age', 'a0')
+        self.assert_(isinstance(agewidget, IntDAVWidget))
+
+        namewidget = nr.getWidget(context, request, 'name', 'a0')
+        xmlel = namewidget.renderProperty()
+        self.assertEqual(xmlel.toxml(),
+                         '<name xmlns="a0">The Other Michael Kerrin</name>')
+
+    def test_has_property(self):
+        nr = component.getUtility(INamespaceManager, namespace)
+        context = Content(None, 'contenttype')
+
+        self.assert_(nr.hasProperty(context, 'age'))
+        self.assert_(nr.hasProperty(context, 'job') is False)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestNamespaceRegistry))
+
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest = 'test_suite')


Property changes on: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_namespace.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_propfind.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_propfind.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_propfind.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -25,9 +25,11 @@
 
 from zope.pagetemplate.tests.util import normalize_xml
 from zope.schema import getFieldNamesInOrder
-from zope.schema.interfaces import IText, ITextLine, IDatetime, ISequence, IInt
+from zope.schema.interfaces import IText, ITextLine, IDatetime, ISequence, \
+     IList, IInt
 
-from zope.app import zapi
+from zope import component
+from zope.app.traversing.browser.absoluteurl import absoluteURL
 from zope.app.testing import ztapi
 
 from zope.app.traversing.api import traverse
@@ -48,16 +50,18 @@
 from zope.app.dav.interfaces import IDAVWidget
 from zope.app.dav.interfaces import IXMLEmptyElementList, XMLEmptyElementList
 from zope.app.dav.widget import TextDAVWidget, DatetimeDAVWidget, \
-     XMLEmptyElementListDAVWidget, SequenceDAVWidget, DAVXMLSubPropertyWidget, \
-     DAVOpaqueWidget
+     XMLEmptyElementListDAVWidget, SequenceDAVWidget, ListDAVWidget, \
+     DAVXMLSubPropertyWidget, DAVOpaqueWidget, ISO8601DateDAVWidget
 from zope.app.dav.opaquenamespaces import DAVOpaqueNamespacesAdapter
 from zope.app.dav.opaquenamespaces import IDAVOpaqueNamespaces
 from zope.app.dav.adapter import DAVSchemaAdapter
 from zope.app.dav.fields import IDAVXMLSubProperty, IDAVOpaqueField
+from zope.app.dav.interfaces import INamespaceManager
+from zope.app.dav.namespaces import NamespaceManager
 
 from unitfixtures import File, Folder, FooZPT
+import xmldiff
 
-
 def _createRequest(body=None, headers=None, skip_headers=None):
     if body is None:
         body = '''<?xml version="1.0" encoding="utf-8"?>
@@ -99,8 +103,9 @@
     def sizeForDisplay(self):
         return 'big'
 
-class TestPlacefulPROPFIND(PlacefulSetup, TestCase):
 
+class TestPlacefulPROPFINDBase(PlacefulSetup, TestCase):
+
     def setUp(self):
         PlacefulSetup.setUp(self)
         PlacefulSetup.buildFolders(self)
@@ -124,6 +129,7 @@
         ztapi.browserViewProviding(ITextLine, TextDAVWidget, IDAVWidget)
         ztapi.browserViewProviding(IDatetime, DatetimeDAVWidget, IDAVWidget)
         ztapi.browserViewProviding(ISequence, SequenceDAVWidget, IDAVWidget)
+        ztapi.browserViewProviding(IList, ListDAVWidget, IDAVWidget)
         ztapi.browserViewProviding(IXMLEmptyElementList,
                                    XMLEmptyElementListDAVWidget, IDAVWidget)
         ztapi.browserViewProviding(IDAVXMLSubProperty, DAVXMLSubPropertyWidget,
@@ -138,13 +144,51 @@
         ztapi.provideAdapter(None, IDAVSchema,
                              DAVSchemaAdapter)
         ztapi.provideAdapter(IFile, ISized, FileSized)
-        sm = zapi.getGlobalSiteManager()
-        directlyProvides(IDAVSchema, IDAVNamespace)
-        sm.provideUtility(IDAVNamespace, IDAVSchema, 'DAV:')
-        directlyProvides(IZopeDublinCore, IDAVNamespace)
-        sm.provideUtility(IDAVNamespace, IZopeDublinCore,
-                          'http://www.purl.org/dc/1.1')
 
+        # new webdav configuration
+        davnamespace = NamespaceManager('DAV:', schemas = (IDAVSchema,))
+        davnamespace.registerWidget('creationdate', ISO8601DateDAVWidget)
+        component.provideUtility(davnamespace, INamespaceManager, 'DAV:')
+
+        dcnamespace = NamespaceManager('http://www.purl.org/dc/1.1',
+                                       schemas = (IZopeDublinCore,))
+        component.provideUtility(dcnamespace, INamespaceManager,
+                                 'http://www.purl.org/dc/1.1')
+
+    def _checkPropfind(self, obj, req, expect, depth='0', resp=None):
+        if req:
+            body = '''<?xml version="1.0" ?>
+            <propfind xmlns="DAV:">%s</propfind>
+            ''' % req
+        else:
+            body = ''
+        request = _createRequest(body=body, headers={
+            'Content-type': 'text/xml', 'Depth': depth})
+        resource_url = absoluteURL(obj, request)
+        if IReadContainer.providedBy(obj):
+            resource_url += '/'
+        if resp is None:
+            resp = '''<?xml version="1.0" encoding="utf-8"?>
+            <multistatus xmlns="DAV:"><response>
+            <href>%%(resource_url)s</href>
+            <propstat>%s
+            <status>HTTP/1.1 200 OK</status>
+            </propstat></response></multistatus>
+            '''
+        expect = resp % expect
+        expect = expect % {'resource_url': resource_url}
+        pfind = propfind.PROPFIND(obj, request)
+        pfind.PROPFIND()
+        # Check HTTP Response
+        self.assertEqual(request.response.getStatus(), 207)
+        self.assertEqual(pfind.getDepth(), depth)
+        s1 = normalize_xml(request.response.consumeBody())
+        s2 = normalize_xml(expect)
+        xmldiff.compareMultiStatus(self, s1, s2)
+
+
+class TestPlacefulPROPFIND(TestPlacefulPROPFINDBase):
+
     def test_contenttype1(self):
         file = self.file
         request = _createRequest(headers={'Content-type':'text/xml'})
@@ -247,37 +291,6 @@
         self.assertEqual(request.response.getStatus(), 400)
         self.assertEqual(pfind.getDepth(), 'full')
 
-    def _checkPropfind(self, obj, req, expect, depth='0', resp=None):
-        if req:
-            body = '''<?xml version="1.0" ?>
-            <propfind xmlns="DAV:">%s</propfind>
-            ''' % req
-        else:
-            body = ''
-        request = _createRequest(body=body, headers={
-            'Content-type': 'text/xml', 'Depth': depth})
-        resource_url = zapi.absoluteURL(obj, request)
-        if IReadContainer.providedBy(obj):
-            resource_url += '/'
-        if resp is None:
-            resp = '''<?xml version="1.0" encoding="utf-8"?>
-            <multistatus xmlns="DAV:"><response>
-            <href>%%(resource_url)s</href>
-            <propstat>%s
-            <status>HTTP/1.1 200 OK</status>
-            </propstat></response></multistatus>
-            '''
-        expect = resp % expect
-        expect = expect % {'resource_url': resource_url}
-        pfind = propfind.PROPFIND(obj, request)
-        pfind.PROPFIND()
-        # Check HTTP Response
-        self.assertEqual(request.response.getStatus(), 207)
-        self.assertEqual(pfind.getDepth(), depth)
-        s1 = normalize_xml(request.response.consumeBody())
-        s2 = normalize_xml(expect)
-        self.assertEqual(s1, s2)
-
     def test_resourcetype(self):
         file = self.file
         folder = traverse(self.rootFolder, 'folder')
@@ -296,6 +309,38 @@
         self._checkPropfind(file, req, expect_file)
         self._checkPropfind(folder, req, expect_folder)
 
+    def test_creationdate(self):
+        root = self.rootFolder
+        file = traverse(root, 'zpt')
+        dc = IZopeDublinCore(file)
+        dc.created = now = datetime.utcnow()
+
+        req = '''<prop>
+        <creationdate/>
+        </prop>
+        '''
+        expect_file = '''<prop>
+        <creationdate>%s</creationdate>
+        </prop>
+        ''' % now.strftime('%Y-%m-%dT%TZ')
+        self._checkPropfind(file, req, expect_file)
+
+    def test_lastmodifieddate(self):
+        root = self.rootFolder
+        file = traverse(root, 'zpt')
+        dc = IZopeDublinCore(file)
+        dc.modified = now = datetime.utcnow()
+
+        req = '''<prop>
+        <getlastmodified/>
+        </prop>
+        '''
+        expect_file = '''<prop>
+        <getlastmodified>%s</getlastmodified>
+        </prop>
+        ''' % now.strftime('%a, %d %b %Y %H:%M:%S +0000')
+        self._checkPropfind(file, req, expect_file)
+
     def test_getcontentlength(self):
         file = self.file
         folder = traverse(self.rootFolder, 'folder')
@@ -353,8 +398,7 @@
         dc.created = datetime.utcnow()
         req = '''<prop xmlns:DC="http://www.purl.org/dc/1.1">
         <DC:created /></prop>'''
-        ## the format for the created date below is '%s' dc.created - changing
-        ## to the format generated by the new date time dav widget
+
         expect = '''<prop xmlns:a0="http://www.purl.org/dc/1.1">
         <created xmlns="a0">%s</created></prop>''' % \
                         dc.created.strftime('%a, %d %b %Y %H:%M:%S %z').rstrip()
@@ -372,39 +416,54 @@
         <subjects xmlns="a0">%s</subjects></prop>''' % u', '.join(dc.subjects)
         self._checkPropfind(zpt, req, expect)
 
+    def test_davallprop(self):
+        req = '<allprop/>'
+
+        expect = ''
+        # set at list one value
+        dc = IZopeDublinCore(self.rootFolder)
+        dc.title = u'Root Folder'
+        props = getFieldNamesInOrder(IZopeDublinCore)
+        for p in props:
+            v = getattr(dc, p, '')
+            if not v:
+                v = ''
+            expect += '<%s xmlns="a0">%s</%s>' %(p, v, p)
+        props = getFieldNamesInOrder(IDAVSchema)
+        for p in props:
+            v = ''
+            expect += '<%s>%s</%s>' %(p, v, p)
+        expect = '<prop>%s</prop>' % expect
+        self._checkPropfind(self.rootFolder, req, expect)
+
     def test_davpropname(self):
         root = self.rootFolder
         zpt = traverse(root, 'zpt')
-        oprops = IDAVOpaqueNamespaces(zpt)
-        oprops[u'http://foo/bar'] = {u'egg': '<egg>spam</egg>'}
         req = '''<propname/>'''
 
         expect = ''
         props = getFieldNamesInOrder(IZopeDublinCore)
         for p in props:
             expect += '<%s xmlns="a0"/>' % p
-        expect += '<egg xmlns="a1"/>'
         props = getFieldNamesInOrder(IDAVSchema)
         for p in props:
             expect += '<%s/>' % p
         expect = '''
-        <prop xmlns:a0="http://www.purl.org/dc/1.1" xmlns:a1="http://foo/bar">
+        <prop xmlns:a0="http://www.purl.org/dc/1.1">
         %s</prop>''' % expect
         self._checkPropfind(zpt, req, expect)
 
     def test_davpropnamefolderdepth0(self):
+        # DC properties are not supported on this folder object.
         root = self.rootFolder
         folder = traverse(root, 'folder')
         req = '''<propname/>'''
 
         expect = ''
-        props = getFieldNamesInOrder(IZopeDublinCore)
-        for p in props:
-            expect += '<%s xmlns="a0"/>' % p
         props = getFieldNamesInOrder(IDAVSchema)
         for p in props:
             expect += '<%s/>' % p
-        expect = '''<prop xmlns:a0="http://www.purl.org/dc/1.1">
+        expect = '''<prop>
         %s</prop>''' % expect
         self._checkPropfind(folder, req, expect)
 
@@ -414,9 +473,6 @@
         req = '''<propname/>'''
 
         props_xml = ''
-        props = getFieldNamesInOrder(IZopeDublinCore)
-        for p in props:
-            props_xml += '<%s xmlns="a0"/>' % p
         props = getFieldNamesInOrder(IDAVSchema)
         for p in props:
             props_xml += '<%s/>' % p
@@ -425,7 +481,7 @@
         for p in ('', '1', '2', 'sub1/'):
             expect += '''
             <response><href>%(path)s</href>
-            <propstat><prop xmlns:a0="http://www.purl.org/dc/1.1">
+            <propstat><prop>
             %(props_xml)s</prop><status>HTTP/1.1 200 OK</status>
             </propstat></response>
             ''' % {'path': '%(resource_url)s' + p, 'props_xml': props_xml}
@@ -440,19 +496,15 @@
         req = '''<propname/>'''
 
         props_xml = ''
-        props = getFieldNamesInOrder(IZopeDublinCore)
-        for p in props:
-            props_xml += '<%s xmlns="a0"/>' % p
         props = getFieldNamesInOrder(IDAVSchema)
         for p in props:
             props_xml += '<%s/>' % p
-
         expect = ''
         for p in ('', '1', '2', 'sub1/', 'sub1/1', 'sub1/2', 'sub1/sub1/',
                   'sub1/sub1/last'):
             expect += '''
             <response><href>%(path)s</href>
-            <propstat><prop xmlns:a0="http://www.purl.org/dc/1.1">
+            <propstat><prop>
             %(props_xml)s</prop><status>HTTP/1.1 200 OK</status>
             </propstat></response>
             ''' % {'path': '%(resource_url)s' + p, 'props_xml': props_xml}
@@ -461,10 +513,13 @@
         <multistatus xmlns="DAV:">%s</multistatus>'''
         self._checkPropfind(folder, req, expect, depth='infinity', resp=resp)
 
+#
+# opaque property support is now broken
+#
+
+class TestPlacefulDeadPropsPROPFIND(PlacefulSetup, TestCase):
+
     def test_davemptybodyallpropzptdepth0(self):
-        # XXX - this test is failing since the creationdate property is
-        # currently not implemented properly.
-        
         # RFC 2518, Section 8.1: A client may choose not to submit a
         # request body.  An empty PROPFIND request body MUST be
         # treated as a request for the names and values of all
@@ -478,10 +533,7 @@
         req = ''
         expect = ''
         props = getFieldNamesInOrder(IZopeDublinCore)
-        ## XXX - The created date below used to take the format defined in this
-        ## comment. I need to need the date time specification for both DC and
-        ## standard WebDAV.
-        ## '%s+00:00' % now}
+        ## XXX - '%s+00:00' % now}
         pvalues = {'created': now.strftime('%a, %d %b %Y %H:%M:%S %z').rstrip()}
         for p in props:
             if pvalues.has_key(p):
@@ -518,7 +570,7 @@
         request = _createRequest(body = body,
                                  headers = {'Content-type': 'text/xml',
                                             'Depth': depth})
-        resource_url = zapi.absoluteURL(zpt, request)
+        resource_url = absoluteURL(zpt, request)
         resp = '''<?xml version="1.0" encoding="utf-8"?>
         <multistatus xmlns="DAV:"><response>
         <href>%(resource_url)s</href>
@@ -565,9 +617,12 @@
         </egg></prop>'''
         self._checkPropfind(zpt, req, expect)
 
+
 def test_suite():
     return TestSuite((
         makeSuite(TestPlacefulPROPFIND),
+        ## XXX - fix deab properties support in zope.app.dav
+        ## makeSuite(TestPlacefulDeadPropsPROPFIND),
         ))
 
 if __name__ == '__main__':

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_widget.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_widget.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/test_widget.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -23,7 +23,7 @@
 
 from zope.interface import Interface, implements
 from zope.interface.verify import verifyClass
-from zope.schema import Text, Datetime, Date, List, Tuple
+from zope.schema import Text, Datetime, Date, List, Tuple, Int
 from zope.schema import ValidationError
 from zope.publisher.browser import TestRequest
 from zope.testing.doctest import DocTestSuite
@@ -33,12 +33,14 @@
 
 from zope.app.dav.interfaces import IDAVWidget
 from zope.app.dav.widget import DAVWidget, TextDAVWidget, DatetimeDAVWidget, \
-     DateDAVWidget, XMLEmptyElementListDAVWidget, TupleDAVWidget
+     DateDAVWidget, XMLEmptyElementListDAVWidget, TupleDAVWidget, IntDAVWidget
 
 
 class DAVWidgetTest(placelesssetup.PlacelessSetup, TestCase):
 
+    # type of Field we are going to display during this test 
     _FieldFactory = Text
+    # type of DAV widget we are going to test
     _WidgetFactory = DAVWidget
 
     def setUp(self):
@@ -62,6 +64,9 @@
         self.failUnless(verifyClass(IDAVWidget, DAVWidget))
 
     def test_widget_input(self):
+        # make sure that the widget can handle an input value recevied via
+        # the setRenderedValue method. This method is only called rendering
+        # a property.
         content = self.test_content
 
         self.failIf(self._widget.hasInput())
@@ -71,6 +76,9 @@
         self.assertEqual(self._widget.getInputValue(), content)
 
     def _test_widget_bad_input(self, propel):
+        # helper method that tests setting a xml dom fragment that represents
+        # a property. propel should correspond to a correctly formatted
+        # property.
         self._widget.setProperty(propel)
         self.assert_(self._widget.hasInput())
         self.failIf(self._widget.hasValidInput())
@@ -87,12 +95,27 @@
 
 class TextDAVWidgetTest(DAVWidgetTest):
     _WidgetFactory = TextDAVWidget
+    _FieldFactory  = Text
 
     test_content = u'This is some text content'
 
+
+class IntDAVWidgetTest(DAVWidgetTest):
+    _WidgetFactory = IntDAVWidget
+    _FieldFactory  = Int
+
+    test_content = 10
+
+    def test_widget_bad_input(self):
+        doc = minidom.Document()
+        propel = doc.createElement('foo')
+        propel.appendChild(doc.createTextNode(u'This is NOT an integer'))
+        super(IntDAVWidgetTest, self)._test_widget_bad_input(propel)
+
+
 class DatetimeDAVWidgetTest(DAVWidgetTest):
     _WidgetFactory = DatetimeDAVWidget
-    _FieldFactory = Datetime
+    _FieldFactory  = Datetime
 
     test_content = datetime.datetime.fromtimestamp(1131234842)
 
@@ -150,10 +173,9 @@
 
 class XMLEmptyElementListDAVWidgetTest(DAVWidgetTest):
     _WidgetFactory = XMLEmptyElementListDAVWidget
-    _FieldFactory = List
+    _FieldFactory  = List
 
     test_content = [u'hello', u'there']
-    test_bad_contents = [10, u'hello']
 
 
 class TupleDAVWidgetTest(DAVWidgetTest):
@@ -161,12 +183,12 @@
     _FieldFactory = Tuple
 
     test_content = (u'hello', u'there')
-    test_bad_contents = [10, u'hello']
 
 
 def test_suite():
     return TestSuite((
         makeSuite(TextDAVWidgetTest),
+        makeSuite(IntDAVWidgetTest),
         makeSuite(DatetimeDAVWidgetTest),
         makeSuite(DateDAVWidgetTest),
         makeSuite(XMLEmptyElementListDAVWidgetTest),

Added: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/xmldiff.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/xmldiff.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/xmldiff.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -0,0 +1,108 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XML differences for use in testing the WebDAV code base
+
+$Id:$
+"""
+__docformat__ = 'restructuredtext'
+
+from xml.dom import minidom
+
+def getTextNode(self, el):
+    value = None
+    for node in el.childNodes:
+        if node.nodeType != node.TEXT_NODE:
+            continue
+        self.assert_(value is None)
+        value = node.nodeValue
+    return value
+
+def convertToDict(self, ms):
+    responses = {}
+
+    for response in ms.childNodes:
+        self.assertEqual(response.localName, 'response')
+
+        hrefel = response.getElementsByTagNameNS('DAV:', 'href')
+        self.assertEqual(len(hrefel), 1)
+        href = getTextNode(self, hrefel[0])
+        self.assert_(responses.has_key(href) is False)
+        propstats = responses[href] = {}
+
+        for propstat in response.getElementsByTagNameNS('DAV:', 'propstat'):
+            statusel = propstat.getElementsByTagNameNS('DAV:', 'status')
+            self.assertEqual(len(statusel), 1)
+            status = getTextNode(self, statusel[0])
+            properties = propstats[status] = {}
+            propel = propstat.getElementsByTagNameNS('DAV:', 'prop')
+            self.assertEqual(len(propel), 1)
+
+            for propertyel in propel[0].childNodes:
+                if propertyel.nodeType != propertyel.ELEMENT_NODE:
+                    continue
+                ns = propertyel.namespaceURI
+                propname = propertyel.localName
+                value = propertyel.toxml()
+
+                nsprops = properties.setdefault(ns, {})
+                nsprops[propname] = value
+
+    return responses
+
+def compareMultiStatus(self, status1str, status2str):
+    s1 = minidom.parseString(status1str)
+    s2 = minidom.parseString(status2str)
+
+    ms1 = s1.documentElement
+    ms2 = s2.documentElement
+
+    self.assertEqual(ms1.localName, 'multistatus')
+    self.assertEqual(ms2.localName, 'multistatus')
+
+    resp1 = convertToDict(self, ms1)
+    resp2 = convertToDict(self, ms2)
+
+    self.assertEqual(len(resp1), len(resp2))
+    for href, status1 in resp1.items():
+        self.assert_(resp2.has_key(href),
+                     "the expected result is missing a response for the" \
+                     " %s object\n" \
+                     "'%s' != '%s'" % (href, status1str, status2str))
+        status2 = resp2[href]
+
+        for status, namespaces1 in status1.items():
+            self.assert_(status2.has_key(status))
+            namespaces2 = status2[status]
+
+            self.assertEqual(len(namespaces1), len(namespaces2),
+                             "namespace count doesn't match." \
+                             "'%s' != '%s'" %(status1str, status2str))
+        
+            for namespace, properties1 in namespaces1.items():
+                self.assert_(namespaces2.has_key(namespace),
+                             "the namespace %s is missing from the " \
+                             " expected result.\n" \
+                             "'%s' != '%s'" % (namespace, status1str,
+                                               status2str))
+                properties2 = namespaces2[namespace]
+
+                self.assertEqual(len(properties1), len(properties2))
+
+                for propname, value1 in properties1.items():
+                    self.assert_(properties2.has_key(propname),
+                                 "the property %s is missing from the " \
+                                 "expected result" % propname)
+                    value2 = properties2[propname]
+
+                    self.assertEqual(value1, value2)


Property changes on: Zope3/branches/mkerrin-webdav/src/zope/app/dav/tests/xmldiff.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: Zope3/branches/mkerrin-webdav/src/zope/app/dav/widget.py
===================================================================
--- Zope3/branches/mkerrin-webdav/src/zope/app/dav/widget.py	2006-02-17 11:07:32 UTC (rev 41638)
+++ Zope3/branches/mkerrin-webdav/src/zope/app/dav/widget.py	2006-02-17 12:35:02 UTC (rev 41639)
@@ -59,9 +59,10 @@
     def getErrors(self):
         return self._error
 
-    def setNamespace(self, ns, ns_prefix):
-        self.namespace = ns
-        self.ns_prefix = ns_prefix
+    def setNamespace(self, namespace, namespace_prefix):
+        if namespace is not None:
+            self.namespace = namespace
+        self.ns_prefix = namespace_prefix
 
     def setRenderedValue(self, value):
         # don't use this
@@ -134,13 +135,11 @@
             return str(value)
         return None
 
-    def renderProperty(self, ns, ns_prefix):
-        self.setNamespace(ns, ns_prefix)
+    def renderProperty(self):
+        el = self._xmldoc.createElementNS(self.namespace, self.name)
+        if self.ns_prefix is not None:
+            el.setAttributeNS(self.namespace, 'xmlns', self.ns_prefix)
 
-        el = self._xmldoc.createElementNS(ns, self.name)
-        if ns_prefix is not None:
-            el.setAttributeNS(ns, 'xmlns', ns_prefix)
-
         # this is commented out because it cased some problems with values
         # being security proxied when they they are returned from the adapters
         ## value = self._toDAVValue(self.getInputValue())
@@ -187,7 +186,7 @@
     setUpWidget method
 
       >>> widget.setRenderedValue(u'This is some content')
-      >>> rendered = widget.renderProperty(None, None)
+      >>> rendered = widget.renderProperty()
       >>> rendered #doctest:+ELLIPSIS
       <DOM Element: foo at 0x...>
       >>> rendered.toxml()
@@ -197,6 +196,41 @@
     pass
 
 
+class IntDAVWidget(DAVWidget):
+    """
+    Renders a WebDAV property that contains an integer.
+
+      >>> from zope.schema import Int
+      >>> field = Int(__name__ = 'foo', title = u'Foo Title')
+      >>> request = TestRequest()
+      >>> widget = IntDAVWidget(field, request)
+
+    Set up the value stored in the widget. In reality this is done in the
+    setUpWidget method
+
+      >>> widget.setRenderedValue(10)
+      >>> rendered = widget.renderProperty()
+      >>> rendered #doctest:+ELLIPSIS
+      <DOM Element: foo at 0x...>
+      >>> rendered.toxml()
+      '<foo>10</foo>'
+      >>> widget.setProperty(rendered)
+
+    """
+
+    def _setFieldValue(self, value):
+        if not value.childNodes:
+            return self.context.missing_value
+
+        value = value.childNodes[0]
+        try:
+            return int(value.nodeValue)
+        except ValueError, e:
+            raise ConversionError("Invalid int data", e)
+
+    def _toDAVValue(self, value):
+        return str(value)
+
 class DatetimeDAVWidget(DAVWidget):
     """Render a WebDAV date property
 
@@ -211,7 +245,7 @@
       >>> from datetime import datetime
       >>> date = datetime.utcfromtimestamp(1131233651)
       >>> widget.setRenderedValue(date)
-      >>> rendered = widget.renderProperty(None, None)
+      >>> rendered = widget.renderProperty()
       >>> rendered #doctest:+ELLIPSIS
       <DOM Element: foo at ...>
       >>> rendered.toxml() # this was '<foo>2005-11-05 23:34:11Z</foo>'
@@ -236,20 +270,20 @@
         return value.strftime('%a, %d %b %Y %H:%M:%S %z').rstrip()
 
 
-class CreatationDateDAVWidget(DAVWidget):
+class ISO8601DateDAVWidget(DAVWidget):
     """Render a WebDAV date property
 
       >>> from zope.schema import Datetime
       >>> field = Datetime(__name__ = 'foo', title = u'Foo Date Title')
       >>> request = TestRequest()
-      >>> widget = CreatationDateDAVWidget(field, request)
+      >>> widget = ISO8601DateDAVWidget(field, request)
 
     Set the value of the widget to that of the current time.
 
       >>> from datetime import datetime
       >>> date = datetime.utcfromtimestamp(1131233651)
       >>> widget.setRenderedValue(date)
-      >>> rendered = widget.renderProperty(None, None)
+      >>> rendered = widget.renderProperty()
       >>> rendered #doctest:+ELLIPSIS
       <DOM Element: foo at ...>
       >>> rendered.toxml()
@@ -279,7 +313,7 @@
       >>> from datetime import datetime
       >>> date = datetime.utcfromtimestamp(1131233651)
       >>> widget.setRenderedValue(date)
-      >>> rendered = widget.renderProperty(None, None)
+      >>> rendered = widget.renderProperty()
       >>> rendered #doctest:+ELLIPSIS
       <DOM Element: foo at ...>
       >>> rendered.toxml() # this was '<foo>2005-11-05 23:34:11Z</foo>'
@@ -337,7 +371,7 @@
       >>> request = TestRequest()
       >>> widget = XMLEmptyElementListDAVWidget(field, request)
       >>> widget.setRenderedValue(['first', 'second'])
-      >>> rendered = widget.renderProperty(None, None)
+      >>> rendered = widget.renderProperty()
       >>> rendered #doctest:+ELLIPSIS
       <DOM Element: foo at ...>
       >>> rendered.toxml()
@@ -387,7 +421,7 @@
             assert widget is not None
             ## namespace information is not needed here since we set the
             ## default namespace on elements
-            el = widget.renderProperty(self.namespace, self.ns_prefix)
+            el = widget.renderProperty() # self.namespace, self.ns_prefix)
             if pname:
                 res.appendChild(el)
             else:
@@ -398,23 +432,21 @@
 
 class DAVOpaqueWidget(DAVWidget):
 
-    def renderProperty(self, ns, ns_prefix):
-        self.setNamespace(ns, ns_prefix)
-        
+    def renderProperty(self):
         value = self.getInputValue()
         if value == self.context.missing_value:
-            el = self._xmldoc.createElementNS(ns, self.name)
-            if ns_prefix is not None:
-                el.setAttributeNS(ns, 'xmlns', ns_prefix)
+            el = self._xmldoc.createElementNS(self.namespace, self.name)
+            if self.ns_prefix is not None:
+                el.setAttributeNS(self.namespace, 'xmlns', self.ns_prefix)
             return el
         el = minidom.parseString(value)
 
         el = el.documentElement
 
-        if ns_prefix is not None and el.attributes is not None:
+        if self.ns_prefix is not None and el.attributes is not None:
             xmlns = el.attributes.getNamedItem('xmlns')
             if xmlns is None:
-                el.setAttributeNS(ns, 'xmlns', ns_prefix)
+                el.setAttributeNS(self.namespace, 'xmlns', self.ns_prefix)
 
         return el
 



More information about the Zope3-Checkins mailing list