<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Passing Curiosity: Posts tagged template</title>
    <link href="https://passingcuriosity.com/tags/template/template.xml" rel="self" />
    <link href="https://passingcuriosity.com" />
    <id>https://passingcuriosity.com/tags/template/template.xml</id>
    <author>
        <name>Thomas Sutton</name>
        
        <email>me@thomas-sutton.id.au</email>
        
    </author>
    <updated>2008-11-19T00:00:00Z</updated>
    <entry>
    <title>Creating custom tags for SPIP - Static tags</title>
    <link href="https://passingcuriosity.com/2008/creating-custom-tags-spip-static/" />
    <id>https://passingcuriosity.com/2008/creating-custom-tags-spip-static/</id>
    <published>2008-11-19T00:00:00Z</published>
    <updated>2008-11-19T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>The SPIP template language has two constructions: loops (which determine the
objects to be “output”) and tags (which actually output particular values).
The reasonably simple syntax of tags – most look like ’‘#THE_TAG’’ – belies
their power and flexibility and the ease with which we can use them to extend
SPIP with additional features and integrate it with other PHP-based packages.</p>
<p>In this post, I’ll describe how to create your own static SPIP tags. In a
future post, I’ll cover dynamic tags, and how to package your tags (and other
code) as a plug-in. Before reading this post, you should be familiar with
<a href="http://www.spip.net/">SPIP</a> and it’s <a href="/2008/spip-template-languag/">template
language</a>, and with programming in PHP.</p>
<p>SPIP tags are used to “output” a “value”. For many tags, this value is taken
from the context in which it is called: the title of the “current” article,
for example, or the logo the “current” news item. Others output global values
like the name of the web-site, or the version of the software. Still more tags
allow users to interact with the site (e.g. <code>#LOGIN_PUBLIC</code> which outputs a
log-in form <em>and</em> processes the log-in request when the user submits it) and a
few (like ’‘#SET’’ and ’‘#GET’’) implement the features of a general purpose
programming language and producing output is only a side effect. All of these
effects are achieved using the same relatively simple syntax and mechanism.</p>
<p>Tags can be divided into two groups based on their behaviour. <em>Static</em> tags
are those that output some statically determined value – one that does not
necessarily change from one evaluation to another. The title of an article,
for example, will not <em>necessarily</em> change between one page view and another.
It <em>may</em>, but not necessarily. <em>Dynamic</em> tags are those that generate dynamic
output – values that necessarily change between invocations. The current date
and time, for example. Implementing a static tag is significantly easier than
a dynamic tag, so we’ll look at that first and leave dynamic tags to a later
post.</p>
<h2 id="static-tags">Static tags</h2>
<p>Static tags are those that output a “static” value. That is, a value that is
not expected to change over any particular time period. This means that SPIP
can evaluate a static tag once and then cache the result and reuse it in
future requests.</p>
<p>Like many aspects of SPIP, a tag (for the rest of this section, you can assume
that “tag” means “static tag”) is a function<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> with a special name – the
name of the tag appended to “balise_”. For example a tag called <code>#FOO</code> will be
implemented by a function called <code>balise_FOO</code>. Like much of SPIP, these
functions can be overridden by plug-ins, or site-specific files. When it sees
a <code>#FOO</code> tag, SPIP will first look for a function called <code>balise_FOO</code>, then
one called <code>balise_FOO_dist</code>, and then decide that the tag doesn’t exist.</p>
<p>Rather than blather on, I’ll give you a trivial example: the <code>#HELLO_WORLD</code>
tag. This tag simply outputs the string “Hello World!” (To use the code,
simply copy the function into the file <code>mes_fonctions.php</code> in the root of your
SPIP installation):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode php"><code class="sourceCode php"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;?php</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> balise_HELLO_WORLD(<span class="va">$p</span>) {</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="va">$p</span>-&gt;code <span class="op">=</span> <span class="st">&quot;'Hello World!'&quot;</span><span class="ot">;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="va">$p</span><span class="ot">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>As you can see there are a few more details than just the name, namely, this
<code>$p</code> thing. The parameter to the function implementing the tag is a reference
to the abstract syntax tree node for that tag. This contains all of the
information about the tag that SPIP has about the tag, the filters called on
it, the brackets around it, the context, etc., etc., etc. All that is missing
is the value itself, which is where our function comes into play. After SPIP
has analysed the templates and handled everything <em>it</em> can, it calls the
function responsible for each tag to fill in the gaps.</p>
<p>There a numerous fields in the <code>Champ</code> object (the class is defined in
<a href="http://trac.rezo.net/trac/spip/browser/spip/ecrire/public/interfaces.php"><code>ecrire/public/interfaces.php</code></a>
but the definition isn’t particularly edifying), but most of them are best
left alone and accessed by way of helper functions:</p>
<ul>
<li><p><code>type</code> – a string describing the type of AST node. Should be <code>"champ"</code> for
tags.</p></li>
<li><p><code>nom_champ</code> – the name of the tag without the <code>#</code>.</p></li>
<li><p><code>nom_boucle</code> – the name of the loop. Tags aren’t loops, so this will be
empty.</p></li>
<li><p><code>avant</code> – a list of preceding nodes that are conditional on this one.</p></li>
<li><p><code>apres</code> – a list of succeeding nodes that are conditional on this one.</p></li>
<li><p><code>etoile</code> – “star”. The tag was called like <code>#HELLO_WORLD*</code> and, thus,
should output raw, as opposed to HTML safe, values.</p></li>
<li><p><code>param</code> – a list of parameters and filters to the tag call. This is pretty
complex.</p></li>
<li><p><code>fonctions</code> – similar to <code>param</code> but structured differently.</p></li>
<li><p><code>id_boucle</code> – the name of the loop within which this tag occurs.</p></li>
<li><p><code>boucles</code> – an array of AST nodes for the loops (“boulces”) in the
template.</p></li>
<li><p><code>type_requete</code> – no idea.</p></li>
<li><p><code>code</code> – the PHP code which, when <code>eval()</code>d, produces the value of the tag.</p></li>
<li><p><code>interdire_scripts</code> – whether “scripts” will be interpreted. No idea.</p></li>
<li><p><code>ramasser_miettes</code> – whether to “collect the crumbs”. No idea.</p></li>
<li><p><code>descr</code> – an array of values describing the AST node, the file it came
from, etc.</p></li>
<li><p><code>ligne</code> – the line number of the tag call in the template.</p></li>
</ul>
<p><strong>An example</strong> Replacing the contents of your <code>dist/sommaire.html</code> template
with this:</p>
<pre><code>[before (#HELLO_WORLD{arg1}|strtoupper) after]</code></pre>
<p>might result in the following AST being passed to <code>balise_HELLO_WORLD</code> above
(<a href="/files/2008/11/spip-ast-example.txt">download the full SPIP AST</a>):</p>
<pre><code>type =&gt; &quot;champ&quot;
nom_champ =&gt; &quot;HELLO_WORLD&quot;
avant =&gt; the node/s for &quot;before&quot;, ...
apres =&gt; the node/s for &quot;after&quot;, ...
etoile =&gt; 
param =&gt; [
        0 =&gt; &quot;arg1&quot; is in here, ...
        1 =&gt; &quot;strtoupper&quot; is in here, ...
        ]
fonctions =&gt; [
        0 =&gt; &quot;{arg1}&quot; is also in here, ...
        1 =&gt; &quot;strtoupper&quot; is also in here, ...
        ]</code></pre>
<p>Thankfully, you can ignore almost all of this and trust to the helpers. The
function <a href="http://doc.spip.org/@interprete_argument_balise" title="fonction interprete_argument_balise"><code>interprete_argument_balise</code>
(FR)</a> is particularly useful:
<code>interprete_argument_balise(1, $p)</code> returns the first argument in the AST node
<code>$p</code>.</p>
<p>Using what I’ve described so far, it’s pretty to write a <code>#HELLO</code> tag which
output’s a message “Hello <em>name</em>” when given a name, and “Hello World!”
otherwise (again, this code goes in <code>mes_fonctions.php</code>):</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode php"><code class="sourceCode php"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">&lt;?php</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> balise_HELLO(<span class="va">$p</span>) {</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="va">$name</span> <span class="op">=</span> interprete_argument_balise(<span class="dv">1</span><span class="ot">,</span> <span class="va">$p</span>)<span class="ot">;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> (<span class="op">!</span> <span class="va">$name</span>) {</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>        <span class="va">$name</span> <span class="op">=</span> <span class="st">&quot;World!&quot;</span><span class="ot">;</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>    <span class="va">$p</span>-&gt;code <span class="op">=</span> <span class="st">&quot;'Hello </span><span class="va">$name</span><span class="st">'&quot;</span><span class="ot">;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="va">$p</span><span class="ot">;</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>There are a number of other issues involved in writing static templates. It’s
good practice to make sure that your tags (and everything else, for that
matter) supports translation, especially if you’re going to be distributing it
to others. Doing so is reasonably easy using the <a href="http://doc.spip.org/@Les-chaines-de-langue" title="Les chaines de langue"><code>_T</code>
(FR)</a>
function and “lang” files. Getting data from a loop (article titles, section
IDs, etc.) is also reasonably straightforward with <a href="http://doc.spip.org/@champ_sql"><code>champ_sql</code>
(FR)</a>.</p>
<p>For more examples, you can have a look at the code for some of SPIP’s built-in
tags in the code of <a href="http://doc.spip.org/balises-php"><code>ecrire/public/balises.php</code>
(FR)</a> (I rather like
<a href="http://doc.spip.org/@balise_INTRODUCTION_dist"><code>balise_INTRODUTION_dist</code>
(FR)</a>). If you do look to the
SPIP source code for examples, you should be aware that a lot of the built-in
tags are not implemented using this mechanism, but are magically drawn from
database columns with the same name. If you extend the database this magic
will work for you too, but that is beyond the scope of this post and will have
to wait for my post/s about writing SPIP plug-ins.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>In one sense, this is not entirely true: the function just manipulates
the abstract syntax tree node for the tag which will then be processed by SPIP
to generate the tag. For more on this see <a href="http://doc.spip.org/@details-sur-l-AST" title="dÈtails sur líAST">details of the AST
(FR)</a>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>]]></summary>
</entry>
<entry>
    <title>The SPIP Template Language</title>
    <link href="https://passingcuriosity.com/2008/spip-template-languag/" />
    <id>https://passingcuriosity.com/2008/spip-template-languag/</id>
    <published>2008-07-22T00:00:00Z</published>
    <updated>2008-07-22T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>SPIP, like many content management systems, provides a templating facility
based on the idea of loops. A template is a mixture of static content – HTML
tags, for instance – dynamic content placeholders – that stand for things
like “the title”, “the body”, “the date of publication”, etc.– and loops –
which determine what and how much is displayed. Unlike some CMS’, SPIP uses a
rather sophisticated language for its template system rather than just
providing a library of PHP functions that can be called. The placeholders in
SPIP templates look like <code>#VALUE</code> and can be supplied with arguments
<code>#VALUE{argument}</code>, modified using filters <code>#VALUE|uppercase</code> (which can also
take arguments <code>#VALUE|foo{bar}</code>), and bracketed with conditional content
which is output only when the tag has a non-empty <code>[&lt;li&gt;(#VALUE)&lt;/li&gt;]</code>. I
can’t remember if it’s necessary, but I always wrap any tag with a parameter
or filter with brackets, just in case.</p>
<p>The loops look somewhat stranger with a pseudo HTML tag format. In general
each loop has :</p>
<ul>
<li><p>a name;</p></li>
<li><p>a type (of things it iterates over, pretty much 1-to-1 with database
tables);</p></li>
<li><p>some of criteria (determine <em>which</em> things to loop over);</p></li>
<li><p>a body; <em>and</em></p></li>
<li><p>optionally, some conditional content.</p></li>
</ul>
<p>An example will help illustrate:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&lt;</span><span class="kw">B_aloop</span><span class="dt">&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&lt;</span><span class="kw">ol</span><span class="dt">&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&lt;</span><span class="kw">BOUCLE_aloop</span><span class="ot">(ARTICLES)</span><span class="er">{</span><span class="ot">id_article IN </span><span class="er">1,2,3,5,7,11}</span><span class="dt">&gt;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>            <span class="dt">&lt;</span><span class="kw">li</span><span class="dt">&gt;</span>This article was published on: [(#DATE|affdate('Y'))].<span class="dt">&lt;/</span><span class="kw">li</span><span class="dt">&gt;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&lt;/</span><span class="kw">BOUCLE_aloop</span><span class="dt">&gt;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&lt;/</span><span class="kw">ol</span><span class="dt">&gt;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&lt;/</span><span class="kw">B_aloop</span><span class="dt">&gt;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>        <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span>There are no matching articles.<span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    <span class="er">&lt;</span>//B_aloop&gt;</span></code></pre></div>
<p>Upon seeing this code SPIP does the following (along with some other stuff in
the database and caching layers). It retrieves all of the articles with an ID
of 1, 2, 3, 5, 7, or 11. If there <em>are</em> some articles it outputs <code>&lt;ol&gt;</code>,
followed by a line like <code>&lt;li&gt;This article was published on: 2008&lt;/li&gt;</code> for
each article, followed by <code>&lt;/ol&gt;</code>. If there were no matching articles, then it
instead outputs <code>&lt;p&gt;There are no matching articles.&lt;/p&gt;</code>.</p>
<p>There are a few more details (the name of the loop above is “_aloop” not
“aloop”, you can reach out of a loop to one that contains it like so
<code>#outerloop:VALUE</code>…), but that’s basically it. For more details about the
specific loops, criteria, tags, and filters available in SPIP you can see the
<a href="http://www.spip.net/@?lang=en">SPIP Glossary</a>, though do note that SPIP, like
pretty much all open source software, has documentation that is a little bit
patchy in places, especially in English.</p>
<p>In my next SPIP post, I’ll describe ways to extend SPIP with your own tags and
filters and alter on, I’ll explore modifying and even creating our own custom
loops.</p>]]></summary>
</entry>

</feed>
