Request More Information

We are hiring!

Xtivia, Inc.
2013 North America Liferay Partner of the Year

Join our top-notch team of Enterprise Java and Liferay Portal professionals in our Austin, TX office or work remotely! Please check out our current Dice listings.

Recent Bloggers

Chetan Chadalavada
Posts: 3
Stars: 0
Date: 12/17/14
Chris Provolt
Posts: 2
Stars: 0
Date: 12/16/14
Kat Meadows
Posts: 7
Stars: 0
Date: 12/16/14
Godwin Anasigre
Posts: 1
Stars: 0
Date: 12/12/14
Sonu Verghese
Posts: 9
Stars: 0
Date: 12/12/14
Nick Ortman
Posts: 2
Stars: 0
Date: 12/12/14
Chris Shaw
Posts: 9
Stars: 0
Date: 12/9/14
Kaushik Nagaraj
Posts: 9
Stars: 0
Date: 12/9/14
Luke A Smith
Posts: 22
Stars: 0
Date: 12/8/14
Richard Ngo
Posts: 1
Stars: 0
Date: 12/3/14
« Back

Tutorial: Advanced Navigation in Liferay Themes - Part 2

This is part 2 of a series written for UI-dev/FED new to Liferay. This assumes you’re solid with css/html/javascript, and have at least a passing understanding of dynamic languages like PHP or ASP. If you have a Java background, you may want to skip the parts that explain stuff you already know. This post builds on the last post, so if you haven't read that one, you might want to go back and do that first.

This time we'll build a non-architectural footer. The mega-footer's been around for several years now and users have gotten used to it. But in a majorly complex site, you might find that the footer doesn't actually mimic the entire architecture; it's not a site map that's just couched in the footer. Instead, it's a menu that calls out specific pages based on some organization other than the site map. Frex, the architecture's headers are Solutions, Services, Products, and Support -- but the footer's headers will be Learn More, Latest News, More Information, etc. These don't really "map" to the architecture, which is why I call it a non-architectural menu.

You can see more examples over at the Footers Design Gallery. My version looks like this:

The first step is to figure out our structure, starting with the headers. You can see the five columns I chose in the screenshot. Then we do a bit of css to get the columns.

    footer {
        margin: -23px 0 0 0;
        background: #EEE;
        display: block;
        overflow: visible;
        height: 208px;
        clear: both;
        border-top: 1px solid #AAA;
    }
    .footer_inner {
        margin: 0 auto;
        width: 100%;
        text-align: center;
        }
    
    footer a { color: #555; text-decoration: none; }
    footer a:hover { color: #222; text-decoration: none; }
    
    footer ul li {
        list-style: square;
        color: #555;
        margin: 0 0 .25em 0;
    }
        footer ul li:hover { color: #222; }
        footer ul li span:hover { color: #222; }
    
    footer h4 {
        margin: 6px 0 3px -12px;
        color: #444;
        font-weight: 600;
        }

Next up is the custom field, which I'm calling "pagetag". On the Custom Fields page, add a page-level field called "pagetag". Here's what my page looked like when I was done (ignore the other custom field for now, that's next post).

You'll notice "pagetag" has no values. It's an input field, not a drop-down. You can do either, really. An input field has the drawback that if the admin can't spell, links won't show up since the tag-text must match exactly what you're looking for. If you do it as a drop-down, you can bypass the need for proper spelling (can capitalization) and offer the admins a limited selection. We'll be covering the drop-down version in the next post.

The other thing an input field can do (along with checkboxes) is to easily allow multiple tags. If you look back up at the example screenshot, you'll see "Solutions Group B" is listed in two places. If you've got pages that are important enough to get repeated, or that users might look in several places for, you can add those pages wherever, as many times as you like. Just add the tag in a comma-delineated list and off you go.

Now that you've got your input field, head to your individual pages. Go to the Custom Fields option in the inset (right-hand) menu. There's now an input field called "pagetag" -- enter the keyword you want to use for your various headers. In my case, I'm using learn-more, latest-news, more-info, solutions, and other. Give each tag to a handful of pages, just so there's enough to show up in the footer.

Next we start building the loop to pull out these tagged pages. Like I mentioned in the last post, velocity doesn't have a way to just hand you all the pages on a plate. That means we've got to set up an array, because the trick behind this menu is that we're completely setting aside any child/parent heirarchy. We need everything in one big bundle. We get that by looping through all the levels and adding each menu-item into one array.

    #set( $navBiglist = [] )
    #foreach ( $nav_item in $nav_items )
        #set ( $navItem = $nav_item )
        #set ( $addthis = $navBiglist.add($navItem) )
        #if ( $nav_item.hasChildren() )
        #foreach ( $nav_child in $nav_item.getChildren() )
            #set ( $navItem = $nav_child )
            #set ( $addthis = $navBiglist.add($navItem) )
            #if ( $nav_child.hasChildren() )
            #foreach ( $nav_child in $nav_child.getChildren() )
                #set ( $navItem = $nav_child )
                #set ( $addthis = $navBiglist.add($navItem) )
                #if ( $nav_child.hasChildren() )
                #foreach ( $nav_child in $nav_child.getChildren() )
                    #set ( $navItem = $nav_child )
                    #set ( $addthis = $navBiglist.add($navItem) )
                #end    
                #end    
            #end
            #end
        #end
        #end
    #end

This array ($navBiglist) contains every page in the site. Each item in the array is an object, and that means we can fetch any property we like -- including the ExpandoBridge properties. After all that prep, the actual substance of the footer menu is pretty simple:

    <div class="column5">
    <h4>Learn More</h4>
    <ul>
        #foreach ( $nav in $navBiglist )
        #set ( $pagetag = $nav.getLayout().getExpandoBridge().getAttribute("pagetag"))
        #if ( $pagetag.contains("learn-more") )
            <li>
                <span>
                    <a href="$nav.getURL()" $nav.getTarget()>$nav.getName()</a>
                </span>
            </li>
        #end
        #end        
    </ul>
    </div
>

When you cut & paste to create the second column, just replace the Learn More in the h4 and the learn-more in the first #if. Repeat for all five columns, and there you go. Your footer menu is done, and you can add/remove pages on the fly, just by changing the value of the custom field for a page.

(No, really, it is that simple!)

Obviously it's a little more work if you ever want to change the title-headers to be something completely different. Since this is input-field based, it's not quite as easy to change compared to when it's based on a preset drop-down list. We'll get to that in the final post on advanced navigation.

Comments
Trackback URL:

Mike Hartman
I've been using this custom page property technique in my theme for a while, and it works fine locally. But I've spent the last several days trying to deploy it all to a production environment and it just refuses to work there.

#set ( $pagetag = $nav.getLayout().getExpandoBridge().getAttribute("pagetag"))

results in $pagetag having the value of the "pagetag" property in my local environment, but the value "$nav.getLayout().getExpandoBridge().getAttribute("pagetag")" on the production boxes. Normally a result like that means that the method call failed somehow.

It's almost as if it doesn't see a property called "pagetag" on the pages, but it's definitely there. Our production environment consists of a staging server and a separate (remote live) production server, and I see the issue on both of them. But I added the property in the control panel exactly like I did locally. I've compared all the values for key, type, default, hidden, visible with update, searchability, secret, height, width, the permissions - everything's identical. Every role has the "View" permission, Owner and Power User also have all the other permissions. I've filled in the same values for "pagetag" on the pages.

Any thoughts? I'm tearing my hair out.
Posted on 7/18/13 1:17 PM.

Request More Information