Commit c75f735c authored by Evan Simpson's avatar Evan Simpson

Merge Before-traverse-Dev-branch

parent dbf73423
...@@ -89,8 +89,8 @@ Aqueduct database adapters, etc. ...@@ -89,8 +89,8 @@ Aqueduct database adapters, etc.
This module can also be used as a simple template for implementing new This module can also be used as a simple template for implementing new
item types. item types.
$Id: SimpleItem.py,v 1.75 2000/06/01 17:29:37 jim Exp $''' $Id: SimpleItem.py,v 1.76 2000/06/09 23:57:46 evan Exp $'''
__version__='$Revision: 1.75 $'[11:-2] __version__='$Revision: 1.76 $'[11:-2]
import regex, sys, Globals, App.Management, Acquisition, App.Undo import regex, sys, Globals, App.Management, Acquisition, App.Undo
import AccessControl.Role, AccessControl.Owned, App.Common import AccessControl.Role, AccessControl.Owned, App.Common
...@@ -338,14 +338,19 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, ...@@ -338,14 +338,19 @@ class Item(Base, Resource, CopySource, App.Management.Tabs,
return 1 return 1
def absolute_url(self, relative=0): def absolute_url(self, relative=0):
id=quote(self.id) req = self.REQUEST
rpp = req.get('VirtualRootPhysicalPath', ('',))
p=getattr(self,'aq_inner', None) spp = self.getPhysicalPath()
if p is not None: i = 0
url=p.aq_parent.absolute_url(relative) for name in rpp[:len(spp)]:
if url: id=url+'/'+id if spp[i] == name:
i = i + 1
return id else:
break
path = map(quote, spp[i:])
if relative: # Deprecated - use getPhysicalPath
return join(path, '/')
return join([req['SERVER_URL']] + req._script + path, '/')
def getPhysicalPath(self): def getPhysicalPath(self):
'''Returns a path (an immutable sequence of strings) '''Returns a path (an immutable sequence of strings)
...@@ -462,14 +467,20 @@ class Item_w__name__(Item): ...@@ -462,14 +467,20 @@ class Item_w__name__(Item):
self.__name__=id self.__name__=id
def absolute_url(self, relative=0): def absolute_url(self, relative=0):
id=quote(self.__name__) req = self.REQUEST
rpp = req.get('VirtualRootPhysicalPath', ('',))
p=getattr(self,'aq_inner', None) spp = self.getPhysicalPath()
if p is not None: i = 0
url=p.aq_parent.absolute_url(relative) for name in rpp[:len(spp)]:
if url: id=url+'/'+id if spp[i] == name:
i = i + 1
return id else:
break
path = map(quote, spp[i:])
if relative:
# This is useful for physical path relative to a VirtualRoot
return join(path, '/')
return join([req['SERVER_URL']] + req._script + path, '/')
def getPhysicalPath(self): def getPhysicalPath(self):
'''Returns a path (an immutable sequence of strings) '''Returns a path (an immutable sequence of strings)
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
# attributions are listed in the accompanying credits file. # attributions are listed in the accompanying credits file.
# #
############################################################################## ##############################################################################
__version__='$Revision: 1.26 $'[11:-2] __version__='$Revision: 1.27 $'[11:-2]
from string import join, split, find, rfind, lower, upper from string import join, split, find, rfind, lower, upper
from urllib import quote from urllib import quote
...@@ -258,16 +258,11 @@ class BaseRequest: ...@@ -258,16 +258,11 @@ class BaseRequest:
method='index_html' method='index_html'
else: baseflag=1 else: baseflag=1
URL=request['URL']
parents=request['PARENTS'] parents=request['PARENTS']
object=parents[-1] object=parents[-1]
del parents[:] del parents[:]
try: roles = getattr(object, '__roles__', UNSPECIFIED_ROLES)
# We build parents in the wrong order, so we
# need to make sure we reverse it when we're doe.
roles=getattr(object,'__roles__', UNSPECIFIED_ROLES)
# if the top object has a __bobo_traverse__ method, then use it # if the top object has a __bobo_traverse__ method, then use it
# to possibly traverse to an alternate top-level object. # to possibly traverse to an alternate top-level object.
...@@ -275,54 +270,63 @@ class BaseRequest: ...@@ -275,54 +270,63 @@ class BaseRequest:
try: object=object.__bobo_traverse__(request) try: object=object.__bobo_traverse__(request)
except: pass except: pass
# Get default object if no path was specified: if not path and not method:
if not path: return response.forbiddenError(self['URL'])
if not method: return response.forbiddenError(entry_name)
entry_name=method
try:
if hasattr(object,entry_name):
response.setBase(URL)
path=[entry_name]
else:
try:
if object.has_key(entry_name):
path=[entry_name]
except: pass
except: pass
# Traverse the URL to find the object: # Traverse the URL to find the object:
if hasattr(object, '__of__'): if hasattr(object, '__of__'):
# Try to bind the top-level object to the request # Try to bind the top-level object to the request
# This is how you get 'self.REQUEST' # This is how you get 'self.REQUEST'
object=object.__of__(RequestContainer(REQUEST=request)) object=object.__of__(RequestContainer(REQUEST=request))
parents.append(object)
steps=self.steps steps=self.steps
self._steps = _steps = map(quote, steps)
path.reverse() path.reverse()
pop=path.pop pop = path.pop
request['TraversalRequestNameStack'] = request.path = path
# request['path']=path
while path: entry_name = ''
entry_name=pop() try:
URL="%s/%s" % (URL,quote(entry_name)) # We build parents in the wrong order, so we
got=0 # Can't find it? XXX # need to make sure we reverse it when we're doe.
if entry_name: while 1:
bpth = getattr(object, '__before_publishing_traverse__', None)
if bpth is not None:
bpth(object, self)
# Check for method:
if path:
entry_name = pop()
elif (method and hasattr(object,method)
and entry_name != method
and getattr(object, method) is not None):
request._hacked_path=1
entry_name = method
else:
if (hasattr(object, '__call__') and
hasattr(object.__call__,'__roles__')):
roles=object.__call__.__roles__
if request._hacked_path:
i=rfind(URL,'/')
if i > 0: response.setBase(URL[:i])
break
if not entry_name: continue
step = quote(entry_name)
_steps.append(step)
request['URL'] = URL = '%s/%s' % (request['URL'], step)
got = 0
if entry_name[:1]=='_': if entry_name[:1]=='_':
if debug_mode: if debug_mode:
return response.debugError( return response.debugError(
"Object name begins with an underscore at: %s" "Object name begins with an underscore at: %s" % URL)
% URL)
else: return response.forbiddenError(entry_name) else: return response.forbiddenError(entry_name)
if hasattr(object,'__bobo_traverse__'): if hasattr(object,'__bobo_traverse__'):
request['URL']=URL
subobject=object.__bobo_traverse__(request,entry_name) subobject=object.__bobo_traverse__(request,entry_name)
if type(subobject) is type(()) and len(subobject) > 1: if type(subobject) is type(()) and len(subobject) > 1:
# Add additional parents into the path # Add additional parents into the path
while len(subobject) > 2: parents[-1:] = subobject[:-1]
parents.append(subobject[0]) object, subobject = subobject[-2:]
subobject=subobject[1:]
object, subobject = subobject
else: else:
try: try:
...@@ -348,8 +352,9 @@ class BaseRequest: ...@@ -348,8 +352,9 @@ class BaseRequest:
TypeError, AttributeError): TypeError, AttributeError):
if debug_mode: if debug_mode:
return response.debugError( return response.debugError(
"Cannot locate object at: %s" %URL) "Cannot locate object at: %s" % URL)
else: return response.notFoundError(URL) else:
return response.notFoundError(URL)
try: try:
try: doc=subobject.__doc__ try: doc=subobject.__doc__
...@@ -359,44 +364,22 @@ class BaseRequest: ...@@ -359,44 +364,22 @@ class BaseRequest:
if debug_mode: if debug_mode:
return response.debugError( return response.debugError(
"Missing doc string at: %s" % URL) "Missing doc string at: %s" % URL)
else: return response.notFoundError("%s" % (URL)) else: return response.notFoundError("%s" % URL)
r=getattr(subobject, '__roles__', UNSPECIFIED_ROLES) r = getattr(subobject, '__roles__', UNSPECIFIED_ROLES)
if r is not UNSPECIFIED_ROLES: if r is not UNSPECIFIED_ROLES:
roles=r roles = r
elif not got: elif not got:
roles=getattr(subobject, entry_name+'__roles__', roles) roles = getattr(subobject, entry_name+'__roles__', roles)
# Promote subobject to object # Promote subobject to object
parents.append(object)
object=subobject object=subobject
steps.append(entry_name)
# Check for method:
if not path:
if (method and hasattr(object,method)
and entry_name != method
and getattr(object, method) is not None
):
request._hacked_path=1
path.append(method)
else:
if (hasattr(object, '__call__') and
hasattr(object.__call__,'__roles__')):
roles=object.__call__.__roles__
if request._hacked_path:
i=rfind(URL,'/')
if i > 0: response.setBase(URL[:i])
except:
# Save the last found object before handling the exception.
if object is not None:
parents.append(object) parents.append(object)
parents.reverse()
raise
steps.append(entry_name)
finally:
parents.reverse() parents.reverse()
parents.pop(0) # Get rid of final method object
# Do authorization checks # Do authorization checks
user=groups=None user=groups=None
...@@ -459,11 +442,10 @@ class BaseRequest: ...@@ -459,11 +442,10 @@ class BaseRequest:
if user is None and roles != UNSPECIFIED_ROLES: if user is None and roles != UNSPECIFIED_ROLES:
response.unauthorized() response.unauthorized()
steps=join(steps[:-i],'/')
if user is not None: if user is not None:
if validated_hook is not None: validated_hook(self, user) if validated_hook is not None: validated_hook(self, user)
request['AUTHENTICATED_USER']=user request['AUTHENTICATED_USER']=user
request['AUTHENTICATION_PATH']=steps request['AUTHENTICATION_PATH']=join(steps[:-i],'/')
# Remove http request method from the URL. # Remove http request method from the URL.
request['URL']=URL request['URL']=URL
......
"""BeforeTraverse interface and helper classes"""
# Interface
def registerBeforeTraverse(container, object, app_handle, priority=99):
"""Register an object to be called before a container is traversed.
'app_handle' should be a string or other hashable value that
distinguishes the application of this object, and which must
be used in order to unregister the object.
If the container will be pickled, the object must be a callable class
instance, not a function or method.
'priority' is optional, and determines the relative order in which
registered objects will be called.
"""
btr = getattr(container, '__before_traverse__', {})
btr[(priority, app_handle)] = object
rewriteBeforeTraverse(container, btr)
def unregisterBeforeTraverse(container, app_handle):
"""Unregister a __before_traverse__ hook object, given its 'app_handle'.
Returns a list of unregistered objects."""
btr = getattr(container, '__before_traverse__', {})
objects = []
for k in btr.keys():
if k[1] == app_handle:
objects.append(btr[k])
del btr[k]
if objects:
rewriteBeforeTraverse(container, btr)
return objects
def queryBeforeTraverse(container, app_handle):
"""Find __before_traverse__ hook objects, given an 'app_handle'.
Returns a list of (priority, object) pairs."""
btr = getattr(container, '__before_traverse__', {})
objects = []
for k in btr.keys():
if k[1] == app_handle:
objects.append((k[0], btr[k]))
return objects
# Implementation tools
def rewriteBeforeTraverse(container, btr):
"""Rewrite the list of __before_traverse__ hook objects"""
container.__before_traverse__ = btr
hookname = '__before_publishing_traverse__'
dic = hasattr(container.__class__, hookname)
bpth = container.__dict__.get(hookname, None)
if isinstance(bpth, MultiHook):
bpth = bpth._prior
bpth = MultiHook(hookname, bpth, dic)
setattr(container, hookname, bpth)
keys = btr.keys()
keys.sort()
for key in keys:
bpth.add(btr[key])
class MultiHook:
"""Class used to multiplex hook.
MultiHook calls the named hook from the class of the container, then
the prior hook, then all the hooks in its list.
"""
def __init__(self, hookname, prior, defined_in_class):
self._hookname = hookname
self._prior = prior
self._defined_in_class = defined_in_class
self._list = []
def __call__(self, container, request):
if self._defined_in_class:
# Assume it's an unbound method
getattr(container.__class__, self._hookname)(container, request)
prior = self._prior
if prior is not None:
prior(container, request)
for cob in self._list:
cob(container, request)
def add(self, cob):
self._list.append(cob)
# Helper class
class NameCaller:
"""Class used to proxy sibling objects by name.
When called with a container and request object, it gets the named
attribute from the container and calls it. If the name is not
found, it fails silently.
>>> registerBeforeTraverse(folder, NameCaller('preop'), 'XApp')
"""
def __init__(self, name):
self.name = name
def __call__(self, container, request):
try:
meth = getattr(container, self.name)
except AttributeError:
return
args = getattr(getattr(meth, 'func_code', None), 'co_argcount', 2)
apply(meth, (container, request, None)[:args])
...@@ -83,14 +83,14 @@ ...@@ -83,14 +83,14 @@
# #
############################################################################## ##############################################################################
__version__='$Revision: 1.34 $'[11:-2] __version__='$Revision: 1.35 $'[11:-2]
import regex, sys, os, string import regex, sys, os, string, urllib
from string import lower, atoi, rfind, split, strip, join, upper, find from string import lower, atoi, rfind, split, strip, join, upper, find
from BaseRequest import BaseRequest from BaseRequest import BaseRequest
from HTTPResponse import HTTPResponse from HTTPResponse import HTTPResponse
from cgi import FieldStorage from cgi import FieldStorage
from urllib import quote, unquote from urllib import quote, unquote, splittype, splitport
from Converters import get_converter from Converters import get_converter
from maybe_lock import allocate_lock from maybe_lock import allocate_lock
xmlrpc=None # Placeholder for module that we'll import if we have to. xmlrpc=None # Placeholder for module that we'll import if we have to.
...@@ -120,6 +120,8 @@ hide_key={'HTTP_AUTHORIZATION':1, ...@@ -120,6 +120,8 @@ hide_key={'HTTP_AUTHORIZATION':1,
'HTTP_CGI_AUTHORIZATION': 1, 'HTTP_CGI_AUTHORIZATION': 1,
}.has_key }.has_key
default_port={'http': '80', 'https': '443'}
_marker=[] _marker=[]
class HTTPRequest(BaseRequest): class HTTPRequest(BaseRequest):
"""\ """\
...@@ -186,19 +188,46 @@ class HTTPRequest(BaseRequest): ...@@ -186,19 +188,46 @@ class HTTPRequest(BaseRequest):
return r return r
def setSite(self, base=None): def setServerURL(self, protocol=None, hostname=None, port=None):
""" blah """ """ Set the parts of generated URLs. """
if base is not None: other = self.other
self.script = self.base = base server_url = other.get('SERVER_URL', '')
if protocol is None and hostname is None and port is None:
# reset the URL return server_url
self.other['URL'] = self.script oldprotocol, oldhost = splittype(server_url)
# clear the 'cache' of URLx and BASEx oldhostname, oldport = splitport(oldhost[2:])
if protocol is None: protocol = oldprotocol
if hostname is None: hostname = oldhostname
if port is None: port = oldport
if (port is None or default_port[protocol] == port):
host = hostname
else:
host = hostname + ':' + port
server_url = other['SERVER_URL'] = '%s://%s' % (protocol, host)
self._resetURLS()
return server_url
def setVirtualRoot(self, path, hard=0):
""" Treat the current publishing object as a VirtualRoot """
other = self.other
if type(path) is type(''):
path = filter(None, split(path, '/'))
self._script[:] = map(quote, path)
del self._steps[:]
parents = other['PARENTS']
if hard:
del parents[:-1]
other['VirtualRootPhysicalPath'] = parents[-1].getPhysicalPath()
self._resetURLS()
def _resetURLS(self):
other = self.other
other['URL'] = join([other['SERVER_URL']] + self._script +
self._steps, '/')
for x in self._urls: for x in self._urls:
del self.other[x] del self.other[x]
self._urls = ()
del self.other['PARENTS'][:]
def __init__(self, stdin, environ, response, clean=0): def __init__(self, stdin, environ, response, clean=0):
self._orig_env=environ self._orig_env=environ
...@@ -221,11 +250,16 @@ class HTTPRequest(BaseRequest): ...@@ -221,11 +250,16 @@ class HTTPRequest(BaseRequest):
other=self.other={'RESPONSE': response} other=self.other={'RESPONSE': response}
self.form={} self.form={}
self.steps=[] self.steps=[]
self._steps=[]
################################################################ ################################################################
# Get base info first. This isn't likely to cause # Get base info first. This isn't likely to cause
# errors and might be useful to error handlers. # errors and might be useful to error handlers.
b=script=strip(get_env('SCRIPT_NAME','')) b=script=strip(get_env('SCRIPT_NAME',''))
# _script and the other _names are meant for URL construction
self._script = map(quote, filter(None, split(script, '/')))
while b and b[-1]=='/': b=b[:-1] while b and b[-1]=='/': b=b[:-1]
p = rfind(b,'/') p = rfind(b,'/')
if p >= 0: b=b[:p+1] if p >= 0: b=b[:p+1]
...@@ -234,23 +268,24 @@ class HTTPRequest(BaseRequest): ...@@ -234,23 +268,24 @@ class HTTPRequest(BaseRequest):
server_url=get_env('SERVER_URL',None) server_url=get_env('SERVER_URL',None)
if server_url is not None: if server_url is not None:
server_url=strip(server_url) other['SERVER_URL'] = server_url = strip(server_url)
else: else:
if have_env('HTTPS') and ( if have_env('HTTPS') and (
environ['HTTPS'] == "on" or environ['HTTPS'] == "ON"): environ['HTTPS'] == "on" or environ['HTTPS'] == "ON"):
server_url='https://' protocol = 'https'
elif (have_env('SERVER_PORT_SECURE') and elif (have_env('SERVER_PORT_SECURE') and
environ['SERVER_PORT_SECURE'] == "1"): environ['SERVER_PORT_SECURE'] == "1"):
server_url='https://' protocol = 'https'
else: server_url='http://' else: protocol = 'http'
if have_env('HTTP_HOST'): if have_env('HTTP_HOST'):
server_url=server_url+strip(environ['HTTP_HOST']) host = strip(environ['HTTP_HOST'])
hostname, port = splitport(host)
else: else:
server_url=server_url+strip(environ['SERVER_NAME']) hostname = strip(environ['SERVER_NAME'])
server_port=environ['SERVER_PORT'] port = environ['SERVER_PORT']
if server_port!='80': server_url=server_url+':'+server_port self.setServerURL(protocol=protocol, hostname=hostname, port=port)
other['SERVER_URL']=server_url server_url = other['SERVER_URL']
if server_url[-1:]=='/': server_url=server_url[:-1] if server_url[-1:]=='/': server_url=server_url[:-1]
...@@ -437,7 +472,7 @@ class HTTPRequest(BaseRequest): ...@@ -437,7 +472,7 @@ class HTTPRequest(BaseRequest):
reclist.reverse() reclist.reverse()
if not hasattr(x,attr): if not hasattr(x,attr):
#If the attribute does not #If the attribute does not
#exist, set it #exist, setit
if flags&SEQUENCE: item=[item] if flags&SEQUENCE: item=[item]
reclist.remove(x) reclist.remove(x)
setattr(x,attr,item) setattr(x,attr,item)
...@@ -722,13 +757,11 @@ class HTTPRequest(BaseRequest): ...@@ -722,13 +757,11 @@ class HTTPRequest(BaseRequest):
return other[key] return other[key]
if key[:1]=='U' and URLmatch(key) >= 0: if key[:1]=='U' and URLmatch(key) >= 0:
n=atoi(key[3:]) path = self._script + self._steps
URL=other['URL'] n = len(path) - atoi(key[3:])
for i in range(0,n): if n < 0:
l=rfind(URL,'/') raise KeyError, key
if l >= 0: URL=URL[:l] URL=join([other['SERVER_URL']] + path[:n], '/')
else: raise KeyError, key
if len(URL) < len(self.base) and n > 1: raise KeyError, key
other[key]=URL other[key]=URL
self._urls = self._urls + (key,) self._urls = self._urls + (key,)
return URL return URL
...@@ -743,20 +776,17 @@ class HTTPRequest(BaseRequest): ...@@ -743,20 +776,17 @@ class HTTPRequest(BaseRequest):
if key[:1]=='B': if key[:1]=='B':
if BASEmatch(key) >= 0: if BASEmatch(key) >= 0:
n=atoi(key[4:]) path = self._steps
n = atoi(key[4:])
if n: if n:
n=n-1 n = n - 1
if len(path) < n:
if len(self.steps) < n:
raise KeyError, key raise KeyError, key
v=self.script v = self._script + path[:n]
while v[-1:]=='/': v=v[:-1]
v=join([v]+self.steps[:n],'/')
else: else:
v=self.base v = self._script[:-1]
while v[-1:]=='/': v=v[:-1] other[key] = v = join([other['SERVER_URL']] + v, '/')
other[key]=v
self._urls = self._urls + (key,) self._urls = self._urls + (key,)
return v return v
...@@ -977,8 +1007,6 @@ def parse_cookie(text, ...@@ -977,8 +1007,6 @@ def parse_cookie(text,
return apply(parse_cookie,(text[l:],result)) return apply(parse_cookie,(text[l:],result))
import sys
# add class # add class
class record: class record:
def __getattr__(self, key, default=None): def __getattr__(self, key, default=None):
......
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