Skip to content

Commit 9af2b52

Browse files
author
Richard Jones
committed
*** empty log message ***
1 parent 3a06ccd commit 9af2b52

File tree

1 file changed

+4
-357
lines changed

1 file changed

+4
-357
lines changed

doc/templating.txt

Lines changed: 4 additions & 357 deletions
Original file line numberDiff line numberDiff line change
@@ -1,359 +1,6 @@
1-
==========================
2-
HTML Templating Mechanisms
3-
==========================
1+
=========================
2+
Roundup Tracker Templates
3+
=========================
44

5-
:Version: $Revision: 1.14 $
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.
5+
:Version: $Revision: 1.15 $
3596

0 commit comments

Comments
 (0)