[Zope3-checkins] CVS: Zope3/src/zope/app/dav - adapter.py:1.1 propfind.py:1.1 widget.py:1.1 configure.zcml:1.4

Sidnei da Silva sidnei@x3ng.com.br
Wed, 21 May 2003 12:10:37 -0400


Update of /cvs-repository/Zope3/src/zope/app/dav
In directory cvs.zope.org:/tmp/cvs-serv19036/src/zope/app/dav

Modified Files:
	configure.zcml 
Added Files:
	adapter.py propfind.py widget.py 
Log Message:
PROPFIND support, with minimal tests.

=== Added File Zope3/src/zope/app/dav/adapter.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""WebDAV Adapters

$Id: adapter.py,v 1.1 2003/05/21 16:10:06 sidnei Exp $
"""

from xml.dom import minidom
from zope.component import getView, getAdapter, queryAdapter
from zope.app.interfaces.traversing import IObjectName
from zope.app.interfaces.dublincore import IDCTimes
from zope.app.interfaces.file import IReadFile
from zope.app.interfaces.file import IReadDirectory
from zope.app.interfaces.size import ISized

class DAVSchemaAdapter:

    def __init__(self, object):
        self.context = object

    def displayname(self):
        value = getAdapter(self.context, IObjectName)()
        return value

    def creationdate(self):
        value = getAdapter(self.context, IDCTimes).created
        if value is None:
            return ''
        value = value.strftime('%Y-%m-%d %TZ')
        return value

    def resourcetype(self):
        value = queryAdapter(self.context, IReadDirectory, None)
        xml = minidom.Document()
        if value is not None:
            node = xml.createElement('collection')
            return node
        return ''

    def getcontentlength(self):
        value = getAdapter(self.context, ISized).sizeForDisplay()
        return str(value)

    def getlastmodified(self):
        value = getAdapter(self.context, IDCTimes).modified
        if value is None:
            return ''
        value = value.strftime('%a, %d %b %Y %H:%M:%S GMT')
        return value

    def executable(self):
        return ''


=== Added File Zope3/src/zope/app/dav/propfind.py ===
##############################################################################
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
##############################################################################
"""WebDAV method PROPFIND

$Id: propfind.py,v 1.1 2003/05/21 16:10:06 sidnei Exp $
"""
__metaclass__ = type

from xml.dom import minidom
from zope.component import getAdapter, getView, queryView, queryAdapter
from zope.proxy.context import ContextWrapper
from zope.proxy.introspection import removeAllProxies
from zope.schema import getFieldNamesInOrder
from zope.app.interfaces.container import IReadContainer
from zope.app.dav.globaldavschemaservice import queryInterface, availableNamespaces, queryNamespace
from zope.app.form.utility import setUpWidgets, getWidgetsDataFromAdapter

class PROPFIND:
    """PROPFIND handler for all objects
    """

    def __init__(self, context, request):
        self.context = context
        self.request = request
        self.setDepth(request.getHeader('depth', 'infinity'))
        self.content_type = request.getHeader('content-type', '')
        self.default_ns = 'DAV:'

    def getDepth(self):
        return self._depth

    def setDepth(self, depth):
        self._depth = depth.lower()

    def PROPFIND(self):
        request = self.request
        resource_url = str(getView(self.context, 'absolute_url', request))
        data = request.bodyFile
        data.seek(0)
        response = ''
        body = ''

        if self.content_type.lower() not in ['text/xml', 'application/xml']:
            request.response.setStatus(400)
            return body

        if self.getDepth() not in ['0', '1', 'infinity']:
            request.response.setStatus(400)
            return body

        xmldoc = minidom.parse(data)
        response = minidom.Document()
        ms = response.createElement('multistatus')
        ms.setAttribute('xmlns', self.default_ns)
        response.appendChild(ms)
        re = response.createElement('response')
        ms.appendChild(re)
        href = response.createElement('href')
        re.appendChild(href)
        r_url = response.createTextNode(resource_url)
        href.appendChild(r_url)
        _avail_props = {}
        # XXX For now, list the propnames for the all namespaces
        # but later on, we need to list *all* propnames from *all* known
        # namespaces that this object has.
        for ns in availableNamespaces():
            _avail_props[ns] = getFieldNamesInOrder(queryInterface(ns))
        propname = xmldoc.getElementsByTagNameNS(self.default_ns, 'propname')
        if propname:
            self._handlePropname(response, re, _avail_props)

        source = xmldoc.getElementsByTagNameNS(self.default_ns, 'prop')
        _props = {}
        if not source and not propname:
            _props = self._handleAllprop(_avail_props, _props)

        if source and not propname:
            _props = self._handleProp(source, _props)

        avail, not_avail = self._propertyResolver(_props)

        if avail:
            pstat = response.createElement('propstat')
            re.appendChild(pstat)
            prop = response.createElement('prop')
            pstat.appendChild(prop)
            status = response.createElement('status')
            pstat.appendChild(status)
            text = response.createTextNode('HTTP/1.1 200 OK')
            status.appendChild(text)
            for ns in avail.keys():
                iface = _props[ns]['iface']
                adapter = queryAdapter(self.context, iface, None)
                initial = getWidgetsDataFromAdapter(adapter, iface, names=avail.get(ns))
                setUpWidgets(self, iface, initial=initial, \
                             names=avail.get(ns), force=True)
                for p in avail.get(ns):
                    el = response.createElement('%s' % p )
                    if ns is not None and ns != self.default_ns:
                        el.setAttribute('xmlns', ns)
                    prop.appendChild(el)
                    value = getattr(self, p)()
                    if isinstance(value, (unicode, str)):
                        value = response.createTextNode(value) ## Get the widget value here
                        el.appendChild(value)
                    else:
                        if isinstance(removeAllProxies(value), minidom.Node):
                            el.appendChild(removeAllProxies(value))
                        else:
                            raise TypeError('Value should be string or minidom.Node. Got %r' % type(value))

        if not_avail:
            pstat = response.createElement('propstat')
            re.appendChild(pstat)
            prop = response.createElement('prop')
            pstat.appendChild(prop)
            status = response.createElement('status')
            pstat.appendChild(status)
            text = response.createTextNode('HTTP/1.1 403 Forbidden')
            status.appendChild(text)
            for ns in not_avail.keys():
                for p in not_avail.get(ns):
                    el = response.createElement('%s' % p )
                    prop.appendChild(el)
                    if ns is not None and ns != self.default_ns:
                        el.setAttribute('xmlns', ns)

        self._depthRecurse(ms)

        body = response.toxml('utf-8')
        request.response.setBody(body)
        request.response.setStatus(207)
        return body

    def _depthRecurse(self, ms):
        depth = self.getDepth()
        if depth == '1':
            subdepth = '0'
        if depth == 'infinity':
            subdepth = 'infinity'
        if depth != '0':
            if IReadContainer.isImplementedBy(self.context):
                for id, obj in self.context.items():
                    wrapped = ContextWrapper(obj, self.context, name=id)
                    pfind = queryView(wrapped, 'PROPFIND', self.request, None)
                    if pfind is not None:
                        pfind.setDepth(subdepth)
                        value = pfind.PROPFIND()
                        parsed = minidom.parseString(value)
                        responses = parsed.getElementsByTagNameNS(self.default_ns, 'response')
                        for r in responses:
                            ms.appendChild(r)

    def _handleProp(self, source, _props):
        source = source[0]
        childs = [e for e in source.childNodes
                  if e.nodeType == e.ELEMENT_NODE]
        for node in childs:
            ns = node.namespaceURI
            iface = queryInterface(ns, None)
            value = _props.get(ns, {'iface': iface, 'props': []})
            value['props'].append(node.localName)
            _props[ns] = value
        return _props

    def _handleAllprop(self, _avail_props, _props):
        for ns in _avail_props.keys():
            iface = queryInterface(ns, None)
            _props[ns] = {'iface': iface, 'props': _avail_props.get(ns)}
        return _props

    def _handlePropname(self, response, re, _avail_props):
        pstat = response.createElement('propstat')
        re.appendChild(pstat)
        prop = response.createElement('prop')
        pstat.appendChild(prop)
        count = 0
        for ns in _avail_props.keys():
            attr_name = 'a%s' % count
            if ns is not None and ns != self.default_ns:
                count += 1
                prop.setAttribute('xmlns:%s' % attr_name, ns)
            for p in _avail_props.get(ns):
                el = response.createElement(p)
                prop.appendChild(el)
                if ns is not None and ns != self.default_ns:
                    el.setAttribute('xmlns', attr_name)
        status = response.createElement('status')
        pstat.appendChild(status)
        text = response.createTextNode('HTTP/1.1 200 OK')
        status.appendChild(text)

    def _propertyResolver(self, _props):
        avail = {}
        not_avail = {}
        for ns in _props.keys():
            iface = _props[ns]['iface']
            for p in _props[ns]['props']:
                if _props[ns]['iface'] is None:
                    l = not_avail.get(ns, [])
                    l.append(p)
                    not_avail[ns] = l
                    continue
                adapter = queryAdapter(self.context, iface, None)
                if adapter is None:
                    l = not_avail.get(ns, [])
                    l.append(p)
                    not_avail[ns] = l
                    continue
                if hasattr(adapter, p):
                    l = avail.get(ns, [])
                    l.append(p)
                    avail[ns] = l
                else:
                    l = not_avail.get(ns, [])
                    l.append(p)
                    not_avail[ns] = l

        return avail, not_avail


=== Added File Zope3/src/zope/app/dav/widget.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Widgets specific to WebDAV

$Id: widget.py,v 1.1 2003/05/21 16:10:06 sidnei Exp $
"""

from zope.app.interfaces.dav import ISimpleDAVWidget
from zope.app.interfaces.form import IWidget
from zope.component.interfaces import IViewFactory
from zope.app.form.widget import Widget, CustomWidget

class SimpleDAVWidget(Widget):
    __implements__ = (ISimpleDAVWidget, IWidget, IViewFactory)

    def haveData(self):
        return 1

    def getData(self):
        return self._data

    def __str__(self):
        return str(self._data)

    def __call__(self):
        return self.getData()


=== Zope3/src/zope/app/dav/configure.zcml 1.3 => 1.4 ===
--- Zope3/src/zope/app/dav/configure.zcml:1.3	Tue May 20 15:43:27 2003
+++ Zope3/src/zope/app/dav/configure.zcml	Wed May 21 12:10:06 2003
@@ -2,42 +2,50 @@
    xmlns='http://namespaces.zope.org/zope'
    xmlns:dav='http://namespaces.zope.org/zope/dav'>
 
-
-<!-- not finished yet
-<view for="*"
- 	name="PROPFIND"
-	type="zope.publisher.interfaces.http.IHTTPPresentation"
-	factory=".propfind.PROPFIND"
-	permission="zope.ManageContent"
-	allowed_attributes="PROPFIND setDepth getDepth"
-/>
-
-<adapter
-     provides="zope.app.interfaces.dav.IDAVSchema"
-     for="*"
-     permission="zope.Public"
-     factory=".adapter.DAVSchemaAdapter"
-     />
-
--->
-
-<serviceType
-    id='DAVSchema'
-    interface='zope.app.interfaces.component.IDAVSchemaService' />
-
-<service
-   serviceType='DAVSchema'
-   permission='zope.Public'
-   component='zope.app.dav.globaldavschemaservice.davSchemaService' />
-
-<dav:provideInterface
-   for="http://purl.org/dc/1.1"
-   interface="zope.app.interfaces.dublincore.IZopeDublinCore"
-/>
-
-<dav:provideInterface
-   for="DAV:"
-   interface="zope.app.interfaces.dav.IDAVSchema"
-/>
+ <view for="*"
+	 name="PROPFIND"
+	 type="zope.publisher.interfaces.http.IHTTPPresentation"
+	 factory=".propfind.PROPFIND"
+	 permission="zope.ManageContent"
+	 allowed_attributes="PROPFIND setDepth getDepth"
+	 />
+
+ <defaultView
+       for="zope.schema.interfaces.IText"
+       name="view"
+       permission="zope.Public"
+       type="zope.publisher.interfaces.http.IHTTPPresentation"
+       factory="zope.app.dav.widget.SimpleDAVWidget"
+       allowed_attributes="getData haveData setData __call__"
+       />
+
+ <adapter
+      provides="zope.app.interfaces.dav.IDAVSchema"
+      for="*"
+      permission="zope.Public"
+      factory=".adapter.DAVSchemaAdapter"
+      />
+
+
+ <serviceType
+	id='DAVSchema'
+	interface='zope.app.interfaces.component.IDAVSchemaService'
+	/>
+
+ <service
+	serviceType='DAVSchema'
+	permission='zope.Public'
+	component='zope.app.dav.globaldavschemaservice.davSchemaService'
+	/>
+
+ <dav:provideInterface
+	for="http://purl.org/dc/1.1"
+ 	interface="zope.app.interfaces.dublincore.IZopeDublinCore"
+	/>
+
+ <dav:provideInterface
+	for="DAV:"
+	interface="zope.app.interfaces.dav.IDAVSchema"
+	/>
 
 </zopeConfigure>