[[PageOutline(2-3,Contents)]] = Porting Templates from ClearSilver to Genshi This page describes some of the differences between Genshi and Clearsilver. It is not a replacement for the [http://genshi.edgewall.org/ Genshi documentation] and you should go there for a more in-depth understanding of how Genshi actually works and should be used. 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. Note that ClearSilver is supported up to Trac 0.12.x, although no changes happened in that area since Trac 0.11. In Trac [milestone:0.13], this support has been dropped (r10405), effectively making the migration to Genshi templates mandatory. == Changes in the template syntax Most of the time, the porting is a straightforward operation. === expand a variable - Clearsilver [[xml()]] - Genshi [[xml($the_variable)]] === expand a simple computation - Clearsilver [[xml()]] - Genshi [[xml(${the_variable+1})]] === include another template - Clearsilver [[xml()]] - Genshi [[xml()]] === simple if...then (no else) - Clearsilver [[xml(OK)]] - Genshi [[xml(OK)]] or simply [[xml(OK)]] === if...then...else - Clearsilver {{{#!xml OK !!! }}} - Genshi {{{#!xml OK !!! }}} or simply: {{{#!xml OK !!! }}} The // 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
}}} - Genshi {{{#!xml
  • $element
}}} or simply: {{{#!xml
  • $element
}}} === define a macro - Clearsilver {{{#!xml
}}} - Genshi {{{#!xml
$key
$val
}}} As you can see, with Genshi it's also easy to specify default values for the macro arguments. === set a variable - Clearsilver {{{#!xml We have 10 ?>too much elements. }}} - Genshi {{{#!xml We have ${count > 10 and 'too much' or count} elements. }}} 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 Available Projects

Available Projects

  • : Error
    ()
}}} - Genshi [source:sandbox/genshi/templates/index.html@3728 index.html]: {{{#!xml Available Projects

Available Projects

}}} Some remarks: - Note the possible use of multiple Genshi attributes in the same element: in the above, the `
  • ` element has a `py:for` and a `py:choose` attribute. - When there's only one element to output conditionally, use a Genshi attribute: the `py:for="project in projects"` and the `py:when="project.href"` in the above. Otherwise, use a Genshi element (here, the ``). - 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@3831 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

    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 [source:sandbox/genshi/templates/query.html@3831 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. You could still insert directly some non well-formed `Markup` data in your template, but 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 `` and ``, to convert a Clearsilver macro using `` and ``: - Clearsilver {{{#!xml def:browser_path_links(path, file) ?> class="first" title="Go to root directory" title="View " href="">/ $part.name / }}} == 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@3831 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 == Debugging Genshi is very different from ClearSilver. For ClearSilver the possibilities were essentially defined by the syntax + the HDF dataset that was available. Genshi evaluates Python, and operates in a Python context that makes a large number of objects directly available for use. However, doing `?hdfdump=1` on a Genshi template will only show a fraction of this content - whatever is added to the dictionary returned from the request handler and post-processors. Where is the project name? Where is the chrome links? Permissions? Here is a starting point for getting insight into the context, and help debugging your own templates. It is an example `site.html` file that can be added to global or project 'templates' folder, and which adds a debug output to all pages viewed. If you already have a `site.html`, just add the ` ${select('*')}

    Debug output - showing information from the rendering context, and edit site.html to test own expressions:

    Checking some href information:
    req.path_info: ${repr(req.path_info)}
    req.base_path: ${repr(req.base_path)}
    req.href(): ${repr(req.href())}
    req.base_url: ${repr(req.base_url)}
    req.abs_href(): ${repr(req.abs_href())}

    Try using reg.hef(): ${req.href('wiki')}

    req.args: ${repr(req.args)}

    Test fetching an element: ${select('div[@id="mainnav"]')}

    perm for ${perm.username}: ${repr(perm.permissions())}

    project: ${repr(project)}

    trac: ${repr(trac or 'not defined')}

    context: ${repr(context)}

    context members: ${repr(b_dir(context))}

    context __dict__:

    ${item}: ${repr(context.__dict__[item])}

    req.environ:

    ${item}: ${repr(req.environ[item])}

    req members: ${repr(b_dir(req))}

    req __dict__:

    ${item}: ${repr(req.__dict__[item])}

    all objects from locals().['__data__']:

    ${item}: ${repr(locals()['__data__'][item])}

    __builtins__:

    ${key}: ${repr(globals()['__builtins__'][key])}

    sys.path:
    ${pprint(sys.path)}

    }}}