I'm can't figure out how to test if a non-mandatory property is empty in my umbraco site, and currently my pages cause an XSLT parsing error if said property is empty. My current code is simple:
<xsl:variable name="media" select="umbraco.library:GetMedia(sectionImage, 0)" />
<xsl:if test="$media"> <!-- or $media != null -->
<xsl:variable name="url" select="$media/umbracoFile" />
<xsl:element name="img">
<xsl:attribute name="src"><xsl:value-of select="$url" />
</xsl:attribute>
</xsl:element>
</xsl:if>
I'm running Umbraco v6.0.6 and I've using the error checking solution as provided on the umbraco wiki, at http://our.umbraco.org/wiki/reference/umbracolibrary/getmedia
When I tried a similar style logic in C# I found that the test variable, $media, would have a value such as "umbraco.presentation.nodeFactory.Property." This filler content would wrongly bypass the if test, and then cause a break down.
This is happening on a variety of data types; media files, text strings, integers, etc.
Thank you for your time reading my post.
umbraco.library:GetMedia cannot return null, you may get an error back if no media was matched, in example
<error>No media is maching '123123'</error>
The thing is that your code works but you dont close the <xsl:attribute name="src"> as you should
<xsl:attribute name="src">
<xsl:value-of select="$url" />
</xsl:attribute>
If you for some reason really like to ensure that there is an image in there you should write a "test" and count values in the nodeTypeAlias
<xsl:variable name="media" select="umbraco.library:GetMedia(sectionImage, 0)" />
<xsl:if test="count($media[#nodeTypeAlias='Image']) > 0">
<xsl:variable name="url" select="$media/umbracoFile" />
<xsl:element name="img">
<xsl:attribute name="src">
<xsl:value-of select="$url" />
</xsl:attribute>
</xsl:element>
</xsl:if>
The xpath becomes a bit "odd" since you work with zero depth selection, if you fetch media with a greater depth you must adjust the count a bit
<xsl:variable name="media" select="umbraco.library:GetMedia(sectionImage, 0)" />
<xsl:if test="count($media/*[#nodeTypeAlias='Image']) > 0">
...
</xsl:if>
To test if a folder was selected simply check for folder instead
<xsl:variable name="media" select="umbraco.library:GetMedia(sectionImage, 0)" />
<xsl:if test="count($media[#nodeTypeAlias='Folder']) > 0">
...
</xsl:if>
Good luck
Okay so the initial IF condition has to do this:
<xsl:if test = "sectionImage != ''">
Which is how XSLT/Xpath checks if a XML tag is null. Then Eric Herlitz's solution can be used, or a dummy approach to just start assigning values without the secondary IF condition.
Related
I'm trying to determine if ../../coupon_code is null or empty. I've tried the methods in this thread Check if a string is null or empty in XSLT to no avail. Maybe I'm doing something wrong?
<!--Coupon Code Name and Code-->
<xsl:choose>
<xsl:when test="not(../../coupon_code)">
<xsl:if test="../../coupon_code != ''">
<xsl:value-of select="../../coupon_rule_name" /> <xsl:value-of select="../../coupon_code" /><xsl:value-of select="$sepend" />D.PROMOTION<xsl:value-of select="$sepend" />
</xsl:if>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
<!--End Coupon Code Name and Code-->
I'm doing
<xsl:when test="not(../../coupon_code)">
To determine if it's null. Then, I'm doing
<xsl:if test="../../coupon_code != ''">
To determine if it's empty.
However, I'm looking at data where this is clearly populated, and it's not entering the when/if to display the data at all. So it's failing somewhere and I don't know where.
Sometimes, ../../coupon_code will have a coupon code in it, like COUPON122. Sometimes it won't have anything in it.
You can check
<xsl:if test="normalize-space(../../coupon_code)">
<xsl:value-of select="../../coupon_rule_name" /> <xsl:value-of select="../../coupon_code" /><xsl:value-of select="$sepend" />D.PROMOTION<xsl:value-of select="$sepend" />
</xsl:if>
to have the xsl:value-ofs processed only if the ../../coupon_code element has some non-whitespace content.
I am using Umbraco 4.5 (yes, I know I should upgrade to 7 now!)
I have an XSLT transform which builds up a list of products which match user filters.
I am making an XSL:variable which is a collection of products from the CMS database.
Each product has several Yes/No properties (radio buttons). Some of these haven't been populated however.
As a result, the following code breaks occasionally if the dataset includes products which don't have one of the options populated with an answer.
The error I get when it transforms the XSLT is "Value was either too large or too small for an Int32". I assume this is the value being passed into the GetPreValueAsString method.
How do I check to see if ./option1 is empty and if so, use a specific integer, otherwise use ./option1
<xsl:variable name="nodes"
select="umbraco.library:GetXmlNodeById(1098)/*
[#isDoc and string(umbracoNaviHide) != '1' and
($option1= '' or $option1=umbraco.library:GetPreValueAsString(./option1)) and
($option2= '' or $option2=umbraco.library:GetPreValueAsString(./option2)) and
($option3= '' or $option3=umbraco.library:GetPreValueAsString(./option3)) and
($option4= '' or $option4=umbraco.library:GetPreValueAsString(./option4))
]" />
Note: you tagged your question as XSLT 2.0, but Umbraco does not use XSLT 2.0, it is (presently) stuck with XSLT 1.0.
$option1= '' or $option1=umbraco.library:GetPreValueAsString(./option1)
There can be multiple causes for your error. A processor is not required to process the or-expression left-to-right or right-to-left, and it is even allowed to always evaluate both expressions, even if the first is true (this is comparable with bit-wise operators (unordered) in other languages, whereas boolean operators (ordered) in those languages typically use early breakout).
Another error can be that your option value in the context node is not empty and is not an integer or empty, in which case your code will always return an error.
You could expand your expression by testing ./optionX, but then you still have the problem of order of evaluation.
That said, how can you resolve it and prevent the error from arising? In XSLT 1.0, this is a bit clumsy (i.e., you cannot define functions and cannot use sequences), but here's one way to do it:
<xsl:variable name="pre-default-option">
<default>1</default>
<default>2</default>
<default>3</default>
<default>4</default>
</xsl:variable>
<xsl:variable name="default-option"
select="exslt:node-set($pre-default-option)" />
<xsl:variable name="pre-selected-option">
<option><xsl:value-of select="$option1" /></option>
<option><xsl:value-of select="$option2" /></option>
<option><xsl:value-of select="$option3" /></option>
<option><xsl:value-of select="$option4" /></option>
</xsl:variable>
<xsl:variable name="selected-option" select="exslt:node-set($pre-selected-option)" />
<xsl:variable name="pre-process-nodes">
<xsl:variable name="selection">
<xsl:apply-templates
select="umbraco.library:GetXmlNodeById(1098)/*"
mode="pre">
<xsl:with-param name="opt-no" select="1" />
</xsl:apply-templates>
</xsl:variable>
<!-- your original code uses 'and', so is only true if all
conditions are met, hence there must be four found nodes,
otherwise it is false (i.e., this node set will be empty) -->
<xsl:if test="count($selection) = 4">
<xsl:copy-of select="$selection" />
</xsl:if>
</xsl:variable>
<!-- your original variable, should now contain correct set, no errors -->
<xsl:variable name="nodes" select="exslt:node-set($pre-process-nodes)"/>
<xsl:template match="*[#isDoc and string(umbracoNaviHide) != '1']" mode="pre">
<xsl:param name="opt-no" />
<xsl:variable name="option"
select="$selected-option[. = string($opt-no)]" />
<!-- gets the child node 'option1', 'option2' etc -->
<xsl:variable
name="pre-ctx-option"
select="*[local-name() = concat('option', $opt-no)]" />
<xsl:variable name="ctx-option">
<xsl:choose>
<!-- empty option param always allowed -->
<xsl:when test="$option = ''">
<xsl:value-of select="$option"/>
</xsl:when>
<!-- if NaN or 0, this will return false -->
<xsl:when test="number($pre-ctx-option)">
<xsl:value-of select="$default-option[$opt-no]"/>
</xsl:when>
<!-- valid number (though you could add a range check as well) -->
<xsl:otherwise>
<xsl:value-of select="umbraco.library:GetPreValueAsString($pre-ctx-option)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- prevent eternal recursion -->
<xsl:if test="4 >= $opt-no">
<xsl:apply-templates select="self::*" mode="pre">
<xsl:with-param name="opt-no" select="$opt-no + 1" />
</xsl:apply-templates>
<!-- the predicate is now ctx-independent and just true/false
this copies nothing if the conditions are not met -->
<xsl:copy-of select="self::*[$option = $ctx-option]" />
</xsl:if>
</xsl:template>
<xsl:template match="*" mode="pre" />
Note (1): I have written the above code by hand, tested only for syntax errors, I couldn't test it because you didn't provide an input document to test it against. If you find errors, by all means, edit my response so that it becomes correct.
Note (2): the above code generalizes working with the numbered parameters. By generalizing it, the code becomes a bit more complicated, but it becomes easier to maintain and to extend, and less error-prone for copy/paste errors.
I'm not very familiar with xsl, so I'm sort of stumbling my way though this.
My xsl file is building a menu. I am trying to sort the menu items by the value in menu title field in Sitecore. When I run the code, it does not sort. It just writes out each menu item four times.
Can anyone shed some light on what I am missing?
<xsl:template name="show-title">
<xsl:param name="root" />
<xsl:for-each select="$sc_currentitem/item">
<xsl:sort select="sc:fld('menu title',.)" order="ascending"/>
<xsl:choose>
<xsl:when test="sc:fld('menu title',$root)!=''">
<sc:text field="menu title" select="$root" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$root/#name" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
EDIT: Below is the data that the code above is generating
Example Output:
03/05/201203/05/201203/05/201203/05/2012
03/01/201203/01/201203/01/201203/01/2012
03/08/201203/08/201203/08/201203/08/2012
03/02/201203/02/201203/02/201203/02/2012
03/07/201203/07/201203/07/201203/07/2012
I am trying to get it to generate the following:
03/01/2012
03/02/2012
03/05/2012
03/07/2012
03/08/2012
Thanks!
It looks like you are trying to read menu title field from the wrong node. You should be reading it from context node --> . <--
Try this
<xsl:template name="show-title">
<xsl:param name="root" />
<xsl:for-each select="$sc_currentitem/item">
<xsl:sort select="sc:fld('menu title',.)" order="ascending"/>
<xsl:choose>
<xsl:when test="sc:fld('menu title',$root)!=''">
<sc:text field="menu title" select="." />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="./#name" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
This is just a guess as you don't really supply enough information for anyone to do more than guess but....
Within your for-each you are referring to $root e.g. <xsl:value-of select="$root/#name" />
I am guessing that the $root parameter contains a list of some kind and that you should be selecting just part of this list based on some value from the current for-each context
I've got the following XSLT code which lists out the folders and their file items from a specified node.
This all works fine but I'd like to parameterise the page and optionally filter its output by a tag value.
Being an XLST numpty I'm stumped with the syntax for the conditional I should be putting in under the <xsl:when test="$tag"> clause - can someone please help ?
<xsl:variable name="tag" select="umbraco.library:Request('tag')" />
<xsl:template match="/">
<!-- Root folder in Media that holds the folders to output -->
<xsl:variable name="mediaRootFolderId" select="5948" />
<!-- Pass in true() to get XML for all nodes below -->
<xsl:variable name="mediaRootNode" select="umbraco.library:GetMedia($mediaRootFolderId, true())" />
<xsl:choose>
<xsl:when test="$tag">
</xsl:when>
<xsl:otherwise>
<!-- If we didn't get an error, output Folder elements that contain Image elements -->
<xsl:apply-templates select="$mediaRootNode[not(error)]/Folder[File]" >
<xsl:sort select="#nodeName"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Template for folders -->
<xsl:template match="Folder">
<div class="folder">
<h2>Folder: <xsl:value-of select="#nodeName" /></h2>
<div class="images">
<xsl:apply-templates select="File">
<xsl:sort select="#nodeName"/>
</xsl:apply-templates>
</div>
</div>
</xsl:template>
<!-- Template for files -->
<xsl:template match="File">
File: <a href="{umbracoFile}" alt="{#nodeName}" ><xsl:value-of select="#nodeName" /></a> <br/>
</xsl:template>
Instead of the long <xsl:choose> instruction, use:
<xsl:apply-templates select=
"$mediaRootNode[not($tag)][not(error)]
/Folder[File]" >
Explanation: For the XPath expression in the select attribute above to select a non-empty set of nodes it is necessary that boolean($tag) is true(). Thus the above single <xsl:apply-templates> instruction is equivalent to the long <xsl:choose> in the question.
you can test if $tag is set like this.
<xsl:param name="tag">
<xsl:message terminate="yes">
$tag has not been set
</xsl:message>
</xsl:param>
This isn't standard though, it'll work on most XSLT processors though.
If you wanted to be absolutely save, you could set the value to an illegal value (such as 1 div 0) and test for it in the body of the template:
<xsl:param name="tag" select="1 div 0" />
<xsl:if test="$tag = 1 div 0">
<xsl:message terminate="yes">
$tag has not been set, or has been set to Infinity, which is invalid.
</xsl:message>
</xsl:if>
Source: O'Reilly XSLT Cookbook
In XSLT, what is the preferred way to keep code DRY when it comes to 'if's?
At the moment I am doing this:
<xsl:if test="select/some/long/path">
<element>
<xsl:value-of select="select/some/long/path" />
</element>
</xsl:if>
I would prefer to only write "select/some/long/path" once.
I see your point. When the path is 200 chars long the code can get messy.
You could just add it to a variable
<xsl:variable name="path" select="select/some/long/path"/>
<xsl:if test="$path">
<xsl:value-of select="$path" />
</xsl:if>
Where is the difference between:
<xsl:if test="select/some/long/path">
<xsl:value-of select="select/some/long/path" />
</xsl:if>
and
<xsl:value-of select="select/some/long/path" />
? If it does not exist, value-of will output an empty string (i.e.: nothing). So why the test?