comparison doc/templating.txt @ 2132:6535aa11418a

*** empty log message ***
author Richard Jones <richard@users.sourceforge.net>
date Fri, 26 Mar 2004 01:10:19 +0000
parents 43ab730ee194
children
comparison
equal deleted inserted replaced
2131:dc65f4a91433 2132:6535aa11418a
1 ========================== 1 =========================
2 HTML Templating Mechanisms 2 Roundup Tracker Templates
3 ========================== 3 =========================
4 4
5 :Version: $Revision: 1.14 $ 5 :Version: $Revision: 1.15 $
6 6
7 Current Situation and Issues
8 ============================
9
10 Syntax
11 ------
12
13 Roundup currently uses an element-based HTML-tag-alike templating syntax::
14
15 <display call="checklist('status')">
16
17 The templates were initially parsed using recursive regular expression
18 parsing, and since no template tag could 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::
23
24 <tr class="row-<display call="plain('status')">">
25
26 don't parse as we'd hope. We can modify the parser to work, but that doesn't
27 another couple of issues that have arisen:
28
29 1. the template syntax is not well-formed, and therefore is a pain to parse
30 and doesn't play well with other tools, and
31 2. user requirements generally have to be anticipated and accounted for in
32 templating functions (like ``plain()`` and ``checklist()`` above), and
33 we are therefore artificially restrictive.
34
35 Arguments for switching templating systems:
36
37 *Pros*
38
39 - more flexibility in templating control and content
40 - we can be well-formed
41
42 *Cons*
43
44 - installed user base (though they'd have to edit their templates with the
45 next release anyway)
46 - current templating system is pretty trivial, and a more flexible system
47 is likely to be more complex
48
49
50 Templates
51 ---------
52
53 We should also take this opportunity to open up the flexibility of the
54 templates through:
55
56 1. allowing the tracker to define a "page" template, which holds the overall
57 page structure, including header and footer
58
59
60
61 Possible approaches
62 ===================
63
64 Zope's PageTemplates
65 --------------------
66
67 Using Zope's PageTemplates seems to be the best approach of the lot.
68 In my opinion, it's the peak of HTML templating technology at present. With
69 appropriate infrastructure, the above two examples would read:
70
71 <span tal:replace="item/status/checklist">status checklist</span>
72
73 <tr tal:attributes="class string:row-${item/status/name}">
74
75 ... which doesn't look that much more complicated... honest...
76
77 Other fun can be had when you start playing with stuff like:
78
79 <table>
80 <tr tal:repeat="message item/msg/list">
81 <td tal:define="from message/from">
82 <a href="" tal:attributes="href string:mailto:${from/address}"
83 tal:content="from/name">mailto link</a>
84 </td>
85 <td tal:content="message/title">subject</td>
86 <td tal:content="message/created">received date</td>
87 </tr>
88 </table>
89
90 Note: even if we don't switch templating as a whole, this document may be
91 applied to the ZRoundup frontend.
92
93 PageTemplates in a Nutshell
94 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
95
96 PageTemplates consist of three technologies:
97
98 TAL - Template Attribute Language
99 This is the syntax which is woven into the HTML using the ``tal:`` tag
100 attributes. A TAL parser pulls out the TAL commands from the attributes
101 runs them using some expression engine.
102
103 TALES - TAL Expression Syntax
104 The expression engine used in this case is TALES, which runs the expressions
105 that form the tag attribute values. TALES expressions come in three
106 flavours:
107
108 Path Expressions - eg. ``item/status/checklist``
109 These are object attribute / item accesses. Roughly speaking, the path
110 ``item/status/checklist`` is broken into parts ``item``, ``status``
111 and ``checklist``. The ``item`` part is the root of the expression.
112 We then look for a ``status`` attribute on ``item``, or failing that, a
113 ``status`` item (as in ``item['status']``). If that
114 fails, the path expression fails. When we get to the end, the object we're
115 left with is evaluated to get a string - methods are called, objects are
116 stringified. Path expressions may have an optional ``path:`` prefix, though
117 they are the default expression type, so it's not necessary.
118
119 String Expressions - eg. ``string:hello ${user/name}``
120 These expressions are simple string interpolations (though they can be just
121 plain strings with no interpolation if you want. The expression in the
122 ``${ ... }`` is just a path expression as above.
123
124 Python Expressions - eg. ``python: 1+1``
125 These expressions give the full power of Python. All the "root level"
126 variables are available, so ``python:item.status.checklist()`` would be
127 equivalent to ``item/status/checklist``, assuming that ``checklist`` is
128 a method.
129
130 PageTemplates
131 The PageTemplates module glues together TAL and TALES.
132
133
134 Implementation
135 ~~~~~~~~~~~~~~
136
137 I'm envisaging an infrastructure layer where each template has the following
138 "root level" (that is, directly accessible in the TALES namespace) variables
139 defined:
140
141 *klass*
142 The current class of item being displayed as an HTMLClass instance. Name is
143 mangled so it can be used in Python expressions.
144
145 *item*
146 The current item from the database, if we're viewing a specific item, as an
147 HTMLItem instance. If it doesn't exist, then we're on a new item page.
148
149 (*classname*)
150 this is one of two things:
151
152 1. the *item* is also available under its classname, so a *user* item
153 would also be available under the name *user*. This is also an HTMLItem
154 instance.
155 2. if there's no *item* then the current class is available through this
156 name, thus "user/name" and "user/name/menu" will still work - the latter
157 will pull information from the form if it can.
158
159 this is a dangerous attribute, and may cause us pain the long run (its name
160 may clash with other top-level variables ... it already clashed with the
161 proposed *user* variable). It might be safer to go with just *class* and
162 *item*, actually...
163
164 *form*
165 The current CGI form information as a mapping of form argument name to value
166
167 *request*
168 Includes information about the current request, including:
169 - the url
170 - the current index information (``filterspec``, ``filter`` args,
171 ``properties``, etc) parsed out of the form.
172 - methods for easy filterspec link generation
173 - *user*, the current user item as an HTMLItem instance
174
175 *tracker*
176 The current tracker
177
178 *db*
179 The current open database
180
181 *config*
182 The current instance config
183
184 *modules*
185 python modules made available (XXX: not sure what's actually in there tho)
186
187 Accesses through a class (either through *klass* or *db.<classname>*)::
188
189 class HTMLClass:
190 def __getattr__(self, attr):
191 ''' return an HTMLItem instance '''
192 def classhelp(self, ...)
193 def list(self, ...)
194 def filter(self):
195 ''' Return a list of items from this class, filtered and sorted
196 by the current requested filterspec/filter/sort/group args
197 '''
198
199 Accesses through an *item*::
200
201 class HTMLItem:
202 def __getattr__(self, attr):
203 ''' return an HTMLItem instance '''
204 def history(self, ...)
205 def remove(self, ...)
206
207 Note: the above could cause problems if someone wants to have properties
208 called "history" or "remove"...
209
210 String, Number, Date, Interval HTMLProperty
211 a wrapper object which may be stringified for the current plain() behaviour
212 and has methods emulating all the current display functions, so
213 ``item/name/plain`` would emulate the current ``call="plain()``". Also,
214 ``python:item.name.plain(name=value)`` would work just fine::
215
216 class HTMLProperty:
217 def __init__(self, instance, db, ...)
218 def __str__(self):
219 return self.plain()
220
221 class StringHTMLProperty(HTLProperty):
222 def plain(self, ...)
223 def field(self, ...)
224 def stext(self, ...)
225 def multiline(self, ...)
226 def email(self, ...)
227
228 class NumberHTMLProperty(HTMLProperty):
229 def plain(self, ...)
230 def field(self, ...)
231
232 class BooleanHTMLProperty(HTMLProperty):
233 def plain(self, ...)
234 def field(self, ...)
235
236 class DateHTMLProperty(HTMLProperty):
237 def plain(self, ...)
238 def field(self, ...)
239 def reldate(self, ...)
240
241 class IntervalHTMLProperty(HTMLProperty):
242 def plain(self, ...)
243 def field(self, ...)
244 def pretty(self, ...)
245
246 Link HTMLProperty
247 the wrapper object would include the above as well as being able to access
248 the class information. Stringifying the object itself would result in the
249 value from the item being displayed. Accessing attributes of this object
250 would result in the appropriate entry from the class being queried for the
251 property accessed (so item/assignedto/name would look up the user entry
252 identified by the assignedto property on item, and then the name property of
253 that user)::
254
255 class LinkHTMLProperty(HTMLProperty):
256 ''' Be a HTMLItem too '''
257 def __getattr__(self, attr):
258 ''' return a new HTMLProperty '''
259 def download(self, ...)
260 def checklist(self, ...)
261
262 Multilink HTMLProperty
263 the wrapper would also be iterable, returning a wrapper object like the Link
264 case for each entry in the multilink::
265
266 class MultilinkHTMLProperty(HTMLProperty):
267 def __len__(self):
268 ''' length of the multilink '''
269 def __getitem(self, num):
270 ''' return a new HTMLItem '''
271 def checklist(self, ...)
272 def list(self, ...)
273
274 *request*
275 the request object will handle::
276
277 class Request:
278 def __init__(self, ...)
279 def filterspec(self, ...)
280
281 Accesses through the *user* attribute of *request*::
282
283 class HTMLUser(HTMLItem):
284 def hasPermission(self, ...)
285
286 (note that the other permission check implemented by the security module may
287 be implemented easily in a tal:condition, so isn't needed here)
288
289 Template files
290 ~~~~~~~~~~~~~~
291
292 Each instance will have the opportunity to supply the following templates:
293
294 page
295 This is the overall page look template, and includes at some point a TAL
296 command that includes the variable "content". This variable causes the actual
297 page content to be generated.
298
299 [classname].[template type]
300 Templates that have this form are applied to item data. There are three forms
301 of special template types:
302
303 [classname].index
304 This template is used when the URL specifies only the class, and not an item
305 designator. It displays a list of [classname] items from the database, and
306 a "filter refinement" form.
307 Would perform a TAL ``repeat`` command using the list supplied by
308 ``class/filter``. This deviates from the current situation in that currently
309 the index template specifies a single row, and the filter part is
310 automatically generated.
311
312 [classname].item
313 This template is used when the URL specifies an item designator. It's the
314 default template used (when no template is explicitly given). It displays
315 a single item from the database using the *classname* variable (that
316 is, the variable of the same name as the class being displayed. If
317
318 These two special template types may be overridden by the :template CGI
319 variable.
320
321 Note that the "newitem" template doesn't exist any more because the item
322 templates may determine whether the page has an existing item to render. The
323 new item page would be accessed by "/tracker/url/issue?:template=item".
324 The old "filter" template has been subsumed by the index template.
325
326
327 Integrating Code
328 ~~~~~~~~~~~~~~~~
329
330 We will install PageTemplates, TAL and ZTUtils in site-packages. If there is a
331 local Zope installation, it will use its own PageTemplates code (Zope modifies
332 the module search path to give precedence to its own module library).
333
334 We will then install the trivial MultiMapping and ComputedAttribute modules in
335 the Roundup package, and have some import trickery that determines whether
336 they are required, and if so they will be imported as if they were at the
337 "top level" of the module namespace.
338
339 New CGI client structure
340 ~~~~~~~~~~~~~~~~~~~~~~~~
341
342 Handling of a request in the CGI client will take three phases:
343
344 1. Determine user, pre-set "content" to authorisation page if necessary
345 2. Render main page, with callback to "content"
346 3. Render content - if not pre-set, then determine which content to render
347
348
349 Use Cases
350 ~~~~~~~~~
351
352 Meta/parent bug
353 Can be done with addition to the schema and then the actual parent heirarchy
354 may be displayed with a new template page ":dependencies" or something.
355
356 Submission wizard
357 Can be done using new templates ":page1", ":page2", etc and some additional
358 actions on the CGI Client class in the instance.
359

Roundup Issue Tracker: http://roundup-tracker.org/