First, to support localization, the navbar structure had to be separate from the localized content, a rule that the apps-list.xml implementation does not follow.
Second, I wanted to support an arbitrary menu depth so we can have a top level and sub levels and sub-sub levels etc.
Third, the presentation had to be independent of the structure so if someone prefers a horizontal menu with drop-downs or a navigation tree, they could implement the UI and still use the defined menu structure and localization.
To start, I defined a menu structure in a file I called "nav.xml". Here is an excerpt from such a file:
<menu>In this file, we specify an optional title for the navbar and each displayable element has an @ref attribute containing an XPath expression referring to localized content in an XML resource file. The "label" elements are typically displayed to the user while the "hint" elements are typically used for mouseover events . Section elements can have child sections.
<title ref="navbar/title"/>
<section id="home" href="/welcome/">
<label ref="navbar/home/label" />
<hint ref="navbar/home/hint"/>
<item href="/documentation/">
<label ref="navbar/Documentation" />
<hint/>
</item>
</section>
<section id="manage">
<label ref="navbar/manage/label" />
<item id="manage.wo" href="/wfdemo/manage/wo-manager/">
<label ref="navbar/manage/WorkOrders/label" />
<hint ref="navbar/manage/WorkOrders/hint"/>
</item>
<item id="manage.assign" href="/wfdemo/manage/assign/">
<label ref="navbar/manage/Assignments/label" />
<hint ref="navbar/manage/Assignments/hint" />
</item>
</section>
</menu>
The navbar is rendered by the following entry in the page-flow.xml file at the top-level of the Orbeon navigation tree:
And for the curious, the nav.xpl is as follows:
<!-- navbar resources -->
<page id="navbar" path-info="/navbar" view="/nav.xpl"/>
<p:param type="output" name="data" />As one can see, the first processor gets the localized resource file from i18n/resources.xpl and this content serves as input to the XLST transformation generating the HTML code for the navbar.
<p:processor name="oxf:pipeline">
<p:input name="config" href="/i18n/resources.xpl" />
<p:output name="data" id="resources" />
</p:processor>
<p:processor name="oxf:unsafe-xslt">
<p:input name="data" href="oxf:nav.xml" />
<p:input name="resources" href="#resources" />
<p:input name="config" href="nav.xsl" />
<p:output name="data" ref="data" />
</p:processor>
The nav.xsl file is a bit long so I prefer not to paste a complete version here but if someone is really interested, I can make it available somehow. Nevertheless, the XSL does use a few tricks to get things done so I included part of the file here. The remaining code is just more of the same.
<xsl:import href="/oxf/xslt/utils/evaluate.xsl" />Essentially, we make use of the /oxf/xslt/utils/evaluate.xsl file that provides us with a fn:evaluate function (you do need to declare the following namespace: xmlns:fn="http://www.orbeon.com/xslt-function"). This function is used to evaluate the XPath expressions retrieved from the nav.xml file. In essence, we bind the $resources variable to the root node of the localized resources file and then use the following idiom each time we need a localized string:
<xsl:variable name="resources" select="doc('input:resources')/resources"
as="node()" />
<xsl:template match="/">
<div>
<xsl:if test="menu/title">
<h1>
<xsl:value-of
select="if (menu/title/@ref) then fn:evaluate($resources, menu/title/@ref, ()) else menu/title" />
</h1>
</xsl:if>
<ul class="tree-sections">
<xsl:apply-templates select="menu/section" />
</ul>
</div>
</xsl:template>
fn:evaluate($resources, [XPATH_EXPR]/@ref, ())Where [XPATH_EXPR] is the (relative) path to the navbar element and @ref is the attribute containing the XPath expression for the String resource in the $resources document.
Finally, we need to modify the epilogue pipeline processor (epilogue-servlet.xpl) to force it to use our navbar processor. I added the following processor in the
<p:when test="/xhtml:html">
section of the epilogue-servlet.xpl:<p:processor name="oxf:pipeline">And the following input to the
<p:input name="config" href="/nav.xpl" />
<p:output name="data" id="navbar" />
</p:processor>
<p:when test="not(p:property('oxf.epilogue.use-theme') = false())">
section:<p:input name="navbar" href="#navbar"/>This scheme finally works out really well. It could certainly be enhanced and made even more general but for the time being it serves me well and follows the KISS principle although it does make use of a few tricks.
Have a nice day
Great solution. I've to redesign my orbeon application this way. Do you have any idea, how to add language change combobox (select1, xforms) in the application header div?
ReplyDeleteSorry for a late response.
ReplyDeleteI did implement a quick and not totally satisfactory solution to change the language associated with the session. It works however. I guess I would have to post a somewhat detailed description of it but here is a (very) short rundown:
You can use the following pipeline:
[p:processor name="oxf:scope-serializer"]
[p:input name="config" href="scope-key.xml"/]
[p:input name="data" ref="#instance" /]
[/p:processor]
[p:processor name="oxf:scope-generator"]
[p:input name="config" href="scope-key.xml" /]
[p:output name="data" ref="data" /]
[/p:processor]
In my case, the scope-key.xml looks like this:
[config]
[key]workforse/locale[/key]
[scope]session[/scope]
[session-scope]application[/session-scope]
[/config]
Hope this helps. Maybe more later.