content query webpart itemstyle wrapped by group style in XSLT - 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

Related

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>

Transforming adjacent links to a list using XLST

I'm looking for some help with XSLT Tranforms.
I'm currently transforming links that match the format:
<link type="button" url="/page.html" text="Do something" />
By using the transform:
<xsl:template match="link">
<a target="_blank" href="{#url}" title="{#text}">
<xsl:if test="#type='button'">
<xsl:attribute name="class">btn</xsl:attribute>
</xsl:if>
<xsl:value-of select="#text" />
</a>
</xsl:template>
Which gives me the output:
<a class="btn" title="Do Something" href="/page.html" target="_blank">Do Something</a>
But now I'm looking to be able to detect when multiple links with the type "button" are grouped together like this:
<link type="button" url="/page.html" text="Do something" />
<link type="button" url="/page.html" text="Do something else" />
And output like so:
<ul class="btns">
<li>Do something</li>
<li>Do something else</li>
</ul>
Can anyone assist on this?
Thanks,
C.
The logic needs to go in the template for the parent of the link elements. Assuming you are using XSLT 2.0 it will be something like this:
<xsl:template match="parent">
<xsl:for-each-group select="*" group-adjacent="node-name()">
<xsl:choose>
<xsl:when test="self::link">
<ul>
<xsl:apply-templates select="current-group()"/>
</ul>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>

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.)

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

<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