I'm using xslt 1.0 to transform a Sharepoint 2013 Data View.
The data source consists of 3 joined lists:
ISOList
Threat_Catalogue (has a lookup (multiple) to ISOList)
Risks (has a lookup (single) to Threat_Catalogue)
I'm looping thru items in ISOList, and want to show all Risks that are relevant for this specific item. The logic being: First find Threats related to the specific item in ISOList, and then find Risks related to one of those Threats.
So I'm calling a template with the item ID (ISORef) from ISOList as a parameter - like so:
<xsl:template name="Risks">
<xsl:param name="ISORef" />
<!-- add extra info to string, so it only selects relevant items -->
<xsl:variable name="ISORefString" select="concat('&ID=',$ISORef,'&RootFolder')"/>
<!-- get relevant threats -->
<xsl:variable name="SelectedThreats" select="/dsQueryResponse/Threat_Catalogue/Rows/Row[contains(#ISO_x0020_Reference, $ISORefString)]"/>
<!-- Create variable, that contains all ID's in Threat_Catalogue that are relevant -->
<xsl:variable name="ListThreats"><xsl:for-each select="$SelectedThreats">=<xsl:value-of select="#ID"/>&</xsl:for-each></xsl:variable>
<!-- Variable - just to illustrate problem -->
<xsl:variable name="ListThreatsAlternative" select="$ListThreats" />
<!-- this is where I'm trying to use variable to get relevant risks, but fails -->
<!-- Old: Didn't Work, has been replaced -->
<!--
<xsl:variable name="SelectedRisks" select="/dsQueryResponse/Risks/Rows/Row[contains($ListThreats, substring-before(substring-after(#Threat, 'ID'), 'RootFolder'))]"/>
-->
<!-- New: Works as intended -->
<xsl:variable name="SelectedRisks" select="/dsQueryResponse/Risks/Rows/Row[contains($ListThreats, substring-before(substring-after(#Threat, 'ID'), 'RootFolder')) and $ListThreats != '' and #Threat != '']"/>
<!-- Example output: -->
Original: <xsl:value-of select="$ListThreats" /> <!-- contains: '=118&' --> <br/>
Alternative: <xsl:value-of select="$ListThreatsAlternative" /> <!-- empty -->
</xsl:template>
The problem is that variable ListThreats appears as an empty string when used as a criteria in the select for either SelectedRisks or ListThreatsAlternative.
*Edited:
The source XML is generated by Sharepoint. I don't know how to get the XML of the specific data source, but below I've added the XML from the Risks list. I've shortened down the XML source substantially, but hopefully it is sufficient.
Also - there are some significant differences in source XML and what Sharepoint presents in a list. Especially when it comes to the lookup-fields.
Example:
When Sharepoint presents the content:
https://xxx.xxx.xxx.com/sites/xxx_xxx/ISMS/_layouts/15/listform.aspx?PageType=4&ListId={4ab4bbff-588b-4c06-9685-c814cdefd59f}&ID=56&RootFolder=*', RefreshPage); return false;" href="https://xxx.xxx.xxx.com/sites/xxx_xxx/ISMS/_layouts/15/listform.aspx?PageType=4&ListId={4ab4bbff-588b-4c06-9685-c814cdefd59f}&ID=56&RootFolder=*">Hackers: 14. Password Cracking
When similar content is shown in source XML:
55;#Hackers: 13. Malware
This is the XML source (shortened):
<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">
<s:Schema id="RowsetSchema">
<s:ElementType name="row" content="eltOnly" rs:CommandTimeout="30">
<s:AttributeType name="ows_ID" rs:name="ID" rs:number="1">
<s:datatype dt:type="i4" dt:maxLength="4"/>
</s:AttributeType>
<s:AttributeType name="ows_LinkTitle" rs:name="Risk Title" rs:number="2">
<s:datatype dt:type="string" dt:maxLength="512"/>
</s:AttributeType>
<s:AttributeType name="ows_Threat" rs:name="Threat" rs:number="15">
<s:datatype dt:type="variant" dt:lookup="true" dt:maxLength="8009"/>
</s:AttributeType>
</s:ElementType>
</s:Schema>
<rs:data>
<z:row ows_ID="72" ows_LinkTitle="Test risk" ows_Threat="55;#Hackers: 13. Malware" />
</rs:data>
</xml>
Found the answer, stupid mistake in the select. Edited in OP.
Related
I'm having some troubles with XSLHelper.fld method. I have a very simple XSLT file that I am trying to access sub-values that are inside my Sitecore image item.
Code Sample:
<?xml version="1.0" encoding="UTF-8"?>
<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">
TEST BACKGROUND IMAGE
<br/>
ALT: <br/>
<xsl:value-of select="sc:fld('background',.,'alt')"/>
<br/>
SRC: <br/>
<xsl:value-of select="sc:fld('background',.,'src')"/>
<br/>
Field SRC: <br/>
<xsl:value-of select="sc:field('background',.,'src')"/>
</xsl:template>
</xsl:stylesheet>
When I test the following code, the results are strange as can be seen from the following screenshot:
You can clearly see that the alt field has a value and the src field is empty. So I definitely have access to my Sitecore image item but cannot get access to the src field.....
Can anyone shed some light on why the src field is empty?
Regards,
Comic Coder
As I stated on your other question, there is no longer a src attribute in the raw value of image fields. The alt attribute exists if you modify the Alternate Text in the properties of the image field or it will fall back to the alt text on the image item in the media library.
The sc:field method uses a field renderer to output a full <img> tag. The third parameter to that method is supposed to be for additional parameters like width and height or additional attributes. It should be formatted as a query string (e.g. width=150&height=100). It is probably just ignoring the src value that you are passing because it isn't valid.
Here's a generic XSLT 1.0 question which I need to know to write an XSLT statement for processing docbook xml files. In my docbook XML, I'm trying to write a compound xpath statement in XSLT 1.0 that says, hardcode a new attribute "class = "play" for p tags in html output.
I want this action to be done for every <para> tag which does NOT have these attributes
role="normal-play-paragraph" AND
role ="no-indent" AND
"role="line-verse"
Here is my XML source:
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="5.0" xml:id="play">
<title> Hamlet </title>
<para role="no-indent"> SPHINX. Do you think about it very much?</para>
<para role="normal-play-para"> INTERVIEWER. I do so say. </para>
<para>SPHINX. Hello </para>
<para> INTERVIEWER. dddddWhy I do so say. </para>
<para> SPHINX. Yes. </para>
<para role="line-verse"> Cosmologists have theorized or guessed</para>
</chapter>
I want the HTML output to look like this after Docbook XSLT processes it:
<html>
<body>
<p class="no-indent">SPHINX. Do you think about it very much? much. </p>
<p class="normal-play-para"> INTERVIEWER. I do so say. </p>
<p class="play">SPHINX. Hello </p>
<p class="play">INTERVIEWER. dddddWhy I do so say. </p>
<p class="play">SPHINX. Yes. </p>
<p class="line-verse"> Cosmologists have theorized or guessed</p>
</body>
<html>
The docbook xslt has 2 mechanisms at work which you don't really need to know about.
First, in <para role=""> elements, the value of role is changed into class of p. This is the default behavior.
Second, I'm using a special mode to hardcode a "class='play'" into p tags.
<xsl:template match="d:chapter[#xml:id = 'play']/d:para" mode="class.attribute" >
<xsl:param name="class" select="local-name(.)"/>
<xsl:attribute name="class">play</xsl:attribute>
</xsl:template>
However, I want class="play" to be hardcoded only when there are other attributes & values NOT present. I can modify the above statement to exclude all para tags with the attribute role="line-verse" :
<xsl:template match="d:chapter[#xml:id = 'play']/d:para[#role != 'line-verse']" mode="class.attribute" >
<xsl:param name="class" select="local-name(.)"/>
<xsl:attribute name="class">play</xsl:attribute>
</xsl:template>
But I need more than that. I want to exclude not only role= "line-verse," but also role="no-indent" and role="normal-play-para".
So I have to change the value of the xpath statement in the match attribute so that it excludes three attribute values. I haven't the foggiest idea how to do that. Does anybody know? Thanks.
Update about Answer:
First, I want to thank all of you for taking the time to understand my question and formulate an answer. I should mention that I am still a novice on this stuff, and also, my question was a little unfair because I am using some sophisticated/complicated Docbook XSL. Therefore I need an answer that doesn't cause collisions with the Docbook XSL stylesheets. Also, I realize that you wrote transformations that may be perfectly valid answers in generating html output if I were not also importing the docbook xsl.
The answer which I chose as "best" here may not be the most elegant, but simply the one that worked for me in the case when I am importing the epub3 docbook-ns stylesheets. So Mr. Rishe's one line answer actually does exactly I need it to do even if it isn't as elegant.
I really don't know what's going on in this customization which I started out with:
<xsl:template match="d:chapter[#xml:id = 'play']/d:para" mode="class.attribute" >
<xsl:param name="class" select="local-name(.)"/>
<xsl:attribute name="class">play</xsl:attribute>
</xsl:template>
What I do know is that it's invoking a <xsl:template name="generate.class.attribute"> which is found here. http://50.56.245.89/xsl-ns/xhtml-1_1/html.xsl
Another thing. Dimitre Novatchev's 2 answers looks as though they would work. By the way, you forgot to include the <xsl:param name="class" select="local-name(.)"/> statement -- which is easily fixed -- and that solution works.
However, Dimitre, I have another question. The second answer you gave used variables, which looks simple and functional. If I try it, my Saxon 6.5 parser gives a validation error. (E [Saxon6.5.5] The match pattern in xsl:template may not contain references to variables). Maybe it's something simple like a typo. But is it possible that variables are not allowed in XSLT 1.0 template matches?
Could you give this a try:
<!-- Special handling for paras with one of the three roles -->
<xsl:template
match="d:chapter[#xml:id = 'play']/d:para[#role = 'line-verse' or #role = 'normal-play-para' or #role - 'line-indent']"
mode="class.attribute" >
<xsl:attribute name="class">
<xsl:value-of select="#role" />
</xsl:attribute>
</xsl:template>
<!-- Other paras get the default class "play" -->
<xsl:template match="d:chapter[#xml:id = 'play']/d:para" mode="class.attribute">
<xsl:attribute name="class">play</xsl:attribute>
</xsl:template>
One step further would be to have the <xsl:attribute> in the template that's calling these templates, and just have the needed value in the class.attribute templates themselves. Something like this:
<xsl:template match="d:chapter[#xml:id = 'play']/d:para">
<p>
<xsl:attribute name="class">
<xsl:apply-templates select="." mode="class.attribute" />
</xsl:attribute>
...
</p>
</xsl:template>
<!-- Special handling for paras with one of the three roles -->
<xsl:template
match="d:chapter[#xml:id = 'play']/d:para[#role = 'line-verse' or #role = 'normal-play-para' or #role - 'line-indent']"
mode="class.attribute" >
<xsl:value-of select="#role" />
</xsl:template>
<!-- Other paras get the default class "play" -->
<xsl:template match="d:chapter[#xml:id = 'play']/d:para" mode="class.attribute">
<xsl:text>play</xsl:text>
</xsl:template>
To specifically answer your original question, if you really needed a template that specifically matches paras that don't have one of those #role values, you could match on this XPath:
d:chapter[#xml:id = 'play']/d:para[not(#role = 'line-verse' or #role = 'normal-play-para' or #role - 'line-indent')]
But I think the approach I've presented above (treat paras those roles as the special case, and treat everything else as the default) is the better way to go.
One possible solution is:
<xsl:template mode="class.attribute" match=
"d:chapter[#xml:id = 'play']
/d:para[not(#role = 'line-verse'
or #role = 'no-indent'
or #role = 'normal-play-para'
)]" >
<xsl:attribute name="class">play</xsl:attribute>
</xsl:template>
However, I would use a more flexible and extensible solution, that allows easy modification of the "non-play" values:
<xsl:param name="pNonPlayVals">
<val>line-verse</val>
<val>no-indent</val>
<val>normal-play-para</val>
</xsl:param>
<xsl:template mode="class.attribute" match=
"d:chapter[#xml:id = 'play']/d:para
[not(#role = document('')/*/xsl:param[#name='pNonPlayVals']/val)]" >
<xsl:attribute name="class">play</xsl:attribute>
</xsl:template>
I have two functions:
GetHomeXml() -> returns a collection of pages with a custom meta type
SiteMapXml() -> standard sitemap xml
SiteMapXml() has a property (isCurrent) that gets returned if the page is the current page. I need to determine if the name of the page appears in GetHomeXml(), and whether the node in GetHomeXml() has an Image.FileName property assigned to it.
I can get both returned, but I cannot figure out the XSL to accomplish this. Following is what I have so far:
<xsl:param name="currentPage" select="/in:inputs/in:result[#name='SitemapXml']/Page/#iscurrent" />
<xsl:for-each select="/in:inputs/in:result[#name='GetHomeXml']/Image">
<xsl:if test="#Image.PageTitle = $currentPage.Title">
<img src="#Image.FileName" />
</xsl:if>
</xsl:for-each>
I created a custom meta data entry, and added 3 fields to it {IsTab, Image, Color}. I want to determine that if the current page contains an Image field, and the Page.Title assigned to it, is the same as the current page, to render the designated image (it's a data reference field, with an image lookup property to the images directory) into the tag.
But this code does not seem to return anything?
Update:
The XML that is returned for GetHomeXml() is as follows:
<!-- Function Call Result (0 ms), XPath /in:inputs/in:result[#name='GetHomeXml']/IsTab -->
<in:result name="GetHomeXml">
<IsTab Id="9eba448e-9a30-478e-81b2-530bc7da2545" IsTab="true" BackgroundImg="MediaArchive:34f9be39-8273-4960-8cc6-e6b76f34e6ac" BackgroundImg.FileName="home-group-2.png" PageId.Title="Contact Us" xmlns=""/>
<IsTab Id="a46e2e98-ffcd-4675-8840-389d1a7f46ca" IsTab="true" PageId.Title="Welcome" xmlns=""/>
<IsTab Id="c76fa101-8c63-46e2-9431-e18ce875866d" IsTab="false" PageId.Title="What we do" xmlns=""/>
I want to get the name of the image, based on the current page (PageId.Title) I need to match back to the returned value associated with the current page in SiteMapXml:
<!-- Function Call Result (0 ms), XPath /in:inputs/in:result[#name='SitemapXml']/Page -->
<in:result name="SitemapXml">
<Page Id="62c66cb6-b2ec-4469-b0d7-54bc61b22c20" Title="Home" MenuTitle="Home" UrlTitle="Home" Description="Default web site for... Please do not change any of the settings for this site. Add, modify and delete pages underneath this web site, but do no touch this site." ChangedDate="2011-12-04T22:36:33.2651194+02:00" ChangedBy="admin" URL="/Home/c1mode(unpublished)" Depth="1" xmlns="">
<Page Id="041c7d66-60cd-4098-ac98-728c0db111a1" Title="Welcome" MenuTitle="Welcome" UrlTitle="Welcome" Description="" ChangedDate="2011-12-04T22:38:06.2815949+02:00" ChangedBy="admin" URL="/Home/Welcome/c1mode(unpublished)" Depth="2"/>
<Page Id="8ae4d8a5-f4d9-43ed-85de-90b6d3a6f0b8" Title="Contact Us" MenuTitle="Contact Us" UrlTitle="Contact-Us" Description="" ChangedDate="2011-12-04T22:54:10.1503871+02:00" ChangedBy="admin" URL="/Home/Contact-Us/c1mode(unpublished)" isopen="true" iscurrent="true" Depth="2"/>
<Page Id="ed5560a4-140b-4851-ac19-5ddc6c66a770" Title="What we do" MenuTitle="What we do" UrlTitle="What-we-do" Description="" ChangedDate="2011-11-28T07:23:25.8851421+02:00" ChangedBy="admin" URL="/Home/What-we-do/c1mode(unpublished)" Depth="2"/>
</Page>
</in:result>
</in:result>
Instead of looping through the resultset, use a xpath predicate to match the page by title. For example, the URL of the welcome page would be given by:
$pages[#Page.Title='Welcome']/#URL
Here is a complete XSLT which shows the URLs for the HomeXML pages:
<xsl:param name="homeXml" select="/in:inputs/in:result[#name='GetHomeXml']/IsTab" />
<xsl:param name="sitemap" select="/in:inputs/in:result[#name='SitemapXml']/Page" />
<xsl:template match="/">
<html>
<head>
<!-- markup placed here will be shown in the head section of the rendered page -->
</head>
<body>
<xsl:for-each select="$homeXml">
<xsl:variable name="pageTitle" select="#PageId.Title" />
<xsl:variable name="sitemapPage" select="$sitemap//Page[#Title=$pageTitle]/#URL" />
<xsl:value-of select="$sitemapPage" />
</xsl:for-each>
</body>
</html>
</xsl:template>
Corresponding XsltCake (like JsFiddle, but for Xslt)
I'm struggling to create a list to be displayed on the condition of the parent node ('folder') with attribute 'folded' set to either 'yes' or 'no'.
The result shall display the first two levels of the list only and not the third level as below.
1st. Level: display
2nd. Level: display
3rd. Level: NO display
The idea is to check the 'folder'-attribute <folder folded="yes"> with <xsl:if test="not(parent::yes)">.
That should qualify for the 3rd. Level to NOT being displayed, but somehow it doesn't do what I want it to do.
I probably use the wrong construct and/or syntax.
Assistance is highly appreciated, thanks.
The XML structure with some content:
<xbel>
<folder folded="yes">
<level>1</level>
<title>bookmarks</title>
<desc>my bookmarks</desc>
<folder folded="no">
<level>2</level>
<title>Android</title>
<desc>my Android</desc>
<bookmark href="http://www.phonesreview.co.uk/">
<title>HTC Sync 3.0.5422 Update: Aria, Desire, Hero, Legend</title>
<desc>The new HTC Sync 3.0.5422 update will be most welcome...</desc>
</bookmark>
<folder folded="no">
<level>3</level>
<title>Apps</title>
<desc>Android Apps</desc>
<bookmark href="http://www.androidzoom.com/">
<title>Android Communication Apps</title>
<desc>Download Communication Apps for Android.</desc>
</bookmark>
<bookmark href="http://www.htc.com/">
<title>HTC - Android</title>
<desc>Apps for HTC-Android.</desc>
</bookmark>
</folder>
</folder>
</folder>
</xbel>
The XSLT:
<!--creates a nested list of elements named 'folder'-->
<xsl:template match="folder" mode="linklist">
<li>
<xsl:if test="folder/level = 2">
Level:<xsl:value-of select="level"/> /
Title:<xsl:value-of select="title"/> /
Desc:<xsl:value-of select="desc"/>
<ul>
<xsl:apply-templates mode="linklist" />
</ul>
</xsl:if>
</li>
</xsl:template>
<xsl:template match="bookmark" mode="linklist">
<li> <!-- this bookmark is just another item in the list of bookmarks -->
<!-- the title -->
<a rel="nofollow" href="{#href}"><xsl:value-of select="title"/></a>
<!-- the description -->
<xsl:if test="desc">
<span class="bookmarkDesc">
<xsl:value-of select="desc"/>
</span>
</xsl:if>
</li>
</xsl:template>
The Stylesheet HTML
<body>
<ul>
<xsl:apply-templates mode="linklist" />
</ul>
</body>
The generated output (levels:1-3)
Level:1 / Title:bookmarks / Desc:my bookmarks
Level:2 / Title:Android / Desc:my Android
HTC Sync 3.0.5422 Update: Aria, Desire, Hero, Legend ...
Level:3 / Title:Apps / Desc:Android Apps
Android Communication AppsDownload Communication Apps for Android.
HTC - AndroidApps for HTC-Android.
The anticipated output: (levels: 1-2)
Level:1 / Title:bookmarks / Desc:my bookmarks
Level:2 / Title:Android / Desc:my Android
HTC Sync 3.0.5422 Update: Aria, Desire, Hero, Legend ...
I tried this template, but that outputs the last two nodes, I need the two first nodes.
<xsl:template match="folder[parent::folder/#folded = 'yes']" mode="linklist">
The simplest possible change you could make to prevent processing of unfolded folder elements is to add an empty template that swallows them (i.e. produces no output):
<xsl:template match="folder[#folded='no']" mode="linklist"/>
All folder elements not having a folded attribute equal to no will be processed using your existing template; those that do will be captured by this new one.
If instead you want to process each folder element having either its own folded attribute equal to yes or that of its parent (as in the updated XML example), then use the following template:
<xsl:template match="folder[#folded='yes' or ../#folded='yes']" mode="linklist">
<!-- body elided -->
</xsl:template>
You'll probably also want to include an empty template for hiding all other folder elements:
<xsl:template match="folder" mode="linklist" />
I have the following XSLT Section Menu Code which working correctly to retreive items. But I'd like to replaced the items query by items name how ?
<!--<xsl:if test="contains($EnabledTemplates, concat('!', #template, '!')) ">-->
<xsl:for-each select="$root/item[contains($EnabledTemplates, concat('!', #template, '!'))]">
<xsl:sort select="#sortorder" data-type="number" />
<xsl:variable name="IsHaveChild" select="boolean(./item)" />
<xsl:variable name="IsSelected" select="boolean(./descendant-or-self::item[#id=$sc_currentitem/#id])" />
<xsl:variable name="IsShow" select="boolean(sc:fld($IsHideFieldName,.)!=1)" />
<xsl:variable name="IsCurrent" select="boolean(#id=$sc_currentitem/#id)" />
<sc:sec />
Do you want to filter items by name?
Try something like this:
[contains(#name, 'some text')]
For your querying you can choose between XPath and Sitecore query. The latter is a Sitecore implementation of XPath which is more readable and allows you to use the item names instead of XPath syntax.
Sitecore query however cannot be put directly in a select attribute in your XSL elements, it only works in the Sitecore XSL helper functions like sc:item (please also not the . in the sc:item function to specify the context item in which the query needs to be applied, . stands for current item).
<!-- using XPath -->
<xsl:value-of select="/item[#key='sitecore']/item[#key='content']" />
<!-- using Sitecore query -->
<xsl:value-of select="sc:item('/sitecore/content', .)" />