Changes between Version 47 and Version 48 of TracDev/PortingFromGenshiToJinja
- Timestamp:
- Apr 8, 2020, 11:22:17 PM (4 years ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
TracDev/PortingFromGenshiToJinja
v47 v48 12 12 In the last part of this document, we try to cover all the Genshi features used by Trac and present their Jinja2 equivalent. Whenever possible, we tried to minimize these differences by customizing the Jinja2 syntax. For example, we use `${...}` for variable expansion, like Genshi does, instead of `{{...}}`. Another aspect of our usage convention is that we favor [#ifthenelse line statements] over `{% ... %}`. So even someone familiar with the "default" Jinja2 syntax should glance through this document to see how "we" use Jinja2, as summarized in the table below. 13 13 14 Note that Genshi will be supported concurrently with Jinja2 only for a short while, for the 1.3.x development period and for the 1.4-stable period. This support will beremoved in Trac [milestone:1.5.1]. If for some reason you're stuck to having to support Genshi templates, you'll have to stick to Trac 1.2.x or 1.3.1. But you really should make the transition effort as Jinja2 templates are 5-10x faster than their Genshi equivalent, for only a 1/5th of their cost in memory usage.14 Note that Genshi will be supported concurrently with Jinja2 only for a short while, for the 1.3.x development period and for the 1.4-stable period. This support is removed in Trac [milestone:1.5.1]. If for some reason you're stuck to having to support Genshi templates, you'll have to stick to Trac 1.2.x or 1.3.1. But you really should make the transition effort as Jinja2 templates are 5-10x faster than their Genshi equivalent, for only a 1/5th of their cost in memory usage. 15 15 16 16 == Examples! … … 21 21 Let's first take a simple full-contained example from the Trac source, the simple index.html / jindex.html templates. 22 22 23 - Genshi [source: sandbox/genshi/templates/index.html@3728index.html]:23 - Genshi [source:tags/trac-1.2/genshi/templates/index.html index.html]: 24 24 {{{#!html+genshi 25 25 <!DOCTYPE html … … 47 47 </html> 48 48 }}} 49 - Jinja [source: cboos.git/trac/templates/index.html@jinja2-trunk-r15379index.html]:49 - Jinja [source:/tags/trac-1.4/trac/templates/index.html index.html]: 50 50 {{{#!html+jinja 51 51 <!DOCTYPE html> … … 76 76 In this small example, there's no common Trac layout used (as the index is a bit special). 77 77 For how a "normal" template looks like, see for example 78 [source: cboos.git/trac/templates/diff_view.html@jinja2-trunk-r15379 diff_view.html], another small template. The generic templates in [source:cboos.git/trac/templates@jinja2-trunk-r15379 trac/templates] are also interesting because we still have their Genshi equivalent close by, in [source:cboos.git/trac/templates/genshi@jinja2-trunk-r15379trac/templates/genshi], so you can easily compare them if you're stuck during the migration of your own templates.78 [source:tags/trac-1.4/trac/templates/diff_view.html diff_view.html], another small template. The generic templates in [source:tags/trac-1.4/trac/templates trac/templates] are also interesting because we still have their Genshi equivalent close by, in [source:tags/trac-1.4/trac/templates/genshi trac/templates/genshi], so you can easily compare them if you're stuck during the migration of your own templates. 79 79 80 80 Note that a Jinja2 .html template can usually be rendered directly in the browser, to have a rough idea about how it will look like: … … 101 101 **Never** go back to the bad old habits from the ClearSilver time, were sometimes the logic in those templates took advantage of the lack of well-formedness constraints, e.g. by conditionally inserting end/start pairs of tags to split sequences. Such templates were hard to maintain, and you always have cleaner alternatives. 102 102 103 The [ ./Checker jinjachecker] tool should also help you maintain well-formed templates by stripping off Jinja2 expressions and line statements before attempting to XML validate the document ([pypi:lxml] should be installed for this feature).103 The [JinjaChecker jinjachecker] tool should also help you maintain well-formed templates by stripping off Jinja2 expressions and line statements before attempting to XML validate the document ([pypi:lxml] should be installed for this feature). 104 104 105 105 … … 110 110 Instead of the Genshi way of including a template containing filters, the Jinja2 way follows an "object oriented" approach, with inheritance and overriders. Consider that some named sections (or "blocks") of the base template are similar to "methods", imagine that you only have to "subclass" this base template and "reimplement" the overridable methods with your specific content, and there you have it. 111 111 112 More specifically, you'll have to "extend" the [source:cboos.git/trac/templates/layout.html@jinja2-trunk-r15379 layout.html] template, and redefine the "head", and "content" blocks if needed. 113 114 All the details are available in HtmlTemplates#Jinja2architecture, including a walkthrough for the specific example of the [source:cboos.git/trac/search/templates/search.html@jinja2-trunk-r15379 search.html] template. 115 116 For the search.html example we focus on the //structure// of the templates, the //include// relationship and the decomposition in //blocks//. 117 118 119 But we also have a complete conversion [./Example example], which displays the Genshi wiki_view.html template and the Jinja2 wiki_view.html template side-by-side, along with comments explaining the conversion choices. 112 More specifically, you'll have to "extend" the [source:/tags/trac-1.4/trac/templates/layout.html layout.html] template, and redefine the "head", and "content" blocks if needed. 113 114 All the details are available in HtmlTemplates#Jinja2architecture, including a walkthrough for the specific example of the [source:tags/trac-1.4/trac/search/templates/search.html search.html] template. 115 116 For the `search.html` example we focus on the //structure// of the templates, the //include// relationship and the decomposition in //blocks//. 117 118 But we also have a complete conversion [./Example example], which displays the Genshi wiki_view.html template and the Jinja2 `wiki_view.html` template side-by-side, along with comments explaining the conversion choices. 120 119 121 120 … … 172 171 `render_fragment` can be used instead. It returns a `Markup` string when generating output for the web (`text=False`) or an `unicode` string when generating plain text output (`text=True`). 173 172 174 //See [source: cboos.git/trac/ticket/query.py@jinja2-trunk-r15379#L1423Ticket Query] macro (''table'' mode)//173 //See [source:tags/trac-1.4/trac/ticket/query.py#L1455 Ticket Query] macro (''table'' mode)// 175 174 176 175 When the fragment needs to be sent to the client, there's still a better choice than `render_template`, it's `generate_fragment`, as it won't impose as much overhead on the data dictionary as `render_template`. It's best suited for responding to XHRs: … … 185 184 186 185 There's even a lower-level public API in `Chrome` for generating content using Jinja2 templates, which provides even greater control. 187 //See the [source: cboos.git/trac/ticket/notification.py@jinja2-trunk-r15379#L311 rendering of the ticket change notification e-mail]//186 //See the [source:tags/trac-1.4/trac/ticket/notification.py#L311 rendering of the ticket change notification e-mail]// 188 187 189 188 See the API documentation for further details. … … 194 193 Genshi provided a nice Python API for programmatically building (X)HTML elements. This consisted of the `Fragment`, `Element` and the `tag` builder, all from the `genshi.builder` module: 195 194 {{{#!python 196 from genshi import Fragment, Element, tag195 from genshi import Element, Fragment, tag 197 196 }}} 198 197 This has now been replaced by an equivalent API which lives in `trac.util.html`, so the above import should be replaced with: 199 198 {{{#!python 200 from trac.util.html import Fragment, Element, tag199 from trac.util.html import Element, Fragment, tag 201 200 }}} 202 201 Note that the `html` symbol from `trac.util.html` which used to be a alias to `genshi.builder.tag` is now naturally an alias to `trac.util.html.tag`. … … 251 250 }}} 252 251 253 The `jmacros` in the above corresponds to the [source: cboos.git/trac/templates/macros.html@jinja2-trunk-r15371trac/templates/macros.html] default macros, and this file is included by default (in `layout.html`), so you don't have to bother to include it yourself, as long as your template extends `layout.html`.252 The `jmacros` in the above corresponds to the [source:tags/trac-1.4/trac/templates/macros.html trac/templates/macros.html] default macros, and this file is included by default (in `layout.html`), so you don't have to bother to include it yourself, as long as your template extends `layout.html`. 254 253 255 254 For the accessibility key, it's also quite simple: instead of hard-coding the key as an `accesskey="e"` attribute, simply use the `accesskey('e')` function call, it will know if it has to produce the attribute or not depending on the current user preferences. … … 279 278 - instead of using the Transform filter API to append/prepend the content to some place in the input stream identified by XPath expressions, use jQuery DOM manipulation API 280 279 281 We'll discuss the specific example of the ticket [source: cboos.git/tracopt/ticket/deleter.py deleter].280 We'll discuss the specific example of the ticket [source:tags/trac-1.4/tracopt/ticket/deleter.py deleter]. 282 281 283 282 ==== Implement `ITemplateProvider.get_htdocs_dirs` to be able to provide extra JavaScript code … … 381 380 """ 382 381 }}} 383 (`captioned_button(req, symbol, text)` is a small Python utility function, which is trivial to adapt in JavaScript; note however that it's for this part of the logic that we needed to pass `req.session.get('ui.use_symbols')` from Python to JavaScript's `ui.use_symbols` via the call to `add_script_data` in 2.)382 `captioned_button(req, symbol, text)` is a small Python utility function, which is trivial to adapt in JavaScript; note however that it's for this part of the logic that we needed to pass `req.session.get('ui.use_symbols')` from Python to JavaScript's `ui.use_symbols` via the call to `add_script_data` in 2. 384 383 \\ \\ 385 384 The second Python helper method: … … 461 460 The basics remain the same, we use Babel for extracting the source strings from Jinja2 templates and for performing the translations at runtime. One notable differences though is how the //domain// used by the plugin specific catalogs is specified. With Genshi, the templates themselves contained a directive which specified the name of the domain, while Jinja2 doesn't provide this facility. It is therefore up to the controller to do this job, and we use for that the //metadata// `dict` returned by [#IRequestHandler IRequestHandler.process_request]. 462 461 463 The specification for the extraction is slightly more verbose than it use to bewith Genshi. As we use a syntax for the Jinja2 templates which is different than the default ([#TheJinja2syntaxusedbyTrac see below]), it has to be specified for the extractor as well.462 The specification for the extraction is slightly more verbose than with Genshi. As we use a syntax for the Jinja2 templates which is different than the default ([#TheJinja2syntaxusedbyTrac see below]), it has to be specified for the extractor as well. 464 463 465 464 In what follows, we'll take the example of the SpamFilter plugin.