Search In
Learn from 350 other Umbracians at the annual Umbraco Conference - CodeGarden '13. More than twenty high quality sessions, open spaces, hackathons and social events you'll remember. Not to be missed! Less than 25 tickets left - get yours now!
I'm having some issues getting multiple level navigation to work with the new xml schema, has anyone got a working example ready?
in the end i would like this to be able to work with a sourcenode to start iterating from, and respecting umbraconavihide...
This is the code that I am using now, but it doesn't go into levels, and doesn't return all nodes but only 1 document type for some reason...
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE xsl:stylesheet [<!ENTITY nbsp " ">]><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:msxml="urn:schemas-microsoft-com:xslt"xmlns:msxsl="urn:schemas-microsoft-com:xslt"xmlns:umbraco.library="urn:umbraco.library"xmlns:netaddicts-be="urn:netaddicts-be:xslt"exclude-result-prefixes="msxml umbraco.library netaddicts-be"><xsl:output method="xml" omit-xml-declaration="yes"/><!-- Holds the start node for the navigation. Optional --><xsl:param name="startNodeId" select="/macro/startNodeId"/><!-- Holds number of sublevels to generate. Macro parameter is optional. Defaults to one if not supplied --><xsl:param name="maxDrilldownLevel" select="netaddicts-be:ReadMacroParameter(//macro/maxLevels,'1')"/><xsl:template match="/"><!-- Check whether a start node has been supplied --><xsl:choose><xsl:when test="$startNodeId != ''"><!-- Start building the top navigation from the node supplied by start node id --><xsl:call-template name="buildTopNavigation"><xsl:with-param name="navigationNodes" select="umbraco.library:GetXmlNodeById($startNodeId)"/></xsl:call-template></xsl:when><xsl:otherwise><!-- Start building navigation from top level node --><xsl:call-template name="buildTopNavigation"><xsl:with-param name="navigationNodes" select="umbraco.library:GetXmlAll()"/></xsl:call-template></xsl:otherwise></xsl:choose></xsl:template><!-- Start building the top navigation (first level navigation) --><xsl:template name="buildTopNavigation"><xsl:param name="navigationNodes"/><ul class="mainNav"><!-- Iterate child nodes --> <xsl:for-each select="$navigationNodes/child/* [@isDoc]"><!-- Create var for easier reading/processing --><xsl:variable name="currentProcessedNode" select="."/><xsl:variable name="currentLevel" select="0"/><!-- Check whether node should be visible in first level navigation --><xsl:if test="string($currentProcessedNode/umbracoNaviHide) != '1'"><li><!-- Build the navigation link using the node currently being processed in the for-each loop --><xsl:call-template name="buildLink"><xsl:with-param name="node" select="$currentProcessedNode"/></xsl:call-template><!-- Build next level navigation only if applicable --><!-- Still need to check whether all child nodes have been set to umbracoHideChildren = 1 whereas umbracoNaviHide = 0this case would yield an empty ul element --><xsl:if test="(count($currentProcessedNode/node) > 0)and (string($currentProcessedNode/umbracoHideChildren) != '1')and ($currentLevel < $maxDrilldownLevel)"><xsl:call-template name="buildNavigation"><xsl:with-param name="parentNode" select="$currentProcessedNode"/><xsl:with-param name="level" select="$currentLevel + 1"/></xsl:call-template></xsl:if></li></xsl:if></xsl:for-each></ul></xsl:template><!-- A template used for building the non top navigation tree --><xsl:template name="buildNavigation"><xsl:param name="parentNode"/><xsl:param name="level"/><ul>a<!-- Iterate over the child nodes--><xsl:for-each select="$parentNode/* [@isDoc]"><!-- Create var for easier reading/processing --><xsl:variable name="currentProcessedNode" select="."/><!-- Check whether node should be processed --><xsl:if test="string($currentProcessedNode/umbracoNaviHide) != '1'"><li class="child"><!-- Build the navigation link --><xsl:call-template name="buildLink"><xsl:with-param name="node" select="$currentProcessedNode"/></xsl:call-template><!-- Build next level navigation only if applicable; recursive call --><!-- Still need to check whether all child nodes have been set to umbracoHideChildren = 1 whereas umbracoNaviHide = 0this case would yield an empty ul element --><xsl:if test="(count($currentProcessedNode/node) > 0)and (string($currentProcessedNode/umbracoHideChildren) != '1')and ($level < $maxDrilldownLevel)"><xsl:call-template name="buildNavigation"><xsl:with-param name="parentNode" select="$currentProcessedNode"/><xsl:with-param name="level" select="$level + 1"/></xsl:call-template></xsl:if></li></xsl:if></xsl:for-each></ul></xsl:template><!-- A template that builds our navigation link based on node properties --><xsl:template name="buildLink"><xsl:param name="node"/><xsl:choose><!-- Build link to external page --><xsl:when test="string($node/externalURL) != ''"><xsl:call-template name="buildExternalLink"><xsl:with-param name="node" select="$node"/></xsl:call-template></xsl:when><!-- Build link for redirecting to a custom supplied url --><xsl:when test="string($node/umbracoRedirect) != ''"><xsl:call-template name="buildRedirectLink"><xsl:with-param name="node" select="$node"/></xsl:call-template></xsl:when><!-- Default link builder --><xsl:otherwise><xsl:call-template name="buildNormalLink"><xsl:with-param name="node" select="$node"/></xsl:call-template></xsl:otherwise></xsl:choose></xsl:template><!-- A template that builds a link to an external page --><xsl:template name="buildExternalLink"><xsl:param name="node"/><!--<xsl:call-template name ="outputNode"><xsl:with-param name="currentNode" select="$* [@isDoc]"/></xsl:call-template>--><a><!-- Set the href attribute --><xsl:attribute name="href"><xsl:value-of select="$node/externalURL"/></xsl:attribute><!-- Set the target attribute if available from the properties --><xsl:if test="string($node/externalTarget) != ''"><xsl:attribute name="target"><xsl:value-of select="$node/externalTarget"/></xsl:attribute></xsl:if><!-- Set the title attribute if available from the properties --><xsl:if test="string($node/navTooltip) != ''"><xsl:attribute name="title"><xsl:value-of select="string($node/navTooltip)"/></xsl:attribute></xsl:if><!-- Set actual text for the link, either available from the properties or just plain umbraco link--><xsl:choose><xsl:when test="string($node/navText) != ''"><xsl:value-of select="string($node/navText)"/></xsl:when><xsl:otherwise><xsl:value-of select="$node/@nodeName"/></xsl:otherwise></xsl:choose></a></xsl:template><xsl:template name="buildRedirectLink"><xsl:param name="node"/><!--<xsl:call-template name ="outputNode"><xsl:with-param name="currentNode" select="$* [@isDoc]"/></xsl:call-template>--><a><!-- Set the href attribute based on the redirect supplied --><xsl:attribute name="href"><xsl:value-of select="netaddicts-be:FixLink(string($node/umbracoRedirect))"/></xsl:attribute><!-- Set the title attribute if available from the properties --><xsl:if test="string($node/navTooltip) != ''"><xsl:attribute name="title"><xsl:value-of select="string($node/navTooltip)"/></xsl:attribute></xsl:if><!-- Set actual text for the link, either available from the properties or just plain umbraco link--><xsl:choose><xsl:when test="string($node/navText) != ''"><xsl:value-of select="string($node/navText)"/></xsl:when><xsl:otherwise><xsl:value-of select="$node/@nodeName"/></xsl:otherwise></xsl:choose></a></xsl:template><xsl:template name="buildNormalLink"><xsl:param name="node"/><!--<xsl:call-template name ="outputNode"><xsl:with-param name="currentNode" select="$* [@isDoc]"/></xsl:call-template>--><a><!-- Set the href attribute, either the alias if available, else use NiceUrl() --><xsl:attribute name="href"><xsl:choose><xsl:when test="string($node/umbracoUrlAlias) != ''"><xsl:value-of select="netaddicts-be:FixLink(string($node/umbracoUrlAlias))"/></xsl:when><xsl:otherwise><xsl:value-of select="umbraco.library:NiceUrl($node/@id)"/></xsl:otherwise></xsl:choose></xsl:attribute><!-- Set the title attribute if available from the properties --><xsl:if test="string($node/navTooltip) != ''"><xsl:attribute name="title"><xsl:value-of select="string($node/navTooltip)"/></xsl:attribute></xsl:if><!-- Set actual text for the link, either available from the properties or just plain umbraco link--><xsl:choose><xsl:when test="string($node/navText) != ''"><xsl:value-of select="string($node/navText)"/></xsl:when><xsl:otherwise><xsl:value-of select="$node/@nodeName"/></xsl:otherwise></xsl:choose></a></xsl:template><!-- For debugging purposes, writes out all relevant node properties --><xsl:template name="outputNode"><xsl:param name="currentNode"/><ul><li>@id=<xsl:value-of select="$currentNode/@id"/></li><li>@nodeName=<xsl:value-of select="$currentNode/@nodeName"/></li><li>@umbracoNaviHide=<xsl:value-of select="$currentNode/umbracoNaviHide"/></li><li>@umbracoHideChildren=<xsl:value-of select="$currentNode/umbracoHideChildren"/></li><li>@navText=<xsl:value-of select="$currentNode/navText"/></li><li>@navTooltip=<xsl:value-of select="$currentNode/navTooltip"/></li><li>@externalURL=<xsl:value-of select="$currentNode/externalURL"/></li><li>@externalTarget=<xsl:value-of select="$currentNode/externalTarget"/></li><li>@umbracoRedirect=<xsl:value-of select="$currentNode/umbracoRedirect"/></li><li>@umbracoUrlAlias=<xsl:value-of select="$currentNode/umbracoUrlAlias"/></li></ul></xsl:template><msxsl:script language="C#" implements-prefix="netaddicts-be"><![CDATA[//Function is taken from XSLTSearch by Douglas Robar from Percipient Studios (http://www.percipientstudios.com/)public string ReadMacroParameter(string value, string defaultValue) {if (value == "")return defaultValue;elsereturn value.Replace(" ", "");}//Function fixes a possible wrongly formatted linkpublic string FixLink(string oldLink) {string newLink = string.Empty;if (!oldLink.StartsWith("/"))newLink += "/";newLink += oldLink;if (!oldLink.EndsWith(".aspx"))newLink += ".aspx";return newLink;}]]></msxsl:script></xsl:stylesheet>
I haven't tried this yet, but it seems like this line should be the problem
<xsl:for-each select="$navigationNodes/child/* [@isDoc]">
Have you tried
<xsl:for-each select="$navigationNodes/* [@isDoc]">
I've removed a lot of the complicated xslt and ended up with this:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE xsl:stylesheet [<!ENTITY nbsp " ">]><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:msxml="urn:schemas-microsoft-com:xslt"xmlns:msxsl="urn:schemas-microsoft-com:xslt"xmlns:umbraco.library="urn:umbraco.library"><xsl:output method="xml" /><xsl:param name="startNodeId" select="/macro/startNodeId"/><xsl:param name="maxDrilldownLevel" select="/macro/maxLevels"/><xsl:template match="/"> Startnode: <xsl:value-of select="$startNodeId"/><br />MAxlevel: <xsl:value-of select="$maxDrilldownLevel"/><br /><xsl:choose><xsl:when test="$startNodeId != ''"> <!-- Start building the top navigation from the node supplied by start node id --> <xsl:call-template name="buildTopNavigation"> <xsl:with-param name="navigationNodes" select="umbraco.library:GetXmlNodeById($startNodeId)"/> </xsl:call-template></xsl:when><xsl:otherwise> <!-- Start building navigation from top level node --> <xsl:call-template name="buildTopNavigation"> <xsl:with-param name="navigationNodes" select="umbraco.library:GetXmlAll()"/> </xsl:call-template></xsl:otherwise></xsl:choose></xsl:template> <xsl:template name="buildTopNavigation"><!-- Start building the top navigation (first level navigation) --><xsl:param name="navigationNodes"/>NAvigationnodes: <xsl:value-of select="$navigationNodes"/><ul class="mainNav"> <xsl:for-each select="$navigationNodes/* [@isDoc]"> a <xsl:variable name="currentProcessedNode" select="."/> <xsl:variable name="currentLevel" select="0"/> <!-- Check whether node should be visible in first level navigation --> <xsl:if test="string($currentProcessedNode/umbracoNaviHide) != '1'"> <li> <!-- Build the navigation link using the node currently being processed in the for-each loop --> <xsl:call-template name="buildLink"> <xsl:with-param name="node" select="$currentProcessedNode"/> </xsl:call-template><!-- Build next level navigation only if applicable --><xsl:if test="(count($currentProcessedNode/node) > 0) and ($currentLevel < $maxDrilldownLevel)"><xsl:call-template name="buildNavigation"><xsl:with-param name="parentNode" select="$currentProcessedNode"/><xsl:with-param name="level" select="$currentLevel + 1"/></xsl:call-template></xsl:if></li></xsl:if></xsl:for-each></ul></xsl:template><!-- A template used for building the non top navigation tree --><xsl:template name="buildNavigation"><xsl:param name="parentNode"/><xsl:param name="level"/><ul>a<!-- Iterate over the child nodes--><xsl:for-each select="$parentNode/* [@isDoc]"><!-- Create var for easier reading/processing --><xsl:variable name="currentProcessedNode" select="."/><!-- Check whether node should be processed --><xsl:if test="string($currentProcessedNode/umbracoNaviHide) != '1'"><li class="child"><!-- Build the navigation link --><xsl:call-template name="buildLink"><xsl:with-param name="node" select="$currentProcessedNode"/></xsl:call-template><!-- Build next level navigation only if applicable; recursive call --><!-- Still need to check whether all child nodes have been set to umbracoHideChildren = 1 whereas umbracoNaviHide = 0this case would yield an empty ul element --><xsl:if test="(count($currentProcessedNode/node) > 0)and (string($currentProcessedNode/umbracoHideChildren) != '1')and ($level < $maxDrilldownLevel)"><xsl:call-template name="buildNavigation"><xsl:with-param name="parentNode" select="$currentProcessedNode"/><xsl:with-param name="level" select="$level + 1"/></xsl:call-template></xsl:if></li></xsl:if></xsl:for-each></ul></xsl:template><!-- A template that builds our navigation link based on node properties --><xsl:template name="buildLink"><xsl:param name="node"/><xsl:call-template name="buildNormalLink"><xsl:with-param name="node" select="$node"/></xsl:call-template></xsl:template><xsl:template name="buildNormalLink"><xsl:param name="node"/><a><!-- Set the href attribute, either the alias if available, else use NiceUrl() --><xsl:attribute name="href"><xsl:value-of select="umbraco.library:NiceUrl($node/id)"/></xsl:attribute><!-- Set actual text for the link, either available from the properties or just plain umbraco link--><xsl:choose><xsl:when test="string($node/navText) != ''"><xsl:value-of select="string($node/navText)"/></xsl:when><xsl:otherwise><xsl:value-of select="$node/nodeName"/></xsl:otherwise></xsl:choose></a></xsl:template></xsl:stylesheet>
But now i get this error when saving:
Error occuredSystem.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 providerat 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, XPathNavigator node)at (XmlQueryRuntime {urn:schemas-microsoft-com:xslt-debug}runtime, IList`1 navigationNodes)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)at System.Xml.Xsl.XslCompiledTransform.Transform(IXPathNavigable input, XsltArgumentList arguments, XmlWriter results, XmlResolver documentResolver)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)
So you're passing an extension expecting an int a bad value. Maybe here
<xsl:value-of select="umbraco.library:NiceUrl($node/id)"/>
Should be @id instead? Might be time for the debugger to see which line is throwing the exception.
Jeff: are you sure? that's not what I conclude from these examples: our.umbraco.org/.../xslt-examples-updated-to-new-schema
Yes, id is still an attribute of whatever element you have in the node variable, so you access it with '@'. I can see where that document might have gotten you though. NodeTypeAlias used to be an attribute of the data element, so you had to do 'data [@nodeTypeAlias='whatever']'. That one attribute is now an element and that was the big change (and a very nice one). Other attributes are still attributes though, so need the @.
To create this navigation:
I finally came up with this xslt:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets "><xsl:output method="xml" omit-xml-declaration="yes" /><xsl:param name="currentPage"/><!-- Input the documenttype you want here --><xsl:variable name="level" select="2"/><xsl:template match="/"><!-- The fun starts here --> <xsl:for-each select="$currentPage/ancestor-or-self::* [@level=$level]/* [@isDoc and string(umbracoNaviHide) != '1']"> <xsl:if test="$currentPage/@id = current()/@id or $currentPage/../@id = current()/@id or $currentPage/../../@id = current()/@id"> <!-- output navigation Title --> <a class="leftnavtitle" href="{umbraco.library:NiceUrl(@id)}"> <xsl:value-of select="@nodeName"/> </a> <ul> <!-- 1st navigation items --> <xsl:for-each select="./child::* [@isDoc and string(umbracoNaviHide) != '1']"> <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id"></xsl:if> <li> <a href="{umbraco.library:NiceUrl(@id)}"> <xsl:if test="@id = $currentPage/@id"><xsl:attribute name="class">selected</xsl:attribute></xsl:if> <xsl:value-of select="@nodeName"/> </a> <!--output 2nd level nevigation items --> <xsl:if test="count(current()/child::* [@isDoc]) > 0 and $currentPage/@id = current()/@id or $currentPage/../@id = current()/@id or $currentPage/../../@id = current()/@id"> <ul> <!--output 3rd level nevigation items --> <xsl:for-each select="./child::* [@isDoc and string(umbracoNaviHide) != '1']"> <xsl:if test="$currentPage/ancestor-or-self::node/@id = current()/@id"></xsl:if> <li> <a href="{umbraco.library:NiceUrl(@id)}"> <xsl:if test="@id = $currentPage/@id"><xsl:attribute name="class">selected</xsl:attribute></xsl:if> <xsl:value-of select="@nodeName"/> </a> </li> </xsl:for-each> </ul> </xsl:if> <!--end of 4th level children --> </li> </xsl:for-each> </ul> </xsl:if> </xsl:for-each></xsl:template></xsl:stylesheet>
Thank you Rik!
I needed:
and $currentPage/@id = current()/@id or $currentPage/../@id = current()/@id or $currentPage/../../@id = current()/@id"
For my level 2 check, awesome!