[Zope] multi-level acquisition (or the "images" folder problem)

Tim Moore tmoore@tembel.org
26 Jun 2001 21:46:40 +0000


OK, a lot of people have run into this class of problems before, but I
haven't seen a general solution yet, so I thought I'd offer one.

But first, let me explain the problem for those who haven't had it yet
:-)

OK, so as an example say we have a site set up like this:

/
|
+-/images
| |
| +-img1
|
+-/index_html
|
+-/folder
  |
  +-/index_html

Both index_html methods contain something like

<dtml-var "images.img1">

...if you're familiar with how acquisition works, then you'll know
that everything is OK...the images folder will be acquired from the
root folder, and img1 is contained therein.

OK, now say we want to add some images specific to the contents of
folder.  We can create an images subfolder, and put a new image in
there:

/
|
+-/images
| |
| +-img1
|
+-/index_html
|
+-/folder
  |
  +-/images
  | |
  | +-img2
  |
  +-/index_html

...and edit the tag in /folder/index_html to say

<dtml-var "images.img2">

So far, still good.  But now lets say that we want /folder/index_html
to use both /images/img1 and /folder/images/img2...

<dtml-var "images.img1">
<dtml-var "images.img2">

The first statement fails.  This is an important subtlety about
acquisition: once it finds an element of the path, it only searches
within that for the rest of the path...so the first statement is
looking for img1 in /folder/images.  This trips up a lot of people who
would expect Zope to keep searching up the path for other "images"
folders until the full path is found (or until it reaches the root
folder in the case that the path isn't present at all).

There are a number of workarounds to this, but mostly they involve
explicitly naming paths and avoiding acquisition entirely, which isn't
always what you want.

So I threw together a simple python script that I put in my root
folder called "find" which takes a path (as a python sequence of
names), starts at the current context, and moves up the acquisition
chain looking for the specified path.  So you'd replace the above
with:

<dtml-var "find(['images', 'img1'])">
<dtml-var "find(['images', 'img2'])">

...and it would all work just fine.  Best of all, you could later add
an img1 to /folder/images to replace the default one in /images for
everything under /folder, just as one might expect.

It seems to work fine OMM, but hasn't been tested extensively, so use
at your own risk :-) Also note that if the specified path doesn't
exist at all anywhere in the acquisition chain, you'll get an error.

Hope this helps some people out there!

## Script (Python) "find"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=path
##title=
##
searchPoint = context
target = None

while searchPoint and not target:
  target = searchPoint.restrictedTraverse(path)
  if not target:
    searchPoint = searchPoint.aq_parent

return target

  
-- 
Tim Moore