Edgewall Software

Changes between Version 16 and Version 17 of TracDev/Proposals/Jinja


Ignore:
Timestamp:
Feb 25, 2016, 12:51:03 AM (8 years ago)
Author:
Christian Boos
Comment:

moved the example to PortingFromGenshiToJinja/Example

Legend:

Unmodified
Added
Removed
Modified
  • TracDev/Proposals/Jinja

    v16 v17  
    447447Some systematic comparison of the Genshi and Jinja2 template syntax can be seen in PortingFromGenshiToJinja#Changesinthetemplatesyntax (modeled after the old PortingFromClearSilverToGenshi page).
    448448
    449 === Conversion example
    450 
    451 Here we detail the conversion process of a sample Genshi template ([source:tags/trac-1.0.9/trac/wiki/templates/wiki_view.html wiki_view.html]) into the corresponding Jinja2 template ([source:cboos.git/trac/wiki/templates/jwiki_view.html jwiki_view.html]).
    452 
    453 As will become apparent, we mainly use [http://jinja.pocoo.org/docs/dev/templates/#line-statements line statements] for the Jinja2 control structures.
    454 Note that we have reformatted the Genshi template to use shorter line widths, so that both columns of the table below stay visible (a wide screen also helps).
    455 
    456 {{{#!th style="max-width: 200px"
    457 wiki_view.html (Genshi template)
    458 }}}
    459 {{{#!th style="max-width: 200px"
    460 jwiki_view.html (Jinja2 template)
    461 }}}
    462 |------
    463 ||= ||= ||
    464 {{{#!td
    465 {{{#!html+genshi
    466 <!--!  Copyright (C) 2006-2014 Edgewall Software
    467 
    468   This software is licensed as described in the file COPYING, which
    469   you should have received as part of this distribution. The terms
    470   are also available at http://trac.edgewall.com/license.html.
    471 
    472   This software consists of voluntary contributions made by many
    473   individuals. For the exact contribution history, see the revision
    474   history and logs, available at http://trac.edgewall.org/.
    475 -->
    476 }}}
    477 }}}
    478 {{{#!td
    479 {{{#!html+jinja
    480 {# Copyright (C) 2006-2014 Edgewall Software
    481 
    482   This software is licensed as described in the file COPYING, which
    483   you should have received as part of this distribution. The terms
    484   are also available at http://trac.edgewall.com/license.html.
    485 
    486   This software consists of voluntary contributions made by many
    487   individuals. For the exact contribution history, see the revision
    488   history and logs, available at http://trac.edgewall.org/.
    489 #}
    490 }}}
    491 }}}
    492 |-----------------------------------------------------------------------------
    493 |||| Block comments ||
    494 |-----------------------------------------------------------------------------
    495 {{{#!td
    496 {{{#!html+genshi
    497 }}}
    498 }}}
    499 {{{#!td
    500 {{{#!html+jinja
    501 # extends "jlayout.html"
    502 }}}
    503 }}}
    504 |-----------------------------------------------------------------------------
    505 |||| This is the equivalent of Genshi's `<py:include href="layout.html" />`. We have to do the extends before outputing any content on our own, otherwise it would show up in the final result. Once we made the `extends`, only the content written within //blocks// will matter. ||
    506 |-----------------------------------------------------------------------------
    507 {{{#!td
    508 {{{#!html+genshi
    509 <!DOCTYPE html
    510     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    511     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    512 }}}
    513 }}}
    514 {{{#!td
    515 {{{#!html+jinja
    516 <!DOCTYPE html>
    517 }}}
    518 }}}
    519 |-----------------------------------------------------------------------------
    520 |||| Let's directly make the jump to HTML5, while we're at it (I'm not sure what is the current state of the HTML5 support in Genshi, but it no longer matters) ||
    521 |-----------------------------------------------------------------------------
    522 {{{#!td
    523 {{{#!html+genshi
    524 <html xmlns="http://www.w3.org/1999/xhtml"
    525       xmlns:py="http://genshi.edgewall.org/"
    526       xmlns:i18n="http://genshi.edgewall.org/i18n"
    527       xmlns:xi="http://www.w3.org/2001/XInclude"
    528       py:with="modify_perm = 'WIKI_MODIFY' in perm(page.resource);
    529                create_perm = 'WIKI_CREATE' in perm(page.resource);
    530                admin_perm = 'WIKI_ADMIN' in perm(page.resource);
    531                is_not_latest = page.exists and page.version != latest_version">
    532 }}}
    533 }}}
    534 {{{#!td
    535 {{{#!html+jinja
    536 <html>
    537 
    538 }}}
    539 }}}
    540 |-----------------------------------------------------------------------------
    541 {{{#!td
    542 No need for exotic namespace declarations.
    543 
    544 Note that we can't use a `with` declaration that would encompass the ''head'' and ''content'' blocks we'll define in a moment, as this would force the generation of content twice, once by having the with block present in the template, and a second time because of the extends and the call of the blocks.
    545 }}}
    546 |-----------------------------------------------------------------------------
    547 {{{#!td
    548 {{{#!html+genshi
    549   <xi:include href="layout.html" />
    550 }}}
    551 }}}
    552 {{{#!td
    553 {{{#!html+jinja
    554 }}}
    555 }}}
    556 |-----------------------------------------------------------------------------
    557 |||| See above, this will be converted to an initial `extends` statement. ||
    558 |-----------------------------------------------------------------------------
    559 {{{#!td
    560 {{{#!html+genshi
    561   <head>
    562     <title py:if="title">$title</title>
    563 }}}
    564 }}}
    565 {{{#!td
    566 {{{#!html+jinja
    567   <head>
    568     <title>
    569       # block title
    570       #   if title:
    571       ${title} ${ super() }
    572       #   endif
    573       # endblock title
    574     </title>
    575 
    576     # block head
    577     #  set modify_perm = 'WIKI_MODIFY' in perm(page.resource)
    578     #  set is_not_latest = page.exists and page.version != latest_version
    579 
    580     ${ super() }
    581 }}}
    582 }}}
    583 |-----------------------------------------------------------------------------
    584 {{{#!td colspan=2
    585 The content of the <title> element is placed in the ''title'' block,
    586 followed by the content of the parent ''title'' block (`${ super() }`).
    587 When closing the block, we reuse the name of the block.
    588 
    589 Note that while the only thing that really matters in an extending template
    590 is the content of the blocks, we try hard to keep a correct HTML structure
    591 for the whole template page, so that its HTML content can be validated in a
    592 standalone way (cf. [#jinjachecker]). This is why we add the <html>, <head> and
    593 <title> tags.
    594 
    595 We explained earlier why the title block has to be outside of the ''head'' block:
    596 to avoid appearing twice, once as part of the head block, and the second time
    597 when we call `${ super() }` in order to retrieve the content of the parent ''head''
    598 block.
    599 
    600 Finally, we define some variables at the beginning of the block (corresponding to
    601 some which were part of the `py:with` attribute in the `<html>` tag, in the Genshi
    602 template).
    603 }}}
    604 |-----------------------------------------------------------------------------
    605 {{{#!td
    606 {{{#!html+genshi
    607     <meta py:if="version or page.author == 'trac'"
    608           name="ROBOTS" content="NOINDEX, NOFOLLOW" />
    609     <link py:if="modify_perm" rel="alternate"
    610           type="application/x-wiki"
    611           href="${href.wiki(page.name, action='edit',
    612                   version=page.version if is_not_latest else None)}"
    613           title="${_('Revert page to this version') if
    614                      is_not_latest else _('Edit this page')}"/>
    615 }}}
    616 }}}
    617 {{{#!td
    618 {{{#!html+jinja
    619     #   if version or page.author == 'trac':
    620     <meta name="ROBOTS" content="NOINDEX, NOFOLLOW" />
    621     #   endif
    622     #   if modify_perm:
    623     <link rel="alternate" type="application/x-wiki"
    624           href="${href.wiki(page.name, action='edit',
    625                 version=page.version if is_not_latest)}"
    626           title="${_("Revert page to this version") if is_not_latest else
    627                                                   _("Edit this page")}"/>
    628     #   endif
    629 }}}
    630 }}}
    631 |-----------------------------------------------------------------------------
    632 |||| With Jinja2, the logic is now clearly distinct from the content. ||
    633 |-----------------------------------------------------------------------------
    634 {{{#!td
    635 {{{#!html+genshi
    636     <script type="text/javascript">
    637       jQuery(document).ready(function($) {
    638         $("#content").find("h1,h2,h3,h4,h5,h6")
    639           .addAnchor(_("Link to this section"));
    640         $("#content").find(".wikianchor").each(function() {
    641           $(this).addAnchor(babel.format(_("Link to #%(id)s"),
    642                                          {id: $(this).attr('id')}));
    643         });
    644         $(".foldable").enableFolding(true, true);
    645       });
    646     </script>
    647 }}}
    648 }}}
    649 {{{#!td
    650 {{{#!html+jinja
    651     <script type="text/javascript">
    652       jQuery(document).ready(function($) {
    653         $("#content").find("h1,h2,h3,h4,h5,h6")
    654           .addAnchor(_("Link to this section"));
    655         $("#content").find(".wikianchor").each(function() {
    656         $(this).addAnchor(babel.format(_("Link to #%(id)s"), {
    657                                        id: $(this).attr('id')}));
    658         });
    659         $(".foldable").enableFolding(true, true);
    660       });
    661     </script>
    662 }}}
    663 }}}
    664 |-----------------------------------------------------------------------------
    665 |||| No changes here, except for some reformatting (we try to stay below 80 chars for nicer future side-by-side diffs). ||
    666 |-----------------------------------------------------------------------------
    667 {{{#!td
    668 {{{#!html+genshi
    669   </head>
    670 
    671 }}}
    672 }}}
    673 {{{#!td
    674 {{{#!html+jinja
    675     # endblock head
    676   </head>
    677 
    678 }}}
    679 }}}
    680 |-----------------------------------------------------------------------------
    681 |||| We close the ''head'' block. ||
    682 |-----------------------------------------------------------------------------
    683 {{{#!td
    684 {{{#!html+genshi
    685   <body>
    686     <div id="content" class="${classes('wiki', create=not page.exists)}">
    687 
    688 }}}
    689 }}}
    690 {{{#!td
    691 {{{#!html+jinja
    692   <body>
    693     # block content
    694     #   set modify_perm = 'WIKI_MODIFY' in perm(page.resource)
    695     #   set create_perm = 'WIKI_CREATE' in perm(page.resource)
    696     #   set admin_perm = 'WIKI_ADMIN' in perm(page.resource)
    697     #   set is_not_latest = page.exists and page.version != latest_version
    698 
    699     <div id="content" class="${classes('wiki', create=not page.exists)}">
    700 
    701 }}}
    702 }}}
    703 |-----------------------------------------------------------------------------
    704 |||| We start the ''content'' block. As before with the ''header'' block, we define some variables at the beginning of the block. ||
    705 |-----------------------------------------------------------------------------
    706 {{{#!td
    707 {{{#!html+genshi
    708       <py:if test="version">
    709         <br />
    710         <table id="info" summary="Revision info">
    711           <tr><th scope="row" i18n:msg="version, author, date">
    712              Version $page.version (modified by ${authorinfo(page.author)},
    713                                             ${pretty_dateinfo(page.time)})
    714              (<a href="${href.wiki(page.name, action='diff',
    715                                    version=page.version)}">diff</a>)
    716           </th></tr>
    717           <tr><td class="message" xml:space="preserve">
    718             ${wiki_to_html(context, page.comment or '--')}
    719           </td></tr>
    720         </table>
    721       </py:if>
    722 }}}
    723 }}}
    724 {{{#!td
    725 {{{#!html+jinja
    726       # if version:
    727       <br />
    728       <table id="info" summary="${_("Revision info")}">
    729         <tr><th scope="row">
    730             # with
    731             #   set version = page.version
    732             #   set author = authorinfo(page.author)
    733             #   set date = pretty_dateinfo(page.time)
    734             #   set hef = href.wiki(page.name, action='diff', version=page.version)
    735             #   trans version, author, date, href
    736 
    737             Version ${version} (modified by ${author}, ${date})
    738             (<a href="${href}">diff</a>)
    739 
    740             #   endtrans
    741             # endwith
    742         </th></tr>
    743         <tr><td class="message">
    744             ${wiki_to_html(context, page.comment or '--')}
    745         </td></tr>
    746       </table>
    747       # endif
    748 
    749 }}}
    750 }}}
    751 |-----------------------------------------------------------------------------
    752 {{{#!td colspan=2
    753 Here we illustrate the i18n changes.
    754 
    755 First, any sentence that corresponds to visible end-user text that should be
    756 translated has to be marked somehow.
    757 
    758 One way is to use the standard i18n calls,
    759 `_`, `ngettext()`, etc.
    760 
    761 The other way is to use `trans` blocks.
    762 There are two big differences with their Genshi i18n equivalent:
    763  - variable substitutions can only be those of direct variables,
    764    no kind of expression is allowed, even as simple as attribute
    765    lookup
    766  - Jinja2, markup neutral as it is, will not do any substitutions
    767    on the markup found in a trans block; what would have ended
    768    in a Genshi bracketed expression `... [1:diff]` in the catalogs
    769    will now remain HTML markup: `... <a href="${href}">diff</a>`.
    770 Note that one can use a `with` statement for breaking up the
    771 assignments needed on multiple separate lines.
    772 Smaller lists of variables can be placed on the `trans` line directly.
    773 }}}
    774 |-----------------------------------------------------------------------------
    775 {{{#!td
    776 {{{#!html+genshi
    777       <div class="wikipage searchable" py:choose="" xml:space="preserve">
    778         <py:when test="page.exists">
    779           <div id="wikipage" class="trac-content"
    780                py:content="wiki_to_html(context, text)" />
    781           <?python
    782             last_modification = (page.comment and
    783                  _('Version %(version)s by %(author)s: %(comment)s',
    784                    version=page.version, author=format_author(page.author),
    785                    comment=page.comment) or
    786                  _('Version %(version)s by %(author)s',
    787                    version=page.version, author=format_author(page.author)))
    788           ?>
    789           <div py:if="not version" class="trac-modifiedby">
    790             <span i18n:msg="reldate">
    791               <a href="${href.wiki(page.name, action='diff',
    792                                    version=page.version)}"
    793                  title="$last_modification">Last modified</a>
    794                  ${pretty_dateinfo(page.time)}
    795             </span>
    796             <span class="trac-print" i18n:msg="date">Last modified on
    797               ${format_datetime(page.time)}</span>
    798           </div>
    799         </py:when>
    800         <py:otherwise>
    801           <p i18n:msg="name">The page <strong>${name_of(page.resource)}</strong>
    802           does not exist. You can create it here.</p>
    803         </py:otherwise>
    804       </div>
    805 }}}
    806 }}}
    807 {{{#!td
    808 {{{#!html+jinja
    809       <div class="wikipage searchable">
    810         # if page.exists:
    811         <div id="wikipage" class="trac-content">${
    812           wiki_to_html(context, text)
    813           }</div>
    814         #   set last_modification = (page.comment and
    815         _('Version %(version)s by %(author)s: %(comment)s',
    816         version=page.version, author=format_author(page.author),
    817         comment=page.comment) or
    818         _('Version %(version)s by %(author)s',
    819         version=page.version, author=format_author(page.author)))
    820         #   if not version:
    821         <div class="trac-modifiedby">
    822           <span>
    823             # with
    824             #   set href = href.wiki(page.name, action='diff',
    825                                      version=page.version),
    826             #   set date = pretty_dateinfo(page.time)
    827             #   trans href, last_modification, date
    828 
    829             <a href="${href}"
    830                title="${last_modification}">Last modified</a> ${date}
    831 
    832             #   endtrans
    833             # endwith
    834           </span>
    835           <span class="trac-print">
    836             ${_("Last modified on %(date)s", date=format_datetime(page.time))}
    837           </span>
    838         </div>
    839         #   endif
    840         # else:
    841         <p>
    842           # trans name = name_of(page.resource)
    843 
    844           The page <strong>${name}</strong> does not exist.
    845           You can create it here.
    846 
    847           # endtrans
    848         </p>
    849         # endif
    850       </div>
    851 }}}
    852 }}}
    853 |-----------------------------------------------------------------------------
    854 |||| ||
    855 |-----------------------------------------------------------------------------
    856 {{{#!td
    857 {{{#!html+genshi
    858       <xi:include href="list_of_attachments.html"
    859                   py:with="alist = attachments; compact = True;
    860                            foldable = True"/>
    861 }}}
    862 }}}
    863 {{{#!td
    864 {{{#!html+jinja
    865       # with
    866       #   set alist = attachments
    867       #   set compact = True
    868       #   set foldable = True
    869       #   include "jlist_of_attachments.html"
    870       # endwith
    871 }}}
    872 }}}
    873 |-----------------------------------------------------------------------------
    874 |||| Jinja2 includes also know about their context, so that make them kind of parametric. ||
    875 |-----------------------------------------------------------------------------
    876 {{{#!td
    877 {{{#!html+genshi
    878       <py:with vars="delete_perm = 'WIKI_DELETE' in perm(page.resource);
    879                      rename_perm = 'WIKI_RENAME' in perm(page.resource)">
    880         <py:if test="modify_perm or create_perm or delete_perm">
    881           <div class="buttons">
    882             <py:if test="modify_perm or create_perm">
    883 }}}
    884 }}}
    885 {{{#!td
    886 {{{#!html+jinja
    887       # with
    888       #   set delete_perm = 'WIKI_DELETE' in perm(page.resource)
    889       #   set rename_perm = 'WIKI_RENAME' in perm(page.resource)
    890       #   if modify_perm or create_perm or delete_perm:
    891       <div class="buttons">
    892         #   if modify_perm or create_perm:
    893 }}}
    894 }}}
    895 |-----------------------------------------------------------------------------
    896 |||| Again, even when there's a lot of control lines, it's a bit easier to immediately spot the actual content in Jinja2 templates, due to the clearer syntactic difference. ||
    897 |-----------------------------------------------------------------------------
    898 {{{#!td
    899 {{{#!html+genshi
    900               <form method="get" action="${href.wiki(page.name)}" id="modifypage">
    901                 <div>
    902                   <input type="hidden" name="action" value="edit" />
    903                   <py:choose>
    904                     <py:when test="is_not_latest and modify_perm">
    905                       <input type="hidden" name="version" value="${page.version}"/>
    906                       <input type="submit" value="${_('Revert to this version')}"/>
    907                     </py:when>
    908                     <py:when test="page.exists and modify_perm">
    909                       <input type="submit" value="${_('Edit this page')}"
    910                              accesskey="e" />
    911                     </py:when>
    912                     <py:when test="not page.exists and create_perm">
    913                       <input type="submit" value="${_('Create this page')}"
    914                              accesskey="e" />
    915                       <div py:if="templates" id="template">
    916                         <label for="template">using the template:</label>
    917                         <select name="template">
    918                           <option selected="${not default_template in templates
    919                                               or None}"
    920                                   value="">(blank page)</option>
    921                           <option py:for="t in sorted(templates)" value="$t"
    922                                   selected="${t == default_template or None}"
    923                                   >$t</option>
    924                         </select>
    925                       </div>
    926                     </py:when>
    927                   </py:choose>
    928                 </div>
    929               </form>
    930 }}}
    931 }}}
    932 {{{#!td
    933 {{{#!html+jinja
    934         <form method="get" action="${href.wiki(page.name)}" id="modifypage">
    935           <div>
    936             <input type="hidden" name="action" value="edit" />
    937             # if is_not_latest and modify_perm:
    938             <input type="hidden" name="version" value="${page.version}"/>
    939             <input type="submit" value="${_('Revert to this version')}"/>
    940             # elif page.exists and modify_perm:
    941             <input type="submit" value="${_('Edit this page')}"
    942                    accesskey="e" />
    943             # elif not page.exists and create_perm:
    944             <input type="submit" value="${_('Create this page')}"
    945                    accesskey="e" />
    946             #   if templates:
    947             <div id="template">
    948               <label for="template">${_("using the template:")}</label>
    949               <select name="template">
    950                 <option ${{'selected': not default_template in templates
    951                         }|htmlattr}
    952                         value="">${_("(blank page)")}</option>
    953                 # for t in sorted(templates):
    954                 <option value="${t}"
    955                         ${{'selected': t == default_template
    956                         }|htmlattr}>${t}</option>
    957                 # endfor
    958               </select>
    959             </div>
    960             #   endif
    961             # endif
    962           </div>
    963         </form>
    964 
    965 }}}
    966 }}}
    967 |-----------------------------------------------------------------------------
    968 {{{#!td
    969 A `<py:choose>` and its series of `<py:when>` translates very smoothly into a sequence of `if / elif` statements.
    970 
    971 Special care should be taken when producing attributes dynamically, as it's the
    972 case here for `selected`. We use the `htmlattr` filter that takes care of
    973 producing the right value for the attribute depending on its content: with
    974 a truth value, we'll output `selected="selected"` otherwise we'll just omit
    975 the parameter.
    976 }}}
    977 |-----------------------------------------------------------------------------
    978 |||| ||
    979 |-----------------------------------------------------------------------------
    980 {{{#!td
    981 {{{#!html+genshi
    982         <py:if test="page.exists">
    983           <xi:include href="attach_file_form.html"
    984                       py:with="alist = attachments"/>
    985         </py:if>
    986       </py:if>
    987 }}}
    988 }}}
    989 {{{#!td
    990 {{{#!html+jinja
    991         #   if page.exists:
    992         #     with alist = attachments
    993         #       include "jattach_file_form.html"
    994         #     endwith
    995         #   endif
    996         # endif
    997 }}}
    998 }}}
    999 |-----------------------------------------------------------------------------
    1000 |||| We pass `attachments` as `alist` to the included template (a `j...` template, obviously). ||
    1001 |-----------------------------------------------------------------------------
    1002 {{{#!td
    1003 {{{#!html+genshi
    1004             <form method="get" action="${href.wiki(page.name)}"
    1005                   id="rename" py:if="page.exists and rename_perm">
    1006               <div>
    1007                 <input type="hidden" name="action" value="rename" />
    1008                 <input type="submit" value="${_('Rename page')}" />
    1009               </div>
    1010             </form>
    1011             <form method="get" action="${href.wiki(page.name)}"
    1012                   id="delete" py:if="page.exists and delete_perm">
    1013               <div>
    1014                 <input type="hidden" name="action" value="delete" />
    1015                 <input type="hidden" name="version" value="$page.version" />
    1016                 <py:if test="page.version == latest_version">
    1017                   <input type="submit" name="delete_version"
    1018                          value="${_('Delete this version')}" />
    1019                 </py:if>
    1020                 <input type="submit" value="${_('Delete page')}" />
    1021               </div>
    1022             </form>
    1023           </div>
    1024         </py:if>
    1025       </py:with>
    1026 }}}
    1027 }}}
    1028 {{{#!td
    1029 {{{#!html+jinja
    1030         # if page.exists and rename_perm:
    1031         <form method="get" action="${href.wiki(page.name)}" id="rename">
    1032           <div>
    1033             <input type="hidden" name="action" value="rename" />
    1034             <input type="submit" value="${_('Rename page')}" />
    1035           </div>
    1036         </form>
    1037         # endif
    1038         # if page.exists and delete_perm:
    1039         <form method="get" action="${href.wiki(page.name)}" id="delete">
    1040           <div>
    1041             <input type="hidden" name="action" value="delete" />
    1042             <input type="hidden" name="version" value="${page.version}" />
    1043             # if page.version == latest_version:
    1044             <input type="submit" name="delete_version" value="${_('Delete this version')}" />
    1045             # endif
    1046             <input type="submit" value="${_('Delete page')}" />
    1047           </div>
    1048         </form>
    1049         # endif
    1050       </div>
    1051       # endif
    1052       # endwith
    1053 
    1054 }}}
    1055 }}}
    1056 |-----------------------------------------------------------------------------
    1057 |||| ||
    1058 |-----------------------------------------------------------------------------
    1059 {{{#!td
    1060 {{{#!html+genshi
    1061       <div class="wikipage searchable" py:if="not page.exists and higher">
    1062         <p>You could also create the same page higher in the hierarchy:</p>
    1063         <ul>
    1064           <li py:for="markup in higher">${markup}</li>
    1065         </ul>
    1066       </div>
    1067 }}}
    1068 }}}
    1069 {{{#!td
    1070 {{{#!html+jinja
    1071       # if not page.exists and higher:
    1072       <div class="wikipage searchable">
    1073         <p>You could also create the same page higher in the hierarchy:</p>
    1074         <ul>
    1075           # for markup in higher:
    1076           <li>${markup}</li>
    1077           # endfor
    1078         </ul>
    1079       </div>
    1080       # endif
    1081 
    1082 }}}
    1083 }}}
    1084 |-----------------------------------------------------------------------------
    1085 |||| Here's the first use of the `for` statement. Straightforward. ||
    1086 |-----------------------------------------------------------------------------
    1087 {{{#!td
    1088 {{{#!html+genshi
    1089       <div class="wikipage searchable" py:if="not page.exists and related">
    1090         <p>The following pages have a name similar to this page,
    1091            and may be related:</p>
    1092         <ul>
    1093           <li py:for="markup in related">${markup}</li>
    1094         </ul>
    1095       </div>
    1096 }}}
    1097 }}}
    1098 {{{#!td
    1099 {{{#!html+jinja
    1100       # if not page.exists and related:
    1101       <div class="wikipage searchable">
    1102         <p>${_("The following pages have a name similar to this page, and may be related:")}</p>
    1103         <ul>
    1104           # for markup in related:
    1105           <li>${markup}</li>
    1106           # endif
    1107         </ul>
    1108       </div>
    1109       # endif
    1110 }}}
    1111 }}}
    1112 |-----------------------------------------------------------------------------
    1113 |||| ||
    1114 |-----------------------------------------------------------------------------
    1115 {{{#!td
    1116 {{{#!html+genshi
    1117     </div>
    1118   </body>
    1119 </html>
    1120 }}}
    1121 }}}
    1122 {{{#!td
    1123 {{{#!html+jinja
    1124     </div>
    1125 
    1126     ${ super() }
    1127 
    1128     # endblock content
    1129   </body>
    1130 </html>
    1131 }}}
    1132 }}}
    1133 |-----------------------------------------------------------------------------
    1134 |||| Once we're done with the `<div id="content">`, we also call `${ super() }` to re-insert the parent content of the ''content'' block, i.e. the altlinks div and the late_links/late_scripts logic. ||
    1135 |-----------------------------------------------------------------------------
     449See also PortingFromGenshiToJinja/Example for a full example presented side-by-side.
    1136450
    1137451=== `jinjachecker`