[Zope] Problem when trying to use metal macros from another page template

Josef Meile jmeile at hotmail.com
Wed Nov 5 08:18:32 EST 2003


Hi,

in the past I had some security issues because
I was using a bad way to add dinamically attributes
to my Page Templates (thanks to Chris Withers to
point it out and to Dieter for the solution). I was doing:

manage_addJMBoringForm=PageTemplateFile('zpt/JMBoring_Add',globals())
manage_addJMBoringForm.Kind='JMBoring'

then I replaced it for:

_addJMBoringForm=PageTemplateFile('zpt/JMBoring_Add',globals())
def manage_addJMBoringForm(self):
  return _addJMBoringForm.__of__(self)(Kind='JMBoring')

Now the issue that I'm having is that the later approach
doesn't work when the attribute that I want to store in the
"options" built-in variable is a Page Template with macros.

I have the following structure:

Base Class JMZPTMacros:

<ZopeHome>/lib/python/Shared/JMUtils/JMZPTMacros.py
<ZopeHome>/lib/python/Shared/JMUtils/zpt:
Here I have the following Page Templates with some macros
that I use from my other product JMBoring:
generic.zpt
generic_add.zpt
generic_edit.zpt
generic_view.zpt

Product JMBoring, which subclases from JMZPTMacros:

<ZopeHome>/lib/python/Products/JMBoring/JMBoring.py
<ZopeHome>/lib/python/Products/JMBoring/zpt
Here I have some Page Templates that use the macros
defined on the JMZPTMacros product:
index_html.zpt
JMBoring_Add.zpt
JMBoring_Edit.zpt
JMBoring_View.zpt

What I'm trying to do is to call some macros from "generic.zpt"
and "generic_add.zpt" in "JMBoring_Add.zpt". The main problem
is that the "JMBoring_Add.zpt" Template is loaded outside of
the JMBoring class definition (constructor methods are declared
before the classe definition), while the other Templates like:
"manage_view", "index_html", and "manage_main" are inside, so,
they can access the macros in the "JMZPTMacros" base class
just by typing:

<p metal:use-macro="here/TemplateID/macros/MacroID">Execute macro</p>

but in the JMBoring_Add.zpt, an AttributeError exception is
raised. I guess it's because the other Templates are called from
an instance of JMBoring, so, they have access to the methods
of the base class JMZPTMacros, while when calling this
template, an instance of such class still doesn't exist, for this
is a contructor method.

That's how I did it (It has the security assertions that Chris
mentioned before):

file <ZopeHome>/lib/python/Shared/JMUtils/JMZPTMacros.py:

from Products.PageTemplates.PageTemplateFile import PageTemplateFile

class JMZPTMacros:
  generic_add=PageTemplateFile('zpt/generic_add',globals())
  generic_add._owner=None

  generic_edit=PageTemplateFile('zpt/generic_edit',globals())
  generic_edit._owner=None

  generic_view=PageTemplateFile('zpt/generic_view',globals())
  generic_view._owner=None

  generic=PageTemplateFile('zpt/generic',globals())
  generic._owner=None

file <ZopeHome>/lib/python/Products/JMBoring/JMBoring.py:

################################################
  Here comes some imports and variable definitions that one
  has to do to give the class the basic characteristics of a zope
  product.

  They weren't included on this message.
################################################

_addJMBoringForm=PageTemplateFile('zpt/JMBoring_Add',globals())

def manage_addJMBoringForm(self):
  """Here some attributes are added to the template"""
  #The following two lines should be replaced with a safer
  #implementation

  _addJMBoringForm.generic=JMZPTMacros.generic
  _addJMBoringForm.generic_add=JMZPTMacros.generic_add

  _addJMBoringForm._owner=None
  return _addJMBoringForm.__of__(self)(Kind='JMBoring')

def
manage_addJMBoring(self,id,title='',description='',REQUEST=None,submit=None)
:
  """Invoques the __init__ method of the JMBoring class"""
  id=id.replace(' ','_')
  boringObj=JMBoring(id,title,description)
  self._setObject(id,boringObj)
  if REQUEST is not None:
    try:
      destURL=self.DestinationURL()
    except:
      destURL=REQUEST['URL1']

    if submit==" Add and Edit ":
      destURL="%s/%s" % (destURL,quote(id))
    REQUEST.RESPONSE.redirect(destURL+'/manage_main')
  return ''


class JMBoring(SimpleItem, PropertyManager, JMZPTMacros):
  """JMBoring class definition"""

################################################
  Here comes all the stuff necessary to make my class work
  as a zope product (__init__ method, security declarations,
  properties, etc.).

  It wasn't included on this message
################################################

  manage_main=PageTemplateFile('zpt/JMBoring_Edit',globals())
  manage_main._owner=None

################################################
  More stuff comes here
################################################

  manage_view=PageTemplateFile('zpt/JMBoring_View',globals())
  manage_view._owner=None

  index_html=PageTemplateFile('zpt/index_html',globals())
  index_html._owner=None

InitializeClass(JMBoring)

Then I called the macros in JMBoring_Add.zpt:

<p metal:use-macro="template/generic_add/macros/addHeader">Add Header</p>
  <p class="form-help">
    Optional fields are written in <b><i>italic</i></b>
  </p>
  <span metal:use-macro="template/generic_add/macros/addForm">
    <span metal:fill-slot="moreProps" tal:omit-tag="">
      <tr>
        <td align="left" valign="top">
          <div class="form-optional">description</div>
        </td>
        <td align="left" valign="top">
          <textarea name="description" rows="6" cols="35"></textarea>
        </td>
      </tr>
    </span>
  </span>
<p metal:use-macro="template/generic/macros/genericFooter">Generic
Footer</p>

This works, but as mentioned before, it's not safe. Then I tried the
following without any success:

def manage_addJMBoringForm(self):
  """Here some attributes are added to the template"""

  #I only replaced the line where the generic
  #Template was included. This line uses the unsafe
  #sintax
  _addJMBoringForm.generic_add=JMZPTMacros.generic_add


  _addJMBoringForm._owner=None

  #Here I included the generic Template as an attribute of the options
  #built-in variable
  return
_addJMBoringForm.__of__(self)(Kind='JMBoring',generic=JMZPTMacros.generic)

Then I called it:
<p metal:use-macro="options/generic/macros/genericFooter">Generic Footer</p>


I got the following traceback:

Traceback (innermost last):
  Module ZPublisher.Publish, line 98, in publish
  Module ZPublisher.mapply, line 88, in mapply
  Module ZPublisher.Publish, line 39, in call_object
  Module Products.JMBoring.JMBoring, line 70, in manage_addJMBoringForm
  Module Shared.DC.Scripts.Bindings, line 252, in __call__
  Module Shared.DC.Scripts.Bindings, line 283, in _bindAndExec
  Module Products.PageTemplates.PageTemplateFile, line 96, in _exec
  Module Products.PageTemplates.PageTemplate, line 95, in pt_render
   - <PageTemplateFile at />
  Module TAL.TALInterpreter, line 200, in __call__
  Module TAL.TALInterpreter, line 244, in interpret
  Module TAL.TALInterpreter, line 689, in do_useMacro
  Module Products.PageTemplates.TALES, line 220, in evaluate
   - URL: JMBoring_Add
   - Line 17, Column 0
   - Expression: standard:'options/generic/macros/genericFooter'
   - Names:
      {'container': <__FactoryDispatcher__ instance at 015C0980>,
       'default': <Products.PageTemplates.TALES.Default instance at
00A85474>,
       'here': <__FactoryDispatcher__ instance at 015C0980>,
       'loop': <SafeMapping instance at 015EFFF0>,
       'modules': <Products.PageTemplates.ZRPythonExpr._SecureModuleImporter
instance at 00A85A34>,
       'nothing': None,
       'options': {'Kind': 'JMBoring',
                   'args': (),
                   'generic': <PageTemplateFile at 0xa69970>},
       'repeat': <SafeMapping instance at 015EFFF0>,
       'request': <HTTPRequest,
URL=http://localhost:8080/manage_addProduct/JMBoring/manage_addJMBoringForm>
,
       'root': <Application instance at 015ABE18>,
       'template': <PageTemplateFile at />,
       'traverse_subpath': [],
       'user': admin}
  Module Products.PageTemplates.Expressions, line 206, in __call__
  Module Products.PageTemplates.Expressions, line 194, in _eval
  Module Products.PageTemplates.Expressions, line 150, in _eval
   - __traceback_info__: options
  Module Products.PageTemplates.Expressions, line 371, in restrictedTraverse
   - __traceback_info__: {'path': ['generic', 'macros', 'genericFooter'],
'TraversalRequestNameStack': ['genericFooter', 'macros']}
TypeError: 'in' or 'not in' needs sequence right argument

Same when I only use:

<p tal:define="generic options/generic"></p>

or

<p tal:define="generic python:options['generic']"></p>

So, I did:
<p tal:replace="options"></p>

and I got:
{'args': (), 'Kind': 'JMBoring', 'generic': <PageTemplateFile at 0xa69970>}

Wich means that my template is in the options variable,
but for some reason the macros stored there can't be
used. Does somebody know how I can solve the problem?
or perhaps an alternative way of solving it?

Thanks in advanced,
Josef



More information about the Zope mailing list