XSL check param length and make a choice - xslt

I need to check if a param has got a value in it and if it has then do this line otherwise do this line.
I've got it working whereas I don't get errors but it's not taking the right branch
The branch that is wrong is in the volunteer_role template
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="volunteers-by-region" match="volunteer" use="region" />
<xsl:template name="hoo" match="/">
<html>
<head>
<title>Registered Volunteers</title>
<link rel="stylesheet" type="text/css" href="volunteer.css" />
</head>
<body>
<h1>Registered Volunteers</h1>
<h3>Ordered by the username ascending</h3>
<xsl:for-each select="folktask/member[user/account/userlevel='2']">
<xsl:for-each select="volunteer[count(. | key('volunteers-by-region', region)[1]) = 1]">
<xsl:sort select="region" />
<xsl:for-each select="key('volunteers-by-region', region)">
<xsl:sort select="folktask/member/user/personal/name" />
<div class="userdiv">
<xsl:call-template name="volunteer_volid">
<xsl:with-param name="volid" select="../volunteer/#id" />
</xsl:call-template>
<xsl:call-template name="volunteer_role">
<xsl:with-param name="volrole" select="../volunteer/roles" />
</xsl:call-template>
<xsl:call-template name="volunteer_region">
<xsl:with-param name="volloc" select="../volunteer/region" />
</xsl:call-template>
</div>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
<xsl:if test="position()=last()">
<div class="count">
<h2>Total number of volunteers: <xsl:value-of select="count(/folktask/member/user/account/userlevel[text()=2])" />
</h2>
</div>
</xsl:if>
</body>
</html>
</xsl:template>
<xsl:template name="volunteer_volid">
<xsl:param name="volid" select="'Not Available'" />
<div class="heading2 bold"><h2>VOLUNTEER ID: <xsl:value-of select="$volid" /></h2></div>
</xsl:template>
<xsl:template name="volunteer_role">
<xsl:param name="volrole" select="'Not Available'" />
<div class="small bold">ROLES:</div>
<div class="large">
<xsl:choose>
<xsl:when test="string-length($volrole)!=0">
<xsl:value-of select="$volrole" />
</xsl:when>
<xsl:otherwise>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:template>
<xsl:template name="volunteer_region">
<xsl:param name="volloc" select="'Not Available'" />
<div class="small bold">REGION:</div>
<div class="large"><xsl:value-of select="$volloc" /></div>
</xsl:template>
</xsl:stylesheet>
And the XML:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="volunteers.xsl"?>
<folktask xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="folktask.xsd">
<member>
<user id="1">
<personal>
<name>Abbie Hunt</name>
<sex>Female</sex>
<address1>108 Access Road</address1>
<address2></address2>
<city>Wells</city>
<county>Somerset</county>
<postcode>BA5 8GH</postcode>
<telephone>01528927616</telephone>
<mobile>07085252492</mobile>
<email>adrock#gmail.com</email>
</personal>
<account>
<username>AdRock</username>
<password>269eb625e2f0cf6fae9a29434c12a89f</password>
<userlevel>4</userlevel>
<signupdate>2010-03-26T09:23:50</signupdate>
</account>
</user>
<volunteer id="1">
<roles></roles>
<region>South West</region>
</volunteer>
</member>
<member>
<user id="2">
<personal>
<name>Aidan Harris</name>
<sex>Male</sex>
<address1>103 Aiken Street</address1>
<address2></address2>
<city>Chichester</city>
<county>Sussex</county>
<postcode>PO19 4DS</postcode>
<telephone>01905149894</telephone>
<mobile>07784467941</mobile>
<email>ambientexpert#yahoo.co.uk</email>
</personal>
<account>
<username>AmbientExpert</username>
<password>8e64214160e9dd14ae2a6d9f700004a6</password>
<userlevel>2</userlevel>
<signupdate>2010-03-26T09:23:50</signupdate>
</account>
</user>
<volunteer id="2">
<roles>Van Driver,gas Fitter</roles>
<region>South Central</region>
</volunteer>
</member>
</folktask>

The following should do the trick:
<xsl:template name="volunteer_volid">
<xsl:param name="volid" />
<xsl:choose>
<xsl:when test="string-length($volid) > 0">
<div class="heading2 bold"><h2>VOLUNTEER ID: <xsl:value-of select="$volid" /></h2></div>
</xsl:when>
<xsl:otherwise>
<!-- No volid -->
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I've replaced the default value with an empty string so that not providing a parameter value is the same as providing the parameter value as "". If this isn't the desired behaviour then change the parameters select and modify the test expression accordingly, for example:
$volid != 'Not Available'

Related

XSLT - Choose between one of 2 Sitecore fields

I am very new to XSLT and am trying to edit some code that sets up the breadcrumb navigation for a Sitecore site. Right now, it's set to get the value of the Menu Title field and use that for the breadcrumb navigation. However, there are templates that don't have this field, so I would like to have the option to get the value of Page Title's field in these cases.
I can do this sort of thing with C#, but XSLT is completely different. Any suggestions?
Here is the complete XSLT file:
<?xml version="1.0" encoding="UTF-8"?>
<!--=============================================================
File: BreadCrumb.xslt
Copyright notice at bottom of file
==============================================================-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sc="http://www.sitecore.net/sc" xmlns:dot="http://www.sitecore.net/dot" exclude-result-prefixes="dot sc">
<!-- output directives -->
<xsl:output method="html" indent="no" encoding="UTF-8" />
<!-- parameters -->
<xsl:param name="lang" select="'en'" />
<xsl:param name="id" select="''" />
<xsl:param name="sc_item" />
<xsl:param name="sc_currentitem" />
<!-- variables -->
<!-- Uncomment one of the following lines if you need a "home" variable in you code -->
<!--<xsl:variable name="home" select="sc:item('/sitecore/content/home',.)" />-->
<!--<xsl:variable name="home" select="/*/item[#key='content']/item[#key='home']" />-->
<!--<xsl:variable name="home" select="$sc_currentitem/ancestor-or-self::item[#template='site root']" />-->
<!-- entry point -->
<xsl:template match="*">
<xsl:apply-templates select="$sc_item" mode="main" />
</xsl:template>
<!--==============================================================-->
<!-- main-->
<xsl:template match="*" mode="main">
<!--==============================================================-->
<div class="bread-crumb-box" style="margin-left:auto; margin-right:auto; margin-top: -25px;">
<div class="container" style="width:100%">
<div class="row">
<small style="font-size:12px; float:left">
Home
<xsl:for-each select="ancestor-or-self::item">
<xsl:if test="position() > 2 and #template != 'folder' and #template != 'blogtypefolder'">
<xsl:choose>
<xsl:when test="position() != last()">
<sc:link class="white">
<xsl:call-template name="GetNavTitle" />
</sc:link>
</xsl:when>
<xsl:otherwise>
<!--<strong>-->
<xsl:call-template name="GetNavTitle" />
<!--</strong>-->
</xsl:otherwise>
</xsl:choose>
<xsl:if test="position() != last()">
>
</xsl:if>
</xsl:if>
</xsl:for-each>
</small>
</div>
</div>
</div>
</xsl:template>
<!--This part is what needs to be adjusted, I believe-->
<xsl:template name="GetNavTitle">
<xsl:param name="item" select="." />
<xsl:choose>
<xsl:when test="sc:fld( 'navtitle', $item )">
<xsl:value-of select="sc:fld( 'Menu Title', $item )" />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="holder" select="$item/#id" />
<!--<xsl:value-of select="$item/#name" />-->
<xsl:value-of select="sc:fld('Menu Title', sc:item($holder,.))" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Your XSLT needs to be changed to this:
<xsl:choose>
<xsl:when test="sc:fld( 'Menu Title', $item ) != '' ">
<xsl:value-of select="sc:fld( 'Menu Title', $item )" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="sc:fld('Page Title', $item )" />
</xsl:otherwise>
</xsl:choose>

XSLT Not working

Hi my XML document and XSLT are not working to produce good HTML... what is going on?
This is basically XML file and I have validated this with the XML Schema but
when I use XSLT file to convert it into HTML file it just generates Courses Catalogue heading
with one paragraph of whole bunch of text.
What kinds of problem Do I have here?
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Generate HTML output -->
<xsl:output method="html"/>
<!-- The root template is defined here -->
<xsl:template match="/">
<html>
<head>
<title>Courses Catalogue</title>
</head>
<body>
<h2>Courses catalogue</h2>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="course">
<p>
<xsl:apply-templates select="code" />
<xsl:apply-templates select="title" />
<xsl:apply-templates select="year" />
<xsl:apply-templates select="science" />
<xsl:apply-templates select="area" />
<xsl:apply-templates select="subject" />
<xsl:apply-templates select="updated" />
<xsl:apply-templates select="unit" />
<xsl:apply-templates select="description" />
<xsl:apply-templates select="outcomes" />
<xsl:apply-templates select="incompatibility" />
</p>
</xsl:template>
<xsl:template match="code">
Course Code: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="title">
Course Title: <span style="color:#000">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="year">
Student Year: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="science">
Science Group: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="area">
Area: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="subject">
Course Subject: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="updated">
Page was updated in: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="unit">
Unit: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="description">
Course Description: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="outcomes">
Course Outcomes: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
< xsl:template match="incompatibility">
Incompatible courses: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
</xsl:stylesheet>
AND MY XML FILE
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl"
href="courses.xsl"?>
<!--catalogue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="catalogue.xsd"-->
<catalogue xmlns="file://"
xmlns:xsi="http:/e"
xsi:schemaLocation="file://">
<course>
<code>COMP3410</code>
<title> Information Technology in Electronic Commerce </title>
<year>later year</year>
<science>C</science>
<area> Research School of Computer Science </area>
<subject> Computer Science </subject>
<updated>2012-03-13T13:12:00</updated>
<unit>6</unit>
Thanks
Your problem is that all of the elements are in the file://Volumes/u1234567/Assignment namespace, but in your XSLT your templates are matching on elements without a namespace.
If you look closely at the <catalogue> you will see a namespace declaration without a prefix. <catalogue xmlns="file://Volumes/u1234567/Assignment" All of the descendant elements inherit that namespace.
Define that namespace with a prefix in your XSLT and then change the places where you refer to those elements to use that namespace prefix:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="file://Volumes/u1234567/Assignment">
<!-- Generate HTML output -->
<xsl:output method="html"/>
<!-- The root template is defined here -->
<xsl:template match="/">
<html>
<head>
<title>Courses Catalogue</title>
</head>
<body>
<h2>Courses catalogue</h2>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="a:course">
<p>
<xsl:apply-templates select="a:code" />
<xsl:apply-templates select="a:title" />
<xsl:apply-templates select="a:year" />
<xsl:apply-templates select="a:science" />
<xsl:apply-templates select="a:area" />
<xsl:apply-templates select="a:subject" />
<xsl:apply-templates select="a:updated" />
<xsl:apply-templates select="a:unit" />
<xsl:apply-templates select="a:description" />
<xsl:apply-templates select="a:outcomes" />
<xsl:apply-templates select="a:incompatibility" />
</p>
</xsl:template>
<xsl:template match="a:code">
Course Code:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:title">
Course Title:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:year">
Student Year: <span style="color:#C66">
<xsl:value-of select="." /> </span>
<br />
</xsl:template>
<xsl:template match="a:science">
Science Group:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:area">
Area:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:subject">
Course Subject:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:updated">
Page was updated in:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:unit">
Unit:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:description">
Course Description:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:outcomes">
Course Outcomes:
<xsl:value-of select="." />
<br />
</xsl:template>
<xsl:template match="a:incompatibility">
Incompatible courses:
<xsl:value-of select="." />
<br />
</xsl:template>
</xsl:stylesheet>

Xslt extracting attributes and generating html

I'm trying to transform the following XML..
<?xml version="1.0" encoding="utf-16"?><Member TextRank="unknown" FullName="My Name" ..etc.. />
Into something like the following,
<div class="member">
<span class="label">
Text Rank (note: i want to override the labels in the xslt)
</div>
<span class="value">
unknown
</span>
<span class="label">
Full Name
</div>
<span class="value">
My Name
</span>
..etc..
</div>
How if possible could I do this using xslt?
Here's a different approach, that does away for the need for the xsl:choose element. Instead, you could take advantage of the matching templates to have specific templates for the cases of attributes who names you want to override, and a generic template for other case.
To avoid repetition of code, you could also make the generic template a named template, with a parameter to override the name
<xsl:template match="#*" name="attribute">
<xsl:param name="label" select="local-name()" />
So, the default for most attributes would be to use the attribute name, but the specific template for #FullName could then call this with a different name.
<xsl:template match="#FullName">
<xsl:call-template name="attribute">
<xsl:with-param name="label" select="'Full Name'" />
</xsl:call-template>
</xsl:template>
Here is the full XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="*">
<div class="{local-name()}">
<div> Title: </div>
<xsl:apply-templates select="#*"/>
</div>
</xsl:template>
<xsl:template match="#FullName">
<xsl:call-template name="attribute">
<xsl:with-param name="label" select="'Full Name'" />
</xsl:call-template>
</xsl:template>
<xsl:template match="#*" name="attribute">
<xsl:param name="label" select="local-name()" />
<span class="label">
<xsl:value-of select="concat($label, ' : ')"/>
</span>
<span class="value">
<xsl:value-of select="."/>
</span>
<br/>
</xsl:template>
</xsl:stylesheet>
When applied to the following XML:
<Member TextRank="unknown" ID="12" FullName="My Name" Dob="01/01/1970" />
The following is output:
<div class="Member">
<div> Title: </div>
<span class="label">TextRank : </span>
<span class="value">unknown</span>
<br>
<span class="label">ID : </span>
<span class="value">12</span>
<br>
<span class="label">Full Name : </span>
<span class="value">My Name</span>
<br>
<span class="label">Dob : </span>
<span class="value">01/01/1970</span>
<br>
</div>
This is the solution I came up with.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" />
<xsl:template match="*">
<xsl:element name="div">
<xsl:attribute name="class">className</xsl:attribute>
<div>
Title:
</div>
<!-- UID, Name, DBO-->
<xsl:apply-templates select="#ID"/>
<xsl:apply-templates select="#FullName"/>
<xsl:apply-templates select="#Dob"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:element name="span">
<xsl:attribute name="class">label</xsl:attribute>
<xsl:choose>
<xsl:when test="name() = 'FullName'">
Full Name
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name()"/>
</xsl:otherwise>
</xsl:choose>
:
</xsl:element>
<xsl:element name="span">
<xsl:attribute name="class">value</xsl:attribute>
<xsl:value-of select="."/>
</xsl:element>
<br/>
</xsl:template>
</xsl:stylesheet>

Extreme XSLT XML Flattening

I have the following input XML file:
<root>
<a>
<b>1</b>
</a>
<c>
<d>
<e>2</e>
<f>3</f> or <e>3</e>
</d>
<g h="4"/>
<i>
<j>
<k>
<l m="5" n="6" o="7" />
<l m="8" n="9" o="0" />
</k>
</j>
</i>
</c>
</root>
I would like to use XSLT to transform it into the follow outputs:
OUTPUT 1
<root>
<row b="1" e="2" f="3" h="4" m="5" n="6" o="7" />
<row b="1" e="2" f="3" h="4" m="8" n="9" o="0" />
<root>
OUTPUT 2
<root>
<row b="1" e="2" h="4" m="5" n="6" o="7" />
<row b="1" e="2" h="4" m="8" n="9" o="0" />
<row b="1" e="3" h="4" m="5" n="6" o="7" />
<row b="1" e="3" h="4" m="8" n="9" o="0" />
<root>
Can anyone help my XSLT isn't very strong. Thanks.
It will be easier if you let the occurrence of <e> determine the outer loop constructing your <row>s and have a inner loop iterating over all <l>s.
Try something like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="root">
<root>
<xsl:apply-templates />
</root>
</xsl:template>
<xsl:template match="e">
<!-- store values of 'e' and 'f' in params -->
<xsl:param name="value_of_e" select="." />
<xsl:param name="value_of_f" select="ancestor::d[1]/f" />
<!-- iterate over all 'l's -->
<xsl:for-each select="//l">
<xsl:element name="row">
<xsl:attribute name="b">
<xsl:value-of select="//b" />
</xsl:attribute>
<xsl:attribute name="e">
<xsl:value-of select="$value_of_e" />
</xsl:attribute>
<!-- only include 'f' if it has a value -->
<xsl:if test="$value_of_f != ''">
<xsl:attribute name="f">
<xsl:value-of select="$value_of_f" />
</xsl:attribute>
</xsl:if>
<xsl:attribute name="h">
<xsl:value-of select="ancestor::c[1]/g/#h" />
</xsl:attribute>
<xsl:attribute name="m">
<xsl:value-of select="./#m" />
</xsl:attribute>
<xsl:attribute name="n">
<xsl:value-of select="./#n" />
</xsl:attribute>
<xsl:attribute name="o">
<xsl:value-of select="./#o" />
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="b" />
<xsl:template match="f" />
<xsl:template match="g" />
</xsl:stylesheet>

Sorting a nodeset before passing to xsl:for-each

I have a situation where loop through a sorted nodeset and apply a template on each of the nodes:
<div id="contractscontainer">
<xsl:for-each select="document">
<xsl:sort select="content[#name='ClientName']/text()" />
<xsl:apply-templates select="." mode="client-contract" />
</xsl:for-each>
</div>
I want to do something special with the "first" 5 nodes in the node set and render them nested element. The problem is that they need to be in the same order as if they were sorted (as they are in the loop).
I had planned on doing this by using two xsl:for-each elements, each with the correct nodes selected from the set. I can't do this, however, because they need to be sorted before I can select the "first" 5.
Example:
<div id="contractscontainer">
<div class="first-five">
<xsl:for-each select="document[position() < 6]">
<xsl:sort select="content[#name='ClientName']/text()" />
<xsl:apply-templates select="." mode="client-contract" />
</xsl:for-each>
</div>
<div class="rest-of-them">
<xsl:for-each select="document[position() > 5]">
<xsl:sort select="content[#name='ClientName']/text()" />
<xsl:apply-templates select="." mode="client-contract" />
</xsl:for-each>
</div>
</div>
I don't think this will work because I'm selecting the nodes by position before sorting them, but I can't use xsl:sort outside of the xsl:for-each.
Am I approaching this incorrectly?
Edit: My current solution is to sort them and store the sorted set in another variable:
<xsl:variable name="sorted-docs">
<xsl:for-each select="document">
<xsl:sort select="content[#name='ClientName']/text()" />
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:variable>
It works, but is there a better way?
Here is one way to do this in XSLT 1.0 without the need to use the xxx:node-set() extension function:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<html>
<div class="first-five">
<xsl:apply-templates select="num">
<xsl:sort select="." data-type="number"/>
<xsl:with-param name="pStart" select="1"/>
<xsl:with-param name="pEnd" select="5"/>
</xsl:apply-templates>
</div>
<div class="rest-of-them">
<xsl:apply-templates select="num">
<xsl:sort select="." data-type="number"/>
<xsl:with-param name="pStart" select="6"/>
</xsl:apply-templates>
</div>
</html>
</xsl:template>
<xsl:template match="num">
<xsl:param name="pStart"/>
<xsl:param name="pEnd" select="999999999999"/>
<xsl:if test="position() >= $pStart
and
not(position() > $pEnd)">
<p><xsl:value-of select="."/></p>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When the above transformation is applied on this simple XML document:
<nums>
<num>5</num>
<num>3</num>
<num>6</num>
<num>8</num>
<num>4</num>
<num>1</num>
<num>9</num>
<num>2</num>
<num>7</num>
<num>10</num>
</nums>
the wanted result is produced:
<html>
<div class="first-five">
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
</div>
<div class="rest-of-them">
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>
</div>
</html>