Commit 3beb18c2 authored by Jeffrey Shell's avatar Jeffrey Shell

Merging in WriteLocking. Yeehaw!

parent 9563f2bc
......@@ -84,7 +84,7 @@
##############################################################################
"""Access control package"""
__version__='$Revision: 1.138 $'[11:-2]
__version__='$Revision: 1.139 $'[11:-2]
import Globals, socket, ts_regex, SpecialUsers
import os
......@@ -536,7 +536,7 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
return None
def authorize(self, user, accessed, container, name, value, roles):
newSecurityManager(None, user)
newSecurityManager(None, user.__of__(self))
security = getSecurityManager()
try:
try:
......
......@@ -83,7 +83,7 @@
#
##############################################################################
__doc__="""System management components"""
__version__='$Revision: 1.64 $'[11:-2]
__version__='$Revision: 1.65 $'[11:-2]
import sys,os,time,string,Globals, Acquisition, os, Undo
......@@ -91,6 +91,7 @@ from Globals import DTMLFile
from OFS.ObjectManager import ObjectManager
from OFS.Folder import Folder
from CacheManager import CacheManager
from DavLockManager import DavLockManager
from DateTime.DateTime import DateTime
from OFS import SimpleItem
from App.Dialogs import MessageDialog
......@@ -258,6 +259,7 @@ class ApplicationManager(Folder,CacheManager):
Database= DatabaseManager()
Versions= VersionManager()
DebugInfo=DebugManager()
DavLocks = DavLockManager()
manage=manage_main=DTMLFile('dtml/cpContents', globals())
manage_undoForm=DTMLFile('dtml/undo', globals())
......@@ -276,6 +278,8 @@ class ApplicationManager(Folder,CacheManager):
'meta_type': Database.meta_type},
{'id': 'Versions',
'meta_type': Versions.meta_type},
{'id': 'DavLocks',
'meta_type': DavLocks.meta_type},
{'id': 'Products',
'meta_type': 'Product Management'},
{'id': 'DebugInfo',
......
import OFS, Acquisition, Globals
from AccessControl import getSecurityManager, ClassSecurityInfo
from webdav.Lockable import wl_isLocked
class DavLockManager(OFS.SimpleItem.Item, OFS.FindSupport.FindSupport,
Acquisition.Implicit):
id = 'DavLockManager'
name = title = 'WebDAV Lock Manager'
meta_type = 'WebDAV Lock Manager'
icon = 'p_/davlocked'
security = ClassSecurityInfo()
security.declareProtected('Manage WebDAV Locks',
'findLockedObjects', 'manage_davlocks',
'manage_unlockObjects')
security.declarePrivate('unlockObjects')
manage_davlocks=manage_main=manage=Globals.DTMLFile(
'dtml/davLockManager', globals())
manage_options = (
{'label': 'Write Locks', 'action': 'manage_main',
'help': ('OFSP', 'DavLocks-ManageLocks.stx'), },
)
def locked_in_version(self): return 0
def findLockedObjects(self, frompath=''):
app = self.getPhysicalRoot()
if frompath:
if frompath[0] == '/': frompath = frompath[1:]
# since the above will turn '/' into an empty string, check
# for truth before chopping a final slash
if frompath and frompath[-1] == '/': frompath= frompath[:-1]
# Now we traverse to the node specified in the 'frompath' if
# the user chose to filter the search, and run a ZopeFind with
# the expression 'wl_isLocked()' to find locked objects.
obj = app.unrestrictedTraverse(frompath)
lockedobjs = self._findapply(obj, path=frompath)
return lockedobjs
def unlockObjects(self, paths=[]):
app = self.getPhysicalRoot()
for path in paths:
ob = app.unrestrictedTraverse(path)
ob.wl_clearLocks()
def manage_unlockObjects(self, paths=[], REQUEST=None):
" Management screen action to unlock objects. "
if paths: self.unlockObjects(paths)
if REQUEST is not None:
m = '%s objects unlocked.' % len(paths)
return self.manage_davlocks(self, REQUEST, manage_tabs_message=m)
def _findapply(self, obj, result=None, path=''):
# recursive function to actually dig through and find the locked
# objects.
if result is None:
result = []
base = Acquisition.aq_base(obj)
if not hasattr(base, 'objectItems'):
return result
try: items = obj.objectItems()
except: return result
addresult = result.append
for id, ob in items:
if path: p = '%s/%s' % (path, id)
else: p = id
dflag = hasattr(ob, '_p_changed') and (ob._p_changed == None)
bs = Acquisition.aq_base(ob)
if wl_isLocked(bs):
li = []
addlockinfo = li.append
for token, lock in bs.wl_lockItems():
addlockinfo({'owner':lock.getCreatorPath(),
'token':token})
addresult(p, li)
dflag = 0
if hasattr(bs, 'objectItems'):
self._findapply(ob, result, p)
if dflag: ob._p_deactivate()
return result
Globals.default__class_init__(DavLockManager)
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<script type="text/javascript">
<!--
isSelected = false;
function toggleSelect() {
if (isSelected == false) {
for (i = 0; i < document.objectItems.length; i++)
document.objectItems.elements[i].checked = true ;
isSelected = true;
document.objectItems.selectButton.value = "Deselect All";
return isSelected;
}
else {
for (i = 0; i < document.objectItems.length; i++)
document.objectItems.elements[i].checked = false ;
isSelected = false;
document.objectItems.selectButton.value = "Select All";
return isSelected;
}
}
//-->
</script>
<dtml-let lockedobjs="findLockedObjects(frompath=REQUEST.get('frompath',''))">
<dtml-if lockedobjs>
<p class="std-text">All locked objects
<dtml-if frompath>from path <em>&dtml-frompath;</em></dtml-if>
are listed below.</p>
<dtml-else>
<p class="std-text">No locked objects
<dtml-if frompath>from path <em>&dtml-frompath;</em></dtml-if>
were found.</p>
</dtml-if>
<form action="&dtml-URL0;" name="finderform">
<p class="std-text">Search for locked objects starting from path:
<input type="text" size="14" name="frompath" value="&dtml.missing-frompath;"
class="form-element" />
<input type="submit" value="Go" class="form-element" />
</p>
</form>
<form action="manage_unlockObjects" name="objectItems" method="post">
<dtml-if lockedobjs>
<table width="100%" cellspacing="0" cellpadding="2" border="0">
<tr class="list-header">
<td width="60%" class="list-item">Locked Item</td>
<td width="40%" class="list-item">Lock Info</td>
</tr>
</table>
<table width="100%" cellspacing="0" cellpadding="2" border="0">
<dtml-in lockedobjs>
<dtml-if sequence-odd>
<tr class="row-normal">
<dtml-else>
<tr class="row-hilite">
</dtml-if sequence-odd>
<td align="left" valign="top" width="16">
<input type="checkbox" name="paths:list" value="&dtml-sequence-key;"
id="cb_&dtml-sequence-index;" />
</td>
<td align="left" valign="top" width="16">
<dtml-if icon>
<img src="&dtml-BASEPATH1;/&dtml-icon;" alt="[&dtml-meta_type;]"
title="[&dtml-meta_type;]" />
</dtml-if icon>
</td>
<td align="left" valign="top" class="list-item">
<label for="cb_&dtml-sequence-index;">&dtml-sequence-key;</label>
</td>
<td align="left" valign="top" width="40%" class="list-item">
<dtml-in sequence-item mapping>
<strong>Owner:</strong> &dtml-owner;,
<strong>Token:</strong> &dtml-token;<br />
</dtml-in>
</td>
</tr>
</dtml-in lockedobjs>
</table>
<input type="hidden" name="frompath" value="&dtml.missing-frompath;" />
<script type="text/javascript">
<!--
document.write('<input class="form-element" type="button" name="selectButton" value="Select All" onClick="toggleSelect(); return false">')
//-->
</script>
&nbsp; <input class="form-element" type="submit" value="Unlock objects" />
</dtml-if>
</form>
</dtml-let>
<dtml-var manage_page_footer>
......@@ -125,6 +125,11 @@
(<em><dtml-var locked_in_version></em>)
</dtml-if>
</dtml-if>
<dtml-if wl_isLocked>
<img src="&dtml-BASEPATH1;/p_/davlocked"
alt="This item has been locked by WebDAV"
title="This item has been locked by WebDAV" />
</dtml-if wl_isLocked>
</div>
</td>
<dtml-if "_.has_key('help_topic') and _.has_key('help_product')">
......
......@@ -84,7 +84,8 @@
##############################################################################
"""DTML Document objects."""
__version__='$Revision: 1.42 $'[11:-2]
__version__='$Revision: 1.43 $'[11:-2]
from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
from ZPublisher.Converters import type_converters
from Globals import HTML, DTMLFile, MessageDialog
......@@ -92,6 +93,8 @@ from OFS.content_types import guess_content_type
from DTMLMethod import DTMLMethod, decapitate
from PropertyManager import PropertyManager
from webdav.common import rfc1123_date
from webdav.Lockable import ResourceLockedError
from webdav.WriteLockInterface import WriteLockInterface
from sgmllib import SGMLParser
from string import find
from urllib import quote
......@@ -106,6 +109,7 @@ class DTMLDocument(PropertyManager, DTMLMethod):
"""DTML Document objects are DocumentTemplate.HTML objects that act
as methods whose 'self' is the DTML Document itself."""
__implements__ = (WriteLockInterface,)
meta_type='DTML Document'
icon ='p_/dtmldoc'
......@@ -139,6 +143,10 @@ class DTMLDocument(PropertyManager, DTMLMethod):
self._validateProxy(REQUEST)
if self._size_changes.has_key(SUBMIT):
return self._er(data,title,SUBMIT,dtpref_cols,dtpref_rows,REQUEST)
if self.wl_isLocked():
raise ResourceLockedError, (
'This document has been locked via WebDAV.')
self.title=str(title)
if type(data) is not type(''): data=data.read()
self.munge(data)
......@@ -150,6 +158,9 @@ class DTMLDocument(PropertyManager, DTMLMethod):
def manage_upload(self,file='', REQUEST=None):
"""Replace the contents of the document with the text in file."""
self._validateProxy(REQUEST)
if self.wl_isLocked():
raise ResourceLockedError, (
'This document has been locked via WebDAV.')
if type(file) is not type(''): file=file.read()
self.munge(file)
self.ZCacheable_invalidate()
......
......@@ -84,7 +84,7 @@
##############################################################################
"""DTML Method objects."""
__version__='$Revision: 1.60 $'[11:-2]
__version__='$Revision: 1.61 $'[11:-2]
import History
from Globals import HTML, DTMLFile, MessageDialog
......@@ -94,6 +94,8 @@ from OFS.content_types import guess_content_type
from PropertyManager import PropertyManager
from AccessControl.Role import RoleManager
from webdav.common import rfc1123_date
from webdav.Lockable import ResourceLockedError
from webdav.WriteLockInterface import WriteLockInterface
from ZDOM import ElementWithTitle
from DateTime.DateTime import DateTime
from urllib import quote
......@@ -115,6 +117,8 @@ class DTMLMethod(HTML, Acquisition.Implicit, RoleManager,
index_html=None # Prevent accidental acquisition
_cache_namespace_keys=()
__implements__ = (WriteLockInterface,)
# Documents masquerade as functions:
class func_code: pass
func_code=func_code()
......@@ -304,6 +308,8 @@ class DTMLMethod(HTML, Acquisition.Implicit, RoleManager,
self._validateProxy(REQUEST)
if self._size_changes.has_key(SUBMIT):
return self._er(data,title,SUBMIT,dtpref_cols,dtpref_rows,REQUEST)
if self.wl_isLocked():
raise ResourceLockedError, 'This DTML Method is locked via WebDAV'
self.title=str(title)
if type(data) is not type(''): data=data.read()
......@@ -316,6 +322,9 @@ class DTMLMethod(HTML, Acquisition.Implicit, RoleManager,
def manage_upload(self,file='', REQUEST=None):
"""Replace the contents of the document with the text in file."""
self._validateProxy(REQUEST)
if self.wl_isLocked():
raise ResourceLockedError, 'This DTML Method is locked via WebDAV'
if type(file) is not type(''): file=file.read()
self.munge(file)
self.ZCacheable_invalidate()
......@@ -369,6 +378,7 @@ class DTMLMethod(HTML, Acquisition.Implicit, RoleManager,
def PUT(self, REQUEST, RESPONSE):
"""Handle HTTP PUT requests."""
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
body=REQUEST.get('BODY', '')
self._validateProxy(REQUEST)
self.munge(body)
......
......@@ -87,12 +87,13 @@
Folders are the basic container objects and are analogous to directories.
$Id: Folder.py,v 1.93 2001/01/11 21:11:04 chrism Exp $"""
$Id: Folder.py,v 1.94 2001/01/31 21:26:52 jeffrey Exp $"""
__version__='$Revision: 1.93 $'[11:-2]
__version__='$Revision: 1.94 $'[11:-2]
import Globals, SimpleItem, ObjectManager, PropertyManager
import AccessControl.Role, webdav.Collection, FindSupport
from webdav.WriteLockInterface import WriteLockInterface
from Globals import DTMLFile
from AccessControl import getSecurityManager
......@@ -150,6 +151,7 @@ class Folder(
interface for object management. Folder objects also implement
a management interface and can have arbitrary properties.
"""
__implements__ = (WriteLockInterface,)
meta_type='Folder'
_properties=({'id':'title', 'type': 'string'},)
......
......@@ -84,7 +84,7 @@
##############################################################################
"""Image object"""
__version__='$Revision: 1.124 $'[11:-2]
__version__='$Revision: 1.125 $'[11:-2]
import Globals, string, struct, content_types
from OFS.content_types import guess_content_type
......@@ -92,6 +92,8 @@ from Globals import DTMLFile, MessageDialog
from PropertyManager import PropertyManager
from AccessControl.Role import RoleManager
from webdav.common import rfc1123_date
from webdav.Lockable import ResourceLockedError
from webdav.WriteLockInterface import WriteLockInterface
from SimpleItem import Item_w__name__
from cStringIO import StringIO
from Globals import Persistent
......@@ -135,6 +137,7 @@ class File(Persistent, Implicit, PropertyManager,
RoleManager, Item_w__name__, Cacheable):
"""A File object is a content object for arbitrary files."""
__implements__ = (WriteLockInterface,)
meta_type='File'
......@@ -265,6 +268,9 @@ class File(Persistent, Implicit, PropertyManager,
"""
Changes the title and content type attributes of the File or Image.
"""
if self.wl_isLocked():
raise ResourceLockedError, "File is locked via WebDAV"
self.title=str(title)
self.content_type=str(content_type)
if precondition: self.precondition=str(precondition)
......@@ -280,6 +286,9 @@ class File(Persistent, Implicit, PropertyManager,
The file or images contents are replaced with the contents of 'file'.
"""
if self.wl_isLocked():
raise ResourceLockedError, "File is locked via WebDAV"
data, size = self._read_data(file)
content_type=self._get_content_type(file, data, self.__name__,
'application/octet-stream')
......@@ -369,6 +378,7 @@ class File(Persistent, Implicit, PropertyManager,
def PUT(self, REQUEST, RESPONSE):
"""Handle HTTP PUT requests"""
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
type=REQUEST.get_header('content-type', None)
file=REQUEST['BODYFILE']
......@@ -504,6 +514,7 @@ class Image(File):
as File objects. Images also have a string representation that
renders an HTML 'IMG' tag.
"""
__implements__ = (WriteLockInterface,)
meta_type='Image'
......
......@@ -84,9 +84,10 @@
##############################################################################
"""Property sheets"""
__version__='$Revision: 1.64 $'[11:-2]
__version__='$Revision: 1.65 $'[11:-2]
import time, string, App.Management, Globals
from webdav.WriteLockInterface import WriteLockInterface
from ZPublisher.Converters import type_converters
from DocumentTemplate.DT_Util import html_quote
from Globals import DTMLFile, MessageDialog
......@@ -531,6 +532,8 @@ class DAVProperties(Virtual, PropertySheet, View):
{'id':'getcontenttype', 'mode':'r'},
{'id':'getcontentlength', 'mode':'r'},
{'id':'source', 'mode':'r'},
{'id':'supportedlock', 'mode':'r'},
{'id':'lockdiscovery', 'mode':'r'},
)
def getProperty(self, id, default=None):
......@@ -598,6 +601,17 @@ class DAVProperties(Virtual, PropertySheet, View):
' <d:locktype><d:write/></d:locktype>\n' \
' </n:lockentry>\n '
def dav__lockdiscovery(self):
vself = self.v_self()
out = '\n'
if WriteLockInterface.isImplementedBy(vself):
locks = vself.wl_lockValues(killinvalids=1)
for lock in locks:
out = '%s\n%s' % (out, lock.asLockDiscoveryProperty('n'))
out = '%s\n' % out
return out
Globals.default__class_init__(DAVProperties)
......
......@@ -36,7 +36,11 @@ the <em>browse</em> button to select a local file to upload.
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit" name="SUBMIT" value="Save Changes">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="SUBMIT" value="Save Changes">
</dtml-if>
&nbsp;&nbsp;
<input class="form-element" type="submit" name="SUBMIT" value="Taller">
<input class="form-element" type="submit" name="SUBMIT" value="Shorter">
......@@ -64,7 +68,11 @@ the <em>browse</em> button to select a local file to upload.
<td></td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" value="Upload File">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" value="Upload File">
</dtml-if>
</div>
</td>
</tr>
......
......@@ -77,8 +77,12 @@ kind>.
<td></td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="manage_edit:method"
value="Save Changes">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="manage_edit:method"
value="Save Changes">
</dtml-if>
</div>
</td>
</tr>
......@@ -100,8 +104,12 @@ kind>.
<td></td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="manage_upload:method"
value="Upload">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="manage_upload:method"
value="Upload">
</dtml-if>
</div>
</td>
</tr>
......
......@@ -66,8 +66,12 @@ kind>.
<td></td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Save Changes">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="submit"
value="Save Changes">
</dtml-if>
</div>
</td>
</tr>
......@@ -95,8 +99,12 @@ kind>.
<td></td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Upload">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="submit"
value="Upload">
</dtml-if>
</div>
</td>
</tr>
......
......@@ -131,6 +131,11 @@ function toggleSelect() {
(<em>&dtml-locked_in_version;</em>)
</dtml-if>
</dtml-if>
<dtml-if wl_isLocked>
<img src="&dtml-BASEPATH1;/p_/davlocked"
alt="This item has been locked by WebDAV"
title="This item has been locked by WebDAV" />
</dtml-if>
</div>
</td>
......
......@@ -101,6 +101,8 @@ class p_:
locked=ImageFile('www/modified.gif', globals())
lockedo=ImageFile('www/locked.gif', globals())
davlocked=ImageFile('webdav/www/davlock.gif')
pl=ImageFile('TreeDisplay/www/Plus_icon.gif')
mi=ImageFile('TreeDisplay/www/Minus_icon.gif')
rtab=ImageFile('App/www/rtab.gif')
......
WebDAV Lock Management - Manage Locks
Description
This view allows you to manage WebDAV WriteLocks. WebDAV, as an
extension to the HTTP Protocol, allows the users to create *Write
Locks* as a way of trying to avoid the "lost updates problem".
However, sometimes WriteLocks may become abandoned. This may be due
to users forgetting to unlock their resources, software failures
such as crashes, etc. In many cases, locks might just time out
before this becomes a problem. In cases where it's not, this
control panel object may be used to locate locked resources inside
of Zope and clear *ALL* of their WebDAV writelocks. **This does not
clear locks caused by use of Versions**.
Controls
'Path' -- This lets you enter a path (based off the root of the Zope
site) to filter down the list of locked objects. Clicking 'Go'
executes the filter.
When locked objects are found, they are listed one per line with a
checkbox that can be used to select the item. Also listed in each
line is information about the lock(s) on the object - the user who
created the lock (identified by the path to the user folder the user
is defined in), and the locktoken that identifies the lock. In the
majority of cases, there should only be one lock per object.
'[Checkbox]' -- Selects locked items.
'Select All' -- This button marks all items displayed as selected.
'Deselect All' -- After 'Select All' has been clicked, it changes to
say 'Deselect All'. Clicking this deselects all displayed items.
'Unlock objects' -- Unlocks the selected items.
\ No newline at end of file
......@@ -89,7 +89,7 @@ This product provides support for Script objects containing restricted
Python code.
"""
__version__='$Revision: 1.20 $'[11:-2]
__version__='$Revision: 1.21 $'[11:-2]
import sys, os, traceback, re
from Globals import DTMLFile, MessageDialog
......@@ -98,6 +98,8 @@ from OFS.SimpleItem import SimpleItem
from DateTime.DateTime import DateTime
from string import join, strip, rstrip, split, replace, lower
from urllib import quote
from webdav.Lockable import ResourceLockedError
from webdav.WriteLockInterface import WriteLockInterface
from Shared.DC.Scripts.Script import Script, BindingsUI, defaultBindings
from AccessControl import getSecurityManager
from OFS.History import Historical, html_diff
......@@ -136,6 +138,7 @@ class PythonScript(Script, Historical, Cacheable):
not attempt to use the "exec" statement or certain restricted builtins.
"""
__implements__ = (WriteLockInterface,)
meta_type='Script (Python)'
_proxy_roles = ()
......@@ -190,6 +193,8 @@ class PythonScript(Script, Historical, Cacheable):
def ZPythonScript_edit(self, params, body):
self._validateProxy()
if self.wl_isLocked():
raise ResourceLockedError, "The script is locked via WebDAV."
if type(body) is not type(''):
body = body.read()
if self._params <> params or self._body <> body:
......@@ -198,6 +203,8 @@ class PythonScript(Script, Historical, Cacheable):
def ZPythonScriptHTML_upload(self, REQUEST, file=''):
"""Replace the body of the script with the text in file."""
if self.wl_isLocked():
raise ResourceLockedError, "The script is locked via WebDAV."
if type(file) is not type(''): file = file.read()
self.write(file)
message = 'Saved changes.'
......@@ -376,6 +383,7 @@ class PythonScript(Script, Historical, Cacheable):
def PUT(self, REQUEST, RESPONSE):
""" Handle HTTP PUT requests """
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
self.write(REQUEST.get('BODY', ''))
RESPONSE.setStatus(204)
return RESPONSE
......
......@@ -70,8 +70,12 @@
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit"
name="ZPythonScriptHTML_editAction:method" value="Save Changes">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit"
name="ZPythonScriptHTML_editAction:method" value="Save Changes">
</dtml-if>
&nbsp;&nbsp;
<input class="form-element" type="submit" name="height" value="Taller">
<input class="form-element" type="submit" name="height" value="Shorter">
......@@ -108,7 +112,11 @@ to <a href="document_src">view or download</a> the current source.
<td></td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" value="Upload File">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" value="Upload File">
</dtml-if>
</div>
</td>
</tr>
......
......@@ -85,11 +85,12 @@
__doc__='''SQL Methods
$Id: SQL.py,v 1.16 2001/01/11 19:52:54 chrism Exp $'''
__version__='$Revision: 1.16 $'[11:-2]
$Id: SQL.py,v 1.17 2001/01/31 21:26:55 jeffrey Exp $'''
__version__='$Revision: 1.17 $'[11:-2]
import Shared.DC.ZRDB.DA
from Globals import DTMLFile
from webdav.WriteLockInterface import WriteLockInterface
def SQLConnectionIDs(self):
"""Find SQL database connections in the current folder and above
......@@ -188,6 +189,7 @@ class SQL(Shared.DC.ZRDB.DA.DA):
employees/employee_id/1234/service_record
"""
__implements__ = (WriteLockInterface,)
meta_type='Z SQL Method'
manage_main=DTMLFile('dtml/edit', globals())
......
......@@ -69,10 +69,14 @@
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit" name="SUBMIT"
value="Save Changes">
<input class="form-element" type="submit" name="SUBMIT"
value="Change and Test">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="SUBMIT"
value="Save Changes">
<input class="form-element" type="submit" name="SUBMIT"
value="Change and Test">
</dtml-if wl_isLocked>
<br />
<input class="form-element" type="submit" name="SUBMIT" value="Taller">
<input class="form-element" type="submit" name="SUBMIT" value="Shorter">
......
......@@ -85,8 +85,7 @@
__doc__='''Generic Database adapter
$Id: DA.py,v 1.97 2001/01/24 16:14:54 brian Exp $'''
__version__='$Revision: 1.97 $'[11:-2]
__version__='$Revision: 1.98 $'[11:-2]
import OFS.SimpleItem, Aqueduct, RDB, re
import DocumentTemplate, marshal, md5, base64, Acquisition, os
......@@ -108,6 +107,7 @@ from Results import Results
from App.Extensions import getBrain
from AccessControl import getSecurityManager
from webdav.Resource import Resource
from webdav.Lockable import ResourceLockedError
try: from IOBTree import Bucket
except: Bucket=lambda:{}
......@@ -232,6 +232,9 @@ class DA(
return self._er(title,connection_id,arguments,template,
SUBMIT,sql_pref__cols,sql_pref__rows,REQUEST)
if self.wl_isLocked():
raise ResourceLockedError, 'SQL Method is locked via WebDAV'
self.title=str(title)
self.connection_id=str(connection_id)
arguments=str(arguments)
......@@ -338,6 +341,7 @@ class DA(
def PUT(self, REQUEST, RESPONSE):
"""Handle put requests"""
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
body = REQUEST.get('BODY', '')
m = re.match('\s*<params>(.*)</params>\s*\n', body, re.I)
if m:
......
......@@ -85,12 +85,14 @@
"""WebDAV support - collection objects."""
__version__='$Revision: 1.17 $'[11:-2]
__version__='$Revision: 1.18 $'[11:-2]
import sys, os, string, Globals
import sys, os, string, Globals, davcmds, Lockable
from common import urlfix, rfc1123_date
from Resource import Resource
from AccessControl import getSecurityManager
from urllib import unquote
from WriteLockInterface import WriteLockInterface
class Collection(Resource):
......@@ -143,11 +145,45 @@ class Collection(Resource):
success, or may return 207 (Multistatus) to indicate partial
success. Note that in Zope a DELETE currently never returns 207."""
self.dav__init(REQUEST, RESPONSE)
url=urlfix(REQUEST['URL'], 'DELETE')
name=unquote(filter(None, string.split(url, '/'))[-1])
# TODO: add lock checking here
self.aq_parent._delObject(name)
RESPONSE.setStatus(204)
ifhdr = REQUEST.get_header('If', '')
url = urlfix(REQUEST['URL'], 'DELETE')
name = unquote(filter(None, string.split(url, '/'))[-1])
parent = self.aq_parent
user = getSecurityManager().getUser()
token = None
# Level 1 of lock checking (is the collection or its parent locked?)
if Lockable.wl_isLocked(self):
if ifhdr:
self.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1)
else:
raise 'Locked'
elif Lockable.wl_isLocked(parent):
if ifhdr:
parent.dav__simpleifhandler(REQUEST, RESPONSE, 'DELETE', col=1)
else:
raise 'Precondition Failed'
# Second level of lock\conflict checking (are any descendants locked,
# or is the user not permitted to delete?). This results in a
# multistatus response
if ifhdr:
tokens = self.wl_lockTokens()
for tok in tokens:
# We already know that the simple if handler succeeded,
# we just want to get the right token out of the header now
if string.find(ifhdr, tok) > -1:
token = tok
cmd = davcmds.DeleteCollection()
result = cmd.apply(self, token, user, REQUEST['URL'])
if result:
# There were conflicts, so we need to report them
RESPONSE.setStatus(207)
RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"')
RESPONSE.setBody(result)
else:
# There were no conflicts, so we can go ahead and delete
self.aq_parent._delObject(name)
RESPONSE.setStatus(204)
return RESPONSE
......
from string import capitalize, split, join, strip
import time, Interface, re
class EtagBaseInterface(Interface.Base):
"""\
Basic Etag support interface, meaning the object supports generating
an Etag that can be used by certain HTTP and WebDAV Requests.
"""
def http__etag(self):
"""\
Entity tags are used for comparing two or more entities from
the same requested resource. Predominantly used for Caching,
Etags can also be used to deal with the 'Lost Updates Problem'.
An HTTP Client such as Amaya that supports PUT for editing can
use the Etag value returned in the head of a GET response in the
'if-match' header submitted with a PUT request. If the Etag
for the requested resource in the PUT request's 'if-match' header
is different from the current Etag value returned by this method,
the PUT will fail (it means that the state of the resource has
changed since the last copy the Client recieved) because the
precondition (the 'if-match') fails (the submitted Etag does not
match the current Etag).
"""
def http__refreshEtag(self):
"""\
While it may make sense to use the ZODB Object Id or
bobobase_modification_time to generate an Etag, this could
fail on certain REQUESTS because:
o The object is not stored in the ZODB, or
o A Request such as PUT changes the oid or bobobase_modification_time
*AFTER* the Response has been written out, but the Etag needs
to be updated and returned with the Response of the PUT request.
Thus, Etags need to be refreshed manually when an object changes.
"""
class EtagSupport:
"""\
This class is the basis for supporting Etags in Zope. It's main
function right now is to support the *Lost Updates Problem* by
allowing Etags and If-Match headers to be checked on PUT calls to
provide a *Seatbelt* style functionality. The Etags is based on
the bobobase_modification_time, and thus is updated whenever the
object is updated. If a PUT request, or other HTTP or Dav request
comes in with an Etag different than the current one, that request
can be rejected according to the type of header (If-Match,
If-None-Match).
"""
__implements__ = (EtagBaseInterface,)
def http__etag(self):
try: etag = self.__etag
except AttributeError:
self.http__refreshEtag()
etag = self.__etag
return etag
def http__refreshEtag(self):
self.__etag = 'ts%s' % str(time.time())[2:]
def http__parseMatchList(self, REQUEST, header="if-match"):
# Return a sequence of strings found in the header specified
# (should be one of {'if-match' or 'if-none-match'}). If the
# header is not in the request, returns None. Otherwise,
# returns a tuple of Etags.
matchlist = REQUEST.get_header(header)
if matchlist is None:
# capitalize the words of the header, splitting on '-'
tmp = map(capitalize, split(header, '-'))
tmp = join(tmp,'-')
matchlist = REQUEST.get_header(tmp)
if matchlist is None:
return None
matchlist = map(strip, split(matchlist, ','))
r = []
for match in matchlist:
if match == '*': r.insert(0, match)
elif (match[0] + match[-1] == '""') and (len(match) > 2):
r.append(match[1:-1])
return tuple(r)
def http__processMatchHeaders(self, REQUEST=None):
# Process if-match and if-none-match headers
if REQUEST is None: REQUEST = self.aq_acquire('REQUEST')
matchlist = self.http__parseMatchList(REQUEST, 'if-match')
nonematch = self.http__parseMatchList(REQUEST, 'if-none-match')
if matchlist is None:
# There's no Matchlist, but 'if-none-match' might need processing
pass
elif ('*' in matchlist):
return 1 # * matches everything
elif self.http__etag() not in matchlist:
# The resource etag is not in the list of etags required
# to match, as specified in the 'if-match' header. The
# condition fails and the HTTP Method may *not* execute.
raise "Precondition Failed"
elif self.http__etag() in matchlist:
return 1
if nonematch is None:
# There's no 'if-none-match' header either, so there's no
# problem continuing with the request
return 1
elif ('*' in nonelist):
# if-none-match: * means that the operation should not
# be performed if the specified resource exists
# (webdav.NullResource will want to do special behavior
# here)
raise "Precondition Failed"
elif self.http__etag() in nonelist:
# The opposite of if-match, the condition fails
# IF the resources Etag is in the if-none-match list
raise "Precondition Failed"
elif self.http__etag() not in nonelist:
return 1
from string import lower, split, join
from Globals import Persistent
from WriteLockInterface import LockItemInterface
from AccessControl import ClassSecurityInfo
from AccessControl.Owned import ownerInfo
from common import generateLockToken
import time
MAXTIMEOUT = (2L**32)-1 # Maximum timeout time
DEFAULTTIMEOUT = 12 * 60L # Default timeout
def validateTimeout(timeout):
# Timeout *should* be in the form "Seconds-XXX" or "Infinite"
errors = []
try:
t = split(str(timeout), '-')[-1]
if lower(t) == 'infinite':
timeout = DEFAULTTIMEOUT # Default to 1800 secods for infinite
else: # requests
timeout = long(t)
except ValueError:
errors.append("Bad timeout value")
if timeout > MAXTIMEOUT:
errors.append("Timeout request is greater than %s" % MAXTIMEOUT)
return timeout, errors
class LockItem(Persistent):
__implements__ = (LockItemInterface,)
# Use the Zope 2.3 declarative security to manage access
security = ClassSecurityInfo()
security.declarePublic('getOwner', 'getLockToken', 'getDepth',
'getTimeout', 'getTimeoutString',
'getModifiedTime', 'isValid', 'getLockScope',
'getLockType')
security.declareProtected('Change Lock Information',
'setTimeout', 'refresh')
security.declareProtected('Access contents information',
'getCreator', 'getCreatorPath')
def __init__(self, creator, owner='', depth=0, timeout='Infinite',
locktype='write', lockscope='exclusive', token=None):
errors = []
# First check the values and raise value errors if outside of contract
if not getattr(creator, 'getUserName', None):
errors.append("Creator not a user object")
if lower(str(depth)) not in ('0', 'infinity'):
errors.append("Depth must be 0 or infinity")
if lower(locktype) != 'write':
errors.append("Lock type '%s' not supported" % locktype)
if lower(lockscope) != 'exclusive':
errors.append("Lock scope '%s' not supported" % lockscope)
timeout, e = validateTimeout(timeout)
errors = errors + e
# Finally, if there were errors, report them ALL to on high
if errors:
raise ValueError, errors
# AccessControl.Owned.ownerInfo returns the id of the creator
# and the path to the UserFolder they're defined in
self._creator = ownerInfo(creator)
self._owner = owner
self._depth = depth
self._timeout = timeout
self._locktype = locktype
self._lockscope = lockscope
self._modifiedtime = time.time()
if token is None:
self._token = generateLockToken()
else:
self._token = token
def getCreator(self):
return self._creator
def getCreatorPath(self):
db, name = self._creator
path = join(db,'/')
return "/%s/%s" % (path, name)
def getOwner(self):
return self._owner
def getLockToken(self):
return self._token
def getDepth(self):
return self._depth
def getTimeout(self):
return self._timeout
def getTimeoutString(self):
t = str(self._timeout)
if t[-1] == 'L': t = t[:-1] # lob off Long signifier
return "Second-%s" % t
def setTimeout(self, newtimeout):
timeout, errors = validateTimeout(newtimeout)
if errors:
raise ValueError, errors
else:
self._timeout = timeout
self._modifiedtime = time.time() # reset modified
def getModifiedTime(self):
return self._modifiedtime
def refresh(self):
self._modifiedtime = time.time()
def isValid(self):
now = time.time()
modified = self._modifiedtime
timeout = self._timeout
return (modified + timeout) > now
def getLockType(self):
return self._locktype
def getLockScope(self):
return self._lockscope
def asLockDiscoveryProperty(self, ns='d'):
s = (' <%(ns)s:activelock>\n'
' <%(ns)s:locktype><%(ns)s:%(locktype)s/></%(ns)s:locktype>\n'
' <%(ns)s:lockscope><%(ns)s:%(lockscope)s/></%(ns)s:lockscope>\n'
' <%(ns)s:depth>%(depth)s</%(ns)s:depth>\n'
' <%(ns)s:owner>%(owner)s</%(ns)s:owner>\n'
' <%(ns)s:timeout>%(timeout)s</%(ns)s:timeout>\n'
' <%(ns)s:locktoken>\n'
' <%(ns)s:href>opaquelocktoken:%(locktoken)s</%(ns)s:href>\n'
' </%(ns)s:locktoken>\n'
' </%(ns)s:activelock>\n'
) % {
'ns': ns,
'locktype': self._locktype,
'lockscope': self._lockscope,
'depth': self._depth,
'owner': self._owner,
'timeout': self.getTimeoutString(),
'locktoken': self._token,
}
return s
def asXML(self):
s = """<?xml version="1.0" encoding="utf-8" ?>
<d:prop xmlns:d="DAV:">
<d:lockdiscovery>
%s
</d:lockdiscovery>
</d:prop>""" % self.asLockDiscoveryProperty(ns="d")
return s
from WriteLockInterface import WriteLockInterface, LockItemInterface
from EtagSupport import EtagSupport
from LockItem import LockItem
from AccessControl import ClassSecurityInfo
from Globals import PersistentMapping
import Acquisition
class ResourceLockedError(Exception): pass
class LockableItem(EtagSupport):
"""\
Implements the WriteLockInterface, and is inherited by Resource which
is then inherited by the majority of Zope objects. For an object to
be lockable, however, it should have the WriteLockInterface in its
__implements__ list, ie:
__implements__ = (WriteLockInterface,)
"""
# Protect methods using declarative security
security = ClassSecurityInfo()
security.declarePrivate('wl_lockmapping')
security.declarePublic('wl_isLocked', 'wl_getLock', 'wl_isLockedByUser',
'wl_lockItems', 'wl_lockValues', 'wl_lockTokens',)
security.declareProtected('WebDAV Lock items',
'wl_grantLockToUser', 'wl_setLock')
security.declareProtected('WebDAV Unlock items',
'wl_delLock')
security.declareProtected('Manage WebDAV Locks', 'wl_killAllLocks')
def wl_lockmapping(self, killinvalids=0):
""" if 'killinvalids' is 1, locks who are no longer valid
will be deleted """
try: locks = getattr(self, '_dav_writelocks', None)
except: locks = None
if locks is None:
locks = self._dav_writelocks = PersistentMapping()
return locks
elif killinvalids:
# Delete invalid locks
for token, lock in locks.items():
if not lock.isValid():
del locks[token]
if (not locks) and hasattr(Acquisition.aq_base(self),
'__no_valid_write_locks__'):
self.__no_valid_write_locks__()
return locks
else:
return locks
def wl_lockItems(self, killinvalids=0):
return self.wl_lockmapping(killinvalids).items()
def wl_lockValues(self, killinvalids=0):
return self.wl_lockmapping(killinvalids).values()
def wl_lockTokens(self, killinvalids=0):
return self.wl_lockmapping(killinvalids).keys()
def wl_hasLock(self, token, killinvalids=0):
if not token: return 0
return token in self.wl_lockmapping(killinvalids).keys()
def wl_isLocked(self):
# returns true if 'self' is locked at all
# We set 'killinvalids' to 1 to delete all locks who are no longer
# valid (timeout has been exceeded)
locks = self.wl_lockmapping(killinvalids=1)
if locks.keys(): return 1
else: return 0
def wl_setLock(self, locktoken, lock):
locks = self.wl_lockmapping()
if LockItemInterface.isImplementedBy(lock):
if locktoken == lock.getLockToken():
locks[locktoken] = lock
else:
raise ValueError, 'Lock tokens do not match'
else:
raise ValueError, 'Lock does not implement the LockItem Interface'
def wl_getLock(self, locktoken):
locks = self.wl_lockmapping(killinvalids=1)
return locks.get(locktoken, None)
def wl_delLock(self, locktoken):
locks = self.wl_lockmapping()
if locks.has_key(locktoken):
del locks[locktoken]
def wl_clearLocks(self):
# Called by lock management machinery to quickly and effectively
# destroy all locks.
locks = self.wl_lockmapping()
locks.clear()
import Globals
Globals.default__class_init__(LockableItem)
### Utility functions
def wl_isLocked(ob):
""" Returns true if the object is locked, returns 0 if the object
is not locked or does not implement the WriteLockInterface """
return WriteLockInterface.isImplementedBy(ob) and ob.wl_isLocked()
This diff is collapsed.
This diff is collapsed.
import Interface
class LockItemInterface(Interface.Base):
"""\
A LockItem contains information about a lock. This includes:
o The locktoken uri (used to identify the lock by WebDAV)
o The lock owner (The string passed in the 'owner' property by WebDAV)
o The lock creator (the Zope user who physically owns the lock)
o Depth
o Timeout information
o Modified time (for calculating timeouts)
o LockType (only EXCLUSIVE is supported right now)
"""
def __init__(self, creator, owner, depth=0, timeout='Infinite',
locktype='write', lockscope='exclusive', token=None):
"""\
If any of the following are untrue, a **ValueError** exception
will be raised.
- **creator** MUST be a Zope user object or string to find a
valid user object.
- **owner** MUST be a nonempty string, or type that can be converted
to a nonempty string.
- **depth** MUST be in the set {0,'infinity'}
- **timeout** MUST either be an integer, or a string in the form
of 'Seconds-nnn' where nnn is an integer. The timeout value
MUST be less than (2^32)-1. *IF* timeout is the string value
'Infinite', the timeout value will be set to 1800 (30 minutes).
(Timeout is the value in seconds from creation\modification
time until the lock MAY time out).
- **locktype** not in set {'write'} *this may expand later*
- **lockscope** not in set {'exclusive'} *this may expand later*
If the value passed in to 'token' is 'None', the a new locktoken
will be generated during the construction process.
__init__ must generate the opaquelocktoken uri used to identify the
lock (if 'token' is 'None')and set all of the above attributes on
the object.
"""
def getCreator(self):
""" Returns the Zope user who created the lock. This is returned
in a tuple containing the Users ID and the path to the user folder
they came from."""
def getCreatorPath(self):
""" Returns a string of the path to the user object in the user
folder they were found in. """
def getOwner(self):
""" Returns the string value of the 'owner' property sent
in by WebDAV """
def getLockToken(self):
""" returns the opaque lock token """
def getDepth(self):
""" returns the depth of the lock """
def getTimeout(self):
""" returns an integer value of the timeout setting """
def getTimeoutString(self):
""" returns the timeout value in a form acceptable by
WebDAV (ie - 'Seconds-40800') """
def setTimeout(self, newtimeout):
""" refreshes the timeout information """
def getModifiedTime(self):
""" returns a time.time value of the last time the Lock was
modified. From RFC 2518:
The timeout counter SHOULD be restarted any time an owner of the
lock sends a method to any member of the lock, including unsupported
methods or methods which are unsucscessful. The lock MUST be
refreshed if a refresh LOCK method is successfully received.
The modified time is used to calculate the refreshed value """
def refresh(self):
""" Tickles the locks modified time by setting it to the current
time.time() value. (As stated in the RFC, the timeout counter
SHOULD be restarted for any HTTP method called by the lock owner
on the locked object). """
def isValid(self):
""" Returns true if (self.getModifiedTime() + self.getTimeout())
is greater than the current time.time() value. """
# now = time.time()
# modified = self.getModifiedTime()
# timeout = self.getTimeout()
#
# return (modified + timeout > now) # there's time remaining
def getLockType(self):
""" returns the lock type ('write') """
def getLockScope(self):
""" returns the lock scope ('exclusive') """
def asLockDiscoveryProperty(self, ns='d'):
""" Return the lock rendered as an XML representation of a
WebDAV 'lockdiscovery' property. 'ns' is the namespace identifier
used on the XML elements."""
def asXML(self):
""" Render a full XML representation of a lock for WebDAV,
used when returning the value of a newly created lock. """
class WriteLockInterface(Interface.Base):
"""\
This represents the 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(self, 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(self, killinvalids=0):
""" Returns a sequence of locks. if 'killinvalids' is true,
invalid locks will be deleted"""
def wl_lockTokens(self, killinvalids=0):
""" Returns a sequence of lock tokens. if 'killinvalids' is true,
invalid locks will be deleted"""
def wl_hasLock(self, token, killinvalids=0):
""" Returns true if the lock identified by the token is attached
to the object. """
def wl_isLocked(self):
""" Returns true if 'self' is locked at all. If invalid locks
still exist, they should be deleted."""
def wl_setLock(self, 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(self, locktoken):
""" Returns the locktoken identified by the locktokenuri """
def wl_delLock(self, locktoken):
""" Deletes the locktoken identified by the locktokenuri """
def wl_clearLocks(self):
""" Deletes ALL DAV locks on the object - should only be called
by lock management machinery. """
"""HTTP 1.1 / WebDAV client library."""
__version__='$Revision: 1.12 $'[11:-2]
__version__='$Revision: 1.13 $'[11:-2]
import sys, os, string, regex, time, types
import socket, httplib, mimetools
......@@ -245,10 +245,11 @@ class Resource:
'<d:lockinfo xmlns:d="DAV:">\n' \
' <d:lockscope><d:%s/></d:lockscope>\n' \
' <d:locktype><d:%s/></d:locktype>\n' \
' <d:depth>%s</d:depth>\n' \
' <d:owner>\n' \
' <d:href>%s</d:href>\n' \
' </d:owner>\n' \
'</d:lockinfo>' % (scope, type, owner)
'</d:lockinfo>' % (scope, type, depth, owner)
headers['Content-Type']='text/xml; charset="utf-8"'
headers['Content-Length']=str(len(body))
headers['Timeout']=timeout
......
......@@ -85,9 +85,9 @@
"""Commonly used functions for WebDAV support modules."""
__version__='$Revision: 1.9 $'[11:-2]
__version__='$Revision: 1.10 $'[11:-2]
import string, time, urllib
import string, time, urllib, re
from Acquisition import aq_base
from App.Common import iso8601_date, rfc850_date, rfc1123_date
......@@ -122,3 +122,69 @@ def urlbase(url, ftype=urllib.splittype, fhost=urllib.splithost):
type, uri=ftype(url)
host, uri=fhost(uri)
return uri or '/'
def generateLockToken():
# Generate a lock token
# XXX This is simple right now, just lifted from the original shortcut
# in Resource.dav__genlocktoken, but should be replaced by something
# better.
return 'AA9F6414-1D77-11D3-B825-00105A989226:%.03f' % time.time()
def tokenFinder(token):
# takes a string like '<opaquelocktoken:afsdfadfadf> and returns the token
# part.
if not token: return None # An empty string was passed in
if token[0] == '[': return None # An Etag was passed in
if token[0] == '<': token = token[1:-1]
return token[string.find(token,':')+1:]
### If: header handling support. IfParser returns a sequence of
### TagList objects in the order they were parsed which can then
### be used in WebDAV methods to decide whether an operation can
### proceed or to raise HTTP Error 412 (Precondition failed)
IfHdr = re.compile(
r"(?P<resource><.+?>)?\s*\((?P<listitem>[^)]+)\)"
)
ListItem = re.compile(
r"(?P<not>not)?\s*(?P<listitem><[a-zA-Z]+:[^>]*>|\[.*?\])",
re.I)
class TagList:
def __init__(self):
self.resource = None
self.list = []
self.NOTTED = 0
def IfParser(hdr):
out = []
i = 0
while 1:
m = IfHdr.search(hdr[i:])
if not m: break
i = i + m.end()
tag = TagList()
tag.resource = m.group('resource')
if tag.resource: # We need to delete < >
tag.resource = tag.resource[1:-1]
listitem = m.group('listitem')
tag.NOTTED, tag.list = ListParser(listitem)
out.append(tag)
return out
def ListParser(listitem):
out = []
NOTTED = 0
i = 0
while 1:
m = ListItem.search(listitem[i:])
if not m: break
i = i + m.end()
out.append(m.group('listitem'))
if m.group('not'): NOTTED = 1
return NOTTED, out
This diff is collapsed.
<dtml-var name="manage_page_header">
<dtml-var name="manage_tabs">
<p>This item is <span style="color: red">locked by WebDAV</span> as a
<strong>Lock-Null Resource</strong>. A lock-null resource is
created when a resource is locked before it is fully created,
basically reserving its name. When the owner of this resource
issues a command to either fill it with content, or turn it into a
collection (folder), that object will replace this one.</p>
<dtml-var name="manage_page_footer">
\ No newline at end of file
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