Code

tweaks
[roundup.git] / doc / templating.txt
1 ==========================
2 HTML Templating Mechanisms
3 ==========================
5 :Version: $Revision: 1.9 $
7 Current Situation and Issues
8 ============================
10 Syntax
11 ------
13 Roundup currently uses an element-based HTML-tag-alike templating syntax::
15    <display call="checklist('status')">
17 The templates were initially parsed using recursive regular expression
18 parsing, and since no template tag could be encapsulate itself, the parser
19 worked just fine. Then we got the ``<require>`` tag, which could have other
20 ``<require>`` tags inside. This forced us to move towards a more complete
21 parser, using the standard python sgmllib/htmllib parser. The downside of this
22 switch is that constructs of the form::
24    <tr class="row-<display call="plain('status')">">
26 don't parse as we'd hope. It would be almost impossible to modify the sgmllib
27 parser to parse the above "correctly", so a wholly new parser would be
28 required. That is a large undertaking, and doesn't address another couple of
29 issues that have arisen:
31 1. the template syntax is not well-formed, and therefore is a pain to parse
32    and doesn't play well with other tools, and
33 2. user requirements generally have to be anticipated and accounted for in
34    templating functions (like ``plain()`` and ``checklist()`` above), and
35    we are therefore artificially restrictive.
37 Arguments for switching templating systems:
39 *Pros*
41   - more flexibility in templating control and content
42   - we can be well-formed
44 *Cons*
46   - installed user base (though they'd have to edit their templates with the
47     next release anyway)
48   - current templating system is pretty trivial, and a more flexible system
49     is likely to be more complex
52 Templates
53 ---------
55 We should also take this opportunity to open up the flexibility of the
56 templates through:
58 1. allowing the instance to define a "page" template, which holds the overall
59    page structure, including header and footer
63 Possible approaches
64 ===================
66 Zope's PageTemplates
67 --------------------
69 Using Zope's PageTemplates seems to be the best approach of the lot.
70 In my opinion, it's the peak of HTML templating technology at present. With
71 appropriate infrastructure, the above two examples would read:
73   <span tal:replace="item/status/checklist">status checklist</span>
75   <tr tal:attributes="class string:row-${item/status/name}">
77 ... which doesn't look that much more complicated... honest...
79 Other fun can be had when you start playing with stuff like:
81   <table>
82    <tr tal:repeat="message item/msg/list">
83     <td tal:define="from message/from">
84      <a href="" tal:attributes="href string:mailto:${from/address}"
85                 tal:content="from/name">mailto link</a>
86     </td>
87     <td tal:content="message/title">subject</td>
88     <td tal:content="message/created">received date</td>
89    </tr>
90   </table>
92 Note: even if we don't switch templating as a whole, this document may be
93 applied to the ZRoundup frontend.
95 PageTemplates in a Nutshell
96 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
98 PageTemplates consist of three technologies:
100 TAL - Template Attribute Language
101   This is the syntax which is woven into the HTML using the ``tal:`` tag
102   attributes. A TAL parser pulls out the TAL commands from the attributes
103   runs them using some expression engine.
105 TALES - TAL Expression Language
106   The expression engine used in this case is TALES, which runs the expressions
107   that form the tag attribute values. TALES expressions come in three
108   flavours:
110   Path Expressions - eg. ``item/status/checklist``
111    These are object attribute / item accesses. Roughly speaking, the path
112    ``item/status/checklist`` is broken into parts ``item``, ``status``
113    and ``checklist``. The ``item`` part is the root of the expression.
114    We then look for a ``status`` attribute on ``item``, or failing that, a
115    ``status`` item (as in ``item['status']``). If that
116    fails, the path expression fails. When we get to the end, the object we're
117    left with is evaluated to get a string - methods are called, objects are
118    stringified. Path expressions may have an optional ``path:`` prefix, though
119    they are the default expression type, so it's not necessary.
121   String Expressions - eg. ``string:hello ${user/name}``
122    These expressions are simple string interpolations (though they can be just
123    plain strings with no interpolation if you want. The expression in the
124    ``${ ... }`` is just a path expression as above.
126   Python Expressions - eg. ``python: 1+1``
127    These expressions give the full power of Python. All the "root level"
128    variables are available, so ``python:item.status.checklist()`` would be
129    equivalent to ``item/status/checklist``, assuming that ``checklist`` is
130    a method.
132 PageTemplates
133   The PageTemplates module glues together TAL and TALES.
136 Implementation
137 ~~~~~~~~~~~~~~
139 I'm envisaging an infrastructure layer where each template has the following
140 "root level" (that is, driectly accessible in the TALES namespace) variables
141 defined:
143 *user*
144   The current user node as an HTMLItem instance
146 *class*
147   The current class of node being displayed as an HTMLClass instance
149 *item*
150   The current node from the database, if we're viewing a specific node, as an
151   HTMLItem instance. If it doesn't exist, then we're on a new item page.
153 (*classname*)
154   this is one of two things:
156   1. the *item* is also available under its classname, so a *user* node
157      would also be available under the name *user*. This is also an HTMLItem
158      instance.
159   2. if there's no *item* then the current class is available through this
160      name, thus "user/name" and "user/name/menu" will still work - the latter
161      will pull information from the form if it can.
163 *form*
164   The current CGI form information as a mapping of form argument name to value
166 *request*
167   Includes information about the current request, including:
168    - the url
169    - the current index information (``filterspec``, ``filter`` args,
170      ``properties``, etc) parsed out of the form. 
171    - methods for easy filterspec link generation
173 *instance*
174   The current instance
176 *db*
177   The current open database
179 *config*
180   The current instance config
182 *modules*
183   python modules made available (XXX: not sure what's actually in there tho)
185 Accesses through the *user*::
187     class HTMLUser:
188         def hasPermission(self, ...)
190 (note that the other permission check implemented by the security module may
191  be implemented easily in a tal:condition, so isn't needed here)
193 Accesses through a class (either through *class* or *db.<classname>*):
195     class HTMLClass:
196         def __getattr__(self, attr):
197             ''' return an HTMLItem instance '''
198         def classhelp(self, ...)
199         def list(self, ...)
201 Accesses through an *item*::
203     class HTMLItem:
204         def __getattr__(self, attr):
205             ''' return an HTMLItem instance '''
206         def history(self, ...)
207         def remove(self, ...)
209 Note: the above could cause problems if someone wants to have properties
210 called "history" or "remove"...
212 String, Number, Date, Interval HTMLProperty
213  a wrapper object which may be stringified for the current plain() behaviour
214  and has methods emulating all the current display functions, so
215  ``item/name/plain`` would emulate the current ``call="plain()``". Also, 
216  ``python:item.name.plain(name=value)`` would work just fine::
218     class HTMLProperty:
219         def __init__(self, instance, db, ...)
220         def __str__(self):
221             return self.plain()
223     class StringHTMLProperty(HTLProperty):
224         def plain(self, ...)
225         def field(self, ...)
226         def stext(self, ...)
227         def multiline(self, ...)
228         def email(self, ...)
230     class NumberHTMLProperty(HTMLProperty):
231         def plain(self, ...)
232         def field(self, ...)
234     class BooleanHTMLProperty(HTMLProperty):
235         def plain(self, ...)
236         def field(self, ...)
238     class DateHTMLProperty(HTMLProperty):
239         def plain(self, ...)
240         def field(self, ...)
241         def reldate(self, ...)
243     class IntervalHTMLProperty(HTMLProperty):
244         def plain(self, ...)
245         def field(self, ...)
246         def pretty(self, ...)
248 Link HTMLProperty
249  the wrapper object would include the above as well as being able to access
250  the class information. Stringifying the object itself would result in the
251  value from the item being displayed. Accessing attributes of this object
252  would result in the appropriate entry from the class being queried for the
253  property accessed (so item/assignedto/name would look up the user entry
254  identified by the assignedto property on item, and then the name property of
255  that user)::
256     
257     class LinkHTMLProperty(HTMLProperty):
258         ''' Be a HTMLItem too '''
259         def __getattr__(self, attr):
260             ''' return a new HTMLProperty '''
261         def download(self, ...)
262         def checklist(self, ...)
264 Multilink HTMLProperty
265  the wrapper would also be iterable, returning a wrapper object like the Link
266  case for each entry in the multilink::
268     class MultilinkHTMLProperty(HTMLProperty):
269         def __len__(self):
270             ''' length of the multilink '''
271         def __getitem(self, num):
272             ''' return a new HTMLItem '''
273         def checklist(self, ...)
274         def list(self, ...)
275  
276 *request*
277  the request object will handle::
279     class Request:
280         def __init__(self, ...)
281         def filterspec(self, ...)
283 Action
284 ======
286 1. Investigate how PageTemplates would be integrated into Roundup:
288    - we could go for a fully-divorced-from-Zope approach, which would involve
289      bundling PageTemplates/TAL/ZTUtils in with Roundup, with all the
290      Zope-specific bits removed.
291    - we could try to coexist with a Zope installation, but there the problem
292      would be that Zope includes its own copy of PageTemplates/TAL/ZTUtils and
293      we'd be installing a version in site-packages, which would be bad.
295    The latter may allow nicer integration with Zope itself, giving Zope
296    Roundup users access to acquired information in their templates. We could
297    get around that by modifying the ZRoundup interface to use the "real Zope"
298    ZPT. Maybe.
300 2. Implement the Roundup infrastructure described in the `implementation`_
301    above.