Version 1 (modified by 17 years ago) ( diff ) | ,
---|
Porting Templates from ClearSilver to Genshi
The following documentation is by no means a replacement for the excellent documentation you'll find in the Genshi site. You should go there for a more in-depth understanding of how Genshi actually works and should be used. Here we'll focus on the differences with Clearsilver.
For migrating your own templates, a good way to start is to learn by example. Compare the Clearsilver templates found in source:trunk/templates@3831 and their corresponding Genshi ones in source:sandbox/genshi/templates@3831.
Then, in the same way, compare the various web_ui.py controllers you'll find in both branches.
Changes in the template syntax
Most of the time, the porting is a straightforward operation.
expand a variable
- Clearsilver
<b><?cs var:the_variable ?></b>
- Genshi
<b>$the_variable</b>
expand a simple computation
- Clearsilver
<b><?cs var:the_variable+1 ?></b>
- Genshi
<b>${the_variable+1}</b>
include another template
- Clearsilver
<?cs include:the_file.cs ?>
- Genshi
<xi:include href="the_file.html"><xi:fallback/></xi:include>
simple if…then (no else)
- Clearsilver
<?cs if:flag ?><b>OK</b><?cs /if ?>
- Genshi or simply
<py:if test="flag"><b>OK</b></py:if>
<b py:if="flag">OK</b>
if…then…else
- Clearsilver
<?cs if:flag ?> <b>OK</b> <?cs else ?> <i>!!!</i> <?cs /if ?>
- Genshi
or simply:
<py:choose test="flag"> <py:when test="True"> <b>OK</b> </py:when> <py:otherwise> <i>!!!</i> </py:otherwise> </py:choose>
The <py:choose>/<py:when>/<py:otherwise> is a bit heavy-weight for a simple if/else, but on the other hand, the construct is more general (think switch/case, or the equivalent choose/when/otherwise in XSLT).<py:choose> <b py:when="flag">OK</b> <i py:otherwise="">!!!</i> </py:choose>
iterate over a collection
- Clearsilver
<ul><?cs each:element = list ?> <li><?cs var:element ?></li><?cs /each ?> </ul>
- Genshi
or simply:
<ul> <py:for each="element in list"> <li>$element</li> </py:for> </ul>
<ul> <li py:for="element in list">$element</li> </ul>
define a macro
- Clearsilver
<?cs def:entry(key, val)?> <dt><?cs var:key ?></dt><dd><?cs var:val ?></dd> <?cs /def ?>
- Genshi
As you can see, with Genshi it's also easy to specify default values for the macro arguments.
<py:def function="entry(key, val='--')"> <dt>$key</dt><dd>$val</dd> </py:def>
set a variable
- Clearsilver
<?cs set:count = len(collection) ?> We have <?cs if:count > 10 ?>too much<?cs else ?><?cs var:count ?><?cs /if ?> elements.
- Genshi
Note that we had to use
<py:with vars="count = len(collection)"> We have ${count > 10 and 'too much' or count} elements. </py:with>
>
in Genshi, instead of directly>
as in Clearsilver.
Examples
Let's first take a simple full-contained example from the Trac source, the simple index.cs / index.html templates.
- Clearsilver index.cs:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head><title>Available Projects</title></head> <body><h1>Available Projects</h1><ul><?cs each:project = projects ?><li><?cs if:project.href ?> <a href="<?cs var:project.href ?>" title="<?cs var:project.description ?>"> <?cs var:project.name ?></a><?cs else ?> <small><?cs var:project.name ?>: <em>Error</em> <br /> (<?cs var:project.description ?>)</small><?cs /if ?> </li><?cs /each ?></ul></body> </html>
- Genshi index.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" xmlns:xi="http://www.w3.org/2001/XInclude"> <head><title>Available Projects</title></head> <body> <h1>Available Projects</h1> <ul> <li py:for="project in projects" py:choose=""> <a py:when="project.href" href="$project.href" title="$project.description"> $project.name</a> <py:otherwise> <small>$project.name: <em>Error</em> <br /> ($project.description)</small> </py:otherwise> </li> </ul> </body> </html>
Some remarks:
- Note the possible use of multiple genshi attributes in the same element
(in the above, the
<li>
element has apy:for
and apy:choose
attribute). - When there's only one element to output conditionally, one should use a genshi attribute
(the
py:for="project in projets"
and thepy:when="project.href"
in the above). Otherwise, one should use a genshi element (here, the<py:otherwise>
). - In this small example, there's no common Trac layout used (as the index is a bit special). For how a "normal" template looks like, see for example diff_form.html, another small template.
Note that a Genshi template can usually be rendered directly to have a taste of how it will look like:
Available Projects
This comes from an important property of Genshi templates: they must themselves be well-formed XML documents.
That was not a constraint in Clearsilver, and sometimes the logic in those templates took "advantage" of that, e.g. by conditionally inserting end/start pairs of tags. Such templates are the hardest to port, because you actually have to think a bit… See for example the query.html template.
Of course, the great benefit of this constraint is that you'll end up quite naturally with well-formed content, which was far from being a trivial achievement using Clearsilver templates. Granted, you could still insert directly some non well-formed Markup
data in your template, but again, if you use the genshi.builder tag facility for this, that's hardly a risk.
Another example from Trac, a bit more complex.
This illustrates how to use <py:def>
and <py:with>
, to convert a Clearsilver macro using <?cs def: ?>
and <?cs set: ?>
.
- Clearsilver
def:browser_path_links(path, file) ?><?cs <?cs set:first = #1 ?><?cs each:part = path ?><?cs set:last = name(part) == len(path) - #1 ?><a<?cs if:first ?> class="first" title="Go to root directory"<?cs set:first = #0 ?><?cs else ?> title="View <?cs var:part.name ?>"<?cs /if ?> href="<?cs var:part.href ?>"><?cs var:part.name ?></a><?cs if:!last ?><span class="sep">/</span><?cs /if ?><?cs /each ?><?cs /def ?><?cs
- Genshi
<py:def function="browser_path_links(path_links)"> <py:for each="idx, part in enumerate(path_links)"> <py:with vars="first = (idx == 0); last = (idx == len(path_links) - 1)"> <a class="${first and 'first' or None}" title="${first and 'Go to root directory' or 'View ' + part.name}" href="$part.href">$part.name</a> <py:if test="not last"><span class="sep">/</span></py:if> </py:with> </py:for> </py:def>
Changes in the controllers
Implementing the IRequestHandler
interface
Previously, all the data fed to a template had to be placed inside the req.hdf
HDF wrapper object.
With Genshi, the data for the template is basically a dict
, which has to be returned by process_request
at the same time as the template name. Check trac.wiki.web_ui for an example.
Generating content
When one wants to directly render a template, the Chrome component facilities should be used.
Check the Chrome.load_template
and render_method
methods. Note however that this API is still rapidly evolving.
Usage examples:
- Implementing Ticket Query macro (table mode)
- Sending notification e-mails