Problem when trying to use metal macros from another page template
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
Josef Meile wrote at 2003-11-5 14:18 +0100:
... 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 ...
Product JMBoring, which subclases from JMZPTMacros:
... JMBoring_Add.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.
Make them additional constructors in your "registerClass". This ensures that they are registered in your "Product factory" and can be accessed in the usual way. -- Dieter
participants (2)
-
Dieter Maurer -
Josef Meile