[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