I’m in the process of builidng my first site using the Symphony CMS, which uses XSLT as its template language.
Symphony uses XSLT as it’s template language. In theory, this should make it more flexible than core Textpattern, with its limited number of built-in tags (Although the huge number of plugins available for Textpattern definitely means Txp is still capable of holding its own). That is, if I actually knew XSLT well enough to write a single template turning to The Google dozens of times for answers to newbie questions.
For the site in question, I need to display a list of categories as a set of song lyrics, divided into verses of four lines each. In this first version, I’d decided I wanted the song divided into columns of four verses each. I’ve changed my mind on this design decision, but rather than let the headache experience go to waste, I’ve decided the code might interest other, still-newer-than-me newbies. It demonstrates how to build nested lists in XSLT, which is much less intuitive than in a procedural language like PHP (or Textpattern Tags with a plugin like zem_nth).
Your first impulse, if you’re used to PHP, is to do something like this to group blocks of items together:
<p>
<xsl:template match="data/posts">
<xsl:if test="position() mod 4 = 1"></p><p></xsl:if>
<xsl:apply-templates />
</xsl:template>
</p>
That so doesn’t work.
The problem is, it’s not valid XML—the parser will see an xsl:if tag matched with a closing p tag and scold you (Symphony won’t even let you save a template with this markup in it—it fails the first test the CMS subjects it to).
To handle nested groups, you have to do something like this (I’m using the Blueprint CSS framework, thus the divs with class "span-8"):
<xsl:template match="/data/categorylist">
<!-- For every sixteenth item in the list: -->
<xsl:for-each select="entry[position() mod 16 = 1]">
<!--Process this item and the next 16, putting them inside a div -->
<div class="lyrics span-8">
<xsl:for-each select=".|following-sibling::entry[position() < 16]">
<!-- With every fourth item in this block of 16: -->
<xsl:if test="position() mod 4 = 1">
<!-- Wrap the current item and the next four in a p -->
<p>
<xsl:apply-templates select=".|following-sibling::entry[position() < 4]"/>
</p>
</xsl:if>
</xsl:for-each>
</div>
</xsl:for-each>
</xsl:template>
As the comments explain, you have to grab every nth item, then apply your templates to that item plus the following n-1, or, in XPath, .|following-sibling::entry[position() < 16] (’.’ means the current item in the list being processed; ‘|’ means “and”).