Code

tweak
[roundup.git] / doc / templating.txt
1 ==========================
2 HTML Templating Mechanisms
3 ==========================
5 :Version: $Revision: 1.5 $
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>
93 Implementation
94 ~~~~~~~~~~~~~~
96 I'm envisaging an infrastructure layer where each template has the following
97 variables defined:
99 *class*
100   the current class of node being displayed
101 *item*
102   the current node from the database, if we're viewing a specific node
103 (*classname*)
104   the current node is also available under its classname, so a *user* node
105   would also be available under the name *user*.
106 *form*
107   the current CGI form information
108 *instance*
109   the current instance
110 *db*
111   the current open database
112 *config*
113   the current instance config
114 *util*
115   utility methods
117 Accesses through a class (either through *class* or *db.<classname>*):
119     class HTMLItem:
120         def __getattr__(self, attr):
121             ''' return an HTMLItem instance '''
122         def classhelp(self, ...)
123         def list(self, ...)
125 Accesses through an *item*::
127     class HTMLItem:
128         def __getattr__(self, attr):
129             ''' return an HTMLItem instance '''
130         def history(self, ...)
131         def classhelp(self, ...)
132         def remove(self, ...)
134 String, Number, Date, Interval HTMLProperty
135  a wrapper object which may be stringified for the current plain() behaviour
136  and has methods emulating all the current display functions, so
137  ``item/name/plain`` would emulate the current ``call="plain()``". Also, 
138  ``python:item.name.plain(name=value)`` would work just fine::
140     class HTMLProperty:
141         def __init__(self, instance, db, ...)
142         def __str__(self):
143             return self.plain()
145     class StringHTMLProperty(HTLProperty):
146         def plain(self, ...)
147         def field(self, ...)
148         def stext(self, ...)
149         def multiline(self, ...)
150         def email(self, ...)
152     class NumberHTMLProperty(HTMLProperty):
153         def plain(self, ...)
154         def field(self, ...)
156     class BooleanHTMLProperty(HTMLProperty):
157         def plain(self, ...)
158         def field(self, ...)
160     class DateHTMLProperty(HTMLProperty):
161         def plain(self, ...)
162         def field(self, ...)
163         def reldate(self, ...)
165     class IntervalHTMLProperty(HTMLProperty):
166         def plain(self, ...)
167         def field(self, ...)
169 Link HTMLProperty
170  the wrapper object would include the above as well as being able to access
171  the class information. Stringifying the object itself would result in the
172  value from the item being displayed. Accessing attributes of this object
173  would result in the appropriate entry from the class being queried for the
174  property accessed (so item/assignedto/name would look up the user entry
175  identified by the assignedto property on item, and then the name property of
176  that user)::
177     
178     class LinkHTMLProperty(HTMLProperty):
179         ''' Be a HTMLItem too '''
180         def __getattr__(self, attr):
181             ''' return a new HTMLProperty '''
182         def download(self, ...)
183         def checklist(self, ...)
185 Multilink HTMLProperty
186  the wrapper would also be iterable, returning a wrapper object like the Link
187  case for each entry in the multilink::
189     class MultilinkHTMLProperty(HTMLProperty):
190         def __len__(self):
191             ''' length of the multilink '''
192         def __getitem(self, num):
193             ''' return a new HTMLItem '''
194         def checklist(self, ...)
195         def list(self, ...)
196  
197 *util*
198  the util object will handle::
200     class Util:
201         def __init__(self, ...)
202         def filterspec(self, ...)
203         def note(self, ...)
204         def submit(self, ...)
206 Action
207 ======
209 1. Investigate how PageTemplates would be integrated into Roundup:
211    - we could go for a fully-divorced-from-Zope approach, which would involve
212      bundling PageTemplates/TAL/ZTUtils in with Roundup, with all the
213      Zope-specific bits removed.
214    - we could try to coexist with a Zope installation, but there the problem
215      would be that Zope includes its own copy of PageTemplates/TAL/ZTUtils and
216      we'd be installing a version in site-packages, which would be bad.
218    The latter may allow nicer integration with Zope itself, giving Zope
219    Roundup users access to acquired information in their templates. We could
220    get around that by modifying the ZRoundup interface to use the "real Zope"
221    ZPT. Maybe.
223 2. Implement the Roundup infrastructure described in the `implementation`_
224    above.