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

Merged philikon-aq branch into trunk, yeah!

parents d6687526 010ab27a
......@@ -78,6 +78,10 @@ Zope Changes
Features added
- 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.
......
......@@ -17,10 +17,8 @@ import os
import string
from logging import getLogger
from Acquisition import aq_base
from Acquisition import aq_parent
from Acquisition import aq_inner
from Acquisition import aq_acquire
from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire
from Acquisition import aq_inContextOf
from ExtensionClass import Base
from zope.interface import implements
......@@ -98,10 +96,10 @@ def rolesForPermissionOn(perm, object, default=_default_roles, n=None):
else:
r = r + list(roles)
object = getattr(object, 'aq_inner', None)
object = aq_inner(object)
if object is None:
break
object = object.aq_parent
object = aq_parent(object)
if r is None:
if _embed_permission_in_roles:
......@@ -295,7 +293,7 @@ class ZopeSecurityPolicy:
raise Unauthorized(name, value)
else:
# Try to acquire roles
try: roles = container.aq_acquire('__roles__')
try: roles = aq_acquire(container, '__roles__')
except AttributeError:
if containerbase is not accessedbase:
if self._verbose:
......@@ -840,17 +838,10 @@ def verifyAcquisitionContext(user, object, object_roles=None):
# This is a strange rule, though
# it doesn't cause any security holes. SDH
return 1
if not hasattr(object, 'aq_inContextOf'):
if hasattr(object, 'im_self'):
# This is a method. Grab its self.
object=object.im_self
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 not aq_inContextOf(object, ucontext, 1):
if 'Shared' in object_roles:
# Old role setting. Waaa
object_roles=user._shared_roles(object)
......
......@@ -21,7 +21,7 @@ from AccessControl import ClassSecurityInfo
from AccessControl import getSecurityManager, Unauthorized
from AccessControl.Permissions import view_management_screens
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 zope.interface import implements
......@@ -236,12 +236,12 @@ class Owned(ExtensionClass.Base):
def manage_fixupOwnershipAfterAdd(self):
# 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)
else: _owner=None
if (_owner is None and
((not hasattr(self, 'aq_parent')) or
((getattr(self, '__parent__', None) is None) or
(not hasattr(self, 'getPhysicalRoot'))
)
):
......@@ -298,13 +298,13 @@ def ownerInfo(user, getattr=getattr):
return None
uid=user.getId()
if uid is None: return uid
db=user.aq_inner.aq_parent
db=aq_parent(aq_inner(user))
path=[absattr(db.id)]
root=db.getPhysicalRoot()
while 1:
db=getattr(db,'aq_inner', None)
if db is None: break
db=db.aq_parent
db=aq_parent(db)
if db is root: break
id=db.id
if not isinstance(id, str):
......
......@@ -17,6 +17,7 @@ $Id$
import string, Products, Globals
from Acquisition import aq_base
name_trans=filter(lambda c, an=string.letters+string.digits+'_': c not in an,
map(chr,range(256)))
......@@ -36,8 +37,7 @@ class Permission:
self.name=name
self._p='_'+string.translate(name,name_trans)+"_Permission"
self.data=data
if hasattr(obj, 'aq_base'): obj=obj.aq_base
self.obj=obj
self.obj=aq_base(obj)
self.default=default
def getRoles(self, default=_marker):
......
......@@ -105,7 +105,7 @@ class RoleManager:
return r
def _isBeingAccessedAsZClassDefinedInstanceMethod(self):
p=getattr(self,'aq_parent',None)
p=getattr(self,'__parent__',None)
if p is None: return 0 # Not wrapped
base=getattr(p, 'aq_base', None)
return type(base) is PermissionMapper
......
......@@ -188,7 +188,7 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
if userObj:
break
else:
current = current.aq_parent
current = current.__parent__
newSecurityManager(None, userObj) # necessary?
......@@ -414,7 +414,7 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
raise OverflowError
for name in unl:
dict[name]=1
item = getattr(item, 'aq_parent', _notfound)
item = getattr(item, '__parent__', _notfound)
if item is _notfound:
break
keys=dict.keys()
......@@ -511,9 +511,9 @@ class RoleManager(ExtensionClass.Base, PermissionMapping.RoleManager):
for role in roles:
if not dup(role):
dict[role]=1
if not hasattr(obj, 'aq_parent'):
if getattr(obj, '__parent__', None) is None:
break
obj=obj.aq_parent
obj=obj.__parent__
x=x+1
roles=dict.keys()
roles.sort()
......
......@@ -20,6 +20,9 @@ import re
import socket
from base64 import decodestring
from Acquisition import aq_base
from Acquisition import aq_parent
from Acquisition import aq_inContextOf
from Acquisition import Implicit
from App.Management import Navigation, Tabs
from Globals import DTMLFile, MessageDialog, Persistent, PersistentMapping
......@@ -106,7 +109,7 @@ class BasicUser(Implicit):
for r in dict.get(userid, []):
local[r]=1
inner = getattr(object, 'aq_inner', object)
parent = getattr(inner, 'aq_parent', None)
parent = getattr(inner, '__parent__', None)
if parent is not None:
object = parent
continue
......@@ -148,10 +151,10 @@ class BasicUser(Implicit):
else:
try: return r+list(roles)
except: return r
if hasattr(parent, 'aq_parent'):
if getattr(parent, '__parent__', None) is not None:
while hasattr(parent.aq_self,'aq_self'):
parent=parent.aq_self
parent=parent.aq_parent
parent = parent.aq_self
parent = aq_parent(parent)
else: return r
def _check_context(self, object):
......@@ -160,19 +163,15 @@ class BasicUser(Implicit):
# to prevent "stealing" access through acquisition tricks.
# Return true if in context, false if not or if context
# cannot be determined (object is not wrapped).
parent = getattr(self, 'aq_parent', None)
context = getattr(parent, 'aq_parent', None)
parent = getattr(self, '__parent__', None)
context = getattr(parent, '__parent__', None)
if context is not None:
if object is None:
return 1
if not hasattr(object, 'aq_inContextOf'):
if hasattr(object, 'im_self'):
# This is a method. Grab its self.
object=object.im_self
if not hasattr(object, 'aq_inContextOf'):
# Object is not wrapped, so return false.
return 0
return object.aq_inContextOf(context, 1)
return aq_inContextOf(object, context, 1)
# This is lame, but required to keep existing behavior.
return 1
......@@ -230,7 +229,7 @@ class BasicUser(Implicit):
return 1
return 0
inner = getattr(inner_obj, 'aq_inner', inner_obj)
parent = getattr(inner, 'aq_parent', None)
parent = getattr(inner, '__parent__', None)
if parent is not None:
inner_obj = parent
continue
......@@ -751,11 +750,11 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
request.RESPONSE.notFoundError('no default view (root default view'
' was probably deleted)')
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]
# try to find actual container
inner = getattr(v, 'aq_inner', v)
innerparent = getattr(inner, 'aq_parent', None)
innerparent = getattr(inner, '__parent__', None)
if innerparent is not None:
# this is not a method, we needn't treat it specially
c = innerparent
......@@ -763,8 +762,8 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
# this is a method, we need to treat it specially
c = v.im_self
c = getattr(v, 'aq_inner', v)
request_container = getattr(request['PARENTS'][-1], 'aq_parent', [])
# if pub's aq_parent or container is the request container, it
request_container = getattr(request['PARENTS'][-1], '__parent__', [])
# if pub's __parent__ or container is the request container, it
# means pub was accessed from the root
if a is request_container:
a = request['PARENTS'][-1]
......@@ -775,7 +774,7 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
def _isTop(self):
try:
return self.aq_parent.aq_base.isTopLevelPrincipiaApplicationObject
return aq_base(aq_parent(self)).isTopLevelPrincipiaApplicationObject
except:
return 0
......@@ -990,8 +989,8 @@ class BasicUserFolder(Implicit, Persistent, Navigation, Tabs, RoleManager,
def manage_afterAdd(self, item, container):
if item is self:
if hasattr(self, 'aq_base'): self=self.aq_base
container.__allow_groups__=self
self = aq_base(self)
container.__allow_groups__ = self
def __creatable_by_emergency_user__(self): return 1
......
......@@ -1878,13 +1878,11 @@ c_rolesForPermissionOn(PyObject *perm, PyObject *object,
/*
object = getattr(object, 'aq_inner', None)
object = aq_inner(object)
if object is None:
break
object = object.aq_parent
object = aq_parent(object)
*/
if (! aq_isWrapper(object))
break;
{
PyObject *tobj = aq_inner(object);
if (tobj == NULL)
......@@ -1895,8 +1893,6 @@ c_rolesForPermissionOn(PyObject *perm, PyObject *object,
if (object == Py_None)
break;
if (! aq_isWrapper(object))
break;
tobj = aq_parent(object);
if (tobj == NULL)
goto end;
......
......@@ -38,7 +38,8 @@ static PyObject *py__add__, *py__sub__, *py__mul__, *py__div__,
*py__long__, *py__float__, *py__oct__, *py__hex__,
*py__getitem__, *py__setitem__, *py__delitem__,
*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;
......@@ -82,7 +83,7 @@ init_py_names(void)
INIT_PY_NAME(__repr__);
INIT_PY_NAME(__str__);
INIT_PY_NAME(__cmp__);
INIT_PY_NAME(__parent__);
#undef INIT_PY_NAME
}
......@@ -414,13 +415,38 @@ static PyObject *
Wrapper_findattr(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig,
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;
char *name="";
if (PyString_Check(oname)) name=PyString_AS_STRING(oname);
if (*name=='a' && name[1]=='q' && name[2]=='_')
if ((r=Wrapper_special(self, name+3, oname)))
if ((*name=='a' && name[1]=='q' && name[2]=='_') ||
(strcmp(name, "__parent__")==0))
{
/* __parent__ is an alias to aq_parent */
if (strcmp(name, "__parent__")==0)
name = "parent";
else
name = name + 3;
if ((r=Wrapper_special(self, name, oname)))
{
if (filter)
switch(apply_filter(filter,OBJECT(self),oname,r,extra,orig))
......@@ -431,6 +457,7 @@ Wrapper_findattr(Wrapper *self, PyObject *oname,
else return r;
}
else PyErr_Clear();
}
else if (*name=='_' && name[1]=='_' &&
(strcmp(name+2,"reduce__")==0 ||
strcmp(name+2,"reduce_ex__")==0 ||
......@@ -477,6 +504,7 @@ Wrapper_findattr(Wrapper *self, PyObject *oname,
Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
r=NULL;
}
/* normal attribute lookup */
else if ((r=PyObject_GetAttr(self->obj,oname)))
{
if (r==Acquired)
......@@ -511,6 +539,7 @@ Wrapper_findattr(Wrapper *self, PyObject *oname,
PyErr_Clear();
}
/* Lookup has failed, acquire it from parent. */
if (sco && (*name != '_' || explicit))
return Wrapper_acquire(self, oname, filter, extra, orig, explicit,
containment);
......@@ -524,16 +553,19 @@ Wrapper_acquire(Wrapper *self, PyObject *oname,
PyObject *filter, PyObject *extra, PyObject *orig,
int explicit, int containment)
{
PyObject *r;
PyObject *r, *v, *tb;
int sob=1, sco=1;
if (self->container)
{
/* If the container has an acquisition wrapper itself, we'll use
Wrapper_findattr to progress further. */
if (isWrapper(self->container))
{
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==
WRAPPER(self->container)->container)
sco=0;
......@@ -542,6 +574,14 @@ Wrapper_acquire(Wrapper *self, PyObject *oname,
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,
oname, filter, extra, orig, sob, sco, explicit,
containment);
......@@ -549,8 +589,46 @@ Wrapper_acquire(Wrapper *self, PyObject *oname,
if (r && has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
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
{
/* 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 == Acquired) {
Py_DECREF(r);
......@@ -618,8 +696,8 @@ Wrapper_setattro(Wrapper *self, PyObject *oname, PyObject *v)
/* Allow assignment to parent, to change context. */
if (PyString_Check(oname)) name=PyString_AS_STRING(oname);
if (*name=='a' && name[1]=='q' && name[2]=='_'
&& strcmp(name+3,"parent")==0)
if ((*name=='a' && name[1]=='q' && name[2]=='_'
&& strcmp(name+3,"parent")==0) || (strcmp(name, "__parent__")==0))
{
Py_XINCREF(v);
ASSIGN(self->container, v);
......@@ -1112,57 +1190,18 @@ Wrapper_acquire_method(Wrapper *self, PyObject *args, PyObject *kw)
# 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 *
Wrapper_inContextOf(Wrapper *self, PyObject *args)
{
PyObject *subob, *o, *c;
int inner=1;
PyObject *o;
int inner=1;
UNLESS(PyArg_ParseTuple(args,"O|i",&o,&inner)) return NULL;
if (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);
return capi_aq_inContextOf((PyObject*)self, o, inner);
}
PyObject *
......@@ -1332,8 +1371,7 @@ static PyObject *
capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
PyObject *extra, int explicit, PyObject *defalt, int containment)
{
PyObject *result;
PyObject *result, *v, *tb;
if (filter==Py_None) filter=0;
......@@ -1344,21 +1382,45 @@ capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
explicit ||
WRAPPER(self)->ob_type==(PyTypeObject*)&Wrappertype,
explicit, 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, 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);
/* Not wrapped and no filter, so just getattr */
if (! filter) return PyObject_GetAttr(self, name);
/* Crap, we've got to construct a wrapper so we can use Wrapper_findattr */
/* Crap, we've got to construct a wrapper so we can use
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);
/* get rid of temp wrapper */
/* Get rid of temporary wrapper */
Py_DECREF(self);
return result;
}
}
static PyObject *
......@@ -1384,13 +1446,35 @@ module_aq_acquire(PyObject *ignored, PyObject *args, PyObject *kw)
static PyObject *
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 */
if (isWrapper(self))
result=Wrapper_findattr(WRAPPER(self), name, 0, 0, OBJECT(self), 1, 1, 1,
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
{
/* 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)
{
......@@ -1453,13 +1537,34 @@ module_aq_base(PyObject *ignored, PyObject *args)
static PyObject *
capi_aq_parent(PyObject *self)
{
PyObject *result=Py_None;
PyObject *result, *v, *tb;
if (isWrapper(self) && 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);
result=Py_None;
Py_INCREF(result);
return result;
}
}
static PyObject *
......@@ -1535,7 +1640,7 @@ module_aq_inner(PyObject *ignored, PyObject *args)
static PyObject *
capi_aq_chain(PyObject *self, int containment)
{
PyObject *result;
PyObject *result, *v, *tb;
UNLESS (result=PyList_New(0)) return NULL;
......@@ -1558,9 +1663,28 @@ capi_aq_chain(PyObject *self, int containment)
}
}
else
{
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;
}
......@@ -1582,6 +1706,53 @@ module_aq_chain(PyObject *ignored, PyObject *args)
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[] = {
{"aq_acquire", (PyCFunction)module_aq_acquire, METH_VARARGS|METH_KEYWORDS,
"aq_acquire(ob, name [, filter, extra, explicit]) -- "
......@@ -1599,10 +1770,13 @@ static struct PyMethodDef methods[] = {
"aq_self(ob) -- Get the object with the outermost wrapper removed"},
{"aq_inner", (PyCFunction)module_aq_inner, METH_VARARGS,
"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(ob [, containment]) -- "
"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}
};
......
......@@ -357,6 +357,11 @@ def test_unwrapped():
...
AttributeError: aq_parent
>>> c.__parent__
Traceback (most recent call last):
...
AttributeError: __parent__
>>> Acquisition.aq_acquire(c, 'id')
'unwrapped'
>>> Acquisition.aq_acquire(c, 'x')
......@@ -452,6 +457,13 @@ def test_simple():
>>> a.b.c.aq_inContextOf(a.b.c)
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')
42
......@@ -533,6 +545,13 @@ def test_simple():
>>> show(Acquisition.aq_self(a.b.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():
......@@ -1201,7 +1220,7 @@ def test_mixed_explicit_and_explicit():
"""
def old_tests():
def test_aq_inContextOf():
"""
>>> from ExtensionClass import Base
>>> import Acquisition
......@@ -1213,6 +1232,9 @@ def old_tests():
... def hi(self):
... print "%s()" % self.__class__.__name__, self.color
>>> class Location(object):
... __parent__ = None
>>> b=B()
>>> b.a=A()
>>> b.a.hi()
......@@ -1242,18 +1264,24 @@ def old_tests():
>>> b.c == c
1
>>> l = Location()
>>> l.__parent__ = b.c
>>> def checkContext(self, o):
... # Python equivalent to aq_inContextOf
... from Acquisition import aq_base, aq_parent, aq_inner
... subob = self
... next = self
... o = aq_base(o)
... while 1:
... if aq_base(subob) is o: return 1
... self = aq_inner(subob)
... if self is None: break
... subob = aq_parent(self)
... if subob is None: break
... if aq_base(next) is o:
... return 1
... self = aq_inner(next)
... if self is None:
... break
... next = aq_parent(self)
... if next is None:
... break
... return 0
>>> checkContext(b.c, b)
......@@ -1261,6 +1289,27 @@ def old_tests():
>>> not checkContext(b.c, b.a)
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)
1
>>> b.c.aq_inContextOf(b)
......@@ -1271,12 +1320,12 @@ def old_tests():
1
>>> b.c.d.aq_inContextOf(b.c)
1
>>> not b.c.aq_inContextOf(foo)
1
>>> not b.c.aq_inContextOf(b.a)
1
>>> not b.a.aq_inContextOf('somestring')
1
>>> b.c.aq_inContextOf(foo)
0
>>> b.c.aq_inContextOf(b.a)
0
>>> b.a.aq_inContextOf('somestring')
0
"""
def test_AqAlg():
......@@ -1389,7 +1438,7 @@ def test_creating_wrappers_directly():
...
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.color = 'green'
......@@ -1397,6 +1446,20 @@ def test_creating_wrappers_directly():
>>> w.color
'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()
Traceback (most recent call last):
...
......@@ -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
from zope.testing.doctest import DocTestSuite, DocFileSuite
......
......@@ -66,7 +66,7 @@ class FactoryDispatcher(Acquisition.Implicit):
_owner=UnownableOwner
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._d=dest
if REQUEST is not None:
......@@ -100,7 +100,7 @@ class FactoryDispatcher(Acquisition.Implicit):
m=d[name]
w=getattr(m, '_permissionMapper', 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
......
......@@ -22,6 +22,7 @@ from AccessControl import ClassSecurityInfo
from AccessControl.DTML import RestrictedDTML
from AccessControl.Permission import name_trans
from AccessControl.Permissions import view_management_screens
from Acquisition import aq_base
from DateTime import DateTime
from DocumentTemplate.DT_Util import Eval
from DocumentTemplate.DT_Util import InstanceDict, TemplateDict
......@@ -92,9 +93,7 @@ class FindSupport(ExtensionClass.Base):
md=td()
obj_expr=(Eval(obj_expr), md, md._push, md._pop)
base=obj
if hasattr(obj, 'aq_base'):
base=obj.aq_base
base = aq_base(obj)
if hasattr(base, 'objectItems'):
try: items=obj.objectItems()
......@@ -118,9 +117,7 @@ class FindSupport(ExtensionClass.Base):
if hasattr(ob, '_p_changed') and (ob._p_changed == None):
dflag=1
if hasattr(ob, 'aq_base'):
bs=ob.aq_base
else: bs=ob
bs = aq_base(ob)
if (
(not obj_ids or absattr(bs.getId()) in obj_ids)
and
......@@ -200,9 +197,7 @@ class FindSupport(ExtensionClass.Base):
md=td()
obj_expr=(Eval(obj_expr), md, md._push, md._pop)
base=obj
if hasattr(obj, 'aq_base'):
base=obj.aq_base
base = aq_base(obj)
if not hasattr(base, 'objectItems'):
return result
......@@ -221,10 +216,7 @@ class FindSupport(ExtensionClass.Base):
if hasattr(ob, '_p_changed') and (ob._p_changed == None):
dflag=1
if hasattr(ob, 'aq_base'):
bs=ob.aq_base
else: bs=ob
bs = aq_base(ob)
if (
(not obj_ids or absattr(bs.getId()) in obj_ids)
and
......
......@@ -20,13 +20,14 @@ from webdav.WriteLockInterface import WriteLockInterface
from ZPublisher.Converters import type_converters
from Globals import InitializeClass
from Globals import DTMLFile, MessageDialog
from Acquisition import aq_base
from Acquisition import aq_parent
from Acquisition import Implicit, Explicit
from App.Common import rfc1123_date, iso8601_date
from webdav.common import urlbase
from ExtensionClass import Base
from Globals import Persistent
from Traversable import Traversable
from Acquisition import aq_base
from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import access_contents_information
from AccessControl.Permissions import manage_properties
......@@ -71,7 +72,7 @@ class View(App.Management.Tabs, Base):
pre=pre+'/'
r=[]
for d in self.aq_parent.aq_parent.manage_options:
for d in aq_parent(aq_parent(self)).manage_options:
path=d['action']
option={'label': d['label'],
'action': pre+path,
......@@ -92,7 +93,7 @@ class View(App.Management.Tabs, Base):
self, script, path)
def meta_type(self):
try: return self.aq_parent.aq_parent.meta_type
try: return aq_parent(aq_parent(self)).meta_type
except: return ''
......@@ -489,7 +490,7 @@ class Virtual:
pass
def v_self(self):
return self.aq_parent.aq_parent
return aq_parent(aq_parent(self))
class DefaultProperties(Virtual, PropertySheet, View):
......@@ -635,7 +636,7 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs):
return (self.webdav,)
def __propsets__(self):
propsets=self.aq_parent.__propsets__
propsets = aq_parent(self).__propsets__
__traceback_info__= propsets, type(propsets)
return self._get_defaults() + propsets
......@@ -684,17 +685,17 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs):
security.declareProtected(manage_properties, 'addPropertySheet')
def addPropertySheet(self, propset):
propsets=self.aq_parent.__propsets__
propsets=propsets+(propset,)
self.aq_parent.__propsets__=propsets
propsets = aq_parent(self).__propsets__
propsets = propsets+(propset,)
aq_parent(self).__propsets__ = propsets
security.declareProtected(manage_properties, 'delPropertySheet')
def delPropertySheet(self, name):
result=[]
for propset in self.aq_parent.__propsets__:
for propset in aq_parent(self).__propsets__:
if propset.getId() != name and propset.xml_namespace() != name:
result.append(propset)
self.aq_parent.__propsets__=tuple(result)
aq_parent(self).__propsets__=tuple(result)
## DM: deletion support
def isDeletable(self,name):
......@@ -743,7 +744,7 @@ class PropertySheets(Traversable, Implicit, App.Management.Tabs):
pre=pre+'/'
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']})
return r
......
......@@ -205,14 +205,16 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, Traversable,
if match is not None:
error_message=error_value
if client is None: client=self
if not REQUEST: REQUEST=self.aq_acquire('REQUEST')
if client is None:
client = self
if not REQUEST:
REQUEST = aq_acquire(self, 'REQUEST')
try:
if hasattr(client, 'standard_error_message'):
s=getattr(client, 'standard_error_message')
else:
client = client.aq_parent
client = aq_parent(client)
s=getattr(client, 'standard_error_message')
kwargs = {'error_type': error_type,
'error_value': error_value,
......@@ -329,7 +331,7 @@ class Item(Base, Resource, CopySource, App.Management.Tabs, Traversable,
raise ValueError('FTP List not supported on acquired objects')
if not hasattr(ob,'aq_parent'):
break
ob=ob.aq_parent
ob = aq_parent(ob)
stat=marshal.loads(self.manage_FTPstat(REQUEST))
id = self.getId()
......
......@@ -22,7 +22,8 @@ from AccessControl import ClassSecurityInfo
from AccessControl import getSecurityManager
from AccessControl import Unauthorized
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 ZODB.POSException import ConflictError
from OFS.interfaces import ITraversable
......@@ -64,7 +65,7 @@ class Traversable:
spp = self.getPhysicalPath()
try:
toUrl = self.REQUEST.physicalPathToURL
toUrl = aq_acquire(self, 'REQUEST').physicalPathToURL
except AttributeError:
return path2url(spp[1:])
return toUrl(spp)
......@@ -78,7 +79,7 @@ class Traversable:
"""
spp = self.getPhysicalPath()
try:
toUrl = self.REQUEST.physicalPathToURL
toUrl = aq_acquire(self, 'REQUEST').physicalPathToURL
except AttributeError:
return path2url(spp) or '/'
return toUrl(spp, relative=1) or '/'
......@@ -93,7 +94,7 @@ class Traversable:
"""
spp = self.getPhysicalPath()
try:
toVirt = self.REQUEST.physicalPathToVirtualPath
toVirt = aq_acquire(self, 'REQUEST').physicalPathToVirtualPath
except AttributeError:
return path2url(spp[1:])
return path2url(toVirt(spp))
......@@ -191,7 +192,9 @@ class Traversable:
ns, nm = nsParse(name)
try:
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(
obj, obj, name, next):
raise Unauthorized(name)
......@@ -256,11 +259,10 @@ class Traversable:
except (AttributeError, NotFound, KeyError), e:
# Try to look for a view
next = queryMultiAdapter((obj, self.REQUEST),
next = queryMultiAdapter((obj, aq_acquire(self, 'REQUEST')),
Interface, name)
if next is not None:
next = next.__of__(obj)
if restricted and not validate(obj, obj, name, next):
raise Unauthorized(name)
elif bobo_traverse is not None:
......
......@@ -16,6 +16,8 @@ DOM implementation in ZOPE : Read-Only methods
All standard Zope objects support DOM to a limited extent.
"""
import Acquisition
from Acquisition import aq_base
from Acquisition import aq_parent
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import access_contents_information
......@@ -149,7 +151,7 @@ class Node:
When this is a document this is None"""
node = self
if hasattr(node, 'aq_parent'):
node = self.aq_parent
node = aq_parent(self)
return node.getOwnerDocument()
return node
......@@ -198,7 +200,7 @@ class Document(Acquisition.Explicit, Node):
This is a convenience attribute that allows direct access to
the child node that is the root element of the document.
"""
return self.aq_parent
return aq_parent(self)
# Node Methods
# ------------
......@@ -219,17 +221,17 @@ class Document(Acquisition.Explicit, Node):
def getChildNodes(self):
"""Returns a NodeList that contains all children of this node.
If there are no children, this is a empty NodeList"""
return NodeList([self.aq_parent])
return NodeList([aq_parent(self)])
def getFirstChild(self):
"""The first child of this node. If there is no such node
this returns None."""
return self.aq_parent
return aq_parent(self)
def getLastChild(self):
"""The last child of this node. If there is no such node
this returns None."""
return self.aq_parent
return aq_parent(self)
def hasChildNodes(self):
"""Returns true if the node has any children, false
......@@ -324,7 +326,7 @@ class Element(Node):
"""The node immediately preceding this node. If
there is no such node, this returns None."""
if hasattr(self, 'aq_parent'):
parent = self.aq_parent
parent = aq_parent(self)
ids=list(parent.objectIds())
id=self.id
if type(id) is not type(''): id=id()
......@@ -338,7 +340,7 @@ class Element(Node):
"""The node immediately preceding this node. If
there is no such node, this returns None."""
if hasattr(self, 'aq_parent'):
parent = self.aq_parent
parent = aq_parent(self)
ids=list(parent.objectIds())
id=self.id
if type(id) is not type(''): id=id()
......@@ -432,7 +434,7 @@ class ElementWithTitle(Element):
def getAttribute(self, 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 ''
......
......@@ -19,9 +19,31 @@ from zope.interface import Interface, implements
from zope.component.interfaces import ComponentLookupError
from zope.app.publisher.browser import getDefaultViewName
import zExceptions
import Products.Five.security
from Products.Five import fivemethod
import Acquisition
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):
"""Provide a hook for deciding about the default view for an object"""
......
......@@ -18,8 +18,24 @@ $Id$
import Acquisition
import zope.publisher.browser
class BrowserView(Acquisition.Explicit, zope.publisher.browser.BrowserView):
"""Five browser view
from Products.Five.bbb import AcquisitionBBB
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 @@
$Id$
"""
import urllib
from Acquisition import aq_inner, aq_parent
from OFS.interfaces import ITraversable
from zope.interface import implements
from zope.component import getMultiAdapter
from zope.traversing.browser.interfaces import IAbsoluteURL
from zope.traversing.browser.absoluteurl import _insufficientContext, _safe
from Products.Five.browser import 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)
def __init__(self, context, request):
self.context, self.request = context, request
def __unicode__(self):
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):
context = aq_inner(self.context)
......@@ -47,10 +110,10 @@ class AbsoluteURL(BrowserView):
name = context.getId()
if container is None or self._isVirtualHostRoot() \
or not ITraversable.providedBy(container):
return (
{'name': name, 'url': context.absolute_url()},)
if (container is None
or self._isVirtualHostRoot()
or not ITraversable.providedBy(container)):
return ({'name': name, 'url': context.absolute_url()},)
view = getMultiAdapter((container, request), IAbsoluteURL)
base = tuple(view.breadcrumbs())
......@@ -66,15 +129,9 @@ class AbsoluteURL(BrowserView):
context = aq_inner(self.context)
return context.restrictedTraverse(virtualrootpath) == context
class SiteAbsoluteURL(AbsoluteURL):
"""An adapter for Zope3-style absolute_url using Zope2 methods
This one is just used to stop breadcrumbs from crumbing up
to the Zope root.
(original: zope.traversing.browser.absoluteurl)
class RootAbsoluteURL(OFSTraversableAbsoluteURL):
"""An absolute_url adapter for the root object (OFS.Application)
"""
def breadcrumbs(self):
context = self.context
request = self.request
......
......@@ -23,6 +23,8 @@ $Id$
__docformat__ = 'restructuredtext'
from warnings import warn
from zope.component import getMultiAdapter
from zope.component import getUtility
from zope.component import queryMultiAdapter
......@@ -41,15 +43,13 @@ from zope.app.container.interfaces import IAdding, INameChooser
from zope.app.container.interfaces import IContainerNamesContainer
from zope.app.publisher.browser.menu import getMenu
from Acquisition import Implicit
from zExceptions import BadRequest
from OFS.SimpleItem import SimpleItem
from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class Adding(Implicit, BrowserView):
class BasicAdding(BrowserView):
implements(IAdding, IPublishTraverse)
def add(self, content):
......@@ -78,7 +78,7 @@ class Adding(Implicit, BrowserView):
# Invoke the name chooser even when we have a
# name. It'll do useful things with it like converting
# the incoming unicode to an ASCII string.
name = chooser.chooseName(name, content)
name = chooser.chooseName(name, container)
content.id = name
container._setObject(name, content)
......@@ -92,12 +92,18 @@ class Adding(Implicit, BrowserView):
# XXX this is definitely not right for all or even most uses
# of Five, but can be overridden by an AddView subclass, using
# 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__
request = None
context = None
def renderAddButton(self):
warn("The renderAddButton method is deprecated, use nameAllowed",
DeprecationWarning, 2)
def publishTraverse(self, request, name):
"""See zope.publisher.interfaces.IPublishTraverse"""
if '=' in name:
......@@ -119,7 +125,7 @@ class Adding(Implicit, BrowserView):
factory = queryUtility(IFactory, name)
if factory is None:
return super(Adding, self).publishTraverse(request, name)
return super(BasicAdding, self).publishTraverse(request, name)
return factory
......@@ -135,10 +141,11 @@ class Adding(Implicit, BrowserView):
else:
view_name = type_name
if queryMultiAdapter((self, self.request),
name=view_name) is not None:
if (queryMultiAdapter((self, self.request), name=view_name)
is not None):
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)
return
......@@ -153,10 +160,16 @@ class Adding(Implicit, BrowserView):
self.add(content)
self.request.response.redirect(self.nextURL())
def namesAccepted(self):
return not IContainerNamesContainer.providedBy(self.context)
def nameAllowed(self):
"""Return whether names can be input by the user."""
return not IContainerNamesContainer.providedBy(self.context)
class Adding(BasicAdding):
menu_id = None
index = ViewPageTemplateFile("adding.pt")
......
......@@ -39,16 +39,32 @@
/>
<browser:page
for="zope.traversing.interfaces.IContainmentRoot"
for="OFS.interfaces.ITraversable"
name="absolute_url"
class=".absoluteurl.SiteAbsoluteURL"
class=".absoluteurl.OFSTraversableAbsoluteURL"
permission="zope.Public"
allowed_interface="zope.traversing.browser.interfaces.IAbsoluteURL"
/>
<view
for="zope.traversing.interfaces.IContainmentRoot"
factory=".absoluteurl.SiteAbsoluteURL"
for="OFS.interfaces.ITraversable"
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"
permission="zope.Public"
provides="zope.traversing.browser.interfaces.IAbsoluteURL"
......
......@@ -29,17 +29,17 @@ from zope.configuration.exceptions import ConfigurationError
from zope.publisher.interfaces.browser import IBrowserRequest, \
IDefaultBrowserLayer
from zope.app.publisher.browser.viewmeta import pages as zope_app_pages
from zope.app.publisher.browser.viewmeta import view as zope_app_view
from zope.app.publisher.browser.viewmeta import providesCallable, \
_handle_menu, _handle_for
import zope.app.publisher.browser.viewmeta
import zope.app.pagetemplate.simpleviewclass
from zope.app.publisher.browser.viewmeta import (providesCallable,
_handle_menu, _handle_for)
from Products.Five.browser import BrowserView
from Products.Five.browser.resource import FileResourceFactory
from Products.Five.browser.resource import ImageResourceFactory
from Products.Five.browser.resource import PageTemplateResourceFactory
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.security import getSecurityInfo, protectClass, protectName
from Products.Five.security import CheckerPrivateId
......@@ -159,7 +159,7 @@ def page(_context, name, permission, for_,
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,
menu=None, title=None):
......@@ -172,7 +172,7 @@ class pages(zope_app_pages):
# view (named view with pages)
class view(zope_app_view):
class view(zope.app.publisher.browser.viewmeta.view):
def __call__(self):
(_context, name, for_, permission, layer, class_,
......@@ -185,7 +185,7 @@ class view(zope_app_view):
for pname, attribute, template in self.pages:
if template:
cdict[pname] = ZopeTwoPageTemplateFile(template)
cdict[pname] = ViewPageTemplateFile(template)
if attribute and attribute != name:
cdict[attribute] = cdict[pname]
else:
......@@ -209,9 +209,9 @@ class view(zope_app_view):
view = component.queryMultiAdapter((self, request), name=name,
default=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)
else:
......@@ -223,7 +223,7 @@ class view(zope_app_view):
view = component.queryMultiAdapter((self, request), name=name,
default=None)
if view is not None:
return view.__of__(self)
return view
raise NotFoundError(self, name, request)
......@@ -389,39 +389,29 @@ def resourceDirectory(_context, name, directory, layer=IDefaultBrowserLayer,
args = (new_class,)
)
#
# mixin classes / class factories
#
class ViewMixinForAttributes(BrowserView):
class ViewMixinForAttributes(BrowserView,
zope.app.publisher.browser.viewmeta.simple):
# we have an attribute that we can simply tell ZPublisher to go to
def __browser_default__(self, request):
return self, (self.__page_attribute__,)
# For some reason, the 'simple' baseclass doesn't implement this
# mandatory method (see https://bugs.launchpad.net/zope3/+bug/129296)
def browserDefault(self, request):
return getattr(self, self.__page_attribute__), ()
# this is technically not needed because ZPublisher finds our
# attribute through __browser_default__; but we also want to be
# able to call pages from python modules, PythonScripts or ZPT
__call__ = property(lambda self: 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]
# __call__ should have the same signature as the original method
@property
def __call__(self):
return getattr(self, self.__page_attribute__)
# make the template publishable
def __call__(self, *args, **kw):
return self.index(self, *args, **kw)
class ViewMixinForTemplates(BrowserView,
zope.app.pagetemplate.simpleviewclass.simple):
pass
def makeClassForTemplate(filename, globals=None, used_for=None,
bases=(), cdict=None, name=u''):
# XXX needs to deal with security from the bases?
if cdict is None:
cdict = {}
cdict.update({'index': ZopeTwoPageTemplateFile(filename, globals),
cdict.update({'index': ViewPageTemplateFile(filename, globals),
'__name__': name})
bases += (ViewMixinForTemplates,)
class_ = makeClass("SimpleViewClass from %s" % filename, bases, cdict)
......
......@@ -15,85 +15,84 @@
$Id$
"""
import os, sys
from os.path import basename
from zope.app.pagetemplate import viewpagetemplatefile
from Acquisition import aq_inner
from Globals import package_home
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Acquisition import aq_get
from AccessControl import getSecurityManager
from Products.PageTemplates.Expressions import SecureModuleImporter
from Products.PageTemplates.Expressions import createTrustedZopeEngine
from zope.app.pagetemplate.viewpagetemplatefile import ViewMapper
from Products.Five.bbb import AcquisitionBBB
_engine = createTrustedZopeEngine()
def getEngine():
return _engine
class ZopeTwoPageTemplateFile(PageTemplateFile):
"""A strange hybrid between Zope 2 and Zope 3 page template.
Uses Zope 2's engine, but with security disabled and with some
initialization and API from Zope 3.
class ViewPageTemplateFile(viewpagetemplatefile.ViewPageTemplateFile):
"""Page Template used as class variable of views defined as Python classes.
"""
def __init__(self, filename, _prefix=None, content_type=None):
# XXX doesn't use content_type yet
def getId(self):
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)
self.filename = os.path.join(path, filename)
if not os.path.isfile(self.filename):
raise ValueError("No such file", self.filename)
def pt_getContext(self, instance, request, **kw):
context = super(ViewPageTemplateFile, self).pt_getContext(
instance, request, **kw)
basepath, ext = os.path.splitext(self.filename)
self.__name__ = os.path.basename(basepath)
# get the root
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):
if isinstance(_prefix, str):
path = _prefix
else:
if _prefix is None:
_prefix = sys._getframe(2).f_globals
path = package_home(_prefix)
return path
def __get__(self, instance, type):
return BoundPageTemplate(self, instance)
def pt_getEngine(self):
return getEngine()
def pt_getContext(self):
try:
root = self.getPhysicalRoot()
except AttributeError:
try:
root = self.context.getPhysicalRoot()
except AttributeError:
root = None
# When a view's template is accessed e.g. as template.view, a
# BoundPageTemplate object is retured. For BBB reasons, it needs to
# support the aq_* methods and attributes known from Acquisition. For
# that it also needs to be locatable thru __parent__.
class BoundPageTemplate(viewpagetemplatefile.BoundPageTemplate,
AcquisitionBBB):
__parent__ = property(lambda self: self.im_self)
# 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
# BBB
ZopeTwoPageTemplateFile = ViewPageTemplateFile
......@@ -12,21 +12,23 @@
#
##############################################################################
"""Provider expression.
$Id$
"""
import zope.event
import zope.interface
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):
"""Create a custom provider expression which overrides __call__ to
acquisition wrap the provider so that security lookups can be done."""
from zope.tales import expressions
from zope.contentprovider import interfaces, tales
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):
name = super(Z2ProviderExpression, self).__call__(econtext)
context = econtext.vars['context']
......@@ -35,19 +37,26 @@ class Z2ProviderExpression(StringExpr):
# Try to look up the provider.
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.
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)
# Insert the data gotten from the context
addTALNamespaceData(provider, econtext)
tales.addTALNamespaceData(provider, econtext)
# Stage 1: Do the state update.
zope.event.notify(interfaces.BeforeUpdateEvent(provider, request))
provider.update()
# Stage 2: Render the HTML content.
......
......@@ -18,111 +18,60 @@ $Id$
import os
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.component import getMultiAdapter
from zope.component.interfaces import IResource
from zope.datetime import time as timeFromDateTimeString
from zope.traversing.browser.interfaces import IAbsoluteURL
from zope.traversing.browser import absoluteURL
from zope.publisher.interfaces import NotFound
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
_marker = []
class Resource(Acquisition.Explicit):
"""A publishable resource
"""
implements(IResource)
_marker = object()
class Resource(object):
"""A mixin that changes the URL-rendering of resources (__call__).
def __init__(self, request):
self.request = request
In Zope 3, resource URLs are of the form
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):
name = self.__name__
container = self.__parent__
# TODO Zope 3 uses site = getSite() instead of container here
# and the @@ resource access view
url = str(getMultiAdapter((container, self.request), IAbsoluteURL))
url = urllib.unquote(url)
url = urllib.unquote(absoluteURL(container, self.request))
if not isinstance(container, DirectoryResource):
name = '++resource++%s' % name
return "%s/%s" % (url, name)
class PageTemplateResource(BrowserView, Resource):
#implements(IBrowserPublisher)
class PageTemplateResource(Resource, BrowserView):
implements(IBrowserPublisher)
def __browser_default__(self, request):
return self, ('render',)
def browserDefault(self, request):
return self.render, ()
def publishTraverse(self, request, name):
raise NotFound(self, name, request)
def render(self):
"""Rendered content"""
# ZPublisher might have called setBody with an incorrect URL
# we definitely don't want that if we are plain html
self.request.RESPONSE.setBase(None)
self.request.response.setBase(None)
pt = self.context
return pt(self.request)
class FileResource(BrowserView, Resource):
"""A publishable file-based resource"""
#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 FileResource(Resource, fileresource.FileResource):
pass
class ResourceFactory:
......@@ -173,8 +122,7 @@ class Directory:
self.path = path
self.__name__ = name
class DirectoryResource(BrowserView, Resource, OFSTraversable):
#implements(IBrowserPublisher)
class DirectoryResource(Resource, directoryresource.DirectoryResource):
resource_factories = {
'gif': ImageResourceFactory,
......@@ -187,28 +135,12 @@ class DirectoryResource(BrowserView, Resource, OFSTraversable):
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):
name = self.__name__
if not name.startswith('++resource++'):
name = '++resource++%s' % self.__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):
path = self.context.path
filename = os.path.join(path, name)
......@@ -229,11 +161,7 @@ class DirectoryResource(BrowserView, Resource, OFSTraversable):
resource = factory(name, filename)(self.request)
resource.__name__ = name
resource.__parent__ = self
# XXX __of__ wrapping is usually done on traversal.
# 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)
return resource
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 @@
$Id$
"""
from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class SimpleView(BrowserView):
"""More docstring. Please Zope"""
......@@ -39,6 +40,10 @@ class CallView(BrowserView):
def __call__(self):
return u"I was __call__()'ed"
class CallTemplate(BrowserView):
__call__ = ViewPageTemplateFile('falcon.pt')
class CallableNoDocstring:
def __call__(self):
......@@ -58,7 +63,7 @@ class NoDocstringView(BrowserView):
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
"""
......
......@@ -157,7 +157,7 @@ Make sure that global template variables in ZPT pages are correct:
>>> print view()
View is a view: 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
Here is context: True
Nothing is None: True
......@@ -215,22 +215,27 @@ high-level security tests). Let's manually look up a protected view:
It's protecting the object with the permission, and not the attribute,
so we get ('',) instead of ('eagle',):
>>> getattr(view, '__ac_permissions__')
>>> view.__ac_permissions__
(('View management screens', ('',)),)
Wrap into an acquisition so that imPermissionRole objects can be
evaluated. __roles__ is a imPermissionRole object:
The view's __roles__ attribute can be evaluated correctly:
>>> view = view.__of__(self.folder.testoid)
>>> view_roles = getattr(view, '__roles__', None)
>>> view_roles
(We have to use aq_acquire here instead of a simple getattr. The
reason is that __roles__ actually is an object that expects being
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',)
Check to see if view's context properly acquires its true
parent
>>> from Acquisition import aq_parent, aq_base, aq_inner
>>> context = getattr(view, 'context')
>>> context = view.context
Check the wrapper type
......@@ -238,13 +243,14 @@ Check the wrapper type
>>> type(context) == ImplicitAcquisitionWrapper
True
The acquired parent is the view. This isn't
usually what you want.
The parent of the view is the view's context:
>>> aq_parent(context) == view
>>> 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
<Folder at /test_folder_1_>
......
......@@ -161,6 +161,13 @@
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 -->
<browser:pages
for="Products.Five.tests.testing.simplecontent.ISimpleContent"
......
......@@ -144,6 +144,14 @@ class bearing only a __call__ method:
...
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
--------
......
......@@ -188,18 +188,15 @@ information is used:
</body>
</html>
Now we test a provider using a PageTemplateFile to render itself. It must
inherit from an Acquisition base class so that the template can use Zope 2
security mechanisms:
Now we test a provider using a PageTemplateFile to render itself:
>>> import os, tempfile
>>> temp_dir = tempfile.mkdtemp()
>>> dynTemplate = os.path.join(temp_dir, 'dynamic_template.pt')
>>> open(dynTemplate, 'w').write(
... 'A simple template: <tal:simple replace="python:view.my_property" />')
>>> from Acquisition import Explicit
>>> from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
>>> class TemplateProvider(Explicit):
>>> class TemplateProvider(object):
... zope.component.adapts(zope.interface.Interface,
... browser.IDefaultBrowserLayer,
... zope.interface.Interface)
......
......@@ -33,6 +33,15 @@ Image resource
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
~~~~~~~~~~~~~
......@@ -43,6 +52,15 @@ File resource
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
~~~~~~~~~~~~~~~~~
......@@ -53,6 +71,15 @@ Template resource
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
~~~~~~~~~~~~~~~~~~
......@@ -66,18 +93,6 @@ Page templates aren't guaranteed to render, so exclude them from the test:
... 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:
>>> print http(r'''
......@@ -105,6 +120,13 @@ We also can traverse into sub-directories:
</html>
<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
--------
......
View is a view: <tal:block
content="python:hasattr(view,'context') and hasattr(view, 'request')" />
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_'" />
Container is context: <tal:block content="python:container 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
Root is the application: <tal:block
replace="python:repr(root).find('Application') != -1" />
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
replace="python:traverse_subpath == []" />
Request is a request: <tal:block
......
......@@ -51,12 +51,11 @@ def test_absoluteurl():
This test assures and demonstrates that the absolute url stops
traversing through an object's parents when it has reached the
root object. In Zope 3 this is marked with the IContainmentRoot
interface:
root object.
>>> from zope.interface import directlyProvides, providedBy
>>> from zope.traversing.interfaces import IContainmentRoot
>>> directlyProvides(self.folder, IContainmentRoot)
>>> from zope.interface import alsoProvides, noLongerProvides
>>> from OFS.interfaces import IApplication
>>> alsoProvides(self.folder, IApplication)
>>> for crumb in view.breadcrumbs():
... info = crumb.items()
......@@ -65,8 +64,7 @@ def test_absoluteurl():
[('name', 'test_folder_1_'), ('url', 'http://nohost/test_folder_1_')]
[('name', 'testoid'), ('url', 'http://nohost/test_folder_1_/testoid')]
>>> directlyProvides(self.folder,
... providedBy(self.folder) - IContainmentRoot)
>>> noLongerProvides(self.folder, IApplication)
The absolute url view is obviously not affected by virtual hosting:
......
......@@ -19,47 +19,6 @@ import os, sys
if __name__ == '__main__':
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():
"""
It may be desirable when writing tests for views themselves to
......@@ -118,7 +77,9 @@ def test_suite():
ZopeDocTestSuite(),
ZopeDocFileSuite('pages.txt', package='Products.Five.browser.tests'),
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
......
......@@ -36,7 +36,7 @@ def findSite(obj, iface=ISite):
"""Find a site by walking up the object hierarchy, supporting both
the ``ILocation`` API and Zope 2 Acquisition."""
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
@zope.component.adapter(zope.interface.Interface)
......
......@@ -268,13 +268,6 @@ view class are:
attribute. Typically this comes from a base class, such as
``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::
from Products.Five import BrowserView
......
......@@ -14,31 +14,26 @@
"""Five-compatible version of ObjectWidget
This is needed because ObjectWidget uses ViewPageTemplateFile whose
macro definition is unfortunately incompatible with
ZopeTwoPageTemplateFile. So this subclass uses
ZopeTwoPageTemplateFile for the template that renders the widget's
sub-editform. Acquisition has to be mixed in to provide the
ZopeTwoPageTemplateFile with the proper acquisition context.
macro definition is unfortunately incompatible with ZopeTwoPageTemplateFile.
So this subclass uses ZopeTwoPageTemplateFile for the template that renders
the widget's sub-editform.
$Id$
"""
import Acquisition
import zope.app.form.browser.objectwidget
from AccessControl import ClassSecurityInfo
from Globals import InitializeClass as initializeClass
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class ObjectWidgetView(Acquisition.Explicit,
zope.app.form.browser.objectwidget.ObjectWidgetView):
class ObjectWidgetView(zope.app.form.browser.objectwidget.ObjectWidgetView):
security = ClassSecurityInfo()
security.declareObjectPublic()
template = ZopeTwoPageTemplateFile('objectwidget.pt')
template = ViewPageTemplateFile('objectwidget.pt')
initializeClass(ObjectWidgetView)
class ObjectWidgetClass(Acquisition.Explicit,
zope.app.form.browser.objectwidget.ObjectWidget):
class ObjectWidgetClass(zope.app.form.browser.objectwidget.ObjectWidget):
def __init__(self, context, request, factory, **kw):
super(ObjectWidgetClass, self).__init__(context, request, factory, **kw)
......@@ -58,8 +53,4 @@ class ObjectWidgetClass(Acquisition.Explicit,
val = self.context.schema[name].default
self.getSubWidget(name).setRenderedValue(val)
def ObjectWidget(context, request, factory, **kw):
"""Return an ObjectWidget suitable in the Five environment, with
right acquisition context"""
return ObjectWidgetClass(context, request, factory, **kw
).__of__(context.context)
ObjectWidget = ObjectWidgetClass
......@@ -18,7 +18,6 @@ $Id$
import os.path
from datetime import datetime
import Acquisition
import zope.event
import zope.formlib
......@@ -36,7 +35,7 @@ _PAGEFORM_PATH = os.path.join(_FORMLIB_DIR, 'pageform.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
# using NamedTemplates. NamedTemplates using ViewPageTemplateFile (like
......
......@@ -15,7 +15,7 @@
$Id$
"""
from Acquisition import aq_acquire
from Acquisition import aq_get
from zope.interface import implements
from zope.i18n.interfaces import IFallbackTranslationDomainFactory
from zope.i18n.interfaces import ITranslationDomain
......@@ -23,6 +23,7 @@ from zope.i18n.interfaces import IUserPreferredLanguages
from zope.i18n.negotiator import normalize_lang
from zope.component import queryUtility
from zope.i18nmessageid import Message
from zope.publisher.interfaces.browser import IBrowserRequest
class FiveTranslationService:
......@@ -60,8 +61,11 @@ class FiveTranslationService:
# in Zope3, context is adapted to IUserPreferredLanguages,
# 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:
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,
target_language=target_language, default=default)
......
......@@ -283,17 +283,17 @@ class LocalUtilityServiceTest(ZopeTestCase.ZopeTestCase):
# let's see if we can acquire something all the way from the
# root (Application) object; we need to be careful to choose
# something that's only available from the root object
from Acquisition import aq_acquire
from Acquisition import aq_get
dummy = zapi.getUtility(IDummyUtility)
acquired = aq_acquire(dummy, 'ZopeAttributionButton', None)
acquired = aq_get(dummy, 'ZopeAttributionButton', None)
self.failUnless(acquired is not None)
name, dummy = zapi.getUtilitiesFor(IDummyUtility).next()
acquired = aq_acquire(dummy, 'ZopeAttributionButton', None)
acquired = aq_get(dummy, 'ZopeAttributionButton', None)
self.failUnless(acquired is not None)
dummy = zapi.getAllUtilitiesRegisteredFor(IDummyUtility).next()
acquired = aq_acquire(dummy, 'ZopeAttributionButton', None)
acquired = aq_get(dummy, 'ZopeAttributionButton', None)
self.failUnless(acquired is not None)
def test_getNextUtility(self):
......
......@@ -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 IBrowserView
>>> from Acquisition import Explicit
>>> class WeatherBox(Explicit):
>>> class WeatherBox(object):
... zope.interface.implements(interfaces.IViewlet)
...
... def __init__(self, context, request, view, manager):
......@@ -109,7 +108,7 @@ But now we register some viewlets for the manager
... IBrowserView, ILeftColumn),
... interfaces.IViewlet, name='weather')
>>> class SportBox(Explicit):
>>> class SportBox(object):
... zope.interface.implements(interfaces.IViewlet)
...
... def __init__(self, context, request, view, manager):
......@@ -311,7 +310,7 @@ The same is true if the specified attribute does not exist:
>>> foo.render()
Traceback (most recent call last):
...
AttributeError: bar
AttributeError: 'FooViewlet' object has no attribute 'bar'
To create simple template-based viewlets you can use the
``SimpleViewletClass()`` function. This function is very similar to its view
......@@ -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
absolute URL for it:
>>> class JSResource(Explicit):
>>> class JSResource(object):
... def __init__(self, request):
... self.request = request
...
......@@ -381,7 +380,7 @@ absolute URL for it:
The same works for the CSS resource viewlet:
>>> class CSSResource(Explicit):
>>> class CSSResource(object):
... def __init__(self, request):
... self.request = request
...
......@@ -487,7 +486,7 @@ different item:
>>> shownColumns = []
>>> class ContentsViewletManager(Explicit):
>>> class ContentsViewletManager(object):
... zope.interface.implements(interfaces.IViewletManager)
... index = None
...
......@@ -562,7 +561,7 @@ The Viewlets and the Final Result
Now let's create a first viewlet for the manager...
>>> class NameViewlet(Explicit):
>>> class NameViewlet(object):
...
... def __init__(self, context, request, view, manager):
... self.__parent__ = view
......@@ -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
us:
>>> class SizeViewlet(Explicit):
>>> class SizeViewlet(object):
...
... def __init__(self, context, request, view, manager):
... self.__parent__ = view
......
......@@ -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 ...>
>>> ILeftColumn.providedBy(manager)
True
>>> manager.template.meta_type
'Page Template (File)'
>>> manager.update()
>>> print manager.render().strip()
<div class="column">
......@@ -164,8 +162,6 @@ weight attribute in the viewlet:
<class 'Products.Five.viewlet.manager.ViewletManagerBase'>)
>>> ILeftColumn.providedBy(manager)
True
>>> manager.template.meta_type
'Page Template (File)'
>>> manager.update()
>>> print manager.render().strip()
<div class="column">
......
......@@ -15,7 +15,7 @@
$Id$
"""
import Acquisition
from Acquisition import aq_base
from AccessControl.ZopeGuards import guarded_hasattr
import zope.interface
import zope.security
......@@ -24,9 +24,7 @@ from zope.viewlet.manager import ViewletManagerBase as origManagerBase
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
aq_base = Acquisition.aq_base
class ViewletManagerBase(origManagerBase, Acquisition.Explicit):
class ViewletManagerBase(origManagerBase):
"""A base class for Viewlet managers to work in Zope2"""
def __getitem__(self, name):
......@@ -41,9 +39,6 @@ class ViewletManagerBase(origManagerBase, Acquisition.Explicit):
raise zope.component.interfaces.ComponentLookupError(
'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
# unauthorized error
if not guarded_hasattr(viewlet, 'render'):
......@@ -65,7 +60,6 @@ class ViewletManagerBase(origManagerBase, Acquisition.Explicit):
# the object has a real context from which to determine owner
# security.
for name, viewlet in viewlets:
viewlet = viewlet.__of__(viewlet.context)
if guarded_hasattr(viewlet, 'render'):
results.append((name, viewlet))
return results
......
......@@ -16,19 +16,18 @@
$Id$
"""
import os
from Acquisition import Explicit
from zope.viewlet import viewlet as orig_viewlet
import zope.viewlet.viewlet
from Products.Five.bbb import AcquisitionBBB
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
# We add Acquisition to all the base classes to enable security machinery
class ViewletBase(orig_viewlet.ViewletBase, Explicit):
class ViewletBase(zope.viewlet.viewlet.ViewletBase, AcquisitionBBB):
pass
class SimpleAttributeViewlet(orig_viewlet.SimpleAttributeViewlet, Explicit):
class SimpleAttributeViewlet(zope.viewlet.viewlet.SimpleAttributeViewlet,
AcquisitionBBB):
pass
class simple(orig_viewlet.simple):
class simple(zope.viewlet.viewlet.simple):
# We need to ensure that the proper __init__ is called.
__init__ = ViewletBase.__init__.im_func
......@@ -41,7 +40,7 @@ def SimpleViewletClass(template, bases=(), attributes=None,
# Create the base class hierarchy
bases += (simple, ViewletBase)
attrs = {'index' : ZopeTwoPageTemplateFile(template),
attrs = {'index' : ViewPageTemplateFile(template),
'__name__' : name}
if attributes:
attrs.update(attributes)
......@@ -52,7 +51,7 @@ def SimpleViewletClass(template, bases=(), attributes=None,
return class_
class ResourceViewletBase(orig_viewlet.ResourceViewletBase, Explicit):
class ResourceViewletBase(zope.viewlet.viewlet.ResourceViewletBase):
pass
def JavaScriptViewlet(path):
......@@ -61,13 +60,13 @@ def JavaScriptViewlet(path):
klass = type('JavaScriptViewlet',
(ResourceViewletBase, ViewletBase),
{'index': ZopeTwoPageTemplateFile(src),
{'index': ViewPageTemplateFile(src),
'_path': path})
return klass
class CSSResourceViewletBase(orig_viewlet.CSSResourceViewletBase):
class CSSResourceViewletBase(zope.viewlet.viewlet.CSSResourceViewletBase):
pass
def CSSViewlet(path, media="all", rel="stylesheet"):
......@@ -76,7 +75,7 @@ def CSSViewlet(path, media="all", rel="stylesheet"):
klass = type('CSSViewlet',
(CSSResourceViewletBase, ViewletBase),
{'index': ZopeTwoPageTemplateFile(src),
{'index': ViewPageTemplateFile(src),
'_path': path,
'_media':media,
'_rel':rel})
......
......@@ -17,7 +17,7 @@ from logging import getLogger
import AccessControl
from Globals import package_home, InitializeClass, DevelopmentMode
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 OFS.SimpleItem import SimpleItem
from OFS.Traversable import Traversable
......@@ -87,7 +87,10 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable):
self.filename = filename
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()
c = {'template': self,
'here': context,
......@@ -96,7 +99,7 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable):
'nothing': None,
'options': {},
'root': root,
'request': getattr(root, 'REQUEST', None),
'request': aq_get(root, 'REQUEST', None),
'modules': SecureModuleImporter,
}
return c
......@@ -108,12 +111,11 @@ class PageTemplateFile(SimpleItem, Script, PageTemplate, Traversable):
kw['args'] = args
bound_names['options'] = kw
try:
response = self.REQUEST.RESPONSE
request = aq_get(self, 'REQUEST', None)
if request is not None:
response = request.response
if not response.headers.has_key('content-type'):
response.setHeader('content-type', self.content_type)
except AttributeError:
pass
# Execute the template in a new security context.
security = AccessControl.getSecurityManager()
......
......@@ -17,6 +17,7 @@ $Id$
import re
import os
import Acquisition
from Acquisition import aq_get
from Globals import ImageFile, package_home, InitializeClass
from DateTime.DateTime import DateTime
from Shared.DC.Scripts.Script import Script
......@@ -271,7 +272,10 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
historyComparisonResults=html_diff(rev1._text, rev2._text) )
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()
c = {'template': self,
'here': context,
......@@ -280,7 +284,7 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
'nothing': None,
'options': {},
'root': root,
'request': getattr(root, 'REQUEST', None),
'request': aq_get(root, 'REQUEST', None),
'modules': SecureModuleImporter,
}
return c
......@@ -302,12 +306,11 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
kw['args'] = args
bound_names['options'] = kw
try:
response = self.REQUEST.RESPONSE
request = aq_get(self, 'REQUEST', None)
if request is not None:
response = request.response
if not response.headers.has_key('content-type'):
response.setHeader('content-type', self.content_type)
except AttributeError:
pass
security = getSecurityManager()
bound_names['user'] = security.getUser()
......
......@@ -20,6 +20,7 @@ from AccessControl.Permissions import view_management_screens
from AccessControl.PermissionRole import _what_not_even_god_should_do
from AccessControl.ZopeGuards import guarded_getattr
from Persistence import Persistent
from Acquisition import aq_parent, aq_inner
from string import join, strip
import re
......@@ -179,6 +180,13 @@ class UnauthorizedBinding:
# Make *extra* sure that the wrapper isn't used to access
# __call__, etc.
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()
return guarded_getattr(self._wrapped, name, default)
......@@ -264,11 +272,11 @@ class Bindings:
def _getContext(self):
# Utility for bindcode.
while 1:
self = self.aq_parent
self = aq_parent(self)
if not getattr(self, '_is_wrapperish', None):
parent = getattr(self, 'aq_parent', None)
inner = getattr(self, 'aq_inner', None)
container = getattr(inner, 'aq_parent', None)
parent = aq_parent(self)
inner = aq_inner(self)
container = aq_parent(inner)
try: getSecurityManager().validate(parent, container, '', self)
except Unauthorized:
return UnauthorizedBinding('context', self)
......@@ -277,11 +285,11 @@ class Bindings:
def _getContainer(self):
# Utility for bindcode.
while 1:
self = self.aq_inner.aq_parent
self = aq_parent(aq_inner(self))
if not getattr(self, '_is_wrapperish', None):
parent = getattr(self, 'aq_parent', None)
inner = getattr(self, 'aq_inner', None)
container = getattr(inner, 'aq_parent', None)
parent = aq_parent(self)
inner = aq_inner(self)
container = aq_parent(inner)
try: getSecurityManager().validate(parent, container, '', self)
except Unauthorized:
return UnauthorizedBinding('container', self)
......
......@@ -18,6 +18,7 @@ from urllib import quote as urllib_quote
import xmlrpc
from zExceptions import Forbidden, Unauthorized, NotFound
from Acquisition import aq_base
from Acquisition.interfaces import IAcquirer
from zope.interface import implements, providedBy, Interface
from zope.component import queryMultiAdapter
......@@ -95,7 +96,7 @@ class DefaultPublishTraverse(object):
request.response.setStatus(200)
# We don't need to do the docstring security check
# 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__
raise e
else:
......@@ -105,9 +106,10 @@ class DefaultPublishTraverse(object):
subobject = getattr(object, name)
else:
# 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:
return subobject.__of__(object)
return subobject
# And lastly, of there is no view, try acquired attributes, but
# only if there is no __bobo_traverse__:
......@@ -312,7 +314,9 @@ class BaseRequest:
except TraversalError:
raise KeyError(ob, name)
return ob2.__of__(ob)
if IAcquirer.providedBy(ob2):
ob2 = ob2.__of__(ob)
return ob2
if name == '.':
return ob
......@@ -427,7 +431,7 @@ class BaseRequest:
else:
# If we have reached the end of the path, we look to see
# 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
# (usually self) and a sequence of names to traverse to
# find the method to be published.
......@@ -440,7 +444,8 @@ class BaseRequest:
not hasattr(object,'__bobo_traverse__')):
if object.aq_parent is not object.aq_inner.aq_parent:
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):
adapter = object
......@@ -452,9 +457,8 @@ class BaseRequest:
# of cases so we will just use a default adapter.
adapter = DefaultPublishTraverse(object, self)
newobject, default_path = adapter.browserDefault(self)
if default_path or newobject is not object:
object = newobject
object, default_path = adapter.browserDefault(self)
if default_path:
request._hacked_path=1
if len(default_path) > 1:
path = list(default_path)
......
......@@ -1116,7 +1116,7 @@ class HTTPRequest(BaseRequest):
clone['PARENTS']=[self['PARENTS'][-1]]
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
argument or None if the header is not found. Note that
both original and CGI-ified header names are recognized,
......@@ -1124,7 +1124,8 @@ class HTTPRequest(BaseRequest):
should all return the Content-Type header, if available.
"""
environ=self.environ
name=('_'.join(name.split("-"))).upper()
if not literal:
name = name.replace('-', '_').upper()
val=environ.get(name, None)
if val is not None:
return val
......@@ -1132,6 +1133,8 @@ class HTTPRequest(BaseRequest):
name='HTTP_%s' % name
return environ.get(name, default)
get_header = getHeader # BBB
def get(self, key, default=None, returnTaints=0,
URLmatch=re.compile('URL(PATH)?([0-9]+)$').match,
BASEmatch=re.compile('BASE(PATH)?([0-9]+)$').match,
......
......@@ -12,6 +12,7 @@
##############################################################################
"""Provide an apply-like facility that works with any mapping object
"""
import zope.publisher.publish
def default_call_object(object, args, context):
result=object(*args) # Type s<cr> to step into published object.
......@@ -39,27 +40,15 @@ def mapply(object, positional=(), keyword={},
if hasattr(object,'__bases__'):
f, names, defaults = handle_class(object, context)
else:
f=object
im=0
if hasattr(f, 'im_func'):
im=1
elif not hasattr(f,'func_defaults'):
if hasattr(f, '__call__'):
f=f.__call__
if hasattr(f, 'im_func'):
im=1
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]
try:
f, count = zope.publisher.publish.unwrapMethod(object)
except TypeError:
if maybe:
return object
raise
code = f.func_code
defaults = f.func_defaults
names = code.co_varnames[count:code.co_argcount]
nargs=len(names)
if positional:
......
......@@ -6,6 +6,7 @@ import zope.interface
import zope.component
import zope.testing.cleanup
import zope.traversing.namespace
from zope.publisher.browser import BrowserPage
from zope.publisher.browser import IBrowserRequest
from zope.publisher.browser import IDefaultBrowserLayer
from zope.traversing.interfaces import ITraversable
......@@ -276,9 +277,15 @@ class TestBaseRequestZope3Views(unittest.TestCase):
gsm = zope.component.getGlobalSiteManager()
# Define our 'meth' view
gsm.registerAdapter(DummyView, (IDummy, IDefaultBrowserLayer), None,
'meth')
# Define the views
gsm.registerAdapter(DummyView, (IDummy, IDefaultBrowserLayer),
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)
gsm.registerAdapter(zope.traversing.namespace.view,
......@@ -382,6 +389,27 @@ class TestBaseRequestZope3Views(unittest.TestCase):
r.traverse('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):
......@@ -512,6 +540,34 @@ class DummyView(Implicit):
def __call__(self):
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():
return unittest.TestSuite((
unittest.makeSuite(TestBaseRequest),
......@@ -519,4 +575,4 @@ def test_suite():
))
if __name__ == '__main__':
unitttest.main(defaultTest='test_suite')
unittest.main(defaultTest='test_suite')
import sys
import base64
import unittest
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 = '''
--12345
......@@ -13,13 +22,10 @@ test %s
class AuthCredentialsTests( unittest.TestCase ):
def _getTargetClass(self):
from ZPublisher.HTTPRequest import HTTPRequest
return HTTPRequest
def _makeOne(self, stdin=None, environ=None, response=None, clean=1):
if stdin is None:
from StringIO import StringIO
stdin = StringIO()
if environ is None:
......@@ -40,9 +46,6 @@ class AuthCredentialsTests( unittest.TestCase ):
return self._getTargetClass()(stdin, environ, response, clean)
def test__authUserPW_simple( self ):
import base64
user_id = 'user'
password = 'password'
encoded = base64.encodestring( '%s:%s' % ( user_id, password ) )
......@@ -57,11 +60,7 @@ class AuthCredentialsTests( unittest.TestCase ):
self.assertEqual( password_x, password )
def test__authUserPW_with_embedded_colon( self ):
# http://www.zope.org/Collectors/Zope/2039
import base64
user_id = 'user'
password = 'embedded:colon'
encoded = base64.encodestring( '%s:%s' % ( user_id, password ) )
......@@ -75,43 +74,20 @@ class AuthCredentialsTests( unittest.TestCase ):
self.assertEqual( user_id_x, user_id )
self.assertEqual( password_x, password )
class RecordTests( unittest.TestCase ):
def test_repr( self ):
from ZPublisher.HTTPRequest import record
record = record()
record.a = 1
record.b = 'foo'
r = repr( record )
d = eval( r )
self.assertEqual( d, record.__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 RecordTests(unittest.TestCase):
def test_repr(self):
rec = record()
rec.a = 1
rec.b = 'foo'
r = repr(rec)
d = eval(r)
self.assertEqual(d, rec.__dict__)
class ProcessInputsTests(unittest.TestCase):
def _getHTTPRequest(self, env):
from ZPublisher.HTTPRequest import HTTPRequest
return HTTPRequest(None, env, None)
def _processInputs(self, inputs):
......@@ -141,9 +117,6 @@ class ProcessInputsTests(unittest.TestCase):
# when one is found.
# Also raises an Assertion if a string which *should* have been
# 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
......@@ -221,8 +194,6 @@ class ProcessInputsTests(unittest.TestCase):
self._onlyTaintedformHoldsTaintedStrings(req)
def testSimpleMarshalling(self):
from DateTime import DateTime
inputs = (
('num:int', '42'), ('fract:float', '4.2'), ('bign:long', '45'),
('words:string', 'Some words'), ('2tokens:tokens', 'one two'),
......@@ -476,9 +447,6 @@ class ProcessInputsTests(unittest.TestCase):
self._onlyTaintedformHoldsTaintedStrings(req)
def testSimpleContainersWithTaints(self):
from types import ListType, TupleType
from ZPublisher.HTTPRequest import record
inputs = (
('toneitem:list', '<one>'),
('<tkeyoneitem>:list', 'one'),
......@@ -633,8 +601,6 @@ class ProcessInputsTests(unittest.TestCase):
def testNoTaintedExceptions(self):
# Feed tainted garbage to the conversion methods, and any exception
# returned should be HTML safe
from ZPublisher.Converters import type_converters
from DateTime import DateTime
for type, convert in type_converters.items():
try:
convert('<html garbage>')
......@@ -717,12 +683,10 @@ class RequestTests( unittest.TestCase ):
def testRemoveStdinReferences(self):
# Verifies that all references to the input stream go away on
# request.close(). Otherwise a tempfile may stick around.
import sys
from StringIO import StringIO
s = StringIO(TEST_FILE_DATA)
env = TEST_ENVIRON.copy()
start_count = sys.getrefcount(s)
from ZPublisher.HTTPRequest import HTTPRequest
req = HTTPRequest(s, env, None)
req.processInputs()
self.assertNotEqual(start_count, sys.getrefcount(s)) # Precondition
......@@ -731,10 +695,9 @@ class RequestTests( unittest.TestCase ):
def testFileName(self):
# checks fileupload object supports the filename
from StringIO import StringIO
s = StringIO(TEST_LARGEFILE_DATA)
env = TEST_ENVIRON.copy()
from ZPublisher.HTTPRequest import HTTPRequest
req = HTTPRequest(s, env, None)
req.processInputs()
f = req.form.get('file')
......@@ -743,11 +706,9 @@ class RequestTests( unittest.TestCase ):
def testFileIterator(self):
# checks fileupload object supports the iterator protocol
# collector entry 1837
import sys
from StringIO import StringIO
s = StringIO(TEST_FILE_DATA)
env = TEST_ENVIRON.copy()
from ZPublisher.HTTPRequest import HTTPRequest
req = HTTPRequest(s, env, None)
req.processInputs()
f=req.form.get('file')
......@@ -763,8 +724,6 @@ class RequestTests( unittest.TestCase ):
'SERVER_NAME': 'localhost',
'SERVER_PORT': '80',
}
from StringIO import StringIO
from ZPublisher.HTTPRequest import HTTPRequest
from zope.publisher.base import DebugFlags
s = StringIO('')
......@@ -881,8 +840,6 @@ class RequestTests( unittest.TestCase ):
'SERVER_NAME': 'localhost',
'SERVER_PORT': '80',
}
from StringIO import StringIO
from ZPublisher.HTTPRequest import HTTPRequest
s = StringIO('')
env = TEST_ENVIRON.copy()
......@@ -902,8 +859,6 @@ class RequestTests( unittest.TestCase ):
'REMOTE_ADDR': '127.0.0.1',
'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('')
env = TEST_ENVIRON.copy()
......@@ -925,6 +880,29 @@ class RequestTests( unittest.TestCase ):
request = HTTPRequest(s, env, None)
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():
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
from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager
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 time import asctime
from types import StringType, ListType
......@@ -130,7 +133,7 @@ def validated_hook(request, user):
newSecurityManager(request, user)
version = request.get(Globals.VersionNameName, '')
if version:
object = user.aq_parent
object = aq_parent(user)
if not getSecurityManager().checkPermission(
'Join/leave Versions', object):
request['RESPONSE'].setCookie(
......@@ -231,7 +234,7 @@ class ZPublisherExceptionHook:
while 1:
f = getattr(published, self.raise_error_message, None)
if f is None:
published = getattr(published, 'aq_parent', None)
published = aq_parent(published)
if published is None:
raise t, v, traceback
else:
......@@ -241,8 +244,10 @@ class ZPublisherExceptionHook:
while 1:
if getattr(client, self.error_message, None) is not None:
break
client = getattr(client, 'aq_parent', None)
if client is None:
client = aq_parent(client)
# 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
if REQUEST.get('AUTHENTICATED_USER', None) is None:
......@@ -296,8 +301,7 @@ class TransactionsManager:
object = None
break
to_append = (object.__name__,) + to_append
object = getattr(object, 'aq_inner', object)
object = getattr(object, 'aq_parent', None)
object = aq_parent(aq_inner(object))
if object is not None:
path = '/'.join(object.getPhysicalPath() + to_append)
......@@ -312,11 +316,8 @@ class TransactionsManager:
T.note(path)
auth_user=request_get('AUTHENTICATED_USER',None)
if auth_user is not None:
try:
auth_folder = auth_user.aq_parent
except AttributeError:
# Most likely some product forgot to call __of__()
# on the user object.
auth_folder = aq_parent(auth_user)
if auth_folder is None:
ac_logger.warning(
'A user object of type %s has no aq_parent.',
type(auth_user)
......@@ -326,6 +327,3 @@ class TransactionsManager:
auth_path = '/'.join(auth_folder.getPhysicalPath()[1:-1])
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