Traversable.py 5.64 KB
Newer Older
1
##############################################################################
matt@zope.com's avatar
matt@zope.com committed
2 3
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4
#
matt@zope.com's avatar
matt@zope.com committed
5 6 7 8 9 10
# 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
11
#
12 13 14
##############################################################################
'''This module implements a mix-in for traversable objects.

15 16
$Id: Traversable.py,v 1.21 2003/09/29 12:34:38 andreasjung Exp $'''
__version__='$Revision: 1.21 $'[11:-2]
17 18


19
from Acquisition import Acquired, aq_inner, aq_parent, aq_base
20
from AccessControl import getSecurityManager
21
from AccessControl import Unauthorized
22
from AccessControl.ZopeGuards import guarded_getattr
23
from ZODB.POSException import ConflictError
24
from urllib import quote
25

26 27
NotFound = 'NotFound'

28 29 30 31
_marker=[]

class Traversable:

32
    absolute_url__roles__=None # Public
33
    def absolute_url(self, relative=0):
34 35 36 37 38
        '''Return a canonical URL for this object based on its
        physical containment path, possibly modified by virtual hosting.
        If the optional 'relative' argument is true, only return the
        path portion of the URL.'''
        spp = self.getPhysicalPath()
39
        try:
40
            toUrl = self.REQUEST.physicalPathToURL
41
        except AttributeError:
42
            return '/'.join(map(quote, spp[1:]))
43
        if relative:
44 45 46
            # Remove leading slash for backward compatibility sake.
            return toUrl(spp, relative)[1:]
        return toUrl(spp)
47

48
    getPhysicalRoot__roles__=() # Private
49
    getPhysicalRoot=Acquired
50 51

    getPhysicalPath__roles__=None # Public
52 53 54 55 56 57
    def getPhysicalPath(self):
        '''Returns a path (an immutable sequence of strings)
        that can be used to access this object again
        later, for example in a copy/paste operation.  getPhysicalRoot()
        and getPhysicalPath() are designed to operate together.
        '''
58
        path = (self.getId(),)
59

Andreas Jung's avatar
Andreas Jung committed
60
        p = aq_parent(aq_inner(self))
61
        if p is not None:
62
            path = p.getPhysicalPath() + path
63 64 65

        return path

66
    unrestrictedTraverse__roles__=() # Private
67 68 69 70 71
    def unrestrictedTraverse(self, path, default=_marker, restricted=0):

        if not path: return self

        get=getattr
72
        has=hasattr
73 74 75
        N=None
        M=_marker

76
        if isinstance(path, str): path = path.split('/')
77 78 79 80 81
        else: path=list(path)

        REQUEST={'TraversalRequestNameStack': path}
        path.reverse()
        pop=path.pop
82 83 84 85 86

        if len(path) > 1 and not path[0]:
            # Remove trailing slash
            path.pop(0)

87
        if restricted: securityManager=getSecurityManager()
88
        else: securityManager=N
89 90 91 92 93

        if not path[-1]:
            # If the path starts with an empty string, go to the root first.
            pop()
            self=self.getPhysicalRoot()
94
            if (restricted and not securityManager.validateValue(self)):
95
                raise Unauthorized, name
96

97 98 99 100
        try:
            object = self
            while path:
                name=pop()
101
                __traceback_info__ = path, name
102 103 104

                if name[0] == '_':
                    # Never allowed in a URL.
105
                    raise NotFound, name
106 107 108 109

                if name=='..':
                    o=getattr(object, 'aq_parent', M)
                    if o is not M:
110
                        if (restricted and not securityManager.validate(
111
                            object, object,name, o)):
112
                            raise Unauthorized, name
113 114 115 116 117 118
                        object=o
                        continue

                t=get(object, '__bobo_traverse__', N)
                if t is not N:
                    o=t(REQUEST, name)
119 120 121

                    if restricted:
                        container = N
122 123 124 125 126
                        if aq_base(o) is not o:
                            # The object is wrapped, so the acquisition
                            # context determines the container.
                            container = aq_parent(aq_inner(o))
                        elif has(o, 'im_self'):
127 128 129 130 131 132
                            container = o.im_self
                        elif (has(get(object, 'aq_base', object), name)
                              and get(object, name) == o):
                            container = object
                        if (not securityManager.validate(object,
                                                         container, name, o)):
133
                            raise Unauthorized, name
134

135
                else:
136 137
                    if restricted:
                        o = guarded_getattr(object, name, M)
138
                    else:
139 140
                        o = get(object, name, M)
                    if o is M:
141 142 143
                        try:
                            o=object[name]
                        except AttributeError:
144 145
                            # Raise NotFound for easier debugging
                            raise NotFound, name
146
                        if (restricted and not securityManager.validate(
147
                            object, object, N, o)):
148
                            raise Unauthorized, name
149 150 151 152 153

                object=o

            return object

154
        except ConflictError: raise
155 156 157 158
        except:
            if default==_marker: raise
            return default

159 160
    restrictedTraverse__roles__=None # Public
    def restrictedTraverse(self, path, default=_marker):
161
        return self.unrestrictedTraverse(path, default, restricted=1)