[Checkins] SVN: zope.webdav/trunk/src/zope/webdav/ Fixed bug in
rendering the DAV:lockdiscovery property. We were expecting a
Michael Kerrin
michael.kerrin at openapp.biz
Fri Apr 6 07:29:33 EDT 2007
Log message for revision 74032:
Fixed bug in rendering the DAV:lockdiscovery property. We were expecting a
value for the owner XML element but under certain circumstances it can be
omitted.
Changed:
U zope.webdav/trunk/src/zope/webdav/coreproperties.py
U zope.webdav/trunk/src/zope/webdav/locking.txt
U zope.webdav/trunk/src/zope/webdav/tests/test_doctests.py
U zope.webdav/trunk/src/zope/webdav/tests/test_widgets.py
U zope.webdav/trunk/src/zope/webdav/widgets.py
-=-
Modified: zope.webdav/trunk/src/zope/webdav/coreproperties.py
===================================================================
--- zope.webdav/trunk/src/zope/webdav/coreproperties.py 2007-04-05 20:14:40 UTC (rev 74031)
+++ zope.webdav/trunk/src/zope/webdav/coreproperties.py 2007-04-06 11:29:31 UTC (rev 74032)
@@ -132,13 +132,17 @@
lockscope = schema.List(
title = u"Describes the exclusivity of a lock",
description = u"""Specifies whether a lock is an exclusive lock, or a
- shared lock.""")
+ shared lock.""",
+ required = True,
+ readonly = True)
locktype = schema.List(
title = u"Describes the access type of the lock.",
description = u"""Specifies the access type of a lock. At present,
this specification only defines one lock type, the
- write lock.""")
+ write lock.""",
+ required = True,
+ readonly = True)
class IActiveLock(ILockEntry):
@@ -279,6 +283,95 @@
"""
+class LockdiscoveryDAVWidget(zope.webdav.widgets.ListDAVWidget):
+ """
+ Custom widget for the `{DAV:}lockdiscovery` property. This is basically
+ a list widget but it doesn't display any sub XML element whose value
+ is equal to its field missing_value.
+
+ >>> import zope.schema.interfaces
+ >>> from zope.webdav.tests import test_widgets
+
+ Setup some adapters for rendering the widget.
+
+ >>> gsm = component.getGlobalSiteManager()
+ >>> gsm.registerAdapter(zope.webdav.widgets.TextDAVWidget,
+ ... (zope.schema.interfaces.IText, None))
+ >>> gsm.registerAdapter(zope.webdav.widgets.IntDAVWidget,
+ ... (zope.schema.interfaces.IInt, None))
+
+ Setup a field and test object to render. While this is not the same field
+ as the `{DAV:}lockdiscovery` property but it is lot easier to work with
+ during the tests.
+
+ >>> field = schema.List(__name__ = 'testelement',
+ ... title = u'Test field',
+ ... value_type = schema.Object(
+ ... __name__ = u'testsubelement',
+ ... title = u'Test sub element',
+ ... schema = test_widgets.ISimpleInterface))
+ >>> objectvalue = test_widgets.SimpleObject(name = None, age = 26)
+
+ >>> widget = LockdiscoveryDAVWidget(field, None)
+ >>> widget.namespace = 'DAV:'
+ >>> widget.setRenderedValue([objectvalue])
+
+ The objectvalue name is None which is equal the Text field missing_value
+ so the name sub XML element doesn't show up.
+
+ >>> print etree.tostring(widget.render()) #doctest:+XMLDATA
+ <testelement xmlns='DAV:'>
+ <testsubelement>
+ <age>26</age>
+ </testsubelement>
+ </testelement>
+
+ By setting the name attribute it now shows up in the output.
+
+ >>> objectvalue.name = u'Michael Kerrin'
+ >>> print etree.tostring(widget.render()) #doctest:+XMLDATA
+ <testelement xmlns='DAV:'>
+ <testsubelement>
+ <name>Michael Kerrin</name>
+ <age>26</age>
+ </testsubelement>
+ </testelement>
+
+ But the content object needs to be locked for the `{DAV:}lockdiscovery`
+ element to have a value.
+
+ >>> widget.setRenderedValue(None)
+ >>> print etree.tostring(widget.render()) #doctest:+XMLDATA
+ <testelement xmlns='DAV:' />
+
+ Clean up the component registration.
+
+ >>> gsm.unregisterAdapter(zope.webdav.widgets.TextDAVWidget,
+ ... (zope.schema.interfaces.IText, None))
+ True
+ >>> gsm.unregisterAdapter(zope.webdav.widgets.IntDAVWidget,
+ ... (zope.schema.interfaces.IInt, None))
+ True
+
+ """
+ interface.classProvides(zope.webdav.interfaces.IIDAVWidget)
+
+ def render(self):
+ etree = zope.etree.getEngine()
+ el = etree.Element(etree.QName(self.namespace, self.name))
+
+ if self._value is not self.context.missing_value:
+ for value in self._value:
+ widget = zope.webdav.widgets.ObjectDAVWidget(
+ self.context.value_type, self.request)
+ widget.render_missing_values = False
+ widget.setRenderedValue(value)
+ widget.namespace = self.namespace
+ el.append(widget.render())
+
+ return el
+
+
################################################################################
#
# Collection of default properties has defined in Section 15.
@@ -305,6 +398,7 @@
resourcetype = DAVProperty("{DAV:}resourcetype", IDAVResourcetype)
lockdiscovery = DAVProperty("{DAV:}lockdiscovery", IDAVLockdiscovery)
+lockdiscovery.custom_widget = LockdiscoveryDAVWidget
supportedlock = DAVProperty("{DAV:}supportedlock", IDAVSupportedlock)
Modified: zope.webdav/trunk/src/zope/webdav/locking.txt
===================================================================
--- zope.webdav/trunk/src/zope/webdav/locking.txt 2007-04-05 20:14:40 UTC (rev 74031)
+++ zope.webdav/trunk/src/zope/webdav/locking.txt 2007-04-06 11:29:31 UTC (rev 74032)
@@ -351,7 +351,10 @@
... return self.data['depth']
... @property
... def owner(self):
- ... return self.data['owner'].replace('\n', '')
+ ... value = self.data['owner']
+ ... if value:
+ ... return value.replace('\n', '')
+ ... return None
... @property
... def timeout(self):
... return "Second-%d" % self.data['duration'].seconds
@@ -410,11 +413,46 @@
of application/xml and the body should be a rendering of the
`{DAV:}lockdiscovery` property.
+First we note that the owner element is optional.
+
>>> request = TestWebDAVRequest(
... body = """<?xml version="1.0" encoding="utf-8" ?>
... <D:lockinfo xmlns:D="DAV:">
... <D:lockscope><D:exclusive/></D:lockscope>
... <D:locktype><D:write/></D:locktype>
+ ... </D:lockinfo>""")
+ >>> respbody = LOCK(resource, request).LOCK()
+ Locked the resource.
+ >>> print respbody #doctest:+XMLDATA
+ <prop xmlns="DAV:">
+ <lockdiscovery>
+ <activelock>
+ <lockscope><exclusive /></lockscope>
+ <locktype><write /></locktype>
+ <depth>infinity</depth>
+ <timeout>Second-720</timeout>
+ <locktoken><href>urn:resourcelocktoken</href></locktoken>
+ <lockroot>http://localhost/resource</lockroot>
+ </activelock>
+ </lockdiscovery>
+ </prop>
+ >>> request.response.getStatus()
+ 200
+ >>> request.response.getHeader("Content-type")
+ 'application/xml'
+ >>> request.response.getHeader("Lock-token")
+ '<urn:resourcelocktoken>'
+
+Unlock the resource and try again with the owner element included in the
+request.
+
+ >>> resource._lockinfo = None
+
+ >>> request = TestWebDAVRequest(
+ ... body = """<?xml version="1.0" encoding="utf-8" ?>
+ ... <D:lockinfo xmlns:D="DAV:">
+ ... <D:lockscope><D:exclusive/></D:lockscope>
+ ... <D:locktype><D:write/></D:locktype>
... <D:owner>
... <D:href>http://example.org/~ejw/contact.html</D:href>
... </D:owner>
@@ -512,6 +550,10 @@
'write'
>>> resource._lockinfo['depth']
'infinity'
+ >>> print resource._lockinfo['owner'] #doctest:+XMLDATA
+ <owner xmlns="DAV:">
+ <href>http://example.org/~ejw/contact.html</href>
+ </owner>
>>> print respbody #doctest:+XMLDATA
<prop xmlns="DAV:">
<lockdiscovery>
Modified: zope.webdav/trunk/src/zope/webdav/tests/test_doctests.py
===================================================================
--- zope.webdav/trunk/src/zope/webdav/tests/test_doctests.py 2007-04-05 20:14:40 UTC (rev 74031)
+++ zope.webdav/trunk/src/zope/webdav/tests/test_doctests.py 2007-04-06 11:29:31 UTC (rev 74032)
@@ -195,7 +195,10 @@
checker = zope.etree.testing.xmlOutputChecker,
setUp = zope.etree.testing.etreeSetup,
tearDown = zope.etree.testing.etreeTearDown),
- doctest.DocTestSuite("zope.webdav.coreproperties"),
+ doctest.DocTestSuite("zope.webdav.coreproperties",
+ checker = zope.etree.testing.xmlOutputChecker,
+ setUp = zope.etree.testing.etreeSetup,
+ tearDown = zope.etree.testing.etreeTearDown),
doctest.DocFileSuite("datamodel.txt", package = "zope.webdav",
checker = zope.etree.testing.xmlOutputChecker,
setUp = zope.etree.testing.etreeSetup,
Modified: zope.webdav/trunk/src/zope/webdav/tests/test_widgets.py
===================================================================
--- zope.webdav/trunk/src/zope/webdav/tests/test_widgets.py 2007-04-05 20:14:40 UTC (rev 74031)
+++ zope.webdav/trunk/src/zope/webdav/tests/test_widgets.py 2007-04-06 11:29:31 UTC (rev 74032)
@@ -22,7 +22,7 @@
from zope import schema
from zope import component
-from zope.schema.interfaces import ITextLine
+import zope.schema.interfaces
from zope.interface import Interface, implements
from zope.interface.verify import verifyObject
from zope.datetime import tzinfo
@@ -97,8 +97,8 @@
self.setUpWidget(element)
def setUpWidget(self, element = None):
- request = TestWebDAVRequest(element)
- self.widget = self._WidgetFactory(self.field, request)
+ self.request = TestWebDAVRequest(element)
+ self.widget = self._WidgetFactory(self.field, self.request)
self.widget.namespace = self.namespace
@@ -268,7 +268,8 @@
self.etree = etreeSetup()
component.getGlobalSiteManager().registerAdapter(
widgets.TextDAVWidget,
- (ITextLine, zope.webdav.interfaces.IWebDAVRequest))
+ (zope.schema.interfaces.ITextLine,
+ zope.webdav.interfaces.IWebDAVRequest))
foofield = schema.List(__name__ = self.name,
title = u"Foo Title",
@@ -293,27 +294,40 @@
def tearDown(self):
component.getGlobalSiteManager().unregisterAdapter(
widgets.TextDAVWidget,
- (ITextLine, zope.webdav.interfaces.IWebDAVRequest))
+ (zope.schema.interfaces.ITextLine,
+ zope.webdav.interfaces.IWebDAVRequest))
super(ListTextWebDAVWidgetTest, self).tearDown()
+class ISimpleInterface(Interface):
+ name = schema.TextLine(
+ title = u"Name subproperty",
+ description = u"",
+ required = False)
+
+ age = schema.Int(
+ title = u"Age subproject",
+ description = u"",
+ required = True)
+
+
+class SimpleObject(object):
+ implements(ISimpleInterface)
+
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+
class ObjectDAVWidgetTest(WebDAVWidgetTest):
_WidgetFactory = widgets.ObjectDAVWidget
- rendered_content = '<ns0:name>Michael Kerrin</ns0:name>'
+ rendered_content = """<ns0:name>Michael Kerrin</ns0:name>
+ <ns0:age>26</ns0:age>"""
def setUp(self):
self.etree = etreeSetup()
- class ISimpleInterface(Interface):
- name = schema.TextLine(
- title = u"Named subproperty",
- description = u"")
-
- class SimpleObject(object):
- name = u"Michael Kerrin"
-
foofield = schema.Object(__name__ = self.name,
title = u"Foo Title",
description = u"Foo field",
@@ -324,7 +338,8 @@
class DemoContent(object):
implements(IDemoContent)
- self.field_content = SimpleObject()
+ self.field_content = SimpleObject(name = u"Michael Kerrin",
+ age = 26)
self.content = DemoContent()
field = IDemoContent['foo']
self.field = field.bind(self.content)
@@ -332,16 +347,111 @@
component.getGlobalSiteManager().registerAdapter(
widgets.TextDAVWidget,
- (ITextLine,
+ (zope.schema.interfaces.ITextLine,
zope.webdav.interfaces.IWebDAVRequest))
+ component.getGlobalSiteManager().registerAdapter(
+ widgets.IntDAVWidget,
+ (zope.schema.interfaces.IInt,
+ zope.webdav.interfaces.IWebDAVRequest))
def tearDown(self):
component.getGlobalSiteManager().unregisterAdapter(
widgets.TextDAVWidget,
- (ITextLine, zope.webdav.interfaces.IWebDAVRequest))
+ (zope.schema.interfaces.ITextLine,
+ zope.webdav.interfaces.IWebDAVRequest))
+ component.getGlobalSiteManager().unregisterAdapter(
+ widgets.IntDAVWidget,
+ (zope.schema.interfaces.IInt,
+ zope.webdav.interfaces.IWebDAVRequest))
super(ObjectDAVWidgetTest, self).tearDown()
+ def test_default_render_missing_values(self):
+ widget = self._WidgetFactory(self.field, self.request)
+ self.assertEqual(widget.render_missing_values, True)
+ def test_renderMissingAttribute(self):
+ content = SimpleObject(name = u"Michael Kerrin")
+
+ widget = self._WidgetFactory(self.field, self.request)
+ widget.namespace = self.namespace
+ widget.setRenderedValue(content)
+
+ self.assertRaises(AttributeError, widget.render)
+
+ def test_renderMissingFieldValue(self):
+ ## In this case the name attribute (which is not required) is equal
+ ## to the missing_value. The ObjectDAVWidget view still renders this
+ ## elements because the render_missing_values is set to True.
+ content = SimpleObject(name = None, age = 26)
+
+ widget = self._WidgetFactory(self.field, self.request)
+ widget.namespace = self.namespace
+ widget.setRenderedValue(content)
+
+ self.assertEqual(widget.render_missing_values, True)
+
+ element = widget.render()
+ assertXMLEqual(element, """<ns0:foo xmlns:ns0="testns:">
+ <ns0:name />
+ <ns0:age>26</ns0:age>
+ </ns0:foo>""")
+
+ def test_dontRenderMissingFieldValue(self):
+ ## In this case the name attribute (which is not required) is equal
+ ## to the missing_value and because the render_missing_values
+ ## attribute is False we don't render this XML element.
+ content = SimpleObject(name = None, age = 26)
+
+ widget = self._WidgetFactory(self.field, self.request)
+ widget.render_missing_values = False
+ widget.namespace = self.namespace
+ widget.setRenderedValue(content)
+
+ self.assertEqual(widget.render_missing_values, False)
+
+ element = widget.render()
+ assertXMLEqual(element, """<ns0:foo xmlns:ns0="testns:">
+ <ns0:age>26</ns0:age>
+ </ns0:foo>""")
+
+ def test_renderMissingRequiredFieldValue(self):
+ ## In this case the age attribute (which is required) is equal
+ ## to the missing value so the ObjectDAVWidget should try and
+ ## render it.
+ content = SimpleObject(name = u"Michael Kerrin", age = None)
+
+ widget = self._WidgetFactory(self.field, self.request)
+ widget.namespace = self.namespace
+ widget.setRenderedValue(content)
+
+ self.assertEqual(widget.render_missing_values, True)
+
+ element = widget.render()
+ assertXMLEqual(element, """<ns0:foo xmlns:ns0="testns:">
+ <ns0:name>Michael Kerrin</ns0:name>
+ <ns0:age />
+ </ns0:foo>""")
+
+ def test_dontRenderMissingRequiredFieldValue(self):
+ ## In this case the age attribute (which is required) is equal
+ ## to the missing value so the ObjectDAVWidget should try and
+ ## render it, even since the render_missing_values is False.
+ content = SimpleObject(name = u"Michael Kerrin", age = None)
+
+ widget = self._WidgetFactory(self.field, self.request)
+ widget.render_missing_values = False
+ widget.namespace = self.namespace
+ widget.setRenderedValue(content)
+
+ self.assertEqual(widget.render_missing_values, False)
+
+ element = widget.render()
+ assertXMLEqual(element, """<ns0:foo xmlns:ns0="testns:">
+ <ns0:name>Michael Kerrin</ns0:name>
+ <ns0:age />
+ </ns0:foo>""")
+
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(WebDAVBaseWidgetTest),
Modified: zope.webdav/trunk/src/zope/webdav/widgets.py
===================================================================
--- zope.webdav/trunk/src/zope/webdav/widgets.py 2007-04-05 20:14:40 UTC (rev 74031)
+++ zope.webdav/trunk/src/zope/webdav/widgets.py 2007-04-06 11:29:31 UTC (rev 74032)
@@ -124,8 +124,17 @@
class ObjectDAVWidget(DAVWidget):
+ """
+ ObjectDAVWidget that will display all properties whether or not the
+ value of specific property in question is the missing value or not.
+
+ `render_missing_values` attribute is a marker to tell webdav to render
+ all fields which instance value is equal to the fields missing_value.
+ """
interface.classProvides(interfaces.IIDAVWidget)
+ render_missing_values = True
+
def render(self):
etree = component.getUtility(IEtree)
el = etree.Element(etree.QName(self.namespace, self.name))
@@ -136,7 +145,15 @@
interface = self.context.schema
for name, field in getFieldsInOrder(interface):
field = field.bind(self._value)
+ field_value = field.get(self._value)
+ # Careful this could result in elements not been displayed that
+ # should be. This is tested in test_widgets but it mightened be
+ # what some people think.
+ if field_value == field.missing_value and \
+ not self.render_missing_values and not field.required:
+ continue
+
widget = component.getMultiAdapter((field, self.request),
interfaces.IDAVWidget)
widget.namespace = self.namespace
More information about the Checkins
mailing list