If you've started reading up on the ton of
new stuff that's been added to the open API in NetCommunity version 5.5, you may have noticed a number of new methods that expose programmatic access to the menu parts of your web site. One of the product areas we get frequent requests for enhancing is the menu part. Menus are like noses, everybody has one, and they all do the same thing, yet everyone of them looks just a little bit different. In this article, we are going to explore a custom part for creating a simple menu that taps the built-in page security model of NetCommunity, yet gives you absolute complete control over it's look and behavior.
Menu Challenges
Making a menu part built into the system that can look and behave in so many different ways has always been a challenge. In earlier versions of the product, we used a menu that, while offering the dynamic popup and styling flexibility clients demanded, lacked the required accessibility features to gracefully downgrade and still work, without javascript or CSS enabled in the browser. In version 5.5 a new menu part type has been added, Menu 2.0, that is based on the Microsoft ASP.net menu control that ships with .net. While it solves most of the accessibility issues, and adds keyboard support, it still leaves some features to be desired, particularly around controlling it's down-level behavior and appearance in less capable browsers, such as those needed for accessibility and those found on mobile devices like Blackberries, Windows Smartphones, and iPhones.
If a menu is really just a list of links to other pages, why don't you just build menus in NetCommunity with a Formatted Text part and call it a day? Then you could use what ever personal HTML markup preference you may have (tables vs. unordered lists vs. divs vs. flash and so on). You can even sprinkle in some Javascript and CSS, and Bob's your uncle. You certainly can do this, but what you would miss is what NetCommunity really adds to the equation: Page Security and Targeted Content.
How NetCommunity Helps
NetCommunity has this wicked concept that you can assign viewing rights to pages based on what roles the person looking at the site is a member of. These roles can be based on practically anything you know about them in your CRM. The roles you define can be based on your CRM Queries and can be as dynamic as you desire. So if you're a Gold Member you can access the Gold Member pages, or if you are a Big Donor the Donate Now menu item takes you to the Big Donor donation page vs. the little donor donation page and so on. With so many potential combinations of secured and targeted pages building a menu system manually would become very complicated, if not impossible.
So, NetCommunity's menu part handles the tricky bit. You can define one menu with all possible links in it, and based on who's looking, the menu only renders the items that link to pages the user has the right to see. A logged in Gold Member would see more links in the menu than a casual anonymous visitor to your site, but you only had to define one menu and one part.
But there's the rub. We want to build our own menu but we need NetCommunity to help us manage its contents dynamically. Enter the new Navigation methods of the API.
The Menu API
The way it works is that you still use the built-in menu part to define your menu, since it has a nice UI for building the hierarchical menu tree, grouping and nesting items, selecting links to internal pages, external URLs, documents, and special pages like your login page. The Menu part also handles the database storage of your menu and an optimized and cached retrieval of your menu from the database, with security in mind. Another cool thing the menu does is automatically exclude a link to your login page, if the user is already logged in. So we want to keep the built in menu.
Once you have a built-in menu created, you can use the API from within a custom part to retrieve it's definition as an XML document. This document will only contain the menu items that the user requesting the document has the rights to see. Once you have this document, your custom part can build and render your own menu in any fashion it sees fit.
There are two ways (methods) to get the XML document from the API.
- API.Navigation.GetMenuXML(MenuPartId) - this version simply returns the XML document as a string, passing in the id of the menu part whose data you want. You would then typically load the document with something like:
Dim xmlDoc AsNew XmlDocument
xmlDoc.LoadXml(Me.API.Navigation.GetMenuXml(MyContent.MenuPartId))
- API.Navigation.GetMenuXMLUrl(MenuPartId) - this versions returns a fully qualified URL from your NetCommunity server that will return the XML document. This is very handy if you want to get your menu data from within a Flash or Silverlight menu implementation for example.
Menu XML
So what does the menu XML look like?
Using the second method above, I got this url to retrieve the menu XML for the main left navigation menu on this website, so that you can check it out:
Go ahead...click on it. You should get back an XML document that contains a recursive list of <menuitem> tags.
Notice that each menu item and the hierarchy is represented and that only items you have rights to see are included. Also note that none of the styling you may have specified in the built-in menu is represented in the document. The idea of course is that we don't care about that, since we will be doing a custom rendering.
What you do get for each item is:
- active - meaning the page this item is linked to is the page you are currently on
- target - the name of any target window you specified for this link
- caption - the menu item text
- tooltip - the tooltip you specified
- pageid - the id of the NetCommunity page if this item is linked to an internal page, otherwise 0
- tabid - some internal pages have "tabs" (e.g. login, fundraiser) so you can link directly to the different stages of the certain parts that are on them, like straight to the new user sign up form of the login page.
- url - the fully qualified url to the linked page built for you
That's all there is to it.
Building a Different Menu
The menu we are building in our sample is actually the one you see on the left side of this page. For our demo we decided we wanted to build a very simple menu with the following characteristics:
- <ul><li> based - using strictly an html unordered list of anchor tags, creating sub lists where appropriate to keep the hierarchy represented.
- CSS based - the elements are all styled with some crafty CSS.
- No Javascript required
- No dynamic pop-out sub menus
- Graceful down-level rendering for accessible and mobile browsers
As a point of reference we decided to follow much of the advise on building UL menus from a common implementation called
Suckerfish. This website has some excellent articles on HTML, CSS, and is well worth perusing, so definitely check it out.
At this point I would suggest you download the attached source code and compile the project. Don't forget to update the reference path in the project to point to your NetCommunity\bin folder so that it can find BBNCExtensions.dll. You may also need to tweak the BuildEvents commands in the Compile properties of the project to point to your NetCommunity installation. If you've never built and deployed a custom part, I strongly recommend
reading this first.
As you can see the implementation is very simple. Essentially, in the part editor of our Custom Menu, the user selects a built-in menu part that they want to render. In the display control for our part, we load the xml for the selected menu, walking its <menuitem> nodes recursively, spitting out <ul>, <li>, and <a> tags as appropriate.
The entire menu is rendered within a <div> tag that gets a CSS class assigned to it whose name you decide, in the part's editor. This is important because if you were to place multiple custom menu parts on the same page, you would want some way to apply different CSS to each. Having a containing <div> with a unique classname allows us to handle this in the CSS.
So, if you look at the emitted HTML source for our Labs menu you will see it essentially looks like this:
<div class="LeftMenu1">
<ul>
<li>
<a href="...">
<ul>
<li>
<a href="...">
</li>
</ul>
</li>
</ul>
</div>
Pretty simple. The fun part is styling it. Which we do in the custom section of our labs stylesheet. Note that if CSS is not enabled in the visitor's browser, the default rendering of this HTML is an easy to read indented hierarchal list of links.
Style It
Let's take a look at the styles we added to our labs stylesheet to format our menu.
For our containing <div> tag, we gave it a class name (LeftMenu1) in the part editor so we can style it:
.LeftMenu1 {
border: 1pt solid #ccc;
margin: .5em;
width: 12em;
padding: 5px;
}
Note how every subsequent class selector is preceded with the class name we gave our <div> in the Custom Menu part editor.
Here are the styes for the outer most <ul> list:
.LeftMenu1 UL {
color:#90ba33;
padding-left:1em;
margin-left:0;
list-style:none;
}
See how list-style:none removes all bullets from our lists. Again, check out the articles
found here for some great ideas on styling lists.
Here are the defaults for our main menu links:
.LeftMenu1 A:link,
.LeftMenu1 A:active,
.LeftMenu1 A:visited{
text-decoration:none;
color:#90ba33;
}
.LeftMenu1 A:hover {
text-decoration:none;
color:#90ba33;
border-bottom:1pt dotted #14b1d0;
}
For styling the second level of links we use some fancy CSS selectors like this:
.LeftMenu1 li ul a:link {
color:#888;
}
This isolates "all A tags, under a UL tag that is under an LI tag that is inside our DIV" meaning: the second level of anchors in our menu. Which is why you see them all gray. You could use this approach to style uniquely to any depth in the menu.
The Current Active Item
Finally worth mentioning is how we style the "current item", in other words the menu item that represents the page we are currently on. See how the Home item is highlighted when we are on the home page? This takes a couple of steps.
First, in the source code we need to determine as we walk the menu XML nodes, when we've hit an item that is a link to the current page. Check out the GetCurrentPageNode function in the CustomMenu.ascx.vb class. It executes an XPATH query on the menu XML to locate the first node that has a pageid attribute equal to the current page id. We can get the current page id from the API using me.API.Pages.CurrentPage.Id.
PrivateFunction GetCurrentPageNode() As XmlNode
'find node in menu tree that has a link to the page we
'are currently on. There may not be one - since current page may
'not be represented in the menu. If there is one, we want
'to give the menu item its own class - so user can
'highlight it if they want to.
Dim ns As XmlNodeList = mXmlDoc.SelectNodes( _
String.Format("/menu//menuitem[@pageid={0}]", _
Me.API.Pages.CurrentPage.Id))
If ns.Count > 0 Then
Return ns(0)
Else
ReturnNothing
EndIf
EndFunction
As the nodes are walked and the <LI> tags are built, we look out for this node and add the MenuItemActive class to it. So in the CSS we can isolate these items with something like:
.LeftMenu1 .MenuItemActive {
border:1pt solid #14b1d0;
color:#fff;
background-color:#14b1d0;
}
.LeftMenu1 .MenuItemActive:link,
.LeftMenu1 .MenuItemActive:visited,
.LeftMenu1 .MenuItemActive:hover,
.LeftMenu1 .MenuItemActive:active{
color:#fff;
}
Since we've been so specific on the sub level items already, we have to do the same thing for the active sub level item too:
.LeftMenu1 li ul .MenuItemActive:link,
.LeftMenu1 li ul .MenuItemActive:visited,
.LeftMenu1 li ul .MenuItemActive:hover,
.LeftMenu1 li ul .MenuItemActive:active
{
color:#fff;
}
That's basically it for how the menu is built and styled. I would encourage you to play with the hundreds of interesting style tricks you can apply to lists. In a subsequent post we may take a stab at adding some popout capabilities, using interesting combinations of CSS and Javascript.
PartSelect and PartEdit Server Controls
Before we part, we've used two new powerful Server Controls that can be found in the NetCommunity v5.5 API that are worth explaining, The
PartSelectButton and the
PartEditButton.
If you take a look at the Custom Menu part editor you'll see these two buttons:
These handy controls can be found in BBNCExtensions.dll. Take a look at the CustomMenuEditor.ascx file to see how we reference the dll with a <@Register> tag. So that we can use them in the markup.
The PartSelect button renders a button that when clicked summons the modal NetCommunity dialog for searching for and selecting a part. The PartEdit button summons the NetCommunity modal dialog for editing a specific part. Since, any part's editor may be summoned this way it offers some interesting ideas for building parts that party on other parts. Each control follows a pattern of calling a javascript function of your choosing, when the dialog is closed. You specify the function name in the CallbackFunctionName property of each control. Each callback will be called with two arguments, a context string that you provide on the button (via CallbackContext property) and a returnValue object.
The CallbackContext allows you to have a common callback function but know which button called it. The returnValue object differs based on the button.
For a PartSelect button the returnValue object has two properties:
For the PartEdit button the retValue is a string which is either "SAVE" or "CANCEL", indicating the user's action to close the part editor's dialog.
The PartSelect button has FilterByPartType property that takes an integer that is the NetCommunity id of a part type, to only allow selecting parts of a specific type.
Look here to find the ids for all of the build in part types.
The PartEdit button has a PartId property that is the id of the part instance you want to edit. Any part can be edited from this button, but you will also have to specify the PartTypeId for this button as well. For convenience, all Part Property dialogs in the system now show you the part's id.
It is up to you to handle a part being selected and passing that info back to your custom part. In the Custom Menu sample, we used hidden elements to store the selected part id and name, for passing it back to the server on the postback. We then stash the menu id, into our Custom Menu's content object so we'll have it at display time.
Summary
That's probably good for this installment. We've covered how to get access to the menu data for any built-in menu, how to use it to render a simple yet useful and highly accessible menu, how most noses menus are different, we touched on some interesting CSS, and finally covered how to use two new server controls available in the NetCommunity Open Platform Release shipping with version 5.5.
This is a certainly a simple example, and there is much room for enhancing it, so stay tuned for more samples as we discover what's new in NetCommunity v.5.5.