= Architecture of the HTML templates Here we explain the current template page architecture in Trac which is used since the adoption of the Genshi template engine, i.e. since Trac 0.11. We also describe the future template page architecture which will be used when we switch to the Jinja2 template engine (see Proposals/Jinja). For now this architecture closely follows the Genshi one. == Genshi architecture The Genshi template page architecture in Trac follows two key ideas: - all pages focus on providing the content which is unique to them, and the common parts and look and feel is obtained by //including// a "base" template, //layout.html// - the layout.html includes a dynamic template which takes care of the common look and feel and can be substituted through the configuration; this is how "themes" are handled, and the default dynamic inclusion is the //theme.html// template I've never really tried the TH:ThemeEnginePlugin plugin, or alternatives, so I can't be sure if I got it right, but from what I can see in Trac's code base itself, the idea with Genshi-based themeing (and page architecture in general) was to have a dynamically loaded "theme" template page that would primarily be in charge of the main structure of all HTML pages. Let's take the example of a simple "end user" page, the search.html page. What happens is that: - search.html includes - layout.html, which includes - $chrome.theme (typically "theme.html", the default theme page that ships with Trac) In more details, the [source:tags/trac-1.0.9/trac/search/templates/search.html search.html] is structured like this: {{{#!html+genshi Search ... }}} - it starts by including the layout.html page (``) - it then provides the `` and `` elements specific to that search page; these elements will be processed by the ``es filters defined so far, in the order in which they have been included The [source:tags/trac-1.0.9/trac/templates/layout.html layout.html] page in turn is structured like this: {{{#!html+genshi ${title} – ${project.name or 'Trac'} <!-- e.g. "Search - Trac" --> ... ${select("*[local-name() != 'title']|text()|comment()")} ${select('*|text()|comment()')} ... }}} - it defines `` filters for transforming the content provided in `` and `` elements in the including template (search.html); the head filter adds some content to the `` and prepends some content in the <head>, the body filter appends some content in the <body> - it then **dynamically** includes some "theme" page, `$chrome.theme` By default, this theme page will be our [source:tags/trac-1.0.9/trac/templates/theme.html theme.html] template: {{{#!xml <html> <body> <div id="banner"> ... </div> <div id="main"> ... ${select('*|text()|comment()')} <!-- e.g. "<h1>Search</h1>" ... <div id="altlinks" ... </div> --> </div> <div id="footer"> ... </div> </body> </html> }}} - it defines a `<py:match>` filter on the `<body>` tag (the one that will be produced by the previously applied filters, i.e. the output of the `<py:match>` from the layout.html) and it will **embed** that element into some predefined HTML structure (inside a div element), and it will prepend and append other divs around it: So this dynamic theme template has the last say, and can theoretically re-order the content generated by the previous filters any way it likes, although in practice it simply inserts the body content produced by previous steps inside a predefined structure (the `<div id="main">`). An example of another theme page: [https://github.com/chevah/trac-bootstrap-theme/blob/master/templates/theme.html trac-bootstrap-theme's theme.html]. Note that this scheme can be extended to additional intermediate levels, for example see what we do with the admin panels. One such panel is [source:tags/trac-1.0.9/trac/admin/templates/admin_basics.html admin_basics.html]: {{{#!html+genshi <html> <xi:include href="admin.html" /> <head> <title>Basics ... }}} - it starts by including the admin.html page - then it provides the `` and `` elements specific to that panel The [source:tags/trac-1.0.9/trac/admin/templates/admin.html admin.html] page is similar to the search.html page in that it includes the layout.html, but it also first contains its own `` templates to organize the content of the admin panel which included it: {{{#!html+genshi Administration: ${select('title/text()')} ${select("*[local-name() != 'title']")}

Administration

${select("*|text()")}
}}} - defines `` filters for `` and `` (of the including panel page), which in turn will produce modified `` and `` elements - it then includes the layout.html page (see above) Feel free to brush up your Genshi craft by reading ([G:GenshiTutorial#AddingaLayoutTemplate]), as I just did ;-) == Jinja2 architecture Jinja2 can do dynamic includes as well, or more precisely, dynamic ''extends''. Therefore the Genshi approach can be transposed to Jinja: have the end user page extend the layout page, then have the layout page extend whatever has been defined to be the theme page. The differences with Genshi are subtle: while in both case the control of the output is delegated to the more generic page, with Jinja2 the parent only controls what it puts around //blocks//. It can put some default content in these blocks, but the end user page has the final say about what to do with this default content, as it can reuse it inside its block (by calling `super()`) or not. Let's transpose the previous example of the search.html template. For as long as we'll have both template engines coexisting, we'll prefix the new Jinja2 templates with a `j`. So we're now discussing: - jsearch.html, which extends - jlayout.html, which extends - jtheme.html (as this is our default theme page) A template which extends another can redefine the content of the //named blocks// defined in the extended template. This redefinition may or may not refer to the original content of the block in the extended template (`${ super() }`). We first give an overview of the three templates and how their content will be combined in the generated output. |-------------------------------------------------------------------------- || __jtheme.html__ || \ || __jlayout.html__ \\ //# extends jtheme.html// || \ || __jsearch.html__ \\ //# extends jlayout.html// || |-------------------------------------------------------------------------- {{{#!td style="vertical-align:top" {{{#!html
<html>
 <head>
# block head
# endblock head
 </head>
 <body>
# block body
… banner + metanav + mainnav + contextnav + warnings + notices …
# block content
# endblock content
… footer …
# endblock body
 </body>
</html>
}}} \\ (//head// and //content// blocks \\ have no default content) }}} {{{#!td style="vertical-align:top" {{{#!html
<html>
 <head>
# block head
 <title>
# block title
- Trac
# endblock title
 </title>
… meta + link + script …
# endblock head
 </head>
 <body>
# block content
… alternate formats …
# endblock content
 </body>
</html>
}}} \\ (//head// and //content// blocks \\ have default content) }}} {{{#!td style="vertical-align:top" {{{#!html
<html>
 <head>
  <title>
# block title
Search
${ super() }
# endblock title
  </title>
# block head
${ super() }
… meta …
# endblock head
 </head>
 <body>
# block content
<div class="content">
… Search Results …
</div>
${ super() }
# endblock content
 </body>
</html>
}}} }}} |-------------------------------------------------------------------------- {{{#!td colspan=3 style="vertical-align:top" generated output: {{{#!html
<html>
 <head>
  <title>
Search
- Trac
  </title>
… meta + link + script …
… meta …
 </head>
 <body>
… banner + metanav + mainnav + contextnav + warnings + notices …
<div class="content">
… Search Results …
</div>
… alternate formats …
… footer …
 </body>
</html>
}}} }}} Let's have a closer look at the content of each template, starting with the most specific template, in our example the [source:cboos.git/trac/search/templates/jsearch.html jsearch.html] page: {{{#!html+jinja # extends 'jlayout.html' # block title ${_("Search")} ${ super() } # endblock title # block head ${ super() } ... # endblock head # block content }}} - it starts by //extending// the jlayout.html page (`# extends 'jlayout.html') - then it redefines the ''title'', ''head'' and ''content'' blocks, and has to place a `${ super() }` expression in order to insert the default content proposed by the extended template at the right place; note that the presence of the ``, `` and `` tags here is strictly "decorative", it will be ignored in the final output. As we're in a template extending another, only what's in the redefined blocks matters. These blocks are first defined in the extended template, jlayout.html. The [source:cboos.git/trac/templates/jlayout.html jlayout.html] page looks like this: {{{#!html+jinja # extends ('j' + chrome.theme) # block head # block title – ${project.name or 'Trac'} # endblock title ... # endblock head # block content # endblock content ... }}} - first **dynamically** //extends// in turn some "theme" page (`# extends ('j' + chrome.theme)`) - then the jlayout.html template defines a few blocks: - the ''head'' block and its ''title'' sub-block; here we understand why we've put the ''title'' block **outside** of the ''head'' block in the jsearch.html template: the jlayout.html's ''head'' block contains among other things a `` element, and we're reusing that default content in the inheriting ''head'' block; if in jsearch.html we had defined the <title> element in the ''head'' block as well, we would have had two of these <title> elements in that block - the ''content'' block which is filled with some predefined, generic content, mostly the same stuff that could be found in the corresponding layout.html, in `<py:match>` filters By default, this theme template will be our [source:cboos.git/trac/templates/jtheme.html jtheme.html] page: {{{#!html+jinja <!DOCTYPE html> <html> <head> # block head # endblock head </head> <body> # block body <div id="banner"> ... </div> <div id="main"> ... # block content (here goes the content of the content block produced by layout.html) # endblock content </div> <div id="footer"> ... </div> # block body </body> </html> }}} - it defines a ''head'' block inside an otherwise empty `<head>` element; this means this is simply a "slot" that will be filled by the content of the `head` block in the extending templates (in this case, jlayout.html) - it contains a `<body>` element; as we want to replicate what the original theme.html did, what we want to achieve here is to provide a ''slot'' at the place where we want to insert the content produced by the jlayout.html template; I didn't name that inner block "body", as this could be confusing: we're not in control of the `<body>` element there, just of a fraction of it, the bottom part of the main div. Note that //if// we wanted to be in full control of the body in the extending template, we could (as opposed to what you can do in Genshi): we would simply have to redefine the ''body'' block which contains all of the default structure (possibly reusing the content of that block by a call to `${ super() }`). In our case, neither jsearch.html nor jlayout.html redefine the `body` block, as they're happy with what jtheme does with it. Depending how one looks at it, it seems this approach is even more flexible than what we had in Genshi, as the end user template can decide which bits of the parent template it wants or not ("bottom-up" control), something that was not readily doable with Genshi ("top-down" control). Like what we did with Genshi, this scheme can be extended to support additional intermediate levels. We did that for the admin and the preference panels. Let's take for example the Logging admin panel, [source:cboos.git/trac/admin/templates/jadmin_logging.html jadmin_logging.html]: {{{#!html+jinja # extends "jadmin.html" <html> <head> <title> # block admintitle Logging # endblock admintitle # block adminpanel ... # block adminpanel }}} - it starts by extending the jadmin.html page - then it provides the `` and `` elements specific to that panel, more precisely the //admintitle// and //adminpanel// blocks (if some JavaScript or other resources are needed, the //head// could be redefined as well) The [source:cboos.git/trac/admin/templates/jadmin.html@jinja2 jadmin.html] page is similar to the jsearch.html page in that it extends the jlayout.html: {{{#!html+jinja # extends "jlayout.html" # block title Administration: # block admintitle # endblock admintitle ${ super() } # endblock # block content

Administration

# block adminpanel # endblock adminpanel
# endblock content }}} //See [1df4e05c/cboos.git] for the full conversion of admin.html -> jadmin.html and admin_logging.html -> jadmin_logging.html.// I omitted the discussion of the replacement for the `