XSLT: trying to do a contains() with a full XPath. Why does this fail? - xslt

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" />
<xsl:template match="/">
<html>
<body>
<span>
<div style="background-color:#000066;color:#EEEEEE;padding:7px">
<a name="top" style="padding-left:10px;font-size:28pt">Alerting Variables</a>
</div>
</span>
<div style="display:block;padding-left:50px;padding-bottom:10px" class="hbuttons">
LDMS Alerts
LDSM Alerts
Why?
Examples
Resources
</div>
<div style="clear: left;"></div>
<!-- This is the Table of Contents-->
<div style="padding:5px">
<div style="padding:5px;margin-top:10pt;margin-bottom:10pt;font-weight:bold;font-size:20px">Table of Contents -
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintAll.html">
<img border="0" src="images/PrintButton.png" />
</a></div>
<div style="font-family:Arial;font-weight:bold;margin-left:30px;font-size:10pt">
<xsl:if test="contains(identifiers/sectionname/alert/#name, 'Agent Watcher')">
Agent Watcher
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintAW.html">
<img border="0" src="images/PrintButton.png" />
</a>
</xsl:if>
<ol style="margin-top:5">
<xsl:for-each select="identifiers/sectionname/alert">
<xsl:if test="contains(#name, 'Agent Watcher')">
<li style="margin-left:10pt;font-size:8pt">
<a>
<xsl:attribute name="href">#
<xsl:value-of select="#name" /></xsl:attribute>
<xsl:value-of select="#name" />
</a>
</li>
</xsl:if>
</xsl:for-each>
</ol>
</div>
<div style="font-family:Arial;font-weight:bold;margin-left:30px;font-size:10pt">
Intel vPro
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintvPro.html">
<img border="0" src="images/PrintButton.png" />
</a>
<ol style="margin-top:5">
<xsl:for-each select="identifiers/SectionName/alert">
<xsl:if test="contains(#name, 'Intel vPro')">
<li style="margin-left:10pt;font-size:8pt">
<a>
<xsl:attribute name="href">#
<xsl:value-of select="#name" /></xsl:attribute>
<xsl:value-of select="#name" />
</a>
</li>
</xsl:if>
</xsl:for-each>
</ol>
</div>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Above is my code sample.
The first xsl:if statement always fails and never shows the Agent Watcher text or print me button. Even if the section is filled out in the XML. If the section is there, the first xsl:if statement fails, but the second one, contained in the xsl:for-each shows the content. How do I get this to work.
I want to have it encompassing so that if the XML has content in the section it will put it up but if not it wont be empty content with a header or vice versa. Attaching sample XML to process.
<identifiers>
<sectionname>
<alert name="Agent Watcher Service Startup"></alert>
<alert name="Agent Watcher Service Not Started"></alert>
<alert name="Agent Watcher Service Uninstalled"></alert>
<alert name="Agent Watcher File Deleted"></alert>
</sectionname>
<sectionname>
<alert name="Intel vPro agentless discovery failure"></alert>
<alert name="Intel vPro System Defense Remediation Alert"></alert>
<alert name="Intel vPro Enhanced System Defense Remediation Alert"></alert>
<alert name="Intel vPro Enhanced System Defense Alert"></alert>
</sectionname>
</identifiers>
Blockquote

I have a few other suggestions but you need to post the entire (relevant) XSLT before I can go on. At least the enclosing template is necessary.
EDIT: Here is my proposal for your stylesheet:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tmp="http://tempuri.org"
exclude-result-prefixes="tmp"
>
<tmp:config>
<tmp:alert label="Agent Watcher" link="PrintPages/PrintAW.html" />
<tmp:alert label="Intel vPro" link="PrintPages/PrintvPro.html" />
</tmp:config>
<xsl:variable name="everyAlert" select="
/identifiers/sectionname/alert
" />
<xsl:template match="/">
<html>
<body>
<!-- 8< snip -->
<div style="...">
<div style="...">
<xsl:text>Table of Contents - </xsl:text>
<a style="..." href="PrintPages/PrintAll.html">
<img border="0" src="images/PrintButton.png" />
</a>
</div>
<xsl:for-each select="document('')/*/tmp:config/tmp:alert">
<xsl:call-template name="section" />
</xsl:for-each>
</div>
</body>
</html>
</xsl:template>
<xsl:template name="section">
<xsl:variable name="this" select="." />
<xsl:variable name="alerts" select="
$everyAlert[contains(#name, $this/#label)]
" />
<xsl:if test="$alerts">
<div style="...">
<a href="#{translate($this/#label, ' ', '_')}">
<xsl:value-of select="$this/#label" />
</a>
<a style="..." href="{$this/#link}">
<img border="0" src="images/PrintButton.png" />
</a>
<ol style="...">
<xsl:for-each select="$alerts">
<li style="...">
<xsl:value-of select="#name" />
</li>
</xsl:for-each>
</ol>
</div>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Key features:
efficient code reuse through a named template
printed sections are easily configurable
uses <xsl:text> elements to avoid unwanted whitespace in the output while retaining freedom to format the XSLT code properly
uses attribute literal notation (the curly braces {}) instead of verbose <xsl:attribute> elements
uses a temporary namespace to allow storing config data in the stylesheet itself
uses an <xsl:for-each> loop and the document() function to retrieve and work with that config data
the for-each makes use of the context to transport the current #label and #link so no <xsl:param> is necessary (the <xsl:template name="section"> runs in tmp:config/tmp:alert context, not in sectionname/alert context!)
uses a global variable ($everyAlert) to store all nodes for later use

Related

xslt - Date Format

I'm having some issue with my xslt code and I was Wondering if someone could take a look. I think the problem is with the date formatting but i'm not sure.
I need to format a datetime into a day and month.
this is my code:
<xsl:stylesheet
version="1.0"
exclude-result-prefixes="x d xsl msxsl cmswrt"
xmlns:x="http://www.w3.org/2001/XMLSchema"
xmlns:d="http://schemas.microsoft.com/sharepoint/dsp"
xmlns:cmswrt="http://schemas.microsoft.com/WebParts/v3/Publishing/runtime"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime">
<xsl:template name="NewsAndEvents" match="Row[#Style='NewsAndEvents']" mode="itemstyle">
<xsl:variable name="SafeImageUrl">
<xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
<xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="DisplayTitle">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="#Title"/>
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ArticleDate">
<xsl:value-of select="#SongDate"/>
</xsl:variable>
<xsl:if test="string-length($SafeImageUrl) != 0">
<div class="event-contain">
<div class="event-img pull-right">
<div class="white-box">
<h1><xsl:value-of select="ddwrt:FormatDate(string($ArticleDate),1037, 'MMMM')"/></h1>
<h2><xsl:value-of select="ddwrt:FormatDate(string($ArticleDate),1037, 'd')"/></h2>
</div>
<img src="{$SafeImageUrl}" title="{#ImageUrlAltText}">
<xsl:if test="$ImageWidth != ''">
<xsl:attribute name="width">
<xsl:value-of select="$ImageWidth" />
</xsl:attribute>
</xsl:if>
<xsl:if test="$ImageHeight != ''">
<xsl:attribute name="height">
<xsl:value-of select="$ImageHeight" />
</xsl:attribute>
</xsl:if>
</img>
</div>
<div class="text-sec pull-right">
<header>
<hgroup>
<h3 class="ttl md fnt-bld blue"> <xsl:value-of select="$DisplayTitle"/> </h3>
<h4 class="ttl sm fnt-bld blue-mob"> <xsl:value-of select="#SongDescription"/> </h4>
</hgroup>
</header>
<div class="row dynamic-text-contain pull-right">
<p class="ttl xs">
<span class="display-on-mob"><xsl:value-of select="#NewsDescription" /></span>
</p>
</div>
<div class="row">
<div class="display-all read-more-eruim no-mob">
<div class="display-more open-link">
<a>
<i class="icon-icons2-50"></i>
<p>
<span class="btn-text">Show All</span>
<i class="icon-icons2-10 left-arrow" id="arrow"></i>
</p>
</a>
</div>
</div>
</div>
</div>
<div class="clearfix"></div>
<div class="line"></div>
</div>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Tnx For the Help Yotam.
I Have a Solution If any one ever has this problem:
the thing is that:
<xsl:value-of select="ddwrt:FormatDate(string($ArticleDate),1037, 'd')"/>
is the wrong syntax and should be :
<xsl:value-of select="ddwrt:FormatDateTime(string($ArticleDate),1037, 'd')"/>
Now, another thing is that it returns the value as: dd/mm/yyyy and I only wanted the day so I used a sub string function like so:
<xsl:value-of select="substring-before(ddwrt:FormatDateTime(string($ArticleDate),1037, 'd'),'/')"/>
This Solved me this issue :)

XSLT Parsing error when using Umbraco GetMedia

I am trying to retrieve the url to an image using the GetMedia mediapicker.
The code below works fine:
<xsl:for-each select="umbraco.library:GetXmlNodeById(1123)/* [#isDoc]">
<article>
<img width="1822" height="600">
<xsl:attribute name="src">
<xsl:value-of select="umbraco.library:GetMedia(1139, 0)/umbracoFile" />
</xsl:attribute>
</img>
<div class="contents">
<h1>
<xsl:value-of select="bannerHeading1"/>
</h1>
</div>
</article>
</xsl:for-each>
However, if I replace the key line with this:
<xsl:value-of select="umbraco.library:GetMedia(bannerImage, 0)/umbracoFile" />
I get a parsing error with the exception being an OverflowException (Value was either too large or too small for an Int32), which suggests that it's not the 1139 that is being passed in.
Is there a way I can pass in the property I want? The value of "bannerImage" is 1139 as I want it to be?
Thanks for any help.
Further: This is the XML structure being returned by GetXMLNodeById:
<?xml version="1.0" encoding="utf-8" ?>
<HomepageBanner id="1141" parentID="1123" level="3" writerID="0" creatorID="0" nodeType="1124" template="1125" sortOrder="0" createDate="2013-08-12T15:53:48" updateDate="2013-08-12T15:54:18" nodeName="Members" urlName="members" writerName="admin" creatorName="admin" path="-1,1065,1123,1141" isDoc="">
<bannerImage>1139</bannerImage>
<bannerHeading1>Members Area</bannerHeading1>
<bannerHeading2>..the place for all your needs</bannerHeading2>
</HomepageBanner>
For anyone else trying to get an image from an item in a content folder, this is how I got it to work:
<xsl:for-each select="umbraco.library:GetXmlNodeById(1123)/* [#isDoc]">
<article>
<!-- Store the ID -->
<xsl:variable name="mediaId" select="bannerImage" />
<!-- Check the ID is numeric -->
<xsl:if test="number($mediaId) > 0">
<xsl:variable name="mediaNode" select="umbraco.library:GetMedia($mediaId, false())" />
<xsl:if test="string-length($mediaNode/umbracoFile) > 0">
<img src="{$mediaNode/umbracoFile}" width="1822" height="600" />
<div class="contents">
<h1>
<xsl:value-of select="bannerHeading1"/>
</h1>
</div>
</xsl:if>
</xsl:if>
</article>
</xsl:for-each>
You first need to check that the value is numeric and then, the bit that was failing me, you need to add the "/umbracoFile" part to your media node variable.
Thanks to the contributors who helped me in the right direction.

xsl 1.0, count if more than 2 elements then change css class

The data comes from the server, usually two rows, but sometimes it's more. So I try to make the list dynamic change.
<xsl:template match="Event">
<ul class="lines">
<xsl:apply-templates select="Line"/>
</ul>
</xsl:template>
<xsl:template match="Line">
<li class="something">
<a href="">
<span class="result"><xsl:value-of select="#result"/></span>
<span class="odds"><xsl:value-of select="#odds"/></span>
</a>
</li>
</xsl:template>
I have to count the number of "li" and if it's more than 2, i have to change the class of "li"
Within the template matching Line you can access the total number of Line elements within this Event using the last() function (which returns the index number of the last node in the "current node list" determined by the select expression of the apply-templates that caused this template to fire, which in this case is the set of Line children of a particular Event).
<li>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="last() <= 2">something</xsl:when>
<xsl:otherwise>somethingElse</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
How about something like this:
<xsl:template match="Event">
<ul class="lines">
<xsl:apply-templates select="Line"/>
</ul>
</xsl:template>
<xsl:template match="Line" name="Line">
<xsl:param name="classVal" select="'something'" />
<li class="{$classVal}">
<a href="">
<span class="result">
<xsl:value-of select="#result"/>
</span>
<span class="odds">
<xsl:value-of select="#odds"/>
</span>
</a>
</li>
</xsl:template>
<xsl:template match="Line[count(../Line) > 1]">
<xsl:call-template name="Line">
<xsl:with-param name="classVal" select="'somethingElse'" />
</xsl:call-template>
</xsl:template>

content query webpart itemstyle wrapped by group style in XSLT

I have the following markup:
<ul id="slider">
<!-- slider item -->
<li>
...
</li>
<!-- end of slider item -->
</ul>
and I have defined the following itemStyle and GroupStyle xsl in header.xsl and itemStyle.xsl for displaying data from a SharePoint 2010 List:
<!-- in header.xsl -->
<xsl:template name="Slider" match="*[#GroupStyle='Slider']" mode="header">
<ul id="slider">
</ul>
</xsl:template>
<!-- in itemStyle.xsl -->
<xsl:template name="Slider" match="Row[#Style='Slider']" mode="itemstyle">
<xsl:variable name="SafeImageUrl">
<xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
<xsl:with-param name="UrlColumnName" select="#Picture"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="Title">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="#Title" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="Details">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="#Details" />
</xsl:call-template>
</xsl:variable>
<li>
<img src="{$SafeImageUrl}" alt="{$Title}" />
<section class="media-description">
<h2 class="slider-headline"><xsl:value-of disable-output-escaping="yes" select="$Title" /></h2>
<p><xsl:value-of disable-output-escaping="yes" select="$Details" /></p>
</section>
</li>
</xsl:template>
but the thing is, when applying the previous two templates, the <ul id="slider"></ul> appears isolated from all <li> items as below:
<ul id="slider"></ul>
<!-- a bunch of tables and td here.. -->
<ul style="width: 100%;" class="dfwp-column dfwp-list">
<li class="dfwp-item"></li>
<li>
<img alt="Must-see US exhibitions" src="">
<section class="media-description"><h2 class="slider-headline">Must-see US exhibitions</h2>
<p>(Blank)</p>
</section>
</li>
...
</ul>
all I want is to have <ul id="slider>" element to wrap those li's directly,
so how can i do that ?
Thanks
What's your input XML?
You'd do something like this:
<xsl:template name="Slider" match="*[#GroupStyle='Slider']" mode="header"><!-- Sure you want to match *? -->
<ul id="slider">
<!-- Match the input XML path to your rows from the context of the matched element above -->
<xsl:apply-templates select="Row[#Style='Slider']" mode="itemstyle" />
</ul>
</xsl:template>
<xsl:template name="Slider" match="Row[#Style='Slider']" mode="itemstyle">
<li>..</li>
</xsl:template>
Can't figure out why you are using "mode"s either.
problem solved, thanks to #James Love,
and here are the steps:
make a copy of ContentQueryMain.xsl, as the following article says
after having a copy of ContentQueryMain.xsl, edit it and look for OutTemplate.Body
then you can place your wrapper in the following variables (but they have to be escaped)
<xsl:template name="OuterTemplate.Body">
<xsl:variable name="BeginColumn1" select="string('<ul id="slider" class="dfwp-column dfwp-list" style="width:')" />
<!-- ^------------------^ -->
<xsl:variable name="BeginColumn2" select="string('%" >')" />
<xsl:variable name="BeginColumn" select="concat($BeginColumn1, $cbq_columnwidth, $BeginColumn2)" />
<xsl:variable name="EndColumn" select="string('</ul>')" />
<!-- ^---------^ -->
stupid workaround, but its working :S

XSLT: Flat XML to Nested HTML List

I'm new to XSLT. I know I need to use xsl:for-each-group, but I can't figure out anything other than a basic list. Would some sort of recursion work better? Any XSLT 1.0 or 2.0 solution would be fine.
Below is the example XML. Note the most important attribute for organizing data into a tree structure is #taxonomy. Other attributes #taxonomyName and #level are provided as optional helper attributes.
<?xml version="1.0" encoding="utf-8"?>
<documents>
<document level="0" title="Root document test" taxonomy="" taxonomyName="" />
<document level="1" title="Level one document test" taxonomy="\CategoryI" taxonomyName="CategoryI" />
<document level="1" title="Level one document test #2" taxonomy="\CategoryII" taxonomyName="CategoryII" />
<document level="2" title="Level two document test" taxonomy="\CategoryII\SubcategoryA" taxonomyName="SubcategoryA" />
<document level="2" title="Level two document test #2" taxonomy="\CategoryII\SubcategoryA" taxonomyName="SubcategoryA" />
<document level="3" title="Level three document test" taxonomy="\CategoryII\SubcategoryA\Microcategory1" taxonomyName="Microcategory1" />
<document level="2" title="Level two, no level one test" taxonomy="\CategoryIII\SubcategoryZ" taxonomyName="SubcategoryZ" />
</documents>
Here's the expected output. (Please note that indenting is not necessary in the output. I've done it here for readability.)
<ul>
<li>Root document test</li>
<li>CategoryI
<ul>
<li>Level one document test</li>
</ul>
</li>
<li>CategoryII
<ul>
<li>Level one document test #2</li>
<li>SubcategoryA
<ul>
<li>Level two document test</li>
<li>Level two document test #2</li>
<li>Microcategory1
<ul>
<li>Level three document test</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>CategoryIII
<ul>
<li>SubcategoryZ
<ul>
<li>Level two, no subcategory test</li>
</ul>
</li>
</ul>
</li>
</ul>
Here's the best I can do.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="contacts-by-taxonomy" match="document" use="#taxonomy" />
<xsl:template match="documents">
<ul>
<xsl:for-each-group select="document" group-by="#taxonomy">
<xsl:sort select="#taxonomy" />
<li>
<h3><xsl:value-of select="current-grouping-key()"/></h3>
<ul>
<xsl:for-each select="current-group()">
<li><xsl:value-of select="#title"/></li>
</xsl:for-each>
</ul>
</li>
</xsl:for-each-group>
</ul>
</xsl:template>
</xsl:stylesheet>
I'll keep chugging away at it, but would be eternally grateful if someone could throw me a life jacket. Thanks!
OK, here's my solution at last. :-) Basically it recurses through the tree, and at each level, it does a for-each-group group-by="the next level of #taxonomy".
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="documents">
<ul>
<xsl:call-template name="tree-depth-n">
<xsl:with-param name="population" select="document"/>
<xsl:with-param name="depth" select="0"/>
<xsl:with-param name="taxonomy-so-far" select="''"/>
</xsl:call-template>
</ul>
</xsl:template>
<!-- This template is called with a population that are all descendants
of the same ancestors up to level n. -->
<xsl:template name="tree-depth-n">
<xsl:param name="depth" required="yes"/>
<xsl:param name="population" required="yes"/>
<xsl:param name="taxonomy-so-far" required="yes"/>
<!-- output a <li> for each document that is a leaf at this level,
and a <li> for each sub-taxon of this level. -->
<xsl:for-each-group select="$population"
group-by="string(tokenize(#taxonomy, '\\')[$depth + 2])">
<xsl:sort select="#taxonomy" />
<xsl:choose>
<!-- process documents at this level. -->
<xsl:when test="current-grouping-key() = ''">
<xsl:for-each select="current-group()">
<li><xsl:value-of select="#title"/></li>
</xsl:for-each>
</xsl:when>
<!-- process subcategories -->
<xsl:otherwise>
<li>
<h3><xsl:value-of select="current-grouping-key()"/></h3>
<ul>
<!-- recurse -->
<xsl:call-template name="tree-depth-n">
<xsl:with-param name="population" select="current-group()"/>
<xsl:with-param name="depth" select="$depth + 1"/>
<xsl:with-param name="taxonomy-so-far"
select="concat($taxonomy-so-far, '\\', current-grouping-key())"/>
</xsl:call-template>
</ul>
</li>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
With the given input, the output is:
<ul>
<li>Root document test</li>
<li>
<h3>CategoryI</h3>
<ul>
<li>Level one document test</li>
</ul>
</li>
<li>
<h3>CategoryII</h3>
<ul>
<li>Level one document test #2</li>
<li>
<h3>SubcategoryA</h3>
<ul>
<li>Level two document test</li>
<li>Level two document test #2</li>
<li>
<h3>Microcategory1</h3>
<ul>
<li>Level three document test</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>
<h3>CategoryIII</h3>
<ul>
<li>
<h3>SubcategoryZ</h3>
<ul>
<li>Level two, no level one test</li>
</ul>
</li>
</ul>
</li>
</ul>
Which I believe is what you wanted. (I put <h3>s in there as you did in your XSL attempt, for the category names and not for the document titles.)