|
- <?xml version="1.0" encoding="utf-8" ?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head>
- <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
- <meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
- <title>Twig Template Engine Specification</title>
- <style type="text/css">
-
- /*
- :Author: David Goodger
- :Contact: goodger@users.sourceforge.net
- :Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
- :Revision: $Revision: 4224 $
- :Copyright: This stylesheet has been placed in the public domain.
-
- Default cascading style sheet for the HTML output of Docutils.
-
- See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
- customize this style sheet.
- */
-
- /* used to remove borders from tables and images */
- .borderless, table.borderless td, table.borderless th {
- border: 0 }
-
- table.borderless td, table.borderless th {
- /* Override padding for "table.docutils td" with "! important".
- The right padding separates the table cells. */
- padding: 0 0.5em 0 0 ! important }
-
- .first {
- /* Override more specific margin styles with "! important". */
- margin-top: 0 ! important }
-
- .last, .with-subtitle {
- margin-bottom: 0 ! important }
-
- .hidden {
- display: none }
-
- a.toc-backref {
- text-decoration: none ;
- color: black }
-
- blockquote.epigraph {
- margin: 2em 5em ; }
-
- dl.docutils dd {
- margin-bottom: 0.5em }
-
- /* Uncomment (and remove this text!) to get bold-faced definition list terms
- dl.docutils dt {
- font-weight: bold }
- */
-
- div.abstract {
- margin: 2em 5em }
-
- div.abstract p.topic-title {
- font-weight: bold ;
- text-align: center }
-
- div.admonition, div.attention, div.caution, div.danger, div.error,
- div.hint, div.important, div.note, div.tip, div.warning {
- margin: 2em ;
- border: medium outset ;
- padding: 1em }
-
- div.admonition p.admonition-title, div.hint p.admonition-title,
- div.important p.admonition-title, div.note p.admonition-title,
- div.tip p.admonition-title {
- font-weight: bold ;
- font-family: sans-serif }
-
- div.attention p.admonition-title, div.caution p.admonition-title,
- div.danger p.admonition-title, div.error p.admonition-title,
- div.warning p.admonition-title {
- color: red ;
- font-weight: bold ;
- font-family: sans-serif }
-
- /* Uncomment (and remove this text!) to get reduced vertical space in
- compound paragraphs.
- div.compound .compound-first, div.compound .compound-middle {
- margin-bottom: 0.5em }
-
- div.compound .compound-last, div.compound .compound-middle {
- margin-top: 0.5em }
- */
-
- div.dedication {
- margin: 2em 5em ;
- text-align: center ;
- font-style: italic }
-
- div.dedication p.topic-title {
- font-weight: bold ;
- font-style: normal }
-
- div.figure {
- margin-left: 2em ;
- margin-right: 2em }
-
- div.footer, div.header {
- clear: both;
- font-size: smaller }
-
- div.line-block {
- display: block ;
- margin-top: 1em ;
- margin-bottom: 1em }
-
- div.line-block div.line-block {
- margin-top: 0 ;
- margin-bottom: 0 ;
- margin-left: 1.5em }
-
- div.sidebar {
- margin-left: 1em ;
- border: medium outset ;
- padding: 1em ;
- background-color: #ffffee ;
- width: 40% ;
- float: right ;
- clear: right }
-
- div.sidebar p.rubric {
- font-family: sans-serif ;
- font-size: medium }
-
- div.system-messages {
- margin: 5em }
-
- div.system-messages h1 {
- color: red }
-
- div.system-message {
- border: medium outset ;
- padding: 1em }
-
- div.system-message p.system-message-title {
- color: red ;
- font-weight: bold }
-
- div.topic {
- margin: 2em }
-
- h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
- h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
- margin-top: 0.4em }
-
- h1.title {
- text-align: center }
-
- h2.subtitle {
- text-align: center }
-
- hr.docutils {
- width: 75% }
-
- img.align-left {
- clear: left }
-
- img.align-right {
- clear: right }
-
- ol.simple, ul.simple {
- margin-bottom: 1em }
-
- ol.arabic {
- list-style: decimal }
-
- ol.loweralpha {
- list-style: lower-alpha }
-
- ol.upperalpha {
- list-style: upper-alpha }
-
- ol.lowerroman {
- list-style: lower-roman }
-
- ol.upperroman {
- list-style: upper-roman }
-
- p.attribution {
- text-align: right ;
- margin-left: 50% }
-
- p.caption {
- font-style: italic }
-
- p.credits {
- font-style: italic ;
- font-size: smaller }
-
- p.label {
- white-space: nowrap }
-
- p.rubric {
- font-weight: bold ;
- font-size: larger ;
- color: maroon ;
- text-align: center }
-
- p.sidebar-title {
- font-family: sans-serif ;
- font-weight: bold ;
- font-size: larger }
-
- p.sidebar-subtitle {
- font-family: sans-serif ;
- font-weight: bold }
-
- p.topic-title {
- font-weight: bold }
-
- pre.address {
- margin-bottom: 0 ;
- margin-top: 0 ;
- font-family: serif ;
- font-size: 100% }
-
- pre.literal-block, pre.doctest-block {
- margin-left: 2em ;
- margin-right: 2em ;
- background-color: #eeeeee }
-
- span.classifier {
- font-family: sans-serif ;
- font-style: oblique }
-
- span.classifier-delimiter {
- font-family: sans-serif ;
- font-weight: bold }
-
- span.interpreted {
- font-family: sans-serif }
-
- span.option {
- white-space: nowrap }
-
- span.pre {
- white-space: pre }
-
- span.problematic {
- color: red }
-
- span.section-subtitle {
- /* font-size relative to parent (h1..h6 element) */
- font-size: 80% }
-
- table.citation {
- border-left: solid 1px gray;
- margin-left: 1px }
-
- table.docinfo {
- margin: 2em 4em }
-
- table.docutils {
- margin-top: 0.5em ;
- margin-bottom: 0.5em }
-
- table.footnote {
- border-left: solid 1px black;
- margin-left: 1px }
-
- table.docutils td, table.docutils th,
- table.docinfo td, table.docinfo th {
- padding-left: 0.5em ;
- padding-right: 0.5em ;
- vertical-align: top }
-
- table.docutils th.field-name, table.docinfo th.docinfo-name {
- font-weight: bold ;
- text-align: left ;
- white-space: nowrap ;
- padding-left: 0 }
-
- h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
- h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
- font-size: 100% }
-
- tt.docutils {
- background-color: #eeeeee }
-
- ul.auto-toc {
- list-style-type: none }
-
- </style>
- </head>
- <body>
- <div class="document" id="twig-template-engine-specification">
- <h1 class="title">Twig Template Engine Specification</h1>
- <p>This specification specifies a simple cross-language template engine for at least
- PHP, Python and Ruby.</p>
- <div class="section">
- <h1><a id="purpose" name="purpose">Purpose</a></h1>
- <p>A language independent and simple template engine is useful for applications that
- use code which is written in more than one programming language. Good Examples
- are portal systems which use a blog written in Ruby, a forum software written in
- PHP and a planet system written in Python.</p>
- </div>
- <div class="section">
- <h1><a id="inspiration" name="inspiration">Inspiration</a></h1>
- <p>Twig uses a syntax similar to the Genshi text templates which in turn were
- inspired by django which also inspired Jinja (all three of them python template
- engines) which inspired the Twig runtime environment.</p>
- </div>
- <div class="section">
- <h1><a id="undefined-behavior" name="undefined-behavior">Undefined Behavior</a></h1>
- <p>To simplify porting the template language to different platforms in a couple of
- situations the behavior is undefined. Template authors may never take advantage
- of such a situation!</p>
- </div>
- <div class="section">
- <h1><a id="syntax" name="syntax">Syntax</a></h1>
- <p>I'm too lazy to write down the syntax as BNF diagram but the following snippet
- should explain the syntax elements:</p>
- <pre class="literal-block">
- <!DOCTYPE HTML>
- {# This is a comment #}
- <title>{% block title %}Page Title Goes Here{% endblock %}</title>
- {% if show_navigation %}
- <nav>
- <ul>
- {% for item in navigation %}
- <li><a href="${item.href|e}">$item.caption</a></li>
- {% endfor %}
- </ul>
- </nav>
- {% endif %}
- <article>{% block body %}{% endblock %}</article>
- </pre>
- <div class="section">
- <h2><a id="comments-and-whitespace" name="comments-and-whitespace">Comments and Whitespace</a></h2>
- <p>Everything between <tt class="docutils literal"><span class="pre">{#</span></tt> and <tt class="docutils literal"><span class="pre">#}</span></tt> is ignored by the lexer. Inside blocks and
- variable sections the Lexer has to remove whitespace too.</p>
- </div>
- <div class="section">
- <h2><a id="output-expressions" name="output-expressions">Output Expressions</a></h2>
- <p>To output expressions two syntaxes exist. Simple variable output or full
- expression output:</p>
- <pre class="literal-block">
- $this.is.a.variable.output
- ${ expression | goes | here }
- </pre>
- <p>The former is what we call a variable expression, the second a full expression.
- Variable expressions must not contain whitespace, whereas a full expression
- must print the output of the full wrapped expression.</p>
- </div>
- <div class="section">
- <h2><a id="expressions" name="expressions">Expressions</a></h2>
- <p>Expressions allow basic string manipulation and arithmetic calculations. It is
- an infix syntax with the following operators in this precedence:</p>
- <blockquote>
- <table border="1" class="docutils">
- <colgroup>
- <col width="15%" />
- <col width="85%" />
- </colgroup>
- <thead valign="bottom">
- <tr><th class="head">Operator</th>
- <th class="head">Description</th>
- </tr>
- </thead>
- <tbody valign="top">
- <tr><td><tt class="docutils literal"><span class="pre">+</span></tt></td>
- <td>Convert both arguments into a number and add them up.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">-</span></tt></td>
- <td>Convert both arguments into a number and substract them.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">*</span></tt></td>
- <td>Convert both arguments into a number and multiply them.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">/</span></tt></td>
- <td>Convert both arguments into a number and divide them.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">%</span></tt></td>
- <td>Convert both arguments into a number and calculate the rest
- of the integer division.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">~</span></tt></td>
- <td>Convert both arguments into a string and concatenate them.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">or</span></tt></td>
- <td>True if the left or the right expression is true.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">and</span></tt></td>
- <td>True if the left and the right expression is true.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">not</span></tt></td>
- <td>negate the expression</td>
- </tr>
- </tbody>
- </table>
- </blockquote>
- <p>All number conversions have an undefined precision but the implementations
- should try to select the best possible type. For example, if the implementation
- sees an integer and a float that looks like an integer it may convert the
- latter into a long and add them.</p>
- <p>Use parentheses to group expressions.</p>
- <p>If an object cannot be compared the implementation might raise an error or fail
- silently. Template authors may never apply mathematical operators to untrusted
- data. This is especially true for the php implementation where the following
- outputs <tt class="docutils literal"><span class="pre">42</span></tt>:</p>
- <pre class="literal-block">
- ${ "foo41" + 1 }
- </pre>
- <p>This is undefined behavior and will break on different implementations or
- return <tt class="docutils literal"><span class="pre">0</span></tt> as <tt class="docutils literal"><span class="pre">"foo41"</span></tt> is not a valid number.</p>
- <div class="section">
- <h3><a id="types" name="types">Types</a></h3>
- <p>The following types exist:</p>
- <blockquote>
- <table border="1" class="docutils">
- <colgroup>
- <col width="15%" />
- <col width="21%" />
- <col width="64%" />
- </colgroup>
- <thead valign="bottom">
- <tr><th class="head">Type</th>
- <th class="head">Literal</th>
- <th class="head">Description</th>
- </tr>
- </thead>
- <tbody valign="top">
- <tr><td><tt class="docutils literal"><span class="pre">integer</span></tt></td>
- <td><cite>d+</cite></td>
- <td>One of the two numeric types. Which of them
- is used and when is up to the implementation.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">float</span></tt></td>
- <td><cite>d+.d+</cite></td>
- <td>Floating point values.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">string</span></tt></td>
- <td>see below</td>
- <td>A unicode string. The PHP implementation has
- to use bytestrings here and may use mb_string.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">bool</span></tt></td>
- <td><cite>(true|false)</cite></td>
- <td>Represents boolean values.</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">none</span></tt></td>
- <td><cite>none</cite></td>
- <td>This type is returned on missing variables or
- attributes.</td>
- </tr>
- </tbody>
- </table>
- </blockquote>
- <p>String regex:</p>
- <pre class="literal-block">
- (?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')(?sm)
- </pre>
- </div>
- <div class="section">
- <h3><a id="attribute-lookup" name="attribute-lookup">Attribute Lookup</a></h3>
- <p>There are two ways to look up attributes on objects. The dot and the
- subscript syntax, both inspired by JavaScript. Basically the following
- expressions do the very same:</p>
- <pre class="literal-block">
- foo.name.0
- foo['name'][0]
- </pre>
- <p>This is useful to dynamically get attributes from objects:</p>
- <pre class="literal-block">
- foo[bar]
- </pre>
- <p>The underlaying implementation is free to specify on it's own what an attribute
- lookup means. The PHP reference implementation for example performs those
- actions on <tt class="docutils literal"><span class="pre">foo.bar</span></tt>:</p>
- <ul class="simple">
- <li>try <tt class="docutils literal"><span class="pre">$foo['bar']</span></tt></li>
- <li>try <tt class="docutils literal"><span class="pre">$foo->bar()</span></tt></li>
- <li>try <tt class="docutils literal"><span class="pre">$foo->bar</span></tt></li>
- <li>try <tt class="docutils literal"><span class="pre">$foo->getBar()</span></tt></li>
- </ul>
- <p>The first match returns the object, attribute access to nonexisting attributes
- returns <cite>none</cite>.</p>
- </div>
- <div class="section">
- <h3><a id="filtering" name="filtering">Filtering</a></h3>
- <p>The template language does not specify function calls, but filters can be used
- to further modify variables using functions the template engine provides.</p>
- <p>The following snippet shows how filters are translated to function calls:</p>
- <pre class="literal-block">
- ${ 42 | foo(1, 2) | bar | baz }
- -> baz(bar(foo(42, 1, 2)))
- </pre>
- <p>The following filters must be provided by the implementation:</p>
- <blockquote>
- <table border="1" class="docutils">
- <colgroup>
- <col width="26%" />
- <col width="74%" />
- </colgroup>
- <thead valign="bottom">
- <tr><th class="head">Name</th>
- <th class="head">Description</th>
- </tr>
- </thead>
- <tbody valign="top">
- <tr><td><cite>date</cite></td>
- <td>Format the timestamp using the PHP date formatting
- rules. This may sound like a nonstandard way of
- formatting dates but it's a way very popular among
- template designers and also used by django.</td>
- </tr>
- <tr><td><cite>strftime</cite></td>
- <td>Format the timestamp using standard strftime rules.</td>
- </tr>
- <tr><td><cite>numberformat</cite></td>
- <td>Apply number formatting on the string. This may or
- may not use local specific rules.</td>
- </tr>
- <tr><td><cite>moneyformat</cite></td>
- <td>Like <cite>numberformat</cite> but for money.</td>
- </tr>
- <tr><td><cite>filesizeformat</cite></td>
- <td>Takes a number of bytes and displays it as KB/MB/GB</td>
- </tr>
- <tr><td><cite>format</cite></td>
- <td><dl class="first last docutils">
- <dt>Applies <cite>sprintf</cite> formatting on the string::</dt>
- <dd>${ "%s %2f" | format(string, float) }</dd>
- </dl>
- </td>
- </tr>
- <tr><td><cite>even</cite></td>
- <td>Is the number even?</td>
- </tr>
- <tr><td><cite>odd</cite></td>
- <td>Is the number odd?</td>
- </tr>
- <tr><td><cite>escape</cite></td>
- <td>Apply HTML escaping on a string. This also has to
- convert <cite>"</cite> to <cite>&quot; but leave `'</cite> unmodified.</td>
- </tr>
- <tr><td><cite>e</cite></td>
- <td>Alias for <cite>escape</cite>.</td>
- </tr>
- <tr><td><cite>urlencode</cite></td>
- <td>URL encode the string. If the second parameter is
- true this function should encode for path sections,
- otherwise for query strings.</td>
- </tr>
- <tr><td><cite>quotes</cite></td>
- <td>Escape quotes (', ", etc.)</td>
- </tr>
- <tr><td><cite>title</cite></td>
- <td>Make the string lowercase and upper case the first
- characters of all words.</td>
- </tr>
- <tr><td><cite>capitalize</cite></td>
- <td>Like <cite>title</cite> but capitalizes only the first char of
- the whole string.</td>
- </tr>
- <tr><td><cite>upper</cite></td>
- <td>Convert the string to uppercase.</td>
- </tr>
- <tr><td><cite>lower</cite></td>
- <td>Convert the string to lowercase.</td>
- </tr>
- <tr><td><cite>strip</cite></td>
- <td>Trim leading and trailing whitespace.</td>
- </tr>
- <tr><td><cite>lstrip</cite></td>
- <td>Trim leading whitespace.</td>
- </tr>
- <tr><td><cite>rstrip</cite></td>
- <td>Trim trailing whitespace.</td>
- </tr>
- <tr><td><cite>translate</cite></td>
- <td>Translate the string using either the "theme" domain
- or the "chyrp" domain if in Admin. (Chyrp-specific)</td>
- </tr>
- <tr><td><cite>translate_plural</cite></td>
- <td>Translate the (singular) string, or the plural string
- if the number passed is not 1.</td>
- </tr>
- <tr><td><cite>normalize</cite></td>
- <td>Convert all excessive whitespace (including linebreaks)
- into a single space.</td>
- </tr>
- <tr><td><cite>truncate</cite></td>
- <td>Truncate a string, providing ellipsis, if it is longer
- than the passed length. Keeps words in tact by default,
- but with a second boolean parameter will be strict.</td>
- </tr>
- <tr><td><cite>replace</cite></td>
- <td>Replaces the occurrence of the first argument with the
- second argument in the string.</td>
- </tr>
- <tr><td><cite>linebreaks</cite></td>
- <td>Convert linebreaks to <br />'s.</td>
- </tr>
- <tr><td><cite>camelize</cite></td>
- <td>Convert string to camelcase.</td>
- </tr>
- <tr><td><cite>strip_tags</cite></td>
- <td>Strip HTML from the string.</td>
- </tr>
- <tr><td><cite>pluralize</cite></td>
- <td>Return the pluralization of a string, or if a number
- is passed and it is 1, don't pluralize.</td>
- </tr>
- <tr><td><cite>sanitize</cite></td>
- <td>Remove special characters from a string.</td>
- </tr>
- <tr><td><cite>join</cite></td>
- <td>Concatenate the array items and join them with the
- string provided (or commas by default).</td>
- </tr>
- <tr><td><cite>split</cite></td>
- <td>Split a string into an array at the given breakpoints.</td>
- </tr>
- <tr><td><cite>first</cite></td>
- <td>First entry of an Array.</td>
- </tr>
- <tr><td><cite>offset</cite></td>
- <td>Entry at Array[offset].</td>
- </tr>
- <tr><td><cite>last</cite></td>
- <td>Last entry of an Array.</td>
- </tr>
- <tr><td><cite>reverse</cite></td>
- <td>Reverse the Array items.</td>
- </tr>
- <tr><td><cite>count</cite></td>
- <td>Count the number of items in an array or string
- characters.</td>
- </tr>
- <tr><td><cite>length</cite></td>
- <td>Alias for <cite>count</cite>.</td>
- </tr>
- <tr><td><cite>default</cite></td>
- <td>If the value is <cite>none</cite> the first argument is returned</td>
- </tr>
- <tr><td><cite>keys</cite></td>
- <td>Keys of an Array.</td>
- </tr>
- <tr><td><cite>items</cite></td>
- <td>Items of an Array.</td>
- </tr>
- <tr><td><cite>inspect</cite></td>
- <td>Dumps the variable or value.</td>
- </tr>
- <tr><td><cite>fallback</cite></td>
- <td>If the value is empty or <cite>none</cite>, return this value.</td>
- </tr>
- <tr><td><cite>selected</cite></td>
- <td>If the first argument is the same as the value, output
- <cite>class="selected"</cite>, or <cite>selected</cite> if the second
- argument is <cite>true</cite>.</td>
- </tr>
- <tr><td><cite>option_selected</cite></td>
- <td>Same as <cite>selected</cite>, but for <cite>selected="selected"</cite>.</td>
- </tr>
- <tr><td><cite>checked</cite></td>
- <td>Same as <cite>selected</cite>, but for <cite>checked="checked"</cite>.</td>
- </tr>
- </tbody>
- </table>
- </blockquote>
- <p>Additionally, if a filter is missing (say, ${ foo | bar_filter }, in Chyrp it
- checks for an associated Trigger filter by that filter's name.</p>
- </div>
- </div>
- <div class="section">
- <h2><a id="for-loops" name="for-loops">For Loops</a></h2>
- <p>Iteration works via for loops. Loops work a bit like their Python counterparts,
- except that they don't support multilevel tuple unpacking and that they add a new
- layer to the context. Thus at the end of the iteration all the modifications on
- the context disappear. Additionally, inside loops you have access to a special
- <cite>loop</cite> object which provides runtime information:</p>
- <blockquote>
- <table border="1" class="docutils">
- <colgroup>
- <col width="30%" />
- <col width="70%" />
- </colgroup>
- <thead valign="bottom">
- <tr><th class="head">Variable</th>
- <th class="head">Description</th>
- </tr>
- </thead>
- <tbody valign="top">
- <tr><td><tt class="docutils literal"><span class="pre">loop.index</span></tt></td>
- <td>The current iteration of the loop (1-indexed)</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">loop.index0</span></tt></td>
- <td>The current iteration of the loop (0-indexed)</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">loop.revindex</span></tt></td>
- <td>The number of iterations from the end of the
- loop (1-indexed)</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">loop.revindex0</span></tt></td>
- <td>The number of iterations from the end of the
- loop (0-indexed)</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">loop.first</span></tt></td>
- <td>True if this is the first time through the loop</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">loop.last</span></tt></td>
- <td>True if this is the last time through the loop</td>
- </tr>
- <tr><td><tt class="docutils literal"><span class="pre">loop.parent</span></tt></td>
- <td>For nested loops, this is the loop "above" the
- current one</td>
- </tr>
- </tbody>
- </table>
- </blockquote>
- <p>Additionally for loops can have an <cite>else</cite> section that is executed if no
- iteration took place.</p>
- <div class="section">
- <h3><a id="example" name="example">Example</a></h3>
- <pre class="literal-block">
- <ul>
- {% for user in users %}
- <li><a href="$user.href">${ user.username | escape }</a></li>
- {% else %}
- <li><em>No users found!</em></li>
- {% endfor %}
- </ul>
- </pre>
- </div>
- <div class="section">
- <h3><a id="notes-on-iteration" name="notes-on-iteration">Notes on Iteration</a></h3>
- <p>Because we have to cope with PHP too, which has problematic arrays that are
- neither hashmaps nor lists, we have no support for associative array iteration
- at all. How do you iterate over associative arrays then? Using a filter:</p>
- <pre class="literal-block">
- {% for key, value in array | items %}
- ...
- {% endfor %}
- </pre>
- <p>To iterate over the keys only:</p>
- <pre class="literal-block">
- {% for key in array | keys %}
- ...
- {% endfor %}
- </pre>
- </div>
- </div>
- <div class="section">
- <h2><a id="if-conditions" name="if-conditions">If Conditions</a></h2>
- <p>If conditions work like like Ruby, PHP and Python, just that we use PHP
- keywords. Also, use <cite>elseif</cite> and not <cite>else if</cite>:</p>
- <pre class="literal-block">
- {% if expr1 %}
- ...
- {% elseif expr2 %}
- ...
- {% else %}
- ...
- {% endif %}
- </pre>
- </div>
- <div class="section">
- <h2><a id="inheritance" name="inheritance">Inheritance</a></h2>
- <p>Template inheritance allows you to build a base "skeleton" template that
- contains all the common elements of your site and defines <strong>blocks</strong> that
- child templates can override.</p>
- <p>Here a small template inheritance example:</p>
- <pre class="literal-block">
- <!DOCTYPE HTML>
- <html lang="en">
- <link rel="stylesheet" href="style.css">
- <title>{% block title %}My site{% endblock %}</title>
- <div id="sidebar">
- {% block sidebar %}
- <ul>
- <li><a href="/">Home</a></li>
- <li><a href="/blog/">Blog</a></li>
- </ul>
- {% endblock %}
- </div>
- <div id="content">
- {% block content %}{% endblock %}
- </div>
- </html>
- </pre>
- <p>If we call that template "base.html" a "index.html" template could override
- it and fill in the blocks:</p>
- <pre class="literal-block">
- {% extends "base.html" %}
- {% block title %}Foo &mdash; {% super %}{% endblock %}
- {% block content %}
- This is the content
- {% endblock %}
- </pre>
- <p>By using <cite>{% super %}</cite> you can render the parent's block. The template
- filenames must be constant strings (we don't support dynamic inheritance
- for simplicity) and are relative to the loader folder, not the current
- template.</p>
- </div>
- </div>
- </div>
- </body>
- </html>
|