It appears but then how do I know if the "Main" page is selected so that I can apply a css class like I do in the for-each?
2. In the for-each I add a "." after the link. Obviously I don't want this to happen in the case of the last node listed. How do I accomplish that? I probably should know beforehand the number of nodes that are going to be listed and in case it's the last one I don't add the "." .
As for the first one, I keep getting the same error (I got to a solution close to yours and got the same error):
Error occured System.OverflowException: Value was either too large or too small for an Int32. at System.Convert.ToInt32(Double value) at System.Double.System.IConvertible.ToInt32(IFormatProvider provider) at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) at System.Xml.Xsl.Runtime.XmlQueryRuntime.ChangeTypeXsltArgument(XmlQueryType xmlType, Object value, Type destinationType) at System.Xml.Xsl.Runtime.XmlQueryContext.InvokeXsltLateBoundFunction(String name, String namespaceUri, IList`1[] args) at (XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime) at Root(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime) at Execute(XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime) at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlSequenceWriter results) at System.Xml.Xsl.XmlILCommand.Execute(Object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer, Boolean closeWriter) at System.Xml.Xsl.XmlILCommand.Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results) at System.Xml.Xsl.XmlILCommand.Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, TextWriter results) at System.Xml.Xsl.XslCompiledTransform.Transform(IXPathNavigable input, XsltArgumentList arguments, TextWriter results) at umbraco.presentation.webservices.codeEditorSave.SaveXslt(String fileName, String oldName, String fileContents, Boolean ignoreDebugging)
Apparently the problem is in the NiceUrl function. Maybe the @id isn't correct?
Yeah, the error is due to the validator (on the XSLT editor) not liking the "$rootTextpageNode/@id" parameter, because it doesn't know that it's an numeric!
Quick answer would be to wrap number() around it, like so:
Reason is that the $rootTextpageNode/@id doesn't have a value until runtime, resulting in an empty string or NaN, which is invalid as parameter fed to the NiceUrl() function
Hi prl, my example above with the template (match=node) is used to apply to all the <node> elements that are passed to it.
It gives you better separate of HTML mark-up and logic. So if you needed to quickly modify the <a> tag, lets say by adding a new class or attribute, then you only need to do it in one place, rather than twice (for the homepage and in the for-each).
There are also performance gains with using "apply-templates" over "for-each" ... but for an XSLT this size, you'd never ever notice.
Don't forget to mark an answer as the solution... and to vote up any helpful answers!
First of all, the "[@level=$level]" part indicates from which level the "$currentPage/ancestor-or-self::node" is applied right? And "Main" is in level 0, "A, B, C and D" are in level 1?
So, my first doubt regarding this is: why is this printing "Main . A . B . C . D"? If I'm on node A (currentPage is node A) shouldn't it print only "A . Main"? Isn't that what $currentPage/ancestor-or-self::node does? Why is it printing everything? (I used this for reference: http://umbraco.org/documentation/books/xslt-basics/xpath-axes-and-their-shortcuts)
Second question is: if I want to print in my loop the path to my node (for example "Main . A . 2 . 5"), I should only remove the [@level=$level] part, right? But if I do this what I'm getting is: "Main . A . 1 . 2 . 3 . 4 . 5 . B . C . D" .. From the documentation, "ancestor-or-self" on node 5 should return "5 . 2 .A . Main".
Because, what I want is to test if the id of the page I am ($currentPage/self::node/@id) is the same as the id of the page being "looped" (current()/@id).
I tested and it worked. Actually I don't know how it was working the other way.
Well, I advanced a little more but still have doubts about some things, specially the for-each line and the axes.I read the documentation for this and understood well the functioning of it. Can someone explain me in detail how this line works?
I'll use the same structure as before for my example:
Main (TestHomepage doc type) |-- A (TestTextpage doc type, and all the rest are the same) ||--1 ||--2 ||--3 ||--4 ||--5 |-- B |-- C |-- D
I kow that the "$currentPage/ancestor-or-self::node" part returnsthe node I'm in and it's ancestors (or at least it should). So if I'm on the node 5 it should return nodes 5, 2, A and Main right? the "/node" bit returns the elements "node" for I'm in right? I still don't understand what the [@level=$level] is doing and how it works. The rest ([string(data [@alias='umbracoNaviHide']) != '1']) as far as I know is a best practice for this cases and actually in my case isn't doing anything since none of my Document Types has this property.
There are two distinct scenarios that I want to achieve:
1. List in my header the nodes from level 1 and 2, i.e., "Main, A, B, C, D" .. That's what it's doing right now, although I still haven't figured out how exactly.
2. List the complete path to the node I'm in, for example, "Main, A, 2, 5" . I can do this by having the for-each like this: <xsl:for-each select="$currentPage/ancestor-or-self::node"/>
Obviously, you can't learn XSLT from a couple of forum posts, my advice would be to read up about XPath Axes on W3Schools (and also Predicates)
You are right with your thinking about the "ancestor-or-self::node" matching "5, 2, A and Main" ... but also include the "[@level = $level]" predicate.
For a moment, swap $level with the number 1, (as that is what you have in the <xsl:variable>) ... so now you have, "$currentPage/ancestor-or-self::node[@level=1]" ... this will navigate up the node-set (from the current node) until it finds a <node> with the attribute "level" that has a value of "1". Which is the homepage (99% of the time).
As for 'umbracoNaviHide' - if you aren't using that property in your doc-types, then it's a waste of time ... so remove it. The reason it's there is because it gives you more control over which nodes you want to appear in your XSLT. (It's pretty much the de-facto for hiding nodes from sitemaps/navigation).
Lee, thanks for all your patience. I read the documentation here in the Umbraco forum and understood almost all of it but the predicate part was confusing me. I understand it completely now...
"$currentPage/ancestor-or-self::node[@level=1]" - this would return me all the previous nodes and the current node if it wasn't for the precidate @level=1 that limits the results to the node(s) with level = 1. In my case, only the node "Main".
By adding "/node" to the statment, I will get all the child nodes of "Main": A, B, C and D (no predicates here so all of them are returned). So, if I want the root node (Main) to appear on the menu I have to add it before the for-each loop.
I am trying to show few links base on user privileges parent and child li's, stuck at this code please help me, googled a lot nothing on these. these generic list items and user are from active directory and defined by numbers to show node name.
If parent has permission and few of the child elements share the same as parent permission want to hide. any advice.
Using Xslt for Navigation menu
Hello,
This probably was asked before but I didn't find anything, so sorry if I'm repeating a question:
In my Master Template I have a header section in which I want to include some navigation links, The structure of my contents is:
I want the header to look like this: "Main . About Us . About Them" .. Should be pretty simple. The code I have in my Xslt file is:
My problems are:
1. "Main" doesn's appear and I'm only able to do it like this (before the for-each):
It appears but then how do I know if the "Main" page is selected so that I can apply a css class like I do in the for-each?
2. In the for-each I add a "." after the link. Obviously I don't want this to happen in the case of the last node listed. How do I accomplish that? I probably should know beforehand the number of nodes that are going to be listed and in case it's the last one I don't add the "." .
Thanks in advance.
Hi prl,
There are a few ways to handle the "Main" (homepage) link, but I wont over-complicate things just yet. Try this XSLT:
Your second problem is also covered in the above XSLT. By using the following IF statement:
Hope that helps.
Cheers, Lee.
Hey Lee,
Thanks for your answer!
The second question was really simple :)
As for the first one, I keep getting the same error (I got to a solution close to yours and got the same error):
Apparently the problem is in the NiceUrl function. Maybe the @id isn't correct?
Hi prl,
Yeah, the error is due to the validator (on the XSLT editor) not liking the "$rootTextpageNode/@id" parameter, because it doesn't know that it's an numeric!
Quick answer would be to wrap number() around it, like so:
Alternatively, here is my more XSLT-like approach using an extra template:
Good luck, Lee.
Lee,
The number($rootTextpageNode/$id) still gives me the same error .
Can you briefly descbribe the functioning of that xslt, if that's not too much work for you? :)
Thanks!
Possible solutions:
- Surround the offending statement with
<xsl:if test="string($rootTextpageNode/@id)) != ''"> <a href="{umbraco.library:NiceUrl($rootTextpageNode/@id)}">
</xsl:if>
- Check the 'Skip errors' box
Reason is that the $rootTextpageNode/@id doesn't have a value until runtime, resulting in an empty string or NaN, which is invalid as parameter fed to the NiceUrl() function
Hope this helps,
/Dirk
Dirk, thanks for the help. I just had to put the </xsl:if> in the end of the part for the Main page link. Full xslt looks like this:
I guess I'll be stopping here a lot of times :)
Cheers,
Hi prl, my example above with the template (match=node) is used to apply to all the <node> elements that are passed to it.
It gives you better separate of HTML mark-up and logic. So if you needed to quickly modify the <a> tag, lets say by adding a new class or attribute, then you only need to do it in one place, rather than twice (for the homepage and in the for-each).
There are also performance gains with using "apply-templates" over "for-each" ... but for an XSLT this size, you'd never ever notice.
Don't forget to mark an answer as the solution... and to vote up any helpful answers!
Cheers, Lee.
Hey guys, I'll use this topic to post some other questions regarding this subject.
Imagining I have a structure like this:
In my xslt I have the variable level with the value 1 and a simple for-each loop:
First of all, the "[@level=$level]" part indicates from which level the "$currentPage/ancestor-or-self::node" is applied right? And "Main" is in level 0, "A, B, C and D" are in level 1?
So, my first doubt regarding this is: why is this printing "Main . A . B . C . D"? If I'm on node A (currentPage is node A) shouldn't it print only "A . Main"? Isn't that what $currentPage/ancestor-or-self::node does? Why is it printing everything? (I used this for reference: http://umbraco.org/documentation/books/xslt-basics/xpath-axes-and-their-shortcuts)
Second question is: if I want to print in my loop the path to my node (for example "Main . A . 2 . 5"), I should only remove the [@level=$level] part, right? But if I do this what I'm getting is: "Main . A . 1 . 2 . 3 . 4 . 5 . B . C . D" .. From the documentation, "ancestor-or-self" on node 5 should return "5 . 2 .A . Main".
Thanks again.
Sorry, made a confusion there. "Main" is being printed outside the loop. Either way, I'm still not understanding why it's printing everything..
Another question (sorry guys):
In my for-each loop where I apply a certain style to the selected page I'm doing this:
Shouldn't the bold and underlined line be:
Because, what I want is to test if the id of the page I am ($currentPage/self::node/@id) is the same as the id of the page being "looped" (current()/@id).
I tested and it worked. Actually I don't know how it was working the other way.
Thanks.
Hi prl,
You don't need to put the "/self::node" bit, as the context is already there, just use "$currentPage/@id".
The same goes for "current()/@id" - you can just use "@id". For example:
Hope that makes sense?
Cheers, Lee.
It makes, thanks :)
Well, I advanced a little more but still have doubts about some things, specially the for-each line and the axes.I read the documentation for this and understood well the functioning of it. Can someone explain me in detail how this line works?
<xsl:for-each select="$currentPage/ancestor-or-self::node [@level=$level]/node [string(data [@alias='umbracoNaviHide']) != '1']">
I'll use the same structure as before for my example:
I kow that the "$currentPage/ancestor-or-self::node" part returns the node I'm in and it's ancestors (or at least it should). So if I'm on the node 5 it should return nodes 5, 2, A and Main right? the "/node" bit returns the elements "node" for I'm in right? I still don't understand what the [@level=$level] is doing and how it works. The rest ([string(data [@alias='umbracoNaviHide']) != '1']) as far as I know is a best practice for this cases and actually in my case isn't doing anything since none of my Document Types has this property.
There are two distinct scenarios that I want to achieve:
1. List in my header the nodes from level 1 and 2, i.e., "Main, A, B, C, D" .. That's what it's doing right now, although I still haven't figured out how exactly.
2. List the complete path to the node I'm in, for example, "Main, A, 2, 5" . I can do this by having the for-each like this: <xsl:for-each select="$currentPage/ancestor-or-self::node"/>
Thanks for all the help.
Hi prl,
Obviously, you can't learn XSLT from a couple of forum posts, my advice would be to read up about XPath Axes on W3Schools (and also Predicates)
You are right with your thinking about the "ancestor-or-self::node" matching "5, 2, A and Main" ... but also include the "[@level = $level]" predicate.
For a moment, swap $level with the number 1, (as that is what you have in the <xsl:variable>) ... so now you have, "$currentPage/ancestor-or-self::node[@level=1]" ... this will navigate up the node-set (from the current node) until it finds a <node> with the attribute "level" that has a value of "1". Which is the homepage (99% of the time).
As for 'umbracoNaviHide' - if you aren't using that property in your doc-types, then it's a waste of time ... so remove it. The reason it's there is because it gives you more control over which nodes you want to appear in your XSLT. (It's pretty much the de-facto for hiding nodes from sitemaps/navigation).
Hope this helps?
Happy XSLT'ing, Lee
Lee, thanks for all your patience. I read the documentation here in the Umbraco forum and understood almost all of it but the predicate part was confusing me. I understand it completely now...
"$currentPage/ancestor-or-self::node[@level=1]" - this would return me all the previous nodes and the current node if it wasn't for the precidate @level=1 that limits the results to the node(s) with level = 1. In my case, only the node "Main".
By adding "/node" to the statment, I will get all the child nodes of "Main": A, B, C and D (no predicates here so all of them are returned). So, if I want the root node (Main) to appear on the menu I have to add it before the for-each loop.
All's clear now. Thanks!
Hi All,
I am trying to show few links base on user privileges parent and child li's, stuck at this code please help me, googled a lot nothing on these. these generic list items and user are from active directory and defined by numbers to show node name.
If parent has permission and few of the child elements share the same as parent permission want to hide. any advice.
<xsl:variable name="visitors" select="2"/>
<xsl:variable name="hr" select="3"/>
<xsl:variable name="finance" select="6"/>
<xsl:variable name="Admins" select="8"/>
<ul class="nav-menu">
<li class="first">
<a href="/intranet-home.aspx" alt="Intranet-Home">Home</a>
</li>
<xsl:for-each select="$currentPage/ancestor-or-self::*[@isDoc and @level=$level]/* [@isDoc and string(umbracoNaviHide) != '1']">
<li>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="@nodeName"/>
</a>
<xsl:if test="count(./child::*[@isDoc and string(umbracoNaviHide) != '1']) > 0">
<ul>
<xsl:for-each select="./child::*[@isDoc and string(umbracoNaviHide) != '1']"><xsl:choose>
<xsl:when test="contains(umbraco.library:Session('priviliges'), $PM_Privilige)"><xsl:if test="@nodeName='Updates'"><li>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="@nodeName"/>
</a>
</li>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<li>
<a href="{umbraco.library:NiceUrl(@id)}">
<xsl:value-of select="@nodeName"/>
</a>
</li>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</ul>
</xsl:if>
</li>
</xsl:for-each>
</ul>
is working on a reply...