Commit ee70a7f5 authored by 's avatar

initial

parent 4f0072eb
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
"""WebDAV support - collection objects."""
__version__='$Revision: 1.1 $'[11:-2]
import sys, os, string, mimetypes
from Resource import Resource
class Collection(Resource):
"""The Collection class provides basic WebDAV support for
collection objects. It provides default implementations
for all supported WebDAV HTTP methods. The behaviors of some
WebDAV HTTP methods for collections are slightly different
than those for non-collection resources.
"""
def redirect_check(self, req, rsp):
# By the spec, we are not supposed to accept /foo for a
# collection, we have to redirect to /foo/.
if req['PATH_INFO'][-1]=='/':
return
raise 'Moved Permanently', req['URL1']+'/'
def HEAD(self, REQUEST, RESPONSE):
"""Retrieve resource information without a response body."""
self.init_headers(RESPONSE)
self.redirect_check(REQUEST, RESPONSE)
RESPONSE.setStatus(200)
return RESPONSE
def PUT(self, REQUEST, RESPONSE):
"""The PUT method has no inherent meaning for collection
resources, though collections are not specifically forbidden
to handle PUT requests. The default response to a PUT request
for collections is 405 (Method Not Allowed)."""
self.init_headers(RESPONSE)
self.redirect_check(REQUEST, RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
def DELETE(self, REQUEST, RESPONSE):
"""Delete a collection resource. For collection resources, DELETE
may return either 200 (OK) or 204 (No Content) to indicate total
success, or may return 207 (Multistatus) to indicate partial
success."""
self.init_headers(RESPONSE)
self.redirect_check(REQUEST, RESPONSE)
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
path=filter(None, string.split(REQUEST['PATH_INFO'], '/'))
name=path[-1]
# TODO: add lock check here
self.aq_parent._delObject(name)
RESPONSE.setStatus(204)
return RESPONSE
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
"""WebDAV support - null resource objects."""
__version__='$Revision: 1.1 $'[11:-2]
import sys, os, string, mimetypes
import Acquisition, OFS.content_types
from Resource import Resource, aq_base
class NullResource(Acquisition.Implicit, Resource):
"""Null resources are used to handle HTTP method calls on
objects which do not yet exist in the url namespace."""
_isNullResource=1
def __init__(self, parent, id):
self.id=id
self.__parent__=parent
self.__roles__=None # fix this!!
def HEAD(self, REQUEST, RESPONSE):
"""Retrieve resource information without a response message body."""
self.init_headers(RESPONSE)
raise 'Not Found', 'The requested resource does not exist.'
# Most methods return 404 (Not Found) for null resources.
DELETE=OPTIONS=TRACE=PROPFIND=PROPPATCH=COPY=MOVE=HEAD
def PUT(self, REQUEST, RESPONSE):
"""Create a new non-collection resource."""
self.init_headers(RESPONSE)
type=REQUEST.get_header('content-type', None)
body=REQUEST.get('BODY', '')
if type is None:
type, enc=mimetypes.guess_type(self.id)
if type is None:
if OFS.content_types.find_binary(body) >= 0:
content_type='application/octet-stream'
else: type=OFS.content_types.text_type(body)
type=string.lower(type)
from OFS.Image import Image, File
if type in ('text/html', 'text/xml', 'text/plain'):
self.__parent__.manage_addDTMLDocument(self.id, '', body)
elif type[:6]=='image/':
ob=Image(self.id, '', body, content_type=type)
self.__parent__._setObject(self.id, ob)
else:
ob=File(self.id, '', body, content_type=type)
self.__parent__._setObject(self.id, ob)
RESPONSE.setStatus(201)
RESPONSE.setBody('')
return RESPONSE
def MKCOL(self, REQUEST, RESPONSE):
"""Create a new collection resource."""
self.init_headers(RESPONSE)
if REQUEST.get('BODY', ''):
raise 'Unsupported Media Type', 'Unknown request body.'
parent=self.__parent__
if hasattr(aq_base(parent), self.id):
raise 'Method Not Allowed', 'The name %s is in use.' % self.id
if (not hasattr(parent.aq_base, 'isAnObjectManager')) or \
(not parent.isAnObjectManager):
raise 'Forbidden', 'Unable to create collection resource.'
# This should probably do self.__class__(id, ...), except Folder
# doesn't currently have a constructor.
parent.manage_addFolder(self.id)
RESPONSE.setStatus(201)
RESPONSE.setBody('')
return RESPONSE
def LOCK(self, REQUEST, RESPONSE):
"""Create a lock-null resource."""
self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
def UNLOCK(self):
"""Remove a lock-null resource."""
self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
"""WebDAV support - resource objects."""
__version__='$Revision: 1.1 $'[11:-2]
import sys, os, string, time
import mimetypes, xmlcmds
zpns='http://www.zope.org/propertysets/default/'
class Resource:
"""The Resource mixin class provides basic WebDAV support for
non-collection objects. It provides default implementations
for most supported WebDAV HTTP methods, however certain methods
such as PUT should be overridden to ensure correct behavior in
the context of the object type."""
__http_methods__=('GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS',
'TRACE', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY',
'MOVE',
)
__dav_resource__=1
def init_headers(self, r):
# Init expected HTTP 1.1 / WebDAV headers which are not
# currently set by the response object automagically.
r.setHeader('Connection', 'close')
r.setHeader('Date', rfc1123_date())
r.setHeader('DAV', '1')
dav__locks=()
def dav__is_locked(self):
# Return true if this object is locked via a
# session or dav lock.
if hasattr(self, 'locked_in_session') and self.locked_in_session():
return 1
return 0
def dav__get_locks(self):
# Return the current locks on the object.
if hasattr(self, 'locked_in_session') and self.locked_in_session():
lock=Lock('xxxx', 'xxxx')
return self.dav__locks + (lock,)
def dav__is_acquired(self, ob=None):
# Return true if this object is not a direct
# subobject of its aq_parent object.
if ob is None: ob=self
if not hasattr(ob, 'aq_parent'):
return 0
if hasattr(aq_base(ob.aq_parent), absattr(ob.id)):
return 0
if hasattr(aq_base(ob), 'isTopLevelPrincipiaApplicationObject'):
return 0
return 1
# WebDAV class 1 support
def HEAD(self, REQUEST, RESPONSE):
"""Retrieve resource information without a response message
body. It would be great if we had a standard way to ask an
arbitrary object for its headers -- that would allow the
default HEAD implementation to handle most needs."""
self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
def PUT(self, REQUEST, RESPONSE):
"""Replace the GET response entity of an existing resource.
Because this is often object-dependent, objects which handle
PUT should override the default PUT implementation with an
object-specific implementation. By default, PUT requests
fail with a 405 (Method Not Allowed)."""
self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
def OPTIONS(self, REQUEST, RESPONSE):
"""Retrieve communication options."""
self.init_headers(RESPONSE)
RESPONSE.setHeader('Allow', string.join(self.__http_methods__, ', '))
RESPONSE.setHeader('Content-Length', 0)
RESPONSE.setStatus(200)
return RESPONSE
def TRACE(self, REQUEST, RESPONSE):
"""Return the HTTP message received back to the client as the
entity-body of a 200 (OK) response. This will often actually
be intercepted by the web server in use. If not, the TRACE
request will fail with a 405 (Method Not Allowed), since it
is not often possible to reproduce the HTTP request verbatim
from within the Zope environment."""
self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
def DELETE(self, REQUEST, RESPONSE):
"""Delete a resource. For non-collection resources, DELETE may
return either 200 or 204 (No Content) to indicate success."""
self.init_headers(RESPONSE)
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
path=filter(None, string.split(REQUEST['URL'], '/'))
if path[-1]=='DELETE':
del path[-1]
name=path[-1]
# TODO: add lock checking here
self.aq_parent._delObject(name)
RESPONSE.setStatus(204)
return RESPONSE
def PROPFIND(self, REQUEST, RESPONSE):
"""Retrieve properties defined on the resource."""
self.init_headers(RESPONSE)
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
try: request=xmlcmds.PropFind(REQUEST)
except: raise 'Bad Request', 'Invalid xml request.'
result=request.apply(self)
RESPONSE.setStatus(207)
RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"')
RESPONSE.setBody(result)
return RESPONSE
def PROPPATCH(self, REQUEST, RESPONSE):
"""Set and/or remove properties defined on the resource."""
self.init_headers(RESPONSE)
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
if not hasattr(self, '__propsets__'):
raise 'Method Not Allowed', (
'Method not supported for this resource.')
# TODO: add lock checking here
try: request=xmlcmds.PropPatch(REQUEST)
except: raise 'Bad Request', 'Invalid xml request.'
result=request.apply(self)
RESPONSE.setStatus(207)
RESPONSE.setHeader('Content-Type', 'text/xml; charset="utf-8"')
RESPONSE.setBody(result)
return RESPONSE
def MKCOL(self, REQUEST, RESPONSE):
"""Create a new collection resource. If called on an existing
resource, MKCOL must fail with 405 (Method Not Allowed)."""
self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
def COPY(self, REQUEST, RESPONSE):
"""Create a duplicate of the source resource whose state
and behavior match that of the source resource as closely
as possible. Though we may later try to make a copy appear
seamless across namespaces (e.g. from Zope to Apache), COPY
is currently only supported within the Zope namespace."""
self.init_headers(RESPONSE)
if not hasattr(aq_base(self), 'cb_isCopyable') or \
not self.cb_isCopyable():
raise 'Method Not Allowed', 'This object may not be copied.'
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
depth=REQUEST.get_header('Depth', 'infinity')
dest=REQUEST.get_header('Destination', '')
if not dest: raise 'Bad Request', 'No destination given'
flag=REQUEST.get_header('Overwrite', 'F')
flag=string.upper(flag)
body=REQUEST.get('BODY', '')
path, name=os.path.split(dest)
try: parent=REQUEST.resolve_url(path)
except ValueError:
raise 'Conflict', 'Attempt to copy to an unknown namespace.'
except 'Not Found':
raise 'Conflict', 'The resource %s must exist.' % path
except: raise sys.exc_type, sys.exc_value
if hasattr(parent, '_isNullResource'):
raise 'Conflict', 'The resource %s must exist.' % path
if self.dav__is_acquired(parent):
raise 'Conflict', 'The resource %s must exist.' % path
existing=hasattr(aq_base(parent), name)
if existing and flag=='F':
raise 'Precondition Failed', 'Resource %s exists.' % dest
try: parent._checkId(name, allow_dup=1)
except: raise 'Forbidden', sys.exc_value
try: parent._verifyObjectPaste(self, REQUEST)
except: raise 'Forbidden', sys.exc_value
try: self._notifyOfCopyTo(parent, op=0)
except: raise 'Forbidden', sys.exc_value
ob=self._getCopy(parent)
ob._setId(name)
parent._setObject(name, ob)
ob=ob.__of__(parent)
ob._postCopy(parent, op=0)
RESPONSE.setStatus(existing and 204 or 201)
if not existing: RESPONSE.setHeader('Location', dest)
RESPONSE.setBody('')
return RESPONSE
def MOVE(self, REQUEST, RESPONSE):
"""Move a resource to a new location. Though we may later try to
make a move appear seamless across namespaces (e.g. from Zope
to Apache), MOVE is currently only supported within the Zope
namespace."""
self.init_headers(RESPONSE)
if not hasattr(aq_base(self), 'cb_isMoveable') or \
not self.cb_isMoveable():
raise 'Method Not Allowed', 'This object may not be moved.'
if self.dav__is_acquired():
raise 'Not Found', 'The requested resource does not exist.'
dest=REQUEST.get_header('Destination', '')
if not dest: raise 'Bad Request', 'No destination given'
flag=REQUEST.get_header('Overwrite', 'F')
flag=string.upper(flag)
body=REQUEST.get('BODY', '')
path, name=os.path.split(dest)
try: parent=REQUEST.resolve_url(path)
except ValueError:
raise 'Conflict', 'Attempt to move to an unknown namespace.'
except 'Not Found':
raise 'Conflict', 'The resource %s must exist.' % path
except: raise sys.exc_type, sys.exc_value
if hasattr(parent, '_isNullResource'):
raise 'Conflict', 'The resource %s must exist.' % path
if self.dav__is_acquired(parent):
raise 'Conflict', 'The resource %s must exist.' % path
existing=hasattr(aq_base(parent), name)
if existing and flag=='F':
raise 'Precondition Failed', 'Resource %s exists.' % dest
try: parent._checkId(name, allow_dup=1)
except: raise 'Forbidden', sys.exc_value
try: parent._verifyObjectPaste(self, REQUEST)
except: raise 'Forbidden', sys.exc_value
try: self._notifyOfCopyTo(parent, op=1)
except: raise 'Forbidden', sys.exc_value
ob=aq_base(self._getCopy(parent))
self.aq_parent._delObject(absattr(self.id))
ob._setId(name)
parent._setObject(name, ob)
ob=ob.__of__(parent)
ob._postCopy(parent, op=1)
RESPONSE.setStatus(existing and 204 or 201)
if not existing: RESPONSE.setHeader('Location', dest)
RESPONSE.setBody('')
return RESPONSE
# Class 2 support
def LOCK(self, REQUEST, RESPONSE):
"""A write lock MUST prevent a principal without the lock from
successfully executing a PUT, POST, PROPPATCH, LOCK, UNLOCK, MOVE,
DELETE, or MKCOL on the locked resource. All other current methods,
GET in particular, function independently of the lock.
"""
self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
def UNLOCK(self):
"""Remove an existing lock on a resource."""
self.init_headers(RESPONSE)
raise 'Method Not Allowed', 'Method not supported for this resource.'
class Lock:
"""A WebDAV lock object"""
def __init__(self, token, owner, scope='exclusive', type='write',
depth='infinity', timeout='Infinite'):
self.token=token
self.owner=owner
self.scope=scope
self.type=type
self.depth=depth
self.timeout=timeout
def dav__activelock(self):
txt='<d:activelock>\n' \
'<d:locktype><d:%(type)s/></d:locktype>\n' \
'<d:lockscope><d:%(scope)s/></d:lockscope>\n' \
'<d:depth>%(depth)s</d:depth>\n' \
'<d:owner>%(owner)s</d:owner>\n' \
'<d:timeout>%(timeout)s</d:timeout>\n' \
'<d:locktoken>\n' \
'<d:href>opaquelocktoken:%(token)s</d:href>\n' \
'</d:locktoken>\n' \
'</d:activelock>\n' % self.__dict__
def absattr(attr):
if callable(attr):
return attr()
return attr
def aq_base(ob):
if hasattr(ob, 'aq_base'):
return ob.aq_base
return ob
def rfc1123_date(ts=None):
# Return an RFC 1123 format date string, required for
# use in HTTP Date headers per the HTTP 1.1 spec.
if ts is None: ts=time.time()
ts=time.asctime(time.gmtime(ts))
ts=string.split(ts)
return '%s, %s %s %s %s GMT' % (ts[0],ts[2],ts[1],ts[3],ts[4])
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
"""The webdav package provides WebDAV class 1 functionality within
the Zope environment. Based on RFC 2518."""
__version__='$Revision: 1.1 $'[11:-2]
# Hacks to inject WebDAV support into standard Zope classes.
from NullResource import NullResource
def folder__getitem__(self, key):
if hasattr(self, 'REQUEST'):
method=self.REQUEST.get('REQUEST_METHOD', 'GET')
if not method in ('GET', 'POST'):
return NullResource(self, key).__of__(self)
raise KeyError, key
def document_put(self, REQUEST, RESPONSE):
"""Handle HTTP PUT requests."""
self.init_headers(RESPONSE)
type=REQUEST.get_header('content-type', None)
body=REQUEST.get('BODY', '')
self._validateProxy(REQUEST)
self.munge(body)
self.on_update()
RESPONSE.setStatus(204)
return RESPONSE
def image_put(self, REQUEST, RESPONSE):
"""Handle HTTP PUT requests"""
self.init_headers(RESPONSE)
type=REQUEST.get_header('content-type', None)
body=REQUEST.get('BODY', '')
if type is None:
type, enc=mimetypes.guess_type(self.id())
if type is None:
if content_types.find_binary(body) >= 0:
type='application/octet-stream'
else: type=content_types.text_type(body)
type=lower(type)
self.update_data(body, type)
RESPONSE.setStatus(204)
return RESPONSE
import OFS.SimpleItem, Resource
class Item(OFS.SimpleItem.Item, Resource.Resource):
pass
Item.__module__='OFS.SimpleItem'
OFS.SimpleItem.Item=Item
class Item_w__name__(OFS.SimpleItem.Item_w__name__, Resource.Resource):
pass
Item_w__name__.__module__='OFS.SimpleItem'
OFS.SimpleItem.Item_w__name__=Item_w__name__
import OFS.Folder, Collection
class Folder(OFS.Folder.Folder, Collection.Collection):
pass
Folder.__module__='OFS.Folder'
OFS.Folder.Folder=Folder
OFS.Folder.Folder.__getitem__=folder__getitem__
import OFS.DTMLDocument, OFS.DTMLMethod, OFS.Image
OFS.DTMLMethod.DTMLMethod.PUT=document_put
OFS.DTMLDocument.DTMLDocument.PUT=document_put
OFS.Image.Image.PUT=image_put
OFS.Image.File.PUT=image_put
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
"""WebDAV XML request objects."""
__version__='$Revision: 1.1 $'[11:-2]
import sys, os, string, xmllib
from xmltools import XmlParser
from cStringIO import StringIO
zope_id='http://www.zope.org/propsets/default'
dav_id='DAV:'
def compact(self, data):
root=XmlParser().parse(data)
class PropFind:
def __init__(self, request):
self.request=request
data=request.get('BODY', '')
self.depth=request.get_header('Depth', 'infinity')
self.allprop=(not len(data))
self.propname=0
self.propnames=[]
self.parse(data)
def parse(self, data):
if not data: return
root=XmlParser().parse(data)
e=root.elements('propfind', ns=dav_id)[0]
if e.elements('allprop', ns=dav_id):
self.allprop=1
return
if e.elements('propname', ns=dav_id):
self.propname=1
return
prop=e.elements('prop', ns=dav_id)[0]
for val in prop.elements():
self.propnames.append((val.name(), val.namespace()))
return
def apply(self, obj, url=None, depth=0, result=None, top=1):
if result is None:
result=StringIO()
depth=self.depth
url=self.request['URL']
if url[-9:]=='/PROPFIND':
url=url[:-9]
result.write('<?xml version="1.0" encoding="utf-8"?>\n' \
'<d:multistatus xmlns:d="DAV:" ' \
'xmlns:z="%s">\n' % zope_id)
iscol=hasattr(aq_base(obj), 'isAnObjectManager') and \
obj.isAnObjectManager
if iscol and url[-1] != '/': url=url+'/'
result.write('<d:response>\n<d:href>%s</d:href>\n' % url)
if hasattr(obj, '__propsets__'):
for ps in obj.propertysheets.items():
if hasattr(aq_base(ps), 'dav__propstat'):
stat=ps.dav__propstat(self.allprop, self.propnames)
result.write(stat)
result.write('</d:response>\n')
if depth in ('1', 'infinity') and iscol:
for ob in obj.objectValues():
dflag=hasattr(ob, '_p_changed') and (ob._p_changed == None)
if hasattr(ob, '__dav_resource__'):
uri=os.path.join(url, absattr(ob.id))
depth=depth=='infinity' and depth or 0
self.apply(ob, uri, depth, result, top=0)
if dflag: ob._p_deactivate()
if not top: return result
result.write('</d:multistatus>')
return result.getvalue()
class PropPatch:
def __init__(self, request):
self.request=request
data=request.get('BODY', '')
self.values=[]
self.parse(data)
def parse(self, data):
root=XmlParser().parse(data)
e=root.elements('propertyupdate', ns=dav_id)[0]
for ob in e.elements():
if ob.name()=='set' and ob.namespace()==dav_id:
prop=ob.elements('prop', ns=dav_id)[0]
for val in prop.elements():
# We have to ensure that all tag attrs (including
# an xmlns attr for all xml namespaces used by the
# element and its children) are saved, per rfc2518.
attrs={}
val.remap({})
for attr in val.attrs():
md[attr.name()]=attr.value()
md={'attrs':attrs, 'nsid': val.__nskey__}
item=(val.name(), val.namespace(), val.strval(), md)
self.values.append(item)
if ob.name()=='remove' and ob.namespace()==dav_id:
prop=ob.elements('prop', ns=dav_id)[0]
for val in prop.elements():
item=(val.name(), val.namespace())
self.values.append(item)
def apply(self, obj):
url=self.request['URL']
if url[-10:]=='/PROPPATCH':
url=url[:-10]
if hasattr(aq_base(obj), 'isAnObjectManager') and \
obj.isAnObjectManager and url[-1] != '/':
url=url+'/'
result=StringIO()
errors=[]
result.write('<?xml version="1.0" encoding="utf-8"?>\n' \
'<d:multistatus xmlns:d="DAV:" xmlns:z="%s">\n' \
'<d:response>\n' \
'<d:href>%s</d:href>\n' % (zope_id, url))
propsets=obj.propertysheets
for value in self.values:
status='200 OK'
if len(value) > 2:
name, ns, val, md=value
propset=propsets.get(ns, None)
if propset is None:
obj.propertysheets.manage_addPropertySheet('', ns)
propsets=obj.propertysheets.items()
propset=propsets.get(ns)
if propset.hasProperty(name):
try: propset._updateProperty(name, val, meta=md)
except:
errors.append(str(sys.exc_value))
status='409 Conflict'
else:
try: propset._setProperty(name, val, meta=md)
except:
errors.append(str(sys.exc_value))
status='409 Conflict'
else:
name, ns=value
propset=propsets.get(ns, None)
if propset is None or not propset.hasProperty(name):
errors.append('Property not found: %s' % name)
status='404 Not Found'
else:
try: propset._delProperty(name)
except:
errors.append('%s cannot be deleted.' % name)
status='409 Conflict'
if result != '200 OK': abort=1
result.write('<d:propstat xmlns:ps="%s">\n' \
' <d:prop>\n' \
' <ps:%s/>\n' \
' </d:prop>\n' \
' <d:status>HTTP/1.1 %s</d:status>\n' \
'</d:propstat>\n' % (ns, name, status))
errmsg=string.join(errors, '\n') or 'The operation succeded.'
result.write('<d:responsedescription>\n' \
'%s\n' \
'</d:responsedescription>\n' \
'</d:response>\n' \
'</d:multistatus>' % errmsg)
result=result.getvalue()
if not errors: return result
get_transaction().abort()
result=string.replace(result, '200 OK', '424 Failed Dependency')
return result
class Lock:
def __init__(self, data):
self.scope='exclusive'
self.type='write'
self.owner=''
self.parse(data)
def parse(self, data):
root=XmlParser().parse(data)
info=root.elements('lockinfo', ns=dav_id)[0]
ls=info.elements('lockscope', ns=dav_id)[0]
self.scope=ls.elements()[0].name()
lt=info.elements('locktype', ns=dav_id)[0]
self.type=lt.elements()[0].name()
lo=info.elements('owner', ns=dav_id)
if lo: self.owner=lo[0].toxml()
def absattr(attr):
if callable(attr):
return attr()
return attr
def aq_base(ob):
if hasattr(ob, 'aq_base'):
return ob.aq_base
return ob
propfind_xml="""<?xml version="1.0" encoding="utf-8" ?>
<d:propfind xmlns:d="DAV:">
<d:prop xmlns:z="http://www.zope.org/propsets/default">
<z:title/>
<z:author/>
<z:content_type/>
</d:prop>
</d:propfind>
"""
rem_xml="""<?xml version="1.0" encoding="utf-8"?>
<d:propertyupdate xmlns:d="DAV:"
xmlns:z="http://www.zope.org/propsets/default">
<d:remove>
<d:prop>
<z:author/>
<z:title/>
</d:prop>
</d:remove>
</d:propertyupdate>
"""
proppatch_xml="""<?xml version="1.0" encoding="utf-8" ?>
<d:propertyupdate xmlns:d="DAV:"
xmlns:z="http://www.w3.com/standards/z39.50/">
<d:set>
<d:prop>
<z:authors>
<z:Author>Jim Whitehead</z:Author>
<z:Author>Roy Fielding</z:Author>
</z:authors>
</d:prop>
</d:set>
<d:remove>
<d:prop><z:Copyright-Owner/></d:prop>
</d:remove>
</d:propertyupdate>
"""
lock_xml="""<?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://www.ics.uci.edu/~ejw/contact.html</D:href>
</D:owner>
</D:lockinfo>
"""
multistatus_xml="""<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:">
<response xmlns:z="http://www.zope.org/dav/">
<href>http://www.foo.bar/container/</href>
<propstat>
<prop xmlns:R="http://www.foo.bar/boxschema/">
<R:bigbox z:type="int"/>
<R:author/>
<creationdate/>
<displayname/>
<resourcetype/>
<supportedlock/>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
<response>
<href>http://www.foo.bar/container/front.html</href>
<propstat>
<prop xmlns:R="http://www.foo.bar/boxschema/">
<R:bigbox/>
<creationdate/>
<displayname/>
<getcontentlength/>
<getcontenttype/>
<getetag/>
<getlastmodified/>
<resourcetype/>
<supportedlock/>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
</multistatus>
"""
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
"""WebDAV XML parsing tools."""
__version__='$Revision: 1.1 $'[11:-2]
import sys, os, string, xmllib
from Acquisition import Implicit
from cStringIO import StringIO
type_document=0
type_element=1
type_attribute=2
type_text=3
type_cdata=4
type_entityref=5
type_entity=6
type_procinst=7
type_comment=8
type_notation=9
class Node(Implicit):
"""Common base class for Node objects."""
__name__=''
__value__=''
__attrs__=[]
__nodes__=[]
__nskey__=''
def name(self): return self.__name__
def attrs(self): return self.__attrs__
def value(self): return self.__value__
def nodes(self): return self.__nodes__
def nskey(self): return self.__nskey__
def addNode(self, node):
self.__nodes__.append(node.__of__(self))
def namespace(self):
nskey=self.__nskey__
while 1:
if hasattr(self, '__nsdef__'):
val=self.__nsdef__.get(nskey, None)
if val is not None: return val
if not hasattr(self, 'aq_parent'):
return ''
self=self.aq_parent
def elements(self, name=None, ns=None, lower=string.lower):
nodes=[]
name=name and lower(name)
for node in self.__nodes__:
if node.__type__==type_element and \
((name is None) or (lower(node.__name__)==name)) and \
((ns is None) or (node.namespace()==ns)):
nodes.append(node)
return nodes
def __getitem__(self, n):
return self.__nodes__[n]
def qname(self):
ns=self.__nskey__
if ns: ns='%s:' % ns
return '%s%s' % (ns, self.__name__)
def toxml(self):
return self.__value__
def strval(self):
return self.toxml()
class Document(Node):
def __init__(self, encoding='utf-8', stdalone=''):
self.__name__ ='document'
self.__nodes__=[]
self.encoding=encoding
self.stdalone=stdalone
self.document=self
def toxml(self):
result=['<?xml version="1.0" encoding="%s"?>' % self.encoding]
for node in self.__nodes__:
result.append(node.toxml())
return string.join(result, '')
def __del__(self):
self.document=None
print 'bye!'
class Element(Node):
__type__=type_element
def __init__(self, name, attrs={}):
self.__name__ =name
self.__attrs__=[]
self.__nodes__=[]
self.__nsdef__={}
self.__nskey__=''
for name, val in attrs.items():
attr=Attribute(name, val)
self.__attrs__.append(attr)
self.ns_parse()
parts=string.split(self.__name__, ':')
if len(parts) > 1:
self.__nskey__=parts[0]
self.__name__=string.join(parts[1:], ':')
def ns_parse(self):
nsdef=self.__nsdef__={}
for attr in self.attrs():
name, val=attr.name(), attr.value()
key=string.lower(name)
if key[:6]=='xmlns:':
nsdef[name[6:]]=val
elif key=='xmlns':
nsdef['']=val
def fixup(self):
self.__attrs__=map(lambda n, s=self: n.__of__(s), self.__attrs__)
def get_attr(self, name, ns=None, default=None):
for attr in self.__attrs__:
if attr.name()==name and (ns is None) or (ns==attr.namespace()):
return attr
return default
def remap(self, dict, n=0, top=1):
# The remap method effectively rewrites an element and all of its
# children, consolidating namespace declarations into the element
# on which the remap function is called and fixing up namespace
# lookup structures.
nsval=self.namespace()
if not nsval: nsid=''
elif not dict.has_key(nsval):
nsid='ns%d' % n
dict[nsval]=nsid
n=n+1
else: nsid=dict[nsval]
for attr in self.__attrs__:
dict, n=attr.remap(dict, n, 0)
for node in self.elements():
dict, n=node.remap(dict, n, 0)
attrs=[]
for attr in self.__attrs__:
name=attr.__name__
if not (((len(name) >= 6) and (name[:6]=='xmlns:')) or \
name=='xmlns'):
attrs.append(attr)
self.__attrs__=attrs
self.__nsdef__={}
self.__nskey__=nsid
if top:
attrs=self.__attrs__
keys=dict.keys()
keys.sort()
for key in keys:
attr=Attribute('xmlns:%s' % dict[key], key)
attrs.append(attr.__of__(self))
self.__attrs__=attrs
self.ns_parse()
return dict, n
def toxml(self):
qname=self.qname()
result=['<%s' % qname]
for attr in self.__attrs__:
result.append(attr.toxml())
if not self.__value__ and not self.__nodes__:
result.append('/>')
else:
result.append('>')
for node in self.__nodes__:
result.append(node.toxml())
result.append('</%s>' % qname)
return string.join(result, '')
def strval(self, top=1):
if not self.__value__ and not self.__nodes__:
return ''
result=map(lambda n: n.toxml(), self.__nodes__)
return string.join(result, '')
class Attribute(Node):
__type__=type_attribute
def __init__(self, name, val):
self.__name__=name
self.__value__=val
self.__nskey__=''
parts=string.split(name, ':')
if len(parts) > 1:
pre=string.lower(parts[0])
if not (pre in ('xml', 'xmlns')):
self.__nskey__=parts[0]
self.__name__=string.join(parts[1:], ':')
def remap(self, dict, n=0, top=1):
nsval=self.namespace()
if not nsval: nsid=''
elif not dict.has_key(nsval):
nsid='ns%d' % n
dict[nsval]=nsid
n=n+1
else: nsid=dict[nsval]
self.__nskey__=nsid
return dict, n
def toxml(self):
ns=self.__nskey__
if ns: ns='%s:' % ns
return ' %s%s="%s"' % (ns, self.__name__, self.__value__)
class Text(Node):
__name__='#text'
__type__=type_text
def __init__(self, val):
self.__value__=val
def toxml(self):
return escape(self.__value__)
class CData(Node):
__type__=type_cdata
__name__='#cdata'
def __init__(self, val):
self.__value__=val
def toxml(self):
return '<![CDATA[%s]]>' % self.__value__
class EntityRef(Node):
__name__='#entityref'
__type__=type_entityref
def __init__(self, val):
self.__value__=val
def toxml(self):
return '&%s;' % self.__value__
class Entity(Node):
__name__='#entity'
__type__=type_entity
def __init__(self, name, pubid, sysid, nname):
self.__value__=val
def toxml(self):
return ''
class ProcInst(Node):
__type__=type_procinst
def __init__(self, name, val):
self.__name__=name
self.__value__=val
def toxml(self):
return '<?%s %s?>' % (self.__name__, self.__value__)
class Comment(Node):
__name__='#comment'
__type__=type_comment
def __init__(self, val):
self.__value__=val
def toxml(self):
return '<!--%s-->' % self.__value__
class XmlParser(xmllib.XMLParser):
def __init__(self):
xmllib.XMLParser.__init__(self)
self.root=None
self.node=None
def parse(self, data):
self.feed(data)
self.close()
return self.root
def add(self, node):
self.node.addNode(node)
def push(self, node):
self.node.addNode(node)
self.node=self.node.__nodes__[-1]
def pop(self):
self.node=self.node.aq_parent
def unknown_starttag(self, name, attrs):
node=Element(name, attrs)
self.push(node)
# Fixup aq chain!
self.node.fixup()
def unknown_endtag(self, name):
self.pop()
def handle_xml(self, encoding, stdalone):
self.root=Document(encoding, stdalone)
self.node=self.root
def handle_doctype(self, tag, pubid, syslit, data):
pass
def handle_entity(self, name, strval, pubid, syslit, ndata):
self.add(Entity(name, strval, pubid, syslit, ndata))
def handle_cdata(self, data):
self.add(CData(data))
def handle_proc(self, name, data):
self.add(ProcInst(name, data))
def handle_comment(self, data):
self.add(Comment(data))
def handle_data(self, data):
self.add(Text(data))
def unknown_entityref(self, data):
self.add(EntityRef(data))
def escape(data, entities={}):
# snarfed from xml.util...
data = string.replace(data, "&", "&amp;")
data = string.replace(data, "<", "&lt;")
data = string.replace(data, ">", "&gt;")
for chars, entity in entities.items():
data = string.replace(data, chars, entity)
return data
def remap(data):
root=XmlParser().parse(data)
dict={'DAV:':'d', 'http://www.zope.org/propsets/default':'z'}
root.elements()[0].remap(dict, 0)
return root.toxml()
def remap_test():
file=open('big.xml','r')
data=file.read()
file.close()
data=remap(data)
file=open('new.xml','w')
file.write(data)
file.close()
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