Search

Hi there,

I’ve been puzzling over this for days trying to work this out myself and I just can’t seem to get it to work how I would like.

Basically I have a blog style site set up with entries. You can see it here if interested: We Were Here

When i’m looking at the single article/interview page I want to output a list of the four most recent entries at the bottom of the page. Easy! But I want to have some simple logic so that if the article currently being viewed is one of the lastest four articles it doesn’t display that article but skips it and displays the next one instead. So there are always four but I never recommend the one they are already looking at.

Sounds easy but I just can’t get it to work!

There are two data sources single-interview which is filtered to only display one article based on a url parameter, and interviewswhich is a cut down version on the full feed.

This is an example of the Interviews data source (obviously there are lots of entries but i’ve just shown one for simplicity). The interviews data source is ordered descending by the order node:

<interviews>
    <pagination total-entries="8" total-pages="1" entries-per-page="20" current-page="1" />
    <section id="1" handle="interviews">Interviews</section>
    <entry id="9">
        <name handle="kirk-pflaum">Kirk Pflaum</name>
        <business handle="pflaum-enterprises-times-ltd">Pflaum Enterprises</business>
        <excerpt mode="formatted"><p>Whether it was the exact combination of Kylie's dance moves or the strange magnetic forces of Illyis we may never know.</p></excerpt>
        <order>8</order>
    </entry>
</interviews>

Then there is the single interview data source that looks like this:

<single-interview>
    <section id="1" handle="interviews">Interviews</section>
    <entry id="1">
        <name handle="stephen-mccarthy">Stephen McCarthy</name>
        <business handle="mccarthy-design">McCarthy Design</business>
        <website handle="wwwsmccarthyconz">www.smccarthy.co.nz</website>
        <info mode="formatted"><p>It was 1986. Stephen was just thirteen and his     life was about to be changed forever. As he lay in his bed the meteorite <em>Illyis II</em> sped past the earth at approximately 30,000 kilometres per hour. Other than a few scientists and backyard astronomists, this once-in-a-million-year phenomenon went large unnoticed. Stephen however was transfixed. Transfixed by Kylie Minogue as she gyrated on his black and white tele to the sounds of Locomotion. Whether it was the exact combination of Kylie's dance moves or the strange magnetic forces of Illyis we may never know. What is certain though is that the massive surge of endorphins to Stephen's head left him permanently deranged and incapable of ever fully loving another person.</p>
  <p>As MC Hammer came on screen and Illyis II dissappeard for another million years, Stephen slowly turned over and stared at his Garfield Wallpaper. Something was different but he could no longer recall what he felt like before. Was this growing up or just another teenage high? He's still searching for the answer.  </p></info>
        <vimeo-id handle="8066833">8066833</vimeo-id>
        <order>2</order>
        <quote mode="formatted"><p>Kylie Minogue is probably my biggest inspiration. The dance moves, the clothes, the pop melodies. She's basically the perfect women. </p></quote>
    </entry>
</single-interview>

So what I thought was to create a param for the ‘current’ entry and then use recusion to output it four times and to not output anything if was the current entry.

<xsl:template name="latest">
<xsl:param name="current-int" select="/data/single-interview/entry/order"/>
<xsl:param name="count" select="1"/>
<xsl:if test="/data/interviews/entry[position()=$count]/order != $current-int">
<div class="threecol">
    <xsl:if test="$count=4">
        <xsl:attribute name="class">
            <xsl:text>threecol last</xsl:text>
        </xsl:attribute>
    </xsl:if>
    <h3><a href="/interviews/{/data/interviews/entry[position()=$count]/business/@handle}"><xsl:value-of select="/data/interviews/entry[position()=$count]/name"/></a></h3>
    <p class="business_name"><xsl:value-of select="/data/interviews/entry[position()=$count]/business"/></p>
    <p><xsl:copy-of select="/data/interviews/entry[position()=$count]/excerpt/p/node()"/>
        <xsl:text> </xsl:text>
        <a href="/interviews/{/data/interviews/entry[position()=$count]/business/@handle}">&#8594;Watch</a>
        </p>
</div>
</xsl:if>
<xsl:if test="$count &lt; 4">
    <xsl:call-template name="latest">
        <xsl:with-param name="count" select="$count + 1"/>
    </xsl:call-template>
</xsl:if>
</xsl:template>

But it didn’t work because the count would increment when nothing was outputting so I’d end up with one too few entries out putted. Or it would just stop at the current entry if I tried logic on the count itself…. If I could decrease $count by one somehow when it’s not outputting anything that would work but I have no idea how to do that!

So then I tried using for-each but limiting it to the first 4 positions:

ie very similar to above but

    <xsl:param name="current-int" select="/data/single-interview/entry/order"/>

<xsl:for-each select="/data/interviews/entry[order != $current-int and position() &lt; 5]">
CONTENT
</xsl:for-each>

But for some reason that doesn’t work either (haven’t got to the bottom of why to be honest?). It only outputs two entries (if i was viewing one of the first four) or three entries if I was viewing the 5th or later entry.

So now I’m stumped! All the xpath is right as stuff is being outputted but I can never seem to get the right number of entries no matter what I do.

Sorry this has been such a mega long post - just wanted to provide as much info as possible!

Any help very much appreciated!

I think there is a simpler way to do this:

<xsl:choose>
    <xsl:when test="$name">
        <xsl:apply-templates select="interviews/entry[position() &lt; 4][name/@handle != $name]" />
    </xsl:when>
    <xsl:otherwise>
        <xsl:apply-templates select="interviews/entry[position() &lt; 5][name/@handle != $name]" />
    </xsl:otherwise>
</xsl:choose>

where you have a template that matches the interviews/entry node:

<xsl:template match="interviews/entry">
    <article class="interview">
        <h1><xsl:value-of select="name" /></h1>
        <xsl:copy-of select="excerpt/*" />
    </article>
</xsl:template>

Edited to better match your XML.

I’m assuming that you are filtering the single interview by a URL parameter called $name. Did I guess correctly?

I’ve been rereading your question and my response and realized that all you really need should be the following (although this untested and just off the top of my head):

To list four excerpts, you should be able to use the following apply-templates instruction:

<xsl:apply-templates select="/data/interviews/entry[position() &lt; 5][name/@handle != $name]" mode="excerpts" />

Then, the following match template should provide the expected output:

<xsl:template match="/data/interviews/entry" mode="excerpts">
    <div class="threecol">
        <xsl:if test="$count=4">
            <xsl:attribute name="class">
                <xsl:text>threecol last</xsl:text>
            </xsl:attribute>
        </xsl:if>
        <h3><a href="/interviews/{business/@handle}"><xsl:value-of select="name"/></a></h3>
        <p class="business_name"><xsl:value-of select="business"/></p>
        <p><xsl:copy-of select="excerpt/p/node()"/>
            <xsl:text> </xsl:text>
            <a href="/interviews/{business/@handle}">&#8594;Watch</a>
        </p>
    </div>
</xsl:template>

There’s no need to use recursion or count your entries if you do your sorting in the data source and limit the output to the 5 latest entries. This should always output the latest 4 interviews.

Note: Not knowing the context, I modified this to match the root node, data.

Also, you probably don’t need the mode="excerpts" attribute on the match template. I just threw it in there, just in case you had other templates that might be matching that particular node.

Thanks!

Yes you are right on the $name but I’m filtering the single interview by the business name which has a variable name of $interview (just to totally confuse things!) - but that is a quick fix!

Just tried your last idea but I’m having the same issue as before. Ie when I’m viewing one of the four most recent entries I only get 3 excerpts being displayed….

The reason I think is that we are using position() to determine the number of excerpts to be outputted. However, the filtering of the data source in XSLT doesn’t change the actual position of an entry.

I.E if we assume we are currently viewing the entry that is in position 1 of interviews. XSLT still considers that to be position 1 regards of if we filter it out or not. So when it gets to ‘position 4’ it stops even though it didn’t output the content from position 1.

I hope that makes sense - i’m not explaining it very well.

So i’m going to experiment with the xsl:choose idea you suggested but modify it to try and change the output limit depending on if we are viewing one of the four most recent entries or not.

My brain hurts…

Ok so I got it working! The xsl:choose was the key.

This is what I have.

<xsl:template name="latest">
<xsl:param name="current-int" select="/data/single-interview/entry/order"/>
<xsl:param name="latest-entry" select="/data/interviews/entry[1]/order"/>
<xsl:choose>
<xsl:when test="$current-int &gt; $latest-entry - 5">
    <xsl:apply-templates select="/data/interviews/entry[position() &lt; 6][business/@handle != $interview]" />
</xsl:when>
<xsl:otherwise>
    <xsl:apply-templates select="/data/interviews/entry[position() &lt; 5][business/@handle != $interview]" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>

With a template match that looks like this:

<xsl:template match="/data/interviews/entry">
<div class="threecol">
    <xsl:if test="position() = 4">
        <xsl:attribute name="class">
            <xsl:text>threecol last</xsl:text>
        </xsl:attribute>
    </xsl:if>
    <a href="/interviews/{business/@handle}">
    <h3><xsl:value-of select="name"/></h3>
    <p class="business_name"><xsl:value-of select="business"/></p>
    <p><xsl:copy-of select="excerpt/p/node()"/>
        </p>
            </a>
</div>
</xsl:template>

So i’m checking to see if the current entry is one of the first four entries and then outputting one extra to compensate for missing out entry out.

I’m limiting it to 6 and 5 because the position seems to start from 1 not from zero. So it has to be &lt: 5 to get four things out and < 6 when I’m skipping one.

The weird thing is that in the template-match, the test for position()=4still works even though in the case where the current interview is one of the four most recent it’s actually outputting content from the 5th position? Which is extra confusing!

If anyone can think of a better way to do this I’d love to hear it - seems like there should be something really simple but I can’t think of it.

Thanks for the help and ideas! Definitely helped finally crack it.

T

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details