Commit 32e8dea3 authored by Hanno Schlichting's avatar Hanno Schlichting

Move IWriteLock from webdav into OFS.

The locking concept is generally useful and used outside of
the webdav context.
parent 35fe223b
...@@ -31,7 +31,6 @@ from App.Common import rfc1123_date ...@@ -31,7 +31,6 @@ from App.Common import rfc1123_date
from App.special_dtml import DTMLFile from App.special_dtml import DTMLFile
from DateTime.DateTime import DateTime from DateTime.DateTime import DateTime
from Persistence import Persistent from Persistence import Persistent
from webdav.interfaces import IWriteLock
from ZPublisher import HTTPRangeSupport from ZPublisher import HTTPRangeSupport
from ZPublisher.HTTPRequest import FileUpload from ZPublisher.HTTPRequest import FileUpload
from zExceptions import Redirect, ResourceLockedError from zExceptions import Redirect, ResourceLockedError
...@@ -40,6 +39,7 @@ from zope.interface import implementedBy ...@@ -40,6 +39,7 @@ from zope.interface import implementedBy
from zope.interface import implements from zope.interface import implements
from OFS.Cache import Cacheable from OFS.Cache import Cacheable
from OFS.interfaces import IWriteLock
from OFS.PropertyManager import PropertyManager from OFS.PropertyManager import PropertyManager
from OFS.role import RoleManager from OFS.role import RoleManager
from OFS.SimpleItem import Item_w__name__ from OFS.SimpleItem import Item_w__name__
......
...@@ -66,14 +66,10 @@ from OFS.XMLExportImport import magic ...@@ -66,14 +66,10 @@ from OFS.XMLExportImport import magic
try: try:
from webdav.Collection import Collection from webdav.Collection import Collection
from webdav.NullResource import NullResource
except ImportError: except ImportError:
NullResource = None
class Collection(object): class Collection(object):
pass pass
# Constants: __replaceable__ flags: # Constants: __replaceable__ flags:
NOT_REPLACEABLE = 0 NOT_REPLACEABLE = 0
REPLACEABLE = 1 REPLACEABLE = 1
...@@ -778,9 +774,13 @@ class ObjectManager(CopyContainer, ...@@ -778,9 +774,13 @@ class ObjectManager(CopyContainer,
request = getattr(self, 'REQUEST', None) request = getattr(self, 'REQUEST', None)
if not isinstance(request, (str, NoneType)): if not isinstance(request, (str, NoneType)):
method = request.get('REQUEST_METHOD', 'GET') method = request.get('REQUEST_METHOD', 'GET')
if (NullResource is not None and if (request.maybe_webdav_client and
request.maybe_webdav_client and
method not in ('GET', 'POST')): method not in ('GET', 'POST')):
try:
from webdav.NullResource import NullResource
except ImportError:
pass
else:
return NullResource(self, key, request).__of__(self) return NullResource(self, key, request).__of__(self)
raise KeyError(key) raise KeyError(key)
......
...@@ -28,16 +28,6 @@ from App.interfaces import INavigation ...@@ -28,16 +28,6 @@ from App.interfaces import INavigation
from App.interfaces import IUndoSupport from App.interfaces import IUndoSupport
from persistent.interfaces import IPersistent from persistent.interfaces import IPersistent
try:
from webdav.interfaces import IDAVCollection
from webdav.interfaces import IDAVResource
except ImportError:
class IDAVCollection(Interface):
pass
class IDAVResource(Interface):
pass
class IOrderedContainer(Interface): class IOrderedContainer(Interface):
...@@ -331,10 +321,80 @@ class IManageable(Interface): ...@@ -331,10 +321,80 @@ class IManageable(Interface):
""" """
class IWriteLock(Interface):
"""Basic protocol needed to support the write lock machinery.
It must be able to answer the questions:
o Is the object locked?
o Is the lock owned by the current user?
o What lock tokens are associated with the current object?
o What is their state (how long until they're supposed to time out?,
what is their depth? what type are they?
And it must be able to do the following:
o Grant a write lock on the object to a specified user.
- *If lock depth is infinite, this must also grant locks on **all**
subobjects, or fail altogether*
o Revoke a lock on the object.
- *If lock depth is infinite, this must also revoke locks on all
subobjects*
**All methods in the WriteLock interface that deal with checking valid
locks MUST check the timeout values on the lockitem (ie, by calling
'lockitem.isValid()'), and DELETE the lock if it is no longer valid**
"""
def wl_lockItems(killinvalids=0):
""" Returns (key, value) pairs of locktoken, lock.
if 'killinvalids' is true, invalid locks (locks whose timeout
has been exceeded) will be deleted"""
def wl_lockValues(killinvalids=0):
""" Returns a sequence of locks. if 'killinvalids' is true,
invalid locks will be deleted"""
def wl_lockTokens(killinvalids=0):
""" Returns a sequence of lock tokens. if 'killinvalids' is true,
invalid locks will be deleted"""
def wl_hasLock(token, killinvalids=0):
""" Returns true if the lock identified by the token is attached
to the object. """
def wl_isLocked():
""" Returns true if 'self' is locked at all. If invalid locks
still exist, they should be deleted."""
def wl_setLock(locktoken, lock):
""" Store the LockItem, 'lock'. The locktoken will be used to fetch
and delete the lock. If the lock exists, this MUST
overwrite it if all of the values except for the 'timeout' on the
old and new lock are the same. """
def wl_getLock(locktoken):
""" Returns the locktoken identified by the locktokenuri """
def wl_delLock(locktoken):
""" Deletes the locktoken identified by the locktokenuri """
def wl_clearLocks():
""" Deletes ALL locks on the object - should only be called
by lock management machinery. """
# XXX: might contain non-API methods and outdated comments; # XXX: might contain non-API methods and outdated comments;
# not synced with ZopeBook API Reference; # not synced with ZopeBook API Reference;
# based on OFS.SimpleItem.Item # based on OFS.SimpleItem.Item
class IItem(IZopeObject, IManageable, IFTPAccess, IDAVResource, class IItem(IZopeObject, IManageable, IFTPAccess,
ICopySource, ITraversable, IOwned, IUndoSupport): ICopySource, ITraversable, IOwned, IUndoSupport):
__name__ = BytesLine( __name__ = BytesLine(
...@@ -477,7 +537,7 @@ class ICopyContainer(Interface): ...@@ -477,7 +537,7 @@ class ICopyContainer(Interface):
# not synced with ZopeBook API Reference; # not synced with ZopeBook API Reference;
# based on OFS.ObjectManager.ObjectManager # based on OFS.ObjectManager.ObjectManager
class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable, class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable,
IAcquirer, IPersistent, IDAVCollection, ITraversable, IAcquirer, IPersistent, ITraversable,
IPossibleSite, IContainer): IPossibleSite, IContainer):
"""Generic object manager """Generic object manager
...@@ -819,7 +879,7 @@ class IPropertyManager(Interface): ...@@ -819,7 +879,7 @@ class IPropertyManager(Interface):
# XXX: based on OFS.Folder.Folder # XXX: based on OFS.Folder.Folder
class IFolder(IObjectManager, IPropertyManager, IRoleManager, class IFolder(IObjectManager, IPropertyManager, IRoleManager,
IDAVCollection, IItem, IFindSupport): IItem, IFindSupport):
"""Folders are basic container objects that provide a standard """Folders are basic container objects that provide a standard
interface for object management. Folder objects also implement a interface for object management. Folder objects also implement a
......
import unittest import unittest
try:
from webdav.NullResource import NullResource
except ImportError:
NullResource = None
class ApplicationTests(unittest.TestCase): class ApplicationTests(unittest.TestCase):
...@@ -95,8 +90,15 @@ class ApplicationTests(unittest.TestCase): ...@@ -95,8 +90,15 @@ class ApplicationTests(unittest.TestCase):
request = {'REQUEST_METHOD': 'GET'} request = {'REQUEST_METHOD': 'GET'}
self.assertRaises(KeyError, app.__bobo_traverse__, request, 'NONESUCH') self.assertRaises(KeyError, app.__bobo_traverse__, request, 'NONESUCH')
if NullResource is not None:
def test___bobo_traverse__attribute_key_miss_R_M_not_GET_POST(self): def test___bobo_traverse__attribute_key_miss_R_M_not_GET_POST(self):
try:
from webdav.NullResource import NullResource
except ImportError:
NullResource = None
if NullResource is None:
return
from Acquisition import aq_inner, aq_parent from Acquisition import aq_inner, aq_parent
app = self._makeOne() app = self._makeOne()
......
...@@ -313,7 +313,7 @@ class FileTests(unittest.TestCase): ...@@ -313,7 +313,7 @@ class FileTests(unittest.TestCase):
def test_interfaces(self): def test_interfaces(self):
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
from OFS.Image import File from OFS.Image import File
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
from ZPublisher.HTTPRangeSupport import HTTPRangeInterface from ZPublisher.HTTPRangeSupport import HTTPRangeInterface
verifyClass(HTTPRangeInterface, File) verifyClass(HTTPRangeInterface, File)
...@@ -361,7 +361,7 @@ class ImageTests(FileTests): ...@@ -361,7 +361,7 @@ class ImageTests(FileTests):
def test_interfaces(self): def test_interfaces(self):
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
from OFS.Image import Image from OFS.Image import Image
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
verifyClass(IWriteLock, Image) verifyClass(IWriteLock, Image)
......
...@@ -6,7 +6,7 @@ class TestFolder(unittest.TestCase): ...@@ -6,7 +6,7 @@ class TestFolder(unittest.TestCase):
def test_interfaces(self): def test_interfaces(self):
from OFS.Folder import Folder from OFS.Folder import Folder
from OFS.interfaces import IFolder from OFS.interfaces import IFolder
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
verifyClass(IFolder, Folder) verifyClass(IFolder, Folder)
......
...@@ -6,8 +6,8 @@ class TestOrderedFolder(unittest.TestCase): ...@@ -6,8 +6,8 @@ class TestOrderedFolder(unittest.TestCase):
def test_interfaces(self): def test_interfaces(self):
from OFS.interfaces import IOrderedContainer from OFS.interfaces import IOrderedContainer
from OFS.interfaces import IOrderedFolder from OFS.interfaces import IOrderedFolder
from OFS.interfaces import IWriteLock
from OFS.OrderedFolder import OrderedFolder from OFS.OrderedFolder import OrderedFolder
from webdav.interfaces import IWriteLock
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
verifyClass(IOrderedContainer, OrderedFolder) verifyClass(IOrderedContainer, OrderedFolder)
......
...@@ -12,7 +12,7 @@ class DTMLDocumentTests(unittest.TestCase): ...@@ -12,7 +12,7 @@ class DTMLDocumentTests(unittest.TestCase):
def test_class_conforms_to_IWriteLock(self): def test_class_conforms_to_IWriteLock(self):
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
verifyClass(IWriteLock, self._getTargetClass()) verifyClass(IWriteLock, self._getTargetClass())
......
...@@ -12,7 +12,7 @@ class DTMLMethodTests(unittest.TestCase): ...@@ -12,7 +12,7 @@ class DTMLMethodTests(unittest.TestCase):
def test_class_conforms_to_IWriteLock(self): def test_class_conforms_to_IWriteLock(self):
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
verifyClass(IWriteLock, self._getTargetClass()) verifyClass(IWriteLock, self._getTargetClass())
def test_edit_taintedstring(self): def test_edit_taintedstring(self):
......
...@@ -241,7 +241,7 @@ class ZopePageTemplateFileTests(ZopeTestCase): ...@@ -241,7 +241,7 @@ class ZopePageTemplateFileTests(ZopeTestCase):
def test_class_conforms_to_IWriteLock(self): def test_class_conforms_to_IWriteLock(self):
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
verifyClass(IWriteLock, ZopePageTemplate) verifyClass(IWriteLock, ZopePageTemplate)
def testPT_RenderWithAscii(self): def testPT_RenderWithAscii(self):
......
...@@ -28,6 +28,7 @@ from Acquisition import Implicit ...@@ -28,6 +28,7 @@ from Acquisition import Implicit
from App.special_dtml import DTMLFile from App.special_dtml import DTMLFile
from Persistence import Persistent from Persistence import Persistent
from OFS.CopySupport import CopyError from OFS.CopySupport import CopyError
from OFS.interfaces import IWriteLock
from OFS.SimpleItem import Item_w__name__ from OFS.SimpleItem import Item_w__name__
from zExceptions import BadRequest from zExceptions import BadRequest
from zExceptions import Forbidden from zExceptions import Forbidden
...@@ -44,7 +45,6 @@ from webdav.common import tokenFinder ...@@ -44,7 +45,6 @@ from webdav.common import tokenFinder
from webdav.common import UnsupportedMediaType from webdav.common import UnsupportedMediaType
from webdav.davcmds import Lock from webdav.davcmds import Lock
from webdav.davcmds import Unlock from webdav.davcmds import Unlock
from webdav.interfaces import IWriteLock
from webdav.Resource import Resource from webdav.Resource import Resource
from zope.contenttype import guess_content_type from zope.contenttype import guess_content_type
......
...@@ -18,11 +18,11 @@ from AccessControl.class_init import InitializeClass ...@@ -18,11 +18,11 @@ from AccessControl.class_init import InitializeClass
from AccessControl.SecurityManagement import getSecurityManager from AccessControl.SecurityManagement import getSecurityManager
from App.Common import iso8601_date from App.Common import iso8601_date
from App.Common import rfc1123_date from App.Common import rfc1123_date
from OFS.interfaces import IWriteLock
from OFS.PropertySheets import Virtual, PropertySheet, View from OFS.PropertySheets import Virtual, PropertySheet, View
from webdav.common import isDavCollection from webdav.common import isDavCollection
from webdav.common import urlbase from webdav.common import urlbase
from webdav.interfaces import IWriteLock
if sys.version_info >= (3, 0): if sys.version_info >= (3, 0):
basestring = str basestring = str
......
...@@ -34,6 +34,7 @@ from Acquisition import aq_parent ...@@ -34,6 +34,7 @@ from Acquisition import aq_parent
from ExtensionClass import Base from ExtensionClass import Base
from OFS.event import ObjectClonedEvent from OFS.event import ObjectClonedEvent
from OFS.event import ObjectWillBeMovedEvent from OFS.event import ObjectWillBeMovedEvent
from OFS.interfaces import IWriteLock
from OFS.subscribers import compatibilityCall from OFS.subscribers import compatibilityCall
from zExceptions import BadRequest from zExceptions import BadRequest
from zExceptions import Forbidden from zExceptions import Forbidden
...@@ -63,7 +64,6 @@ from webdav.common import tokenFinder ...@@ -63,7 +64,6 @@ from webdav.common import tokenFinder
from webdav.common import urlbase from webdav.common import urlbase
from webdav.common import urlfix from webdav.common import urlfix
from webdav.interfaces import IDAVResource from webdav.interfaces import IDAVResource
from webdav.interfaces import IWriteLock
ms_dav_agent = re.compile("Microsoft.*Internet Publishing.*") ms_dav_agent = re.compile("Microsoft.*Internet Publishing.*")
......
...@@ -22,7 +22,7 @@ from AccessControl.Permissions import delete_objects ...@@ -22,7 +22,7 @@ from AccessControl.Permissions import delete_objects
from AccessControl.SecurityManagement import getSecurityManager from AccessControl.SecurityManagement import getSecurityManager
from Acquisition import aq_base from Acquisition import aq_base
from Acquisition import aq_parent from Acquisition import aq_parent
from OFS.PropertySheets import DAVProperties from OFS.interfaces import IWriteLock
from zExceptions import BadRequest from zExceptions import BadRequest
from zExceptions import Forbidden from zExceptions import Forbidden
...@@ -33,8 +33,8 @@ from webdav.common import PreconditionFailed ...@@ -33,8 +33,8 @@ from webdav.common import PreconditionFailed
from webdav.common import urlbase from webdav.common import urlbase
from webdav.common import urlfix from webdav.common import urlfix
from webdav.common import urljoin from webdav.common import urljoin
from webdav.interfaces import IWriteLock
from webdav.LockItem import LockItem from webdav.LockItem import LockItem
from webdav.PropertySheets import DAVProperties
from webdav.xmltools import XmlParser from webdav.xmltools import XmlParser
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
from zope.interface import Interface from zope.interface import Interface
from zope.schema import Bool, Tuple from zope.schema import Bool, Tuple
from OFS.interfaces import IWriteLock
class ILockItem(Interface): class ILockItem(Interface):
...@@ -144,77 +146,6 @@ class ILockItem(Interface): ...@@ -144,77 +146,6 @@ class ILockItem(Interface):
used when returning the value of a newly created lock. """ used when returning the value of a newly created lock. """
class IWriteLock(Interface):
"""Basic protocol needed to support the write lock machinery.
It must be able to answer the questions:
o Is the object locked?
o Is the lock owned by the current user?
o What lock tokens are associated with the current object?
o What is their state (how long until they're supposed to time out?,
what is their depth? what type are they?
And it must be able to do the following:
o Grant a write lock on the object to a specified user.
- *If lock depth is infinite, this must also grant locks on **all**
subobjects, or fail altogether*
o Revoke a lock on the object.
- *If lock depth is infinite, this must also revoke locks on all
subobjects*
**All methods in the WriteLock interface that deal with checking valid
locks MUST check the timeout values on the lockitem (ie, by calling
'lockitem.isValid()'), and DELETE the lock if it is no longer valid**
"""
def wl_lockItems(killinvalids=0):
""" Returns (key, value) pairs of locktoken, lock.
if 'killinvalids' is true, invalid locks (locks whose timeout
has been exceeded) will be deleted"""
def wl_lockValues(killinvalids=0):
""" Returns a sequence of locks. if 'killinvalids' is true,
invalid locks will be deleted"""
def wl_lockTokens(killinvalids=0):
""" Returns a sequence of lock tokens. if 'killinvalids' is true,
invalid locks will be deleted"""
def wl_hasLock(token, killinvalids=0):
""" Returns true if the lock identified by the token is attached
to the object. """
def wl_isLocked():
""" Returns true if 'self' is locked at all. If invalid locks
still exist, they should be deleted."""
def wl_setLock(locktoken, lock):
""" Store the LockItem, 'lock'. The locktoken will be used to fetch
and delete the lock. If the lock exists, this MUST
overwrite it if all of the values except for the 'timeout' on the
old and new lock are the same. """
def wl_getLock(locktoken):
""" Returns the locktoken identified by the locktokenuri """
def wl_delLock(locktoken):
""" Deletes the locktoken identified by the locktokenuri """
def wl_clearLocks():
""" Deletes ALL DAV locks on the object - should only be called
by lock management machinery. """
# XXX: might contain non-API methods and outdated comments; # XXX: might contain non-API methods and outdated comments;
# not synced with ZopeBook API Reference; # not synced with ZopeBook API Reference;
# based on webdav.Resource.Resource # based on webdav.Resource.Resource
......
...@@ -17,7 +17,7 @@ class TestUtilFunctions(unittest.TestCase): ...@@ -17,7 +17,7 @@ class TestUtilFunctions(unittest.TestCase):
lockable = LockableResource(locked=False) lockable = LockableResource(locked=False)
self.assertTrue(wl_isLockable(lockable)) self.assertTrue(wl_isLockable(lockable))
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
from zope.interface import implements from zope.interface import implements
class LockableResource: class LockableResource:
......
...@@ -4,7 +4,7 @@ import unittest ...@@ -4,7 +4,7 @@ import unittest
class TestLockNullResource(unittest.TestCase): class TestLockNullResource(unittest.TestCase):
def test_interfaces(self): def test_interfaces(self):
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
from webdav.NullResource import LockNullResource from webdav.NullResource import LockNullResource
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
...@@ -21,7 +21,7 @@ class TestNullResource(unittest.TestCase): ...@@ -21,7 +21,7 @@ class TestNullResource(unittest.TestCase):
return self._getTargetClass()(parent, name, **kw) return self._getTargetClass()(parent, name, **kw)
def test_interfaces(self): def test_interfaces(self):
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
verifyClass(IWriteLock, self._getTargetClass()) verifyClass(IWriteLock, self._getTargetClass())
......
...@@ -98,8 +98,8 @@ class TestResource(unittest.TestCase): ...@@ -98,8 +98,8 @@ class TestResource(unittest.TestCase):
setSecurityPolicy(self._oldPolicy) setSecurityPolicy(self._oldPolicy)
def test_interfaces(self): def test_interfaces(self):
from OFS.interfaces import IWriteLock
from webdav.interfaces import IDAVResource from webdav.interfaces import IDAVResource
from webdav.interfaces import IWriteLock
from zope.interface.verify import verifyClass from zope.interface.verify import verifyClass
verifyClass(IDAVResource, self._getTargetClass()) verifyClass(IDAVResource, self._getTargetClass())
...@@ -149,8 +149,8 @@ class TestResource(unittest.TestCase): ...@@ -149,8 +149,8 @@ class TestResource(unittest.TestCase):
inst.restrictedTraverse = lambda *arg: app inst.restrictedTraverse = lambda *arg: app
inst.getId = lambda *arg: '123' inst.getId = lambda *arg: '123'
inst._dav_writelocks = {'a':DummyLock()} inst._dav_writelocks = {'a':DummyLock()}
from OFS.interfaces import IWriteLock
from zope.interface import directlyProvides from zope.interface import directlyProvides
from webdav.interfaces import IWriteLock
directlyProvides(inst, IWriteLock) directlyProvides(inst, IWriteLock)
from webdav.common import Locked from webdav.common import Locked
self.assertRaises(Locked, inst.MOVE, request, response) self.assertRaises(Locked, inst.MOVE, request, response)
...@@ -172,8 +172,8 @@ class TestResource(unittest.TestCase): ...@@ -172,8 +172,8 @@ class TestResource(unittest.TestCase):
{'If':ifhdr}) {'If':ifhdr})
response = DummyResponse() response = DummyResponse()
inst = self._makeOne() inst = self._makeOne()
from OFS.interfaces import IWriteLock
from zope.interface import directlyProvides from zope.interface import directlyProvides
from webdav.interfaces import IWriteLock
directlyProvides(inst, IWriteLock) directlyProvides(inst, IWriteLock)
from webdav.common import PreconditionFailed from webdav.common import PreconditionFailed
self.assertRaises(PreconditionFailed, inst.dav__simpleifhandler, self.assertRaises(PreconditionFailed, inst.dav__simpleifhandler,
...@@ -198,8 +198,8 @@ class TestResource(unittest.TestCase): ...@@ -198,8 +198,8 @@ class TestResource(unittest.TestCase):
response = DummyResponse() response = DummyResponse()
inst = self._makeOne() inst = self._makeOne()
inst._dav_writelocks = {'a':DummyLock()} inst._dav_writelocks = {'a':DummyLock()}
from OFS.interfaces import IWriteLock
from zope.interface import directlyProvides from zope.interface import directlyProvides
from webdav.interfaces import IWriteLock
directlyProvides(inst, IWriteLock) directlyProvides(inst, IWriteLock)
from webdav.common import Locked from webdav.common import Locked
self.assertRaises(Locked, inst.dav__simpleifhandler, request, response) self.assertRaises(Locked, inst.dav__simpleifhandler, request, response)
......
...@@ -15,7 +15,7 @@ class _DummySecurityPolicy(object): ...@@ -15,7 +15,7 @@ class _DummySecurityPolicy(object):
class _DummyContent(object): class _DummyContent(object):
from webdav.interfaces import IWriteLock from OFS.interfaces import IWriteLock
implements(IWriteLock) implements(IWriteLock)
def __init__(self, token=None): def __init__(self, token=None):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment