Code

more info
[roundup.git] / doc / templating.txt
1 ==========================
2 HTML Templating Mechanisms
3 ==========================
5 :Version: $Revision: 1.7 $
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 PageTemplates in a Nutshell
93 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
95 PageTemplates consist of three technologies:
97 TAL - Template Attribute Language
98   This is the syntax which is woven into the HTML using the ``tal:`` tag
99   attributes. A TAL parser pulls out the TAL commands from the attributes
100   runs them using some expression engine.
102 TALES - TAL Expression Language
103   The expression engine used in this case is TALES, which runs the expressions
104   that form the tag attribute values. TALES expressions come in three
105   flavours:
107   Path Expressions - eg. ``foo/bar/frozz``
108    These are object attribute / item accesses. Roughly speaking, the path
109    ``foo/bar/frozz`` is broken into parts ``foo``, ``bar`` and ``frozz``. The
110    ``foo`` part is the root of the expression. We then look for a ``bar``
111    attribute on foo, or failing that, a bar item (as in foo['bar']). If that
112    fails, the path expression fails. When we get to the end, the object we're
113    left with is evaluated to get a string - methods are called, objects are
114    stringified. Path expressions may have an optional ``path:`` prefix, though
115    they are the default expression type, so it's not necessary.
117   String Expressions - eg. ``string:hello ${user/name}``
118    These expressions are simple string interpolations (though they can be just
119    plain strings with no interpolation if you want. The expression in the
120    ``${ ... }`` is just a path expression as above.
122   Python Expressions - eg. ``python: 1+1``
123    These expressions give the full power of Python. All the "root level"
124    variables are available, so ``python:foo.bar.frozz()`` might be equivalent
125    to ``foo/bar/frozz``, assuming that ``frozz`` is a method.
127 PageTemplates
128   The PageTemplates module glues together TAL and TALES.
131 Implementation
132 ~~~~~~~~~~~~~~
134 I'm envisaging an infrastructure layer where each template has the following
135 "root level" (that is, driectly accessible in the TALES namespace) variables
136 defined:
138 *user*
139   the current user node as an HTMLItem instance
140 *class*
141   the current class of node being displayed as an HTMLClass instance
142 *item*
143   the current node from the database, if we're viewing a specific node, as an
144   HTMLItem instance
145 (*classname*)
146   the current node is also available under its classname, so a *user* node
147   would also be available under the name *user*. This is also an HTMLItem
148   instance.
149 *form*
150   the current CGI form information as a mapping of form argument name to value
151 *instance*
152   the current instance
153 *db*
154   the current open database
155 *config*
156   the current instance config
157 *util*
158   utility methods
159 *modules*
160   Python modules made available (XXX: not sure what's actually in there tho)
162 Accesses through a class (either through *class* or *db.<classname>*):
164     class HTMLClass:
165         def __getattr__(self, attr):
166             ''' return an HTMLItem instance '''
167         def classhelp(self, ...)
168         def list(self, ...)
170 Accesses through an *item*::
172     class HTMLItem:
173         def __getattr__(self, attr):
174             ''' return an HTMLItem instance '''
175         def history(self, ...)
176         def classhelp(self, ...)
177         def remove(self, ...)
179 String, Number, Date, Interval HTMLProperty
180  a wrapper object which may be stringified for the current plain() behaviour
181  and has methods emulating all the current display functions, so
182  ``item/name/plain`` would emulate the current ``call="plain()``". Also, 
183  ``python:item.name.plain(name=value)`` would work just fine::
185     class HTMLProperty:
186         def __init__(self, instance, db, ...)
187         def __str__(self):
188             return self.plain()
190     class StringHTMLProperty(HTLProperty):
191         def plain(self, ...)
192         def field(self, ...)
193         def stext(self, ...)
194         def multiline(self, ...)
195         def email(self, ...)
197     class NumberHTMLProperty(HTMLProperty):
198         def plain(self, ...)
199         def field(self, ...)
201     class BooleanHTMLProperty(HTMLProperty):
202         def plain(self, ...)
203         def field(self, ...)
205     class DateHTMLProperty(HTMLProperty):
206         def plain(self, ...)
207         def field(self, ...)
208         def reldate(self, ...)
210     class IntervalHTMLProperty(HTMLProperty):
211         def plain(self, ...)
212         def field(self, ...)
214 Link HTMLProperty
215  the wrapper object would include the above as well as being able to access
216  the class information. Stringifying the object itself would result in the
217  value from the item being displayed. Accessing attributes of this object
218  would result in the appropriate entry from the class being queried for the
219  property accessed (so item/assignedto/name would look up the user entry
220  identified by the assignedto property on item, and then the name property of
221  that user)::
222     
223     class LinkHTMLProperty(HTMLProperty):
224         ''' Be a HTMLItem too '''
225         def __getattr__(self, attr):
226             ''' return a new HTMLProperty '''
227         def download(self, ...)
228         def checklist(self, ...)
230 Multilink HTMLProperty
231  the wrapper would also be iterable, returning a wrapper object like the Link
232  case for each entry in the multilink::
234     class MultilinkHTMLProperty(HTMLProperty):
235         def __len__(self):
236             ''' length of the multilink '''
237         def __getitem(self, num):
238             ''' return a new HTMLItem '''
239         def checklist(self, ...)
240         def list(self, ...)
241  
242 *util*
243  the util object will handle::
245     class Util:
246         def __init__(self, ...)
247         def filterspec(self, ...)
248         def note(self, ...)
249         def submit(self, ...)
251 Action
252 ======
254 1. Investigate how PageTemplates would be integrated into Roundup:
256    - we could go for a fully-divorced-from-Zope approach, which would involve
257      bundling PageTemplates/TAL/ZTUtils in with Roundup, with all the
258      Zope-specific bits removed.
259    - we could try to coexist with a Zope installation, but there the problem
260      would be that Zope includes its own copy of PageTemplates/TAL/ZTUtils and
261      we'd be installing a version in site-packages, which would be bad.
263    The latter may allow nicer integration with Zope itself, giving Zope
264    Roundup users access to acquired information in their templates. We could
265    get around that by modifying the ZRoundup interface to use the "real Zope"
266    ZPT. Maybe.
268 2. Implement the Roundup infrastructure described in the `implementation`_
269    above.