Commit 17b5a358 authored by Hanno Schlichting's avatar Hanno Schlichting

Merged philikon-aq branch into trunk, yeah!

parents d6687526 010ab27a
...@@ -78,7 +78,11 @@ Zope Changes ...@@ -78,7 +78,11 @@ Zope Changes
Features added Features added
- Zope2 startup: Zope will now send DatabaseOpened and - Acquisition has been made aware of __parent__ pointers. This allows
direct access to many Zope 3 classes without the need to mixin
Acquisition base classes for the security to work.
- Zope2 startup: Zope will now send DatabaseOpened and
ProcessStarting events. ProcessStarting events.
- Testing.ZopeTestCase: Introduced a "ZopeLite" test layer, making it - Testing.ZopeTestCase: Introduced a "ZopeLite" test layer, making it
......
...@@ -17,10 +17,8 @@ import os ...@@ -17,10 +17,8 @@ import os
import string import string
from logging import getLogger from logging import getLogger
from Acquisition import aq_base from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire
from Acquisition import aq_parent from Acquisition import aq_inContextOf
from Acquisition import aq_inner
from Acquisition import aq_acquire
from ExtensionClass import Base from ExtensionClass import Base
from zope.interface import implements from zope.interface import implements
...@@ -98,10 +96,10 @@ def rolesForPermissionOn(perm, object, default=_default_roles, n=None): ...@@ -98,10 +96,10 @@ def rolesForPermissionOn(perm, object, default=_default_roles, n=None):
else: else:
r = r + list(roles) r = r + list(roles)
object = getattr(object, 'aq_inner', None) object = aq_inner(object)
if object is None: if object is None:
break break
object = object.aq_parent object = aq_parent(object)
if r is None: if r is None:
if _embed_permission_in_roles: if _embed_permission_in_roles:
...@@ -295,7 +293,7 @@ class ZopeSecurityPolicy: ...@@ -295,7 +293,7 @@ class ZopeSecurityPolicy:
raise Unauthorized(name, value) raise Unauthorized(name, value)
else: else:
# Try to acquire roles # Try to acquire roles
try: roles = container.aq_acquire('__roles__') try: roles = aq_acquire(container, '__roles__')
except AttributeError: except AttributeError:
if containerbase is not accessedbase: if containerbase is not accessedbase:
if self._verbose: if self._verbose:
...@@ -840,17 +838,10 @@ def verifyAcquisitionContext(user, object, object_roles=None): ...@@ -840,17 +838,10 @@ def verifyAcquisitionContext(user, object, object_roles=None):
# This is a strange rule, though # This is a strange rule, though
# it doesn't cause any security holes. SDH # it doesn't cause any security holes. SDH
return 1 return 1
if not hasattr(object, 'aq_inContextOf'): if hasattr(object, 'im_self'):
if hasattr(object, 'im_self'): # This is a method. Grab its self.
# This is a method. Grab its self. object=object.im_self
object=object.im_self if not aq_inContextOf(object, ucontext, 1):
if not hasattr(object, 'aq_inContextOf'):
# object is not wrapped, therefore we
# can't determine context.
# Fail the access attempt. Otherwise
# this would be a security hole.
return None
if not object.aq_inContextOf(ucontext, 1):
if 'Shared' in object_roles: if 'Shared' in object_roles:
# Old role setting. Waaa # Old role setting. Waaa
object_roles=user._shared_roles(object) object_roles=user._shared_roles(object)
......
...@@ -21,7 +21,7 @@ from AccessControl import ClassSecurityInfo ...@@ -21,7 +21,7 @@ from AccessControl import ClassSecurityInfo
from AccessControl import getSecurityManager, Unauthorized from AccessControl import getSecurityManager, Unauthorized
from AccessControl.Permissions import view_management_screens from AccessControl.Permissions import view_management_screens
from AccessControl.Permissions import take_ownership from AccessControl.Permissions import take_ownership
from Acquisition import aq_get, aq_parent, aq_base from Acquisition import aq_get, aq_parent, aq_base, aq_inner
from requestmethod import requestmethod from requestmethod import requestmethod
from zope.interface import implements from zope.interface import implements
...@@ -236,12 +236,12 @@ class Owned(ExtensionClass.Base): ...@@ -236,12 +236,12 @@ class Owned(ExtensionClass.Base):
def manage_fixupOwnershipAfterAdd(self): def manage_fixupOwnershipAfterAdd(self):
# Sigh, get the parent's _owner # Sigh, get the parent's _owner
parent=getattr(self, 'aq_parent', None) parent=getattr(self, '__parent__', None)
if parent is not None: _owner=aq_get(parent, '_owner', None, 1) if parent is not None: _owner=aq_get(parent, '_owner', None, 1)
else: _owner=None else: _owner=None
if (_owner is None and if (_owner is None and
((not hasattr(self, 'aq_parent')) or ((getattr(self, '__parent__', None) is None) or
(not hasattr(self, 'getPhysicalRoot')) (not hasattr(self, 'getPhysicalRoot'))
) )
): ):
...@@ -298,13 +298,13 @@ def ownerInfo(user, getattr=getattr): ...@@ -298,13 +298,13 @@ def ownerInfo(user, getattr=getattr):
return None return None
uid=user.getId() uid=user.getId()
if uid is None: return uid if uid is None: return uid
db=user.aq_inner.aq_parent db=aq_parent(aq_inner(user))
path=[absattr(db.id)] path=[absattr(db.id)]
root=db.getPhysicalRoot() root=db.getPhysicalRoot()
while 1: while 1:
db=getattr(db,'aq_inner', None) db=getattr(db,'aq_inner', None)
if db is None: break if db is None: break
db=db.aq_parent db=aq_parent(db)
if db is root: break if db is root: break
id=db.id id=db.id
if not isinstance(id, str): if not isinstance(id, str):
......
...@@ -17,6 +17,7 @@ $Id$ ...@@ -17,6 +17,7 @@ $Id$
import string, Products, Globals import string, Products, Globals
from Acquisition import aq_base
name_trans=filter(lambda c, an=string.letters+string.digits+'_': c not in an, name_trans=filter(lambda c, an=string.letters+string.digits+'_': c not in an,
map(chr,range(256))) map(chr,range(256)))
...@@ -36,8 +37,7 @@ class Permission: ...@@ -36,8 +37,7 @@ class Permission:
self.name=name self.name=name
self._p='_'+string.translate(name,name_trans)+"_Permission" self._p='_'+string.translate(name,name_trans)+"_Permission"
self.data=data self.data=data
if hasattr(obj, 'aq_base'): obj=obj.aq_base self.obj=aq_base(obj)
self.obj=obj
self.default=default self.default=default
def getRoles(self, default=_marker): def getRoles(self, default=_marker):
......
...@@ -105,7 +105,7 @@ class RoleManager: ...@@ -105,7 +105,7 @@ class RoleManager:
return r return r
def _isBeingAccessedAsZClassDefinedInstanceMethod(self): def _isBeingAccessedAsZClassDefinedInstanceMethod(self):
p=getattr(self,'aq_parent',None) p=getattr(self,'__parent__',None)
if p is None: return 0 # Not wrapped if p is None: return 0 # Not wrapped
base=getattr(p, 'aq_base', None) base=getattr(p, 'aq_base', None)
return type(base) is PermissionMapper return type(base) is PermissionMapper
......
...@@ -188,7 +188,7 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager): ...@@ -188,7 +188,7 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
if userObj: if userObj:
break break
else: else:
current = current.aq_parent current = current.__parent__
newSecurityManager(None, userObj) # necessary? newSecurityManager(None, userObj) # necessary?
...@@ -414,7 +414,7 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager): ...@@ -414,7 +414,7 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
raise OverflowError raise OverflowError
for name in unl: for name in unl:
dict[name]=1 dict[name]=1
item = getattr(item, 'aq_parent', _notfound) item = getattr(item, '__parent__', _notfound)
if item is _notfound: if item is _notfound:
break break
keys=dict.keys() keys=dict.keys()
...@@ -511,9 +511,9 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager): ...@@ -511,9 +511,9 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
for role in roles: for role in roles:
if not dup(role): if not dup(role):
dict[role]=1 dict[role]=1
if not hasattr(obj, 'aq_parent'): if getattr(obj, '__parent__', None) is None:
break break
obj=obj.aq_parent obj=obj.__parent__
x=x+1 x=x+1
roles=dict.keys() roles=dict.keys()
roles.sort() roles.sort()
......
...@@ -20,6 +20,9 @@ import re ...@@ -20,6 +20,9 @@ import re
import socket import socket
from base64 import decodestring from base64 import decodestring
from Acquisition import aq_base
from Acquisition import aq_parent
from Acquisition import aq_inContextOf
from Acquisition import Implicit from Acquisition import Implicit
from App.Management import Navigation, Tabs from App.Management import Navigation, Tabs
from Globals import DTMLFile, MessageDialog, Persistent, PersistentMapping from Globals import DTMLFile, MessageDialog, Persistent, PersistentMapping
...@@ -106,7 +109,7 @@ class BasicUser(Implicit): ...@@ -106,7 +109,7 @@ class BasicUser(Implicit):
for r in dict.get(userid, []): for r in dict.get(userid, []):
local[r]=1 local[r]=1
inner = getattr(object, 'aq_inner', object) inner = getattr(object, 'aq_inner', object)
parent = getattr(inner, 'aq_parent', None) parent = getattr(inner, '__parent__', None)
if parent is not None: if parent is not None:
object = parent object = parent
continue continue
...@@ -148,10 +151,10 @@ class BasicUser(Implicit): ...@@ -148,10 +151,10 @@ class BasicUser(Implicit):
else: else:
try: return r+list(roles) try: return r+list(roles)
except: return r except: return r
if hasattr(parent, 'aq_parent'): if getattr(parent, '__parent__', None) is not None:
while hasattr(parent.aq_self,'aq_self'): while hasattr(parent.aq_self,'aq_self'):
parent=parent.aq_self parent = parent.aq_self
parent=parent.aq_parent parent = aq_parent(parent)
else: return r else: return r
def _check_context(self, object): def _check_context(self, object):
...@@ -160,19 +163,15 @@ class BasicUser(Implicit): ...@@ -160,19 +163,15 @@ class BasicUser(Implicit):
# to prevent "stealing" access through acquisition tricks. # to prevent "stealing" access through acquisition tricks.
# Return true if in context, false if not or if context # Return true if in context, false if not or if context
# cannot be determined (object is not wrapped). # cannot be determined (object is not wrapped).
parent = getattr(self, 'aq_parent', None) parent = getattr(self, '__parent__', None)
context = getattr(parent, 'aq_parent', None) context = getattr(parent, '__parent__', None)
if context is not None: if context is not None:
if object is None: if object is None:
return 1 return 1
if not hasattr(object, 'aq_inContextOf'): if hasattr(object, 'im_self'):
if hasattr(object, 'im_self'): # This is a method. Grab its self.
# This is a method. Grab its self. object=object.im_self
object=object.im_self return aq_inContextOf(object, context, 1)
if not hasattr(object, 'aq_inContextOf'):
# Object is not wrapped, so return false.
return 0
return object.aq_inContextOf(context, 1)
# This is lame, but required to keep existing behavior. # This is lame, but required to keep existing behavior.
return 1 return 1
...@@ -230,7 +229,7 @@ class BasicUser(Implicit): ...@@ -230,7 +229,7 @@ class BasicUser(Implicit):
return 1 return 1
return 0 return 0
inner = getattr(inner_obj, 'aq_inner', inner_obj) inner = getattr(inner_obj, 'aq_inner', inner_obj)
parent = getattr(inner, 'aq_parent', None) parent = getattr(inner, '__parent__', None)
if parent is not None: if parent is not None:
inner_obj = parent inner_obj = parent
continue continue
...@@ -751,11 +750,11 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -751,11 +750,11 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
request.RESPONSE.notFoundError('no default view (root default view' request.RESPONSE.notFoundError('no default view (root default view'
' was probably deleted)') ' was probably deleted)')
n = request.steps[-1] n = request.steps[-1]
# default to accessed and container as v.aq_parent # default to accessed and container as v.__parent__
a = c = request['PARENTS'][0] a = c = request['PARENTS'][0]
# try to find actual container # try to find actual container
inner = getattr(v, 'aq_inner', v) inner = getattr(v, 'aq_inner', v)
innerparent = getattr(inner, 'aq_parent', None) innerparent = getattr(inner, '__parent__', None)
if innerparent is not None: if innerparent is not None:
# this is not a method, we needn't treat it specially # this is not a method, we needn't treat it specially
c = innerparent c = innerparent
...@@ -763,8 +762,8 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -763,8 +762,8 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
# this is a method, we need to treat it specially # this is a method, we need to treat it specially
c = v.im_self c = v.im_self
c = getattr(v, 'aq_inner', v) c = getattr(v, 'aq_inner', v)
request_container = getattr(request['PARENTS'][-1], 'aq_parent', []) request_container = getattr(request['PARENTS'][-1], '__parent__', [])
# if pub's aq_parent or container is the request container, it # if pub's __parent__ or container is the request container, it
# means pub was accessed from the root # means pub was accessed from the root
if a is request_container: if a is request_container:
a = request['PARENTS'][-1] a = request['PARENTS'][-1]
...@@ -775,7 +774,7 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -775,7 +774,7 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
def _isTop(self): def _isTop(self):
try: try:
return self.aq_parent.aq_base.isTopLevelPrincipiaApplicationObject return aq_base(aq_parent(self)).isTopLevelPrincipiaApplicationObject
except: except:
return 0 return 0
...@@ -990,8 +989,8 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager, ...@@ -990,8 +989,8 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
def manage_afterAdd(self, item, container): def manage_afterAdd(self, item, container):
if item is self: if item is self:
if hasattr(self, 'aq_base'): self=self.aq_base self = aq_base(self)
container.__allow_groups__=self container.__allow_groups__ = self
def __creatable_by_emergency_user__(self): return 1 def __creatable_by_emergency_user__(self): return 1
......
...@@ -1878,13 +1878,11 @@ c_rolesForPermissionOn(PyObject *perm, PyObject *object, ...@@ -1878,13 +1878,11 @@ c_rolesForPermissionOn(PyObject *perm, PyObject *object,
/* /*
object = getattr(object, 'aq_inner', None) object = aq_inner(object)
if object is None: if object is None:
break break
object = object.aq_parent object = aq_parent(object)
*/ */
if (! aq_isWrapper(object))
break;
{ {
PyObject *tobj = aq_inner(object); PyObject *tobj = aq_inner(object);
if (tobj == NULL) if (tobj == NULL)
...@@ -1895,8 +1893,6 @@ c_rolesForPermissionOn(PyObject *perm, PyObject *object, ...@@ -1895,8 +1893,6 @@ c_rolesForPermissionOn(PyObject *perm, PyObject *object,
if (object == Py_None) if (object == Py_None)
break; break;
if (! aq_isWrapper(object))
break;
tobj = aq_parent(object); tobj = aq_parent(object);
if (tobj == NULL) if (tobj == NULL)
goto end; goto end;
......
...@@ -38,7 +38,8 @@ static PyObject *py__add__, *py__sub__, *py__mul__, *py__div__, ...@@ -38,7 +38,8 @@ static PyObject *py__add__, *py__sub__, *py__mul__, *py__div__,
*py__long__, *py__float__, *py__oct__, *py__hex__, *py__long__, *py__float__, *py__oct__, *py__hex__,
*py__getitem__, *py__setitem__, *py__delitem__, *py__getitem__, *py__setitem__, *py__delitem__,
*py__getslice__, *py__setslice__, *py__delslice__, *py__contains__, *py__getslice__, *py__setslice__, *py__delslice__, *py__contains__,
*py__len__, *py__of__, *py__call__, *py__repr__, *py__str__, *py__cmp__; *py__len__, *py__of__, *py__call__, *py__repr__, *py__str__, *py__cmp__,
*py__parent__;
static PyObject *Acquired=0; static PyObject *Acquired=0;
...@@ -82,7 +83,7 @@ init_py_names(void) ...@@ -82,7 +83,7 @@ init_py_names(void)
INIT_PY_NAME(__repr__); INIT_PY_NAME(__repr__);
INIT_PY_NAME(__str__); INIT_PY_NAME(__str__);
INIT_PY_NAME(__cmp__); INIT_PY_NAME(__cmp__);
INIT_PY_NAME(__parent__);
#undef INIT_PY_NAME #undef INIT_PY_NAME
} }
...@@ -414,23 +415,49 @@ static PyObject * ...@@ -414,23 +415,49 @@ static PyObject *
Wrapper_findattr(Wrapper *self, PyObject *oname, Wrapper_findattr(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig, PyObject *filter, PyObject *extra, PyObject *orig,
int sob, int sco, int explicit, int containment) int sob, int sco, int explicit, int containment)
/*
Parameters:
sob
Search self->obj for the 'oname' attribute
sco
Search self->container for the 'oname' attribute
explicit
Explicitly acquire 'oname' attribute from container (assumed with
implicit acquisition wrapper)
containment
Use the innermost wrapper ("aq_inner") for looking up the 'oname'
attribute.
*/
{ {
PyObject *r, *v, *tb; PyObject *r, *v, *tb;
char *name=""; char *name="";
if (PyString_Check(oname)) name=PyString_AS_STRING(oname); if (PyString_Check(oname)) name=PyString_AS_STRING(oname);
if (*name=='a' && name[1]=='q' && name[2]=='_') if ((*name=='a' && name[1]=='q' && name[2]=='_') ||
if ((r=Wrapper_special(self, name+3, oname))) (strcmp(name, "__parent__")==0))
{ {
if (filter) /* __parent__ is an alias to aq_parent */
switch(apply_filter(filter,OBJECT(self),oname,r,extra,orig)) if (strcmp(name, "__parent__")==0)
{ name = "parent";
case -1: return NULL; else
case 1: return r; name = name + 3;
}
else return r; if ((r=Wrapper_special(self, name, oname)))
} {
else PyErr_Clear(); if (filter)
switch(apply_filter(filter,OBJECT(self),oname,r,extra,orig))
{
case -1: return NULL;
case 1: return r;
}
else return r;
}
else PyErr_Clear();
}
else if (*name=='_' && name[1]=='_' && else if (*name=='_' && name[1]=='_' &&
(strcmp(name+2,"reduce__")==0 || (strcmp(name+2,"reduce__")==0 ||
strcmp(name+2,"reduce_ex__")==0 || strcmp(name+2,"reduce_ex__")==0 ||
...@@ -477,6 +504,7 @@ Wrapper_findattr(Wrapper *self, PyObject *oname, ...@@ -477,6 +504,7 @@ Wrapper_findattr(Wrapper *self, PyObject *oname,
Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb); Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
r=NULL; r=NULL;
} }
/* normal attribute lookup */
else if ((r=PyObject_GetAttr(self->obj,oname))) else if ((r=PyObject_GetAttr(self->obj,oname)))
{ {
if (r==Acquired) if (r==Acquired)
...@@ -511,6 +539,7 @@ Wrapper_findattr(Wrapper *self, PyObject *oname, ...@@ -511,6 +539,7 @@ Wrapper_findattr(Wrapper *self, PyObject *oname,
PyErr_Clear(); PyErr_Clear();
} }
/* Lookup has failed, acquire it from parent. */
if (sco && (*name != '_' || explicit)) if (sco && (*name != '_' || explicit))
return Wrapper_acquire(self, oname, filter, extra, orig, explicit, return Wrapper_acquire(self, oname, filter, extra, orig, explicit,
containment); containment);
...@@ -524,23 +553,34 @@ Wrapper_acquire(Wrapper *self, PyObject *oname, ...@@ -524,23 +553,34 @@ Wrapper_acquire(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig, PyObject *filter, PyObject *extra, PyObject *orig,
int explicit, int containment) int explicit, int containment)
{ {
PyObject *r; PyObject *r, *v, *tb;
int sob=1, sco=1; int sob=1, sco=1;
if (self->container) if (self->container)
{ {
/* If the container has an acquisition wrapper itself, we'll use
Wrapper_findattr to progress further. */
if (isWrapper(self->container)) if (isWrapper(self->container))
{ {
if (self->obj && isWrapper(self->obj)) if (self->obj && isWrapper(self->obj))
{ {
/* Try to optimize search by recognizing repeated obs in path */ /* Try to optimize search by recognizing repeated
objects in path. */
if (WRAPPER(self->obj)->container== if (WRAPPER(self->obj)->container==
WRAPPER(self->container)->container) WRAPPER(self->container)->container)
sco=0; sco=0;
else if (WRAPPER(self->obj)->container== else if (WRAPPER(self->obj)->container==
WRAPPER(self->container)->obj) WRAPPER(self->container)->obj)
sob=0; sob=0;
} }
/* Don't search the container when the container of the
container is the same object as 'self'. */
if (WRAPPER(self->container)->container == WRAPPER(self)->obj)
{
sco=0;
containment=1;
}
r=Wrapper_findattr((Wrapper*)self->container, r=Wrapper_findattr((Wrapper*)self->container,
oname, filter, extra, orig, sob, sco, explicit, oname, filter, extra, orig, sob, sco, explicit,
...@@ -549,8 +589,46 @@ Wrapper_acquire(Wrapper *self, PyObject *oname, ...@@ -549,8 +589,46 @@ Wrapper_acquire(Wrapper *self, PyObject *oname,
if (r && has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self))); if (r && has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
return r; return r;
} }
/* If the container has a __parent__ pointer, we create an
acquisition wrapper for it accordingly. Then we can proceed
with Wrapper_findattr, just as if the container had an
acquisition wrapper in the first place (see above). */
else if ((r = PyObject_GetAttr(self->container, py__parent__)))
{
ASSIGN(self->container, newWrapper(self->container, r,
(PyTypeObject*)&Wrappertype));
/* Don't search the container when the parent of the parent
is the same object as 'self' */
if (WRAPPER(r)->obj == WRAPPER(self)->obj)
sco=0;
Py_DECREF(r); /* don't need __parent__ anymore */
r=Wrapper_findattr((Wrapper*)self->container,
oname, filter, extra, orig, sob, sco, explicit,
containment);
/* There's no need to DECREF the wrapper here because it's
not stored in self->container, thus 'self' owns its
reference now */
return r;
}
/* The container is the end of the acquisition chain; if we
can't look up the attribute here, we can't look it up at
all. */
else else
{ {
/* We need to clean up the AttributeError from the previous
getattr (because it has clearly failed). */
PyErr_Fetch(&r,&v,&tb);
if (r && (r != PyExc_AttributeError))
{
PyErr_Restore(r,v,tb);
return NULL;
}
Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
r=NULL;
if ((r=PyObject_GetAttr(self->container,oname))) { if ((r=PyObject_GetAttr(self->container,oname))) {
if (r == Acquired) { if (r == Acquired) {
Py_DECREF(r); Py_DECREF(r);
...@@ -618,8 +696,8 @@ Wrapper_setattro(Wrapper *self, PyObject *oname, PyObject *v) ...@@ -618,8 +696,8 @@ Wrapper_setattro(Wrapper *self, PyObject *oname, PyObject *v)
/* Allow assignment to parent, to change context. */ /* Allow assignment to parent, to change context. */
if (PyString_Check(oname)) name=PyString_AS_STRING(oname); if (PyString_Check(oname)) name=PyString_AS_STRING(oname);
if (*name=='a' && name[1]=='q' && name[2]=='_' if ((*name=='a' && name[1]=='q' && name[2]=='_'
&& strcmp(name+3,"parent")==0) && strcmp(name+3,"parent")==0) || (strcmp(name, "__parent__")==0))
{ {
Py_XINCREF(v); Py_XINCREF(v);
ASSIGN(self->container, v); ASSIGN(self->container, v);
...@@ -1112,57 +1190,18 @@ Wrapper_acquire_method(Wrapper *self, PyObject *args, PyObject *kw) ...@@ -1112,57 +1190,18 @@ Wrapper_acquire_method(Wrapper *self, PyObject *args, PyObject *kw)
# endif # endif
} }
/* forward declaration so that we can use it in Wrapper_inContextOf */
static PyObject * capi_aq_inContextOf(PyObject *self, PyObject *o, int inner);
static PyObject * static PyObject *
Wrapper_inContextOf(Wrapper *self, PyObject *args) Wrapper_inContextOf(Wrapper *self, PyObject *args)
{ {
PyObject *subob, *o, *c; PyObject *o;
int inner=1;
int inner=1;
UNLESS(PyArg_ParseTuple(args,"O|i",&o,&inner)) return NULL; UNLESS(PyArg_ParseTuple(args,"O|i",&o,&inner)) return NULL;
if (inner) { return capi_aq_inContextOf((PyObject*)self, o, inner);
/* subob = self */
subob = OBJECT(self);
/* o = aq_base(o) */
while (isWrapper(o) && WRAPPER(o)->obj) o=WRAPPER(o)->obj;
/* while 1: */
while (1) {
/* if aq_base(subob) is o: return 1 */
c = subob;
while (isWrapper(c) && WRAPPER(c)->obj) c = WRAPPER(c)->obj;
if (c == o) return PyInt_FromLong(1);
/* self = aq_inner(subob) */
/* if self is None: break */
if (isWrapper(subob)) {
self = WRAPPER(subob);
while (self->obj && isWrapper(self->obj))
self = WRAPPER(self->obj);
}
else break;
/* subob = aq_parent(self) */
/* if subob is None: break */
if (self->container)
subob = self->container;
else break;
}
}
else {
/* Follow wrappers instead. */
c = OBJECT(self);
while (1) {
if (c==o) return PyInt_FromLong(1);
if (c && isWrapper(c)) c=WRAPPER(c)->container;
else break;
}
}
return PyInt_FromLong(0);
} }
PyObject * PyObject *
...@@ -1332,8 +1371,7 @@ static PyObject * ...@@ -1332,8 +1371,7 @@ static PyObject *
capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter, capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
PyObject *extra, int explicit, PyObject *defalt, int containment) PyObject *extra, int explicit, PyObject *defalt, int containment)
{ {
PyObject *result, *v, *tb;
PyObject *result;
if (filter==Py_None) filter=0; if (filter==Py_None) filter=0;
...@@ -1343,22 +1381,46 @@ capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter, ...@@ -1343,22 +1381,46 @@ capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
WRAPPER(self), name, filter, extra, OBJECT(self),1, WRAPPER(self), name, filter, extra, OBJECT(self),1,
explicit || explicit ||
WRAPPER(self)->ob_type==(PyTypeObject*)&Wrappertype, WRAPPER(self)->ob_type==(PyTypeObject*)&Wrappertype,
explicit, containment); explicit, containment);
/* Not wrapped; check if we have a __parent__ pointer. If that's
/* Not wrapped and no filter, so just getattr */ the case, create a wrapper and pretend it's business as usual. */
if (! filter) return PyObject_GetAttr(self, name); else if ((result = PyObject_GetAttr(self, py__parent__)))
{
self = newWrapper(self, result, (PyTypeObject*)&Wrappertype);
Py_DECREF(result); /* don't need __parent__ anymore */
result = Wrapper_findattr(WRAPPER(self), name, filter, extra,
OBJECT(self), 1, 1, explicit, containment);
/* Get rid of temporary wrapper */
Py_DECREF(self);
return result;
}
/* No wrapper and no __parent__, so just getattr. */
else
{
/* Clean up the AttributeError from the previous getattr
(because it has clearly failed). */
PyErr_Fetch(&result,&v,&tb);
if (result && (result != PyExc_AttributeError))
{
PyErr_Restore(result,v,tb);
return NULL;
}
Py_XDECREF(result); Py_XDECREF(v); Py_XDECREF(tb);
/* Crap, we've got to construct a wrapper so we can use Wrapper_findattr */ if (! filter) return PyObject_GetAttr(self, name);
UNLESS (self=newWrapper(self, Py_None, (PyTypeObject*)&Wrappertype))
return NULL;
result=Wrapper_findattr(WRAPPER(self), name, filter, extra, OBJECT(self),
1, 1, explicit, containment);
/* get rid of temp wrapper */ /* Crap, we've got to construct a wrapper so we can use
Py_DECREF(self); Wrapper_findattr */
UNLESS (self=newWrapper(self, Py_None, (PyTypeObject*)&Wrappertype))
return NULL;
result=Wrapper_findattr(WRAPPER(self), name, filter, extra, OBJECT(self),
1, 1, explicit, containment);
return result; /* Get rid of temporary wrapper */
Py_DECREF(self);
return result;
}
} }
static PyObject * static PyObject *
...@@ -1384,13 +1446,35 @@ module_aq_acquire(PyObject *ignored, PyObject *args, PyObject *kw) ...@@ -1384,13 +1446,35 @@ module_aq_acquire(PyObject *ignored, PyObject *args, PyObject *kw)
static PyObject * static PyObject *
capi_aq_get(PyObject *self, PyObject *name, PyObject *defalt, int containment) capi_aq_get(PyObject *self, PyObject *name, PyObject *defalt, int containment)
{ {
PyObject *result = NULL; PyObject *result = NULL, *v, *tb;
/* We got a wrapped object, so business as usual */ /* We got a wrapped object, so business as usual */
if (isWrapper(self)) if (isWrapper(self))
result=Wrapper_findattr(WRAPPER(self), name, 0, 0, OBJECT(self), 1, 1, 1, result=Wrapper_findattr(WRAPPER(self), name, 0, 0, OBJECT(self), 1, 1, 1,
containment); containment);
/* Not wrapped; check if we have a __parent__ pointer. If that's
the case, create a wrapper and pretend it's business as usual. */
else if ((result = PyObject_GetAttr(self, py__parent__)))
{
self=newWrapper(self, result, (PyTypeObject*)&Wrappertype);
Py_DECREF(result); /* don't need __parent__ anymore */
result=Wrapper_findattr(WRAPPER(self), name, 0, 0, OBJECT(self),
1, 1, 1, containment);
Py_DECREF(self); /* Get rid of temporary wrapper. */
}
else else
result=PyObject_GetAttr(self, name); {
/* Clean up the AttributeError from the previous getattr
(because it has clearly failed). */
PyErr_Fetch(&result,&v,&tb);
if (result && (result != PyExc_AttributeError))
{
PyErr_Restore(result,v,tb);
return NULL;
}
Py_XDECREF(result); Py_XDECREF(v); Py_XDECREF(tb);
result=PyObject_GetAttr(self, name);
}
if (! result && defalt) if (! result && defalt)
{ {
...@@ -1453,13 +1537,34 @@ module_aq_base(PyObject *ignored, PyObject *args) ...@@ -1453,13 +1537,34 @@ module_aq_base(PyObject *ignored, PyObject *args)
static PyObject * static PyObject *
capi_aq_parent(PyObject *self) capi_aq_parent(PyObject *self)
{ {
PyObject *result=Py_None; PyObject *result, *v, *tb;
if (isWrapper(self) && WRAPPER(self)->container) if (isWrapper(self) && WRAPPER(self)->container)
result=WRAPPER(self)->container; {
result=WRAPPER(self)->container;
Py_INCREF(result);
return result;
}
else if ((result=PyObject_GetAttr(self, py__parent__)))
/* We already own the reference to result (PyObject_GetAttr gives
it to us), no need to INCREF here */
return result;
else
{
/* We need to clean up the AttributeError from the previous
getattr (because it has clearly failed) */
PyErr_Fetch(&result,&v,&tb);
if (result && (result != PyExc_AttributeError))
{
PyErr_Restore(result,v,tb);
return NULL;
}
Py_XDECREF(result); Py_XDECREF(v); Py_XDECREF(tb);
Py_INCREF(result); result=Py_None;
return result; Py_INCREF(result);
return result;
}
} }
static PyObject * static PyObject *
...@@ -1535,7 +1640,7 @@ module_aq_inner(PyObject *ignored, PyObject *args) ...@@ -1535,7 +1640,7 @@ module_aq_inner(PyObject *ignored, PyObject *args)
static PyObject * static PyObject *
capi_aq_chain(PyObject *self, int containment) capi_aq_chain(PyObject *self, int containment)
{ {
PyObject *result; PyObject *result, *v, *tb;
UNLESS (result=PyList_New(0)) return NULL; UNLESS (result=PyList_New(0)) return NULL;
...@@ -1558,8 +1663,27 @@ capi_aq_chain(PyObject *self, int containment) ...@@ -1558,8 +1663,27 @@ capi_aq_chain(PyObject *self, int containment)
} }
} }
else else
if (PyList_Append(result, self) < 0) {
goto err; if (PyList_Append(result, self) < 0)
goto err;
if ((self=PyObject_GetAttr(self, py__parent__)))
{
Py_DECREF(self); /* We don't need our own reference. */
if (self!=Py_None)
continue;
}
else
{
PyErr_Fetch(&self,&v,&tb);
if (self && (self != PyExc_AttributeError))
{
PyErr_Restore(self,v,tb);
return NULL;
}
Py_XDECREF(self); Py_XDECREF(v); Py_XDECREF(tb);
}
}
break; break;
} }
...@@ -1582,6 +1706,53 @@ module_aq_chain(PyObject *ignored, PyObject *args) ...@@ -1582,6 +1706,53 @@ module_aq_chain(PyObject *ignored, PyObject *args)
return capi_aq_chain(self, containment); return capi_aq_chain(self, containment);
} }
static PyObject *
capi_aq_inContextOf(PyObject *self, PyObject *o, int inner)
{
PyObject *next, *c;
/* next = self
o = aq_base(o) */
next = self;
while (isWrapper(o) && WRAPPER(o)->obj)
o=WRAPPER(o)->obj;
while (1) {
/* if aq_base(next) is o: return 1 */
c = next;
while (isWrapper(c) && WRAPPER(c)->obj) c = WRAPPER(c)->obj;
if (c == o) return PyInt_FromLong(1);
if (inner)
{
self = capi_aq_inner(next);
Py_DECREF(self); /* We're not holding on to the inner wrapper */
if (self == Py_None) break;
}
else
self = next;
next = capi_aq_parent(self);
Py_DECREF(next); /* We're not holding on to the parent */
if (next == Py_None) break;
}
return PyInt_FromLong(0);
}
static PyObject *
module_aq_inContextOf(PyObject *ignored, PyObject *args)
{
PyObject *self, *o;
int inner=1;
UNLESS (PyArg_ParseTuple(args, "OO|i", &self, &o, &inner))
return NULL;
return capi_aq_inContextOf(self, o, inner);
}
static struct PyMethodDef methods[] = { static struct PyMethodDef methods[] = {
{"aq_acquire", (PyCFunction)module_aq_acquire, METH_VARARGS|METH_KEYWORDS, {"aq_acquire", (PyCFunction)module_aq_acquire, METH_VARARGS|METH_KEYWORDS,
"aq_acquire(ob, name [, filter, extra, explicit]) -- " "aq_acquire(ob, name [, filter, extra, explicit]) -- "
...@@ -1599,10 +1770,13 @@ static struct PyMethodDef methods[] = { ...@@ -1599,10 +1770,13 @@ static struct PyMethodDef methods[] = {
"aq_self(ob) -- Get the object with the outermost wrapper removed"}, "aq_self(ob) -- Get the object with the outermost wrapper removed"},
{"aq_inner", (PyCFunction)module_aq_inner, METH_VARARGS, {"aq_inner", (PyCFunction)module_aq_inner, METH_VARARGS,
"aq_inner(ob) -- " "aq_inner(ob) -- "
"Get the object with alll but the innermost wrapper removed"}, "Get the object with all but the innermost wrapper removed"},
{"aq_chain", (PyCFunction)module_aq_chain, METH_VARARGS, {"aq_chain", (PyCFunction)module_aq_chain, METH_VARARGS,
"aq_chain(ob [, containment]) -- " "aq_chain(ob [, containment]) -- "
"Get a list of objects in the acquisition environment"}, "Get a list of objects in the acquisition environment"},
{"aq_inContextOf", (PyCFunction)module_aq_inContextOf, METH_VARARGS,
"aq_inContextOf(base, ob [, inner]) -- "
"Determine whether the object is in the acquisition context of base."},
{NULL, NULL} {NULL, NULL}
}; };
......
...@@ -357,6 +357,11 @@ def test_unwrapped(): ...@@ -357,6 +357,11 @@ def test_unwrapped():
... ...
AttributeError: aq_parent AttributeError: aq_parent
>>> c.__parent__
Traceback (most recent call last):
...
AttributeError: __parent__
>>> Acquisition.aq_acquire(c, 'id') >>> Acquisition.aq_acquire(c, 'id')
'unwrapped' 'unwrapped'
>>> Acquisition.aq_acquire(c, 'x') >>> Acquisition.aq_acquire(c, 'x')
...@@ -452,6 +457,13 @@ def test_simple(): ...@@ -452,6 +457,13 @@ def test_simple():
>>> a.b.c.aq_inContextOf(a.b.c) >>> a.b.c.aq_inContextOf(a.b.c)
1 1
>>> Acquisition.aq_inContextOf(a.b.c, a)
1
>>> Acquisition.aq_inContextOf(a.b.c, a.b)
1
>>> Acquisition.aq_inContextOf(a.b.c, a.b.c)
1
>>> a.b.c.aq_acquire('y') >>> a.b.c.aq_acquire('y')
42 42
...@@ -533,6 +545,13 @@ def test_simple(): ...@@ -533,6 +545,13 @@ def test_simple():
>>> show(Acquisition.aq_self(a.b.c)) >>> show(Acquisition.aq_self(a.b.c))
c c
A wrapper's __parent__ attribute (which is equivalent to its
aq_parent attribute) points to the Acquisition parent.
>>> a.b.c.__parent__ == a.b.c.aq_parent
True
>>> a.b.c.__parent__ == a.b
True
""" """
def test__of__exception(): def test__of__exception():
...@@ -1201,7 +1220,7 @@ def test_mixed_explicit_and_explicit(): ...@@ -1201,7 +1220,7 @@ def test_mixed_explicit_and_explicit():
""" """
def old_tests(): def test_aq_inContextOf():
""" """
>>> from ExtensionClass import Base >>> from ExtensionClass import Base
>>> import Acquisition >>> import Acquisition
...@@ -1213,6 +1232,9 @@ def old_tests(): ...@@ -1213,6 +1232,9 @@ def old_tests():
... def hi(self): ... def hi(self):
... print "%s()" % self.__class__.__name__, self.color ... print "%s()" % self.__class__.__name__, self.color
>>> class Location(object):
... __parent__ = None
>>> b=B() >>> b=B()
>>> b.a=A() >>> b.a=A()
>>> b.a.hi() >>> b.a.hi()
...@@ -1242,25 +1264,52 @@ def old_tests(): ...@@ -1242,25 +1264,52 @@ def old_tests():
>>> b.c == c >>> b.c == c
1 1
>>> l = Location()
>>> l.__parent__ = b.c
>>> def checkContext(self, o): >>> def checkContext(self, o):
... # Python equivalent to aq_inContextOf ... # Python equivalent to aq_inContextOf
... from Acquisition import aq_base, aq_parent, aq_inner ... from Acquisition import aq_base, aq_parent, aq_inner
... subob = self ... next = self
... o = aq_base(o) ... o = aq_base(o)
... while 1: ... while 1:
... if aq_base(subob) is o: return 1 ... if aq_base(next) is o:
... self = aq_inner(subob) ... return 1
... if self is None: break ... self = aq_inner(next)
... subob = aq_parent(self) ... if self is None:
... if subob is None: break ... break
... next = aq_parent(self)
... if next is None:
... break
... return 0
>>> checkContext(b.c, b) >>> checkContext(b.c, b)
1 1
>>> not checkContext(b.c, b.a) >>> not checkContext(b.c, b.a)
1 1
>>> checkContext(l, b)
1
>>> checkContext(l, b.c)
1
>>> not checkContext(l, b.a)
1
Acquisition.aq_inContextOf works the same way:
>>> Acquisition.aq_inContextOf(b.c, b)
1
>>> Acquisition.aq_inContextOf(b.c, b.a)
0
>>> Acquisition.aq_inContextOf(l, b)
1
>>> Acquisition.aq_inContextOf(l, b.c)
1
>>> Acquisition.aq_inContextOf(l, b.a)
0
>>> b.a.aq_inContextOf(b) >>> b.a.aq_inContextOf(b)
1 1
>>> b.c.aq_inContextOf(b) >>> b.c.aq_inContextOf(b)
...@@ -1271,12 +1320,12 @@ def old_tests(): ...@@ -1271,12 +1320,12 @@ def old_tests():
1 1
>>> b.c.d.aq_inContextOf(b.c) >>> b.c.d.aq_inContextOf(b.c)
1 1
>>> not b.c.aq_inContextOf(foo) >>> b.c.aq_inContextOf(foo)
1 0
>>> not b.c.aq_inContextOf(b.a) >>> b.c.aq_inContextOf(b.a)
1 0
>>> not b.a.aq_inContextOf('somestring') >>> b.a.aq_inContextOf('somestring')
1 0
""" """
def test_AqAlg(): def test_AqAlg():
...@@ -1389,7 +1438,7 @@ def test_creating_wrappers_directly(): ...@@ -1389,7 +1438,7 @@ def test_creating_wrappers_directly():
... ...
TypeError: __init__() takes exactly 2 arguments (1 given) TypeError: __init__() takes exactly 2 arguments (1 given)
We can reassign aq_parent We can reassign aq_parent / __parent__ on a wrapper:
>>> x = B() >>> x = B()
>>> x.color = 'green' >>> x.color = 'green'
...@@ -1397,6 +1446,20 @@ def test_creating_wrappers_directly(): ...@@ -1397,6 +1446,20 @@ def test_creating_wrappers_directly():
>>> w.color >>> w.color
'green' 'green'
>>> y = B()
>>> y.color = 'blue'
>>> w.__parent__ = y
>>> w.color
'blue'
Note that messing with the wrapper won't in any way affect the
wrapped object:
>>> Acquisition.aq_base(w).__parent__
Traceback (most recent call last):
...
AttributeError: __parent__
>>> w = ImplicitAcquisitionWrapper() >>> w = ImplicitAcquisitionWrapper()
Traceback (most recent call last): Traceback (most recent call last):
... ...
...@@ -1664,6 +1727,434 @@ def test_proxying(): ...@@ -1664,6 +1727,434 @@ def test_proxying():
""" """
class Location(object):
__parent__ = None
class ECLocation(ExtensionClass.Base):
__parent__ = None
def test___parent__no_wrappers():
"""
Acquisition also works with objects that aren't wrappers, as long
as they have __parent__ pointers. Let's take a hierarchy like
z --isParent--> y --isParent--> x:
>>> x = Location()
>>> y = Location()
>>> z = Location()
>>> x.__parent__ = y
>>> y.__parent__ = z
and some attributes that we want to acquire:
>>> x.hello = 'world'
>>> y.foo = 42
>>> z.foo = 43 # this should not be found
>>> z.bar = 3.145
``aq_acquire`` works as we know it from implicit/acquisition
wrappers:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) is y
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
"""
def test_implicit_wrapper_as___parent__():
"""
Let's do the same test again, only now not all objects are of the
same kind and link to each other via __parent__ pointers. The
root is a stupid ExtensionClass object:
>>> class Root(ExtensionClass.Base):
... bar = 3.145
>>> z = Root()
The intermediate parent is an object that supports implicit
acquisition. We bind it to the root via the __of__ protocol:
>>> class Impl(Acquisition.Implicit):
... foo = 42
>>> y = Impl().__of__(z)
The child object is again a simple object with a simple __parent__
pointer:
>>> x = Location()
>>> x.hello = 'world'
>>> x.__parent__ = y
``aq_acquire`` works as expected from implicit/acquisition
wrappers:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) is y
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
Note that also the (implicit) acquisition wrapper has a __parent__
pointer, which is automatically computed from the acquisition
container (it's identical to aq_parent):
>>> y.__parent__ is z
True
Just as much as you can assign to aq_parent, you can also assign
to __parent__ to change the acquisition context of the wrapper:
>>> newroot = Root()
>>> y.__parent__ = newroot
>>> y.__parent__ is z
False
>>> y.__parent__ is newroot
True
Note that messing with the wrapper won't in any way affect the
wrapped object:
>>> Acquisition.aq_base(y).__parent__
Traceback (most recent call last):
...
AttributeError: __parent__
"""
def test_explicit_wrapper_as___parent__():
"""
Let's do this test yet another time, with an explicit wrapper:
>>> class Root(ExtensionClass.Base):
... bar = 3.145
>>> z = Root()
The intermediate parent is an object that supports implicit
acquisition. We bind it to the root via the __of__ protocol:
>>> class Expl(Acquisition.Explicit):
... foo = 42
>>> y = Expl().__of__(z)
The child object is again a simple object with a simple __parent__
pointer:
>>> x = Location()
>>> x.hello = 'world'
>>> x.__parent__ = y
``aq_acquire`` works as expected from implicit/acquisition
wrappers:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) is y
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
Note that also the (explicit) acquisition wrapper has a __parent__
pointer, which is automatically computed from the acquisition
container (it's identical to aq_parent):
>>> y.__parent__ is z
True
Just as much as you can assign to aq_parent, you can also assign
to __parent__ to change the acquisition context of the wrapper:
>>> newroot = Root()
>>> y.__parent__ = newroot
>>> y.__parent__ is z
False
>>> y.__parent__ is newroot
True
Note that messing with the wrapper won't in any way affect the
wrapped object:
>>> Acquisition.aq_base(y).__parent__
Traceback (most recent call last):
...
AttributeError: __parent__
"""
def test_implicit_wrapper_has_nonwrapper_as_aq_parent():
"""Let's do this the other way around: The root and the
intermediate parent is an object that doesn't support acquisition,
>>> y = ECLocation()
>>> z = Location()
>>> y.__parent__ = z
>>> y.foo = 42
>>> z.foo = 43 # this should not be found
>>> z.bar = 3.145
only the outmost object does:
>>> class Impl(Acquisition.Implicit):
... hello = 'world'
>>> x = Impl().__of__(y)
Again, acquiring objects works as usual:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) == y
True
>>> x.aq_parent == y
True
>>> x.aq_parent.aq_parent == z
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
>>> x.aq_chain == [x, y, z]
True
Because the outmost object, ``x``, is wrapped in an implicit
acquisition wrapper, we can also use direct attribute access:
>>> x.hello
'world'
>>> x.foo
42
>>> x.bar
3.145
"""
def test_explicit_wrapper_has_nonwrapper_as_aq_parent():
"""Let's do this the other way around: The root and the
intermediate parent is an object that doesn't support acquisition,
>>> y = ECLocation()
>>> z = Location()
>>> y.__parent__ = z
>>> y.foo = 42
>>> z.foo = 43 # this should not be found
>>> z.bar = 3.145
only the outmost object does:
>>> class Expl(Acquisition.Explicit):
... hello = 'world'
>>> x = Expl().__of__(y)
Again, acquiring objects works as usual:
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'foo')
42
>>> Acquisition.aq_acquire(x, 'bar')
3.145
as does ``aq_get``:
>>> Acquisition.aq_get(x, 'hello')
'world'
>>> Acquisition.aq_get(x, 'foo')
42
>>> Acquisition.aq_get(x, 'bar')
3.145
and ``aq_parent``:
>>> Acquisition.aq_parent(x) == y
True
>>> x.aq_parent == y
True
>>> x.aq_parent.aq_parent == z
True
>>> Acquisition.aq_parent(y) is z
True
as well as ``aq_chain``:
>>> Acquisition.aq_chain(x) == [x, y, z]
True
>>> x.aq_chain == [x, y, z]
True
"""
def test___parent__aq_parent_circles():
"""
As a general safety belt, Acquisition won't follow a mixture of
circular __parent__ pointers and aq_parent wrappers. These can
occurr when code that uses implicit acquisition wrappers meets
code that uses __parent__ pointers.
>>> class Impl(Acquisition.Implicit):
... hello = 'world'
>>> class Impl2(Acquisition.Implicit):
... hello = 'world2'
... only = 'here'
>>> x = Impl()
>>> y = Impl2().__of__(x)
>>> x.__parent__ = y
>>> x.__parent__.aq_base is y.aq_base
True
>>> x.__parent__.__parent__ is x
True
>>> x.hello
'world'
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> x.only
Traceback (most recent call last):
...
AttributeError: only
>>> Acquisition.aq_acquire(x, 'only')
'here'
>>> Acquisition.aq_acquire(x, 'non_existant_attr')
Traceback (most recent call last):
...
AttributeError: non_existant_attr
>>> Acquisition.aq_acquire(y, 'non_existant_attr')
Traceback (most recent call last):
...
AttributeError: non_existant_attr
>>> x.non_existant_attr
Traceback (most recent call last):
...
AttributeError: non_existant_attr
>>> y.non_existant_attr
Traceback (most recent call last):
...
AttributeError: non_existant_attr
"""
def test___parent__parent__circles():
"""
Acquisition won't follow circular __parent__ references:
>>> class Impl(Acquisition.Implicit):
... hello = 'world'
>>> class Impl2(Acquisition.Implicit):
... hello = 'world2'
... only = 'here'
>>> x = Impl()
>>> y = Impl2()
>>> x.__parent__ = y
>>> y.__parent__ = x
>>> x.__parent__.__parent__ is x
True
>>> Acquisition.aq_acquire(x, 'hello')
'world'
>>> Acquisition.aq_acquire(x, 'only')
'here'
>>> Acquisition.aq_acquire(x, 'non_existant_attr')
Traceback (most recent call last):
...
AttributeError: non_existant_attr
>>> Acquisition.aq_acquire(y, 'non_existant_attr')
Traceback (most recent call last):
...
AttributeError: non_existant_attr
"""
import unittest import unittest
from zope.testing.doctest import DocTestSuite, DocFileSuite from zope.testing.doctest import DocTestSuite, DocFileSuite
......
...@@ -66,7 +66,7 @@ class FactoryDispatcher(Acquisition.Implicit): ...@@ -66,7 +66,7 @@ class FactoryDispatcher(Acquisition.Implicit):
_owner=UnownableOwner _owner=UnownableOwner
def __init__(self, product, dest, REQUEST=None): def __init__(self, product, dest, REQUEST=None):
if hasattr(product,'aq_base'): product=product.aq_base product = Acquisition.aq_base(product)
self._product=product self._product=product
self._d=dest self._d=dest
if REQUEST is not None: if REQUEST is not None:
...@@ -100,7 +100,7 @@ class FactoryDispatcher(Acquisition.Implicit): ...@@ -100,7 +100,7 @@ class FactoryDispatcher(Acquisition.Implicit):
m=d[name] m=d[name]
w=getattr(m, '_permissionMapper', None) w=getattr(m, '_permissionMapper', None)
if w is not None: if w is not None:
m=aqwrap(m, getattr(w,'aq_base',w), self) m=aqwrap(m, Acquisition.aq_base(w), self)
return m return m
......
...@@ -22,6 +22,7 @@ from AccessControl import ClassSecurityInfo ...@@ -22,6 +22,7 @@ from AccessControl import ClassSecurityInfo
from AccessControl.DTML import RestrictedDTML from AccessControl.DTML import RestrictedDTML
from AccessControl.Permission import name_trans from AccessControl.Permission import name_trans
from AccessControl.Permissions import view_management_screens from AccessControl.Permissions import view_management_screens
from Acquisition import aq_base
from DateTime import DateTime from DateTime import DateTime
from DocumentTemplate.DT_Util import Eval from DocumentTemplate.DT_Util import Eval
from DocumentTemplate.DT_Util import InstanceDict, TemplateDict from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
...@@ -92,9 +93,7 @@ class FindSupport(ExtensionClass.Base): ...@@ -92,9 +93,7 @@ class FindSupport(ExtensionClass.Base):
md=td() md=td()
obj_expr=(Eval(obj_expr), md, md._push, md._pop) obj_expr=(Eval(obj_expr), md, md._push, md._pop)
base=obj base = aq_base(obj)
if hasattr(obj, 'aq_base'):
base=obj.aq_base
if hasattr(base, 'objectItems'): if hasattr(base, 'objectItems'):
try: items=obj.objectItems() try: items=obj.objectItems()
...@@ -118,9 +117,7 @@ class FindSupport(ExtensionClass.Base): ...@@ -118,9 +117,7 @@ class FindSupport(ExtensionClass.Base):
if hasattr(ob, '_p_changed') and (ob._p_changed == None): if hasattr(ob, '_p_changed') and (ob._p_changed == None):
dflag=1 dflag=1
if hasattr(ob, 'aq_base'): bs = aq_base(ob)
bs=ob.aq_base
else: bs=ob
if ( if (
(not obj_ids or absattr(bs.getId()) in obj_ids) (not obj_ids or absattr(bs.getId()) in obj_ids)
and and
...@@ -200,9 +197,7 @@ class FindSupport(ExtensionClass.Base): ...@@ -200,9 +197,7 @@ class FindSupport(ExtensionClass.Base):
md=td() md=td()
obj_expr=(Eval(obj_expr), md, md._push, md._pop) obj_expr=(Eval(obj_expr), md, md._push, md._pop)
base=obj base = aq_base(obj)
if hasattr(obj, 'aq_base'):
base=obj.aq_base
if not hasattr(base, 'objectItems'): if not hasattr(base, 'objectItems'):
return result return result
...@@ -221,10 +216,7 @@ class FindSupport(ExtensionClass.Base): ...@@ -221,10 +216,7 @@ class FindSupport(ExtensionClass.Base):
if hasattr(ob, '_p_changed') and (ob._p_changed == None): if hasattr(ob, '_p_changed') and (ob._p_changed == None):
dflag=1 dflag=1
if hasattr(ob, 'aq_base'): bs = aq_base(ob)
bs=ob.aq_base
else: bs=ob
if ( if (
(not obj_ids or absattr(bs.getId()) in obj_ids) (not obj_ids or absattr(bs.getId()) in obj_ids)
and and
......
...@@ -20,13 +20,14 @@ from webdav.WriteLockInterface import WriteLockInterface ...@@ -20,13 +20,14 @@ from webdav.WriteLockInterface import WriteLockInterface
from ZPublisher.Converters import type_converters from ZPublisher.Converters import type_converters
from Globals import InitializeClass from Globals import InitializeClass
from Globals import DTMLFile, MessageDialog from Globals import DTMLFile, MessageDialog
from Acquisition import aq_base
from Acquisition import aq_parent
from Acquisition import Implicit, Explicit from Acquisition import Implicit, Explicit
from App.Common import rfc1123_date, iso8601_date from App.Common import rfc1123_date, iso8601_date
from webdav.common import urlbase from webdav.common import urlbase
from ExtensionClass import Base from ExtensionClass import Base
from Globals import Persistent from Globals import Persistent
from Traversable import Traversable from Traversable import Traversable
from Acquisition import aq_base
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import access_contents_information from AccessControl.Permissions import access_contents_information
from AccessControl.Permissions import manage_properties from AccessControl.Permissions import manage_properties
...@@ -71,7 +72,7 @@ class View(App.Management.Tabs, Base): ...@@ -71,7 +72,7 @@ class View(App.Management.Tabs, Base):
pre=pre+'/' pre=pre+'/'
r=[] r=[]
for d in self.aq_parent.aq_parent.manage_options: for d in aq_parent(aq_parent(self)).manage_options:
path=d['action'] path=d['action']
option={'label': d['label'], option={'label': d['label'],
'action': pre+path, 'action': pre+path,
...@@ -92,7 +93,7 @@ class View(App.Management.Tabs, Base): ...@@ -92,7 +93,7 @@ class View(App.Management.Tabs, Base):
self, script, path) self, script, path)
def meta_type(self): def meta_type(self):
try: return self.aq_parent.aq_parent.meta_type try: return aq_parent(aq_parent(self)).meta_type
except: return '' except: return ''
...@@ -489,7 +490,7 @@ class Virtual: ...@@ -489,7 +490,7 @@ class Virtual:
pass pass
def v_self(self): def v_self(self):
return self.aq_parent.aq_parent return aq_parent(aq_parent(self))
class DefaultProperties(Virtual, PropertySheet, View): class DefaultProperties(Virtual, PropertySheet, View):
...@@ -635,7 +636,7 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs): ...@@ -635,7 +636,7 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs):
return (self.webdav,) return (self.webdav,)
def __propsets__(self): def __propsets__(self):
propsets=self.aq_parent.__propsets__ propsets = aq_parent(self).__propsets__
__traceback_info__= propsets, type(propsets) __traceback_info__= propsets, type(propsets)
return self._get_defaults() + propsets return self._get_defaults() + propsets
...@@ -684,17 +685,17 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs): ...@@ -684,17 +685,17 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs):
security.declareProtected(manage_properties, 'addPropertySheet') security.declareProtected(manage_properties, 'addPropertySheet')
def addPropertySheet(self, propset): def addPropertySheet(self, propset):
propsets=self.aq_parent.__propsets__ propsets = aq_parent(self).__propsets__
propsets=propsets+(propset,) propsets = propsets+(propset,)
self.aq_parent.__propsets__=propsets aq_parent(self).__propsets__ = propsets
security.declareProtected(manage_properties, 'delPropertySheet') security.declareProtected(manage_properties, 'delPropertySheet')
def delPropertySheet(self, name): def delPropertySheet(self, name):
result=[] result=[]
for propset in self.aq_parent.__propsets__: for propset in aq_parent(self).__propsets__:
if propset.getId() != name and propset.xml_namespace() != name: if propset.getId() != name and propset.xml_namespace() != name:
result.append(propset) result.append(propset)
self.aq_parent.__propsets__=tuple(result) aq_parent(self).__propsets__=tuple(result)
## DM: deletion support ## DM: deletion support
def isDeletable(self,name): def isDeletable(self,name):
...@@ -743,7 +744,7 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs): ...@@ -743,7 +744,7 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs):
pre=pre+'/' pre=pre+'/'
r=[] r=[]
for d in self.aq_parent.manage_options: for d in aq_parent(self).manage_options:
r.append({'label': d['label'], 'action': pre+d['action']}) r.append({'label': d['label'], 'action': pre+d['action']})
return r return r
......
...@@ -205,14 +205,16 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, Traversable, ...@@ -205,14 +205,16 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, Traversable,
if match is not None: if match is not None:
error_message=error_value error_message=error_value
if client is None: client=self if client is None:
if not REQUEST: REQUEST=self.aq_acquire('REQUEST') client = self
if not REQUEST:
REQUEST = aq_acquire(self, 'REQUEST')
try: try:
if hasattr(client, 'standard_error_message'): if hasattr(client, 'standard_error_message'):
s=getattr(client, 'standard_error_message') s=getattr(client, 'standard_error_message')
else: else:
client = client.aq_parent client = aq_parent(client)
s=getattr(client, 'standard_error_message') s=getattr(client, 'standard_error_message')
kwargs = {'error_type': error_type, kwargs = {'error_type': error_type,
'error_value': error_value, 'error_value': error_value,
...@@ -329,7 +331,7 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, Traversable, ...@@ -329,7 +331,7 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, Traversable,
raise ValueError('FTP List not supported on acquired objects') raise ValueError('FTP List not supported on acquired objects')
if not hasattr(ob,'aq_parent'): if not hasattr(ob,'aq_parent'):
break break
ob=ob.aq_parent ob = aq_parent(ob)
stat=marshal.loads(self.manage_FTPstat(REQUEST)) stat=marshal.loads(self.manage_FTPstat(REQUEST))
id = self.getId() id = self.getId()
......
...@@ -22,7 +22,8 @@ from AccessControl import ClassSecurityInfo ...@@ -22,7 +22,8 @@ from AccessControl import ClassSecurityInfo
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from AccessControl import Unauthorized from AccessControl import Unauthorized
from AccessControl.ZopeGuards import guarded_getattr from AccessControl.ZopeGuards import guarded_getattr
from Acquisition import Acquired, aq_inner, aq_parent, aq_base from Acquisition import Acquired, aq_inner, aq_parent, aq_acquire, aq_base
from Acquisition.interfaces import IAcquirer
from zExceptions import NotFound from zExceptions import NotFound
from ZODB.POSException import ConflictError from ZODB.POSException import ConflictError
from OFS.interfaces import ITraversable from OFS.interfaces import ITraversable
...@@ -64,7 +65,7 @@ class Traversable: ...@@ -64,7 +65,7 @@ class Traversable:
spp = self.getPhysicalPath() spp = self.getPhysicalPath()
try: try:
toUrl = self.REQUEST.physicalPathToURL toUrl = aq_acquire(self, 'REQUEST').physicalPathToURL
except AttributeError: except AttributeError:
return path2url(spp[1:]) return path2url(spp[1:])
return toUrl(spp) return toUrl(spp)
...@@ -78,7 +79,7 @@ class Traversable: ...@@ -78,7 +79,7 @@ class Traversable:
""" """
spp = self.getPhysicalPath() spp = self.getPhysicalPath()
try: try:
toUrl = self.REQUEST.physicalPathToURL toUrl = aq_acquire(self, 'REQUEST').physicalPathToURL
except AttributeError: except AttributeError:
return path2url(spp) or '/' return path2url(spp) or '/'
return toUrl(spp, relative=1) or '/' return toUrl(spp, relative=1) or '/'
...@@ -93,7 +94,7 @@ class Traversable: ...@@ -93,7 +94,7 @@ class Traversable:
""" """
spp = self.getPhysicalPath() spp = self.getPhysicalPath()
try: try:
toVirt = self.REQUEST.physicalPathToVirtualPath toVirt = aq_acquire(self, 'REQUEST').physicalPathToVirtualPath
except AttributeError: except AttributeError:
return path2url(spp[1:]) return path2url(spp[1:])
return path2url(toVirt(spp)) return path2url(toVirt(spp))
...@@ -191,7 +192,9 @@ class Traversable: ...@@ -191,7 +192,9 @@ class Traversable:
ns, nm = nsParse(name) ns, nm = nsParse(name)
try: try:
next = namespaceLookup( next = namespaceLookup(
ns, nm, obj, self.REQUEST).__of__(obj) ns, nm, obj, aq_acquire(self, 'REQUEST'))
if IAcquirer.providedBy(next):
next = next.__of__(obj)
if restricted and not validate( if restricted and not validate(
obj, obj, name, next): obj, obj, name, next):
raise Unauthorized(name) raise Unauthorized(name)
...@@ -256,11 +259,10 @@ class Traversable: ...@@ -256,11 +259,10 @@ class Traversable:
except (AttributeError, NotFound, KeyError), e: except (AttributeError, NotFound, KeyError), e:
# Try to look for a view # Try to look for a view
next = queryMultiAdapter((obj, self.REQUEST), next = queryMultiAdapter((obj, aq_acquire(self, 'REQUEST')),
Interface, name) Interface, name)
if next is not None: if next is not None:
next = next.__of__(obj)
if restricted and not validate(obj, obj, name, next): if restricted and not validate(obj, obj, name, next):
raise Unauthorized(name) raise Unauthorized(name)
elif bobo_traverse is not None: elif bobo_traverse is not None:
......
...@@ -16,6 +16,8 @@ DOM implementation in ZOPE : Read-Only methods ...@@ -16,6 +16,8 @@ DOM implementation in ZOPE : Read-Only methods
All standard Zope objects support DOM to a limited extent. All standard Zope objects support DOM to a limited extent.
""" """
import Acquisition import Acquisition
from Acquisition import aq_base
from Acquisition import aq_parent
from Globals import InitializeClass from Globals import InitializeClass
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import access_contents_information from AccessControl.Permissions import access_contents_information
...@@ -149,7 +151,7 @@ class Node: ...@@ -149,7 +151,7 @@ class Node:
When this is a document this is None""" When this is a document this is None"""
node = self node = self
if hasattr(node, 'aq_parent'): if hasattr(node, 'aq_parent'):
node = self.aq_parent node = aq_parent(self)
return node.getOwnerDocument() return node.getOwnerDocument()
return node return node
...@@ -198,7 +200,7 @@ class Document(Acquisition.Explicit, Node): ...@@ -198,7 +200,7 @@ class Document(Acquisition.Explicit, Node):
This is a convenience attribute that allows direct access to This is a convenience attribute that allows direct access to
the child node that is the root element of the document. the child node that is the root element of the document.
""" """
return self.aq_parent return aq_parent(self)
# Node Methods # Node Methods
# ------------ # ------------
...@@ -219,17 +221,17 @@ class Document(Acquisition.Explicit, Node): ...@@ -219,17 +221,17 @@ class Document(Acquisition.Explicit, Node):
def getChildNodes(self): def getChildNodes(self):
"""Returns a NodeList that contains all children of this node. """Returns a NodeList that contains all children of this node.
If there are no children, this is a empty NodeList""" If there are no children, this is a empty NodeList"""
return NodeList([self.aq_parent]) return NodeList([aq_parent(self)])
def getFirstChild(self): def getFirstChild(self):
"""The first child of this node. If there is no such node """The first child of this node. If there is no such node
this returns None.""" this returns None."""
return self.aq_parent return aq_parent(self)
def getLastChild(self): def getLastChild(self):
"""The last child of this node. If there is no such node """The last child of this node. If there is no such node
this returns None.""" this returns None."""
return self.aq_parent return aq_parent(self)
def hasChildNodes(self): def hasChildNodes(self):
"""Returns true if the node has any children, false """Returns true if the node has any children, false
...@@ -324,7 +326,7 @@ class Element(Node): ...@@ -324,7 +326,7 @@ class Element(Node):
"""The node immediately preceding this node. If """The node immediately preceding this node. If
there is no such node, this returns None.""" there is no such node, this returns None."""
if hasattr(self, 'aq_parent'): if hasattr(self, 'aq_parent'):
parent = self.aq_parent parent = aq_parent(self)
ids=list(parent.objectIds()) ids=list(parent.objectIds())
id=self.id id=self.id
if type(id) is not type(''): id=id() if type(id) is not type(''): id=id()
...@@ -338,7 +340,7 @@ class Element(Node): ...@@ -338,7 +340,7 @@ class Element(Node):
"""The node immediately preceding this node. If """The node immediately preceding this node. If
there is no such node, this returns None.""" there is no such node, this returns None."""
if hasattr(self, 'aq_parent'): if hasattr(self, 'aq_parent'):
parent = self.aq_parent parent = aq_parent(self)
ids=list(parent.objectIds()) ids=list(parent.objectIds())
id=self.id id=self.id
if type(id) is not type(''): id=id() if type(id) is not type(''): id=id()
...@@ -432,7 +434,7 @@ class ElementWithTitle(Element): ...@@ -432,7 +434,7 @@ class ElementWithTitle(Element):
def getAttribute(self, name): def getAttribute(self, name):
"""Retrieves an attribute value by name.""" """Retrieves an attribute value by name."""
if name=='title' and hasattr(self.aq_base, 'title'): if name=='title' and hasattr(aq_base(self), 'title'):
return self.title return self.title
return '' return ''
......
...@@ -19,9 +19,31 @@ from zope.interface import Interface, implements ...@@ -19,9 +19,31 @@ from zope.interface import Interface, implements
from zope.component.interfaces import ComponentLookupError from zope.component.interfaces import ComponentLookupError
from zope.app.publisher.browser import getDefaultViewName from zope.app.publisher.browser import getDefaultViewName
import zExceptions import Acquisition
import Products.Five.security
from Products.Five import fivemethod
class AcquisitionBBB(object):
"""Emulate a class implementing Acquisition.interfaces.IAcquirer and
IAcquisitionWrapper.
"""
def __of__(self, context):
# Technically this isn't in line with the way Acquisition's
# __of__ works. With Acquisition, you get a wrapper around
# the original object and only that wrapper's parent is the
# new context.
return self
aq_self = aq_inner = aq_base = property(lambda self: self)
aq_chain = property(Acquisition.aq_chain)
aq_parent = property(Acquisition.aq_parent)
def aq_acquire(self, *args, **kw):
return Acquisition.aq_acquire(self, *args, **kw)
def aq_inContextOf(self, *args, **kw):
return Acquisition.aq_inContextOf(self, *args, **kw)
class IBrowserDefault(Interface): class IBrowserDefault(Interface):
"""Provide a hook for deciding about the default view for an object""" """Provide a hook for deciding about the default view for an object"""
......
...@@ -18,8 +18,24 @@ $Id$ ...@@ -18,8 +18,24 @@ $Id$
import Acquisition import Acquisition
import zope.publisher.browser import zope.publisher.browser
class BrowserView(Acquisition.Explicit, zope.publisher.browser.BrowserView): from Products.Five.bbb import AcquisitionBBB
"""Five browser view
Mixes in explicit acquisition so that security can be acquired for
views""" class BrowserView(zope.publisher.browser.BrowserView, AcquisitionBBB):
# Use an explicit __init__ to work around problems with magically inserted
# super classes when using BrowserView as a base for viewlets.
def __init__(self, context, request):
zope.publisher.browser.BrowserView.__init__(self, context, request)
# Classes which are still based on Acquisition and access
# self.context in a method need to call aq_inner on it, or get a
# funky aq_chain. We do this here for BBB friendly purposes.
def __getParent(self):
return getattr(self, '_parent', Acquisition.aq_inner(self.context))
def __setParent(self, parent):
self._parent = parent
aq_parent = __parent__ = property(__getParent, __setParent)
...@@ -15,24 +15,87 @@ ...@@ -15,24 +15,87 @@
$Id$ $Id$
""" """
import urllib
from Acquisition import aq_inner, aq_parent from Acquisition import aq_inner, aq_parent
from OFS.interfaces import ITraversable from OFS.interfaces import ITraversable
from zope.interface import implements from zope.interface import implements
from zope.component import getMultiAdapter from zope.component import getMultiAdapter
from zope.traversing.browser.interfaces import IAbsoluteURL from zope.traversing.browser.interfaces import IAbsoluteURL
from zope.traversing.browser.absoluteurl import _insufficientContext, _safe
from Products.Five.browser import BrowserView from Products.Five.browser import BrowserView
class AbsoluteURL(BrowserView): class AbsoluteURL(BrowserView):
"""An adapter for Zope3-style absolute_url using Zope2 methods """An absolute_url adapter for generic objects in Zope 2 that
aren't OFS.Traversable (e.g. views, resources, etc.).
(original: zope.traversing.browser.absoluteurl) This is very close to the generic implementation from
zope.traversing.browser, but the Zope 2 request doesn't support
all the methods that it uses yet.
""" """
implements(IAbsoluteURL) implements(IAbsoluteURL)
def __init__(self, context, request): def __unicode__(self):
self.context, self.request = context, request return urllib.unquote(self.__str__()).decode('utf-8')
def __str__(self):
context = self.context
request = self.request
container = aq_parent(context)
if container is None:
raise TypeError(_insufficientContext)
url = str(getMultiAdapter((container, request), name='absolute_url'))
name = self._getContextName(context)
if name is None:
raise TypeError(_insufficientContext)
if name:
url += '/' + urllib.quote(name.encode('utf-8'), _safe)
return url
__call__ = __str__
def _getContextName(self, context):
if getattr(context, 'getId', None) is not None:
return context.getId()
getattr(context, '__name__', None)
def breadcrumbs(self):
context = self.context
request = self.request
# We do this here do maintain the rule that we must be wrapped
container = aq_parent(context)
if container is None:
raise TypeError(_insufficientContext)
base = tuple(getMultiAdapter((container, request),
name='absolute_url').breadcrumbs())
name = self._getContextName(context)
if name is None:
raise TypeError(_insufficientContext)
if name:
base += ({'name': name,
'url': ("%s/%s" % (base[-1]['url'],
urllib.quote(name.encode('utf-8'),
_safe)))
}, )
return base
class OFSTraversableAbsoluteURL(BrowserView):
"""An absolute_url adapter for OFS.Traversable subclasses
"""
implements(IAbsoluteURL)
def __unicode__(self):
return urllib.unquote(self.__str__()).decode('utf-8')
def __str__(self): def __str__(self):
context = aq_inner(self.context) context = aq_inner(self.context)
...@@ -47,10 +110,10 @@ class AbsoluteURL(BrowserView): ...@@ -47,10 +110,10 @@ class AbsoluteURL(BrowserView):
name = context.getId() name = context.getId()
if container is None or self._isVirtualHostRoot() \ if (container is None
or not ITraversable.providedBy(container): or self._isVirtualHostRoot()
return ( or not ITraversable.providedBy(container)):
{'name': name, 'url': context.absolute_url()},) return ({'name': name, 'url': context.absolute_url()},)
view = getMultiAdapter((container, request), IAbsoluteURL) view = getMultiAdapter((container, request), IAbsoluteURL)
base = tuple(view.breadcrumbs()) base = tuple(view.breadcrumbs())
...@@ -66,15 +129,9 @@ class AbsoluteURL(BrowserView): ...@@ -66,15 +129,9 @@ class AbsoluteURL(BrowserView):
context = aq_inner(self.context) context = aq_inner(self.context)
return context.restrictedTraverse(virtualrootpath) == context return context.restrictedTraverse(virtualrootpath) == context
class SiteAbsoluteURL(AbsoluteURL): class RootAbsoluteURL(OFSTraversableAbsoluteURL):
"""An adapter for Zope3-style absolute_url using Zope2 methods """An absolute_url adapter for the root object (OFS.Application)
This one is just used to stop breadcrumbs from crumbing up
to the Zope root.
(original: zope.traversing.browser.absoluteurl)
""" """
def breadcrumbs(self): def breadcrumbs(self):
context = self.context context = self.context
request = self.request request = self.request
......
...@@ -23,6 +23,8 @@ $Id$ ...@@ -23,6 +23,8 @@ $Id$
__docformat__ = 'restructuredtext' __docformat__ = 'restructuredtext'
from warnings import warn
from zope.component import getMultiAdapter from zope.component import getMultiAdapter
from zope.component import getUtility from zope.component import getUtility
from zope.component import queryMultiAdapter from zope.component import queryMultiAdapter
...@@ -41,15 +43,13 @@ from zope.app.container.interfaces import IAdding, INameChooser ...@@ -41,15 +43,13 @@ from zope.app.container.interfaces import IAdding, INameChooser
from zope.app.container.interfaces import IContainerNamesContainer from zope.app.container.interfaces import IContainerNamesContainer
from zope.app.publisher.browser.menu import getMenu from zope.app.publisher.browser.menu import getMenu
from Acquisition import Implicit
from zExceptions import BadRequest from zExceptions import BadRequest
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from Products.Five import BrowserView from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class BasicAdding(BrowserView):
class Adding(Implicit, BrowserView):
implements(IAdding, IPublishTraverse) implements(IAdding, IPublishTraverse)
def add(self, content): def add(self, content):
...@@ -78,7 +78,7 @@ class Adding(Implicit, BrowserView): ...@@ -78,7 +78,7 @@ class Adding(Implicit, BrowserView):
# Invoke the name chooser even when we have a # Invoke the name chooser even when we have a
# name. It'll do useful things with it like converting # name. It'll do useful things with it like converting
# the incoming unicode to an ASCII string. # the incoming unicode to an ASCII string.
name = chooser.chooseName(name, content) name = chooser.chooseName(name, container)
content.id = name content.id = name
container._setObject(name, content) container._setObject(name, content)
...@@ -92,12 +92,18 @@ class Adding(Implicit, BrowserView): ...@@ -92,12 +92,18 @@ class Adding(Implicit, BrowserView):
# XXX this is definitely not right for all or even most uses # XXX this is definitely not right for all or even most uses
# of Five, but can be overridden by an AddView subclass, using # of Five, but can be overridden by an AddView subclass, using
# the class attribute of a zcml:addform directive # the class attribute of a zcml:addform directive
return absoluteURL(self.context, self.request) + '/manage_main' return str(getMultiAdapter((self.context, self.request),
name=u"absolute_url")) + '/manage_main'
# set in BrowserView.__init__ # set in BrowserView.__init__
request = None request = None
context = None context = None
def renderAddButton(self):
warn("The renderAddButton method is deprecated, use nameAllowed",
DeprecationWarning, 2)
def publishTraverse(self, request, name): def publishTraverse(self, request, name):
"""See zope.publisher.interfaces.IPublishTraverse""" """See zope.publisher.interfaces.IPublishTraverse"""
if '=' in name: if '=' in name:
...@@ -119,7 +125,7 @@ class Adding(Implicit, BrowserView): ...@@ -119,7 +125,7 @@ class Adding(Implicit, BrowserView):
factory = queryUtility(IFactory, name) factory = queryUtility(IFactory, name)
if factory is None: if factory is None:
return super(Adding, self).publishTraverse(request, name) return super(BasicAdding, self).publishTraverse(request, name)
return factory return factory
...@@ -135,10 +141,11 @@ class Adding(Implicit, BrowserView): ...@@ -135,10 +141,11 @@ class Adding(Implicit, BrowserView):
else: else:
view_name = type_name view_name = type_name
if queryMultiAdapter((self, self.request), if (queryMultiAdapter((self, self.request), name=view_name)
name=view_name) is not None: is not None):
url = "%s/%s=%s" % ( url = "%s/%s=%s" % (
absoluteURL(self, self.request), type_name, id) getMultiAdapter((self, self.request), name=u"absolute_url"),
type_name, id)
self.request.response.redirect(url) self.request.response.redirect(url)
return return
...@@ -153,10 +160,16 @@ class Adding(Implicit, BrowserView): ...@@ -153,10 +160,16 @@ class Adding(Implicit, BrowserView):
self.add(content) self.add(content)
self.request.response.redirect(self.nextURL()) self.request.response.redirect(self.nextURL())
def namesAccepted(self):
return not IContainerNamesContainer.providedBy(self.context)
def nameAllowed(self): def nameAllowed(self):
"""Return whether names can be input by the user.""" """Return whether names can be input by the user."""
return not IContainerNamesContainer.providedBy(self.context) return not IContainerNamesContainer.providedBy(self.context)
class Adding(BasicAdding):
menu_id = None menu_id = None
index = ViewPageTemplateFile("adding.pt") index = ViewPageTemplateFile("adding.pt")
......
...@@ -39,16 +39,32 @@ ...@@ -39,16 +39,32 @@
/> />
<browser:page <browser:page
for="zope.traversing.interfaces.IContainmentRoot" for="OFS.interfaces.ITraversable"
name="absolute_url" name="absolute_url"
class=".absoluteurl.SiteAbsoluteURL" class=".absoluteurl.OFSTraversableAbsoluteURL"
permission="zope.Public" permission="zope.Public"
allowed_interface="zope.traversing.browser.interfaces.IAbsoluteURL" allowed_interface="zope.traversing.browser.interfaces.IAbsoluteURL"
/> />
<view <view
for="zope.traversing.interfaces.IContainmentRoot" for="OFS.interfaces.ITraversable"
factory=".absoluteurl.SiteAbsoluteURL" factory=".absoluteurl.OFSTraversableAbsoluteURL"
type="zope.publisher.interfaces.http.IHTTPRequest"
permission="zope.Public"
provides="zope.traversing.browser.interfaces.IAbsoluteURL"
/>
<browser:page
for="OFS.interfaces.IApplication"
name="absolute_url"
class=".absoluteurl.RootAbsoluteURL"
permission="zope.Public"
allowed_interface="zope.traversing.browser.interfaces.IAbsoluteURL"
/>
<view
for="OFS.interfaces.IApplication"
factory=".absoluteurl.RootAbsoluteURL"
type="zope.publisher.interfaces.http.IHTTPRequest" type="zope.publisher.interfaces.http.IHTTPRequest"
permission="zope.Public" permission="zope.Public"
provides="zope.traversing.browser.interfaces.IAbsoluteURL" provides="zope.traversing.browser.interfaces.IAbsoluteURL"
......
...@@ -29,17 +29,17 @@ from zope.configuration.exceptions import ConfigurationError ...@@ -29,17 +29,17 @@ from zope.configuration.exceptions import ConfigurationError
from zope.publisher.interfaces.browser import IBrowserRequest, \ from zope.publisher.interfaces.browser import IBrowserRequest, \
IDefaultBrowserLayer IDefaultBrowserLayer
from zope.app.publisher.browser.viewmeta import pages as zope_app_pages import zope.app.publisher.browser.viewmeta
from zope.app.publisher.browser.viewmeta import view as zope_app_view import zope.app.pagetemplate.simpleviewclass
from zope.app.publisher.browser.viewmeta import providesCallable, \ from zope.app.publisher.browser.viewmeta import (providesCallable,
_handle_menu, _handle_for _handle_menu, _handle_for)
from Products.Five.browser import BrowserView from Products.Five.browser import BrowserView
from Products.Five.browser.resource import FileResourceFactory from Products.Five.browser.resource import FileResourceFactory
from Products.Five.browser.resource import ImageResourceFactory from Products.Five.browser.resource import ImageResourceFactory
from Products.Five.browser.resource import PageTemplateResourceFactory from Products.Five.browser.resource import PageTemplateResourceFactory
from Products.Five.browser.resource import DirectoryResourceFactory from Products.Five.browser.resource import DirectoryResourceFactory
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.Five.metaclass import makeClass from Products.Five.metaclass import makeClass
from Products.Five.security import getSecurityInfo, protectClass, protectName from Products.Five.security import getSecurityInfo, protectClass, protectName
from Products.Five.security import CheckerPrivateId from Products.Five.security import CheckerPrivateId
...@@ -159,7 +159,7 @@ def page(_context, name, permission, for_, ...@@ -159,7 +159,7 @@ def page(_context, name, permission, for_,
args = (new_class,) args = (new_class,)
) )
class pages(zope_app_pages): class pages(zope.app.publisher.browser.viewmeta.pages):
def page(self, _context, name, attribute='__call__', template=None, def page(self, _context, name, attribute='__call__', template=None,
menu=None, title=None): menu=None, title=None):
...@@ -172,7 +172,7 @@ class pages(zope_app_pages): ...@@ -172,7 +172,7 @@ class pages(zope_app_pages):
# view (named view with pages) # view (named view with pages)
class view(zope_app_view): class view(zope.app.publisher.browser.viewmeta.view):
def __call__(self): def __call__(self):
(_context, name, for_, permission, layer, class_, (_context, name, for_, permission, layer, class_,
...@@ -185,7 +185,7 @@ class view(zope_app_view): ...@@ -185,7 +185,7 @@ class view(zope_app_view):
for pname, attribute, template in self.pages: for pname, attribute, template in self.pages:
if template: if template:
cdict[pname] = ZopeTwoPageTemplateFile(template) cdict[pname] = ViewPageTemplateFile(template)
if attribute and attribute != name: if attribute and attribute != name:
cdict[attribute] = cdict[pname] cdict[attribute] = cdict[pname]
else: else:
...@@ -209,9 +209,9 @@ class view(zope_app_view): ...@@ -209,9 +209,9 @@ class view(zope_app_view):
view = component.queryMultiAdapter((self, request), name=name, view = component.queryMultiAdapter((self, request), name=name,
default=None) default=None)
if view is not None: if view is not None:
return view.__of__(self) return view
m = class_.publishTraverse.__get__(self).__of__(self) m = class_.publishTraverse.__get__(self)
return m(request, name) return m(request, name)
else: else:
...@@ -223,7 +223,7 @@ class view(zope_app_view): ...@@ -223,7 +223,7 @@ class view(zope_app_view):
view = component.queryMultiAdapter((self, request), name=name, view = component.queryMultiAdapter((self, request), name=name,
default=None) default=None)
if view is not None: if view is not None:
return view.__of__(self) return view
raise NotFoundError(self, name, request) raise NotFoundError(self, name, request)
...@@ -389,39 +389,29 @@ def resourceDirectory(_context, name, directory, layer=IDefaultBrowserLayer, ...@@ -389,39 +389,29 @@ def resourceDirectory(_context, name, directory, layer=IDefaultBrowserLayer,
args = (new_class,) args = (new_class,)
) )
# class ViewMixinForAttributes(BrowserView,
# mixin classes / class factories zope.app.publisher.browser.viewmeta.simple):
#
class ViewMixinForAttributes(BrowserView):
# we have an attribute that we can simply tell ZPublisher to go to # For some reason, the 'simple' baseclass doesn't implement this
def __browser_default__(self, request): # mandatory method (see https://bugs.launchpad.net/zope3/+bug/129296)
return self, (self.__page_attribute__,) def browserDefault(self, request):
return getattr(self, self.__page_attribute__), ()
# this is technically not needed because ZPublisher finds our # __call__ should have the same signature as the original method
# attribute through __browser_default__; but we also want to be @property
# able to call pages from python modules, PythonScripts or ZPT def __call__(self):
__call__ = property(lambda self: getattr(self, self.__page_attribute__)) return getattr(self, self.__page_attribute__)
class ViewMixinForTemplates(BrowserView):
# short cut to get to macros more easily
def __getitem__(self, name):
if name == 'macros':
return self.index.macros
return self.index.macros[name]
# make the template publishable class ViewMixinForTemplates(BrowserView,
def __call__(self, *args, **kw): zope.app.pagetemplate.simpleviewclass.simple):
return self.index(self, *args, **kw) pass
def makeClassForTemplate(filename, globals=None, used_for=None, def makeClassForTemplate(filename, globals=None, used_for=None,
bases=(), cdict=None, name=u''): bases=(), cdict=None, name=u''):
# XXX needs to deal with security from the bases? # XXX needs to deal with security from the bases?
if cdict is None: if cdict is None:
cdict = {} cdict = {}
cdict.update({'index': ZopeTwoPageTemplateFile(filename, globals), cdict.update({'index': ViewPageTemplateFile(filename, globals),
'__name__': name}) '__name__': name})
bases += (ViewMixinForTemplates,) bases += (ViewMixinForTemplates,)
class_ = makeClass("SimpleViewClass from %s" % filename, bases, cdict) class_ = makeClass("SimpleViewClass from %s" % filename, bases, cdict)
......
...@@ -15,85 +15,84 @@ ...@@ -15,85 +15,84 @@
$Id$ $Id$
""" """
import os, sys from os.path import basename
from zope.app.pagetemplate import viewpagetemplatefile
from Acquisition import aq_inner from Acquisition import aq_get
from Globals import package_home from AccessControl import getSecurityManager
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PageTemplates.Expressions import SecureModuleImporter from Products.PageTemplates.Expressions import SecureModuleImporter
from Products.PageTemplates.Expressions import createTrustedZopeEngine from Products.PageTemplates.Expressions import createTrustedZopeEngine
from zope.app.pagetemplate.viewpagetemplatefile import ViewMapper
from Products.Five.bbb import AcquisitionBBB
_engine = createTrustedZopeEngine() _engine = createTrustedZopeEngine()
def getEngine(): def getEngine():
return _engine return _engine
class ZopeTwoPageTemplateFile(PageTemplateFile): class ViewPageTemplateFile(viewpagetemplatefile.ViewPageTemplateFile):
"""A strange hybrid between Zope 2 and Zope 3 page template. """Page Template used as class variable of views defined as Python classes.
Uses Zope 2's engine, but with security disabled and with some
initialization and API from Zope 3.
""" """
def __init__(self, filename, _prefix=None, content_type=None): def getId(self):
# XXX doesn't use content_type yet return basename(self.filename)
id = property(getId)
def __call__(self, __instance, *args, **keywords):
instance = __instance
namespace = self.pt_getContext(
request=instance.request,
instance=instance, args=args, options=keywords)
debug_flags = instance.request.debug
s = self.pt_render(
namespace,
showtal=getattr(debug_flags, 'showTAL', 0),
sourceAnnotations=getattr(debug_flags, 'sourceAnnotations', 0),
)
response = instance.request.response
if not response.getHeader("Content-Type"):
response.setHeader("Content-Type", self.content_type)
return s
self.ZBindings_edit(self._default_bindings) def pt_getEngine(self):
return getEngine()
path = self.get_path_from_prefix(_prefix) def pt_getContext(self, instance, request, **kw):
self.filename = os.path.join(path, filename) context = super(ViewPageTemplateFile, self).pt_getContext(
if not os.path.isfile(self.filename): instance, request, **kw)
raise ValueError("No such file", self.filename)
basepath, ext = os.path.splitext(self.filename) # get the root
self.__name__ = os.path.basename(basepath) obj = context['context']
root = None
meth = aq_get(obj, 'getPhysicalRoot', None)
if meth is not None:
root = meth()
super(PageTemplateFile, self).__init__(self.filename, _prefix) context.update(here=context['context'],
# philiKON thinks container should be the view,
# but BBB is more important than aesthetics.
container=context['context'],
root=root,
modules=SecureModuleImporter,
traverse_subpath=[], # BBB, never really worked
user = getSecurityManager().getUser()
)
return context
def get_path_from_prefix(self, _prefix): def __get__(self, instance, type):
if isinstance(_prefix, str): return BoundPageTemplate(self, instance)
path = _prefix
else:
if _prefix is None: # When a view's template is accessed e.g. as template.view, a
_prefix = sys._getframe(2).f_globals # BoundPageTemplate object is retured. For BBB reasons, it needs to
path = package_home(_prefix) # support the aq_* methods and attributes known from Acquisition. For
return path # that it also needs to be locatable thru __parent__.
class BoundPageTemplate(viewpagetemplatefile.BoundPageTemplate,
AcquisitionBBB):
__parent__ = property(lambda self: self.im_self)
def pt_getEngine(self):
return getEngine()
def pt_getContext(self): # BBB
try: ZopeTwoPageTemplateFile = ViewPageTemplateFile
root = self.getPhysicalRoot()
except AttributeError:
try:
root = self.context.getPhysicalRoot()
except AttributeError:
root = None
# Even if the context isn't a view (when would that be exaclty?),
# there shouldn't be any dange in applying a view, because it
# won't be used. However assuming that a lack of getPhysicalRoot
# implies a missing view causes problems.
view = self._getContext()
here = aq_inner(self.context)
request = getattr(root, 'REQUEST', None)
c = {'template': self,
'here': here,
'context': here,
'container': here,
'nothing': None,
'options': {},
'root': root,
'request': request,
'modules': SecureModuleImporter,
}
if view is not None:
c['view'] = view
c['views'] = ViewMapper(here, request)
return c
ViewPageTemplateFile = ZopeTwoPageTemplateFile
...@@ -12,21 +12,23 @@ ...@@ -12,21 +12,23 @@
# #
############################################################################## ##############################################################################
"""Provider expression. """Provider expression.
$Id$
""" """
import zope.event
import zope.interface
import zope.component import zope.component
from zope.contentprovider import interfaces as cp_interfaces
from zope.contentprovider.tales import addTALNamespaceData
from zope.interface import implements
from zope.tales.expressions import StringExpr
class Z2ProviderExpression(StringExpr): from zope.tales import expressions
"""Create a custom provider expression which overrides __call__ to from zope.contentprovider import interfaces, tales
acquisition wrap the provider so that security lookups can be done.""" from zope.location.interfaces import ILocation
from Acquisition.interfaces import IAcquirer
implements(cp_interfaces.ITALESProviderExpression) class Z2ProviderExpression(expressions.StringExpr):
zope.interface.implements(interfaces.ITALESProviderExpression)
# This is mostly a copy of
# zope.contentprovider.tales.TALESProviderExpression's __call__
# method.
def __call__(self, econtext): def __call__(self, econtext):
name = super(Z2ProviderExpression, self).__call__(econtext) name = super(Z2ProviderExpression, self).__call__(econtext)
context = econtext.vars['context'] context = econtext.vars['context']
...@@ -35,19 +37,26 @@ class Z2ProviderExpression(StringExpr): ...@@ -35,19 +37,26 @@ class Z2ProviderExpression(StringExpr):
# Try to look up the provider. # Try to look up the provider.
provider = zope.component.queryMultiAdapter( provider = zope.component.queryMultiAdapter(
(context, request, view), cp_interfaces.IContentProvider, name) (context, request, view), interfaces.IContentProvider, name)
# Provide a useful error message, if the provider was not found. # Provide a useful error message, if the provider was not found.
if provider is None: if provider is None:
raise cp_interfaces.ContentProviderLookupError(name) raise interfaces.ContentProviderLookupError(name)
# add the __name__ attribute if it implements ILocation
if ILocation.providedBy(provider):
provider.__name__ = name
if getattr(provider, '__of__', None) is not None: # ATTN: This is where we are different from
# TALESProviderExpression: We support Acquisition wrapping.
if IAcquirer.providedBy(provider):
provider = provider.__of__(context) provider = provider.__of__(context)
# Insert the data gotten from the context # Insert the data gotten from the context
addTALNamespaceData(provider, econtext) tales.addTALNamespaceData(provider, econtext)
# Stage 1: Do the state update. # Stage 1: Do the state update.
zope.event.notify(interfaces.BeforeUpdateEvent(provider, request))
provider.update() provider.update()
# Stage 2: Render the HTML content. # Stage 2: Render the HTML content.
......
...@@ -18,111 +18,60 @@ $Id$ ...@@ -18,111 +18,60 @@ $Id$
import os import os
import urllib import urllib
import Acquisition
from OFS.Traversable import Traversable as OFSTraversable
from zope.app.publisher.browser.resources import empty
from zope.app.publisher.fileresource import File, Image
from zope.app.publisher.pagetemplateresource import PageTemplate
from zope.interface import implements from zope.interface import implements
from zope.component import getMultiAdapter from zope.component import getMultiAdapter
from zope.component.interfaces import IResource from zope.traversing.browser import absoluteURL
from zope.datetime import time as timeFromDateTimeString from zope.publisher.interfaces import NotFound
from zope.traversing.browser.interfaces import IAbsoluteURL from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.app.publisher.browser import fileresource, directoryresource
from zope.app.publisher.fileresource import File, Image
from zope.app.publisher.pagetemplateresource import PageTemplate
from Products.Five.browser import BrowserView from Products.Five.browser import BrowserView
_marker = []
class Resource(Acquisition.Explicit): _marker = object()
"""A publishable resource
""" class Resource(object):
implements(IResource) """A mixin that changes the URL-rendering of resources (__call__).
def __init__(self, request): In Zope 3, resource URLs are of the form
self.request = request nearest_site/@@/resource_name. Since Zope 2 didn't have support
for sites from the beginning of the Five integration, resource
URLs in Zope 2 are of the form context/++resource++resource_name.
TODO It would be good if that could be changed in the long term,
thus making this mixin (and probably the other classes in this
module) obsolete.
"""
def __call__(self): def __call__(self):
name = self.__name__ name = self.__name__
container = self.__parent__ container = self.__parent__
# TODO Zope 3 uses site = getSite() instead of container here url = urllib.unquote(absoluteURL(container, self.request))
# and the @@ resource access view
url = str(getMultiAdapter((container, self.request), IAbsoluteURL))
url = urllib.unquote(url)
if not isinstance(container, DirectoryResource): if not isinstance(container, DirectoryResource):
name = '++resource++%s' % name name = '++resource++%s' % name
return "%s/%s" % (url, name) return "%s/%s" % (url, name)
class PageTemplateResource(BrowserView, Resource): class PageTemplateResource(Resource, BrowserView):
#implements(IBrowserPublisher) implements(IBrowserPublisher)
def browserDefault(self, request):
return self.render, ()
def __browser_default__(self, request): def publishTraverse(self, request, name):
return self, ('render',) raise NotFound(self, name, request)
def render(self): def render(self):
"""Rendered content""" """Rendered content"""
# ZPublisher might have called setBody with an incorrect URL # ZPublisher might have called setBody with an incorrect URL
# we definitely don't want that if we are plain html # we definitely don't want that if we are plain html
self.request.RESPONSE.setBase(None) self.request.response.setBase(None)
pt = self.context pt = self.context
return pt(self.request) return pt(self.request)
class FileResource(BrowserView, Resource): class FileResource(Resource, fileresource.FileResource):
"""A publishable file-based resource""" pass
#implements(IBrowserPublisher)
def __browser_default__(self, request):
return self, (request.REQUEST_METHOD,)
def GET(self):
"""Default content"""
file = self.context
request = self.request
response = request.response
# HTTP If-Modified-Since header handling. This is duplicated
# from OFS.Image.Image - it really should be consolidated
# somewhere...
header = request.environ.get('If-Modified-Since', None)
if header is not None:
header = header.split(';')[0]
# Some proxies seem to send invalid date strings for this
# header. If the date string is not valid, we ignore it
# rather than raise an error to be generally consistent
# with common servers such as Apache (which can usually
# understand the screwy date string as a lucky side effect
# of the way they parse it).
try: mod_since=long(timeFromDateTimeString(header))
except: mod_since=None
if mod_since is not None:
if getattr(file, 'lmt', None):
last_mod = long(file.lmt)
else:
last_mod = long(0)
if last_mod > 0 and last_mod <= mod_since:
response.setStatus(304)
return ''
response.setHeader('Content-Type', file.content_type)
response.setHeader('Last-Modified', file.lmh)
# Cache for one day
response.setHeader('Cache-Control', 'public,max-age=86400')
f = open(file.path, 'rb')
data = f.read()
f.close()
return data
def HEAD(self):
file = self.context
response = self.request.response
response = self.request.response
response.setHeader('Content-Type', file.content_type)
response.setHeader('Last-Modified', file.lmh)
# Cache for one day
response.setHeader('Cache-Control', 'public,max-age=86400')
return ''
class ResourceFactory: class ResourceFactory:
...@@ -173,8 +122,7 @@ class Directory: ...@@ -173,8 +122,7 @@ class Directory:
self.path = path self.path = path
self.__name__ = name self.__name__ = name
class DirectoryResource(BrowserView, Resource, OFSTraversable): class DirectoryResource(Resource, directoryresource.DirectoryResource):
#implements(IBrowserPublisher)
resource_factories = { resource_factories = {
'gif': ImageResourceFactory, 'gif': ImageResourceFactory,
...@@ -187,28 +135,12 @@ class DirectoryResource(BrowserView, Resource, OFSTraversable): ...@@ -187,28 +135,12 @@ class DirectoryResource(BrowserView, Resource, OFSTraversable):
default_factory = FileResourceFactory default_factory = FileResourceFactory
def __init__(self, context, request):
BrowserView.__init__(self, context, request)
# OFSTraversable.absolute_url() assumes self.REQUEST being
# accessible:
self.REQUEST = request
def getId(self): def getId(self):
name = self.__name__ name = self.__name__
if not name.startswith('++resource++'): if not name.startswith('++resource++'):
name = '++resource++%s' % self.__name__ name = '++resource++%s' % self.__name__
return name return name
def __browser_default__(self, request):
'''See interface IBrowserPublisher'''
return empty, ()
def __getitem__(self, name):
res = self.get(name, None)
if res is None:
raise KeyError, name
return res
def get(self, name, default=_marker): def get(self, name, default=_marker):
path = self.context.path path = self.context.path
filename = os.path.join(path, name) filename = os.path.join(path, name)
...@@ -229,11 +161,7 @@ class DirectoryResource(BrowserView, Resource, OFSTraversable): ...@@ -229,11 +161,7 @@ class DirectoryResource(BrowserView, Resource, OFSTraversable):
resource = factory(name, filename)(self.request) resource = factory(name, filename)(self.request)
resource.__name__ = name resource.__name__ = name
resource.__parent__ = self resource.__parent__ = self
# XXX __of__ wrapping is usually done on traversal. return resource
# However, we don't want to subclass Traversable (or do we?)
# The right thing should probably be a specific (and very simple)
# traverser that does __getitem__ and __of__.
return resource.__of__(self)
class DirectoryResourceFactory(ResourceFactory): class DirectoryResourceFactory(ResourceFactory):
......
##############################################################################
#
# Copyright (c) 2007 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Legacy browser view tests.
Here we nake sure that legacy implementations of views (e.g. those
which mix-in one of the Acquisition base classes without knowing
better) still work.
"""
import Acquisition
import OFS.SimpleItem
from zope.interface import implements
from zope.traversing.interfaces import ITraversable
from zope.contentprovider.interfaces import IContentProvider
from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class LegacyAttributes(BrowserView):
"""Make sure that those old aq_* attributes on Five BrowserViews
still work, in particular aq_chain, even though BrowserView may
not be an Acquisition-decendant class anymore...
"""
def __call__(self):
return repr([obj for obj in self.aq_chain])
class ExplicitLegacyAttributes(Acquisition.Explicit):
"""Make sure that those old aq_* attributes work on browser views
that only inherit from Explicit as well."""
def __call__(self):
return repr([obj for obj in self.aq_chain])
class LegacyTemplate(BrowserView):
template = ViewPageTemplateFile('falcon.pt')
def __call__(self):
return self.template()
class LegacyTemplateTwo(BrowserView):
def __init__(self, context, request):
self.__parent__ = context
self.context = context
self.request = request
self.template = ViewPageTemplateFile('falcon.pt')
def __call__(self):
return self.template()
class Explicit(Acquisition.Explicit):
def render(self):
return 'Explicit'
class ExplicitWithTemplate(Acquisition.Explicit):
template = ViewPageTemplateFile('falcon.pt')
class Implicit(Acquisition.Implicit):
def render(self):
return 'Implicit'
class ImplicitWithTemplate(Acquisition.Implicit):
template = ViewPageTemplateFile('falcon.pt')
class ExplicitContentProvider(Acquisition.Explicit):
implements(IContentProvider)
def __init__(self, context, request, view):
self.context = context
self.request = request
self.view = view
# Normally, a content provider should set __parent__ to view
# or context. This one doesn't do this on purpose to ensure
# it works without.
def update(self):
# Make sure that the content provider is acquisition wrapped.
assert self.aq_parent == self.context
assert self.aq_base == self
def render(self):
return 'Content provider inheriting from Explicit'
class ExplicitViewlet(Acquisition.Explicit):
def __init__(self, context, request, view, manager):
self.context = context
self.request = request
def update(self):
# Make sure that the viewlet has the legacy attributes and
# they point to the right objects.
assert self.aq_parent == self.context
assert self.aq_base == self
def render(self):
return 'Viewlet inheriting from Explicit'
class BrowserViewViewlet(BrowserView):
def __init__(self, context, request, view, manager):
# This is the tricky bit. super(...).__init__ wouldn't
# necessarily have to resolve to BrowserView.__init__ because
# <browser:viewlet /> generates classes on the fly with a
# mix-in base class...
super(BrowserViewViewlet, self).__init__(context, request)
self.view = view
self.manager = manager
def update(self):
pass
def render(self):
return 'BrowserView viewlet'
class LegacyNamespace(object):
implements(ITraversable)
def __init__(self, context, request):
self.context = context
self.request = request
def traverse(self, name, ignored):
return LegacyNamespaceObject(name)
class LegacyNamespaceObject(OFS.SimpleItem.SimpleItem):
def __init__(self, name):
self.id = name
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<browser:page
for="*"
name="attributes"
class=".aqlegacy.LegacyAttributes"
permission="zope.Public"
/>
<browser:page
for="*"
name="explicitattributes"
class=".aqlegacy.ExplicitLegacyAttributes"
permission="zope.Public"
/>
<browser:page
for="*"
name="template"
class=".aqlegacy.LegacyTemplate"
permission="zope.Public"
/>
<browser:page
for="*"
name="template_two"
class=".aqlegacy.LegacyTemplateTwo"
permission="zope.Public"
/>
<browser:page
for="*"
name="explicit"
class=".aqlegacy.Explicit"
attribute="render"
permission="zope.Public"
/>
<browser:page
for="*"
name="explicit_zcmltemplate"
class=".aqlegacy.Explicit"
template="falcon.pt"
permission="zope.Public"
/>
<browser:page
for="*"
name="explicit_template"
class=".aqlegacy.ExplicitWithTemplate"
attribute="template"
permission="zope.Public"
/>
<browser:page
for="*"
name="implicit"
class=".aqlegacy.Implicit"
attribute="render"
permission="zope.Public"
/>
<browser:page
for="*"
name="implicit_template"
class=".aqlegacy.ImplicitWithTemplate"
attribute="template"
permission="zope.Public"
/>
<browser:page
for="*"
name="implicit_zcmltemplate"
class=".aqlegacy.Implicit"
template="falcon.pt"
permission="zope.Public"
/>
<!-- Content providers and viewlets -->
<adapter
for="* * *"
provides="zope.contentprovider.interfaces.IContentProvider"
factory=".aqlegacy.ExplicitContentProvider"
name="aqlegacyprovider"
/>
<browser:page
for="*"
name="aqlegacyprovider"
template="legacyprovider.pt"
permission="zope.Public"
/>
<browser:viewletManager
name="aqlegacymanager"
permission="zope.Public"
/>
<browser:viewlet
for="*"
class=".aqlegacy.ExplicitViewlet"
name="explicit"
permission="zope.Public"
/>
<browser:viewlet
for="*"
class=".aqlegacy.BrowserViewViewlet"
name="browserview"
permission="zope.Public"
/>
<browser:page
for="*"
name="aqlegacymanager"
template="legacymanager.pt"
permission="zope.Public"
/>
<!-- Namespace traversal -->
<adapter
for="*"
factory=".aqlegacy.LegacyNamespace"
name="aqlegacy"
/>
<adapter
for="* *"
factory=".aqlegacy.LegacyNamespace"
name="aqlegacy"
/>
<browser:page
for=".aqlegacy.LegacyNamespaceObject"
name="index.html"
template="falcon.pt"
permission="zope.Public"
/>
</configure>
\ No newline at end of file
Testing legacy browser views
============================
This test tests publishing aspects of browser pages. Let's register
some:
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('aqlegacy.zcml', package=Products.Five.browser.tests)
>>> from Products.Five.testbrowser import Browser
>>> browser = Browser()
>>> browser.handleErrors = False
Acquisition API legacy on BrowserView
-------------------------------------
Let's make sure that accessing those old aq_* properties on browser
views still works (the printed output is the aq_chain of the view):
>>> browser.open('http://localhost/test_folder_1_/attributes')
>>> print browser.contents
[<Products.Five.metaclass.LegacyAttributes object at ...>,
<Folder at /test_folder_1_>,
<Application at >,
<ZPublisher.BaseRequest.RequestContainer object at ...>]
The same goes for browser views that just mix in Acquisition.Explicit:
>>> browser.open('http://localhost/test_folder_1_/explicitattributes')
>>> print browser.contents
[<Products.Five.metaclass.ExplicitLegacyAttributes object at ...>,
<Folder at /test_folder_1_>,
<Application at >,
<ZPublisher.BaseRequest.RequestContainer object at ...>]
Let's do some more manual tests with the view object. But first we
must get it:
>>> from zope.component import getMultiAdapter
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> view = getMultiAdapter((self.folder, request), name='attributes')
Let's check for the various aq_* attributes:
>>> view.aq_parent == self.folder
True
>>> view.aq_inner == view
True
>>> view.aq_base == view
True
>>> view.aq_self == view
True
Let's try to acquire something from the root folder:
>>> button = view.aq_acquire('ZopeAttributionButton')
>>> print button()
<a href="http://www.zope.org/Credits" target="_top"><img src="http://nohost/p_/ZopeButton" width="115" height="50" border="0" alt="Powered by Zope" /></a>
Let's check that we're in the right context:
>>> view.aq_inContextOf(self.folder)
1
>>> view.aq_inContextOf(self.app)
1
>>> view.aq_inContextOf(object())
0
Views also still support the __of__ protocol, at least pro forma:
>>> view == view.__of__(self.app)
True
Acquisition API legacy on a browser view's template
---------------------------------------------------
A view's template will also support the various aq_* attributes:
>>> view = getMultiAdapter((self.folder, request), name='template')
>>> template = view.template
>>> template.aq_parent == view
True
>>> template.aq_inner == template
True
>>> template.aq_base == template
True
>>> template.aq_self == template
True
>>> button = template.aq_acquire('ZopeAttributionButton')
>>> print button()
<a href="http://www.zope.org/Credits" target="_top"><img src="http://nohost/p_/ZopeButton" width="115" height="50" border="0" alt="Powered by Zope" /></a>
Mixing in Acquisition.{Ex|Im}plicit
-----------------------------------
Let's make sure that mixing in Acquisition.Explicit or Implicit won't
mess up your views (even though you should never have done it in the
first place...):
>>> browser.open('http://localhost/test_folder_1_/explicit')
>>> print browser.contents
Explicit
>>> browser.open('http://localhost/test_folder_1_/explicit_zcmltemplate')
>>> print browser.contents
<p>The falcon has taken flight</p>
>>> browser.open('http://localhost/test_folder_1_/explicit_template')
>>> print browser.contents
<p>The falcon has taken flight</p>
>>> browser.open('http://localhost/test_folder_1_/implicit')
>>> print browser.contents
Implicit
>>> browser.open('http://localhost/test_folder_1_/implicit_template')
>>> print browser.contents
<p>The falcon has taken flight</p>
>>> browser.open('http://localhost/test_folder_1_/implicit_zcmltemplate')
>>> print browser.contents
<p>The falcon has taken flight</p>
Testing legacy content providers and viewlets
=============================================
>>> browser.open('http://localhost/test_folder_1_/aqlegacyprovider')
>>> print browser.contents
<p>Content provider inheriting from Explicit</p>
>>> browser.open('http://localhost/test_folder_1_/aqlegacymanager')
>>> print browser.contents
<p>BrowserView viewlet
Viewlet inheriting from Explicit</p>
Testing namespace traversal
===========================
Namespace traversal can turn up objects during traversal without
attribute access. That means they might not be wrapped by default.
Here we make sure that they are wrapped and that things like the
request can be acquired.
First let's try ``restrictedTraverse()``:
>>> foo = self.folder.restrictedTraverse('++aqlegacy++foo')
>>> import Acquisition
>>> Acquisition.aq_acquire(foo, 'REQUEST')
<HTTPRequest, URL=http://nohost>
Now let's try URL traversal:
>>> browser.open('http://localhost/test_folder_1_/++aqlegacy++foo/index.html')
>>> print browser.contents
<p>The falcon has taken flight</p>
Testing keyword arguments
=========================
ViewPageTemplateFile's take arbitrary keyword arguments:
>>> view = getMultiAdapter((self.folder, request), name='template')
>>> template = view.template
>>> print template(foo=1, bar=2)
<p>The falcon has taken flight</p>
Passing in an argument called instance was supported by the old Five version
of ViewPageTemplateFile, so we still need to support it.
In the zope.app.pagetemplate version, the first required argument is called
instance, though.
>>> print template(instance='allowed')
<p>The falcon has taken flight</p>
No arguments required
=====================
ViewPageTemplateFile's require no arguments, but you can only use them as
class variables:
>>> view = getMultiAdapter((self.folder, request), name='template_two')
>>> print view()
Traceback (most recent call last):
...
TypeError: __call__() takes at least 2 arguments (1 given)
Clean up
--------
>>> from zope.app.testing.placelesssetup import tearDown
>>> tearDown()
<p tal:content="provider:aqlegacymanager" />
<p tal:content="provider:aqlegacyprovider" />
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
$Id$ $Id$
""" """
from Products.Five import BrowserView from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class SimpleView(BrowserView): class SimpleView(BrowserView):
"""More docstring. Please Zope""" """More docstring. Please Zope"""
...@@ -39,6 +40,10 @@ class CallView(BrowserView): ...@@ -39,6 +40,10 @@ class CallView(BrowserView):
def __call__(self): def __call__(self):
return u"I was __call__()'ed" return u"I was __call__()'ed"
class CallTemplate(BrowserView):
__call__ = ViewPageTemplateFile('falcon.pt')
class CallableNoDocstring: class CallableNoDocstring:
def __call__(self): def __call__(self):
...@@ -58,7 +63,7 @@ class NoDocstringView(BrowserView): ...@@ -58,7 +63,7 @@ class NoDocstringView(BrowserView):
class NewStyleClass(object): class NewStyleClass(object):
""" """
This is a testclass to verify that new style classes are ignored This is a testclass to verify that new style classes work
in browser:page in browser:page
""" """
......
...@@ -157,7 +157,7 @@ Make sure that global template variables in ZPT pages are correct: ...@@ -157,7 +157,7 @@ Make sure that global template variables in ZPT pages are correct:
>>> print view() >>> print view()
View is a view: True View is a view: True
Context is testoid: True Context is testoid: True
Contaxt.aq_parent is test_folder_1_: True Context.aq_parent is test_folder_1_: True
Container is context: True Container is context: True
Here is context: True Here is context: True
Nothing is None: True Nothing is None: True
...@@ -215,36 +215,42 @@ high-level security tests). Let's manually look up a protected view: ...@@ -215,36 +215,42 @@ high-level security tests). Let's manually look up a protected view:
It's protecting the object with the permission, and not the attribute, It's protecting the object with the permission, and not the attribute,
so we get ('',) instead of ('eagle',): so we get ('',) instead of ('eagle',):
>>> getattr(view, '__ac_permissions__') >>> view.__ac_permissions__
(('View management screens', ('',)),) (('View management screens', ('',)),)
Wrap into an acquisition so that imPermissionRole objects can be The view's __roles__ attribute can be evaluated correctly:
evaluated. __roles__ is a imPermissionRole object:
>>> view = view.__of__(self.folder.testoid) (We have to use aq_acquire here instead of a simple getattr. The
>>> view_roles = getattr(view, '__roles__', None) reason is that __roles__ actually is an object that expects being
>>> view_roles called through the __of__ protocol upon which it renders the roles
tuple. aq_acquire will trigger this for us. This isn't a problem,
really, because AccessControl ends up using aq_acquire anyway, so it
Just Works.)
>>> from Acquisition import aq_acquire
>>> aq_acquire(view, '__roles__')
('Manager',) ('Manager',)
Check to see if view's context properly acquires its true Check to see if view's context properly acquires its true
parent parent
>>> from Acquisition import aq_parent, aq_base, aq_inner >>> from Acquisition import aq_parent, aq_base, aq_inner
>>> context = getattr(view, 'context') >>> context = view.context
Check the wrapper type Check the wrapper type
>>> from Acquisition import ImplicitAcquisitionWrapper >>> from Acquisition import ImplicitAcquisitionWrapper
>>> type(context) == ImplicitAcquisitionWrapper >>> type(context) == ImplicitAcquisitionWrapper
True True
The acquired parent is the view. This isn't
usually what you want.
>>> aq_parent(context) == view The parent of the view is the view's context:
True
>>> view.__parent__ == view.context
True
>>> aq_parent(view) == view.context
True
To get what you usually want, do this The direct parent of the context is
>>> context.aq_inner.aq_parent >>> context.aq_inner.aq_parent
<Folder at /test_folder_1_> <Folder at /test_folder_1_>
......
...@@ -161,6 +161,13 @@ ...@@ -161,6 +161,13 @@
permission="zope2.Public" permission="zope2.Public"
/> />
<browser:page
for="Products.Five.tests.testing.simplecontent.ISimpleContent"
class=".pages.CallTemplate"
name="calltemplate.html"
permission="zope2.Public"
/>
<!-- pages from methods/functions/callables that don't have docstrings --> <!-- pages from methods/functions/callables that don't have docstrings -->
<browser:pages <browser:pages
for="Products.Five.tests.testing.simplecontent.ISimpleContent" for="Products.Five.tests.testing.simplecontent.ISimpleContent"
......
...@@ -144,6 +144,14 @@ class bearing only a __call__ method: ...@@ -144,6 +144,14 @@ class bearing only a __call__ method:
... ...
I was __call__()'ed I was __call__()'ed
or a __call__ object that's callable, such as a ViewPageTemplateFile:
>>> print http(r'''
... GET /test_folder_1_/testoid/calltemplate.html HTTP/1.1
... ''')
HTTP/1.1 200 OK
...
<p>The falcon has taken flight</p>
Clean up Clean up
-------- --------
......
...@@ -188,18 +188,15 @@ information is used: ...@@ -188,18 +188,15 @@ information is used:
</body> </body>
</html> </html>
Now we test a provider using a PageTemplateFile to render itself. It must Now we test a provider using a PageTemplateFile to render itself:
inherit from an Acquisition base class so that the template can use Zope 2
security mechanisms:
>>> import os, tempfile >>> import os, tempfile
>>> temp_dir = tempfile.mkdtemp() >>> temp_dir = tempfile.mkdtemp()
>>> dynTemplate = os.path.join(temp_dir, 'dynamic_template.pt') >>> dynTemplate = os.path.join(temp_dir, 'dynamic_template.pt')
>>> open(dynTemplate, 'w').write( >>> open(dynTemplate, 'w').write(
... 'A simple template: <tal:simple replace="python:view.my_property" />') ... 'A simple template: <tal:simple replace="python:view.my_property" />')
>>> from Acquisition import Explicit
>>> from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile >>> from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
>>> class TemplateProvider(Explicit): >>> class TemplateProvider(object):
... zope.component.adapts(zope.interface.Interface, ... zope.component.adapts(zope.interface.Interface,
... browser.IDefaultBrowserLayer, ... browser.IDefaultBrowserLayer,
... zope.interface.Interface) ... zope.interface.Interface)
......
...@@ -33,6 +33,15 @@ Image resource ...@@ -33,6 +33,15 @@ Image resource
HTTP/1.1 200 OK HTTP/1.1 200 OK
... ...
Image resources can't be traversed further:
>>> print http(r'''
... GET /test_folder_1_/testoid/++resource++pattern.png/more HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 404 Not Found
...
File resource File resource
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
...@@ -43,6 +52,15 @@ File resource ...@@ -43,6 +52,15 @@ File resource
HTTP/1.1 200 OK HTTP/1.1 200 OK
... ...
File resources can't be traversed further:
>>> print http(r'''
... GET /test_folder_1_/testoid/++resource++style.css/more HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 404 Not Found
...
Template resource Template resource
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
...@@ -53,6 +71,15 @@ Template resource ...@@ -53,6 +71,15 @@ Template resource
HTTP/1.1 200 OK HTTP/1.1 200 OK
... ...
Template resources can't be traversed further:
>>> print http(r'''
... GET /test_folder_1_/testoid/++resource++cockatiel.html/more HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 404 Not Found
...
Resource directory Resource directory
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
...@@ -66,18 +93,6 @@ Page templates aren't guaranteed to render, so exclude them from the test: ...@@ -66,18 +93,6 @@ Page templates aren't guaranteed to render, so exclude them from the test:
... self.assertEquals(200, response.getStatus()) ... self.assertEquals(200, response.getStatus())
We also can traverse into sub-directories:
>>> print http(r'''
... GET /test_folder_1_/testoid/++resource++fivetest_resources/resource_subdir/resource.txt HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 200 OK
...
This is a resource in a subdirectory of a normal resource to test traversal.
<BLANKLINE>
We also can traverse into sub-directories: We also can traverse into sub-directories:
>>> print http(r''' >>> print http(r'''
...@@ -105,6 +120,13 @@ We also can traverse into sub-directories: ...@@ -105,6 +120,13 @@ We also can traverse into sub-directories:
</html> </html>
<BLANKLINE> <BLANKLINE>
>>> print http(r'''
... GET /test_folder_1_/testoid/++resource++fivetest_resources/resource_subdir/not-existant HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 404 Not Found
...
Clean up Clean up
-------- --------
......
View is a view: <tal:block View is a view: <tal:block
content="python:hasattr(view,'context') and hasattr(view, 'request')" /> content="python:hasattr(view,'context') and hasattr(view, 'request')" />
Context is testoid: <tal:block content="python:context.id == 'testoid'" /> Context is testoid: <tal:block content="python:context.id == 'testoid'" />
Contaxt.aq_parent is test_folder_1_: <tal:block Context.aq_parent is test_folder_1_: <tal:block
content="python:context.aq_parent.id =='test_folder_1_'" /> content="python:context.aq_parent.id =='test_folder_1_'" />
Container is context: <tal:block content="python:container is context" /> Container is context: <tal:block content="python:container is context" />
Here is context: <tal:block content="python:here is context"/> Here is context: <tal:block content="python:here is context"/>
...@@ -10,7 +10,7 @@ Default works: <tal:block replace="non_existent_var|default" />True ...@@ -10,7 +10,7 @@ Default works: <tal:block replace="non_existent_var|default" />True
Root is the application: <tal:block Root is the application: <tal:block
replace="python:repr(root).find('Application') != -1" /> replace="python:repr(root).find('Application') != -1" />
Template is a template: <tal:block Template is a template: <tal:block
replace="python:repr(template.aq_base).startswith('<ZopeTwoPageTemplateFile')" /> replace="python:'ViewPageTemplateFile' in repr(template)" />
Traverse_subpath exists and is empty: <tal:block Traverse_subpath exists and is empty: <tal:block
replace="python:traverse_subpath == []" /> replace="python:traverse_subpath == []" />
Request is a request: <tal:block Request is a request: <tal:block
......
...@@ -51,12 +51,11 @@ def test_absoluteurl(): ...@@ -51,12 +51,11 @@ def test_absoluteurl():
This test assures and demonstrates that the absolute url stops This test assures and demonstrates that the absolute url stops
traversing through an object's parents when it has reached the traversing through an object's parents when it has reached the
root object. In Zope 3 this is marked with the IContainmentRoot root object.
interface:
>>> from zope.interface import directlyProvides, providedBy >>> from zope.interface import alsoProvides, noLongerProvides
>>> from zope.traversing.interfaces import IContainmentRoot >>> from OFS.interfaces import IApplication
>>> directlyProvides(self.folder, IContainmentRoot) >>> alsoProvides(self.folder, IApplication)
>>> for crumb in view.breadcrumbs(): >>> for crumb in view.breadcrumbs():
... info = crumb.items() ... info = crumb.items()
...@@ -65,8 +64,7 @@ def test_absoluteurl(): ...@@ -65,8 +64,7 @@ def test_absoluteurl():
[('name', 'test_folder_1_'), ('url', 'http://nohost/test_folder_1_')] [('name', 'test_folder_1_'), ('url', 'http://nohost/test_folder_1_')]
[('name', 'testoid'), ('url', 'http://nohost/test_folder_1_/testoid')] [('name', 'testoid'), ('url', 'http://nohost/test_folder_1_/testoid')]
>>> directlyProvides(self.folder, >>> noLongerProvides(self.folder, IApplication)
... providedBy(self.folder) - IContainmentRoot)
The absolute url view is obviously not affected by virtual hosting: The absolute url view is obviously not affected by virtual hosting:
......
...@@ -19,47 +19,6 @@ import os, sys ...@@ -19,47 +19,6 @@ import os, sys
if __name__ == '__main__': if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py')) execfile(os.path.join(sys.path[0], 'framework.py'))
def test_ViewAcquisitionWrapping():
"""
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('pages.zcml', package=Products.Five.browser.tests)
>>> from Products.Five.tests.testing import simplecontent as sc
>>> sc.manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> self.login('manager')
>>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
>>> view is not None
True
>>> from Products.Five.browser.tests.pages import SimpleView
>>> isinstance(view, SimpleView)
True
>>> view()
u'The eagle has landed'
This sucks, but we know it
>>> from Acquisition import aq_parent, aq_base
>>> aq_parent(view.context) is view
True
This is the right way to get the context parent
>>> view.context.aq_inner.aq_parent is not view
True
>>> view.context.aq_inner.aq_parent is self.folder
True
Clean up:
>>> from zope.app.testing.placelesssetup import tearDown
>>> tearDown()
"""
def test_view_with_unwrapped_context(): def test_view_with_unwrapped_context():
""" """
It may be desirable when writing tests for views themselves to It may be desirable when writing tests for views themselves to
...@@ -118,7 +77,9 @@ def test_suite(): ...@@ -118,7 +77,9 @@ def test_suite():
ZopeDocTestSuite(), ZopeDocTestSuite(),
ZopeDocFileSuite('pages.txt', package='Products.Five.browser.tests'), ZopeDocFileSuite('pages.txt', package='Products.Five.browser.tests'),
FunctionalDocFileSuite('pages_ftest.txt', FunctionalDocFileSuite('pages_ftest.txt',
package='Products.Five.browser.tests') package='Products.Five.browser.tests'),
FunctionalDocFileSuite('aqlegacy_ftest.txt',
package='Products.Five.browser.tests'),
)) ))
return suite return suite
......
...@@ -36,7 +36,7 @@ def findSite(obj, iface=ISite): ...@@ -36,7 +36,7 @@ def findSite(obj, iface=ISite):
"""Find a site by walking up the object hierarchy, supporting both """Find a site by walking up the object hierarchy, supporting both
the ``ILocation`` API and Zope 2 Acquisition.""" the ``ILocation`` API and Zope 2 Acquisition."""
while obj is not None and not iface.providedBy(obj): while obj is not None and not iface.providedBy(obj):
obj = getattr(obj, '__parent__', aq_parent(aq_inner(obj))) obj = aq_parent(aq_inner(obj))
return obj return obj
@zope.component.adapter(zope.interface.Interface) @zope.component.adapter(zope.interface.Interface)
......
...@@ -268,13 +268,6 @@ view class are: ...@@ -268,13 +268,6 @@ view class are:
attribute. Typically this comes from a base class, such as attribute. Typically this comes from a base class, such as
``BrowserView``. ``BrowserView``.
* This also means they need to be part of the Zope 2 acquisition
system, as this is a requirement for Zope 2 security to
function. The ``BrowserView`` base class, available from
``Products.Five``, already inherits from ``Acquisition.Explicit`` to
make this be the case. Acquisition is explicit so no attributes can
be acquired by accident.
An example of a simple view:: An example of a simple view::
from Products.Five import BrowserView from Products.Five import BrowserView
......
...@@ -14,31 +14,26 @@ ...@@ -14,31 +14,26 @@
"""Five-compatible version of ObjectWidget """Five-compatible version of ObjectWidget
This is needed because ObjectWidget uses ViewPageTemplateFile whose This is needed because ObjectWidget uses ViewPageTemplateFile whose
macro definition is unfortunately incompatible with macro definition is unfortunately incompatible with ZopeTwoPageTemplateFile.
ZopeTwoPageTemplateFile. So this subclass uses So this subclass uses ZopeTwoPageTemplateFile for the template that renders
ZopeTwoPageTemplateFile for the template that renders the widget's the widget's sub-editform.
sub-editform. Acquisition has to be mixed in to provide the
ZopeTwoPageTemplateFile with the proper acquisition context.
$Id$ $Id$
""" """
import Acquisition
import zope.app.form.browser.objectwidget import zope.app.form.browser.objectwidget
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Globals import InitializeClass as initializeClass from Globals import InitializeClass as initializeClass
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class ObjectWidgetView(Acquisition.Explicit, class ObjectWidgetView(zope.app.form.browser.objectwidget.ObjectWidgetView):
zope.app.form.browser.objectwidget.ObjectWidgetView):
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectPublic() security.declareObjectPublic()
template = ZopeTwoPageTemplateFile('objectwidget.pt') template = ViewPageTemplateFile('objectwidget.pt')
initializeClass(ObjectWidgetView) initializeClass(ObjectWidgetView)
class ObjectWidgetClass(Acquisition.Explicit, class ObjectWidgetClass(zope.app.form.browser.objectwidget.ObjectWidget):
zope.app.form.browser.objectwidget.ObjectWidget):
def __init__(self, context, request, factory, **kw): def __init__(self, context, request, factory, **kw):
super(ObjectWidgetClass, self).__init__(context, request, factory, **kw) super(ObjectWidgetClass, self).__init__(context, request, factory, **kw)
...@@ -58,8 +53,4 @@ class ObjectWidgetClass(Acquisition.Explicit, ...@@ -58,8 +53,4 @@ class ObjectWidgetClass(Acquisition.Explicit,
val = self.context.schema[name].default val = self.context.schema[name].default
self.getSubWidget(name).setRenderedValue(val) self.getSubWidget(name).setRenderedValue(val)
def ObjectWidget(context, request, factory, **kw): ObjectWidget = ObjectWidgetClass
"""Return an ObjectWidget suitable in the Five environment, with
right acquisition context"""
return ObjectWidgetClass(context, request, factory, **kw
).__of__(context.context)
...@@ -18,7 +18,6 @@ $Id$ ...@@ -18,7 +18,6 @@ $Id$
import os.path import os.path
from datetime import datetime from datetime import datetime
import Acquisition
import zope.event import zope.event
import zope.formlib import zope.formlib
...@@ -36,7 +35,7 @@ _PAGEFORM_PATH = os.path.join(_FORMLIB_DIR, 'pageform.pt') ...@@ -36,7 +35,7 @@ _PAGEFORM_PATH = os.path.join(_FORMLIB_DIR, 'pageform.pt')
_SUBPAGEFORM_PATH = os.path.join(_FORMLIB_DIR, 'subpageform.pt') _SUBPAGEFORM_PATH = os.path.join(_FORMLIB_DIR, 'subpageform.pt')
class FiveFormlibMixin(Acquisition.Explicit): class FiveFormlibMixin(object):
# Overrides the formlib.form.FormBase.template attributes implemented # Overrides the formlib.form.FormBase.template attributes implemented
# using NamedTemplates. NamedTemplates using ViewPageTemplateFile (like # using NamedTemplates. NamedTemplates using ViewPageTemplateFile (like
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
$Id$ $Id$
""" """
from Acquisition import aq_acquire from Acquisition import aq_get
from zope.interface import implements from zope.interface import implements
from zope.i18n.interfaces import IFallbackTranslationDomainFactory from zope.i18n.interfaces import IFallbackTranslationDomainFactory
from zope.i18n.interfaces import ITranslationDomain from zope.i18n.interfaces import ITranslationDomain
...@@ -23,6 +23,7 @@ from zope.i18n.interfaces import IUserPreferredLanguages ...@@ -23,6 +23,7 @@ from zope.i18n.interfaces import IUserPreferredLanguages
from zope.i18n.negotiator import normalize_lang from zope.i18n.negotiator import normalize_lang
from zope.component import queryUtility from zope.component import queryUtility
from zope.i18nmessageid import Message from zope.i18nmessageid import Message
from zope.publisher.interfaces.browser import IBrowserRequest
class FiveTranslationService: class FiveTranslationService:
...@@ -60,8 +61,11 @@ class FiveTranslationService: ...@@ -60,8 +61,11 @@ class FiveTranslationService:
# in Zope3, context is adapted to IUserPreferredLanguages, # in Zope3, context is adapted to IUserPreferredLanguages,
# which means context should be the request in this case. # which means context should be the request in this case.
# Do not attempt to acquire REQUEST from the context, when we already
# got a request as the context
if context is not None: if context is not None:
context = aq_acquire(context, 'REQUEST', None) if not IBrowserRequest.providedBy(context):
context = aq_get(context, 'REQUEST', None)
return util.translate(msgid, mapping=mapping, context=context, return util.translate(msgid, mapping=mapping, context=context,
target_language=target_language, default=default) target_language=target_language, default=default)
......
...@@ -283,17 +283,17 @@ class LocalUtilityServiceTest(ZopeTestCase.ZopeTestCase): ...@@ -283,17 +283,17 @@ class LocalUtilityServiceTest(ZopeTestCase.ZopeTestCase):
# let's see if we can acquire something all the way from the # let's see if we can acquire something all the way from the
# root (Application) object; we need to be careful to choose # root (Application) object; we need to be careful to choose
# something that's only available from the root object # something that's only available from the root object
from Acquisition import aq_acquire from Acquisition import aq_get
dummy = zapi.getUtility(IDummyUtility) dummy = zapi.getUtility(IDummyUtility)
acquired = aq_acquire(dummy, 'ZopeAttributionButton', None) acquired = aq_get(dummy, 'ZopeAttributionButton', None)
self.failUnless(acquired is not None) self.failUnless(acquired is not None)
name, dummy = zapi.getUtilitiesFor(IDummyUtility).next() name, dummy = zapi.getUtilitiesFor(IDummyUtility).next()
acquired = aq_acquire(dummy, 'ZopeAttributionButton', None) acquired = aq_get(dummy, 'ZopeAttributionButton', None)
self.failUnless(acquired is not None) self.failUnless(acquired is not None)
dummy = zapi.getAllUtilitiesRegisteredFor(IDummyUtility).next() dummy = zapi.getAllUtilitiesRegisteredFor(IDummyUtility).next()
acquired = aq_acquire(dummy, 'ZopeAttributionButton', None) acquired = aq_get(dummy, 'ZopeAttributionButton', None)
self.failUnless(acquired is not None) self.failUnless(acquired is not None)
def test_getNextUtility(self): def test_getNextUtility(self):
......
...@@ -89,8 +89,7 @@ But now we register some viewlets for the manager ...@@ -89,8 +89,7 @@ But now we register some viewlets for the manager
>>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer >>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
>>> from zope.publisher.interfaces.browser import IBrowserView >>> from zope.publisher.interfaces.browser import IBrowserView
>>> from Acquisition import Explicit >>> class WeatherBox(object):
>>> class WeatherBox(Explicit):
... zope.interface.implements(interfaces.IViewlet) ... zope.interface.implements(interfaces.IViewlet)
... ...
... def __init__(self, context, request, view, manager): ... def __init__(self, context, request, view, manager):
...@@ -109,7 +108,7 @@ But now we register some viewlets for the manager ...@@ -109,7 +108,7 @@ But now we register some viewlets for the manager
... IBrowserView, ILeftColumn), ... IBrowserView, ILeftColumn),
... interfaces.IViewlet, name='weather') ... interfaces.IViewlet, name='weather')
>>> class SportBox(Explicit): >>> class SportBox(object):
... zope.interface.implements(interfaces.IViewlet) ... zope.interface.implements(interfaces.IViewlet)
... ...
... def __init__(self, context, request, view, manager): ... def __init__(self, context, request, view, manager):
...@@ -311,7 +310,7 @@ The same is true if the specified attribute does not exist: ...@@ -311,7 +310,7 @@ The same is true if the specified attribute does not exist:
>>> foo.render() >>> foo.render()
Traceback (most recent call last): Traceback (most recent call last):
... ...
AttributeError: bar AttributeError: 'FooViewlet' object has no attribute 'bar'
To create simple template-based viewlets you can use the To create simple template-based viewlets you can use the
``SimpleViewletClass()`` function. This function is very similar to its view ``SimpleViewletClass()`` function. This function is very similar to its view
...@@ -365,7 +364,7 @@ fully demonstrate the functionality of the base classes as well. ...@@ -365,7 +364,7 @@ fully demonstrate the functionality of the base classes as well.
The viewlet will look up the resource it was given and tries to produce the The viewlet will look up the resource it was given and tries to produce the
absolute URL for it: absolute URL for it:
>>> class JSResource(Explicit): >>> class JSResource(object):
... def __init__(self, request): ... def __init__(self, request):
... self.request = request ... self.request = request
... ...
...@@ -381,7 +380,7 @@ absolute URL for it: ...@@ -381,7 +380,7 @@ absolute URL for it:
The same works for the CSS resource viewlet: The same works for the CSS resource viewlet:
>>> class CSSResource(Explicit): >>> class CSSResource(object):
... def __init__(self, request): ... def __init__(self, request):
... self.request = request ... self.request = request
... ...
...@@ -487,7 +486,7 @@ different item: ...@@ -487,7 +486,7 @@ different item:
>>> shownColumns = [] >>> shownColumns = []
>>> class ContentsViewletManager(Explicit): >>> class ContentsViewletManager(object):
... zope.interface.implements(interfaces.IViewletManager) ... zope.interface.implements(interfaces.IViewletManager)
... index = None ... index = None
... ...
...@@ -562,7 +561,7 @@ The Viewlets and the Final Result ...@@ -562,7 +561,7 @@ The Viewlets and the Final Result
Now let's create a first viewlet for the manager... Now let's create a first viewlet for the manager...
>>> class NameViewlet(Explicit): >>> class NameViewlet(object):
... ...
... def __init__(self, context, request, view, manager): ... def __init__(self, context, request, view, manager):
... self.__parent__ = view ... self.__parent__ = view
...@@ -635,7 +634,7 @@ that we want to see the name as a column in the table: ...@@ -635,7 +634,7 @@ that we want to see the name as a column in the table:
Let's now write a second viewlet that will display the size of the object for Let's now write a second viewlet that will display the size of the object for
us: us:
>>> class SizeViewlet(Explicit): >>> class SizeViewlet(object):
... ...
... def __init__(self, context, request, view, manager): ... def __init__(self, context, request, view, manager):
... self.__parent__ = view ... self.__parent__ = view
......
...@@ -130,8 +130,6 @@ Next let's see what happens, if we specify a template for the viewlet manager: ...@@ -130,8 +130,6 @@ Next let's see what happens, if we specify a template for the viewlet manager:
<Products.Five.viewlet.manager.<ViewletManager providing ILeftColumn> object ...> <Products.Five.viewlet.manager.<ViewletManager providing ILeftColumn> object ...>
>>> ILeftColumn.providedBy(manager) >>> ILeftColumn.providedBy(manager)
True True
>>> manager.template.meta_type
'Page Template (File)'
>>> manager.update() >>> manager.update()
>>> print manager.render().strip() >>> print manager.render().strip()
<div class="column"> <div class="column">
...@@ -164,8 +162,6 @@ weight attribute in the viewlet: ...@@ -164,8 +162,6 @@ weight attribute in the viewlet:
<class 'Products.Five.viewlet.manager.ViewletManagerBase'>) <class 'Products.Five.viewlet.manager.ViewletManagerBase'>)
>>> ILeftColumn.providedBy(manager) >>> ILeftColumn.providedBy(manager)
True True
>>> manager.template.meta_type
'Page Template (File)'
>>> manager.update() >>> manager.update()
>>> print manager.render().strip() >>> print manager.render().strip()
<div class="column"> <div class="column">
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
$Id$ $Id$
""" """
import Acquisition from Acquisition import aq_base
from AccessControl.ZopeGuards import guarded_hasattr from AccessControl.ZopeGuards import guarded_hasattr
import zope.interface import zope.interface
import zope.security import zope.security
...@@ -24,9 +24,7 @@ from zope.viewlet.manager import ViewletManagerBase as origManagerBase ...@@ -24,9 +24,7 @@ from zope.viewlet.manager import ViewletManagerBase as origManagerBase
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
aq_base = Acquisition.aq_base class ViewletManagerBase(origManagerBase):
class ViewletManagerBase(origManagerBase, Acquisition.Explicit):
"""A base class for Viewlet managers to work in Zope2""" """A base class for Viewlet managers to work in Zope2"""
def __getitem__(self, name): def __getitem__(self, name):
...@@ -41,9 +39,6 @@ class ViewletManagerBase(origManagerBase, Acquisition.Explicit): ...@@ -41,9 +39,6 @@ class ViewletManagerBase(origManagerBase, Acquisition.Explicit):
raise zope.component.interfaces.ComponentLookupError( raise zope.component.interfaces.ComponentLookupError(
'No provider with name `%s` found.' %name) 'No provider with name `%s` found.' %name)
# Wrap the viewlet for security lookups
viewlet = viewlet.__of__(viewlet.context)
# If the viewlet cannot be accessed, then raise an # If the viewlet cannot be accessed, then raise an
# unauthorized error # unauthorized error
if not guarded_hasattr(viewlet, 'render'): if not guarded_hasattr(viewlet, 'render'):
...@@ -65,7 +60,6 @@ class ViewletManagerBase(origManagerBase, Acquisition.Explicit): ...@@ -65,7 +60,6 @@ class ViewletManagerBase(origManagerBase, Acquisition.Explicit):
# the object has a real context from which to determine owner # the object has a real context from which to determine owner
# security. # security.
for name, viewlet in viewlets: for name, viewlet in viewlets:
viewlet = viewlet.__of__(viewlet.context)
if guarded_hasattr(viewlet, 'render'): if guarded_hasattr(viewlet, 'render'):
results.append((name, viewlet)) results.append((name, viewlet))
return results return results
......
...@@ -16,19 +16,18 @@ ...@@ -16,19 +16,18 @@
$Id$ $Id$
""" """
import os import os
from Acquisition import Explicit import zope.viewlet.viewlet
from zope.viewlet import viewlet as orig_viewlet from Products.Five.bbb import AcquisitionBBB
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile class ViewletBase(zope.viewlet.viewlet.ViewletBase, AcquisitionBBB):
# We add Acquisition to all the base classes to enable security machinery
class ViewletBase(orig_viewlet.ViewletBase, Explicit):
pass pass
class SimpleAttributeViewlet(orig_viewlet.SimpleAttributeViewlet, Explicit): class SimpleAttributeViewlet(zope.viewlet.viewlet.SimpleAttributeViewlet,
AcquisitionBBB):
pass pass
class simple(orig_viewlet.simple): class simple(zope.viewlet.viewlet.simple):
# We need to ensure that the proper __init__ is called. # We need to ensure that the proper __init__ is called.
__init__ = ViewletBase.__init__.im_func __init__ = ViewletBase.__init__.im_func
...@@ -41,7 +40,7 @@ def SimpleViewletClass(template, bases=(), attributes=None, ...@@ -41,7 +40,7 @@ def SimpleViewletClass(template, bases=(), attributes=None,
# Create the base class hierarchy # Create the base class hierarchy
bases += (simple, ViewletBase) bases += (simple, ViewletBase)
attrs = {'index' : ZopeTwoPageTemplateFile(template), attrs = {'index' : ViewPageTemplateFile(template),
'__name__' : name} '__name__' : name}
if attributes: if attributes:
attrs.update(attributes) attrs.update(attributes)
...@@ -52,7 +51,7 @@ def SimpleViewletClass(template, bases=(), attributes=None, ...@@ -52,7 +51,7 @@ def SimpleViewletClass(template, bases=(), attributes=None,
return class_ return class_
class ResourceViewletBase(orig_viewlet.ResourceViewletBase, Explicit): class ResourceViewletBase(zope.viewlet.viewlet.ResourceViewletBase):
pass pass
def JavaScriptViewlet(path): def JavaScriptViewlet(path):
...@@ -61,13 +60,13 @@ def JavaScriptViewlet(path): ...@@ -61,13 +60,13 @@ def JavaScriptViewlet(path):
klass = type('JavaScriptViewlet', klass = type('JavaScriptViewlet',
(ResourceViewletBase, ViewletBase), (ResourceViewletBase, ViewletBase),
{'index': ZopeTwoPageTemplateFile(src), {'index': ViewPageTemplateFile(src),
'_path': path}) '_path': path})
return klass return klass
class CSSResourceViewletBase(orig_viewlet.CSSResourceViewletBase): class CSSResourceViewletBase(zope.viewlet.viewlet.CSSResourceViewletBase):
pass pass
def CSSViewlet(path, media="all", rel="stylesheet"): def CSSViewlet(path, media="all", rel="stylesheet"):
...@@ -76,7 +75,7 @@ def CSSViewlet(path, media="all", rel="stylesheet"): ...@@ -76,7 +75,7 @@ def CSSViewlet(path, media="all", rel="stylesheet"):
klass = type('CSSViewlet', klass = type('CSSViewlet',
(CSSResourceViewletBase, ViewletBase), (CSSResourceViewletBase, ViewletBase),
{'index': ZopeTwoPageTemplateFile(src), {'index': ViewPageTemplateFile(src),
'_path': path, '_path': path,
'_media':media, '_media':media,
'_rel':rel}) '_rel':rel})
......
...@@ -17,7 +17,7 @@ from logging import getLogger ...@@ -17,7 +17,7 @@ from logging import getLogger
import AccessControl import AccessControl
from Globals import package_home, InitializeClass, DevelopmentMode from Globals import package_home, InitializeClass, DevelopmentMode
from App.config import getConfiguration from App.config import getConfiguration
from Acquisition import aq_parent, aq_inner from Acquisition import aq_parent, aq_inner, aq_get
from ComputedAttribute import ComputedAttribute from ComputedAttribute import ComputedAttribute
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from OFS.Traversable import Traversable from OFS.Traversable import Traversable
...@@ -87,7 +87,10 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable): ...@@ -87,7 +87,10 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable):
self.filename = filename self.filename = filename
def pt_getContext(self): def pt_getContext(self):
root = self.getPhysicalRoot() root = None
meth = aq_get(self, 'getPhysicalRoot', None)
if meth is not None:
root = meth()
context = self._getContext() context = self._getContext()
c = {'template': self, c = {'template': self,
'here': context, 'here': context,
...@@ -96,7 +99,7 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable): ...@@ -96,7 +99,7 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable):
'nothing': None, 'nothing': None,
'options': {}, 'options': {},
'root': root, 'root': root,
'request': getattr(root, 'REQUEST', None), 'request': aq_get(root, 'REQUEST', None),
'modules': SecureModuleImporter, 'modules': SecureModuleImporter,
} }
return c return c
...@@ -108,12 +111,11 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable): ...@@ -108,12 +111,11 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable):
kw['args'] = args kw['args'] = args
bound_names['options'] = kw bound_names['options'] = kw
try: request = aq_get(self, 'REQUEST', None)
response = self.REQUEST.RESPONSE if request is not None:
response = request.response
if not response.headers.has_key('content-type'): if not response.headers.has_key('content-type'):
response.setHeader('content-type', self.content_type) response.setHeader('content-type', self.content_type)
except AttributeError:
pass
# Execute the template in a new security context. # Execute the template in a new security context.
security = AccessControl.getSecurityManager() security = AccessControl.getSecurityManager()
......
...@@ -17,6 +17,7 @@ $Id$ ...@@ -17,6 +17,7 @@ $Id$
import re import re
import os import os
import Acquisition import Acquisition
from Acquisition import aq_get
from Globals import ImageFile, package_home, InitializeClass from Globals import ImageFile, package_home, InitializeClass
from DateTime.DateTime import DateTime from DateTime.DateTime import DateTime
from Shared.DC.Scripts.Script import Script from Shared.DC.Scripts.Script import Script
...@@ -271,7 +272,10 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable, ...@@ -271,7 +272,10 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
historyComparisonResults=html_diff(rev1._text, rev2._text) ) historyComparisonResults=html_diff(rev1._text, rev2._text) )
def pt_getContext(self, *args, **kw): def pt_getContext(self, *args, **kw):
root = self.getPhysicalRoot() root = None
meth = aq_get(self, 'getPhysicalRoot', None)
if meth is not None:
root = meth()
context = self._getContext() context = self._getContext()
c = {'template': self, c = {'template': self,
'here': context, 'here': context,
...@@ -280,7 +284,7 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable, ...@@ -280,7 +284,7 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
'nothing': None, 'nothing': None,
'options': {}, 'options': {},
'root': root, 'root': root,
'request': getattr(root, 'REQUEST', None), 'request': aq_get(root, 'REQUEST', None),
'modules': SecureModuleImporter, 'modules': SecureModuleImporter,
} }
return c return c
...@@ -302,12 +306,11 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable, ...@@ -302,12 +306,11 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
kw['args'] = args kw['args'] = args
bound_names['options'] = kw bound_names['options'] = kw
try: request = aq_get(self, 'REQUEST', None)
response = self.REQUEST.RESPONSE if request is not None:
response = request.response
if not response.headers.has_key('content-type'): if not response.headers.has_key('content-type'):
response.setHeader('content-type', self.content_type) response.setHeader('content-type', self.content_type)
except AttributeError:
pass
security = getSecurityManager() security = getSecurityManager()
bound_names['user'] = security.getUser() bound_names['user'] = security.getUser()
......
...@@ -20,6 +20,7 @@ from AccessControl.Permissions import view_management_screens ...@@ -20,6 +20,7 @@ from AccessControl.Permissions import view_management_screens
from AccessControl.PermissionRole import _what_not_even_god_should_do from AccessControl.PermissionRole import _what_not_even_god_should_do
from AccessControl.ZopeGuards import guarded_getattr from AccessControl.ZopeGuards import guarded_getattr
from Persistence import Persistent from Persistence import Persistent
from Acquisition import aq_parent, aq_inner
from string import join, strip from string import join, strip
import re import re
...@@ -179,6 +180,13 @@ class UnauthorizedBinding: ...@@ -179,6 +180,13 @@ class UnauthorizedBinding:
# Make *extra* sure that the wrapper isn't used to access # Make *extra* sure that the wrapper isn't used to access
# __call__, etc. # __call__, etc.
if name.startswith('__'): if name.startswith('__'):
# Acquisition will nowadays try to do an getattr on all
# objects which aren't Acquisition wrappers, asking for a
# __parent__ pointer. We don't want to raise Unauthorized
# in this case but simply an AttributeError.
if name in ('__parent__', '__name__'):
raise AttributeError(name)
self.__you_lose() self.__you_lose()
return guarded_getattr(self._wrapped, name, default) return guarded_getattr(self._wrapped, name, default)
...@@ -264,11 +272,11 @@ class Bindings: ...@@ -264,11 +272,11 @@ class Bindings:
def _getContext(self): def _getContext(self):
# Utility for bindcode. # Utility for bindcode.
while 1: while 1:
self = self.aq_parent self = aq_parent(self)
if not getattr(self, '_is_wrapperish', None): if not getattr(self, '_is_wrapperish', None):
parent = getattr(self, 'aq_parent', None) parent = aq_parent(self)
inner = getattr(self, 'aq_inner', None) inner = aq_inner(self)
container = getattr(inner, 'aq_parent', None) container = aq_parent(inner)
try: getSecurityManager().validate(parent, container, '', self) try: getSecurityManager().validate(parent, container, '', self)
except Unauthorized: except Unauthorized:
return UnauthorizedBinding('context', self) return UnauthorizedBinding('context', self)
...@@ -277,11 +285,11 @@ class Bindings: ...@@ -277,11 +285,11 @@ class Bindings:
def _getContainer(self): def _getContainer(self):
# Utility for bindcode. # Utility for bindcode.
while 1: while 1:
self = self.aq_inner.aq_parent self = aq_parent(aq_inner(self))
if not getattr(self, '_is_wrapperish', None): if not getattr(self, '_is_wrapperish', None):
parent = getattr(self, 'aq_parent', None) parent = aq_parent(self)
inner = getattr(self, 'aq_inner', None) inner = aq_inner(self)
container = getattr(inner, 'aq_parent', None) container = aq_parent(inner)
try: getSecurityManager().validate(parent, container, '', self) try: getSecurityManager().validate(parent, container, '', self)
except Unauthorized: except Unauthorized:
return UnauthorizedBinding('container', self) return UnauthorizedBinding('container', self)
......
...@@ -18,6 +18,7 @@ from urllib import quote as urllib_quote ...@@ -18,6 +18,7 @@ from urllib import quote as urllib_quote
import xmlrpc import xmlrpc
from zExceptions import Forbidden, Unauthorized, NotFound from zExceptions import Forbidden, Unauthorized, NotFound
from Acquisition import aq_base from Acquisition import aq_base
from Acquisition.interfaces import IAcquirer
from zope.interface import implements, providedBy, Interface from zope.interface import implements, providedBy, Interface
from zope.component import queryMultiAdapter from zope.component import queryMultiAdapter
...@@ -95,7 +96,7 @@ class DefaultPublishTraverse(object): ...@@ -95,7 +96,7 @@ class DefaultPublishTraverse(object):
request.response.setStatus(200) request.response.setStatus(200)
# We don't need to do the docstring security check # We don't need to do the docstring security check
# for views, so lets skip it and return the object here. # for views, so lets skip it and return the object here.
return subobject.__of__(object) return subobject
# No view found. Reraise the error raised by __bobo_traverse__ # No view found. Reraise the error raised by __bobo_traverse__
raise e raise e
else: else:
...@@ -105,9 +106,10 @@ class DefaultPublishTraverse(object): ...@@ -105,9 +106,10 @@ class DefaultPublishTraverse(object):
subobject = getattr(object, name) subobject = getattr(object, name)
else: else:
# We try to fall back to a view: # We try to fall back to a view:
subobject = queryMultiAdapter((object, request), Interface, name) subobject = queryMultiAdapter((object, request), Interface,
name)
if subobject is not None: if subobject is not None:
return subobject.__of__(object) return subobject
# And lastly, of there is no view, try acquired attributes, but # And lastly, of there is no view, try acquired attributes, but
# only if there is no __bobo_traverse__: # only if there is no __bobo_traverse__:
...@@ -312,7 +314,9 @@ class BaseRequest: ...@@ -312,7 +314,9 @@ class BaseRequest:
except TraversalError: except TraversalError:
raise KeyError(ob, name) raise KeyError(ob, name)
return ob2.__of__(ob) if IAcquirer.providedBy(ob2):
ob2 = ob2.__of__(ob)
return ob2
if name == '.': if name == '.':
return ob return ob
...@@ -427,7 +431,7 @@ class BaseRequest: ...@@ -427,7 +431,7 @@ class BaseRequest:
else: else:
# If we have reached the end of the path, we look to see # If we have reached the end of the path, we look to see
# if we can find IBrowserPublisher.browserDefault. If so, # if we can find IBrowserPublisher.browserDefault. If so,
# we call it to let the object tell us how to publish it # we call it to let the object tell us how to publish it.
# BrowserDefault returns the object to be published # BrowserDefault returns the object to be published
# (usually self) and a sequence of names to traverse to # (usually self) and a sequence of names to traverse to
# find the method to be published. # find the method to be published.
...@@ -440,7 +444,8 @@ class BaseRequest: ...@@ -440,7 +444,8 @@ class BaseRequest:
not hasattr(object,'__bobo_traverse__')): not hasattr(object,'__bobo_traverse__')):
if object.aq_parent is not object.aq_inner.aq_parent: if object.aq_parent is not object.aq_inner.aq_parent:
from webdav.NullResource import NullResource from webdav.NullResource import NullResource
object = NullResource(parents[-2], object.getId(), self).__of__(parents[-2]) object = NullResource(parents[-2], object.getId(),
self).__of__(parents[-2])
if IBrowserPublisher.providedBy(object): if IBrowserPublisher.providedBy(object):
adapter = object adapter = object
...@@ -451,10 +456,9 @@ class BaseRequest: ...@@ -451,10 +456,9 @@ class BaseRequest:
# Zope2 doesn't set up its own adapters in a lot # Zope2 doesn't set up its own adapters in a lot
# of cases so we will just use a default adapter. # of cases so we will just use a default adapter.
adapter = DefaultPublishTraverse(object, self) adapter = DefaultPublishTraverse(object, self)
newobject, default_path = adapter.browserDefault(self) object, default_path = adapter.browserDefault(self)
if default_path or newobject is not object: if default_path:
object = newobject
request._hacked_path=1 request._hacked_path=1
if len(default_path) > 1: if len(default_path) > 1:
path = list(default_path) path = list(default_path)
......
...@@ -1116,7 +1116,7 @@ class HTTPRequest(BaseRequest): ...@@ -1116,7 +1116,7 @@ class HTTPRequest(BaseRequest):
clone['PARENTS']=[self['PARENTS'][-1]] clone['PARENTS']=[self['PARENTS'][-1]]
return clone return clone
def get_header(self, name, default=None): def getHeader(self, name, default=None, literal=False):
"""Return the named HTTP header, or an optional default """Return the named HTTP header, or an optional default
argument or None if the header is not found. Note that argument or None if the header is not found. Note that
both original and CGI-ified header names are recognized, both original and CGI-ified header names are recognized,
...@@ -1124,7 +1124,8 @@ class HTTPRequest(BaseRequest): ...@@ -1124,7 +1124,8 @@ class HTTPRequest(BaseRequest):
should all return the Content-Type header, if available. should all return the Content-Type header, if available.
""" """
environ=self.environ environ=self.environ
name=('_'.join(name.split("-"))).upper() if not literal:
name = name.replace('-', '_').upper()
val=environ.get(name, None) val=environ.get(name, None)
if val is not None: if val is not None:
return val return val
...@@ -1132,6 +1133,8 @@ class HTTPRequest(BaseRequest): ...@@ -1132,6 +1133,8 @@ class HTTPRequest(BaseRequest):
name='HTTP_%s' % name name='HTTP_%s' % name
return environ.get(name, default) return environ.get(name, default)
get_header = getHeader # BBB
def get(self, key, default=None, returnTaints=0, def get(self, key, default=None, returnTaints=0,
URLmatch=re.compile('URL(PATH)?([0-9]+)$').match, URLmatch=re.compile('URL(PATH)?([0-9]+)$').match,
BASEmatch=re.compile('BASE(PATH)?([0-9]+)$').match, BASEmatch=re.compile('BASE(PATH)?([0-9]+)$').match,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
############################################################################## ##############################################################################
"""Provide an apply-like facility that works with any mapping object """Provide an apply-like facility that works with any mapping object
""" """
import zope.publisher.publish
def default_call_object(object, args, context): def default_call_object(object, args, context):
result=object(*args) # Type s<cr> to step into published object. result=object(*args) # Type s<cr> to step into published object.
...@@ -39,27 +40,15 @@ def mapply(object, positional=(), keyword={}, ...@@ -39,27 +40,15 @@ def mapply(object, positional=(), keyword={},
if hasattr(object,'__bases__'): if hasattr(object,'__bases__'):
f, names, defaults = handle_class(object, context) f, names, defaults = handle_class(object, context)
else: else:
f=object try:
im=0 f, count = zope.publisher.publish.unwrapMethod(object)
if hasattr(f, 'im_func'): except TypeError:
im=1 if maybe:
elif not hasattr(f,'func_defaults'): return object
if hasattr(f, '__call__'): raise
f=f.__call__ code = f.func_code
if hasattr(f, 'im_func'): defaults = f.func_defaults
im=1 names = code.co_varnames[count:code.co_argcount]
elif not hasattr(f,'func_defaults') and maybe: return object
elif maybe: return object
if im:
f=f.im_func
c=f.func_code
defaults=f.func_defaults
names=c.co_varnames[1:c.co_argcount]
else:
defaults=f.func_defaults
c=f.func_code
names=c.co_varnames[:c.co_argcount]
nargs=len(names) nargs=len(names)
if positional: if positional:
......
...@@ -6,6 +6,7 @@ import zope.interface ...@@ -6,6 +6,7 @@ import zope.interface
import zope.component import zope.component
import zope.testing.cleanup import zope.testing.cleanup
import zope.traversing.namespace import zope.traversing.namespace
from zope.publisher.browser import BrowserPage
from zope.publisher.browser import IBrowserRequest from zope.publisher.browser import IBrowserRequest
from zope.publisher.browser import IDefaultBrowserLayer from zope.publisher.browser import IDefaultBrowserLayer
from zope.traversing.interfaces import ITraversable from zope.traversing.interfaces import ITraversable
...@@ -276,9 +277,15 @@ class TestBaseRequestZope3Views(unittest.TestCase): ...@@ -276,9 +277,15 @@ class TestBaseRequestZope3Views(unittest.TestCase):
gsm = zope.component.getGlobalSiteManager() gsm = zope.component.getGlobalSiteManager()
# Define our 'meth' view # Define the views
gsm.registerAdapter(DummyView, (IDummy, IDefaultBrowserLayer), None, gsm.registerAdapter(DummyView, (IDummy, IDefaultBrowserLayer),
'meth') zope.interface.Interface, 'meth')
gsm.registerAdapter(DummyPage, (IDummy, IDefaultBrowserLayer),
zope.interface.Interface, 'page')
gsm.registerAdapter(DummyPage2, (IDummy, IDefaultBrowserLayer),
zope.interface.Interface, 'page2')
gsm.registerAdapter(DummyPage3, (IDummy, IDefaultBrowserLayer),
zope.interface.Interface, 'page3')
# Bind the 'view' namespace (for @@ traversal) # Bind the 'view' namespace (for @@ traversal)
gsm.registerAdapter(zope.traversing.namespace.view, gsm.registerAdapter(zope.traversing.namespace.view,
...@@ -382,6 +389,27 @@ class TestBaseRequestZope3Views(unittest.TestCase): ...@@ -382,6 +389,27 @@ class TestBaseRequestZope3Views(unittest.TestCase):
r.traverse('folder/obj/++view++meth') r.traverse('folder/obj/++view++meth')
self.assertEqual(r['URL'], '/folder/obj/++view++meth') self.assertEqual(r['URL'], '/folder/obj/++view++meth')
def test_browserDefault(self):
# browserDefault can return self, () to indicate that the
# object itself wants to be published (using __call__):
root, folder = self._makeRootAndFolder()
folder._setObject('obj', DummyObjectZ3('obj'))
r = self._makeOne(root)
ob = r.traverse('folder/obj/page')
self.assertEqual(ob(), 'Test page')
# browserDefault can return another_object, () to indicate
# that that object should be published (using __call__):
r = self._makeOne(root)
ob = r.traverse('folder/obj/page2')
self.assertEqual(ob(), 'Test page')
# browserDefault can also return self.some_method, () to
# indicate that that method should be called.
r = self._makeOne(root)
ob = r.traverse('folder/obj/page3')
self.assertEqual(ob(), 'Test page')
class DummyResponse(Implicit): class DummyResponse(Implicit):
...@@ -512,6 +540,34 @@ class DummyView(Implicit): ...@@ -512,6 +540,34 @@ class DummyView(Implicit):
def __call__(self): def __call__(self):
return 'view on %s' % (self.content.name) return 'view on %s' % (self.content.name)
class DummyPage(BrowserPage):
# BrowserPage is an IBrowserPublisher with a browserDefault that
# returns self, () so that __call__ is invoked by the publisher.
def __call__(self):
return 'Test page'
class DummyPage2(BrowserPage):
def browserDefault(self, request):
# intentionally return something that's not self
return DummyPage(self.context, request), ()
# __call__ remains unimplemented, baseclass raises NotImplementedError
class DummyPage3(BrowserPage):
def browserDefault(self, request):
# intentionally return a method here
return self.foo, ()
def foo(self):
return 'Test page'
# __call__ remains unimplemented, baseclass raises NotImplementedError
def test_suite(): def test_suite():
return unittest.TestSuite(( return unittest.TestSuite((
unittest.makeSuite(TestBaseRequest), unittest.makeSuite(TestBaseRequest),
...@@ -519,4 +575,4 @@ def test_suite(): ...@@ -519,4 +575,4 @@ def test_suite():
)) ))
if __name__ == '__main__': if __name__ == '__main__':
unitttest.main(defaultTest='test_suite') unittest.main(defaultTest='test_suite')
import sys
import base64
import unittest import unittest
from urllib import quote_plus from urllib import quote_plus
from types import ListType, TupleType, StringType, UnicodeType
from StringIO import StringIO
from DateTime import DateTime
from ZPublisher.HTTPRequest import HTTPRequest, record, trusted_proxies
from ZPublisher.TaintedString import TaintedString
from ZPublisher.Converters import type_converters
TEST_LARGEFILE_DATA = ''' TEST_LARGEFILE_DATA = '''
--12345 --12345
Content-Disposition: form-data; name="file"; filename="file" Content-Disposition: form-data; name="file"; filename="file"
...@@ -13,13 +22,10 @@ test %s ...@@ -13,13 +22,10 @@ test %s
class AuthCredentialsTests( unittest.TestCase ): class AuthCredentialsTests( unittest.TestCase ):
def _getTargetClass(self): def _getTargetClass(self):
from ZPublisher.HTTPRequest import HTTPRequest
return HTTPRequest return HTTPRequest
def _makeOne(self, stdin=None, environ=None, response=None, clean=1): def _makeOne(self, stdin=None, environ=None, response=None, clean=1):
if stdin is None: if stdin is None:
from StringIO import StringIO
stdin = StringIO() stdin = StringIO()
if environ is None: if environ is None:
...@@ -40,9 +46,6 @@ class AuthCredentialsTests( unittest.TestCase ): ...@@ -40,9 +46,6 @@ class AuthCredentialsTests( unittest.TestCase ):
return self._getTargetClass()(stdin, environ, response, clean) return self._getTargetClass()(stdin, environ, response, clean)
def test__authUserPW_simple( self ): def test__authUserPW_simple( self ):
import base64
user_id = 'user' user_id = 'user'
password = 'password' password = 'password'
encoded = base64.encodestring( '%s:%s' % ( user_id, password ) ) encoded = base64.encodestring( '%s:%s' % ( user_id, password ) )
...@@ -57,11 +60,7 @@ class AuthCredentialsTests( unittest.TestCase ): ...@@ -57,11 +60,7 @@ class AuthCredentialsTests( unittest.TestCase ):
self.assertEqual( password_x, password ) self.assertEqual( password_x, password )
def test__authUserPW_with_embedded_colon( self ): def test__authUserPW_with_embedded_colon( self ):
# http://www.zope.org/Collectors/Zope/2039 # http://www.zope.org/Collectors/Zope/2039
import base64
user_id = 'user' user_id = 'user'
password = 'embedded:colon' password = 'embedded:colon'
encoded = base64.encodestring( '%s:%s' % ( user_id, password ) ) encoded = base64.encodestring( '%s:%s' % ( user_id, password ) )
...@@ -75,43 +74,20 @@ class AuthCredentialsTests( unittest.TestCase ): ...@@ -75,43 +74,20 @@ class AuthCredentialsTests( unittest.TestCase ):
self.assertEqual( user_id_x, user_id ) self.assertEqual( user_id_x, user_id )
self.assertEqual( password_x, password ) self.assertEqual( password_x, password )
class RecordTests( unittest.TestCase ):
class RecordTests(unittest.TestCase):
def test_repr( self ):
from ZPublisher.HTTPRequest import record def test_repr(self):
record = record() rec = record()
record.a = 1 rec.a = 1
record.b = 'foo' rec.b = 'foo'
r = repr( record ) r = repr(rec)
d = eval( r ) d = eval(r)
self.assertEqual( d, record.__dict__ ) self.assertEqual(d, rec.__dict__)
def test_contains(self):
from ZPublisher.HTTPRequest import record
record = record()
record.a = 1
self.assertTrue('a' in record)
def test_iter(self):
from ZPublisher.HTTPRequest import record
record = record()
record.a = 1
record.b = 2
record.c = 3
for k in record:
self.assertTrue(k in ('a','b','c'))
def test_len(self):
from ZPublisher.HTTPRequest import record
record = record()
record.a = 1
record.b = 2
record.c = 3
self.assertEqual(len(record), 3)
class ProcessInputsTests(unittest.TestCase): class ProcessInputsTests(unittest.TestCase):
def _getHTTPRequest(self, env): def _getHTTPRequest(self, env):
from ZPublisher.HTTPRequest import HTTPRequest
return HTTPRequest(None, env, None) return HTTPRequest(None, env, None)
def _processInputs(self, inputs): def _processInputs(self, inputs):
...@@ -141,9 +117,6 @@ class ProcessInputsTests(unittest.TestCase): ...@@ -141,9 +117,6 @@ class ProcessInputsTests(unittest.TestCase):
# when one is found. # when one is found.
# Also raises an Assertion if a string which *should* have been # Also raises an Assertion if a string which *should* have been
# tainted is found, or when a tainted string is not deemed dangerous. # tainted is found, or when a tainted string is not deemed dangerous.
from types import ListType, TupleType, StringType, UnicodeType
from ZPublisher.HTTPRequest import record
from ZPublisher.TaintedString import TaintedString
retval = 0 retval = 0
...@@ -221,8 +194,6 @@ class ProcessInputsTests(unittest.TestCase): ...@@ -221,8 +194,6 @@ class ProcessInputsTests(unittest.TestCase):
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
def testSimpleMarshalling(self): def testSimpleMarshalling(self):
from DateTime import DateTime
inputs = ( inputs = (
('num:int', '42'), ('fract:float', '4.2'), ('bign:long', '45'), ('num:int', '42'), ('fract:float', '4.2'), ('bign:long', '45'),
('words:string', 'Some words'), ('2tokens:tokens', 'one two'), ('words:string', 'Some words'), ('2tokens:tokens', 'one two'),
...@@ -476,9 +447,6 @@ class ProcessInputsTests(unittest.TestCase): ...@@ -476,9 +447,6 @@ class ProcessInputsTests(unittest.TestCase):
self._onlyTaintedformHoldsTaintedStrings(req) self._onlyTaintedformHoldsTaintedStrings(req)
def testSimpleContainersWithTaints(self): def testSimpleContainersWithTaints(self):
from types import ListType, TupleType
from ZPublisher.HTTPRequest import record
inputs = ( inputs = (
('toneitem:list', '<one>'), ('toneitem:list', '<one>'),
('<tkeyoneitem>:list', 'one'), ('<tkeyoneitem>:list', 'one'),
...@@ -633,8 +601,6 @@ class ProcessInputsTests(unittest.TestCase): ...@@ -633,8 +601,6 @@ class ProcessInputsTests(unittest.TestCase):
def testNoTaintedExceptions(self): def testNoTaintedExceptions(self):
# Feed tainted garbage to the conversion methods, and any exception # Feed tainted garbage to the conversion methods, and any exception
# returned should be HTML safe # returned should be HTML safe
from ZPublisher.Converters import type_converters
from DateTime import DateTime
for type, convert in type_converters.items(): for type, convert in type_converters.items():
try: try:
convert('<html garbage>') convert('<html garbage>')
...@@ -717,12 +683,10 @@ class RequestTests( unittest.TestCase ): ...@@ -717,12 +683,10 @@ class RequestTests( unittest.TestCase ):
def testRemoveStdinReferences(self): def testRemoveStdinReferences(self):
# Verifies that all references to the input stream go away on # Verifies that all references to the input stream go away on
# request.close(). Otherwise a tempfile may stick around. # request.close(). Otherwise a tempfile may stick around.
import sys
from StringIO import StringIO
s = StringIO(TEST_FILE_DATA) s = StringIO(TEST_FILE_DATA)
env = TEST_ENVIRON.copy() env = TEST_ENVIRON.copy()
start_count = sys.getrefcount(s) start_count = sys.getrefcount(s)
from ZPublisher.HTTPRequest import HTTPRequest
req = HTTPRequest(s, env, None) req = HTTPRequest(s, env, None)
req.processInputs() req.processInputs()
self.assertNotEqual(start_count, sys.getrefcount(s)) # Precondition self.assertNotEqual(start_count, sys.getrefcount(s)) # Precondition
...@@ -731,10 +695,9 @@ class RequestTests( unittest.TestCase ): ...@@ -731,10 +695,9 @@ class RequestTests( unittest.TestCase ):
def testFileName(self): def testFileName(self):
# checks fileupload object supports the filename # checks fileupload object supports the filename
from StringIO import StringIO
s = StringIO(TEST_LARGEFILE_DATA) s = StringIO(TEST_LARGEFILE_DATA)
env = TEST_ENVIRON.copy() env = TEST_ENVIRON.copy()
from ZPublisher.HTTPRequest import HTTPRequest
req = HTTPRequest(s, env, None) req = HTTPRequest(s, env, None)
req.processInputs() req.processInputs()
f = req.form.get('file') f = req.form.get('file')
...@@ -743,11 +706,9 @@ class RequestTests( unittest.TestCase ): ...@@ -743,11 +706,9 @@ class RequestTests( unittest.TestCase ):
def testFileIterator(self): def testFileIterator(self):
# checks fileupload object supports the iterator protocol # checks fileupload object supports the iterator protocol
# collector entry 1837 # collector entry 1837
import sys
from StringIO import StringIO
s = StringIO(TEST_FILE_DATA) s = StringIO(TEST_FILE_DATA)
env = TEST_ENVIRON.copy() env = TEST_ENVIRON.copy()
from ZPublisher.HTTPRequest import HTTPRequest
req = HTTPRequest(s, env, None) req = HTTPRequest(s, env, None)
req.processInputs() req.processInputs()
f=req.form.get('file') f=req.form.get('file')
...@@ -763,8 +724,6 @@ class RequestTests( unittest.TestCase ): ...@@ -763,8 +724,6 @@ class RequestTests( unittest.TestCase ):
'SERVER_NAME': 'localhost', 'SERVER_NAME': 'localhost',
'SERVER_PORT': '80', 'SERVER_PORT': '80',
} }
from StringIO import StringIO
from ZPublisher.HTTPRequest import HTTPRequest
from zope.publisher.base import DebugFlags from zope.publisher.base import DebugFlags
s = StringIO('') s = StringIO('')
...@@ -881,8 +840,6 @@ class RequestTests( unittest.TestCase ): ...@@ -881,8 +840,6 @@ class RequestTests( unittest.TestCase ):
'SERVER_NAME': 'localhost', 'SERVER_NAME': 'localhost',
'SERVER_PORT': '80', 'SERVER_PORT': '80',
} }
from StringIO import StringIO
from ZPublisher.HTTPRequest import HTTPRequest
s = StringIO('') s = StringIO('')
env = TEST_ENVIRON.copy() env = TEST_ENVIRON.copy()
...@@ -902,8 +859,6 @@ class RequestTests( unittest.TestCase ): ...@@ -902,8 +859,6 @@ class RequestTests( unittest.TestCase ):
'REMOTE_ADDR': '127.0.0.1', 'REMOTE_ADDR': '127.0.0.1',
'HTTP_X_FORWARDED_FOR': '10.1.20.30, 192.168.1.100', 'HTTP_X_FORWARDED_FOR': '10.1.20.30, 192.168.1.100',
} }
from StringIO import StringIO
from ZPublisher.HTTPRequest import HTTPRequest, trusted_proxies
s = StringIO('') s = StringIO('')
env = TEST_ENVIRON.copy() env = TEST_ENVIRON.copy()
...@@ -925,6 +880,29 @@ class RequestTests( unittest.TestCase ): ...@@ -925,6 +880,29 @@ class RequestTests( unittest.TestCase ):
request = HTTPRequest(s, env, None) request = HTTPRequest(s, env, None)
self.assertEqual(request.getClientAddr(), '') self.assertEqual(request.getClientAddr(), '')
def testGetHeader(self):
s = StringIO('')
env = TEST_ENVIRON.copy()
request = HTTPRequest(s, env, None)
self.assertEqual(request.getHeader('Content-Type'),
'multipart/form-data; boundary=12345')
# getHeader is agnostic of case
self.assertEqual(request.getHeader('content-type'),
'multipart/form-data; boundary=12345')
# and of dashes vs. underscores
self.assertEqual(request.getHeader('content_type'),
'multipart/form-data; boundary=12345')
# the 'literal' argument can turn this normalization off:
self.assertEqual(request.getHeader('Content-Type', literal=True), None)
# the 'default' argument can be used to get something other than
# None when the lookup fails:
self.assertEqual(request.getHeader('Not-existant', default='Whatever'),
'Whatever')
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
......
##############################################################################
#
# Copyright (c) 2007 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Test mapply() function
"""
import unittest
import ExtensionClass
import Acquisition
from ZPublisher.mapply import mapply
class MapplyTests(unittest.TestCase):
def testMethod(self):
def compute(a,b,c=4):
return '%d%d%d' % (a, b, c)
values = {'a':2, 'b':3, 'c':5}
v = mapply(compute, (), values)
self.failUnlessEqual(v, '235')
v = mapply(compute, (7,), values)
self.failUnlessEqual(v, '735')
def testClass(self):
values = {'a':2, 'b':3, 'c':5}
class c(object):
a = 3
def __call__(self, b, c=4):
return '%d%d%d' % (self.a, b, c)
compute = __call__
cc = c()
v = mapply(cc, (), values)
self.failUnlessEqual(v, '335')
del values['c']
v = mapply(cc.compute, (), values)
self.failUnlessEqual(v, '334')
class c2:
"""Must be a classic class."""
c2inst = c2()
c2inst.__call__ = cc
v = mapply(c2inst, (), values)
self.failUnlessEqual(v, '334')
def testObjectWithCall(self):
# Make sure that the __call__ of an object can also be another
# callable object. mapply will do the right thing and
# recursive look for __call__ attributes until it finds an
# actual method:
class CallableObject:
def __call__(self, a, b):
return '%s%s' % (a, b)
class Container:
__call__ = CallableObject()
v = mapply(Container(), (8, 3), {})
self.assertEqual(v, '83')
def testUncallableObject(self):
# Normally, mapply will raise a TypeError if it encounters an
# uncallable object (e.g. an interger ;))
self.assertRaises(TypeError, mapply, 2, (), {})
# Unless you enable the 'maybe' flag, in which case it will
# only maybe call the object
self.assertEqual(mapply(2, (), {}, maybe=True), 2)
def testNoCallButAcquisition(self):
# Make sure that mapply won't erroneously walk up the
# Acquisition chain when looking for __call__ attributes:
class Root(ExtensionClass.Base):
def __call__(self):
return 'The root __call__'
class NoCallButAcquisition(Acquisition.Implicit):
pass
ob = NoCallButAcquisition().__of__(Root())
self.assertRaises(TypeError, mapply, ob, (), {})
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(MapplyTests))
return suite
...@@ -18,6 +18,9 @@ from AccessControl.SecurityManagement import getSecurityManager ...@@ -18,6 +18,9 @@ from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager from AccessControl.SecurityManagement import noSecurityManager
from Acquisition import aq_acquire from Acquisition import aq_acquire
from Acquisition import aq_base
from Acquisition import aq_inner
from Acquisition import aq_parent
from App.config import getConfiguration from App.config import getConfiguration
from time import asctime from time import asctime
from types import StringType, ListType from types import StringType, ListType
...@@ -130,7 +133,7 @@ def validated_hook(request, user): ...@@ -130,7 +133,7 @@ def validated_hook(request, user):
newSecurityManager(request, user) newSecurityManager(request, user)
version = request.get(Globals.VersionNameName, '') version = request.get(Globals.VersionNameName, '')
if version: if version:
object = user.aq_parent object = aq_parent(user)
if not getSecurityManager().checkPermission( if not getSecurityManager().checkPermission(
'Join/leave Versions', object): 'Join/leave Versions', object):
request['RESPONSE'].setCookie( request['RESPONSE'].setCookie(
...@@ -231,7 +234,7 @@ class ZPublisherExceptionHook: ...@@ -231,7 +234,7 @@ class ZPublisherExceptionHook:
while 1: while 1:
f = getattr(published, self.raise_error_message, None) f = getattr(published, self.raise_error_message, None)
if f is None: if f is None:
published = getattr(published, 'aq_parent', None) published = aq_parent(published)
if published is None: if published is None:
raise t, v, traceback raise t, v, traceback
else: else:
...@@ -241,8 +244,10 @@ class ZPublisherExceptionHook: ...@@ -241,8 +244,10 @@ class ZPublisherExceptionHook:
while 1: while 1:
if getattr(client, self.error_message, None) is not None: if getattr(client, self.error_message, None) is not None:
break break
client = getattr(client, 'aq_parent', None) client = aq_parent(client)
if client is None: # If we are going in circles without getting the error_message
# just raise
if client is None or aq_base(client) is aq_base(published):
raise t, v, traceback raise t, v, traceback
if REQUEST.get('AUTHENTICATED_USER', None) is None: if REQUEST.get('AUTHENTICATED_USER', None) is None:
...@@ -296,8 +301,7 @@ class TransactionsManager: ...@@ -296,8 +301,7 @@ class TransactionsManager:
object = None object = None
break break
to_append = (object.__name__,) + to_append to_append = (object.__name__,) + to_append
object = getattr(object, 'aq_inner', object) object = aq_parent(aq_inner(object))
object = getattr(object, 'aq_parent', None)
if object is not None: if object is not None:
path = '/'.join(object.getPhysicalPath() + to_append) path = '/'.join(object.getPhysicalPath() + to_append)
...@@ -312,11 +316,8 @@ class TransactionsManager: ...@@ -312,11 +316,8 @@ class TransactionsManager:
T.note(path) T.note(path)
auth_user=request_get('AUTHENTICATED_USER',None) auth_user=request_get('AUTHENTICATED_USER',None)
if auth_user is not None: if auth_user is not None:
try: auth_folder = aq_parent(auth_user)
auth_folder = auth_user.aq_parent if auth_folder is None:
except AttributeError:
# Most likely some product forgot to call __of__()
# on the user object.
ac_logger.warning( ac_logger.warning(
'A user object of type %s has no aq_parent.', 'A user object of type %s has no aq_parent.',
type(auth_user) type(auth_user)
...@@ -326,6 +327,3 @@ class TransactionsManager: ...@@ -326,6 +327,3 @@ class TransactionsManager:
auth_path = '/'.join(auth_folder.getPhysicalPath()[1:-1]) auth_path = '/'.join(auth_folder.getPhysicalPath()[1:-1])
T.setUser(auth_user.getId(), auth_path) T.setUser(auth_user.getId(), auth_path)
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