Search

Sorry to start another post, but it seems like my last post kinda morphed into a discussion of tutorials/documentation, etc.

I am wondering how to grab out only the year from my date XML. Here’s what a snippet of my XML looks like:

<entry id="116">
            <excerpt word-count="21">This was the excerpt</excerpt>
            <date time="00:00" weekday="4">2009-02-26</date>
            <headline handle="morsekode-focuses-on-brand-integration">Morsekode Focuses on Brand Integration</headline>
            <subheadline handle="promotes-paul-jongeward-to-director">Promotes Paul Jongeward to Director</subheadline>
            <content word-count="204">This was the long-winded content</content>
</entry>

I am using for-each statements right now to go through the XML and post the results on the blog listings page. I need to create a link though to the full content of the post, and I’m having trouble grabbing the year only. For example, here is my link I’m creating that isn’t working yet:

<A HREF="{$root}/pressbox/agencynews/{date@year}/{headline/@handle}/" class="FullStryLinkSelected">Full Story</A>

Any help would be appreciated. Thanks!

There’s a format-date utility built into the default workspace, you could use that like so:

<xsl:call-template name="format-date">
  <xsl:with-param name="date" select="entry/date"/>
  <xsl:with-param name="format" select=" 'Y' "/>
</xsl:call-template>

Or you could use substring:

<A HREF="{$root}/pressbox/agencynews/{substring(date,1,4)}/{headline/@handle}/" class="FullStryLinkSelected">Full Story</A>

You might have to use @date, I’m not sure :)

You will need to use a string function on the value of the date node. Either grab the first four characters, or grab the characters before the first hyphen. Either will do:

substring(date, 1, 4)

or

substring-before(date, '-')

Ok, got another question for you. As you set up a page, there’s the URL parameters field? I’m understanding how to use that and stuff, but I was wondering if it was possible to have an “either” situation in there. For example:

URL Parameters: type|industry|client/campaignname

Where it would take either of the fields type, industry, or client in the URL string, and then add the /campaignname later on when you clicked into a specific case study? So basically a URL could look like:

  • http://www.example.com/casestudies/a-type-name/a-specific-campaign or
  • http://www.example.com/casestudies/a-industry-name/a-specific-campaign or
  • http://www.example.com/casestudies/a-client-name/a-specific-campaign

You could use a nested page for this task. Have a look at the Parent Page setting.

The nested page is probably the way to go. Otherwise, you might try abstracting the URL parameters a little more so that the logic for selecting type, industry or client can be controlled by the page template.

If the URL parameters were a/b you could use these parameters to filter the data sources:

  • $a
  • $b

Then the page template could look something like this:

<xsl:choose>
    <xsl:when test="$b">
        <!-- Do something when $b has a value, 
            such as display entries that have a title handle
            that matches the $b parameter -->
        <xsl:choose>
            <xsl:when test="$a = 'type'">
                <xsl:for-each select="/data/casestudy/entry[category/item/@handle = $a and title/@handle = $b]">
                    <!-- A case study from the type category -->
                </xsl:for-each>
            </xsl:when>
            <xsl:when test="$a = 'industry'">
                <xsl:for-each select="/data/casestudy/entry[category/item/@handle = $a and title/@handle = $b]">
                    <!-- A case study from the industry category -->
                </xsl:for-each>
            </xsl:when>
            <xsl:when test="$a = 'client'">
                <xsl:for-each select="/data/casestudy/entry[category/item/@handle = $a and title/@handle = $b]">
                    <!-- A case study from the client category -->
                </xsl:for-each>
            </xsl:when>
        </xsl:choose>
    </xsl:when>
    <xsl:when test="$a">
        <!-- Do something when $a has a value and $b has no value, 
            such as display an overview of all entries 
            of a particular category -->
        <xsl:choose>
            <xsl:when test="$a = 'type'">
                <xsl:for-each select="/data/casestudies/entry[category/item/@handle = $a]">
                    <!-- A case study from the type category -->
                </xsl:for-each>
            </xsl:when>
            <xsl:when test="$a = 'industry'">
                <xsl:for-each select="/data/casestudies/entry[category/item/@handle = $a]">
                    <!-- A case study from the industry category -->
                </xsl:for-each>
            </xsl:when>
            <xsl:when test="$a = 'client'">
                <xsl:for-each select="/data/casestudies/entry[category/item/@handle = $a]">
                    <!-- A case study from the client category -->
                </xsl:for-each>
            </xsl:when>
        </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
        <!-- Do something when neither $a nor $b has a value, 
            such as an introduction to case studies -->
    </xsl:otherwise>
</xsl:choose>

Use two data sources: Case Studies and Case Study. Select the casestudies node for the overview data source. Select the casestudy node for the complete entry. Filter the Case Study data source to include the entry with the Title field that matches $a with a required URL parameter $b. In theory, that should work.

Edit: I added a test for $a so that you could have a default view for the Case Studies page.

Edited Again: I wasn’t selecting the @handle node properly. I first need to select the node, then the attribute: title/@handle.

Edited yet again: What a gong show! The code comments were pointing to the wrong categories.

This can actually be simplified to the following, since it accomplishes the same thing:

<xsl:choose>
    <xsl:when test="$b">
        <xsl:for-each select="/data/casestudy/entry[category = $a and title/@handle = $b]">
            <!-- A case study: full entry -->
        </xsl:for-each>
    </xsl:when>
    <xsl:when test="$a">
        <xsl:for-each select="/data/casestudies/entry[category = $a]">
            <!-- A case study: excerpt -->
        </xsl:for-each>
    </xsl:when>
    <xsl:otherwise>
        <!-- Do something when neither $a nor $b has a value, 
            such as an introduction to case studies -->
    </xsl:otherwise>
</xsl:choose>

Testing Node Values

Perhaps, the question was a little more complex than I first thought. The goal was not to specify a category, but a particular subcategory of a parent category. Then, it would be necessary to test the value of the items in each category field. Assuming that each category is a select box or select box link with its own field, then we can use the first example with some node selection in the conditionals. More than likely, the entry title will be unique for each entry, so the full case study requires very basic logic. The overviews might be a little different, since the value of the $a parameter needs to match the title handle of the item in each category.

<xsl:choose>
    <xsl:when test="$b">
        <!-- Do something when $b has a value, 
            such as display entries that have a title handle
            that matches the $b parameter -->
        <xsl:for-each select="/data/casestudy/entry[title/@handle = $b]">
            <!-- A case study selected by type -->
        </xsl:for-each>
    </xsl:when>
    <xsl:when test="$a">
        <!-- Do something when $a has a value and $b has no value, 
            such as display an overview of all entries 
            of a particular category -->
        <xsl:choose>
            <xsl:when test="$a = /data/casestudies/entry/type/item/@handle">
                <xsl:for-each select="/data/casestudies/entry[type/item/@handle = $a]">
                    <!-- A case study with an type title handle equal to $a -->
                </xsl:for-each>
            </xsl:when>
            <xsl:when test="$a = /data/casestudies/entry/industry/item/@handle">
                <xsl:for-each select="/data/casestudies/entry[industry/item/@handle = $a]">
                    <!-- A case study with an industry title handle equal to $a -->
                </xsl:for-each>
            </xsl:when>
            <xsl:when test="$a = /data/casestudies/entry/client/item/@handle">
                <xsl:for-each select="/data/casestudies/entry[client/item/@handle = $a]">
                    <!-- A case study with an client title handle equal to $a -->
                </xsl:for-each>
            </xsl:when>
        </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
        <!-- Do something when neither $a nor $b has a value, 
            such as an introduction to case studies -->
    </xsl:otherwise>
</xsl:choose>

Again, this could probably be simplified:

<xsl:choose>
    <xsl:when test="$b">
        <xsl:for-each select="/data/casestudy/entry[title/@handle = $b]">
            <!-- A case study selected by type -->
        </xsl:for-each>
    </xsl:when>
    <xsl:when test="$a">
        <xsl:for-each select="/data/casestudies/entry[type/item/@handle = $a]">
            <!-- A case study with a type title handle equal to $a -->
        </xsl:for-each>
        <xsl:for-each select="/data/casestudies/entry[industry/item/@handle = $a]">
            <!-- A case study with an industry title handle equal to $a -->
        </xsl:for-each>
        <xsl:for-each select="/data/casestudies/entry[client/item/@handle = $a]">
            <!-- A case study with a client title handle equal to $a -->
        </xsl:for-each>
    </xsl:when>
    <xsl:otherwise>
        <!-- Do something when neither $a nor $b has a value, 
            such as an introduction to case studies -->
    </xsl:otherwise>
</xsl:choose>

Wow! Just so you know I’ve been following your postings all afternoon. Thanks so much for taking so much time to think through my question with me! I was working on my site this afternoon as well. I didn’t actually see your last post until just now, but I ended up implementing something almost exactly like it. I had been inspired from your previous post and thought hmmm, I think I can make this more simple. Symphony just continues to amaze me! =D Thanks again, really appreciate it.

::sigh:: Sorry guys, I’m trying really hard to not just post first and let you guys answer my questions for me, but after spending about 4-5 hours on this stupid problem, I guess I need some help. I’ll keep it simple first, and hopefully I don’t need to get too specific into what I’m trying to do.

Basically I’d just love to find the current position of the “entry” node that I’m in and store it in a variable to use later. This code works, kinda:

<xsl:variable name="current-position" select="position()"/>

The problem is, it doesn’t pull the right position, it just pulls “1”, so I’m guessing that since I’m already in an entry node, I need to “back up”. I was trying to use code like this, but it returns XSLT errors:

<xsl:variable name="current-position" select="../position()"/>

Shouldn’t I be able to use Xpath logic here? I even tried really specific logic like this, but it didn’t work either:

<xsl:variable name="current-position" select="//casestudies/entry[campaign/@handle = $thecampaign]/position()"/>

Could anyone help me here? I apologize for all of the postings…

Where do you need the position for?
Variables in XSL are only available in the context of the current template - maybe this is your problem?

And the position of which node are you trying to fetch?

If you could, please post up an example XML and XSLT (a small working sample of what you are trying to achieve will do fine) and we’ll see what we can do to help.

Yeah, basically I’m trying to create a PREV/NEXT button when you’re on a detail page. You can see the Detail Page here, and the Listing page here.

Now that would be easy if the XML was already scaled down to only case studies that were “music” related, but I’ve got the full stream coming in.

In plain english, for the PREV button, I’m trying to link to the previous case study that includes my $variable somewhere in it (could be contained in one of three different fields). I’ve been trying to find the previous through position, though I suppose date could work as well) I’ve tried some stuff like this but without success:

<xsl:variable name="current-position" select="../position()"/>
<xsl:for-each select="../../casestudies/entry[1][*/item/@handle = $thesection and position() &lt; $current-position]">
<span class="PrvLink"><a href="{$root}/casestudies/{$thesection}/{campaign/@handle}/">PREV</a></span>
</xsl:for-each>

You can see the XML here (there will be many more entries later on, making a PREV/NEXT button more worth it) and the important parts of the XSLT here (I highlighted the code above).

Thanks guys, I really appreciate it. I’m almost done converting my companies’ site to Symphony from a crappy custom CMS some programmer made for us awhile ago.

I hesitated to respond to this, since I hadn’t tried it out. Your email prompted me to give this a try. Using the XML that you provided, I was not able to demonstrate the selection of a node set that matched the currently selected section, since each of the four items in the sample XML had different values for industry, client and category.

Instead, I have developed code that demonstrates previous and next buttons for each of the four entries. This code assumes that you have all your entries in the XML data with at least the campaign field and the category field.

Also, I’ve added the variables for the current position and the count for total number of entries. This allows the ability to add logic to display the next and previous buttons only when there are entries to select.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
    omit-xml-declaration="yes"
    encoding="UTF-8"
    indent="yes" />

<xsl:template match="/">
    <html>
        <head>
            <title>Case Studies</title>
        </head>
        <body>
            <h1>Case Studies</h1>

            <h3>Menu</h3>
            <ul>
                <xsl:for-each select="data/casestudies/entry">
                    <li><a href="{$root}/casestudies/{category/item/@handle}/{campaign/@handle}/"><xsl:value-of select="campaign"/></a></li>
                </xsl:for-each>
            </ul>

            <h3>Next/Previous</h3>
            <xsl:for-each select="data/casestudies/entry">
                <xsl:call-template name="next-previous-links"/>
            </xsl:for-each>

            <xsl:choose>
                <xsl:when test="$entry">
                    <xsl:for-each select="data/casestudies/entry[category/item/@handle = $thesection and campaign/@handle = $entry]">

                        <h2><xsl:value-of select="campaign"/></h2>
                        <h3>Strategy</h3>
                        <p><xsl:value-of select="strategy"/></p>

                    </xsl:for-each>
                </xsl:when>
            </xsl:choose>
        </body>
    </html>
</xsl:template>

<xsl:template name="next-previous-links">
    <xsl:variable name="current-position" select="position()"/>
    <xsl:variable name="total-entries" select="count(../entry)"/>
    <xsl:for-each select="current()[//item/@handle = $thesection and campaign/@handle = $entry]">
        <p>Current position: <xsl:value-of select="$current-position"/></p>
        <p>Total entries: <xsl:value-of select="$total-entries"/></p>
        <ul>
            <xsl:if test="$current-position &gt; 1">
                <xsl:for-each select="../entry[position() = $current-position - 1]">
                    <li><a href="{$root}/casestudies/{category/item/@handle}/{campaign/@handle}/">Previous</a></li>
                </xsl:for-each>
            </xsl:if>
            <xsl:if test="$current-position &lt; $total-entries">
                <xsl:for-each select="../entry[position() = $current-position + 1]">
                    <li><a href="{$root}/casestudies/{category/item/@handle}/{campaign/@handle}/">Next</a></li>
                </xsl:for-each>
            </xsl:if>
        </ul>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

I’ve not read this thread fully, but a neat way of determining prev/next links:

<xsl:variable name="next" select="/data/case-studies/entry[@id=current()/@id]/following-sibling::entry[1]" />
<xsl:variable name="previous" select="/data/case-studies/entry[@id=current()/@id]/preceding-sibling::entry[1]" />

The XPath expression for the $next variable selects the current entry (/data/case-studies/entry[@id=current()/@id]), then all sibling entry nodes after it (i.e. all following entry nodes that share the same parent) (following-sibling::entry) and grabs the first one only ([1]).

Testing whether these variable values exist or not will tell you whether you are at the beginning or end, thereby choosing whether or not there is a next/previous to link to.

<xsl:if test="$previous">
   <!-- write previous link -->
</xsl:if>

Thanks, Nick. Very elegant.

I wanted to try this out as a DesignProjectX experiment. Since I have a couple data sources for the Tutorials page, one for lists of tutorials and another for the currently selected entry, I came up with this:

<ul class="menu">
    <xsl:for-each select="entry/entry">
        <xsl:call-template name="previous-next-links">
            <xsl:with-param name="current-entry-id" select="@id"/>
        </xsl:call-template>
    </xsl:for-each>
</ul>

The template looks like this:

<xsl:template name="previous-next-links">
    <xsl:param name="current-entry-id"/>
    <xsl:variable name="next" select="/data/tutorials/entry[@id = $current-entry-id]/following-sibling::entry[1]" />
    <xsl:variable name="previous" select="/data/tutorials/entry[@id = $current-entry-id]/preceding-sibling::entry[1]" />
    <ul class="menu">
        <xsl:if test="$previous">
            <li><a href="{$root}/tutorials/{$previous/title/@handle}/">Previous</a></li>
        </xsl:if>
        <xsl:if test="$next">
            <li><a href="{$root}/tutorials/{$next/title/@handle}/">Next</a></li>
        </xsl:if>
    </ul>
</xsl:template>

This required sorting at the data source, whereas I had been sorting in the XSLT template, so I needed to modify the data source first. Now, this is working well for the Tutorials section entries.

Thanks a ton, I really appreciate you guys taking the time to answer my question. I haven’t responded yet because I’m still digesting the solution you wrote. I got it working on my site, but I don’t think it factors in that I need to find the next/previous that are only in the industry/client/discipline that is being browsed. (EDIT: The code looks like it should, but I wasn’t getting it to work).

I think a good solution for me will be to change my URL structure in order to add in three different pages, one for industry, one for discipline, and one for client, that way I create datasources that only pull case studies in those areas.

Once again, thanks a bunch for your help.

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