[Checkins] SVN: z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/ Split up the lockingutils module into a manager module for DAVLockmanager and

Michael Kerrin michael.kerrin at openapp.ie
Sun May 20 12:40:29 EDT 2007


Log message for revision 75847:
  Split up the lockingutils module into a manager module for DAVLockmanager and
  the properties module which implements all the WebDAV properties relating to
  locking.
  

Changed:
  U   z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/configure.zcml
  D   z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/lockingutils.py
  A   z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/manager.py
  A   z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/properties.py
  U   z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/tests.py

-=-
Modified: z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/configure.zcml
===================================================================
--- z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/configure.zcml	2007-05-20 16:26:58 UTC (rev 75846)
+++ z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/configure.zcml	2007-05-20 16:40:28 UTC (rev 75847)
@@ -1,19 +1,19 @@
 <configure xmlns="http://namespaces.zope.org/zope">
 
   <adapter
-     factory=".lockingutils.DAVSupportedlock"
+     factory=".properties.DAVSupportedlock"
      />
 
   <adapter
-     factory=".lockingutils.DAVLockdiscovery"
+     factory=".properties.DAVLockdiscovery"
      />
 
   <adapter
-     factory=".lockingutils.DAVLockmanager"
+     factory=".manager.DAVLockmanager"
      trusted="1"
      />
 
-  <class class=".lockingutils.DAVLockmanager">
+  <class class=".manager.DAVLockmanager">
     <require
        permission="zope.View"
        attributes="getActivelock islocked islockable"
@@ -26,13 +26,13 @@
   </class>
 
   <adapter
-     factory=".lockingutils.DAVActiveLock"
+     factory=".properties.DAVActiveLock"
      for="zope.interface.Interface
           z3c.dav.interfaces.IWebDAVRequest"
      provides="z3c.dav.coreproperties.IActiveLock"
      />
 
-  <class class=".lockingutils.DAVActiveLockAdapter">
+  <class class=".properties.DAVActiveLockAdapter">
     <require
        permission="zope.View"
        interface="z3c.dav.coreproperties.IActiveLock"

Deleted: z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/lockingutils.py
===================================================================
--- z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/lockingutils.py	2007-05-20 16:26:58 UTC (rev 75846)
+++ z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/lockingutils.py	2007-05-20 16:40:28 UTC (rev 75847)
@@ -1,825 +0,0 @@
-##############################################################################
-#
-# 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.
-#
-##############################################################################
-"""Support for using zope.locking has a locking mechanism for WebDAV locking.
-
-Note that we can't use zope.locking.utility.TokenUtility has a global utility.
-This is because if a recursive lock request fails half through then the
-utility has already been modified and since it is not persistent
-transaction.abort doesn't unlock the pervious successful locks. Since the
-utility gets into an inconsistent state.
-
-$Id: lockingutils.py 75023 2007-05-02 18:42:54Z mkerrin $
-"""
-__docformat__ = 'restructuredtext'
-
-import time
-import random
-from BTrees.OOBTree import OOBTree
-from zope import component
-from zope import interface
-from zope.locking import tokens
-import zope.locking.interfaces
-from zope.security.proxy import removeSecurityProxy
-from zope.traversing.browser.absoluteurl import absoluteURL
-from zope.app.container.interfaces import IReadContainer
-
-from z3c.dav.coreproperties import ILockEntry, IDAVSupportedlock, \
-     IActiveLock
-import z3c.dav.interfaces
-
-import interfaces
-import indirecttokens
-
-_randGen = random.Random(time.time())
-
-
-################################################################################
-#
-# zope.locking adapters.
-#
-################################################################################
-
-class ExclusiveLockEntry(object):
-    interface.implements(ILockEntry)
-
-    lockscope = [u"exclusive"]
-    locktype = [u"write"]
-
-
-class SharedLockEntry(object):
-    interface.implements(ILockEntry)
-
-    lockscope = [u"shared"]
-    locktype = [u"write"]
-
-
- at component.adapter(interface.Interface, z3c.dav.interfaces.IWebDAVRequest)
- at interface.implementer(IDAVSupportedlock)
-def DAVSupportedlock(context, request):
-    """
-    This adapter retrieves the data for rendering in the `{DAV:}supportedlock`
-    property. The `{DAV:}supportedlock` property provides a listing of lock
-    capabilities supported by the resource.
-
-    When their is no ITokenUtility registered with the system then we can't
-    lock any content object and so this property is undefined.
-
-      >>> DAVSupportedlock(None, None) is None
-      True
-
-      >>> from zope.locking.utility import TokenUtility
-      >>> util = TokenUtility()
-      >>> component.getGlobalSiteManager().registerUtility(
-      ...    util, zope.locking.interfaces.ITokenUtility)
-
-    zope.locking supported both the exclusive and shared lock tokens.
-
-      >>> slock = DAVSupportedlock(None, None)
-      >>> len(slock.supportedlock)
-      2
-      >>> exclusive, shared = slock.supportedlock
-
-      >>> exclusive.lockscope
-      [u'exclusive']
-      >>> exclusive.locktype
-      [u'write']
-
-      >>> shared.lockscope
-      [u'shared']
-      >>> shared.locktype
-      [u'write']
-
-    Cleanup
-
-      >>> component.getGlobalSiteManager().unregisterUtility(
-      ...    util, zope.locking.interfaces.ITokenUtility)
-      True
-
-    """
-    utility = component.queryUtility(zope.locking.interfaces.ITokenUtility,
-                                     context = context, default = None)
-    if utility is None:
-        return None
-    return DAVSupportedlockAdapter()
-
-
-class DAVSupportedlockAdapter(object):
-    interface.implements(IDAVSupportedlock)
-    component.adapts(interface.Interface,
-                     z3c.dav.interfaces.IWebDAVRequest)
-
-    @property
-    def supportedlock(self):
-        return [ExclusiveLockEntry(), SharedLockEntry()]
-
-
-WEBDAV_LOCK_KEY = "z3c.dav.lockingutils.info"
-
- at component.adapter(interface.Interface, z3c.dav.interfaces.IWebDAVRequest)
- at interface.implementer(IActiveLock)
-def DAVActiveLock(context, request):
-    """
-    This adapter is responsible for the data for the `{DAV:}activelock`
-    XML element. This XML element occurs within the `{DAV:}lockdiscovery`
-    property.
-
-      >>> import datetime
-      >>> import pytz
-      >>> from cStringIO import StringIO
-      >>> from zope.interface.verify import verifyObject
-      >>> import zope.locking.utils
-      >>> from zope.locking.utility import TokenUtility
-      >>> from zope.locking.adapters import TokenBroker
-      >>> from z3c.dav.publisher import WebDAVRequest
-
-      >>> def hackNow():
-      ...     return datetime.datetime(2007, 4, 7, tzinfo = pytz.utc)
-      >>> oldNow = zope.locking.utils.now
-      >>> zope.locking.utils.now = hackNow
-
-    The activelock property only exists whenever the zope.locking package
-    is configured properly.
-
-      >>> resource = DemoFolder()
-      >>> request = WebDAVRequest(StringIO(''), {})
-      >>> DAVActiveLock(resource, request) is None
-      True
-
-    Now register a ITokenUtility utility and lock the resource with it.
-
-      >>> util = TokenUtility()
-      >>> component.getGlobalSiteManager().registerUtility(
-      ...    util, zope.locking.interfaces.ITokenUtility)
-
-      >>> locktoken = tokens.ExclusiveLock(
-      ...    resource, 'michael', datetime.timedelta(hours = 1))
-      >>> locktoken = util.register(locktoken)
-
-    DAVActiveLock is still None since their is no adapter from the demo
-    content object to zope.locking.interfaces.ITokenBroker. This is part
-    of the zope.locking installation that hasn't been completed yet.
-
-      >>> DAVActiveLock(resource, request) is None
-      True
-
-      >>> component.getGlobalSiteManager().registerAdapter(
-      ...    TokenBroker, (interface.Interface,),
-      ...    zope.locking.interfaces.ITokenBroker)
-
-      >>> activelock = DAVActiveLock(resource, request)
-      >>> IActiveLock.providedBy(activelock)
-      True
-      >>> verifyObject(IActiveLock, activelock)
-      True
-
-    Now test the data managed by the current activelock property.
-
-      >>> activelock.lockscope
-      [u'exclusive']
-      >>> activelock.locktype
-      [u'write']
-      >>> activelock.timeout
-      u'Second-3600'
-      >>> activelock.lockroot
-      '/dummy/'
-
-    The depth attribute is required by the WebDAV specification. But this
-    information is stored by the z3c.dav.lockingutils in the lock token's
-    annotation. But if a lock token is taken out by an alternative Zope3
-    application that uses the zope.locking package then this information will
-    must likely not be set up. So this adapter should provide reasonable
-    default values for this information. Later we will set up the lock
-    token's annotation data to store this information. The data for the owner
-    and locktoken XML elements are also stored on within the lock tokens
-    annotation key but these XML elements are not required by the WebDAV
-    specification so this data just defaults to None.
-
-      >>> activelock.depth
-      '0'
-      >>> activelock.owner is None
-      True
-      >>> activelock.locktoken is None
-      True
-
-    Now if we try and render this information all the required fields, as
-    specified by the WebDAV specification get rendered.
-
-      >>> lockdiscovery = DAVLockdiscovery(resource, request)
-      >>> davwidget = z3c.dav.properties.getWidget(
-      ...    z3c.dav.coreproperties.lockdiscovery,
-      ...    lockdiscovery, request)
-      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
-      <lockdiscovery xmlns="DAV:" />
-
-      >>> component.getGlobalSiteManager().registerAdapter(DAVActiveLock)
-
-      >>> lockdiscovery = DAVLockdiscovery(resource, request)
-      >>> davwidget = z3c.dav.properties.getWidget(
-      ...    z3c.dav.coreproperties.lockdiscovery,
-      ...    lockdiscovery, request)
-      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
-      <lockdiscovery xmlns="DAV:">
-        <activelock>
-          <lockscope><exclusive /></lockscope>
-          <locktype><write /></locktype>
-          <depth>0</depth>
-          <timeout>Second-3600</timeout>
-          <lockroot>/dummy/</lockroot>
-        </activelock>
-      </lockdiscovery>
-
-    We use the lock tokens annotation to store the data for the owner, depth
-    and locktoken attributes.
-
-      >>> locktoken.annotations[WEBDAV_LOCK_KEY] = OOBTree()
-      >>> locktoken.annotations[WEBDAV_LOCK_KEY]['depth'] = 'testdepth'
-      >>> locktoken.annotations[WEBDAV_LOCK_KEY]['owner'] = '<owner xmlns="DAV:">Me</owner>'
-      >>> locktoken.annotations[WEBDAV_LOCK_KEY]['token'] = 'simpletoken'
-
-    After updating the lock token's annotations we need to regenerate the
-    activelock adapter so that the tokendata internal attribute is setup
-    correctly.
-
-      >>> activelock = DAVActiveLock(resource, request)
-
-    The owner attribute is not required by the WebDAV specification, but
-    we can see it anyways, and similarly for the locktoken attribute.
-
-      >>> activelock.owner
-      '<owner xmlns="DAV:">Me</owner>'
-
-    Each lock token on a resource as at most one `token` associated with it,
-    but in order to display this information correctly we must return a
-    a list with one item.
-
-      >>> activelock.locktoken
-      ['simpletoken']
-
-      >>> lockdiscovery = DAVLockdiscovery(resource, request)
-      >>> davwidget = z3c.dav.properties.getWidget(
-      ...    z3c.dav.coreproperties.lockdiscovery,
-      ...    lockdiscovery, request)
-      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
-      <lockdiscovery xmlns="DAV:">
-        <activelock>
-          <lockscope><exclusive /></lockscope>
-          <locktype><write /></locktype>
-          <depth>testdepth</depth>
-          <owner>Me</owner>
-          <timeout>Second-3600</timeout>
-          <locktoken><href>simpletoken</href></locktoken>
-          <lockroot>/dummy/</lockroot>
-        </activelock>
-      </lockdiscovery>
-
-    Test the indirect locktoken. These are used when we try and lock a
-    collection with the depth header set to `infinity`. These lock tokens
-    share the same annotation information, expiry information and lock token,
-    as the top level lock token.
-
-      >>> resource['demo'] = Demo()
-      >>> sublocktoken = indirecttokens.IndirectToken(
-      ...    resource['demo'], locktoken)
-      >>> sublocktoken = util.register(sublocktoken)
-
-      >>> activelock = DAVActiveLock(resource['demo'], request)
-      >>> verifyObject(IActiveLock, activelock)
-      True
-
-      >>> activelock.lockscope
-      [u'exclusive']
-      >>> activelock.locktype
-      [u'write']
-      >>> activelock.depth
-      'testdepth'
-      >>> activelock.owner
-      '<owner xmlns="DAV:">Me</owner>'
-      >>> activelock.timeout
-      u'Second-3600'
-      >>> activelock.locktoken
-      ['simpletoken']
-      >>> activelock.lockroot
-      '/dummy/'
-
-    Now rendering the lockdiscovery DAV widget for this new resource we get
-    the following.
-
-      >>> lockdiscovery = DAVLockdiscovery(resource['demo'], request)
-      >>> davwidget = z3c.dav.properties.getWidget(
-      ...    z3c.dav.coreproperties.lockdiscovery,
-      ...    lockdiscovery, request)
-      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
-      <lockdiscovery xmlns="DAV:">
-        <activelock>
-          <lockscope><exclusive /></lockscope>
-          <locktype><write /></locktype>
-          <depth>testdepth</depth>
-          <owner>Me</owner>
-          <timeout>Second-3600</timeout>
-          <locktoken><href>simpletoken</href></locktoken>
-          <lockroot>/dummy/</lockroot>
-        </activelock>
-      </lockdiscovery>
-
-      >>> locktoken.end()
-
-    Now a locktoken from an other application could be taken out on our
-    demofolder that we know very little about. For example, a
-    zope.locking.tokens.EndableFreeze` lock token. It should be displayed as
-    an activelock on the resource but since we don't know if the scope of this
-    token is an `{DAV:}exclusive` or `{DAV:}shared` (the only lock scopes
-    currently supported by WebDAV), we will render this information as an
-    empty XML element.
-
-      >>> locktoken = tokens.EndableFreeze(
-      ...    resource, datetime.timedelta(hours = 1))
-      >>> locktoken = util.register(locktoken)
-
-      >>> activelock = DAVActiveLock(resource, request)
-      >>> IActiveLock.providedBy(activelock)
-      True
-
-      >>> activelock.timeout
-      u'Second-3600'
-      >>> activelock.locktype
-      [u'write']
-
-    Now the locktoken is None so no WebDAV client should be able to a resource
-    or more likely they shouldn't be able to take out a new lock on this
-    resource, since the `IF` conditional header shored fail.
-
-      >>> activelock.locktoken is None
-      True
-
-    So far so good. But the EndableFreeze token doesn't correspond to any
-    lock scope known by this WebDAV implementation so when we try and access
-    we just return a empty list. This ensures the `{DAV:}lockscope` element
-    gets rendered by its IDAVWidget but it doesn't contain any information.
-
-      >>> activelock.lockscope
-      []
-      >>> activelock.lockscope != z3c.dav.coreproperties.IActiveLock['lockscope'].missing_value
-      True
-
-    Rending this lock token we get the following.
-
-      >>> lockdiscovery = DAVLockdiscovery(resource, request)
-      >>> davwidget = z3c.dav.properties.getWidget(
-      ...    z3c.dav.coreproperties.lockdiscovery,
-      ...    lockdiscovery, request)
-      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
-      <lockdiscovery xmlns="DAV:">
-        <activelock>
-          <lockscope></lockscope>
-          <locktype><write /></locktype>
-          <depth>0</depth>
-          <timeout>Second-3600</timeout>
-          <lockroot>/dummy/</lockroot>
-        </activelock>
-      </lockdiscovery>
-
-    Unlock the resource.
-
-      >>> locktoken.end()
-
-    Now not all lock tokens have a duration associated with them. In this
-    case the timeout is None, as it is not fully required by the WebDAV
-    specification and all the other attributes will have the default values
-    as tested previously.
-
-      >>> locktoken = tokens.ExclusiveLock(resource, 'michael')
-      >>> locktoken = util.register(locktoken)
-
-      >>> activelock = DAVActiveLock(resource, request)
-      >>> verifyObject(IActiveLock, activelock)
-      True
-      >>> activelock.timeout is None
-      True
-
-      >>> lockdiscovery = DAVLockdiscovery(resource, request)
-      >>> davwidget = z3c.dav.properties.getWidget(
-      ...    z3c.dav.coreproperties.lockdiscovery,
-      ...    lockdiscovery, request)
-      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
-      <lockdiscovery xmlns="DAV:">
-        <activelock>
-          <lockscope><exclusive /></lockscope>
-          <locktype><write /></locktype>
-          <depth>0</depth>
-          <lockroot>/dummy/</lockroot>
-        </activelock>
-      </lockdiscovery>
-
-    Cleanup
-
-      >>> zope.locking.utils.now = oldNow # undo time hack
-
-      >>> component.getGlobalSiteManager().unregisterUtility(
-      ...    util, zope.locking.interfaces.ITokenUtility)
-      True
-      >>> component.getGlobalSiteManager().unregisterAdapter(
-      ...    TokenBroker, (interface.Interface,),
-      ...    zope.locking.interfaces.ITokenBroker)
-      True
-      >>> component.getGlobalSiteManager().unregisterAdapter(DAVActiveLock)
-      True
-
-    """
-    try:
-        token = zope.locking.interfaces.ITokenBroker(context).get()
-    except TypeError:
-        token = None
-    if token is None:
-        return None
-    return DAVActiveLockAdapter(token, context, request)
-
-
-class DAVActiveLockAdapter(object):
-    component.adapts(interface.Interface,
-                     z3c.dav.interfaces.IWebDAVRequest)
-    interface.implements(IActiveLock)
-
-    def __init__(self, token, context, request):
-        self.context = self.__parent__ = context
-        self.token = token
-        self.tokendata = token.annotations.get(WEBDAV_LOCK_KEY, {})
-        self.request = request
-
-    @property
-    def lockscope(self):
-        if interfaces.IIndirectToken.providedBy(self.token):
-            roottoken = self.token.roottoken
-        else:
-            roottoken = self.token
-
-        if zope.locking.interfaces.IExclusiveLock.providedBy(roottoken):
-            return [u"exclusive"]
-        elif zope.locking.interfaces.ISharedLock.providedBy(roottoken):
-            return [u"shared"]
-
-        return []
-
-    @property
-    def locktype(self):
-        return [u"write"]
-
-    @property
-    def depth(self):
-        return self.tokendata.get("depth", "0")
-
-    @property
-    def owner(self):
-        return self.tokendata.get("owner", None)
-
-    @property
-    def timeout(self):
-        remaining = self.token.remaining_duration
-        if remaining is None:
-            return None
-        return u"Second-%d" % remaining.seconds
-
-    @property
-    def locktoken(self):
-        token = self.tokendata.get("token", None)
-        if token is None:
-            return None
-        return [token]
-
-    @property
-    def lockroot(self):
-        if interfaces.IIndirectToken.providedBy(self.token):
-            root = self.token.roottoken.context
-        else:
-            root = self.token.context
-
-        return absoluteURL(root, self.request)
-
-
- at component.adapter(interface.Interface, z3c.dav.interfaces.IWebDAVRequest)
- at interface.implementer(z3c.dav.coreproperties.IDAVLockdiscovery)
-def DAVLockdiscovery(context, request):
-    """
-    This adapter is responsible for getting the data for the
-    `{DAV:}lockdiscovery` property.
-
-      >>> import datetime
-      >>> from zope.interface.verify import verifyObject
-      >>> from zope.locking.utility import TokenUtility
-      >>> from zope.locking.adapters import TokenBroker
-      >>> from z3c.dav.publisher import WebDAVRequest
-      >>> from cStringIO import StringIO
-      >>> resource = Demo()
-      >>> request = WebDAVRequest(StringIO(''), {})
-
-      >>> DAVLockdiscovery(resource, request) is None
-      True
-
-      >>> util = TokenUtility()
-      >>> component.getGlobalSiteManager().registerUtility(
-      ...    util, zope.locking.interfaces.ITokenUtility)
-      >>> component.getGlobalSiteManager().registerAdapter(DAVActiveLock,
-      ...    (interface.Interface, z3c.dav.interfaces.IWebDAVRequest),
-      ...     IActiveLock)
-      >>> component.getGlobalSiteManager().registerAdapter(
-      ...    TokenBroker, (interface.Interface,),
-      ...    zope.locking.interfaces.ITokenBroker)
-
-    The `{DAV:}lockdiscovery` is now defined for the resource but its value
-    is None because the resource isn't locked yet.
-
-      >>> lockdiscovery = DAVLockdiscovery(resource, request)
-      >>> lockdiscovery is not None
-      True
-      >>> lockdiscovery.lockdiscovery is None
-      True
-
-      >>> token = tokens.ExclusiveLock(
-      ...    resource, 'michael', datetime.timedelta(hours = 1))
-      >>> token = util.register(token)
-      >>> tokenannot = token.annotations[WEBDAV_LOCK_KEY] = OOBTree()
-      >>> tokenannot['depth'] = 'testdepth'
-
-      >>> lockdiscoveryview = DAVLockdiscovery(resource, request)
-      >>> lockdiscovery = lockdiscoveryview.lockdiscovery
-      >>> len(lockdiscovery)
-      1
-      >>> IActiveLock.providedBy(lockdiscovery[0])
-      True
-      >>> isinstance(lockdiscovery[0], DAVActiveLockAdapter)
-      True
-
-    Cleanup
-
-      >>> component.getGlobalSiteManager().unregisterUtility(
-      ...    util, zope.locking.interfaces.ITokenUtility)
-      True
-      >>> component.getGlobalSiteManager().unregisterAdapter(DAVActiveLock,
-      ...    (interface.Interface, z3c.dav.interfaces.IWebDAVRequest),
-      ...     IActiveLock)
-      True
-      >>> component.getGlobalSiteManager().unregisterAdapter(
-      ...    TokenBroker, (interface.Interface,),
-      ...    zope.locking.interfaces.ITokenBroker)
-      True
-
-    """
-    utility = component.queryUtility(zope.locking.interfaces.ITokenUtility)
-    if utility is None:
-        return None
-    return DAVLockdiscoveryAdapter(context, request)
-
-
-class DAVLockdiscoveryAdapter(object):
-    interface.implements(z3c.dav.coreproperties.IDAVLockdiscovery)
-    component.adapts(interface.Interface,
-                     z3c.dav.interfaces.IWebDAVRequest)
-
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-
-    @property
-    def lockdiscovery(self):
-        adapter = component.queryMultiAdapter((self.context, self.request),
-                                              IActiveLock, default = None)
-        if adapter is None:
-            return None
-        return [adapter]
-
-
-class DAVLockmanager(object):
-    """
-
-      >>> from zope.interface.verify import verifyObject
-      >>> from zope.locking import utility, utils
-      >>> from zope.locking.adapters import TokenBroker
-
-      >>> file = Demo()
-
-    Before we register a ITokenUtility utility make sure that the DAVLockmanager
-    is not lockable.
-
-      >>> adapter = DAVLockmanager(file)
-      >>> adapter.islockable()
-      False
-
-    Now create and register a ITokenUtility utility.
-
-      >>> util = utility.TokenUtility()
-      >>> component.getGlobalSiteManager().registerUtility(
-      ...    util, zope.locking.interfaces.ITokenUtility)
-      >>> component.getGlobalSiteManager().registerAdapter(
-      ...    TokenBroker, (interface.Interface,),
-      ...    zope.locking.interfaces.ITokenBroker)
-
-      >>> import datetime
-      >>> import pytz
-      >>> def hackNow():
-      ...     return datetime.datetime(2006, 7, 25, 23, 49, 51)
-      >>> oldNow = utils.now
-      >>> utils.now = hackNow
-
-    Test the DAVLockmanager implements the descired interface.
-
-      >>> adapter = DAVLockmanager(file)
-      >>> verifyObject(z3c.dav.interfaces.IDAVLockmanager, adapter)
-      True
-
-    The adapter should also be lockable.
-
-      >>> adapter.islockable()
-      True
-
-    Lock with an exclusive lock token.
-
-      >>> roottoken = adapter.lock(u'exclusive', u'write',
-      ...    u'Michael', datetime.timedelta(seconds = 3600), '0')
-      >>> util.get(file) == roottoken
-      True
-      >>> zope.locking.interfaces.IExclusiveLock.providedBy(roottoken)
-      True
-
-      >>> adapter.islocked()
-      True
-
-      >>> activelock = adapter.getActivelock()
-      >>> activelock.lockscope
-      [u'exclusive']
-      >>> activelock.locktype
-      [u'write']
-      >>> activelock.depth
-      '0'
-      >>> activelock.timeout
-      u'Second-3600'
-      >>> activelock.lockroot
-      '/dummy'
-      >>> activelock.owner
-      u'Michael'
-
-      >>> adapter.refreshlock(datetime.timedelta(seconds = 7200))
-      >>> adapter.getActivelock().timeout
-      u'Second-7200'
-
-      >>> adapter.unlock()
-      >>> util.get(file) is None
-      True
-      >>> adapter.islocked()
-      False
-      >>> adapter.getActivelock() is None
-      True
-
-    Shared locking support.
-
-      >>> roottoken = adapter.lock(u'shared', u'write', u'Michael',
-      ...    datetime.timedelta(seconds = 3600), '0')
-      >>> util.get(file) == roottoken
-      True
-      >>> zope.locking.interfaces.ISharedLock.providedBy(roottoken)
-      True
-
-      >>> activelock = adapter.getActivelock()
-      >>> activelock.lockscope
-      [u'shared']
-      >>> activelock.locktoken #doctest:+ELLIPSIS
-      ['opaquelocktoken:...
-
-      >>> adapter.unlock()
-
-    Recursive lock suport.
-
-      >>> demofolder = DemoFolder()
-      >>> demofolder['demo'] = file
-
-      >>> adapter = DAVLockmanager(demofolder)
-      >>> roottoken = adapter.lock(u'exclusive', u'write', u'MichaelK',
-      ...    datetime.timedelta(seconds = 3600), 'infinity')
-
-      >>> demotoken = util.get(file)
-      >>> interfaces.IIndirectToken.providedBy(demotoken)
-      True
-
-      >>> activelock = adapter.getActivelock()
-      >>> activelock.lockroot
-      '/dummy/'
-      >>> DAVLockmanager(file).getActivelock().lockroot
-      '/dummy/'
-      >>> absoluteURL(file, None)
-      '/dummy/dummy'
-      >>> activelock.lockscope
-      [u'exclusive']
-
-    Already locked support.
-
-      >>> adapter.lock(u'exclusive', u'write', u'Michael',
-      ...    datetime.timedelta(seconds = 100), 'infinity') #doctest:+ELLIPSIS
-      Traceback (most recent call last):
-      ...
-      AlreadyLocked...
-      >>> adapter.islocked()
-      True
-
-      >>> adapter.unlock()
-
-    Some error conditions.
-
-      >>> adapter.lock(u'notexclusive', u'write', u'Michael',
-      ...    datetime.timedelta(seconds = 100), 'infinity') # doctest:+ELLIPSIS
-      Traceback (most recent call last):
-      ...
-      UnprocessableError: ...
-
-    Cleanup
-
-      >>> component.getGlobalSiteManager().unregisterUtility(
-      ...    util, zope.locking.interfaces.ITokenUtility)
-      True
-      >>> component.getGlobalSiteManager().unregisterAdapter(
-      ...    TokenBroker, (interface.Interface,),
-      ...    zope.locking.interfaces.ITokenBroker)
-      True
-      >>> utils.now = oldNow
-
-    """
-    interface.implements(z3c.dav.interfaces.IDAVLockmanager)
-    component.adapts(interface.Interface)
-
-    def __init__(self, context):
-        self.context = self.__parent__ = context
-
-    def generateLocktoken(self):
-        return "opaquelocktoken:%s-%s-00105A989226:%.03f" % \
-               (_randGen.random(), _randGen.random(), time.time())
-
-    def islockable(self):
-        utility = component.queryUtility(zope.locking.interfaces.ITokenUtility,
-                                         context = self.context, default = None)
-        return utility is not None
-
-    def lock(self, scope, type, owner, duration, depth,
-             roottoken = None, context = None):
-        if context is None:
-            context = self.context
-
-        tokenBroker = zope.locking.interfaces.ITokenBroker(context)
-        if tokenBroker.get():
-            raise z3c.dav.interfaces.AlreadyLocked(
-                context, message = u"Context or subitem is already locked.")
-
-        if roottoken is None:
-            if scope == u"exclusive":
-                roottoken = tokenBroker.lock(duration = duration)
-            elif scope == u"shared":
-                roottoken = tokenBroker.lockShared(duration = duration)
-            else:
-                raise z3c.dav.interfaces.UnprocessableError(
-                    self.context,
-                    message = u"Invalid lockscope supplied to the lock manager")
-
-            annots = roottoken.annotations.get(WEBDAV_LOCK_KEY, None)
-            if annots is None:
-                annots = roottoken.annotations[WEBDAV_LOCK_KEY] = OOBTree()
-            annots["owner"] = owner
-            annots["token"] = self.generateLocktoken()
-            annots["depth"] = depth
-        else:
-            indirecttoken = indirecttokens.IndirectToken(context, roottoken)
-            ## XXX - using removeSecurityProxy - is this right, has
-            ## it seems wrong
-            removeSecurityProxy(roottoken).utility.register(indirecttoken)
-
-        if depth == "infinity" and IReadContainer.providedBy(context):
-            for subob in context.values():
-                self.lock(scope, type, owner, duration, depth,
-                          roottoken, subob)
-
-        return roottoken
-
-    def getActivelock(self, request = None):
-        if self.islocked():
-            token = zope.locking.interfaces.ITokenBroker(self.context).get()
-            return DAVActiveLockAdapter(token, self.context, request)
-        return None
-
-    def refreshlock(self, timeout):
-        token = zope.locking.interfaces.ITokenBroker(self.context).get()
-        token.duration = timeout
-
-    def unlock(self):
-        tokenBroker = zope.locking.interfaces.ITokenBroker(self.context)
-        token = tokenBroker.get()
-        token.end()
-
-    def islocked(self):
-        tokenBroker = zope.locking.interfaces.ITokenBroker(self.context)
-        return tokenBroker.get() is not None

Added: z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/manager.py
===================================================================
--- z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/manager.py	                        (rev 0)
+++ z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/manager.py	2007-05-20 16:40:28 UTC (rev 75847)
@@ -0,0 +1,263 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+"""
+Implementation of the z3c.dav.interfaces.IDAVLockmanager needed to adapt
+the zope.locking utiltity to integrate it into z3c.dav.
+"""
+
+import time
+import random
+from BTrees.OOBTree import OOBTree
+import zope.component
+import zope.interface
+from zope.security.proxy import removeSecurityProxy
+from zope.traversing.browser.absoluteurl import absoluteURL
+from zope.app.container.interfaces import IReadContainer
+import z3c.dav.interfaces
+
+import interfaces
+import indirecttokens
+import properties
+
+WEBDAV_LOCK_KEY = "z3c.dav.lockingutils.info"
+
+_randGen = random.Random(time.time())
+
+class DAVLockmanager(object):
+    """
+
+      >>> from zope.interface.verify import verifyObject
+      >>> from zope.locking import utility, utils
+      >>> from zope.locking.adapters import TokenBroker
+
+      >>> file = Demo()
+
+    Before we register a ITokenUtility utility make sure that the DAVLockmanager
+    is not lockable.
+
+      >>> adapter = DAVLockmanager(file)
+      >>> adapter.islockable()
+      False
+
+    Now create and register a ITokenUtility utility.
+
+      >>> util = utility.TokenUtility()
+      >>> zope.component.getGlobalSiteManager().registerUtility(
+      ...    util, zope.locking.interfaces.ITokenUtility)
+      >>> zope.component.getGlobalSiteManager().registerAdapter(
+      ...    TokenBroker, (zope.interface.Interface,),
+      ...    zope.locking.interfaces.ITokenBroker)
+
+      >>> import datetime
+      >>> import pytz
+      >>> def hackNow():
+      ...     return datetime.datetime(2006, 7, 25, 23, 49, 51)
+      >>> oldNow = utils.now
+      >>> utils.now = hackNow
+
+    Test the DAVLockmanager implements the descired interface.
+
+      >>> adapter = DAVLockmanager(file)
+      >>> verifyObject(z3c.dav.interfaces.IDAVLockmanager, adapter)
+      True
+
+    The adapter should also be lockable.
+
+      >>> adapter.islockable()
+      True
+
+    Lock with an exclusive lock token.
+
+      >>> roottoken = adapter.lock(u'exclusive', u'write',
+      ...    u'Michael', datetime.timedelta(seconds = 3600), '0')
+      >>> util.get(file) == roottoken
+      True
+      >>> zope.locking.interfaces.IExclusiveLock.providedBy(roottoken)
+      True
+
+      >>> adapter.islocked()
+      True
+
+      >>> activelock = adapter.getActivelock()
+      >>> activelock.lockscope
+      [u'exclusive']
+      >>> activelock.locktype
+      [u'write']
+      >>> activelock.depth
+      '0'
+      >>> activelock.timeout
+      u'Second-3600'
+      >>> activelock.lockroot
+      '/dummy'
+      >>> activelock.owner
+      u'Michael'
+
+      >>> adapter.refreshlock(datetime.timedelta(seconds = 7200))
+      >>> adapter.getActivelock().timeout
+      u'Second-7200'
+
+      >>> adapter.unlock()
+      >>> util.get(file) is None
+      True
+      >>> adapter.islocked()
+      False
+      >>> adapter.getActivelock() is None
+      True
+
+    Shared locking support.
+
+      >>> roottoken = adapter.lock(u'shared', u'write', u'Michael',
+      ...    datetime.timedelta(seconds = 3600), '0')
+      >>> util.get(file) == roottoken
+      True
+      >>> zope.locking.interfaces.ISharedLock.providedBy(roottoken)
+      True
+
+      >>> activelock = adapter.getActivelock()
+      >>> activelock.lockscope
+      [u'shared']
+      >>> activelock.locktoken #doctest:+ELLIPSIS
+      ['opaquelocktoken:...
+
+      >>> adapter.unlock()
+
+    Recursive lock suport.
+
+      >>> demofolder = DemoFolder()
+      >>> demofolder['demo'] = file
+
+      >>> adapter = DAVLockmanager(demofolder)
+      >>> roottoken = adapter.lock(u'exclusive', u'write', u'MichaelK',
+      ...    datetime.timedelta(seconds = 3600), 'infinity')
+
+      >>> demotoken = util.get(file)
+      >>> interfaces.IIndirectToken.providedBy(demotoken)
+      True
+
+      >>> activelock = adapter.getActivelock()
+      >>> activelock.lockroot
+      '/dummy/'
+      >>> DAVLockmanager(file).getActivelock().lockroot
+      '/dummy/'
+      >>> absoluteURL(file, None)
+      '/dummy/dummy'
+      >>> activelock.lockscope
+      [u'exclusive']
+
+    Already locked support.
+
+      >>> adapter.lock(u'exclusive', u'write', u'Michael',
+      ...    datetime.timedelta(seconds = 100), 'infinity') #doctest:+ELLIPSIS
+      Traceback (most recent call last):
+      ...
+      AlreadyLocked...
+      >>> adapter.islocked()
+      True
+
+      >>> adapter.unlock()
+
+    Some error conditions.
+
+      >>> adapter.lock(u'notexclusive', u'write', u'Michael',
+      ...    datetime.timedelta(seconds = 100), 'infinity') # doctest:+ELLIPSIS
+      Traceback (most recent call last):
+      ...
+      UnprocessableError: ...
+
+    Cleanup
+
+      >>> zope.component.getGlobalSiteManager().unregisterUtility(
+      ...    util, zope.locking.interfaces.ITokenUtility)
+      True
+      >>> zope.component.getGlobalSiteManager().unregisterAdapter(
+      ...    TokenBroker, (zope.interface.Interface,),
+      ...    zope.locking.interfaces.ITokenBroker)
+      True
+      >>> utils.now = oldNow
+
+    """
+    zope.interface.implements(z3c.dav.interfaces.IDAVLockmanager)
+    zope.component.adapts(zope.interface.Interface)
+
+    def __init__(self, context):
+        self.context = self.__parent__ = context
+
+    def generateLocktoken(self):
+        return "opaquelocktoken:%s-%s-00105A989226:%.03f" % \
+               (_randGen.random(), _randGen.random(), time.time())
+
+    def islockable(self):
+        utility = zope.component.queryUtility(
+            zope.locking.interfaces.ITokenUtility,
+            context = self.context, default = None)
+        return utility is not None
+
+    def lock(self, scope, type, owner, duration, depth,
+             roottoken = None, context = None):
+        if context is None:
+            context = self.context
+
+        tokenBroker = zope.locking.interfaces.ITokenBroker(context)
+        if tokenBroker.get():
+            raise z3c.dav.interfaces.AlreadyLocked(
+                context, message = u"Context or subitem is already locked.")
+
+        if roottoken is None:
+            if scope == u"exclusive":
+                roottoken = tokenBroker.lock(duration = duration)
+            elif scope == u"shared":
+                roottoken = tokenBroker.lockShared(duration = duration)
+            else:
+                raise z3c.dav.interfaces.UnprocessableError(
+                    self.context,
+                    message = u"Invalid lockscope supplied to the lock manager")
+
+            annots = roottoken.annotations.get(WEBDAV_LOCK_KEY, None)
+            if annots is None:
+                annots = roottoken.annotations[WEBDAV_LOCK_KEY] = OOBTree()
+            annots["owner"] = owner
+            annots["token"] = self.generateLocktoken()
+            annots["depth"] = depth
+        else:
+            indirecttoken = indirecttokens.IndirectToken(context, roottoken)
+            ## XXX - using removeSecurityProxy - is this right, has
+            ## it seems wrong
+            removeSecurityProxy(roottoken).utility.register(indirecttoken)
+
+        if depth == "infinity" and IReadContainer.providedBy(context):
+            for subob in context.values():
+                self.lock(scope, type, owner, duration, depth,
+                          roottoken, subob)
+
+        return roottoken
+
+    def getActivelock(self, request = None):
+        if self.islocked():
+            token = zope.locking.interfaces.ITokenBroker(self.context).get()
+            return properties.DAVActiveLockAdapter(
+                token, self.context, request)
+        return None
+
+    def refreshlock(self, timeout):
+        token = zope.locking.interfaces.ITokenBroker(self.context).get()
+        token.duration = timeout
+
+    def unlock(self):
+        tokenBroker = zope.locking.interfaces.ITokenBroker(self.context)
+        token = tokenBroker.get()
+        token.end()
+
+    def islocked(self):
+        tokenBroker = zope.locking.interfaces.ITokenBroker(self.context)
+        return tokenBroker.get() is not None

Added: z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/properties.py
===================================================================
--- z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/properties.py	                        (rev 0)
+++ z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/properties.py	2007-05-20 16:40:28 UTC (rev 75847)
@@ -0,0 +1,593 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Support for using zope.locking has a locking mechanism for WebDAV locking.
+
+Note that we can't use zope.locking.utility.TokenUtility has a global utility.
+This is because if a recursive lock request fails half through then the
+utility has already been modified and since it is not persistent
+transaction.abort doesn't unlock the pervious successful locks. Since the
+utility gets into an inconsistent state.
+
+$Id: lockingutils.py 75023 2007-05-02 18:42:54Z mkerrin $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope import component
+from zope import interface
+import zope.locking.interfaces
+from zope.traversing.browser.absoluteurl import absoluteURL
+
+from z3c.dav.coreproperties import ILockEntry, IDAVSupportedlock, \
+     IActiveLock
+import z3c.dav.interfaces
+
+import interfaces
+from manager import WEBDAV_LOCK_KEY
+
+################################################################################
+#
+# zope.locking adapters.
+#
+################################################################################
+
+class ExclusiveLockEntry(object):
+    interface.implements(ILockEntry)
+
+    lockscope = [u"exclusive"]
+    locktype = [u"write"]
+
+
+class SharedLockEntry(object):
+    interface.implements(ILockEntry)
+
+    lockscope = [u"shared"]
+    locktype = [u"write"]
+
+
+ at component.adapter(interface.Interface, z3c.dav.interfaces.IWebDAVRequest)
+ at interface.implementer(IDAVSupportedlock)
+def DAVSupportedlock(context, request):
+    """
+    This adapter retrieves the data for rendering in the `{DAV:}supportedlock`
+    property. The `{DAV:}supportedlock` property provides a listing of lock
+    capabilities supported by the resource.
+
+    When their is no ITokenUtility registered with the system then we can't
+    lock any content object and so this property is undefined.
+
+      >>> DAVSupportedlock(None, None) is None
+      True
+
+      >>> from zope.locking import tokens
+      >>> from zope.locking.utility import TokenUtility
+      >>> util = TokenUtility()
+      >>> component.getGlobalSiteManager().registerUtility(
+      ...    util, zope.locking.interfaces.ITokenUtility)
+
+    zope.locking supported both the exclusive and shared lock tokens.
+
+      >>> slock = DAVSupportedlock(None, None)
+      >>> len(slock.supportedlock)
+      2
+      >>> exclusive, shared = slock.supportedlock
+
+      >>> exclusive.lockscope
+      [u'exclusive']
+      >>> exclusive.locktype
+      [u'write']
+
+      >>> shared.lockscope
+      [u'shared']
+      >>> shared.locktype
+      [u'write']
+
+    Cleanup
+
+      >>> component.getGlobalSiteManager().unregisterUtility(
+      ...    util, zope.locking.interfaces.ITokenUtility)
+      True
+
+    """
+    utility = component.queryUtility(zope.locking.interfaces.ITokenUtility,
+                                     context = context, default = None)
+    if utility is None:
+        return None
+    return DAVSupportedlockAdapter()
+
+
+class DAVSupportedlockAdapter(object):
+    interface.implements(IDAVSupportedlock)
+    component.adapts(interface.Interface,
+                     z3c.dav.interfaces.IWebDAVRequest)
+
+    @property
+    def supportedlock(self):
+        return [ExclusiveLockEntry(), SharedLockEntry()]
+
+
+ at component.adapter(interface.Interface, z3c.dav.interfaces.IWebDAVRequest)
+ at interface.implementer(IActiveLock)
+def DAVActiveLock(context, request):
+    """
+    This adapter is responsible for the data for the `{DAV:}activelock`
+    XML element. This XML element occurs within the `{DAV:}lockdiscovery`
+    property.
+
+      >>> import datetime
+      >>> import pytz
+      >>> from cStringIO import StringIO
+      >>> from BTrees.OOBTree import OOBTree
+      >>> from zope.interface.verify import verifyObject
+      >>> import zope.locking.utils
+      >>> from zope.locking import tokens
+      >>> from zope.locking.utility import TokenUtility
+      >>> from zope.locking.adapters import TokenBroker
+      >>> from z3c.dav.publisher import WebDAVRequest
+      >>> import indirecttokens
+
+      >>> def hackNow():
+      ...     return datetime.datetime(2007, 4, 7, tzinfo = pytz.utc)
+      >>> oldNow = zope.locking.utils.now
+      >>> zope.locking.utils.now = hackNow
+
+    The activelock property only exists whenever the zope.locking package
+    is configured properly.
+
+      >>> resource = DemoFolder()
+      >>> request = WebDAVRequest(StringIO(''), {})
+      >>> DAVActiveLock(resource, request) is None
+      True
+
+    Now register a ITokenUtility utility and lock the resource with it.
+
+      >>> util = TokenUtility()
+      >>> component.getGlobalSiteManager().registerUtility(
+      ...    util, zope.locking.interfaces.ITokenUtility)
+
+      >>> locktoken = tokens.ExclusiveLock(
+      ...    resource, 'michael', datetime.timedelta(hours = 1))
+      >>> locktoken = util.register(locktoken)
+
+    DAVActiveLock is still None since their is no adapter from the demo
+    content object to zope.locking.interfaces.ITokenBroker. This is part
+    of the zope.locking installation that hasn't been completed yet.
+
+      >>> DAVActiveLock(resource, request) is None
+      True
+
+      >>> component.getGlobalSiteManager().registerAdapter(
+      ...    TokenBroker, (interface.Interface,),
+      ...    zope.locking.interfaces.ITokenBroker)
+
+      >>> activelock = DAVActiveLock(resource, request)
+      >>> IActiveLock.providedBy(activelock)
+      True
+      >>> verifyObject(IActiveLock, activelock)
+      True
+
+    Now test the data managed by the current activelock property.
+
+      >>> activelock.lockscope
+      [u'exclusive']
+      >>> activelock.locktype
+      [u'write']
+      >>> activelock.timeout
+      u'Second-3600'
+      >>> activelock.lockroot
+      '/dummy/'
+
+    The depth attribute is required by the WebDAV specification. But this
+    information is stored by the z3c.dav.lockingutils in the lock token's
+    annotation. But if a lock token is taken out by an alternative Zope3
+    application that uses the zope.locking package then this information will
+    must likely not be set up. So this adapter should provide reasonable
+    default values for this information. Later we will set up the lock
+    token's annotation data to store this information. The data for the owner
+    and locktoken XML elements are also stored on within the lock tokens
+    annotation key but these XML elements are not required by the WebDAV
+    specification so this data just defaults to None.
+
+      >>> activelock.depth
+      '0'
+      >>> activelock.owner is None
+      True
+      >>> activelock.locktoken is None
+      True
+
+    Now if we try and render this information all the required fields, as
+    specified by the WebDAV specification get rendered.
+
+      >>> lockdiscovery = DAVLockdiscovery(resource, request)
+      >>> davwidget = z3c.dav.properties.getWidget(
+      ...    z3c.dav.coreproperties.lockdiscovery,
+      ...    lockdiscovery, request)
+      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
+      <lockdiscovery xmlns="DAV:" />
+
+      >>> component.getGlobalSiteManager().registerAdapter(DAVActiveLock)
+
+      >>> lockdiscovery = DAVLockdiscovery(resource, request)
+      >>> davwidget = z3c.dav.properties.getWidget(
+      ...    z3c.dav.coreproperties.lockdiscovery,
+      ...    lockdiscovery, request)
+      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
+      <lockdiscovery xmlns="DAV:">
+        <activelock>
+          <lockscope><exclusive /></lockscope>
+          <locktype><write /></locktype>
+          <depth>0</depth>
+          <timeout>Second-3600</timeout>
+          <lockroot>/dummy/</lockroot>
+        </activelock>
+      </lockdiscovery>
+
+    We use the lock tokens annotation to store the data for the owner, depth
+    and locktoken attributes.
+
+      >>> locktoken.annotations[WEBDAV_LOCK_KEY] = OOBTree()
+      >>> locktoken.annotations[WEBDAV_LOCK_KEY]['depth'] = 'testdepth'
+      >>> locktoken.annotations[WEBDAV_LOCK_KEY]['owner'] = '<owner xmlns="DAV:">Me</owner>'
+      >>> locktoken.annotations[WEBDAV_LOCK_KEY]['token'] = 'simpletoken'
+
+    After updating the lock token's annotations we need to regenerate the
+    activelock adapter so that the tokendata internal attribute is setup
+    correctly.
+
+      >>> activelock = DAVActiveLock(resource, request)
+
+    The owner attribute is not required by the WebDAV specification, but
+    we can see it anyways, and similarly for the locktoken attribute.
+
+      >>> activelock.owner
+      '<owner xmlns="DAV:">Me</owner>'
+
+    Each lock token on a resource as at most one `token` associated with it,
+    but in order to display this information correctly we must return a
+    a list with one item.
+
+      >>> activelock.locktoken
+      ['simpletoken']
+
+      >>> lockdiscovery = DAVLockdiscovery(resource, request)
+      >>> davwidget = z3c.dav.properties.getWidget(
+      ...    z3c.dav.coreproperties.lockdiscovery,
+      ...    lockdiscovery, request)
+      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
+      <lockdiscovery xmlns="DAV:">
+        <activelock>
+          <lockscope><exclusive /></lockscope>
+          <locktype><write /></locktype>
+          <depth>testdepth</depth>
+          <owner>Me</owner>
+          <timeout>Second-3600</timeout>
+          <locktoken><href>simpletoken</href></locktoken>
+          <lockroot>/dummy/</lockroot>
+        </activelock>
+      </lockdiscovery>
+
+    Test the indirect locktoken. These are used when we try and lock a
+    collection with the depth header set to `infinity`. These lock tokens
+    share the same annotation information, expiry information and lock token,
+    as the top level lock token.
+
+      >>> resource['demo'] = Demo()
+      >>> sublocktoken = indirecttokens.IndirectToken(
+      ...    resource['demo'], locktoken)
+      >>> sublocktoken = util.register(sublocktoken)
+
+      >>> activelock = DAVActiveLock(resource['demo'], request)
+      >>> verifyObject(IActiveLock, activelock)
+      True
+
+      >>> activelock.lockscope
+      [u'exclusive']
+      >>> activelock.locktype
+      [u'write']
+      >>> activelock.depth
+      'testdepth'
+      >>> activelock.owner
+      '<owner xmlns="DAV:">Me</owner>'
+      >>> activelock.timeout
+      u'Second-3600'
+      >>> activelock.locktoken
+      ['simpletoken']
+      >>> activelock.lockroot
+      '/dummy/'
+
+    Now rendering the lockdiscovery DAV widget for this new resource we get
+    the following.
+
+      >>> lockdiscovery = DAVLockdiscovery(resource['demo'], request)
+      >>> davwidget = z3c.dav.properties.getWidget(
+      ...    z3c.dav.coreproperties.lockdiscovery,
+      ...    lockdiscovery, request)
+      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
+      <lockdiscovery xmlns="DAV:">
+        <activelock>
+          <lockscope><exclusive /></lockscope>
+          <locktype><write /></locktype>
+          <depth>testdepth</depth>
+          <owner>Me</owner>
+          <timeout>Second-3600</timeout>
+          <locktoken><href>simpletoken</href></locktoken>
+          <lockroot>/dummy/</lockroot>
+        </activelock>
+      </lockdiscovery>
+
+      >>> locktoken.end()
+
+    Now a locktoken from an other application could be taken out on our
+    demofolder that we know very little about. For example, a
+    zope.locking.tokens.EndableFreeze` lock token. It should be displayed as
+    an activelock on the resource but since we don't know if the scope of this
+    token is an `{DAV:}exclusive` or `{DAV:}shared` (the only lock scopes
+    currently supported by WebDAV), we will render this information as an
+    empty XML element.
+
+      >>> locktoken = tokens.EndableFreeze(
+      ...    resource, datetime.timedelta(hours = 1))
+      >>> locktoken = util.register(locktoken)
+
+      >>> activelock = DAVActiveLock(resource, request)
+      >>> IActiveLock.providedBy(activelock)
+      True
+
+      >>> activelock.timeout
+      u'Second-3600'
+      >>> activelock.locktype
+      [u'write']
+
+    Now the locktoken is None so no WebDAV client should be able to a resource
+    or more likely they shouldn't be able to take out a new lock on this
+    resource, since the `IF` conditional header shored fail.
+
+      >>> activelock.locktoken is None
+      True
+
+    So far so good. But the EndableFreeze token doesn't correspond to any
+    lock scope known by this WebDAV implementation so when we try and access
+    we just return a empty list. This ensures the `{DAV:}lockscope` element
+    gets rendered by its IDAVWidget but it doesn't contain any information.
+
+      >>> activelock.lockscope
+      []
+      >>> activelock.lockscope != z3c.dav.coreproperties.IActiveLock['lockscope'].missing_value
+      True
+
+    Rending this lock token we get the following.
+
+      >>> lockdiscovery = DAVLockdiscovery(resource, request)
+      >>> davwidget = z3c.dav.properties.getWidget(
+      ...    z3c.dav.coreproperties.lockdiscovery,
+      ...    lockdiscovery, request)
+      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
+      <lockdiscovery xmlns="DAV:">
+        <activelock>
+          <lockscope></lockscope>
+          <locktype><write /></locktype>
+          <depth>0</depth>
+          <timeout>Second-3600</timeout>
+          <lockroot>/dummy/</lockroot>
+        </activelock>
+      </lockdiscovery>
+
+    Unlock the resource.
+
+      >>> locktoken.end()
+
+    Now not all lock tokens have a duration associated with them. In this
+    case the timeout is None, as it is not fully required by the WebDAV
+    specification and all the other attributes will have the default values
+    as tested previously.
+
+      >>> locktoken = tokens.ExclusiveLock(resource, 'michael')
+      >>> locktoken = util.register(locktoken)
+
+      >>> activelock = DAVActiveLock(resource, request)
+      >>> verifyObject(IActiveLock, activelock)
+      True
+      >>> activelock.timeout is None
+      True
+
+      >>> lockdiscovery = DAVLockdiscovery(resource, request)
+      >>> davwidget = z3c.dav.properties.getWidget(
+      ...    z3c.dav.coreproperties.lockdiscovery,
+      ...    lockdiscovery, request)
+      >>> print etree.tostring(davwidget.render()) #doctest:+XMLDATA
+      <lockdiscovery xmlns="DAV:">
+        <activelock>
+          <lockscope><exclusive /></lockscope>
+          <locktype><write /></locktype>
+          <depth>0</depth>
+          <lockroot>/dummy/</lockroot>
+        </activelock>
+      </lockdiscovery>
+
+    Cleanup
+
+      >>> zope.locking.utils.now = oldNow # undo time hack
+
+      >>> component.getGlobalSiteManager().unregisterUtility(
+      ...    util, zope.locking.interfaces.ITokenUtility)
+      True
+      >>> component.getGlobalSiteManager().unregisterAdapter(
+      ...    TokenBroker, (interface.Interface,),
+      ...    zope.locking.interfaces.ITokenBroker)
+      True
+      >>> component.getGlobalSiteManager().unregisterAdapter(DAVActiveLock)
+      True
+
+    """
+    try:
+        token = zope.locking.interfaces.ITokenBroker(context).get()
+    except TypeError:
+        token = None
+    if token is None:
+        return None
+    return DAVActiveLockAdapter(token, context, request)
+
+
+class DAVActiveLockAdapter(object):
+    component.adapts(interface.Interface,
+                     z3c.dav.interfaces.IWebDAVRequest)
+    interface.implements(IActiveLock)
+
+    def __init__(self, token, context, request):
+        self.context = self.__parent__ = context
+        self.token = token
+        self.tokendata = token.annotations.get(WEBDAV_LOCK_KEY, {})
+        self.request = request
+
+    @property
+    def lockscope(self):
+        if interfaces.IIndirectToken.providedBy(self.token):
+            roottoken = self.token.roottoken
+        else:
+            roottoken = self.token
+
+        if zope.locking.interfaces.IExclusiveLock.providedBy(roottoken):
+            return [u"exclusive"]
+        elif zope.locking.interfaces.ISharedLock.providedBy(roottoken):
+            return [u"shared"]
+
+        return []
+
+    @property
+    def locktype(self):
+        return [u"write"]
+
+    @property
+    def depth(self):
+        return self.tokendata.get("depth", "0")
+
+    @property
+    def owner(self):
+        return self.tokendata.get("owner", None)
+
+    @property
+    def timeout(self):
+        remaining = self.token.remaining_duration
+        if remaining is None:
+            return None
+        return u"Second-%d" % remaining.seconds
+
+    @property
+    def locktoken(self):
+        token = self.tokendata.get("token", None)
+        if token is None:
+            return None
+        return [token]
+
+    @property
+    def lockroot(self):
+        if interfaces.IIndirectToken.providedBy(self.token):
+            root = self.token.roottoken.context
+        else:
+            root = self.token.context
+
+        return absoluteURL(root, self.request)
+
+
+ at component.adapter(interface.Interface, z3c.dav.interfaces.IWebDAVRequest)
+ at interface.implementer(z3c.dav.coreproperties.IDAVLockdiscovery)
+def DAVLockdiscovery(context, request):
+    """
+    This adapter is responsible for getting the data for the
+    `{DAV:}lockdiscovery` property.
+
+      >>> import datetime
+      >>> from BTrees.OOBTree import OOBTree
+      >>> from zope.interface.verify import verifyObject
+      >>> from zope.locking import tokens
+      >>> from zope.locking.utility import TokenUtility
+      >>> from zope.locking.adapters import TokenBroker
+      >>> from z3c.dav.publisher import WebDAVRequest
+      >>> from cStringIO import StringIO
+      >>> resource = Demo()
+      >>> request = WebDAVRequest(StringIO(''), {})
+
+      >>> DAVLockdiscovery(resource, request) is None
+      True
+
+      >>> util = TokenUtility()
+      >>> component.getGlobalSiteManager().registerUtility(
+      ...    util, zope.locking.interfaces.ITokenUtility)
+      >>> component.getGlobalSiteManager().registerAdapter(DAVActiveLock,
+      ...    (interface.Interface, z3c.dav.interfaces.IWebDAVRequest),
+      ...     IActiveLock)
+      >>> component.getGlobalSiteManager().registerAdapter(
+      ...    TokenBroker, (interface.Interface,),
+      ...    zope.locking.interfaces.ITokenBroker)
+
+    The `{DAV:}lockdiscovery` is now defined for the resource but its value
+    is None because the resource isn't locked yet.
+
+      >>> lockdiscovery = DAVLockdiscovery(resource, request)
+      >>> lockdiscovery is not None
+      True
+      >>> lockdiscovery.lockdiscovery is None
+      True
+
+      >>> token = tokens.ExclusiveLock(
+      ...    resource, 'michael', datetime.timedelta(hours = 1))
+      >>> token = util.register(token)
+      >>> tokenannot = token.annotations[WEBDAV_LOCK_KEY] = OOBTree()
+      >>> tokenannot['depth'] = 'testdepth'
+
+      >>> lockdiscoveryview = DAVLockdiscovery(resource, request)
+      >>> lockdiscovery = lockdiscoveryview.lockdiscovery
+      >>> len(lockdiscovery)
+      1
+      >>> IActiveLock.providedBy(lockdiscovery[0])
+      True
+      >>> isinstance(lockdiscovery[0], DAVActiveLockAdapter)
+      True
+
+    Cleanup
+
+      >>> component.getGlobalSiteManager().unregisterUtility(
+      ...    util, zope.locking.interfaces.ITokenUtility)
+      True
+      >>> component.getGlobalSiteManager().unregisterAdapter(DAVActiveLock,
+      ...    (interface.Interface, z3c.dav.interfaces.IWebDAVRequest),
+      ...     IActiveLock)
+      True
+      >>> component.getGlobalSiteManager().unregisterAdapter(
+      ...    TokenBroker, (interface.Interface,),
+      ...    zope.locking.interfaces.ITokenBroker)
+      True
+
+    """
+    utility = component.queryUtility(zope.locking.interfaces.ITokenUtility)
+    if utility is None:
+        return None
+    return DAVLockdiscoveryAdapter(context, request)
+
+
+class DAVLockdiscoveryAdapter(object):
+    interface.implements(z3c.dav.coreproperties.IDAVLockdiscovery)
+    component.adapts(interface.Interface,
+                     z3c.dav.interfaces.IWebDAVRequest)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    @property
+    def lockdiscovery(self):
+        adapter = component.queryMultiAdapter((self.context, self.request),
+                                              IActiveLock, default = None)
+        if adapter is None:
+            return None
+        return [adapter]

Modified: z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/tests.py
===================================================================
--- z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/tests.py	2007-05-20 16:26:58 UTC (rev 75846)
+++ z3c.davapp.zopelocking/trunk/src/z3c/davapp/zopelocking/tests.py	2007-05-20 16:40:28 UTC (rev 75847)
@@ -211,10 +211,14 @@
 
 def test_suite():
     return unittest.TestSuite((
-        doctest.DocTestSuite("z3c.davapp.zopelocking.lockingutils",
+        doctest.DocTestSuite("z3c.davapp.zopelocking.properties",
                              checker = z3c.etree.testing.xmlOutputChecker,
                              setUp = lockingSetUp,
                              tearDown = lockingTearDown),
+        doctest.DocTestSuite("z3c.davapp.zopelocking.manager",
+                             checker = z3c.etree.testing.xmlOutputChecker,
+                             setUp = lockingSetUp,
+                             tearDown = lockingTearDown),
         doctest.DocTestSuite("z3c.davapp.zopelocking.indirecttokens",
                              checker = z3c.etree.testing.xmlOutputChecker,
                              setUp = lockingSetUp,



More information about the Checkins mailing list