XSLT nested conditionals - how to do this - xslt

I'm quite new to xslt
I have the following xslt snippet (pseudo'ed for clarity):
<xsl:for-each select="fields/field">
<xsl:if test="field=someThing">
<div>
<!--some content output-->
<!--here by doing-->
<!--some complex stuff-->
</div>
</xsl:if>
</xsl:for-each>
But based on value of field from loop, I optionally want to do some other stuff before the closing div.
So (armed with shiny new xsl book) I thought I could do something like this:
<xsl:for-each select="fields/field">
<xsl:if test="field=someThing">
<div>
<!--some content output-->
<!--here by doing-->
<!--some complex stuff-->
<xsl:choose>
<xsl:when test="field=someThingSpecific">
<div>
<!--process some additional-->
<!--stuff here-->
</div>
<!--then close div-->
</div>
</xsl:when>
<xsl:otherwise>
<!--nothing to do here-->
<!--just close the div-->
</div>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:for-each>
But it blows up.
Apparently because the closing div has moved into the conditional xsl:choose block
So 2 questions really:
why doesnt it work as is?
how can I achieve my objective?

It's blowing up because your XSLT isn't well-formed. When you open the first div outside of the xsl:choose, you have to close it outside the xsl:choose.
Also, if you don't need to do anything in your xsl:otherwise, just do another xsl:if:
<xsl:for-each select="fields/field">
<xsl:if test="field=someThing">
<div>
<!--some content output-->
<!--here by doing-->
<!--some complex stuff-->
<xsl:if test="field=someThingSpecific">
<div>
<!--process some additional-->
<!--stuff here-->
</div>
</xsl:if>
<!--then close div-->
</div>
</xsl:if>
</xsl:for-each>

Related

XSL stylesheet: creating Hyperlink based of query item_id

I am new to coding. Started XSL coding from past 1 month.
I want to creat a hyperlink according to the item_id.
But my concat is not working as desired.
My requirement is that i have to get create hyperlinks based on the variable item_id
For example:
https://xyz.com/webpr/webpr.php?objtype=frames&g_startlink=maintain&g_startdata=194970&g_userid=msbzzh&g_session_id=6017650`
https://xyz.com/webpr/webpr.php?objtype=frames&g_startlink=maintain&g_startdata=194971&g_userid=msbzzh&g_session_id=6017650
where the variable item_id comes inbetween the link. (194970, 194971 and so on)
So here is my code:
<xsl:when test ="$propName ='item_id'">
<td>
<xsl:variable name="itemId" select="$occRef/#*[local-name()=$propName]" />
<xsl:value-of select="$itemId" />
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
</td>
</xsl:when>
and i also tried like this.. But both of them didn't work out.
<xsl:value-of select="$itemId" />
UPDATED: You forgot to escape ampersands and indeed the variable was used incorrectly. See below the correct syntax.
<xsl:when test="$propName='item_id'">
<td>
<xsl:variable name="itemId" select="$occRef/#*[local-name()=$propName]"/>
<a href="{concat('https://xyz.com/webpr/webpr.php?objtype=frames&g_startlink=maintain&g_startdata=', $itemId, '&g_userid=msbzzh&g_session_id=6017650')}" target="_blank">
<xsl:value-of select="$itemId"/>
</a>
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
</td>
</xsl:when>

different cases differentiate and link them

I've the below XML.
<orderedlist type="manual">
<item num="1."><para>identify the issues in dispute;</para></item>
<item num="1.1"><para>explore and generate options;</para></item>
<item num="1.1.1"><para>communicate with one another; and/or</para></item>
<item num="(i)"><para>copy of the cancellation of employment pass; and</para></item>
<orderedlist>
here i want to translate all the . to - in item-num and surround them with a different tag, where there is some number or period after . previously I've received a solution and I've used not(ends-with(./#num,'.')). That worked fine till some point, recently I've came across a situation(4th item num in the above XML) where in the item num has values like (i), i, a, a), and these also satisfy the condition and are getting the new tag. below is my XSL template.
<xsl:template name="orderedlist" match="orderedlist">
<ol class="eng-orderedlist orderedlist">
<xsl:apply-templates/>
</ol>
</xsl:template>
<xsl:template match="item">
<xsl:call-template name="paran"></xsl:call-template>
</xsl:template>
<xsl:template name="paran">
<xsl:apply-templates select="./para/node()[1][self::page]" mode="first"/>
<li class="item">
<div class="para">
<xsl:choose>
<xsl:when test="not(ends-with(./#num,'.'))">
<a name="{translate(./#num,'.','-')}"/>
<span class="phrase">
<xsl:value-of select="./#num"></xsl:value-of>
</span>
</xsl:when>
<xsl:when test="./#num">
<span class="item-num">
<xsl:value-of select="./#num"></xsl:value-of>
</span>
</xsl:when>
</xsl:choose>
<xsl:apply-templates/>
</div>
</li>
</xsl:template>
<xsl:template match="page[not(preceding-sibling::node()[not(self::text()) or normalize-space()])]"/>
here i don't want phrase around other numbers than i format X.X X.X.X.
you can find a demo of this issue here.
please let me know how can i fix it.
Thanks
Perhaps you need to add a check on whether the num attribute contains a full-stop in the first place
<xsl:when test="contains(#num, '.') and not(ends-with(./#num,'.'))">
i.e. #num contains a full-stop, but not one at the end.
I'd suggest you do it this way:
<xsl:template match="item">
<!-- do one thing here -->
</xsl:template>
<xsl:template match="item [not(ends-with(#num, '.'))] [not (translate(#num, '.123456789', ''))]">
<!-- do another thing here -->
</xsl:template>
--
Doing the predicate by regex as suggested by Ian Roberts would probably be more elegant.

Search for a IF-ANY solution that works with <XSL:for-each>

I am searching for a solution for the following problem:
Currently i have this code:
<div class="box">
<div class="header">
<h2>Item</h2>
</div>
</div>
<xsl:for-each select="umbraco.library:GetXmlNodeById($source)/* [name() = $documentTypeAlias and string(umbracoNaviHide) != '0']">
<xsl:if test="string(experuserid)=$currentPage/experuserid">
<xsl:value-of select="#nodeName"/>
<xsl:value-of select="subtitel"/>
<a href="{umbraco.library:NiceUrl(#id)}" class="readmore">
Lees meer
</a>
</div>
</xsl:if>
</xsl:for-each>
This code produces a result for-each time my source(experuserid) equals my currentpage/experuserid.
The problem is when the test comes back negative and there are no experuserid's that match with the currentpage. I want to make it so that when this occures, my layout won't also show. Thus i need something like this:
<xsl:if-any test="string(experuserid)=$currentPage/experuserid">
<div class="box">
<div class="header">
<h2>Item</h2>
</div>
</div>
<xsl:for-each select="umbraco.library:GetXmlNodeById($source)/* [name() = $documentTypeAlias and string(umbracoNaviHide) != '0']">
<xsl:if test="string(experuserid)=$currentPage/experuserid">
<xsl:value-of select="#nodeName"/>
<xsl:value-of select="subtitel"/>
<a href="{umbraco.library:NiceUrl(#id)}" class="readmore">
Lees meer
</a>
</div>
</xsl:if>
</xsl:for-each>
<xsl:if-any>
Does this kind of solution exist? p.s. i am an XSLT-Rookie. I read something about using an if test with a * like so:
<xsl:if test="*string(experuserid)=$currentPage/experuserid">
But this does not work, any help?
you probably want to count the number of matches and only do it if there are some ... something like this ...
<xsl:if test="count( child::Page[experuserid = string(experuserid)] ) > 0">
<div ... blah ... />
</xsl:if>
not entirely sure what the "string(experuserid)" is doing (apart from obviously converting a textual number contained in the child node called experuserid of the current node into string, which it is already), you present it as working in your example so I'm re-using it ... but I have no context to determine it's validity.
I would do the filtering up front rather than using an <xsl:if> within the for-each, e.g.
<xsl:variable name="interestingNodes"
select="umbraco.library:GetXmlNodeById($source)/*
[name() = $documentTypeAlias and string(umbracoNaviHide) != '0']
[experuserid=$currentPage/experuserid]"/>
This will store in the variable only the nodes that you are actually interested in processing (i.e. those with the right experuserid), which may of course be none of them. Now you can simply say
<xsl:if test="$interestingNodes"><!-- true if $interestingNodes is non-empty -->
<div class="box">
...
</div>
<xsl:for-each select="$interestingNodes">
<xsl:value-of select="#nodeName"/>
...
</xsl:for-each>
</xsl:if>
P.S. note that the string() in your original test of string(experuserid)=$currentPage/experuserid is probably redundant. If the node has exactly one experuserid child element then it makes no difference to the behaviour. If the node has more than one experuserid then using string() will mean you check only the first experuserid child in document order, whereas without the string() it'll be true if any of them match.
Just use:
<xsl:for-each select=
"umbraco.library:GetXmlNodeById($source)/*
[name() = $documentTypeAlias and string(umbracoNaviHide) != '0']
[string(experuserid)=$currentPage/experuserid]
">
<!-- Your other code here -->
</xsl:for-each>

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>

Strip HTML-like characters (not markup) from XML with XSLT?

Is there a way to strip all HTML from XML with XSLT,
like PHP strip_tags() ?
I want to keep the text, but remove all formatting, etc.
This will be taking place before truncating.
Hey Kurt, <br> I'd like to suggest that we start inventorying a system feature list at a <br> high-level. From there, we would define the requirements of the features. <br> Next, design to the requirements, develop, test, deploy..wash, rinse, and <br> repeat.. <br> I.E. <br> Event Calendar <br> - Requirement No. 1 <br> - Requirement No. 2
I found this solution on various places in the internet using Google:
Some examples of my research:
here, here, here
<!-- Calling the template that removes tag -->
<xsl:call-template name="remove-html">
<xsl:with-param name="text" select="{HtmlBody}"/>
</xsl:call-template>
<!-- This will remove the tag -->
<xsl:template name="remove-html">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '<')">
<xsl:value-of select="substring-before($text, '<')"/>
<xsl:call-template name="remove-html">
<xsl:with-param name="text" select="substring-after($text, '>')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>