views:

322

answers:

1

I'm developing a skin for DotNetNuke 5 using the Component DNN Done Right menu by Mark Alan which uses XSL-T to convert the XML sitemap into an HTML navigation.

The XML sitemap outputs the following structure:

<Root >
  <root >
    <node id="40" text="Home" url="http://localhost/dnn/Home.aspx" enabled="1" selected="0" breadcrumb="0" first="1" last="0" only="0" depth="0" >
      <node id="58" text="Child1" url="http://localhost/dnn/Home/Child1.aspx" enabled="1" selected="0" breadcrumb="0" first="1" last="0" only="0" depth="1" >
        <keywords >Child1</keywords>
        <description >Child1</description>
        <node id="59" text="Child1 SubItem1" url="http://localhost/dnn/Home/Child1/Child1SubItem1.aspx" enabled="1" selected="0" breadcrumb="0" first="1" last="0" only="0" depth="2" >
          <keywords >Child1 SubItem1</keywords>
          <description >Child1 SubItem1</description>
        </node>
        <node id="60" text="Child1 SubItem2" url="http://localhost/dnn/Home/Child1/Child1SubItem2.aspx" enabled="1" selected="0" breadcrumb="0" first="0" last="0" only="0" depth="2" >
          <keywords >Child1 SubItem2</keywords>
          <description >Child1 SubItem2</description>
        </node>
        <node id="61" text="Child1 SubItem3" url="http://localhost/dnn/Home/Child1/Child1SubItem3.aspx" enabled="1" selected="0" breadcrumb="0" first="0" last="1" only="0" depth="2" >
          <keywords >Child1 SubItem3</keywords>
          <description >Child1 SubItem3</description>
        </node>
      </node>
      <node id="65" text="Child2" url="http://localhost/dnn/Home/Child2.aspx" enabled="1" selected="0" breadcrumb="0" first="0" last="1" only="0" depth="1" >
        <keywords >Child2</keywords>
        <description >Child2</description>
        <node id="66" text="Child2 SubItem1" url="http://localhost/dnn/Home/Child2/Child2SubItem1.aspx" enabled="1" selected="0" breadcrumb="0" first="1" last="0" only="0" depth="2" >
          <keywords >Child2 SubItem1</keywords>
          <description >Child2 SubItem1</description>
        </node>
        <node id="67" text="Child2 SubItem2" url="http://localhost/dnn/Home/Child2/Child2SubItem2.aspx" enabled="1" selected="0" breadcrumb="0" first="0" last="1" only="0" depth="2" >
          <keywords >Child2 SubItem2</keywords>
          <description >Child2 SubItem2</description>
        </node>
      </node>
    </node>
  </root>
</Root>

My Goal is to render this XML block into this HTML Navigation structure only using UL's LI's, etc..

<ul id="topnav">
  <li>
    <a href="#" class="home">Home</a><!-- Parent Node - Depth0 -->
    <div class="sub">
      <ul>
        <li><h2><a href="#">Child1</a></h2></li><!-- Parent Node 1 - Depth1 -->
        <li><a href="#">Child1 SubItem1</a></li><!-- ChildNode - Depth2 -->
        <li><a href="#">Child1 SubItem2</a></li><!-- ChildNode - Depth2 -->
        <li><a href="#">Child1 SubItem3</a></li><!-- ChildNode - Depth2 -->
      </ul>
      <ul>
        <li><h2><a href="#">Child2</a></h2></li><!-- Parent Node 2 - Depth1 -->
        <li><a href="#">Child2 SubItem1</a></li><!-- ChildNode - Depth2 -->
        <li><a href="#">Child2 SubItem2</a></li><!-- ChildNode - Depth2 -->
      </ul>
    </div>
  </li>
</ul>

Can anyone help with the XSL coding? I'm just starting now with XSL..

A: 

I recommend nesting the <ul> lists properly. "Child1 SubItem1" can't be on the same level as "Child1", logically. This XSLT 1.0 transformation:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:output encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />

  <xsl:template match="Root">
    <html>
      <body>
        <!-- render a sub-list if the <root> node -->
        <xsl:apply-templates select="root" mode="sub-list" />
      </body>
    </html>
  </xsl:template>

  <xsl:template match="*[node]" mode="sub-list">
    <ul>
      <!-- render all list items from enabled child nodes -->
      <xsl:apply-templates select="node[@enabled=1]" mode="list-item" />
    </ul>
  </xsl:template>

  <xsl:template match="node" mode="list-item">
    <li id="menu_{@id}" class="level{@depth}">
      <a href="{@url}">
        <xsl:value-of select="@text" />
      </a>
      <!-- if there are any enabled child nodes... -->
      <xsl:if test="node[@enabled=1]">
        <!-- ...make a sub-list from the current node (.) -->
        <xsl:apply-templates select="." mode="sub-list" />
      </xsl:if>
    </li>
  </xsl:template>

</xsl:stylesheet>

produces:

<html>
  <body>
    <ul>
      <li id="menu_40" class="level0">
        <a href="http://localhost/dnn/Home.aspx"&gt;Home&lt;/a&gt;
        <ul>
          <li id="menu_58" class="level1">
            <a href="http://localhost/dnn/Home/Child1.aspx"&gt;Child1&lt;/a&gt;
            <ul>
              <li id="menu_59" class="level2">
                <a href="http://localhost/dnn/Home/Child1/Child1SubItem1.aspx"&gt;Child1 SubItem1</a>
              </li>
              <li id="menu_60" class="level2">
                <a href="http://localhost/dnn/Home/Child1/Child1SubItem2.aspx"&gt;Child1 SubItem2</a>
              </li>
              <li id="menu_61" class="level2">
                <a href="http://localhost/dnn/Home/Child1/Child1SubItem3.aspx"&gt;Child1 SubItem3</a>
              </li>
            </ul>
          </li>
          <li id="menu_65" class="level1">
            <a href="http://localhost/dnn/Home/Child2.aspx"&gt;Child2&lt;/a&gt;
            <ul>
              <li id="menu_66" class="level2">
                <a href="http://localhost/dnn/Home/Child2/Child2SubItem1.aspx"&gt;Child2 SubItem1</a>
              </li>
              <li id="menu_67" class="level2">
                <a href="http://localhost/dnn/Home/Child2/Child2SubItem2.aspx"&gt;Child2 SubItem2</a>
              </li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </body>
</html>

Now you can do the presentation with CSS, for example via the class attribute every <li> has.


EDIT due to special request in the comments, here is the stylesheet that seems to produce exactly what you want:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:template match="root">
    <ul id="topnav">
      <xsl:apply-templates select="node" mode="li" />
    </ul>
  </xsl:template>

  <xsl:template match="node" mode="li">
    <xsl:if test="@enabled=1">
      <li>
        <xsl:apply-templates select="." mode="a" />
        <!-- build sub-menu for enabled child nodes only -->
        <xsl:if test="node[@enabled=1]">
          <div class="sub">
            <xsl:apply-templates select="node" mode="ul" />
          </div>
        </xsl:if>
      </li>
    </xsl:if>
  </xsl:template>

  <xsl:template match="node" mode="ul">
    <ul>
      <!-- heading for this list -->
      <li>
        <h2><xsl:apply-templates select="." mode="a" /></h2>
      </li>
      <!-- other list elements from sub-nodes -->
      <xsl:apply-templates select="node" mode="li" />
    </ul>
  </xsl:template>

  <xsl:template match="node" mode="a">
    <a href="{@url}"><xsl:value-of select="@text" /></a>
  </xsl:template>
</xsl:stylesheet>

Output for your sample XML:

<ul id="topnav">
  <li>
    <a href="http://localhost/dnn/Home.aspx"&gt;Home&lt;/a&gt;
    <div class="sub">
      <ul>
        <li><h2><a href="http://localhost/dnn/Home/Child1.aspx"&gt;Child1&lt;/a&gt;&lt;/h2&gt;&lt;/li&gt;
        <li><a href="http://localhost/dnn/Home/Child1/Child1SubItem1.aspx"&gt;Child1 SubItem1</a></li>
        <li><a href="http://localhost/dnn/Home/Child1/Child1SubItem2.aspx"&gt;Child1 SubItem2</a></li>
        <li><a href="http://localhost/dnn/Home/Child1/Child1SubItem3.aspx"&gt;Child1 SubItem3</a></li>
      </ul>
      <ul>
        <li><h2><a href="http://localhost/dnn/Home/Child2.aspx"&gt;Child2&lt;/a&gt;&lt;/h2&gt;&lt;/li&gt;
        <li><a href="http://localhost/dnn/Home/Child2/Child2SubItem1.aspx"&gt;Child2 SubItem1</a></li>
        <li><a href="http://localhost/dnn/Home/Child2/Child2SubItem2.aspx"&gt;Child2 SubItem2</a></li>
      </ul>
    </div>
  </li>
</ul>
Tomalak
Thanks a lot for your quick answer, actually I need the Child1 to be on the same level as Child1 Sub items and apply the H2 tag, the reason for this is I want to use this mega dropdown menu: http://www.sohtanaka.com/web-design/examples/mega-dropdowns/I don't have enough knowledge to change the javascript to work with properly nested <ul> lists, any thoughts ?
Rui Santos
I really need help on this, no one ?
Rui Santos
@Rui: See changed answer.
Tomalak
Wonderful! that's exactly what I wanted.Just another question, more like an enhancement, Is it possible to have a 'Case' situation to inspect the Parent Nodes Depth 1 (Child1 and Child2), in case it has more than 5 items, to separate them in DIV's with 'Row" class? You can check an example here: http://www.sohtanaka.com/web-design/examples/mega-dropdowns/ Thank you so much for your help.
Rui Santos
@Rui: Yes, that's possible. So you want `<div class="sub row">` instead of `<div class="sub">` in this case, or what? BTW: I'm glad to help, but you might want to try modifying the XSLT code yourself also, since you will be maintaining it anyway, won't you?
Tomalak