Edgewall Software
Home
Trac
Trac Hacks
Genshi
Babel
Bitten
Home
Download
Documentation
Mailing Lists
License
FAQ
Search:
Login
Preferences
Help/Guide
About Trac
Wiki
Timeline
Roadmap
Browse Source
View Tickets
New Ticket
Search
Context Navigation
-1
Start Page
Index
History
Editing TracDev/ApiChanges/0.11
Adjust edit area height:
8
12
16
20
24
28
32
36
40
Edit side-by-side
[[PageOutline(2-4)]] = TracDev/ApiChanges/0.11 = '''Note: Development of Trac [milestone:0.11] has started with r3804 and the trunk now uses ''[http://genshi.edgewall.org/ Genshi]'' instead of ClearSilver, for its template engine. Nevertheless, you should have in mind that the information in this page corresponds to a ''work in progress''.''' == Migrating away from Clearsilver == ClearSilver has proven a bit uncomfortable to work with, and search for better alternatives were done a few months ago. The [http://kid-templating.org/ Kid] templating language was unanimously found appealing, to the point cmlenz did a porting of Trac to Kid, during the DrProject fork. This in turn was found painful, and prompted Christopher to start his own, enhanced, version of Kid currently maturing as [http://genshi.edgewall.org/ Genshi]. Migrating away from ClearSilver to use Genshi is scheduled for early [milestone:0.11], and is mostly complete in the source:sandbox/genshi branch at this point (r3728). 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 and their corresponding Genshi ones in source:sandbox/genshi/templates. 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 [[xml(<b><?cs var:the_variable ?></b>)]] - Genshi [[xml(<b>$the_variable</b>)]] ==== expand a simple computation ==== - Clearsilver [[xml(<b><?cs var:the_variable+1 ?></b>)]] - Genshi [[xml(<b>${the_variable+1}</b>)]] ==== include another template ==== - Clearsilver [[xml(<?cs include:the_file.cs ?>)]] - Genshi [[xml(<xi:include href="the_file.html"><xi:fallback/></xi:include>)]] ==== simple if...then (no else) ==== - Clearsilver [[xml(<?cs if:flag ?><b>OK</b><?cs /if ?>)]] - Genshi [[xml(<py:if test="flag"><b>OK</b></py:if>)]] or simply [[xml(<b py:if="flag">OK</b>)]] ==== if...then...else ==== - Clearsilver {{{ #!xml <?cs if:flag ?> <b>OK</b> <?cs else ?> <i>!!!</i> <?cs /if ?> }}} - Genshi {{{ #!xml <py:choose test="flag"> <py:when test="True"> <b>OK</b> </py:when> <py:otherwise> <i>!!!</i> </py:otherwise> </py:choose> }}} or simply: {{{ #!xml <py:choose> <b py:when="flag">OK</b> <i py:otherwise="">!!!</i> </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). ==== iterate over a collection ==== - Clearsilver {{{ #!xml <ul><?cs each:element = list ?> <li><?cs var:element ?></li><?cs /each ?> </ul> }}} - Genshi {{{ #!xml <ul> <py:for each="element in list"> <li>$element</li> </py:for> </ul> }}} or simply: {{{ #!xml <ul> <li py:for="element in list">$element</li> </ul> }}} ==== define a macro ==== - Clearsilver {{{ #!xml <?cs def:entry(key, val)?> <dt><?cs var:key ?></dt><dd><?cs var:val ?></dd> <?cs /def ?> }}} - Genshi {{{ #!xml <py:def function="entry(key, val='--')"> <dt>$key</dt><dd>$val</dd> </py:def> }}} As you can see, with Genshi it's also easy to specify default values for the macro arguments. ==== set a variable ==== - Clearsilver {{{ #!xml <?cs set:count = len(collection) ?> We have <?cs if:count > 10 ?>too much<?cs else ?><?cs var:count ?><?cs /if ?> elements. }}} - Genshi {{{ #!xml <py:with vars="count = len(collection)"> We have ${count > 10 and 'too much' or count} elements. </py:with> }}} Note that we had to use `>` 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 [source:trunk/templates/index.cs@3725 index.cs]: {{{ #!xml <!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 [source:sandbox/genshi/templates/index.html@3728 index.html]: {{{ #!xml <!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 a `py:for` and a `py:choose` attribute). - When there's only one element to output conditionally, one should use a genshi attribute (the `py:for="project in projets"` and the `py: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 [source:sandbox/genshi/templates/diff_form.html 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: ---- {{{ #!html <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> }}} ---- 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 [source:sandbox/genshi/templates/query.html 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 [http://genshi.edgewall.org/wiki/Documentation/builder.html 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 {{{ #!xml 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 {{{ #!xml <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 [source:sandbox/genshi/trac/wiki/web_ui.py 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 [source:sandbox/genshi/trac/web/chrome.py@3730#L422 Chrome.load_template] and `render_method` methods. Note however that this API is still rapidly evolving. Usage examples: - Implementing [source:sandbox/genshi/trac/ticket/query.py@3725#L740 Ticket Query] macro (''table'' mode) - Sending [source:sandbox/genshi/trac/notification.py@3725#L99 notification] e-mails
Note:
See
WikiFormatting
and
TracWiki
for help on editing wiki content.
Change information
Your email or username:
E-mail address and name can be saved in the
Preferences
Comment about this change (optional):
Note:
See
TracWiki
for help on using the wiki.