Filling a metal slot which name is known only at run time
Hi, I am writing a kind of generic templating system. The details are not so important, but essentially, I need developers to be able to write page templates with slots, which end users can fill with various content via a tool I'm writing. Basically, I need to be able to get the list of all slots in a macro (which I can by traversing the contents of 'template.macros') and then to generate a metal:fill-slot statement for each one. However, the problem is that metal:fill-slot seems only to accept a static string as the slot name. I need to be able to generate the fill-slot statement at run-time, essentially something like: metal:fill-slot="python:here.getSlotToFill ()" though that doesn't work, because it doesn't evaulate the python: statement, taking it instead as a string (which is an invalid slot name). Can this be done? Thanks, Mart -- "Life is both a major and a minor key" -- Travis, Side
Martin Aspeli wrote:
metal:fill-slot="python:here.getSlotToFill ()"
though that doesn't work, because it doesn't evaulate the python: statement, taking it instead as a string (which is an invalid slot name).
Can this be done?
I suppose it could, if you banged hard enough, but that's not really what slots were made for... are you filling the slots with "live" ZPT snippets, or just HTML? If the latter, you probably want to use tal:content="structure stuff_to_insert". Cheers, Evan @ 4-am
I suppose it could, if you banged hard enough, but that's not really what slots were made for... are you filling the slots with "live" ZPT snippets, or just HTML? If the latter, you probably want to use tal:content="structure stuff_to_insert".
Well, I think slots are perfect for what I'm doing. :-) I'm creating a content type which composes the views of several objects (the exact objects are picked by the end user) into a single view. Developers can write a page template with a macro called "CompositeListing" and register this template with a tool. Once registered, users can pick the template as a layout for the composite listing page. The view of the composite listing page invokes the CompositeListing macro in the chosen page template. This renders the layout. The idea is that layout template authors puts slots in the layout template where they want content to go. This is used not only for aggregating content, but also for managing the layout - a different page lets the object creator choose from a drop-down which items appear in which list. This is achieved by filling the relevant slots with form elements rather than aggreagted content. So far, I've done it with hard-coded slot names slot0, ..., slot9. It works very well, except for the inconvenience of having to use these pre-defined slot names. I don't want to invert the control, forcing template layout authors to invoke the correct aggreagted content or display macro. It is an important requirement that layout templates are as easy to write as possible. Something like <div tal:define-macro="CompositeListing"> <div tal:define-slot="slot0"/> <hr/> <div tal:define-slot="slot1"/> </div> is all that is required for a simple two-row composite listing. Any additional complexity comes purely from the layout, not from invoking the correct aggregation. How hard would I have to bang? Martin -- "Life is both a major and a minor key" -- Travis, Side
Martin Aspeli wrote:
I'm creating a content type which composes the views of several objects (the exact objects are picked by the end user) into a single view. Developers can write a page template with a macro called "CompositeListing" and register this template with a tool. Once registered, users can pick the template as a layout for the composite listing page.
I'm having trouble grasping this, or at least what the content object *does* with the macro. What is it filling the slots with, and how does it decide which slots to fill with what, if the slots aren't uniform from template to template? Cheers, Evan @ 4-am
Martin Aspeli wrote:
I'm creating a content type which composes the views of several objects (the exact objects are picked by the end user) into a single view. Developers can write a page template with a macro called "CompositeListing" and register this template with a tool. Once registered, users can pick the template as a layout for the composite listing page.
I'm having trouble grasping this, or at least what the content object *does* with the macro. What is it filling the slots with, and how does it decide which slots to fill with what, if the slots aren't uniform from template to template?
Consider a content type CompositePage. A user selects one from a number of pre-defined layouts. Each layout contains slots (both in the Zope sense and the logical sense) where composited content may appear. The user then selects which content objects (from elsewhere in the site) to put in which of these slots. It is important that creating a new layout is as simple as possible. Hence, I do this by allowing layout creators to write a ZPT which contains metal:define-slot statements where the slots should go (e.g. inside a <td> or <div>). The ZPTs containing the macro is registered with a tool, which the CompositePage queries to let the user choose a layout. This would be a simple layout template: <div metal:define-macro="layout"> <div metal:define-slot="top_slot"/> <hr/> <div metal:define-slot="bottom_slot"/> </div> When viewing the CompositePage, it will do the following: <metal:layout use-macro="here/getChosenLayoutTemplate"> <metal:composite fill-slot="top_slot"> <div tal:content="structure python:here.renderContentForSlot ('top_slot')"/> </metal:composite> <metal:composite fill-slot="bottom_slot"> <div tal:content="structure python:here.renderContentForSlot ('bottom_slot')"/> </metal:composite> </metal:layout> When editing the CompositePage, it will instead of rendering the content render a widget from which the user can choose which item to place in a given slot. This selection is then saved inside the CompositePage, which looks the item up and renders it (via a special view template) so that it may be shown with the tal:content statement above. Does that make more sense? I have implemented this with slot-names hardcoded to slot0, ..., slot9. However, I'd much prefer if the CompositePage macro would just pick up the various slots defined inside the layout, so that template authors could use more meaningful names and potentially use more than 10 slots. I have a script which lets me get the list of slot names defined inside the macro "layout" in the chosen layout template. The problem, as stated before, is that I can't just tal:repeat loop over this and dynamically use metal:fill-slot, as metal:fill-slot appearently only accepts static strings, not TAL/Python statements. Martin -- "Life is both a major and a minor key" -- Travis, Side
Martin Aspeli wrote:
When viewing the CompositePage, it will do the following:
<metal:layout use-macro="here/getChosenLayoutTemplate"> <metal:composite fill-slot="top_slot"> <div tal:content="structure python:here.renderContentForSlot ('top_slot')"/> </metal:composite> <metal:composite fill-slot="bottom_slot"> <div tal:content="structure python:here.renderContentForSlot ('bottom_slot')"/> </metal:composite> </metal:layout>
Ah, so you want layout template authors to be able to define slots with semi-meaningful names, then programmatically fill those slots with either a content-selector widget or the selected content. The fact that 'fill-slot' only accepts a literal string isn't a surface detail -- you'd have to go deep into the guts of the TAL interpreter to change that. The best way I can think of to deal with this is to actually generate the ZPT text with the slot names filled in, as in the example above, and compile the text as a template. Cheers, Evan @ 4-am
On Tue, 14 Dec 2004 12:46:49 -0600, Evan Simpson <evan@tokenexchange.com> wrote:
Martin Aspeli wrote:
When viewing the CompositePage, it will do the following: <metal:layout use-macro="here/getChosenLayoutTemplate"> <metal:composite fill-slot="top_slot"> <div tal:content="structure python:here.renderContentForSlot ('top_slot')"/> </metal:composite> <metal:composite fill-slot="bottom_slot"> <div tal:content="structure python:here.renderContentForSlot ('bottom_slot')"/> </metal:composite> </metal:layout>
Ah, so you want layout template authors to be able to define slots with semi-meaningful names, then programmatically fill those slots with either a content-selector widget or the selected content. The fact that 'fill-slot' only accepts a literal string isn't a surface detail -- you'd have to go deep into the guts of the TAL interpreter to change that. The best way I can think of to deal with this is to actually generate the ZPT text with the slot names filled in, as in the example above, and compile the text as a template.
Interesting... How can I do that (compile + execute as a template)? Martin -- "Life is both a major and a minor key" -- Travis, Side
Martin Aspeli wrote:
Interesting... How can I do that (compile + execute as a template)?
If you're writing a filesystem Product, it should be a simple as: from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate [...] def run_template(self, *args, **kwargs): zpt = getattr(self, '_v_zpt', None) if zpt is None: t = self.generate_template_text() self._v_zpt = zpt = ZopePageTemplate('', t) zpt.__of__(self)(*args, **kwargs) This is totally untested, though. You may wish to look at FSPageTemplate.py from CMF's CMFCore package. Cheers, Evan @ 4-am
participants (2)
-
Evan Simpson -
Martin Aspeli