This time around we’ll be doing a contextual menu in a sidebar. Liferay will let you set related links as part of web content, but what we want is an extension of that: related links per the overall page, not the content. As these change by the page, but may not necessarily be purely architectural (ie a subset of the overall site map), we’re going to head back into Custom Fields and set up some more page tags.
To review from the last post, here’s what my Custom Fields page looks like:
The drop-down values are select, Systems, Services, Products, Support, and News. The “select” is in there for a reason: there’s no blank option in the default drop-down. To see what I mean, create a drop-down without “select” (or some other default-sounding word or phrase). Then open a page’s Custom Fields, and you’ll find the first item in your drop-down is now automatically selected. If having a preset default value isn’t your intention, you’ll need to add “select” or some other “blank” option.
Two things to keep in mind about the way I did my sidebar. The first is that we’re doing a sidebar that isn’t also a portlet, so things are a little more wonky. Doing this in the portal_normal.vm means I need to allow for when there’s a sidebar versus if there’s not, and I did that by setting up a template that’s three columns in a percentage grid. If there’s nothing in the first column, it squishes flat and the page appears to be two columns at 65/35. If there’s a left-hand column, then I don’t add a right-hand column and the page is now 35/65. There are other ways to do this, but this one worked reasonably well for my original demo.
The second detail about the sidebar is that this is all set up inside portal_normal.vm, not an include. The previous post could have been done in an include (ie footer.vm) or inside portal_normal.vm. Although it’s cleaner to do includes, velocity doesn’t carry over arrays from/to an include. You’ll have to redo the entire array we created in the last post ($navBiglist) unless both the footer section and the sidebar section are in the same velocity file.
For now, we’ll pretend that the footer and the sidebar are both in portal_normal.vm, and that way we can set up the $navBiglist array at the top and reuse it as we need through the file. I’ll leave the css to you this time, and get right into the velocity template.
Here’s the logic of what we’re doing: any given page can have a sidebar tag, or no sidebar tag. If it has a sidebar tag, then its sidebar menu will reflect this tag, and the sidebar menu will show all other pages with the same sidebar tag. That means our first step is to determine the current page’s sidebar tag.
#set ( $tagItems = $page.getExpandoBridge().getAttribute("sidebar-head") ) #set ( $myVar = "" ) #foreach ( $tagItem in $tagItems ) #set ($myVar = $tagItem ) #end
Alright, now we know that $myVar is the page’s tag for “sidebar-head”. With that figured out, next we need to slide that into the header, get the sidebar column started.
#if ( $myVar != "" && $myVar != "select" ) <div class="column_sidebar portlet-column portlet-column-first" id="column-menu"> <ul id="primary-link"> <li id="menu-overview"> $myVar </li>
(Reminder: “select” is acting as an alternate form of null.)
From there, we’ve started the left-hand column using basic YUI layout tags. We’ve added $myVar as the header for the contextual menu. You can make sure of capitalization in the value for the custom field, or just css that to your satisfaction. Next we’ll loop through $navBiglist to see what other pages have a sidebar-head tag equal to $myVar.
#foreach ( $nav in $navBiglist ) #set ( $pagetag = $nav.getLayout().getExpandoBridge().getAttribute("sidebar-head")) #if ( $pagetag.contains($myVar) ) <li> <span> <a href="$nav.getURL()" $nav.getTarget()>$nav.getName()</a> </span> </li> #end #end </ul>
Output each (the spans are added for easier css manipulation), then close out the list, and that’s it. You now have a sidebar that’ll show up based on the custom fields. If you want a second contextual menu like I did, here’s another loop:
<h3>Related Links</h3> <ul id="second-link"> #foreach ( $nav in $navBiglist ) #set ( $pagetag = $nav.getLayout().getExpandoBridge().getAttribute("Related")) #if ( $pagetag.contains($myVar) ) <li> <span> <a href="$nav.getURL()" $nav.getTarget()>$nav.getName()</a> </span> </li> #end #end
I did this one by creating another custom field, called “Related”, and giving it the same drop-down values as the “Sidebar-head” list. If I want a page to show up in the Related Links menu, I just select it from the relevant drop-down. (Yes, this does mean I could put a link to the same page in both contextual menus.)
Whether you want a second menu or are fine with the first, you’ll still need to close out the div and the #if that started it all:
Here’s what my demo looks like when done.
As you’ve probably guessed, this makes it easy to go totally crazy with custom fields in templates. Anywhere you’ve got a velocity template, you can just use ExpandoBridge to call up the page’s associated tags, or to create a new array of all the pages (or just specific levels) and add links to other pages in the same tag-group.
The one area that remains elusive is that of the master list of a custom field’s values. Frex, the drop-down list in the mega footer menu is hard-coded. If there were a way to get an array of all possible custom field values, then all of it could be done on the fly: the admin would need only change the possible drop-down values and this would also change the headers at the same time. If you happen to come across the answer in your own adventures, please share the answer with the community.
Hopefully if you’ve ever had to beat your head against dynamic menus (in any location), these navigation posts have been helpful, and I wish you the best of luck in your own navigation adventures!