Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • William Charlton 171 posts 218 karma points
    Dec 22, 2011 @ 19:27
    William Charlton
    0

    Why do all the Umbraco sites I see only have one top-level (menu) node?

    This has probably been answered elsewhere but I couldn't find any mention of it. All the Umbraco examples I see have a Home page and then other pages as children of the Home page. There is then some extra code in the menu xslt to get the level 1 and level 2 nodes to show as the top level menu, generally hard-coding the Home page. Is there a reason for this?

    I have created several pages directly under Content and it all seems to be fine but I'm concerned that I have missed something and it will come back to bite me.

    In case anyone needs the code for a menu that does this, it is below:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xsl:stylesheet [
        <!ENTITY nbsp "&#x00A0;">
    ]>

    <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"
    exclude-result-prefixes="msxml umbraco.library">
     <xsl:output method="xml" omit-xml-declaration="yes"/>
      <xsl:param name="currentPage"/>
      <xsl:param name="pLevel" select="1"/>
      <xsl:template match="/">
        <div class="MenuBar">
        <ul class="level_{$pLevel} Menu MenuHorizontal">
          <xsl:if test="count($currentPage/ancestor::root/* [@level=$pLevel]/* [@isDoc and string(umbracoNaviHide) != '1']) &gt; '0' ">
            <xsl:for-each select="$currentPage/ancestor::root/* [@isDoc and string(umbracoNaviHide) != '1']">
              <li>
                <a href="{umbraco.library:NiceUrl(@id)}">
                  <xsl:if test="$currentPage/@id = current()/@id">
                    <xsl:attribute name="class">ActiveClass</xsl:attribute>
                  </xsl:if>
                  <xsl:value-of select="@nodeName"/>       
                </a>
                <xsl:if test="current()/* [@isDoc]">
                  <xsl:call-template name="Menu">
                    <xsl:with-param name="pLevel" select="$pLevel+1"/>
                  </xsl:call-template>
                </xsl:if>
              </li>
            </xsl:for-each>
          </xsl:if>
        </ul>
        </div>
      </xsl:template>
      <xsl:template name="Menu">
        <xsl:param name="pLevel"/>
        <ul class="level_{$pLevel}">
          <xsl:for-each select="current()/* [@isDoc]">
            <li>
              <a href="{umbraco.library:NiceUrl(@id)}">
                <xsl:if test="$currentPage/@id = current()/@id">
                  <xsl:attribute name="class">ActiveClass</xsl:attribute>
                </xsl:if>
                <xsl:value-of select="@nodeName"/>
              </a>
              <xsl:if test="current()/* [@isDoc]">
                <xsl:call-template name="Menu">
                  <xsl:with-param name="level" select="$pLevel+1"/>
                </xsl:call-template>
              </xsl:if>
            </li>
          </xsl:for-each>
        </ul>
      </xsl:template>
    </xsl:stylesheet>
  • gilad 185 posts 425 karma points
    Dec 22, 2011 @ 19:44
  • William Charlton 171 posts 218 karma points
    Dec 22, 2011 @ 20:09
    William Charlton
    0

    Now that DOES make sense. We come from x years of DotNetNuke and have just started looking again at Umbraco and there is a certain amount of unlearning stuff but it is hard not to compare. One of the things DNN does well is have site-specific, or rather portal-specific, settings in one place, logo, default skin (template) etc...The link you posted handles this nicely.

    Toda Raba

  • William Charlton 171 posts 218 karma points
    Dec 23, 2011 @ 15:00
    William Charlton
    0

    I have had a play with the concept of a SiteSettings node at the top of the tree and think this a very elegant solution and it works well.

    Should anyone need it here is the code for a neat nested unordered list menu. Let me know if I have missed/overlooked anything or if anyone wants an explanation of what is going on in the xslt.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xsl:stylesheet [
      <!ENTITY nbsp "&#x00A0;">
    ]>
    <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" exclude-result-prefixes="msxml umbraco.library">
      <xsl:output method="xml" omit-xml-declaration="yes"/>
      <xsl:param name="currentPage"/>
      <xsl:param name="pLevel" select="1"/>
      <xsl:template match="/">
        <div class="MenuBar" style="color:#fff">
          <xsl:variable name="vMenuNode" select="$currentPage/ancestor::root/* [@level=$pLevel]"/>
          <xsl:if test="$vMenuNode/* [@isDoc and string(umbracoNaviHide) != '1']">
            <xsl:call-template name="Menu">
              <xsl:with-param name="pLevel" select="$pLevel"/>
              <xsl:with-param name="pMenuNode" select="$vMenuNode"/>
              <xsl:with-param name="pClass">
                <xsl:text>Menu MenuHorizontal Level</xsl:text>
                <xsl:value-of select="$pLevel"/>
              </xsl:with-param>
            </xsl:call-template>
          </xsl:if>
        </div>
      </xsl:template>
      <xsl:template name="Menu">
        <xsl:param name="pLevel"/>
        <xsl:param name="pMenuNode"/>
        <xsl:param name="pClass"/>
        <ul class="{$pClass}">
          <xsl:for-each select="$pMenuNode/* [@isDoc and string(umbracoNaviHide) != '1']">
            <li>
    <xsl:choose>
                <xsl:when test="$currentPage/@id = current()/@id">
                  <a class="ActiveClass" href="#">
                    <xsl:value-of select="@nodeName"/>
                  </a>
                </xsl:when>
                <xsl:otherwise>
                  <a href="{umbraco.library:NiceUrl(@id)}">
                    <xsl:value-of select="@nodeName"/>
                  </a>
                </xsl:otherwise>
              </xsl:choose>         
              <xsl:if test="current()/* [@isDoc and string(umbracoNaviHide) != '1']">
    <!--Any child nodes?-->
                <xsl:call-template name="Menu">
                  <xsl:with-param name="level" select="$pLevel+1"/>
                  <xsl:with-param name="pMenuNode" select="."/>
                  <xsl:with-param name="pClass">
                    <xsl:text>Level</xsl:text>
                    <xsl:value-of select="$pLevel"/>
                  </xsl:with-param>
                </xsl:call-template>
              </xsl:if>
            </li>
          </xsl:for-each>
        </ul>
      </xsl:template>
    </xsl:stylesheet>
     
  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Dec 23, 2011 @ 17:36
    Lee Kelleher
    0

    Hi WIlliam,

    Not being a fan of 'call-template', I took the liberty of reworking your XSLT with 'apply-templates', see what you think?

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE xsl:stylesheet [
        <!ENTITY nbsp "&#x00A0;">
    ]>
    <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"
        exclude-result-prefixes="msxml umbraco.library">
        <xsl:output method="xml" omit-xml-declaration="yes"/>
    
        <xsl:param name="currentPage"/>
    
        <xsl:template match="/">
            <xsl:apply-templates select="$currentPage/ancestor-or-self::*[@isDoc and @level = 1 and string(umbracoNaviHide) != '1']" />
        </xsl:template>
    
        <!-- CATCH ANY ROGUE PAGES/NODES (e.g. Any with umbracoNaviHide == true) -->
        <xsl:template match="*" />
    
        <!-- HOMEPAGE -->
        <xsl:template match="*[@isDoc and @level = 1 and string(umbracoNaviHide) != '1']">
            <xsl:if test="*[@isDoc and string(umbracoNaviHide) != '1']">
                <div class="MenuBar" style="color:#fff">
                    <ul class="Menu MenuHorizontal Level{@level}">
                        <xsl:apply-templates select="*[@isDoc and string(umbracoNaviHide) != '1']" />
                    </ul>
                </div>
            </xsl:if>
        </xsl:template>
    
        <!-- CONTENT PAGES/NODES -->
        <xsl:template match="*[@isDoc and string(umbracoNaviHide) != '1']">
            <li>
                <a href="{umbraco.library:NiceUrl(@id)}">
                    <xsl:if test="$currentPage/@id = @id">
                        <xsl:attribute name="class">ActiveClass</xsl:attribute>
                        <xsl:attribute name="href">#</xsl:attribute>
                    </xsl:if>
                    <xsl:value-of select="@nodeName"/>
                </a>
                <xsl:if test="*[@isDoc and string(umbracoNaviHide) != '1']">
                    <ul class="Menu MenuHorizontal Level{@level}">
                        <xsl:apply-templates select="*[@isDoc and string(umbracoNaviHide) != '1']" />
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>
    
    </xsl:stylesheet>

    It removes the use of passing the variables/parameters between template calls too.

    Merry Christmas!

    Cheers, Lee.

  • William Charlton 171 posts 218 karma points
    Dec 28, 2011 @ 15:04
    William Charlton
    0

    Hi Lee,

    I agree that apply-template is generally the "Approved" route to take. I do find however that they are harder to debug and sadly this one didn't work on my dev site, which incidentally has a top level "Settings" node, although that shouldn't have caused any problems. The output is below.

     <li><a href="/">Site Settings</a><ul class="Menu MenuHorizontal Level1"><li><a class="ActiveClass" href="#">Home</a><ul class="Menu MenuHorizontal Level2"><li><a href="/home/about-us.aspx">About Us</a></li></ul></li><li><a href="/xmldump.aspx">XMLDump</a></li></ul></li>
     

    I'll see if I can fathom what is going wrong.

    I also noticed a few queries in the forum about debugging the Umbraco XML output and have yet not seen any good examples of how to debug and develop against. My method is to take a copy of the /App_Data/umbraco.config, strip the DTD (so that it validates easily in XML Spy), remove (temporarily) the umbraco.library: namespaces from the XSL, add an xpath to the <xsl:param name="currentPage"/> so that it now has for example <xsl:param name="currentPage" select="/root/SiteSettings/YourTemplateName"/> and then add a reference in the XML to the XSLT file and away you go. It means I can debug/build faster outside of Umbraco. It's a bit of a fiddle and I don't of course get access to the Umbraco libraries but it's very early days for us with Umbraco so no doubt I'll impove on this and it certainly helps for now.

    What do you think?

    Nice to hear from you anyway.

    Happy New Year, Yoi otoshi o, Felice Anno Nuovo etc..

    William

    P.S. I saw your explanation of the name Umbraco. I can't help but think of shady/shadowy (umbra) company (co) which is obviously not a hugely fortunate translation but then Sweat sells well in Japan, so hey, as Alfred would say, "What me worry?"

    P.P.S You may remember we came to the meet-up in Bristol and to be honest put Umbraco aside for a while as just being too big an upheaval, from DNN, our current preferred CMS (I use the word "preferred" in a VERY broad sense) but have now had a better look and by starting with a completely blank set up and  avoiding the starter kits, which were just confusing us, we are now MUCH happier about transitioning. At some point I'll write up our experiences and findings, I think they may help other newcomers.

     

  • William Charlton 171 posts 218 karma points
    Dec 28, 2011 @ 19:41
    William Charlton
    1

    Unfortunately because I have a root Umbraco node Settings that holds all my top-level menu items I need a top-level <ul/> element that then contains second level UMB nodes.

    So I have to create a empty <ul class="TopLevelClass"></ul>, then select all second level UMB menu items to go inside.

    Now, when I select the top level UMB element (My Settings node) it is only so that I can iterate over its child elements that are "pages" and will make up my top-level menu items. Using the wild card selector however I will also be selecting any text elements in the Settings node, so I will therefore also be selecting all my "Settings" values, which will show as text strings.

    What I have to do then is match all second level elements and then apply templates. The code is below.

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE xsl:stylesheet [
        <!ENTITY nbsp "&#x00A0;">
    ]>
    <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" exclude-result-prefixes="msxml umbraco.library">
        <xsl:output method="xml" omit-xml-declaration="yes"/>
        <xsl:param name="currentPage" select="root/SiteSettings/Master"/>
        <xsl:template match="/">
            <div class="MenuBar" style="color:#fff">
                <ul class=" Menu MenuHorizontal  Level1">
                    <!--SELECT THE SECOND LEVEL ELEMENTS-->
                    <xsl:apply-templates select="$currentPage/ancestor-or-self::*[@isDoc and @level = 1 and string(umbracoNaviHide) != '1']/*[@isDoc and string(umbracoNaviHide) != '1']"/>
                </ul>
            </div>
        </xsl:template>
        <!-- BUILD THE MENU NODES -->
        <xsl:template match="*[@isDoc and @level != 1 and string(umbracoNaviHide) != '1']">
            <li>
                <a href="{umbraco.library:NiceUrl(@id)}">
                    <xsl:if test="$currentPage/@id = @id">
                        <xsl:attribute name="class">ActiveClass</xsl:attribute>
                        <xsl:attribute name="href">#</xsl:attribute>
                    </xsl:if>
                    <xsl:value-of select="@nodeName"/>
                </a>
                <xsl:if test="*[@isDoc and string(umbracoNaviHide) != '1']">
                    <ul class="Level{@level}">
                        <xsl:apply-templates select="*[@isDoc and string(umbracoNaviHide) != '1']"/>
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>
    </xsl:stylesheet>

    Your replacement of my choose block for the current page with a simple if looked like it would have a problem with two href attributes but it does seem to do a replace rather than an add, so thanks for enlightening me ;)

    This took a while to sort out so I hope someone else finds it useful.

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Jan 03, 2012 @ 11:45
    Lee Kelleher
    0

    Hi William, I've been away for Xmas/New Year - hence my delay in getting back to you.

    With debugging XSLT, take a look at the XSLT Visualizer - might be useful?

    Cheers, Lee.

  • William Charlton 171 posts 218 karma points
    Jan 03, 2012 @ 12:39
    William Charlton
    0

    Hi Lee,

    Welcome back, I hope you were somewhere warmer and less windy!!

    I looked at the Visualizer but didn't always get the results I expected, for example I have just installed a new site with the Starter kit from Koiak. When I select all the xslt in the Navigation.xslt and "Visualize" the only result I get is &nbsp; from any of the normal navigation nodes. Maybe I'm doing something wrong?

    We do a lot of xml-xsl work and I have a test rig which I can use for transforms, so I can play with files at will pretty easily. The only problem at the moment is the namespaces, which I'll tackle at some point. Removing the references works for now and gets me pretty well what I need with the method I outlined earlier. I can also fiddle with the XML to see what is doing what, which is not so easy inside Umbraco. I can also of course do the same thing inside XML-Spy but I don't have the same control.

    It's pretty early days for us and Umbraco still seems a bit arcane but I guess as we get more familiar with the methodology and the jargon it will get easier.

    Thanks

    William

     

  • William Charlton 171 posts 218 karma points
    Jan 03, 2012 @ 13:13
    William Charlton
    0

    A small note and not really related to this thread. I notice that Nils' improvement added ALL the namespaces in. I don't know what the overhead is but generally its good practice to use only the libraries you need.

    Just an observation.

  • Lee Kelleher 4020 posts 15802 karma points MVP 13x admin c-trib
    Jan 03, 2012 @ 13:23
    Lee Kelleher
    0

    I do agree, I tend to remove the unused namespace references.

    That said, the overhead for loading in the namespaces/classes for the XSLT extensions is minimal ... especially when compared with attempting to parse an XSLT for what could potentially be used.

    Main reason for including all the EXSLT namespaces was that majority of users/devs weren't aware of them.

  • William Charlton 171 posts 218 karma points
    Sep 21, 2012 @ 15:58
    William Charlton
    0

    The solution to this issue is that there is no "solution". My personal preference is to have a top level node which contains the "site config" data i.e. data that is default for the site and can be inherited by child nodes (site logo, title etc..) and is not actually a page node. Under this node then appear all the pages. This is pretty well as described in Cultiv.

    There are issues with this method though which might not suit all tastes as Umbraco by default expects the "Home" page to be created from the root (top) node and therefore an alternative page needs to be defined as Home.

    The robot wants this thread marked as a solution, perhaps someone with the correct strength/type glasses can enlighten me as to how one achieves this.

    Note to Nils: the ONE thing that Umbraco really needs is to be easier and less arcane e.g. what is the magic code I need to mark this as solved.

Please Sign in or register to post replies

Write your reply to:

Draft