[Checkins] SVN: Zope/trunk/ Merged philikon-aq branch into trunk, yeah!

Hanno Schlichting plone at hannosch.info
Sat Apr 26 13:46:47 EDT 2008


Log message for revision 85767:
  Merged philikon-aq branch into trunk, yeah!
  

Changed:
  U   Zope/trunk/doc/CHANGES.txt
  U   Zope/trunk/lib/python/AccessControl/ImplPython.py
  U   Zope/trunk/lib/python/AccessControl/Owned.py
  U   Zope/trunk/lib/python/AccessControl/Permission.py
  U   Zope/trunk/lib/python/AccessControl/PermissionMapping.py
  U   Zope/trunk/lib/python/AccessControl/Role.py
  U   Zope/trunk/lib/python/AccessControl/User.py
  U   Zope/trunk/lib/python/AccessControl/cAccessControl.c
  U   Zope/trunk/lib/python/Acquisition/_Acquisition.c
  U   Zope/trunk/lib/python/Acquisition/tests.py
  U   Zope/trunk/lib/python/App/FactoryDispatcher.py
  U   Zope/trunk/lib/python/OFS/FindSupport.py
  U   Zope/trunk/lib/python/OFS/PropertySheets.py
  U   Zope/trunk/lib/python/OFS/SimpleItem.py
  U   Zope/trunk/lib/python/OFS/Traversable.py
  U   Zope/trunk/lib/python/OFS/ZDOM.py
  U   Zope/trunk/lib/python/Products/Five/bbb.py
  U   Zope/trunk/lib/python/Products/Five/browser/__init__.py
  U   Zope/trunk/lib/python/Products/Five/browser/absoluteurl.py
  U   Zope/trunk/lib/python/Products/Five/browser/adding.py
  U   Zope/trunk/lib/python/Products/Five/browser/configure.zcml
  U   Zope/trunk/lib/python/Products/Five/browser/metaconfigure.py
  U   Zope/trunk/lib/python/Products/Five/browser/pagetemplatefile.py
  U   Zope/trunk/lib/python/Products/Five/browser/providerexpression.py
  U   Zope/trunk/lib/python/Products/Five/browser/resource.py
  A   Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy.py
  A   Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy.zcml
  A   Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy_ftest.txt
  A   Zope/trunk/lib/python/Products/Five/browser/tests/legacymanager.pt
  A   Zope/trunk/lib/python/Products/Five/browser/tests/legacyprovider.pt
  U   Zope/trunk/lib/python/Products/Five/browser/tests/pages.py
  U   Zope/trunk/lib/python/Products/Five/browser/tests/pages.txt
  U   Zope/trunk/lib/python/Products/Five/browser/tests/pages.zcml
  U   Zope/trunk/lib/python/Products/Five/browser/tests/pages_ftest.txt
  U   Zope/trunk/lib/python/Products/Five/browser/tests/provider.txt
  U   Zope/trunk/lib/python/Products/Five/browser/tests/resource_ftest.txt
  U   Zope/trunk/lib/python/Products/Five/browser/tests/template_variables.pt
  U   Zope/trunk/lib/python/Products/Five/browser/tests/test_absoluteurl.py
  U   Zope/trunk/lib/python/Products/Five/browser/tests/test_pages.py
  U   Zope/trunk/lib/python/Products/Five/component/__init__.py
  U   Zope/trunk/lib/python/Products/Five/doc/manual.txt
  U   Zope/trunk/lib/python/Products/Five/form/objectwidget.py
  U   Zope/trunk/lib/python/Products/Five/formlib/formbase.py
  U   Zope/trunk/lib/python/Products/Five/i18n.py
  U   Zope/trunk/lib/python/Products/Five/site/tests/test_utility.py
  U   Zope/trunk/lib/python/Products/Five/viewlet/README.txt
  U   Zope/trunk/lib/python/Products/Five/viewlet/directives.txt
  U   Zope/trunk/lib/python/Products/Five/viewlet/manager.py
  U   Zope/trunk/lib/python/Products/Five/viewlet/viewlet.py
  U   Zope/trunk/lib/python/Products/PageTemplates/PageTemplateFile.py
  U   Zope/trunk/lib/python/Products/PageTemplates/ZopePageTemplate.py
  U   Zope/trunk/lib/python/Shared/DC/Scripts/Bindings.py
  U   Zope/trunk/lib/python/ZPublisher/BaseRequest.py
  U   Zope/trunk/lib/python/ZPublisher/HTTPRequest.py
  U   Zope/trunk/lib/python/ZPublisher/mapply.py
  U   Zope/trunk/lib/python/ZPublisher/tests/testBaseRequest.py
  U   Zope/trunk/lib/python/ZPublisher/tests/testHTTPRequest.py
  A   Zope/trunk/lib/python/ZPublisher/tests/test_mapply.py
  U   Zope/trunk/lib/python/Zope2/App/startup.py

-=-
Modified: Zope/trunk/doc/CHANGES.txt
===================================================================
--- Zope/trunk/doc/CHANGES.txt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/doc/CHANGES.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -78,7 +78,11 @@
 
     Features added
 
-      - Zope2 startup: Zope will now send DatabaseOpened and    
+      - Acquisition has been made aware of __parent__ pointers. This allows
+        direct access to many Zope 3 classes without the need to mixin
+        Acquisition base classes for the security to work.
+
+      - Zope2 startup: Zope will now send DatabaseOpened and
         ProcessStarting events.
 
       - Testing.ZopeTestCase: Introduced a "ZopeLite" test layer, making it

Modified: Zope/trunk/lib/python/AccessControl/ImplPython.py
===================================================================
--- Zope/trunk/lib/python/AccessControl/ImplPython.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/AccessControl/ImplPython.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -17,10 +17,8 @@
 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 @@
                 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 @@
                         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 @@
             # 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 hasattr(object, 'im_self'):
+            # This is a method.  Grab its self.
+            object=object.im_self
+        if not aq_inContextOf(object, ucontext, 1):
             if 'Shared' in object_roles:
                 # Old role setting. Waaa
                 object_roles=user._shared_roles(object)

Modified: Zope/trunk/lib/python/AccessControl/Owned.py
===================================================================
--- Zope/trunk/lib/python/AccessControl/Owned.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/AccessControl/Owned.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -21,7 +21,7 @@
 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 @@
     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 @@
         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):

Modified: Zope/trunk/lib/python/AccessControl/Permission.py
===================================================================
--- Zope/trunk/lib/python/AccessControl/Permission.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/AccessControl/Permission.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -17,6 +17,7 @@
 
 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 @@
         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):

Modified: Zope/trunk/lib/python/AccessControl/PermissionMapping.py
===================================================================
--- Zope/trunk/lib/python/AccessControl/PermissionMapping.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/AccessControl/PermissionMapping.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -105,7 +105,7 @@
         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

Modified: Zope/trunk/lib/python/AccessControl/Role.py
===================================================================
--- Zope/trunk/lib/python/AccessControl/Role.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/AccessControl/Role.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -188,7 +188,7 @@
             if userObj:
                 break
             else:
-                current = current.aq_parent
+                current = current.__parent__
 
 
         newSecurityManager(None, userObj) # necessary?
@@ -414,7 +414,7 @@
                         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 @@
                 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()

Modified: Zope/trunk/lib/python/AccessControl/User.py
===================================================================
--- Zope/trunk/lib/python/AccessControl/User.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/AccessControl/User.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -20,6 +20,9 @@
 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 @@
                 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 @@
                 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 @@
         # 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)
+            if hasattr(object, 'im_self'):
+                # This is a method.  Grab its self.
+                object=object.im_self
+            return aq_inContextOf(object, context, 1)
 
         # This is lame, but required to keep existing behavior.
         return 1
@@ -230,7 +229,7 @@
                             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 @@
             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 @@
             # 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 @@
 
     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 @@
 
     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
 

Modified: Zope/trunk/lib/python/AccessControl/cAccessControl.c
===================================================================
--- Zope/trunk/lib/python/AccessControl/cAccessControl.c	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/AccessControl/cAccessControl.c	2008-04-26 17:46:46 UTC (rev 85767)
@@ -1878,13 +1878,11 @@
 
       
       /*
-        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 @@
         if (object == Py_None) 
           break;
 
-        if (! aq_isWrapper(object)) 
-          break;
         tobj = aq_parent(object);
         if (tobj == NULL) 
           goto end;

Modified: Zope/trunk/lib/python/Acquisition/_Acquisition.c
===================================================================
--- Zope/trunk/lib/python/Acquisition/_Acquisition.c	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Acquisition/_Acquisition.c	2008-04-26 17:46:46 UTC (rev 85767)
@@ -38,7 +38,8 @@
   *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_NAME(__repr__);
   INIT_PY_NAME(__str__);
   INIT_PY_NAME(__cmp__);
-  
+  INIT_PY_NAME(__parent__);
 #undef INIT_PY_NAME
 }
 
@@ -414,23 +415,49 @@
 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 (filter)
-	  switch(apply_filter(filter,OBJECT(self),oname,r,extra,orig))
-	    {
-	    case -1: return NULL;
-	    case 1: return r;
-	    }
-	else return r;
-      }
-    else PyErr_Clear();
+  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))
+              {
+              case -1: return NULL;
+              case 1: return r;
+              }
+          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 @@
 	  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 @@
       PyErr_Clear();
     }
 
+  /* Lookup has failed, acquire it from parent. */
   if (sco && (*name != '_' || explicit)) 
     return Wrapper_acquire(self, oname, filter, extra, orig, explicit, 
 			   containment);
@@ -524,24 +553,35 @@
 		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;
 	      else if (WRAPPER(self->obj)->container==
 		      WRAPPER(self->container)->obj)  
 		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 @@
 	  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 @@
 
   /* 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 @@
 # 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;
+  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 @@
 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;
 
@@ -1343,22 +1381,46 @@
 	      WRAPPER(self), name, filter, extra, OBJECT(self),1,
 	      explicit || 
 	      WRAPPER(self)->ob_type==(PyTypeObject*)&Wrappertype,
-	      explicit, containment);
-  
-  /* Not wrapped and no filter, so just getattr */
-  if (! filter) return PyObject_GetAttr(self, name);
+	      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);
 
-  /* Crap, we've got to construct a wrapper so we can use Wrapper_findattr */
-  UNLESS (self=newWrapper(self, Py_None, (PyTypeObject*)&Wrappertype)) 
-    return NULL;
+      if (! filter) return PyObject_GetAttr(self, name);
+
+      /* 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);
+      result=Wrapper_findattr(WRAPPER(self), name, filter, extra, OBJECT(self),
+                              1, 1, explicit, containment);
 
-  /* get rid of temp wrapper */
-  Py_DECREF(self);
-
-  return result;
+      /* Get rid of temporary wrapper */
+      Py_DECREF(self);
+      return result;
+    }
 }
 
 static PyObject *
@@ -1384,13 +1446,35 @@
 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);
+                            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
-    result=PyObject_GetAttr(self, name);
+    {
+      /* Clean up the AttributeError from the previous getattr
+         (because it has clearly failed). */
+      PyErr_Fetch(&result,&v,&tb);
+      if (result && (result != PyExc_AttributeError))
+        {
+          PyErr_Restore(result,v,tb);
+          return NULL;
+        }
+      Py_XDECREF(result); Py_XDECREF(v); Py_XDECREF(tb);
+ 
+      result=PyObject_GetAttr(self, name);
+    }
 
   if (! result && defalt)
     {
@@ -1453,13 +1537,34 @@
 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;
+    {
+      result=WRAPPER(self)->container;
+      Py_INCREF(result);
+      return result;
+    }
+  else if ((result=PyObject_GetAttr(self, py__parent__)))
+    /* We already own the reference to result (PyObject_GetAttr gives
+       it to us), no need to INCREF here */
+    return result;
+  else
+    {
+      /* We need to clean up the AttributeError from the previous
+         getattr (because it has clearly failed) */
+      PyErr_Fetch(&result,&v,&tb);
+      if (result && (result != PyExc_AttributeError))
+        {
+          PyErr_Restore(result,v,tb);
+          return NULL;
+        }
+      Py_XDECREF(result); Py_XDECREF(v); Py_XDECREF(tb);
 
-  Py_INCREF(result);
-  return result;
+      result=Py_None;
+      Py_INCREF(result);
+      return result;
+    }
 }
 
 static PyObject *
@@ -1535,7 +1640,7 @@
 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 @@
 	    }
 	}
       else
-	if (PyList_Append(result, self) < 0)
-	  goto err;
+        {
+          if (PyList_Append(result, self) < 0)
+            goto err;
 
+          if ((self=PyObject_GetAttr(self, py__parent__)))
+            {
+              Py_DECREF(self); /* We don't need our own reference. */
+              if (self!=Py_None)
+                continue;
+            }
+          else
+            {
+              PyErr_Fetch(&self,&v,&tb);
+              if (self && (self != PyExc_AttributeError))
+                {
+                  PyErr_Restore(self,v,tb);
+                  return NULL;
+                }
+              Py_XDECREF(self); Py_XDECREF(v); Py_XDECREF(tb);
+            }
+        }
+
       break;
     }
   
@@ -1582,6 +1706,53 @@
   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 @@
    "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}
 };
 

Modified: Zope/trunk/lib/python/Acquisition/tests.py
===================================================================
--- Zope/trunk/lib/python/Acquisition/tests.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Acquisition/tests.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -357,6 +357,11 @@
     ...
     AttributeError: aq_parent
 
+    >>> c.__parent__
+    Traceback (most recent call last):
+    ...
+    AttributeError: __parent__
+
     >>> Acquisition.aq_acquire(c, 'id')
     'unwrapped'
     >>> Acquisition.aq_acquire(c, 'x')
@@ -452,7 +457,14 @@
     >>> 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 @@
     >>> 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 old_tests():
+def test_aq_inContextOf():
     """    
     >>> from ExtensionClass import Base
     >>> import Acquisition
@@ -1213,6 +1232,9 @@
     ...     def hi(self):
     ...         print "%s()" % self.__class__.__name__, self.color
 
+    >>> class Location(object):
+    ...     __parent__ = None
+
     >>> b=B()
     >>> b.a=A()
     >>> b.a.hi()
@@ -1242,25 +1264,52 @@
     >>> 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)
     1
     >>> 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 @@
     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 @@
     ...
     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 @@
     >>> 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 @@
     """
 
 
+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
 

Modified: Zope/trunk/lib/python/App/FactoryDispatcher.py
===================================================================
--- Zope/trunk/lib/python/App/FactoryDispatcher.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/App/FactoryDispatcher.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -66,7 +66,7 @@
     _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 @@
             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
 

Modified: Zope/trunk/lib/python/OFS/FindSupport.py
===================================================================
--- Zope/trunk/lib/python/OFS/FindSupport.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/OFS/FindSupport.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -22,6 +22,7 @@
 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 @@
                 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 @@
             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 @@
                 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 @@
             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

Modified: Zope/trunk/lib/python/OFS/PropertySheets.py
===================================================================
--- Zope/trunk/lib/python/OFS/PropertySheets.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/OFS/PropertySheets.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -20,13 +20,14 @@
 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 @@
             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 @@
             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 @@
         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 @@
         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 @@
 
     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 @@
             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
 

Modified: Zope/trunk/lib/python/OFS/SimpleItem.py
===================================================================
--- Zope/trunk/lib/python/OFS/SimpleItem.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/OFS/SimpleItem.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -205,14 +205,16 @@
                 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 @@
                 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()

Modified: Zope/trunk/lib/python/OFS/Traversable.py
===================================================================
--- Zope/trunk/lib/python/OFS/Traversable.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/OFS/Traversable.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -22,7 +22,8 @@
 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 @@
         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 @@
         """
         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 @@
         """
         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 @@
                         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 @@
 
                 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:

Modified: Zope/trunk/lib/python/OFS/ZDOM.py
===================================================================
--- Zope/trunk/lib/python/OFS/ZDOM.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/OFS/ZDOM.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -16,6 +16,8 @@
 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 @@
         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 @@
         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 @@
     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 @@
         """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 @@
         """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 @@
 
     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 ''
 

Modified: Zope/trunk/lib/python/Products/Five/bbb.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/bbb.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/bbb.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -19,10 +19,32 @@
 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"""
 

Modified: Zope/trunk/lib/python/Products/Five/browser/__init__.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/__init__.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/__init__.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -18,8 +18,24 @@
 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)

Modified: Zope/trunk/lib/python/Products/Five/browser/absoluteurl.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/absoluteurl.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/absoluteurl.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -15,26 +15,89 @@
 
 $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)
         return context.absolute_url()
 
@@ -47,10 +110,10 @@
 
         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 @@
         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

Modified: Zope/trunk/lib/python/Products/Five/browser/adding.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/adding.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/adding.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -23,6 +23,8 @@
 
 __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 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 @@
                 # 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 @@
         # 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 @@
 
         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 @@
         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 @@
         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")
 

Modified: Zope/trunk/lib/python/Products/Five/browser/configure.zcml
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/configure.zcml	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/configure.zcml	2008-04-26 17:46:46 UTC (rev 85767)
@@ -39,21 +39,37 @@
       />
 
   <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"
+      />
+
   <browser:view
       for="OFS.interfaces.IObjectManager"
       name="+"

Modified: Zope/trunk/lib/python/Products/Five/browser/metaconfigure.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/metaconfigure.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/metaconfigure.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -29,17 +29,17 @@
 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 @@
         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 @@
 
 # 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 @@
 
         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 @@
                 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 @@
                 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 @@
             args = (new_class,)
             )
 
-#
-# mixin classes / class factories
-#
+class ViewMixinForAttributes(BrowserView,
+                             zope.app.publisher.browser.viewmeta.simple):
 
-class ViewMixinForAttributes(BrowserView):
+    # 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__), ()
 
-    # we have an attribute that we can simply tell ZPublisher to go to
-    def __browser_default__(self, request):
-        return self, (self.__page_attribute__,)
+    # __call__ should have the same signature as the original method
+    @property
+    def __call__(self):
+        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,
+                            zope.app.pagetemplate.simpleviewclass.simple):
+    pass
 
-class ViewMixinForTemplates(BrowserView):
-
-    # short cut to get to macros more easily
-    def __getitem__(self, name):
-        if name == 'macros':
-            return self.index.macros
-        return self.index.macros[name]
-
-    # make the template publishable
-    def __call__(self, *args, **kw):
-        return self.index(self, *args, **kw)
-
 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)

Modified: Zope/trunk/lib/python/Products/Five/browser/pagetemplatefile.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/pagetemplatefile.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/pagetemplatefile.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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)
 
-        self.ZBindings_edit(self._default_bindings)
+    id = property(getId)
 
-        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 __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
 
-        basepath, ext = os.path.splitext(self.filename)
-        self.__name__ = os.path.basename(basepath)
+    def pt_getEngine(self):
+        return getEngine()
 
-        super(PageTemplateFile, self).__init__(self.filename, _prefix)
+    def pt_getContext(self, instance, request, **kw):
+        context = super(ViewPageTemplateFile, self).pt_getContext(
+            instance, request, **kw)
 
-    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
+        # get the root
+        obj = context['context']
+        root = None
+        meth = aq_get(obj, 'getPhysicalRoot', None)
+        if meth is not None:
+            root = meth()
 
-    def pt_getEngine(self):
-        return getEngine()
+        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 pt_getContext(self):
-        try:
-            root = self.getPhysicalRoot()
-        except AttributeError:
-            try:
-                root = self.context.getPhysicalRoot()
-            except AttributeError:
-                root = None
+    def __get__(self, instance, type):
+        return BoundPageTemplate(self, instance)
 
-        # 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)
+# 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__.
 
-        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)
+class BoundPageTemplate(viewpagetemplatefile.BoundPageTemplate,
+                        AcquisitionBBB):
 
-        return c
+    __parent__ = property(lambda self: self.im_self)
 
-ViewPageTemplateFile = ZopeTwoPageTemplateFile
+
+# BBB
+ZopeTwoPageTemplateFile = ViewPageTemplateFile

Modified: Zope/trunk/lib/python/Products/Five/browser/providerexpression.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/providerexpression.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/providerexpression.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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
 
-    implements(cp_interfaces.ITALESProviderExpression)
+from Acquisition.interfaces import IAcquirer
 
+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 @@
 
         # 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)
 
-        if getattr(provider, '__of__', None) is not None:
+        # add the __name__ attribute if it implements ILocation
+        if ILocation.providedBy(provider):
+            provider.__name__ = name
+
+        # 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.

Modified: Zope/trunk/lib/python/Products/Five/browser/resource.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/resource.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/resource.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -18,112 +18,61 @@
 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()
 
-    def __init__(self, request):
-        self.request = request
+class Resource(object):
+    """A mixin that changes the URL-rendering of resources (__call__).
 
+    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)
+class FileResource(Resource, fileresource.FileResource):
+    pass
 
-    def __browser_default__(self, request):
-        return self, (request.REQUEST_METHOD,)
-
-    def GET(self):
-        """Default content"""
-        file = self.context
-        request = self.request
-        response = request.response
-
-        # HTTP If-Modified-Since header handling. This is duplicated
-        # from OFS.Image.Image - it really should be consolidated
-        # somewhere...
-        header = request.environ.get('If-Modified-Since', None)
-        if header is not None:
-            header = header.split(';')[0]
-            # Some proxies seem to send invalid date strings for this
-            # header. If the date string is not valid, we ignore it
-            # rather than raise an error to be generally consistent
-            # with common servers such as Apache (which can usually
-            # understand the screwy date string as a lucky side effect
-            # of the way they parse it).
-            try:    mod_since=long(timeFromDateTimeString(header))
-            except: mod_since=None
-            if mod_since is not None:
-                if getattr(file, 'lmt', None):
-                    last_mod = long(file.lmt)
-                else:
-                    last_mod = long(0)
-                if last_mod > 0 and last_mod <= mod_since:
-                    response.setStatus(304)
-                    return ''
-
-        response.setHeader('Content-Type', file.content_type)
-        response.setHeader('Last-Modified', file.lmh)
-
-        # Cache for one day
-        response.setHeader('Cache-Control', 'public,max-age=86400')
-        f = open(file.path, 'rb')
-        data = f.read()
-        f.close()
-
-        return data
-
-    def HEAD(self):
-        file = self.context
-        response = self.request.response
-        response = self.request.response
-        response.setHeader('Content-Type', file.content_type)
-        response.setHeader('Last-Modified', file.lmh)
-        # Cache for one day
-        response.setHeader('Cache-Control', 'public,max-age=86400')
-        return ''
-
 class ResourceFactory:
 
     factory = None
@@ -173,8 +122,7 @@
         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 @@
 
     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 @@
         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):
 

Copied: Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy.py (from rev 85463, Zope/branches/philikon-aq/lib/python/Products/Five/browser/tests/aqlegacy.py)
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy.py	                        (rev 0)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -0,0 +1,147 @@
+##############################################################################
+#
+# 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

Copied: Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy.zcml (from rev 85463, Zope/branches/philikon-aq/lib/python/Products/Five/browser/tests/aqlegacy.zcml)
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy.zcml	                        (rev 0)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy.zcml	2008-04-26 17:46:46 UTC (rev 85767)
@@ -0,0 +1,142 @@
+<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

Copied: Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy_ftest.txt (from rev 85463, Zope/branches/philikon-aq/lib/python/Products/Five/browser/tests/aqlegacy_ftest.txt)
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy_ftest.txt	                        (rev 0)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/aqlegacy_ftest.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -0,0 +1,204 @@
+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()

Copied: Zope/trunk/lib/python/Products/Five/browser/tests/legacymanager.pt (from rev 85463, Zope/branches/philikon-aq/lib/python/Products/Five/browser/tests/legacymanager.pt)
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/legacymanager.pt	                        (rev 0)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/legacymanager.pt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -0,0 +1 @@
+<p tal:content="provider:aqlegacymanager" />

Copied: Zope/trunk/lib/python/Products/Five/browser/tests/legacyprovider.pt (from rev 85463, Zope/branches/philikon-aq/lib/python/Products/Five/browser/tests/legacyprovider.pt)
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/legacyprovider.pt	                        (rev 0)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/legacyprovider.pt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -0,0 +1 @@
+<p tal:content="provider:aqlegacyprovider" />

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/pages.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/pages.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/pages.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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 @@
     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 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
     """
 

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/pages.txt
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/pages.txt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/pages.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -157,7 +157,7 @@
   >>> 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,37 +215,43 @@
 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
 
   >>> from Acquisition import ImplicitAcquisitionWrapper
   >>> type(context) == ImplicitAcquisitionWrapper
   True
- 
-The acquired parent is the view. This isn't 
-usually what you want.
 
-  >>> aq_parent(context) == view
-  True 
+The parent of the view is the view's context:
 
-To get what you usually want, do this
+  >>> view.__parent__ == view.context
+  True
+  >>> aq_parent(view) == view.context
+  True
 
+The direct parent of the context is 
+
   >>> context.aq_inner.aq_parent
   <Folder at /test_folder_1_> 
 

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/pages.zcml
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/pages.zcml	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/pages.zcml	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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"

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/pages_ftest.txt
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/pages_ftest.txt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/pages_ftest.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -144,7 +144,15 @@
   ...
   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
 --------
 

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/provider.txt
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/provider.txt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/provider.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -188,18 +188,15 @@
     </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)

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/resource_ftest.txt
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/resource_ftest.txt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/resource_ftest.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -33,6 +33,15 @@
   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 @@
   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 @@
   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
 ~~~~~~~~~~~~~~~~~~
 
@@ -77,19 +104,7 @@
   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'''
-  ... 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>
-
-  >>> print http(r'''
   ... GET /test_folder_1_/testoid/++resource++fivetest_resources/resource_subdir/resource.html HTTP/1.1
   ... Authorization: Basic manager:r00t
   ... ''')
@@ -105,6 +120,13 @@
   </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
 --------
 

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/template_variables.pt
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/template_variables.pt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/template_variables.pt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -1,7 +1,7 @@
 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 @@
 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 

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/test_absoluteurl.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/test_absoluteurl.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/test_absoluteurl.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -51,12 +51,11 @@
 
     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 @@
       [('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:
 

Modified: Zope/trunk/lib/python/Products/Five/browser/tests/test_pages.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/browser/tests/test_pages.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/browser/tests/test_pages.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -19,47 +19,6 @@
 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 @@
         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
 

Modified: Zope/trunk/lib/python/Products/Five/component/__init__.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/component/__init__.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/component/__init__.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -36,7 +36,7 @@
     """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)

Modified: Zope/trunk/lib/python/Products/Five/doc/manual.txt
===================================================================
--- Zope/trunk/lib/python/Products/Five/doc/manual.txt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/doc/manual.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -268,13 +268,6 @@
   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

Modified: Zope/trunk/lib/python/Products/Five/form/objectwidget.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/form/objectwidget.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/form/objectwidget.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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 @@
                 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

Modified: Zope/trunk/lib/python/Products/Five/formlib/formbase.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/formlib/formbase.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/formlib/formbase.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -18,7 +18,6 @@
 import os.path
 
 from datetime import datetime
-import Acquisition
 
 import zope.event
 import zope.formlib
@@ -36,7 +35,7 @@
 _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

Modified: Zope/trunk/lib/python/Products/Five/i18n.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/i18n.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/i18n.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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.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 @@
 
         # 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)
 

Modified: Zope/trunk/lib/python/Products/Five/site/tests/test_utility.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/site/tests/test_utility.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/site/tests/test_utility.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -283,17 +283,17 @@
         # 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):

Modified: Zope/trunk/lib/python/Products/Five/viewlet/README.txt
===================================================================
--- Zope/trunk/lib/python/Products/Five/viewlet/README.txt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/viewlet/README.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -89,8 +89,7 @@
   >>> 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 @@
   ...     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 @@
   >>> 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 @@
 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 @@
 
 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 @@
 
   >>> shownColumns = []
 
-  >>> class ContentsViewletManager(Explicit):
+  >>> class ContentsViewletManager(object):
   ...     zope.interface.implements(interfaces.IViewletManager)
   ...     index = None
   ...
@@ -562,7 +561,7 @@
 
 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 @@
 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

Modified: Zope/trunk/lib/python/Products/Five/viewlet/directives.txt
===================================================================
--- Zope/trunk/lib/python/Products/Five/viewlet/directives.txt	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/viewlet/directives.txt	2008-04-26 17:46:46 UTC (rev 85767)
@@ -130,8 +130,6 @@
   <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 @@
    <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">

Modified: Zope/trunk/lib/python/Products/Five/viewlet/manager.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/viewlet/manager.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/viewlet/manager.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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 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 @@
             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 @@
         # 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

Modified: Zope/trunk/lib/python/Products/Five/viewlet/viewlet.py
===================================================================
--- Zope/trunk/lib/python/Products/Five/viewlet/viewlet.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/Five/viewlet/viewlet.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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 @@
     # 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 @@
     return class_
 
 
-class ResourceViewletBase(orig_viewlet.ResourceViewletBase, Explicit):
+class ResourceViewletBase(zope.viewlet.viewlet.ResourceViewletBase):
     pass
 
 def JavaScriptViewlet(path):
@@ -61,13 +60,13 @@
 
     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 @@
 
     klass = type('CSSViewlet',
                  (CSSResourceViewletBase, ViewletBase),
-                  {'index': ZopeTwoPageTemplateFile(src),
+                  {'index': ViewPageTemplateFile(src),
                    '_path': path,
                    '_media':media,
                    '_rel':rel})

Modified: Zope/trunk/lib/python/Products/PageTemplates/PageTemplateFile.py
===================================================================
--- Zope/trunk/lib/python/Products/PageTemplates/PageTemplateFile.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/PageTemplates/PageTemplateFile.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -17,7 +17,7 @@
 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 @@
         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 @@
              'nothing': None,
              'options': {},
              'root': root,
-             'request': getattr(root, 'REQUEST', None),
+             'request': aq_get(root, 'REQUEST', None),
              'modules': SecureModuleImporter,
              }
         return c
@@ -108,12 +111,11 @@
             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()

Modified: Zope/trunk/lib/python/Products/PageTemplates/ZopePageTemplate.py
===================================================================
--- Zope/trunk/lib/python/Products/PageTemplates/ZopePageTemplate.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Products/PageTemplates/ZopePageTemplate.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -17,6 +17,7 @@
 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 @@
             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 @@
              'nothing': None,
              'options': {},
              'root': root,
-             'request': getattr(root, 'REQUEST', None),
+             'request': aq_get(root, 'REQUEST', None),
              'modules': SecureModuleImporter,
              }
         return c
@@ -302,12 +306,11 @@
             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()

Modified: Zope/trunk/lib/python/Shared/DC/Scripts/Bindings.py
===================================================================
--- Zope/trunk/lib/python/Shared/DC/Scripts/Bindings.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Shared/DC/Scripts/Bindings.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -20,6 +20,7 @@
 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 @@
         # 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 @@
     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 @@
     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)

Modified: Zope/trunk/lib/python/ZPublisher/BaseRequest.py
===================================================================
--- Zope/trunk/lib/python/ZPublisher/BaseRequest.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/ZPublisher/BaseRequest.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -18,6 +18,7 @@
 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 @@
                     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 @@
                 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 @@
                 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 @@
                 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 @@
                         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
@@ -451,10 +456,9 @@
                             # Zope2 doesn't set up its own adapters in a lot
                             # 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)

Modified: Zope/trunk/lib/python/ZPublisher/HTTPRequest.py
===================================================================
--- Zope/trunk/lib/python/ZPublisher/HTTPRequest.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/ZPublisher/HTTPRequest.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -1116,7 +1116,7 @@
         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 @@
         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 @@
             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,

Modified: Zope/trunk/lib/python/ZPublisher/mapply.py
===================================================================
--- Zope/trunk/lib/python/ZPublisher/mapply.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/ZPublisher/mapply.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -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,28 +40,16 @@
     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
+        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]
 
-        if im:
-            f=f.im_func
-            c=f.func_code
-            defaults=f.func_defaults
-            names=c.co_varnames[1:c.co_argcount]
-        else:
-            defaults=f.func_defaults
-            c=f.func_code
-            names=c.co_varnames[:c.co_argcount]
-
     nargs=len(names)
     if positional:
         positional=list(positional)

Modified: Zope/trunk/lib/python/ZPublisher/tests/testBaseRequest.py
===================================================================
--- Zope/trunk/lib/python/ZPublisher/tests/testBaseRequest.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/ZPublisher/tests/testBaseRequest.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -6,6 +6,7 @@
 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 @@
 
         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,7 +389,28 @@
         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):
 
     base = ''
@@ -512,6 +540,34 @@
     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 @@
         ))
 
 if __name__ == '__main__':
-    unitttest.main(defaultTest='test_suite')
+    unittest.main(defaultTest='test_suite')

Modified: Zope/trunk/lib/python/ZPublisher/tests/testHTTPRequest.py
===================================================================
--- Zope/trunk/lib/python/ZPublisher/tests/testHTTPRequest.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/ZPublisher/tests/testHTTPRequest.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -1,6 +1,15 @@
+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
 Content-Disposition: form-data; name="file"; filename="file"
@@ -13,13 +22,10 @@
 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 @@
         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 @@
         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 @@
         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__ )
+class RecordTests(unittest.TestCase):
 
-    def test_contains(self):
-        from ZPublisher.HTTPRequest import record
-        record = record()
-        record.a = 1
-        self.assertTrue('a' in record)
+    def test_repr(self):
+        rec = record()
+        rec.a = 1
+        rec.b = 'foo'
+        r = repr(rec)
+        d = eval(r)
+        self.assertEqual(d, rec.__dict__)
 
-    def test_iter(self):
-        from ZPublisher.HTTPRequest import record
-        record = record()
-        record.a = 1
-        record.b = 2
-        record.c = 3
-        for k in record:
-            self.assertTrue(k in ('a','b','c'))
 
-    def test_len(self):
-        from ZPublisher.HTTPRequest import record
-        record = record()
-        record.a = 1
-        record.b = 2
-        record.c = 3
-        self.assertEqual(len(record), 3)
-
 class ProcessInputsTests(unittest.TestCase):
     def _getHTTPRequest(self, env):
-        from ZPublisher.HTTPRequest import HTTPRequest
         return HTTPRequest(None, env, None)
 
     def _processInputs(self, inputs):
@@ -141,9 +117,6 @@
         # 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 @@
         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 @@
         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 @@
     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 @@
     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 @@
 
     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 @@
     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 @@
             '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 @@
             '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 @@
             '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,7 +880,30 @@
         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()
     suite.addTest(unittest.makeSuite(AuthCredentialsTests, 'test'))

Copied: Zope/trunk/lib/python/ZPublisher/tests/test_mapply.py (from rev 85463, Zope/branches/philikon-aq/lib/python/ZPublisher/tests/test_mapply.py)
===================================================================
--- Zope/trunk/lib/python/ZPublisher/tests/test_mapply.py	                        (rev 0)
+++ Zope/trunk/lib/python/ZPublisher/tests/test_mapply.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# 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

Modified: Zope/trunk/lib/python/Zope2/App/startup.py
===================================================================
--- Zope/trunk/lib/python/Zope2/App/startup.py	2008-04-26 17:36:12 UTC (rev 85766)
+++ Zope/trunk/lib/python/Zope2/App/startup.py	2008-04-26 17:46:46 UTC (rev 85767)
@@ -18,6 +18,9 @@
 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 @@
     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 @@
             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 @@
             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 @@
                     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 @@
         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 @@
                 auth_path = '/'.join(auth_folder.getPhysicalPath()[1:-1])
 
             T.setUser(auth_user.getId(), auth_path)
-
-
-



More information about the Checkins mailing list