Edgewall Software

Version 1 (modified by Christian Boos, 17 years ago) ( diff )

Moved content from TracDev/ApiChanges/0.11

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
    <py:if test="flag"><b>OK</b></py:if>
    
    or simply
    <b py:if="flag">OK</b>
    

if…then…else

  • Clearsilver
    <?cs if:flag ?>
     <b>OK</b>
    <?cs else ?>
     <i>!!!</i>
    <?cs /if ?>
    
  • Genshi
    <py:choose test="flag">
      <py:when test="True">
        <b>OK</b>
      </py:when>
      <py:otherwise>
       <i>!!!</i>
      </py:otherwise>
    </py:choose>
    
    or simply:
    <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
    <ul><?cs 
     each:element = list ?>
      <li><?cs var:element ?></li><?cs 
     /each ?>
    </ul>
    
  • Genshi
    <ul>
      <py:for each="element in list">
        <li>$element</li>
      </py:for>
    </ul>
    
    or simply:
    <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
    <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
    <?cs set:count = len(collection) ?>
    We have <?cs if:count > 10 ?>too much<?cs else ?><?cs var:count ?><?cs /if ?> elements.
    
  • Genshi
    <py:with vars="count = len(collection)">
    We have ${count &gt; 10 and 'too much' or count} elements.
    </py:with>
    
    Note that we had to use &gt; 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 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 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:

Note: See TracWiki for help on using the wiki.