Using Multiple Nested Attributes in XSLT - xslt

What is the best way to construct nested attributes in XSL?
My issue is that onmouseover is an attribute and the src of img is an attribute. The current error given by the builder is:
An item of type 'Element' cannot be constructed within a node of type 'Attribute'.
I used to have an issue of multiple attributes which would have been my preferred route but throws an error:
Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added.
I have since attempted the following as a workaround but with no luck
<xsl:template name="Item3">
<xsl:param name="ItemID" />
<xsl:variable name="IMGSRC">
<xsl:choose>
<xsl:when test="$ItemID = 'ST-18/NM/NM/36'">
<xsl:value-of select="concat('imagesCategories/','ST-18-NM-NM-36','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/NM/NM/48'">
<xsl:value-of select="concat('imagesCategories/','ST-18-NM-NM-48','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/NM/NM/72'">
<xsl:value-of select="concat('imagesCategories/','ST-18-NM-NM-72','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/12'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-12','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/24'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-24','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/36'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-36','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/48'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-48','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/60'">
<xsl:value-of select="concat('imagesCategories/','ST18-SMAM-SMAM-60','.jpg')"/>
</xsl:when>
<xsl:when test="$ItemID = 'ST-18/SMAM/SMAM/72'">
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('imagesCategories/',$ItemID,'.jpg')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="Items/Item[#ItemID=$ItemID]">
<xsl:attribute name="onmouseover">
<xsl:text>ddrivetip('</xsl:text>
<img src="{$IMGSRC}"/>
<br />
<b>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#ItemID" />
</b>
<br />
<b>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#ItemDescription" />
</b>
<br />
<br />
<xsl:text>Price (01-09): </xsl:text>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#PriceLevel1" />
<br/>
<xsl:text>Price (10-24): </xsl:text>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#PriceLevel2" />
<br/>
<xsl:text>Price (25-49): </xsl:text>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#PriceLevel3" />
<br/>
<xsl:text>Qty In Stock: </xsl:text>
<xsl:value-of select="Items/Item[#ItemID=$ItemID]/#QtyOnHand" />
<br />
<br />
<xsl:text>Click </xsl:text>
<b>
<xsl:text>"BUY!"</xsl:text>
</b>
<xsl:text> to add this item to your shopping cart</xsl:text>
<xsl:text>', '', '300')</xsl:text>
</xsl:attribute>
There is some additional code and then the proper closing tags.
Thanks everyone!

It looks like you are trying to pass html as a string to your ddrivetip function. However, you are adding these as nodes instead of text, and nodes cannot be added added to attributes, so one solution is to make the nodes text (You'll have to escape the brackets and double quotes too).
However, you are putting a lot of information into the onmouseover event, which is not recommended. Instead of what you are currently doing, I would make a hidden element with an id that incorporates your itemId with the contents of your html and then show that as needed in your onmouseover event.

Use CDATA sections so that the XSLT Processor interpret your img tags as a part of text nodes and not as an attempt to insert element nodes into an attribute (it is forbidden by the XML specification)
<xsl:attribute name="onmouseover">
<xsl:text><![CDATA[ddrivetip('<img src="]]></xsl:text>
<xsl:value-of select="$IMGSRC" />
<xsl:text><![CDATA["/>
<br />
...

Related

confusing error thown while trying to match templates

I've the below XML line of code.
<entry colname="col3" align="left" valign="top"><para>grandchild, cousin, <content-style font-style="italic">etc</content-style>., shall be described as “lawful” and “one of the next-of-kin” or “only next-of-kin”.</para></entry>
and below XSL
<xsl:template match="entry" name="entry">
<xsl:choose>
<xsl:when test="./#namest">
<xsl:variable name="namest" select="#namest" />
<xsl:variable name="nameend" select="#nameend" />
<xsl:variable name="namestPos" select="count(ancestor::tgroup/colspec[#colname=$namest]/preceding-sibling::colspec)" />
<xsl:variable name="nameendPos" select="count(ancestor::tgroup/colspec[#colname=$nameend]/preceding-sibling::colspec)" />
<td colspan="{$nameendPos - $namestPos + 1}" align="{#align}">
<xsl:apply-templates select="child::node()[not(self::page)]" />
</td>
</xsl:when>
<xsl:otherwise>
<td>
<xsl:if test="./#morerows">
<xsl:attribute name="rowspan">
<xsl:value-of select="number(./#morerows)+1" />
</xsl:attribute>
</xsl:if>
<xsl:if test="./#align">
<xsl:attribute name="align">
<xsl:value-of select="#align" />
</xsl:attribute>
</xsl:if>
<xsl:if test="./#valign">
<xsl:attribute name="valign">
<xsl:value-of select="#valign" />
</xsl:attribute>
</xsl:if>
<xsl:for-each select="para">
<div class="para">
<xsl:choose>
<xsl:when test="../#colname='col3' and contains(./text(),'.')">
<xsl:variable name="strl">
<xsl:value-of select="fn:string-length(fn:substring-before(.,'.'))" />
</xsl:variable>
<xsl:choose>
<xsl:when test="$strl < '6'">
<a href="{concat('er:#SCP_ORD_',//chapter/#num,'/','P',translate(./text(),'.','-'))}">
<xsl:value-of select="./text()" />
</a>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:for-each>
</td>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
and when i run this on my XML in the above mentioned line(the XML given in the sample), it is throwing me an error. and the error is as below.
Wrong occurrence to match required sequence type - Details: - XPTY0004: The supplied sequence ('2' item(s)) has the wrong occurrence to match the sequence type xs:string ('zero or one')
here what i actually was trying to achieve is, i have another table(which is basically a TOC), where in there is some linking needed, and below is such sample entries.
<entry colname="col3" align="right" valign="top"><para>A.1</para></entry>
<entry colname="col3" align="right" valign="top"><para>A.2</para></entry>
here i'm searching if the colname is col3 and if this has a . in it, and the above two cases mentioned are passing and are getting linked successfully, where in the case mentioned in the top is throwing the error, can anyone please suggest some better method to differentiate these two cases, and i use XSLT 2.0.
Thanks
The problem is
contains(./text(),'.')
./text() is not "the text of the current element" but rather the sequence of all text node children of the current element. In the case of
<para>grandchild, cousin, <content-style font-style="italic">etc</content-style>., shall be described as “lawful” and “one of the next-of-kin” or “only next-of-kin”.</para>
there are two such nodes, one containing everything between the <para> and <content-style> tags ("grandchild, cousin, " including the trailing space) and the other containing everything between the </content-style> and the </para>. But the contains function expects its first argument to be a single string, not a sequence of two nodes.
Instead of testing ./text() you probably just need to test .:
contains(., '.')
which is interpreted as the string value of the whole para element, i.e. a single string consisting of the concatenation of all the descendant text nodes (so the whole text "grandchild, cousin, etc., shall be described...").

XSLT setting a variable to either foo or #foo depending on presence of attribute

I'm trying to set parttext to #head if use_head="true" is not present and to the contents of <head> </head> if use_head="true" is present
<xsl:template match="toc_part">
<xsl:variable name="artnum">
<xsl:value-of select="../#num" />
</xsl:variable>
<xsl:variable name="partnum">
<xsl:value-of select="#num" />
</xsl:variable>
<xsl:variable name="parttext">
<xsl:if test="#use_head">
<xsl:value-of select="head"/>
</xsl:if>
<xsl:if test="not[#use_head]">
<xsl:value-of select="#head"/>
</xsl:if>
</xsl:variable>
<tr>
<td>
 
</td>
<td>
<a class="int" href="{$GBLfilePre}{$artnum}.html#{$artnum}{$partnum}"><xsl:text>Part </xsl:text><xsl:value-of select="$partnum" /></a>
</td>
<td>
<xsl:value-of select="$parttext" />
</td>
</tr>
</xsl:template>
I've also tried:
<xsl:if test="#use_head"><xsl:variable name="parttext"><xsl:value-of select="head"></xsl:variable>
<xsl:if test="not[#use_head"]><xsl:variable name="parttext"><xsl:value-of select="#head"></xsl:variable>
which give parttext undefined when referenced.
Try:
<xsl:variable name="parttext">
<xsl:choose>
<xsl:when test="#use_head='true'">
<xsl:value-of select="head"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#head"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Your current test in the first if statement will evaluate to true() if the #use_head attribute exists, regardless of it's value. If you want the test to evaluate whether or not it's value is equal to "true" you should use test="#use_head='true'".
The second if statement is evaluating whether or not there is an element named "not" that has an attribute called "use_head", because the square brackets are a predicate. I believe you had intended to use the not() function: not(#use_head='true')
You could then use an <xsl:choose> instead of two if blocks:
<xsl:variable name="parttext">
<xsl:choose>
<xsl:when test="#use_head='true'">
<xsl:value-of select="head"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#head"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
In XSLT 2.0:
<xsl:variable name="head"
select="if (#use_head='true') then head else #head"/>
In XSLT 1.0, either Hansen's solution, or
<xsl:variable name="head"
select="head[current()/#use_head='true'] |
#head[not(current()/#use_head='true')]"/>
In future, please tell us which version of XSLT you are using. One can sort of guess this must be XSLT 1.0 because in 2.0 the solution is trivial, but it's better not to have to guess.

Get file size from media folder in Umbraco using XSLT

Below is a part of my Umbraco xslt where I am outputting a PDF file link and the size of the file. The problem I am having, is targetting the size attribute of the media file. The size attribute has the alias of umbracoBytes.
I just seem to not be able to target this.
So all I am outputting at the moment is the link to open the PDF, but not the file size.
Anyone who can help would be greatly appreciated. Thanks.
<td>
<xsl:if test="document= ''">
<xsl:value-of select="#nodeName"/>
</xsl:if>
<xsl:if test="document != ''">
<a target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="umbraco.library:GetMedia(document, 'false')/umbracoFile"/>
</xsl:attribute>
<xsl:value-of select="#nodeName"/>
<xsl:variable name="size" select="data [#alias = 'umbracoBytes']" />
<xsl:variable name="sizeAndSuffix">
<xsl:choose>
<xsl:when test="$size >= 1073741824">
<xsl:value-of select="format-number($size div 1073741824,'#,###')"/>
<xsl:text>GB</xsl:text>
</xsl:when>
<xsl:when test="$size >= 1048576">
<xsl:value-of select="format-number($size div 1048576,'#,###')"/>
<xsl:text>MB</xsl:text>
</xsl:when>
<xsl:when test="$size >= 1024">
<xsl:value-of select="format-number($size div 1024,'#,###')"/>
<xsl:text>KB</xsl:text>
</xsl:when>
<xsl:when test="$size > 0 and $size < 1024">
<xsl:value-of select="format-number($size div 0,'#,###')"/>
<xsl:text>Bytes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>0 Bytes</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
</a>
</xsl:if>
</td>
The problem (I think) is that because you didn't prepend your $size variable select attribute with any target, so by default it uses $currentPage or the current value in a loop iteration.
Try assigning the GetMedia statement to a variable and then get the data from that. Your code appears to have syntax used in different versions of Umbraco, so I can't quite tell which one you're using. Different versions of Umbraco use different underlying XML structure.
If using < Umbraco 4.5.1
<xsl:variable name="myDocument" select="umbraco.library:GetMedia(document, 'false')" />
<a target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="$myDocument/data[#alias='umbracoFile']"/>
</xsl:attribute>
...
<xsl:variable name="size" select="$myDocument/data [#alias = 'umbracoBytes']" />
...
</a>
If using >= Umbraco 4.5.1
<xsl:variable name="myDocument" select="umbraco.library:GetMedia(document, 'false')" />
<a target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="$myDocument/umbracoFile"/>
</xsl:attribute>
...
<xsl:variable name="size" select="$myDocument/umbracoBytes" />
...
</a>
I managed to work it out.
I removed the wrapping variable and created a variable named size which selects the alias UmbracoBytes (the file size)
I then pass the variable through the choose function and this outputs the correct size extension.
Thanks for all your suggestions I really appreciate that!
<xsl:variable name="size" select="umbraco.library:GetMedia(document, 'false')/umbracoBytes"/>
<xsl:choose>
<xsl:when test="$size >= 1073741824">
<xsl:value-of select="format-number($size div 1073741824,'#,###')"/>
<xsl:text>GB</xsl:text>
</xsl:when>
<xsl:when test="$size >= 1048576">
<xsl:value-of select="format-number($size div 1048576,'#,###')"/>
<xsl:text>MB</xsl:text>
</xsl:when>
<xsl:when test="$size >= 1024">
<xsl:value-of select="format-number($size div 1024,'#,###')"/>
<xsl:text>KB</xsl:text>
</xsl:when>
<xsl:when test="$size > 0 and $size < 1024">
<xsl:value-of select="format-number($size div 0,'#,###')"/>
<xsl:text>Bytes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>0 Bytes</xsl:text>
</xsl:otherwise>
</xsl:choose>

XSL where statement

I'm looking for some help with an XSL selector:
What I need is a selector that will show the Title of a different list where the Document of the same row matches the Name field. If there is no entry, I will show a link to create a new one. Here is what I have:
<xsl:choose>
<xsl:when test="/dsQueryResponse
/Change_Types
/Rows
/Row
/#Document = #Name"/>
<xsl:value-of select="/dsQueryResponse
/Change_Types
/Rows
/Row
/#Document[
/dsQueryResponse
/Change_Types
/Rows
/Row
/#Document = #Name
]"/>
</xsl:when>
<xsl:otherwise>
<!-- Code to show link -->
</xsl:otherwise>
</xsl:choose>
If anyone could point out where I'm going wrong, it would be greatly appreciated!
In the absence of your source XML it's a complete guess, but I suspect that
#Document = #Name
should be
#Document = current()/#Name
on both occasions. Unless you really do want the Document and Name attributes of the same element to have the same value.
Here's how I did it:
A variable needed to be created for #FileLeafRef so it could be preserved to test against inside the for-each.
<xsl:variable name="document" select="#FileLeafRef"/>
<xsl:choose>
<!-- If there is an entry in the 'Tickets' list for this #FileLeafRef -->
<xsl:when test="/dsQueryResponse/Tickets/Rows/Row/#Document = $document">
<!-- Show it here -->
<xsl:for-each select="/dsQueryResponse/Tickets/Rows/Row">
<xsl:if test="#Document = $document">
<xsl:value-of select="#Title"/>
</xsl:if>
</xsl:for-each>
</xsl:when>
<!-- Else, show a link to add a new Ticket with the document auto-populated -->
<xsl:otherwise>
<xsl:call-template name="addNewItemLink">
<xsl:with-param name="list" select="'Tickets'"/>
<xsl:with-param name="document" select="#FileLeafRef"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>

XSLT Paging - default to current Date

I am using an xslt transform to show a long list of events.
It has paging, but what I would like is for it to default to the first events that are closest to the current date.
I'm assuming you have a useful date format (YYYY-MM-DD).
<xsl:param name="currentDate" select="''" /><!-- fill this from outside! -->
<xsl:template name="isPageSelected"><!-- returns true or false -->
<xsl:param name="eventsOnPage" /><!-- expects a node-set of events -->
<xsl:choose>
<xsl:when test="$eventsOnPage">
<!-- create a string "yyyy-mm-dd,YYYY-MM-DD-" (note the trailing dash) -->
<xsl:variable name="dateRange">
<xsl:for-each select="$eventsOnPage">
<xsl:sort select="date" />
<xsl:if test="position() = 1">
<xsl:value-of select="concat(date, ',')" />
</xsl:if>
<xsl:if test="position() = last()">
<xsl:value-of select="concat(date, '-')" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- trailing dash ensures that the "less than" comparison succeeds -->
<xsl:value-of select="
$currentDate >= substring-before($dateRange, ',')
and
$currentDate < substring-after($dateRange, ',')
" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="false()" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
So in your paging routine, to find out if the current page is the selected one, invoke
<xsl:variable name="isPageSelected">
<xsl:call-template name="isPageSelected">
<xsl:with-param name="eventsOnPage" select="event[whatever]" />
</xsl:call-template>
</xsl:variable>
<!-- $isPageSelected now is true or false, proceed accordingly -->
Since sorting in XSLT 1.0 is horribly, horribly broken, your best bet is to find an extension or include a Unix-style time somewhere in your source XML so you can sort on that (although an ISO-formatted string might also work).