[Zope-dev] Please test this Zope 2.2 beta 1 ZSQLMethods fix...

Brian Lloyd Brian@digicool.com
Wed, 21 Jun 2000 11:25:39 -0400


This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.

------ =_NextPart_000_01BFDB94.F4F26D80
Content-Type: text/plain

Hi all,

Several folks have reported problems in beta 1 with SQLMethods 
having authentication problems when trying to call other SQL 
Methods. I'd like a few intrepid volunteers to try something 
for me to see if it fixes the problem. I'd like to make a 2.2 
beta 2 release tomorrow but I want to make sure this issue is 
resolved first.

Make a backup of the file:

lib/python/Shared/DC/ZRDB/DA.py

in your current (beta 1) installation and replace it with the 
DA.py attached to this email, restart and let me know if the 
problem goes away.

Thanks!

Brian Lloyd        brian@digicool.com
Software Engineer  540.371.6909              
Digital Creations  http://www.digicool.com 





------ =_NextPart_000_01BFDB94.F4F26D80
Content-Type: application/octet-stream;
	name="DA.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="DA.py"

########################################################################=
######=0A=
# =0A=
# Zope Public License (ZPL) Version 1.0=0A=
# -------------------------------------=0A=
# =0A=
# Copyright (c) Digital Creations.  All rights reserved.=0A=
# =0A=
# This license has been certified as Open Source(tm).=0A=
# =0A=
# Redistribution and use in source and binary forms, with or without=0A=
# modification, are permitted provided that the following conditions =
are=0A=
# met:=0A=
# =0A=
# 1. Redistributions in source code must retain the above copyright=0A=
#    notice, this list of conditions, and the following disclaimer.=0A=
# =0A=
# 2. Redistributions in binary form must reproduce the above =
copyright=0A=
#    notice, this list of conditions, and the following disclaimer =
in=0A=
#    the documentation and/or other materials provided with the=0A=
#    distribution.=0A=
# =0A=
# 3. Digital Creations requests that attribution be given to Zope=0A=
#    in any manner possible. Zope includes a "Powered by Zope"=0A=
#    button that is installed by default. While it is not a license=0A=
#    violation to remove this button, it is requested that the=0A=
#    attribution remain. A significant investment has been put=0A=
#    into Zope, and this effort will continue if the Zope community=0A=
#    continues to grow. This is one way to assure that growth.=0A=
# =0A=
# 4. All advertising materials and documentation mentioning=0A=
#    features derived from or use of this software must display=0A=
#    the following acknowledgement:=0A=
# =0A=
#      "This product includes software developed by Digital =
Creations=0A=
#      for use in the Z Object Publishing Environment=0A=
#      (http://www.zope.org/)."=0A=
# =0A=
#    In the event that the product being advertised includes an=0A=
#    intact Zope distribution (with copyright and license included)=0A=
#    then this clause is waived.=0A=
# =0A=
# 5. Names associated with Zope or Digital Creations must not be used =
to=0A=
#    endorse or promote products derived from this software without=0A=
#    prior written permission from Digital Creations.=0A=
# =0A=
# 6. Modified redistributions of any form whatsoever must retain=0A=
#    the following acknowledgment:=0A=
# =0A=
#      "This product includes software developed by Digital =
Creations=0A=
#      for use in the Z Object Publishing Environment=0A=
#      (http://www.zope.org/)."=0A=
# =0A=
#    Intact (re-)distributions of any official Zope release do not=0A=
#    require an external acknowledgement.=0A=
# =0A=
# 7. Modifications are encouraged but must be packaged separately as=0A=
#    patches to official Zope releases.  Distributions that do not=0A=
#    clearly separate the patches from the original work must be =
clearly=0A=
#    labeled as unofficial distributions.  Modifications which do =
not=0A=
#    carry the name Zope may be packaged in any form, as long as =
they=0A=
#    conform to all of the clauses above.=0A=
# =0A=
# =0A=
# Disclaimer=0A=
# =0A=
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY=0A=
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, =
THE=0A=
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A =
PARTICULAR=0A=
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR =
ITS=0A=
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,=0A=
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT=0A=
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF=0A=
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED =
AND=0A=
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT =
LIABILITY,=0A=
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY =
OUT=0A=
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY =
OF=0A=
#   SUCH DAMAGE.=0A=
# =0A=
# =0A=
# This software consists of contributions made by Digital Creations =
and=0A=
# many individuals on behalf of Digital Creations.  Specific=0A=
# attributions are listed in the accompanying credits file.=0A=
# =0A=
########################################################################=
######=0A=
__doc__=3D'''Generic Database adapter=0A=
=0A=
=0A=
$Id$'''=0A=
__version__=3D'$Revision$'[11:-2]=0A=
=0A=
import OFS.SimpleItem, Aqueduct, RDB=0A=
import DocumentTemplate, marshal, md5, base64, Acquisition, os=0A=
from Aqueduct import decodestring, parse=0A=
from Aqueduct import custom_default_report, default_input_form=0A=
from Globals import HTMLFile, MessageDialog=0A=
from cStringIO import StringIO=0A=
import sys, Globals, OFS.SimpleItem, AccessControl.Role=0A=
from string import atoi, find, join, split=0A=
import DocumentTemplate, sqlvar, sqltest, sqlgroup=0A=
from time import time=0A=
from zlib import compress, decompress=0A=
from DateTime.DateTime import DateTime=0A=
md5new=3Dmd5.new=0A=
import ExtensionClass=0A=
import DocumentTemplate.DT_Util=0A=
from cPickle import dumps, loads=0A=
from Results import Results=0A=
from App.Extensions import getBrain=0A=
from AccessControl import getSecurityManager=0A=
=0A=
try: from IOBTree import Bucket=0A=
except: Bucket=3Dlambda:{}=0A=
=0A=
=0A=
class nvSQL(DocumentTemplate.HTML):=0A=
    # Non-validating SQL Template for use by SQLFiles.=0A=
    commands=3D{}=0A=
    for k, v in DocumentTemplate.HTML.commands.items(): =
commands[k]=3Dv=0A=
    commands['sqlvar' ]=3Dsqlvar.SQLVar=0A=
    commands['sqltest']=3Dsqltest.SQLTest=0A=
    commands['sqlgroup' ]=3Dsqlgroup.SQLGroup=0A=
=0A=
    _proxy_roles=3D()=0A=
=0A=
=0A=
class SQL(nvSQL):=0A=
    # Validating SQL template for Zope SQL Methods.=0A=
=0A=
    def validate(self, inst, parent, name, value, md):=0A=
        return getSecurityManager().validate(inst, parent, name, =
value)=0A=
=0A=
class DA(=0A=
    Aqueduct.BaseQuery,Acquisition.Implicit,=0A=
    Globals.Persistent,=0A=
    AccessControl.Role.RoleManager,=0A=
    OFS.SimpleItem.Item,=0A=
    ):=0A=
    'Database Adapter'=0A=
=0A=
    _col=3DNone=0A=
    max_rows_=3D1000=0A=
    cache_time_=3D0=0A=
    max_cache_=3D100=0A=
    class_name_=3Dclass_file_=3D''=0A=
    _zclass=3DNone=0A=
    allow_simple_one_argument_traversal=3DNone=0A=
    template_class=3DSQL=0A=
    =0A=
    manage_options=3D(=0A=
        (=0A=
        {'label':'Edit', 'action':'manage_main',=0A=
         'help':('ZSQLMethods','Z-SQL-Method_Edit.stx')},=0A=
        {'label':'Test', 'action':'manage_testForm',=0A=
         'help':('ZSQLMethods','Z-SQL-Method_Test.stx')},=0A=
        {'label':'Advanced', 'action':'manage_advancedForm',=0A=
         'help':('ZSQLMethods','Z-SQL-Method_Advanced.stx')},=0A=
        )=0A=
        +OFS.SimpleItem.Item.manage_options=0A=
        +AccessControl.Role.RoleManager.manage_options=0A=
        )=0A=
 =0A=
    # Specify how individual operations add up to "permissions":=0A=
    __ac_permissions__=3D(=0A=
        ('View management screens',=0A=
         (=0A=
        'manage_main', 'index_html',=0A=
        'manage_advancedForm', 'PrincipiaSearchSource'=0A=
        )),=0A=
        ('Change Database Methods',=0A=
         ('manage_edit','manage_advanced', =
'manage_testForm','manage_test',=0A=
          'manage_product_zclass_info')),=0A=
        ('Use Database Methods', ('__call__',''), =
('Anonymous','Manager')),=0A=
        )=0A=
   =0A=
=0A=
    def __init__(self, id, title, connection_id, arguments, =
template):=0A=
        self.id=3Dstr(id)=0A=
        self.manage_edit(title, connection_id, arguments, template)=0A=
    =0A=
    manage_advancedForm=3DHTMLFile('advanced', globals())=0A=
=0A=
    test_url___roles__=3DNone=0A=
    def test_url_(self):=0A=
        'Method for testing server connection information'=0A=
        return 'PING'=0A=
=0A=
    _size_changes=3D{=0A=
        'Bigger': (5,5),=0A=
        'Smaller': (-5,-5),=0A=
        'Narrower': (0,-5),=0A=
        'Wider': (0,5),=0A=
        'Taller': (5,0),=0A=
        'Shorter': (-5,0),=0A=
        }=0A=
=0A=
    def _er(self,title,connection_id,arguments,template,=0A=
            SUBMIT,sql_pref__cols,sql_pref__rows,REQUEST):=0A=
        dr,dc =3D self._size_changes[SUBMIT]=0A=
        =0A=
        rows=3Dmax(1,atoi(sql_pref__rows)+dr)=0A=
        cols=3Dmax(40,atoi(sql_pref__cols)+dc)=0A=
        e=3D(DateTime('GMT') + 365).rfc822()=0A=
        resp=3DREQUEST['RESPONSE']=0A=
        =
resp.setCookie('sql_pref__rows',str(rows),path=3D'/',expires=3De)=0A=
        =
resp.setCookie('sql_pref__cols',str(cols),path=3D'/',expires=3De)=0A=
        return self.manage_main(=0A=
            self,REQUEST,=0A=
            title=3Dtitle,=0A=
            arguments_src=3Darguments,=0A=
            connection_id=3Dconnection_id,=0A=
            src=3Dtemplate,=0A=
            sql_pref__cols=3Dcols,sql_pref__rows=3Drows)=0A=
=0A=
    def manage_edit(self,title,connection_id,arguments,template,=0A=
                    SUBMIT=3D'Change',sql_pref__cols=3D'50', =
sql_pref__rows=3D'20',=0A=
                    REQUEST=3DNone):=0A=
        """Change database method  properties=0A=
=0A=
        The 'connection_id' argument is the id of a database =
connection=0A=
        that resides in the current folder or in a folder above the=0A=
        current folder.  The database should understand SQL.=0A=
=0A=
        The 'arguments' argument is a string containing an arguments=0A=
        specification, as would be given in the SQL method cration =
form.=0A=
=0A=
        The 'template' argument is a string containing the source for =
the=0A=
        SQL Template.=0A=
        """=0A=
=0A=
        if self._size_changes.has_key(SUBMIT):=0A=
            return self._er(title,connection_id,arguments,template,=0A=
                            =
SUBMIT,sql_pref__cols,sql_pref__rows,REQUEST)=0A=
=0A=
        self.title=3Dstr(title)=0A=
        self.connection_id=3Dstr(connection_id)=0A=
        arguments=3Dstr(arguments)=0A=
        self.arguments_src=3Darguments=0A=
        self._arg=3Dparse(arguments)=0A=
        template=3Dstr(template)=0A=
        self.src=3Dtemplate=0A=
        self.template=3Dt=3Dself.template_class(template)=0A=
        t.cook()=0A=
        self._v_cache=3D{}, Bucket()=0A=
        if REQUEST:=0A=
            if SUBMIT=3D=3D'Change and Test':=0A=
                return self.manage_testForm(REQUEST)=0A=
            message=3D'ZSQL Method content changed'=0A=
            return self.manage_main(self, REQUEST, =
manage_tabs_message=3Dmessage)=0A=
        return ''=0A=
=0A=
=0A=
    def manage_advanced(self, max_rows, max_cache, cache_time,=0A=
                        class_name, class_file, direct=3DNone,=0A=
                        REQUEST=3DNone, zclass=3D''):=0A=
        """Change advanced properties=0A=
=0A=
        The arguments are:=0A=
=0A=
        max_rows -- The maximum number of rows to be returned from a =
query.=0A=
=0A=
        max_cache -- The maximum number of results to cache=0A=
=0A=
        cache_time -- The maximum amound of time to use a cached =
result.=0A=
=0A=
        class_name -- The name of a class that provides additional=0A=
          attributes for result record objects. This class will be a=0A=
          base class of the result record class.=0A=
=0A=
        class_file -- The name of the file containing the class=0A=
          definition.=0A=
=0A=
        The class file normally resides in the 'Extensions'=0A=
        directory, however, the file name may have a prefix of=0A=
        'product.', indicating that it should be found in a product=0A=
        directory.=0A=
=0A=
        For example, if the class file is: 'ACMEWidgets.foo', then =
an=0A=
        attempt will first be made to use the file=0A=
        'lib/python/Products/ACMEWidgets/Extensions/foo.py'. If this=0A=
        failes, then the file 'Extensions/ACMEWidgets.foo.py' will =
be=0A=
        used.=0A=
  =0A=
        """=0A=
        # paranoid type checking=0A=
        if type(max_rows) is not type(1):=0A=
            max_rows=3Datoi(max_rows)=0A=
        if type(max_cache) is not type(1):=0A=
            max_cache=3Datoi(max_cache)=0A=
        if type(cache_time) is not type(1):=0A=
            cache_time=3Datoi(cache_time)            =0A=
        class_name=3Dstr(class_name)=0A=
        class_file=3Dstr(class_file)=0A=
        =0A=
        self.max_rows_ =3D max_rows=0A=
        self.max_cache_, self.cache_time_ =3D max_cache, cache_time=0A=
        self._v_cache=3D{}, Bucket()=0A=
        self.class_name_, self.class_file_ =3D class_name, =
class_file=0A=
        self._v_brain=3DgetBrain(self.class_file_, self.class_name_, =
1)=0A=
        self.allow_simple_one_argument_traversal=3Ddirect=0A=
=0A=
        if zclass:=0A=
            for d in =
self.aq_acquire('_getProductRegistryData')('zclasses'):=0A=
                if ("%s/%s" % (d.get('product'),d.get('id'))) =3D=3D =
zclass:=0A=
                    self._zclass=3Dd['meta_class']=0A=
                    break=0A=
=0A=
        =0A=
        if REQUEST is not None:=0A=
            m=3D"ZSQL Method advanced settings have been set"=0A=
            return =
self.manage_advancedForm(self,REQUEST,manage_tabs_message=3Dm)=0A=
##            return self.manage_editedDialog(REQUEST)=0A=
=0A=
    #def getFindContent(self):=0A=
    #    """Return content for use by the Find machinery."""=0A=
    #    return '%s\n%s' % (self.arguments_src, self.src)=0A=
=0A=
    def PrincipiaSearchSource(self):=0A=
        """Return content for use by the Find machinery."""=0A=
        return '%s\n%s' % (self.arguments_src, self.src)=0A=
    =0A=
    def manage_testForm(self, REQUEST):=0A=
        " "=0A=
        input_src=3Ddefault_input_form(self.title_or_id(),=0A=
                                     self._arg, 'manage_test',=0A=
                                     '<dtml-var manage_tabs>')=0A=
        return DocumentTemplate.HTML(input_src)(self, REQUEST, =
HTTP_REFERER=3D'')=0A=
=0A=
    def manage_test(self, REQUEST):=0A=
        """Test an SQL method."""=0A=
=0A=
        src=3D"Could not render the query template!"=0A=
        result=3D()=0A=
        t=3Dv=3Dtb=3DNone=0A=
        try:=0A=
            try:=0A=
                src, result=3Dself(REQUEST, test__=3D1)=0A=
                if find(src,'\0'):=0A=
                    src=3Djoin(split(src,'\0'),'\n'+'-'*60+'\n')=0A=
                if result._searchable_result_columns():=0A=
                    r=3Dcustom_default_report(self.id, result)=0A=
                else:=0A=
                    r=3D'This statement returned no results.'=0A=
            except:=0A=
                t, v, tb =3D sys.exc_info()=0A=
                r=3D'<strong>Error, <em>%s</em>:</strong> %s' % (t, =
v)=0A=
=0A=
            report=3DDocumentTemplate.HTML(=0A=
                '<html>\n'=0A=
                '<BODY BGCOLOR=3D"#FFFFFF" LINK=3D"#000099" =
VLINK=3D"#555555">\n'=0A=
                '<dtml-var manage_tabs>\n<hr>\n%s\n\n'=0A=
                '<hr><strong>SQL =
used:</strong><br>\n<pre>\n%s\n</pre>\n<hr>\n'=0A=
                '</body></html>'=0A=
                % (r,src))=0A=
=0A=
            report=3Dapply(report,(self,REQUEST),{self.id:result})=0A=
=0A=
            if tb is not None:=0A=
                self.raise_standardErrorMessage(=0A=
                    None, REQUEST, t, v, tb, None, report)=0A=
=0A=
            return report=0A=
        =0A=
        finally: tb=3DNone=0A=
=0A=
    def index_html(self, URL1):=0A=
        " "=0A=
        raise 'Redirect', ("%s/manage_testForm" % URL1)=0A=
=0A=
    def _searchable_arguments(self): return self._arg=0A=
=0A=
    def _searchable_result_columns(self): return self._col=0A=
=0A=
    def _cached_result(self, DB__, query):=0A=
=0A=
        # Try to fetch from cache=0A=
        if hasattr(self,'_v_cache'): cache=3Dself._v_cache=0A=
        else: cache=3Dself._v_cache=3D{}, Bucket()=0A=
        cache, tcache =3D cache=0A=
        max_cache=3Dself.max_cache_=0A=
        now=3Dtime()=0A=
        t=3Dnow-self.cache_time_=0A=
        if len(cache) > max_cache / 2:=0A=
            keys=3Dtcache.keys()=0A=
            keys.reverse()=0A=
            while keys and (len(keys) > max_cache or keys[-1] < t):=0A=
                key=3Dkeys[-1]=0A=
                q=3Dtcache[key]=0A=
                del tcache[key]=0A=
                del cache[q]=0A=
                del keys[-1]=0A=
                =0A=
        if cache.has_key(query):=0A=
            k, r =3D cache[query]=0A=
            if k > t: return r=0A=
=0A=
        result=3Dapply(DB__.query, query)=0A=
        if self.cache_time_ > 0:=0A=
            tcache[int(now)]=3Dquery=0A=
            cache[query]=3D now, result=0A=
=0A=
        return result=0A=
=0A=
    def __call__(self, REQUEST=3DNone, __ick__=3DNone, src__=3D0, =
test__=3D0, **kw):=0A=
        """Call the database method=0A=
=0A=
        The arguments to the method should be passed via keyword=0A=
        arguments, or in a single mapping object. If no arguments =
are=0A=
        given, and if the method was invoked through the Web, then =
the=0A=
        method will try to acquire and use the Web REQUEST object as=0A=
        the argument mapping.=0A=
=0A=
        The returned value is a sequence of record objects.=0A=
        """=0A=
=0A=
        if REQUEST is None:=0A=
            if kw: REQUEST=3Dkw=0A=
            else:=0A=
                if hasattr(self, 'REQUEST'): REQUEST=3Dself.REQUEST=0A=
                else: REQUEST=3D{}=0A=
=0A=
        try: dbc=3Dgetattr(self, self.connection_id)=0A=
        except AttributeError:=0A=
            raise AttributeError, (=0A=
                "The database connection <em>%s</em> cannot be found." =
% (=0A=
                self.connection_id))=0A=
=0A=
        try: DB__=3Ddbc()=0A=
        except: raise 'Database Error', (=0A=
            '%s is not connected to a database' % self.id)=0A=
        =0A=
        if hasattr(self, 'aq_parent'):=0A=
            p=3Dself.aq_parent=0A=
            if self._isBeingAccessedAsZClassDefinedInstanceMethod():=0A=
                p=3Dp.aq_parent=0A=
        else: p=3DNone=0A=
=0A=
        argdata=3Dself._argdata(REQUEST)=0A=
        argdata['sql_delimiter']=3D'\0'=0A=
        argdata['sql_quote__']=3Ddbc.sql_quote__=0A=
=0A=
        security=3DgetSecurityManager()=0A=
        security.addContext(self)=0A=
        try:     query=3Dapply(self.template, (p,), argdata)=0A=
        finally: security.removeContext(self)=0A=
=0A=
        if src__: return query=0A=
=0A=
        if self.cache_time_ > 0 and self.max_cache_ > 0:=0A=
            result=3Dself._cached_result(DB__, (query, =
self.max_rows_))=0A=
        else: result=3DDB__.query(query, self.max_rows_)=0A=
=0A=
        if hasattr(self, '_v_brain'): brain=3Dself._v_brain=0A=
        else:=0A=
            brain=3Dself._v_brain=3DgetBrain(self.class_file_, =
self.class_name_)=0A=
=0A=
        zc=3Dself._zclass=0A=
        if zc is not None: zc=3Dzc._zclass_=0A=
=0A=
        if type(result) is type(''):=0A=
            f=3DStringIO()=0A=
            f.write(result)=0A=
            f.seek(0)=0A=
            result=3DRDB.File(f,brain,p, zc)=0A=
        else:=0A=
            result=3DResults(result, brain, p, zc)=0A=
        columns=3Dresult._searchable_result_columns()=0A=
        if test__ and columns !=3D self._col: self._col=3Dcolumns=0A=
=0A=
        # If run in test mode, return both the query and results so=0A=
        # that the template doesn't have to be rendered twice!=0A=
        if test__: return query, result=0A=
=0A=
        return result=0A=
=0A=
    def da_has_single_argument(self): return len(self._arg)=3D=3D1=0A=
=0A=
    def __getitem__(self, key):=0A=
        args=3Dself._arg=0A=
        if self.allow_simple_one_argument_traversal and =
len(args)=3D=3D1:=0A=
            results=3Dself({args.keys()[0]: key})=0A=
            if results:=0A=
                if len(results) > 1: raise KeyError, key=0A=
            else: raise KeyError, key=0A=
            r=3Dresults[0]=0A=
            # if hasattr(self, 'aq_parent'): =
r=3Dr.__of__(self.aq_parent)=0A=
            return r=0A=
            =0A=
        self._arg[key] # raise KeyError if not an arg=0A=
        return Traverse(self,{},key)=0A=
=0A=
    def connectionIsValid(self):=0A=
        return (hasattr(self, self.connection_id) and=0A=
                hasattr(getattr(self, self.connection_id), =
'connected'))=0A=
=0A=
    def connected(self):=0A=
        return getattr(getattr(self, self.connection_id), =
'connected')()=0A=
=0A=
=0A=
    def manage_product_zclass_info(self):=0A=
        r=3D[]=0A=
        Z=3Dself._zclass=0A=
        Z=3Dgetattr(Z, 'aq_self', Z)=0A=
        for d in =
self.aq_acquire('_getProductRegistryData')('zclasses'):=0A=
            z=3Dd['meta_class']=0A=
            if hasattr(z._zclass_,'_p_deactivate'):=0A=
                # Eek, persistent=0A=
                continue=0A=
            x=3D{}=0A=
            x.update(d)=0A=
            x['selected'] =3D (z is Z) and 'selected' or ''=0A=
            del x['meta_class']=0A=
            r.append(x)=0A=
            =0A=
        return r =0A=
=0A=
=0A=
=0A=
Globals.default__class_init__(DA)=0A=
=0A=
=0A=
=0A=
ListType=3Dtype([])=0A=
class Traverse(ExtensionClass.Base):=0A=
    """Helper class for 'traversing' searches during URL traversal=0A=
    """=0A=
    _r=3DNone=0A=
    _da=3DNone=0A=
=0A=
    def __init__(self, da, args, name=3DNone):=0A=
        self._da=3Dda=0A=
        self._args=3Dargs=0A=
        self._name=3Dname=0A=
=0A=
    def __bobo_traverse__(self, REQUEST, key):=0A=
        name=3Dself._name=0A=
        da=3Dself.__dict__['_da']=0A=
        args=3Dself._args=0A=
        if name:=0A=
            if args.has_key(name):=0A=
                v=3Dargs[name]=0A=
                if type(v) is not ListType: v=3D[v]=0A=
                v.append(key)=0A=
                key=3Dv=0A=
=0A=
            args[name]=3Dkey=0A=
=0A=
            if len(args) < len(da._arg):            =0A=
                return self.__class__(da, args)=0A=
            key=3Dself # "consume" key=0A=
=0A=
        elif da._arg.has_key(key): return self.__class__(da, args, =
key)=0A=
=0A=
        results=3Dda(args)=0A=
        if results:=0A=
            if len(results) > 1:=0A=
                try: return results[atoi(key)].__of__(da)=0A=
                except: raise KeyError, key=0A=
        else: raise KeyError, key=0A=
        r=3Dresults[0]=0A=
        # if hasattr(da, 'aq_parent'): r=3Dr.__of__(da.aq_parent)=0A=
        self._r=3Dr=0A=
=0A=
        if key is self: return r=0A=
=0A=
        if hasattr(r,'__bobo_traverse__'):=0A=
            try: return r.__bobo_traverse__(REQUEST, key)=0A=
            except: pass=0A=
=0A=
        try: return getattr(r,key)=0A=
        except AttributeError, v:=0A=
            if str(v) !=3D key: raise AttributeError, v=0A=
=0A=
        return r[key]=0A=
=0A=
    def __getattr__(self, name):=0A=
        r=3Dself.__dict__['_r']=0A=
        if hasattr(r, name): return getattr(r,name)=0A=
        return getattr(self.__dict__['_da'], name)=0A=
=0A=

------ =_NextPart_000_01BFDB94.F4F26D80--