使用xslt为Umbraco创建菜单

使用xslt为Umbraco创建菜单,xslt,umbraco,Xslt,Umbraco,我已经使用XSLT在umbraco中创建了一个菜单。菜单使用常用的ul和li元素,我只显示菜单的第一级。其目的是创建一个菜单,当我单击父节点(在顶层)时,该菜单将展开以显示子菜单 我正在寻找xslt,单击时需要公开子菜单 我想我需要使用祖先或self来检测当前菜单和父菜单,并显示它们以及$currentPage变量 我有以下xslt: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xsl:stylesheet [ <!E

我已经使用XSLT在umbraco中创建了一个菜单。菜单使用常用的ul和li元素,我只显示菜单的第一级。其目的是创建一个菜单,当我单击父节点(在顶层)时,该菜单将展开以显示子菜单

我正在寻找xslt,单击时需要公开子菜单

我想我需要使用祖先或self来检测当前菜单和父菜单,并显示它们以及$currentPage变量

我有以下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" 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" xmlns:tagsLib="urn:tagsLib" xmlns:urlLib="urn:urlLib" 
    exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib urlLib ">

<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>

<xsl:template match="/">
    <div id="kb-categories"> 
        <h3>Categories</h3>
        <xsl:call-template name="drawNodes">  
            <xsl:with-param name="parent" select="$currentPage/ancestor-or-self::node [@level=1]"/>  
        </xsl:call-template>
    </div>
</xsl:template>

<xsl:template name="drawNodes">
    <xsl:param name="parent"/> 
    <xsl:if test="(umbraco.library:IsProtected($parent/@id, $parent/@path) = 0 or (umbraco.library:IsProtected($parent/@id, $parent/@path) = 1)) and $parent/@level = 1">           
        <ul class="kb-menuLevel1" >     
        <xsl:for-each select="$parent/node [string(./data [@alias='showInMenu']) = 1]"> 
            <li>  
                <a href="/kb{umbraco.library:NiceUrl(@id)}">
                    <xsl:value-of select="@nodeName"/>
                </a>                
                <xsl:variable name="level" select="@level" />
                <xsl:if test="(count(./node [string(./data [@alias='showInMenu']) = '1']) &gt; 0)">   
                    <xsl:call-template name="drawNodes">    
                        <xsl:with-param name="parent" select="."/>    
                    </xsl:call-template>  
                </xsl:if> 
            </li> 
        </xsl:for-each>
        </ul>
    </xsl:if>
    <xsl:if test="(umbraco.library:IsProtected($parent/@id, $parent/@path) = 0 or (umbraco.library:IsProtected($parent/@id, $parent/@path) = 1)) and $parent/@level &gt; 1">    
        <ul class="kb-menuLevel{@level}" style="display: none;">            
        <xsl:for-each select="$parent/node [string(./data [@alias='showInMenu']) = 1]"> 
            <li>  
                <a href="/kb{umbraco.library:NiceUrl(@id)}">
                    <xsl:value-of select="@nodeName"/>
                </a>                
                <xsl:variable name="level" select="@level" />
                <xsl:if test="(count(./node [string(./data [@alias='showInMenu']) = '1']) &gt; 0)">   
                    <xsl:call-template name="drawNodes">    
                        <xsl:with-param name="parent" select="."/>    
                    </xsl:call-template>  
                </xsl:if> 
            </li>
        </xsl:for-each>
        </ul>
    </xsl:if>

</xsl:template>

</xsl:stylesheet>

类别
我怀疑使用apply模板可以改进这一点,但我还没有达到这个速度(这只是我学习xslt的第二天)

我的菜单:

  • 菜单项1
  • 菜单项2
  • 菜单项3
  • 菜单项4
单击菜单项2时,我将进入菜单项2的页面,并且子菜单也将显示:

  • 菜单项1
  • 菜单项2
    --菜单项2.1
    --菜单项2.2
  • 菜单项3
  • 菜单项4
依此类推嵌套菜单

下面是上述内容的一些示例xml

<root>  
    <node id="1" nodeTypeAlias="kbHomepage" nodeName="Home" level="1">
        <data alias="introduction">
            <![CDATA[<p>Welcome</p>]]>
        </data>
        <node id="2" nodeTypeAlias="guide" nodeName="Menu Item 1" level="2">
            <data alias="bodyText">
                <![CDATA[<p>This is some text</p>]]>
            </data>
            <data alias="showInMenu">1</data>
            <data alias="menuName">Menu Item 1</data>
        </node>     
        <node id="3" nodeTypeAlias="guide" nodeName="Menu Item 2" level="2">
            <data alias="bodyText">
                <![CDATA[<p>This is some text</p>]]>
            </data>
            <data alias="showInMenu">1</data>
            <data alias="menuName">Menu Item 2</data>
            <node id="4" nodeTypeAlias="guide" nodeName="Menu Item 2.1" level="3">
                <data alias="bodyText">
                    <![CDATA[<p>Some Text</p>]]>
                </data>
                <data alias="showInMenu">1</data>
                <data alias="menuName">Menu Item 2.1</data>
            </node>
            <node id="5" nodeTypeAlias="guide" nodeName="Menu Item 2.2" level="3">
                <data alias="bodyText">
                    <![CDATA[<p>Some Text</p>]]>
                </data>
                <data alias="showInMenu">1</data>
                <data alias="menuName">Menu Item 2.2</data>
                <node id="6" nodeTypeAlias="guide" nodeName="Item 2.2.1 Guide" level="4">
                    <data alias="bodyText">
                        <![CDATA[<p>Some Text</p>]]>
                    </data>
                    <data alias="showInMenu">0</data>
                    <data alias="menuName"></data>
                </node>
            </node>
        </node>     
        <node id="8" nodeTypeAlias="guide" nodeName="Menu Item 3" level="2">
            <data alias="bodyText">
                <![CDATA[<p>This is some text</p>]]>
            </data>
            <data alias="showInMenu">1</data>
            <data alias="menuName">Menu Item 3</data>
        </node>     
        <node id="9" nodeTypeAlias="guide" nodeName="Menu Item 4" level="2">
            <data alias="bodyText">
                <![CDATA[<p>This is some text</p>]]>
            </data>
            <data alias="showInMenu">1</data>
            <data alias="menuName">Menu Item 4</data>
        </node>     
    </node>
    <node id="7" nodeTypeAlias="someAlias" nodeName="Some Other Page" level="1">
        <data alias="bodyText">
            <![CDATA[<p>This is some text</p>]]>
        </data>         
    </node>     
</root>

欢迎光临

]> 这是一些文字

]> 1. 菜单项1 这是一些文字

]> 1. 菜单项2 一些文字

]> 1. 菜单项2.1 一些文字

]> 1. 菜单项2.2 一些文字

]> 0 这是一些文字

]> 1. 菜单项3 这是一些文字

]> 1. 菜单项4 这是一些文字

]>
编辑:以下内容几乎满足了我的需要:

<xsl:variable name="visibleChidren" select="node[data[@alias='showInMenu'] = 1 and (@level = 2 or descendant-or-self::*[generate-id($currentPage) = generate-id(.)] or preceding-sibling::*[generate-id($currentPage) = generate-id(.)] or following-sibling::*[generate-id($currentPage) = generate-id(.)])]" />

我只需要包含当前页面中的直接子项。

我尝试(以我对Umbraco非常有限的知识)稍微清理一下您的代码并删除冗余。它看起来似乎可以与您提供的XML示例一起使用,但我无法真正针对Umbraco进行测试

<!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" 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" xmlns:tagsLib="urn:tagsLib" xmlns:urlLib="urn:urlLib"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib urlLib ">

  <xsl:output method="xml" omit-xml-declaration="yes" encoding="utf-8" />

  <xsl:param name="currentPage" />

  <xsl:template match="/">
    <div id="kb-categories">
      <h3>Categories</h3>
      <xsl:apply-templates mode="list" select="/root/node[@nodeTypeAlias='kbHomepage']" />
    </div>
  </xsl:template>

  <!-- matches anything with <node> children and creates an <ul> -->
  <xsl:template match="*[node]" mode="list">
    <!-- prepare a list of all visible children -->
    <xsl:variable name="visibleChidren" select="node[
      data[@alias='showInMenu'] = 1
      and (
        not(umbraco.library:IsProtected(@id, @path)) 
        or umbraco.library:IsLoggedOn() 
      )
    ]" />
    <!-- prepare a CSS class for the "selected path" -->
    <xsl:variable name="display">
      <xsl:if test=".//node[generate-id() = generate-id($currentPage)]">
        <xsl:text>visible</xsl:text>
      </xsl:if>
    </xsl:variable>
    <xsl:if test="$visibleChidren">
      <ul class="menu kb-menuLevel{$visibleChidren[1]/@level} {$display}">
        <xsl:apply-templates mode="item" select="$visibleChidren" />
      </ul>
    </xsl:if>
  </xsl:template>

  <!-- matches <node> elements and turns them into list items -->
  <xsl:template match="node" mode="item">
    <li>
      <xsl:if test="generate-id() = generate-id($currentPage)">
        <xsl:attribute name="class">selected</xsl:attribute>
      </xsl:if>
      <a href="/kb{{umbraco.library:NiceUrl(@id)}}">
        <xsl:value-of select="@nodeName" />
      </a>
      <!-- if there are any child nodes, render them -->
      <xsl:if test="node">
        <xsl:apply-templates mode="list" select="." />
      </xsl:if>
    </li>
  </xsl:template>

</xsl:stylesheet>

这对你有帮助吗

我知道我需要做我想做的事。关键是:

<xsl:variable name="visibleChidren" select="node[data[@alias='showInMenu'] = 1 and (@level = 2 or descendant-or-self::*[generate-id($currentPage) = generate-id(.)] or preceding-sibling::*[generate-id($currentPage) = generate-id(.)] or following-sibling::*[generate-id($currentPage) = generate-id(.)] or parent::*[generate-id($currentPage) = generate-id(.)])]" />

从整个xslt中:

<!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" 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" xmlns:tagsLib="urn:tagsLib" xmlns:urlLib="urn:urlLib"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib urlLib ">

  <xsl:output method="xml" omit-xml-declaration="yes"/>

  <xsl:param name="currentPage"/>
  <xsl:variable name="currentLevel" select="$currentPage/@level" />

  <xsl:template match="/">
    <div id="kb-categories">
      <h3>Categories</h3>
      <xsl:apply-templates mode="list" select="$currentPage/ancestor-or-self::node [@nodeTypeAlias = 'kbHomepage']" />
    </div>
  </xsl:template>

  <!-- matches anything with <node> children and makes a list out of them -->
  <xsl:template match="node" mode="list">
    <!-- select only sub-nodes that have 'showInMenu' = 1 -->
    <xsl:variable name="visibleChidren" select="node[data[@alias='showInMenu'] = 1 and (@level = 2 or descendant-or-self::*[generate-id($currentPage) = generate-id(.)] or preceding-sibling::*[generate-id($currentPage) = generate-id(.)] or following-sibling::*[generate-id($currentPage) = generate-id(.)] or parent::*[generate-id($currentPage) = generate-id(.)])]" />
    <xsl:if test="$visibleChidren">
      <ul>
        <xsl:apply-templates mode="item" select="$visibleChidren" />
      </ul>
    </xsl:if>
  </xsl:template>

  <xsl:template match="node" mode="item">
      <li>
        <a href="/kb{umbraco.library:NiceUrl(@id)}">
          <xsl:value-of select="@nodeName"/>
        </a>
        <xsl:apply-templates mode="list" select="." />
      </li>
  </xsl:template>

</xsl:stylesheet>

类别

  • 或者您可以解决XSLT中的大量黑客问题,并使用我们的.umbraco.org中的以下导航包

    我认为这可以满足您的所有需要,并且不需要在XSLT的黑暗世界中弄脏您的双手


    @rob\u g:在我们实现节点可见性之前,请确认我的代码到目前为止是否生成了正确的HTML输出。Tomalak,我已经添加了xml。除非单击菜单项,否则菜单应仅显示级别2节点。假设单击了菜单项2,则菜单项2下的所有级别2节点和所有级别3节点都应可见。单击菜单项2将打开其页面(由生成的元素决定。打开此页面后,$currentPage将引用它。我希望当前页面足以满足我的需要:显示$currentPage的所有直接子项以及级别2之前的所有祖先。谢谢。@rob_g:现在我有了您的XML…请回答三个问题:1)
    实际上不是菜单的一部分?它做什么?2)如何从XML中判断单击了哪个节点?3)
    $currentPage
    到底包含什么?对不起,我不知道。@Tomalak 1)umbraco页面由节点表示。主页具有由kbHomepage标识的特定布局。我不想显示主页,但它是开始子树的好地方。从技术上讲,可能有其他页面与kbHomepage处于同一级别,但我配置CMS的方式阻止了这一点。2) $currentPage将为您提供当前页面(即已单击的节点)。见#3。3) $currentPage是一个Umbraco对象,它存储表示正在查看的当前页面的节点。不确定magic umbraco如何填充该值。@rob_g:好的,我开始了,现在看清楚了,谢谢。因此,当我在浏览器中单击
    菜单项2
    时,页面将重新加载,
    $currentPage
    将正好是这个节点:
    ?谢谢Tim,但在你发布此评论之前,我已经把它全部工作好了。否则我会用它的!
    <xsl:variable name="visibleChidren" select="node[data[@alias='showInMenu'] = 1 and (@level = 2 or descendant-or-self::*[generate-id($currentPage) = generate-id(.)] or preceding-sibling::*[generate-id($currentPage) = generate-id(.)] or following-sibling::*[generate-id($currentPage) = generate-id(.)] or parent::*[generate-id($currentPage) = generate-id(.)])]" />
    
    <!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" 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" xmlns:tagsLib="urn:tagsLib" xmlns:urlLib="urn:urlLib"
      exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets tagsLib urlLib ">
    
      <xsl:output method="xml" omit-xml-declaration="yes"/>
    
      <xsl:param name="currentPage"/>
      <xsl:variable name="currentLevel" select="$currentPage/@level" />
    
      <xsl:template match="/">
        <div id="kb-categories">
          <h3>Categories</h3>
          <xsl:apply-templates mode="list" select="$currentPage/ancestor-or-self::node [@nodeTypeAlias = 'kbHomepage']" />
        </div>
      </xsl:template>
    
      <!-- matches anything with <node> children and makes a list out of them -->
      <xsl:template match="node" mode="list">
        <!-- select only sub-nodes that have 'showInMenu' = 1 -->
        <xsl:variable name="visibleChidren" select="node[data[@alias='showInMenu'] = 1 and (@level = 2 or descendant-or-self::*[generate-id($currentPage) = generate-id(.)] or preceding-sibling::*[generate-id($currentPage) = generate-id(.)] or following-sibling::*[generate-id($currentPage) = generate-id(.)] or parent::*[generate-id($currentPage) = generate-id(.)])]" />
        <xsl:if test="$visibleChidren">
          <ul>
            <xsl:apply-templates mode="item" select="$visibleChidren" />
          </ul>
        </xsl:if>
      </xsl:template>
    
      <xsl:template match="node" mode="item">
          <li>
            <a href="/kb{umbraco.library:NiceUrl(@id)}">
              <xsl:value-of select="@nodeName"/>
            </a>
            <xsl:apply-templates mode="list" select="." />
          </li>
      </xsl:template>
    
    </xsl:stylesheet>