xmlxsltplonediazo

Plone - XSLT/Diazo - Replace Plone portal-personaltools Markup


I am attempting to do replace the markup for the Plone portal-personaltools menu, but I am encountering a few issues. The first issue is I am not sure if my XSL transformation is running. I am placing it within my tag. My rules.xml file is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<rules xmlns="http://namespaces.plone.org/diazo"
xmlns:css="http://namespaces.plone.org/diazo/css"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<rules css:if-content="#visual-portal-wrapper">

<theme href="theme.html" />

<!-- Head elements -->
<replace css:theme="html head title" css:content="html head title" />
<after css:theme-children="html head" css:content="html head base" />
<after css:theme-children="html head" css:content="html head script" />

<!-- Logo -->
<copy attributes="href" css:theme="#brand" css:content="#portal-logo" />

<!-- Tabs -->
<replace css:theme-children="#tabs" css:content-children="#portal-globalnav" />

<!-- Personal tools -->
<replace css:theme="#account-info" css:content="#portal-personaltools" />

<!-- This is not a complete XSL transfomation code. I was trying to follow someone elses code. -->


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

<xsl:template match="/*">
<ul class="list-inline navbar-btn navbar-right" id="account-info">
<xsl:apply-templates select="dt"/>
</ul>
</xsl:template>

<xsl:template match="dt">
<li>
<xsl:apply-templates select="following-sibling::dd[1]"/>
</li>
</xsl:template>
</xsl:stylesheet>

<!-- Search -->
<copy attributes="action" css:theme="#searchbox" css:content="#portal-searchbox form" />
<copy attributes="name" css:theme="#searchbox .search-query" css:content="#portal-searchbox form .searchField" />

<!-- Edit bar -->
<before css:theme-children="#content" css:content="#edit-bar" />

<!-- Main columns -->
<replace css:content-children="#content" css:theme-children="#content" />
<replace css:content-children="#portal-column-one" css:theme-children="#left-column" />
<replace css:content-children="#portal-column-two" css:theme-children="#right-column" />

<!-- Footer -->
<replace css:theme-children="#footer" css:content-children="#portal-footer" />
<after css:theme-children="#footer" css:content="#portal-siteactions" />

</rules>

</rules>

This is correct? My XSL transformation is not complete, as I am not even sure it is running, but the code I am trying to transform is:

<dl id="portal-personaltools" class="actionMenu deactivated">
<dt class="actionMenuHeader">
<a href="#/useractions" id="user-name">admin</a>
</dt>
<dd class="actionMenuContent">
<ul><li id="personaltools-dashboard">
<a href="#/dashboard">Dashboard</a>
</li>
<li id="personaltools-preferences">
<a href="#/@@personal-preferences">Preferences</a>
</li>
<li id="personaltools-plone_setup">
<a href="#/@@overview-controlpanel">Site Setup</a>
</li>
<li id="personaltools-logout">
<a href="#/logout">Log out</a>
</li>
</ul>
</dd>
</dl>

into this:

<ul class="list-inline navbar-btn navbar-right" id="account-info">
<li><a class="btn btn-primary" href="login.html">Sign In</a></li>
<li class="btn btn-primary dropdown">
<a id="drop1" href="#" role="button" class="dropdown-toggle" data-toggle="dropdown">Account<b class="caret"></b></a>
<ul class="dropdown-menu" role="menu" aria-labelledby="drop1">
<li role="presentation"><a role="menuitem" tabindex="-1" href="dashboard.html">Dashboard</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="preferences.html">Preferences</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="site.html">Site Setup</a></li>
<li class="divider" role="presentation"></li>
<li role="presentation"><a role="menuitem" tabindex="-1" href="index.html">Log Off</a></li>
</ul>
</li>
</ul>

Update:

I have a secondary question regarding how Plone displays the sign in button versus the user actions menu. How do I modify the rules to have a signin button appear when the user is not logged in, and while the user is logged in only show the user action menu? My modified rules.xml is at the bottom of this post, after my addition notes/information.

Screenshot based on Dan's modifications

Screenshot showing a button that has the user name in a separate button, and an account button with the user action menus

After making a few slight changes to the solution provided by Dan, I have the user action menu displaying the user name with a dropdown showing the user action menu while the user is logged in. While the user is logged off, all I have now is an empty Twitter Bootstrap button with no sign in button.

Logged Out Screenshot

Logged Out Screenshot of an empty Twitter Bootstrap button

Logged In Screenshot

Logged In Screenshot with Dropdown active

I would like my HTML markup to look similar to this when the user is not logged:

<ul id="account-info" class="list-inline navbar-btn navbar-right">
    <li>
      <a href="login.html" class="btn btn-primary">Sign In</a>
    </li>
</ul>

But my HTML markup currently appears as whe the user is not logged in:

<ul id="account-info" class="list-inline navbar-btn navbar-right">
    <li></li>
    <li class="btn btn-primary dropdown">
        <ul aria-labelledby="drop1" role="menu" class="dropdown-menu"></ul>
    </li>
</ul>

Finally my modified rules.xml

<replace css:content="#portal-personaltools">
    <ul class="list-inline navbar-btn navbar-right" id="account-info">
        <li>
            <!--Sign out Form-->
            <!--<li><a href="#" class="btn btn-primary">Sign In</a></li>-->
        </li>
        <li class="btn btn-primary dropdown">
            <xsl:apply-templates select="./dt/a" />
            <!--<a id="drop1" href="#" role="button" class="dropdown-toggle" data-toggle="dropdown">Account<b class="caret"></b></a>-->
            <ul class="dropdown-menu" role="menu" aria-labelledby="drop1">
                <xsl:apply-templates select="./dd/ul/li" />
            </ul>
        </li>
    </ul>
</replace>

<replace css:content="#portal-personaltools dt a">
    <a>
        <!--<xsl:attribute name="class">btn btn-primary</xsl:attribute>-->
        <xsl:attribute name="role">button</xsl:attribute>
        <xsl:attribute name="class">dropdown-toggle</xsl:attribute>
        <xsl:attribute name="data-toggle">dropdown</xsl:attribute>
        <xsl:copy-of select="@*|node()" />
        <b class="caret"></b>
    </a>
</replace>

<replace css:content="#portal-personaltools dd li">
    <li role="presentation">
        <xsl:apply-templates select="./a" />
    </li>
</replace>

<replace css:content="#portal-personaltools dd li a">
    <a role="menuitem" tabindex="-1">
        <xsl:attribute name="href">
           <xsl:value-of select="@href" />
        </xsl:attribute>
        <xsl:value-of select="node()" />
    </a>
</replace>

Thanks in advance!


Solution

  • Here's something that gets you most of the way there. Replace from your This is not a complete XSL transformation code comment to the last </xsl:stylesheet>.

    <replace css:content="#portal-personaltools">
      <ul class="list-inline navbar-btn navbar-right" id="account-info">
        <li>
          <xsl:apply-templates select="./dt/a" />
        </li>
        <li class="btn btn-primary dropdown">
          <a id="drop1" href="#" role="button" class="dropdown-toggle" data-toggle="dropdown">Account<b class="caret"></b></a>
          <ul class="dropdown-menu" role="menu" aria-labelledby="drop1">
            <xsl:apply-templates select="./dd/ul/li" />
          </ul>
        </li>
      </ul>
    </replace>
    
    <replace css:content="#portal-personaltools dt a">
      <a>
        <xsl:attribute name="class">btn btn-primary</xsl:attribute>
        <xsl:copy-of select="@*|node()" />
      </a>
    </replace>
    
    <replace css:content="#portal-personaltools dd li">
      <li role="presentation">
        <xsl:apply-templates select="./a" />
      </li>
    </replace>
    
    <replace css:content="#portal-personaltools dd li a">
      <a role="menuitem" tabindex="-1">
        <xsl:attribute name="href">
          <xsl:value-of select="@href" />
        </xsl:attribute>
        <xsl:value-of select="node()" />
        </a>
    </replace>
    

    This exercises Diazo's ability to modify content on the fly.

    Update (corresponding to OP's update):

    My rule of thumb is to limit Diazo HTML rewriting only to very predictable, uniform HTML. Otherwise you end up with overly complex XSLT that's difficult to maintain and is prone to breakage (e.g. what happens when a minor Plone upgrade alters the personal tools markup?) I would switch to a different Diazo/plone.app.theming capability at this point: the ability to fetch content from a supporting view by specifying an href attribute. That is, replace all of your personal tools rules with a one-liner:

    <replace css:theme="#account-info" css:content="#account-info" href="/@@account-menu" />
    

    ... and write a browser view account-menu in a Python package, where you have Python and Zope Page Templates at your disposal.

    (Note: it's not appropriate here, but if your browser view was very simple you could use plone.app.themingplugins' shortcut for registering a view without a Python package.)

    Or, you could keep your original rule

    <replace css:theme="#account-info" css:content="#portal-personaltools" />
    

    ... and override the plone.personal_bar viewlet (which is registered in the plone.app.layout package). One disadvantage is that the markup would also be overridden in the unthemed site (i.e. the default 'Sunburst' theme you see at http://127.0.0.1/Plone).

    I would use the Diazo rule with href, and write my own view based on the plone.personal_bar viewlet.