Edgewall Software

Changes between Initial Version and Version 1 of TracDev/PortingFromGenshiToJinja/Example


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

extracted from Proposals/Jinja@16

Legend:

Unmodified
Added
Removed
Modified
  • TracDev/PortingFromGenshiToJinja/Example

    v1 v1  
     1= Conversion example
     2
     3Here 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]).
     4
     5As will become apparent, we mainly use [http://jinja.pocoo.org/docs/dev/templates/#line-statements line statements] for the Jinja2 control structures.
     6Note 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).
     7
     8{{{#!th style="max-width: 200px"
     9wiki_view.html (Genshi template)
     10}}}
     11{{{#!th style="max-width: 200px"
     12jwiki_view.html (Jinja2 template)
     13}}}
     14|------
     15||= ||= ||
     16{{{#!td
     17{{{#!html+genshi
     18<!--!  Copyright (C) 2006-2014 Edgewall Software
     19
     20  This software is licensed as described in the file COPYING, which
     21  you should have received as part of this distribution. The terms
     22  are also available at http://trac.edgewall.com/license.html.
     23
     24  This software consists of voluntary contributions made by many
     25  individuals. For the exact contribution history, see the revision
     26  history and logs, available at http://trac.edgewall.org/.
     27-->
     28}}}
     29}}}
     30{{{#!td
     31{{{#!html+jinja
     32{# Copyright (C) 2006-2014 Edgewall Software
     33
     34  This software is licensed as described in the file COPYING, which
     35  you should have received as part of this distribution. The terms
     36  are also available at http://trac.edgewall.com/license.html.
     37
     38  This software consists of voluntary contributions made by many
     39  individuals. For the exact contribution history, see the revision
     40  history and logs, available at http://trac.edgewall.org/.
     41#}
     42}}}
     43}}}
     44|-----------------------------------------------------------------------------
     45|||| Block comments ||
     46|-----------------------------------------------------------------------------
     47{{{#!td
     48{{{#!html+genshi
     49}}}
     50}}}
     51{{{#!td
     52{{{#!html+jinja
     53# extends "jlayout.html"
     54}}}
     55}}}
     56|-----------------------------------------------------------------------------
     57|||| 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. ||
     58|-----------------------------------------------------------------------------
     59{{{#!td
     60{{{#!html+genshi
     61<!DOCTYPE html
     62    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     63    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
     64}}}
     65}}}
     66{{{#!td
     67{{{#!html+jinja
     68<!DOCTYPE html>
     69}}}
     70}}}
     71|-----------------------------------------------------------------------------
     72|||| 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) ||
     73|-----------------------------------------------------------------------------
     74{{{#!td
     75{{{#!html+genshi
     76<html xmlns="http://www.w3.org/1999/xhtml"
     77      xmlns:py="http://genshi.edgewall.org/"
     78      xmlns:i18n="http://genshi.edgewall.org/i18n"
     79      xmlns:xi="http://www.w3.org/2001/XInclude"
     80      py:with="modify_perm = 'WIKI_MODIFY' in perm(page.resource);
     81               create_perm = 'WIKI_CREATE' in perm(page.resource);
     82               admin_perm = 'WIKI_ADMIN' in perm(page.resource);
     83               is_not_latest = page.exists and page.version != latest_version">
     84}}}
     85}}}
     86{{{#!td
     87{{{#!html+jinja
     88<html>
     89
     90}}}
     91}}}
     92|-----------------------------------------------------------------------------
     93{{{#!td
     94No need for exotic namespace declarations.
     95
     96Note 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.
     97}}}
     98|-----------------------------------------------------------------------------
     99{{{#!td
     100{{{#!html+genshi
     101  <xi:include href="layout.html" />
     102}}}
     103}}}
     104{{{#!td
     105{{{#!html+jinja
     106}}}
     107}}}
     108|-----------------------------------------------------------------------------
     109|||| See above, this will be converted to an initial `extends` statement. ||
     110|-----------------------------------------------------------------------------
     111{{{#!td
     112{{{#!html+genshi
     113  <head>
     114    <title py:if="title">$title</title>
     115}}}
     116}}}
     117{{{#!td
     118{{{#!html+jinja
     119  <head>
     120    <title>
     121      # block title
     122      #   if title:
     123      ${title} ${ super() }
     124      #   endif
     125      # endblock title
     126    </title>
     127
     128    # block head
     129    #  set modify_perm = 'WIKI_MODIFY' in perm(page.resource)
     130    #  set is_not_latest = page.exists and page.version != latest_version
     131
     132    ${ super() }
     133}}}
     134}}}
     135|-----------------------------------------------------------------------------
     136{{{#!td colspan=2
     137The content of the <title> element is placed in the ''title'' block,
     138followed by the content of the parent ''title'' block (`${ super() }`).
     139When closing the block, we reuse the name of the block.
     140
     141Note that while the only thing that really matters in an extending template
     142is the content of the blocks, we try hard to keep a correct HTML structure
     143for the whole template page, so that its HTML content can be validated in a
     144standalone way (cf. [#jinjachecker]). This is why we add the <html>, <head> and
     145<title> tags.
     146
     147We explained earlier why the title block has to be outside of the ''head'' block:
     148to avoid appearing twice, once as part of the head block, and the second time
     149when we call `${ super() }` in order to retrieve the content of the parent ''head''
     150block.
     151
     152Finally, we define some variables at the beginning of the block (corresponding to
     153some which were part of the `py:with` attribute in the `<html>` tag, in the Genshi
     154template).
     155}}}
     156|-----------------------------------------------------------------------------
     157{{{#!td
     158{{{#!html+genshi
     159    <meta py:if="version or page.author == 'trac'"
     160          name="ROBOTS" content="NOINDEX, NOFOLLOW" />
     161    <link py:if="modify_perm" rel="alternate"
     162          type="application/x-wiki"
     163          href="${href.wiki(page.name, action='edit',
     164                  version=page.version if is_not_latest else None)}"
     165          title="${_('Revert page to this version') if
     166                     is_not_latest else _('Edit this page')}"/>
     167}}}
     168}}}
     169{{{#!td
     170{{{#!html+jinja
     171    #   if version or page.author == 'trac':
     172    <meta name="ROBOTS" content="NOINDEX, NOFOLLOW" />
     173    #   endif
     174    #   if modify_perm:
     175    <link rel="alternate" type="application/x-wiki"
     176          href="${href.wiki(page.name, action='edit',
     177                version=page.version if is_not_latest)}"
     178          title="${_("Revert page to this version") if is_not_latest else
     179                                                  _("Edit this page")}"/>
     180    #   endif
     181}}}
     182}}}
     183|-----------------------------------------------------------------------------
     184|||| With Jinja2, the logic is now clearly distinct from the content. ||
     185|-----------------------------------------------------------------------------
     186{{{#!td
     187{{{#!html+genshi
     188    <script type="text/javascript">
     189      jQuery(document).ready(function($) {
     190        $("#content").find("h1,h2,h3,h4,h5,h6")
     191          .addAnchor(_("Link to this section"));
     192        $("#content").find(".wikianchor").each(function() {
     193          $(this).addAnchor(babel.format(_("Link to #%(id)s"),
     194                                         {id: $(this).attr('id')}));
     195        });
     196        $(".foldable").enableFolding(true, true);
     197      });
     198    </script>
     199}}}
     200}}}
     201{{{#!td
     202{{{#!html+jinja
     203    <script type="text/javascript">
     204      jQuery(document).ready(function($) {
     205        $("#content").find("h1,h2,h3,h4,h5,h6")
     206          .addAnchor(_("Link to this section"));
     207        $("#content").find(".wikianchor").each(function() {
     208        $(this).addAnchor(babel.format(_("Link to #%(id)s"), {
     209                                       id: $(this).attr('id')}));
     210        });
     211        $(".foldable").enableFolding(true, true);
     212      });
     213    </script>
     214}}}
     215}}}
     216|-----------------------------------------------------------------------------
     217|||| No changes here, except for some reformatting (we try to stay below 80 chars for nicer future side-by-side diffs). ||
     218|-----------------------------------------------------------------------------
     219{{{#!td
     220{{{#!html+genshi
     221  </head>
     222
     223}}}
     224}}}
     225{{{#!td
     226{{{#!html+jinja
     227    # endblock head
     228  </head>
     229
     230}}}
     231}}}
     232|-----------------------------------------------------------------------------
     233|||| We close the ''head'' block. ||
     234|-----------------------------------------------------------------------------
     235{{{#!td
     236{{{#!html+genshi
     237  <body>
     238    <div id="content" class="${classes('wiki', create=not page.exists)}">
     239
     240}}}
     241}}}
     242{{{#!td
     243{{{#!html+jinja
     244  <body>
     245    # block content
     246    #   set modify_perm = 'WIKI_MODIFY' in perm(page.resource)
     247    #   set create_perm = 'WIKI_CREATE' in perm(page.resource)
     248    #   set admin_perm = 'WIKI_ADMIN' in perm(page.resource)
     249    #   set is_not_latest = page.exists and page.version != latest_version
     250
     251    <div id="content" class="${classes('wiki', create=not page.exists)}">
     252
     253}}}
     254}}}
     255|-----------------------------------------------------------------------------
     256|||| We start the ''content'' block. As before with the ''header'' block, we define some variables at the beginning of the block. ||
     257|-----------------------------------------------------------------------------
     258{{{#!td
     259{{{#!html+genshi
     260      <py:if test="version">
     261        <br />
     262        <table id="info" summary="Revision info">
     263          <tr><th scope="row" i18n:msg="version, author, date">
     264             Version $page.version (modified by ${authorinfo(page.author)},
     265                                            ${pretty_dateinfo(page.time)})
     266             (<a href="${href.wiki(page.name, action='diff',
     267                                   version=page.version)}">diff</a>)
     268          </th></tr>
     269          <tr><td class="message" xml:space="preserve">
     270            ${wiki_to_html(context, page.comment or '--')}
     271          </td></tr>
     272        </table>
     273      </py:if>
     274}}}
     275}}}
     276{{{#!td
     277{{{#!html+jinja
     278      # if version:
     279      <br />
     280      <table id="info" summary="${_("Revision info")}">
     281        <tr><th scope="row">
     282            # with
     283            #   set version = page.version
     284            #   set author = authorinfo(page.author)
     285            #   set date = pretty_dateinfo(page.time)
     286            #   set hef = href.wiki(page.name, action='diff', version=page.version)
     287            #   trans version, author, date, href
     288
     289            Version ${version} (modified by ${author}, ${date})
     290            (<a href="${href}">diff</a>)
     291
     292            #   endtrans
     293            # endwith
     294        </th></tr>
     295        <tr><td class="message">
     296            ${wiki_to_html(context, page.comment or '--')}
     297        </td></tr>
     298      </table>
     299      # endif
     300
     301}}}
     302}}}
     303|-----------------------------------------------------------------------------
     304{{{#!td colspan=2
     305Here we illustrate the i18n changes.
     306
     307First, any sentence that corresponds to visible end-user text that should be
     308translated has to be marked somehow.
     309
     310One way is to use the standard i18n calls,
     311`_`, `ngettext()`, etc.
     312
     313The other way is to use `trans` blocks.
     314There are two big differences with their Genshi i18n equivalent:
     315 - variable substitutions can only be those of direct variables,
     316   no kind of expression is allowed, even as simple as attribute
     317   lookup
     318 - Jinja2, markup neutral as it is, will not do any substitutions
     319   on the markup found in a trans block; what would have ended
     320   in a Genshi bracketed expression `... [1:diff]` in the catalogs
     321   will now remain HTML markup: `... <a href="${href}">diff</a>`.
     322Note that one can use a `with` statement for breaking up the
     323assignments needed on multiple separate lines.
     324Smaller lists of variables can be placed on the `trans` line directly.
     325}}}
     326|-----------------------------------------------------------------------------
     327{{{#!td
     328{{{#!html+genshi
     329      <div class="wikipage searchable" py:choose="" xml:space="preserve">
     330        <py:when test="page.exists">
     331          <div id="wikipage" class="trac-content"
     332               py:content="wiki_to_html(context, text)" />
     333          <?python
     334            last_modification = (page.comment and
     335                 _('Version %(version)s by %(author)s: %(comment)s',
     336                   version=page.version, author=format_author(page.author),
     337                   comment=page.comment) or
     338                 _('Version %(version)s by %(author)s',
     339                   version=page.version, author=format_author(page.author)))
     340          ?>
     341          <div py:if="not version" class="trac-modifiedby">
     342            <span i18n:msg="reldate">
     343              <a href="${href.wiki(page.name, action='diff',
     344                                   version=page.version)}"
     345                 title="$last_modification">Last modified</a>
     346                 ${pretty_dateinfo(page.time)}
     347            </span>
     348            <span class="trac-print" i18n:msg="date">Last modified on
     349              ${format_datetime(page.time)}</span>
     350          </div>
     351        </py:when>
     352        <py:otherwise>
     353          <p i18n:msg="name">The page <strong>${name_of(page.resource)}</strong>
     354          does not exist. You can create it here.</p>
     355        </py:otherwise>
     356      </div>
     357}}}
     358}}}
     359{{{#!td
     360{{{#!html+jinja
     361      <div class="wikipage searchable">
     362        # if page.exists:
     363        <div id="wikipage" class="trac-content">${
     364          wiki_to_html(context, text)
     365          }</div>
     366        #   set last_modification = (page.comment and
     367        _('Version %(version)s by %(author)s: %(comment)s',
     368        version=page.version, author=format_author(page.author),
     369        comment=page.comment) or
     370        _('Version %(version)s by %(author)s',
     371        version=page.version, author=format_author(page.author)))
     372        #   if not version:
     373        <div class="trac-modifiedby">
     374          <span>
     375            # with
     376            #   set href = href.wiki(page.name, action='diff',
     377                                     version=page.version),
     378            #   set date = pretty_dateinfo(page.time)
     379            #   trans href, last_modification, date
     380
     381            <a href="${href}"
     382               title="${last_modification}">Last modified</a> ${date}
     383
     384            #   endtrans
     385            # endwith
     386          </span>
     387          <span class="trac-print">
     388            ${_("Last modified on %(date)s", date=format_datetime(page.time))}
     389          </span>
     390        </div>
     391        #   endif
     392        # else:
     393        <p>
     394          # trans name = name_of(page.resource)
     395
     396          The page <strong>${name}</strong> does not exist.
     397          You can create it here.
     398
     399          # endtrans
     400        </p>
     401        # endif
     402      </div>
     403}}}
     404}}}
     405|-----------------------------------------------------------------------------
     406|||| ||
     407|-----------------------------------------------------------------------------
     408{{{#!td
     409{{{#!html+genshi
     410      <xi:include href="list_of_attachments.html"
     411                  py:with="alist = attachments; compact = True;
     412                           foldable = True"/>
     413}}}
     414}}}
     415{{{#!td
     416{{{#!html+jinja
     417      # with
     418      #   set alist = attachments
     419      #   set compact = True
     420      #   set foldable = True
     421      #   include "jlist_of_attachments.html"
     422      # endwith
     423}}}
     424}}}
     425|-----------------------------------------------------------------------------
     426|||| Jinja2 includes also know about their context, so that make them kind of parametric. ||
     427|-----------------------------------------------------------------------------
     428{{{#!td
     429{{{#!html+genshi
     430      <py:with vars="delete_perm = 'WIKI_DELETE' in perm(page.resource);
     431                     rename_perm = 'WIKI_RENAME' in perm(page.resource)">
     432        <py:if test="modify_perm or create_perm or delete_perm">
     433          <div class="buttons">
     434            <py:if test="modify_perm or create_perm">
     435}}}
     436}}}
     437{{{#!td
     438{{{#!html+jinja
     439      # with
     440      #   set delete_perm = 'WIKI_DELETE' in perm(page.resource)
     441      #   set rename_perm = 'WIKI_RENAME' in perm(page.resource)
     442      #   if modify_perm or create_perm or delete_perm:
     443      <div class="buttons">
     444        #   if modify_perm or create_perm:
     445}}}
     446}}}
     447|-----------------------------------------------------------------------------
     448|||| 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. ||
     449|-----------------------------------------------------------------------------
     450{{{#!td
     451{{{#!html+genshi
     452              <form method="get" action="${href.wiki(page.name)}" id="modifypage">
     453                <div>
     454                  <input type="hidden" name="action" value="edit" />
     455                  <py:choose>
     456                    <py:when test="is_not_latest and modify_perm">
     457                      <input type="hidden" name="version" value="${page.version}"/>
     458                      <input type="submit" value="${_('Revert to this version')}"/>
     459                    </py:when>
     460                    <py:when test="page.exists and modify_perm">
     461                      <input type="submit" value="${_('Edit this page')}"
     462                             accesskey="e" />
     463                    </py:when>
     464                    <py:when test="not page.exists and create_perm">
     465                      <input type="submit" value="${_('Create this page')}"
     466                             accesskey="e" />
     467                      <div py:if="templates" id="template">
     468                        <label for="template">using the template:</label>
     469                        <select name="template">
     470                          <option selected="${not default_template in templates
     471                                              or None}"
     472                                  value="">(blank page)</option>
     473                          <option py:for="t in sorted(templates)" value="$t"
     474                                  selected="${t == default_template or None}"
     475                                  >$t</option>
     476                        </select>
     477                      </div>
     478                    </py:when>
     479                  </py:choose>
     480                </div>
     481              </form>
     482}}}
     483}}}
     484{{{#!td
     485{{{#!html+jinja
     486        <form method="get" action="${href.wiki(page.name)}" id="modifypage">
     487          <div>
     488            <input type="hidden" name="action" value="edit" />
     489            # if is_not_latest and modify_perm:
     490            <input type="hidden" name="version" value="${page.version}"/>
     491            <input type="submit" value="${_('Revert to this version')}"/>
     492            # elif page.exists and modify_perm:
     493            <input type="submit" value="${_('Edit this page')}"
     494                   accesskey="e" />
     495            # elif not page.exists and create_perm:
     496            <input type="submit" value="${_('Create this page')}"
     497                   accesskey="e" />
     498            #   if templates:
     499            <div id="template">
     500              <label for="template">${_("using the template:")}</label>
     501              <select name="template">
     502                <option ${{'selected': not default_template in templates
     503                        }|htmlattr}
     504                        value="">${_("(blank page)")}</option>
     505                # for t in sorted(templates):
     506                <option value="${t}"
     507                        ${{'selected': t == default_template
     508                        }|htmlattr}>${t}</option>
     509                # endfor
     510              </select>
     511            </div>
     512            #   endif
     513            # endif
     514          </div>
     515        </form>
     516
     517}}}
     518}}}
     519|-----------------------------------------------------------------------------
     520{{{#!td
     521A `<py:choose>` and its series of `<py:when>` translates very smoothly into a sequence of `if / elif` statements.
     522
     523Special care should be taken when producing attributes dynamically, as it's the
     524case here for `selected`. We use the `htmlattr` filter that takes care of
     525producing the right value for the attribute depending on its content: with
     526a truth value, we'll output `selected="selected"` otherwise we'll just omit
     527the parameter.
     528}}}
     529|-----------------------------------------------------------------------------
     530|||| ||
     531|-----------------------------------------------------------------------------
     532{{{#!td
     533{{{#!html+genshi
     534        <py:if test="page.exists">
     535          <xi:include href="attach_file_form.html"
     536                      py:with="alist = attachments"/>
     537        </py:if>
     538      </py:if>
     539}}}
     540}}}
     541{{{#!td
     542{{{#!html+jinja
     543        #   if page.exists:
     544        #     with alist = attachments
     545        #       include "jattach_file_form.html"
     546        #     endwith
     547        #   endif
     548        # endif
     549}}}
     550}}}
     551|-----------------------------------------------------------------------------
     552|||| We pass `attachments` as `alist` to the included template (a `j...` template, obviously). ||
     553|-----------------------------------------------------------------------------
     554{{{#!td
     555{{{#!html+genshi
     556            <form method="get" action="${href.wiki(page.name)}"
     557                  id="rename" py:if="page.exists and rename_perm">
     558              <div>
     559                <input type="hidden" name="action" value="rename" />
     560                <input type="submit" value="${_('Rename page')}" />
     561              </div>
     562            </form>
     563            <form method="get" action="${href.wiki(page.name)}"
     564                  id="delete" py:if="page.exists and delete_perm">
     565              <div>
     566                <input type="hidden" name="action" value="delete" />
     567                <input type="hidden" name="version" value="$page.version" />
     568                <py:if test="page.version == latest_version">
     569                  <input type="submit" name="delete_version"
     570                         value="${_('Delete this version')}" />
     571                </py:if>
     572                <input type="submit" value="${_('Delete page')}" />
     573              </div>
     574            </form>
     575          </div>
     576        </py:if>
     577      </py:with>
     578}}}
     579}}}
     580{{{#!td
     581{{{#!html+jinja
     582        # if page.exists and rename_perm:
     583        <form method="get" action="${href.wiki(page.name)}" id="rename">
     584          <div>
     585            <input type="hidden" name="action" value="rename" />
     586            <input type="submit" value="${_('Rename page')}" />
     587          </div>
     588        </form>
     589        # endif
     590        # if page.exists and delete_perm:
     591        <form method="get" action="${href.wiki(page.name)}" id="delete">
     592          <div>
     593            <input type="hidden" name="action" value="delete" />
     594            <input type="hidden" name="version" value="${page.version}" />
     595            # if page.version == latest_version:
     596            <input type="submit" name="delete_version" value="${_('Delete this version')}" />
     597            # endif
     598            <input type="submit" value="${_('Delete page')}" />
     599          </div>
     600        </form>
     601        # endif
     602      </div>
     603      # endif
     604      # endwith
     605
     606}}}
     607}}}
     608|-----------------------------------------------------------------------------
     609|||| ||
     610|-----------------------------------------------------------------------------
     611{{{#!td
     612{{{#!html+genshi
     613      <div class="wikipage searchable" py:if="not page.exists and higher">
     614        <p>You could also create the same page higher in the hierarchy:</p>
     615        <ul>
     616          <li py:for="markup in higher">${markup}</li>
     617        </ul>
     618      </div>
     619}}}
     620}}}
     621{{{#!td
     622{{{#!html+jinja
     623      # if not page.exists and higher:
     624      <div class="wikipage searchable">
     625        <p>You could also create the same page higher in the hierarchy:</p>
     626        <ul>
     627          # for markup in higher:
     628          <li>${markup}</li>
     629          # endfor
     630        </ul>
     631      </div>
     632      # endif
     633
     634}}}
     635}}}
     636|-----------------------------------------------------------------------------
     637|||| Here's the first use of the `for` statement. Straightforward. ||
     638|-----------------------------------------------------------------------------
     639{{{#!td
     640{{{#!html+genshi
     641      <div class="wikipage searchable" py:if="not page.exists and related">
     642        <p>The following pages have a name similar to this page,
     643           and may be related:</p>
     644        <ul>
     645          <li py:for="markup in related">${markup}</li>
     646        </ul>
     647      </div>
     648}}}
     649}}}
     650{{{#!td
     651{{{#!html+jinja
     652      # if not page.exists and related:
     653      <div class="wikipage searchable">
     654        <p>${_("The following pages have a name similar to this page, and may be related:")}</p>
     655        <ul>
     656          # for markup in related:
     657          <li>${markup}</li>
     658          # endif
     659        </ul>
     660      </div>
     661      # endif
     662}}}
     663}}}
     664|-----------------------------------------------------------------------------
     665|||| ||
     666|-----------------------------------------------------------------------------
     667{{{#!td
     668{{{#!html+genshi
     669    </div>
     670  </body>
     671</html>
     672}}}
     673}}}
     674{{{#!td
     675{{{#!html+jinja
     676    </div>
     677
     678    ${ super() }
     679
     680    # endblock content
     681  </body>
     682</html>
     683}}}
     684}}}
     685|-----------------------------------------------------------------------------
     686|||| 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. ||
     687|-----------------------------------------------------------------------------