Background

Navigation is a common component of almost every web application. You show a bunch of links somewhere on your site to allow your user to easily get from one section of the application to another. If your application is complicated, the business rules surrounding the navigation are likely also complicated. For example, only a regular user can see this link while an administrator can see that link while everyone can see some other link, and, oh, there is this one link that should show up if the current context satisfied some predicate.

I had to deal with complicated navigation in two different applications, recently, and I absolutely hated having to write a long chain of if-else statements. So I spent some time creating a framework that allows me to easily configure an application's menus and all the business rules surrounding the display, availability, and execution of each menu item.

The framework, dubbed Navisphere, is built for ASP .NET MVC in mind, but it can pretty easily be extended to regular .NET web applications, as well.

Features

There are three things about a menu item that might vary from user to user, all of which Navisphere handles:

  1. What to display? Example: "Request Something" to regular users vs. "Order Something" to admins.
  2. How to execute it? Example: "Home" goes to one page for regular users vs. another page for admins.
  3. Who can see it? Example: Admin link is available to admins and nobody else.

And, of course, the display, execution, and availability of a menu item can also be context-sensitive (i.e., dependent on something other than roles).

Usage

The idea is to be able to write something like the following on each page load to get a list of menu items that you have to show to the user:

var menu = menuCreator.CreateFor(myContext);

Setting up the menu

Our application's menu has three items with the following business rules:

Menu Item 1
    - Text = "Home" for everyone
    - Is available to everyone 
    - Goes to admin/index for administrators and home/index for regular users

Menu Item 2 
    - Text = "Edit" for everyone 
    - Is available to administrators 
    - Goes to admin/edit for everyone 

Menu Item 3
    - Text = "Manage User" for administrator and "Manage My Profile" for user
    - Is available to everyone 
    - Goes to user/edit for everyone 

Let's setup the menu with Navisphere:

    MenuCreator
        .Setup(item => item 
            .IsShownTo.Everyone.As("Home")
            .IsAvailableTo.Everyone
            .IsExecutedBy.Roles("Administrator").Via("index", "admin")
            .IsExecutedBy.Roles("Regular").Via("index", "home"))

        .Setup(item => item
            .IsShownTo.Everyone.As("Edit")
            .IsAvailableTo.Roles("Administrator")
            .IsExecutedBy.Everyone.Via("edit", "admin"))

        .Setup(item => item
            .IsShownTo.Roles("Administrator").As("Manage User")
            .IsShownTo.Roles("Regular").As("Manage My Profile")
            .IsAvailableTo.Everyone
            .IsExecutedBy.Everyone.Via("edit", "user"));

And, to display the menu:

<ul>
    <% foreach(var link in MenuBoostrapper.Menu) { %>
        <li>
            <%= Html.ActionLink(
                        link.DisplayText, 
                        link.Action, 
                        link.Controller, 
                        null, 
                        null) %>
        </li>
    <% } %> 
</ul>

Where MenuBootstrapper is:

public static class MenuBootstrapper
{
    private static readonly IMenuCreator<HttpContext> _menuCreator;

    public static IMenu Menu
    {
        get { return _menuCreator.CreateFor(HttpContext.Current); }
    }

    static MenuBootstrapper()
    {
        // Setup the _menuCreator here.
    }
}

Conclusion

Except in simple websites, navigation almost always ends up getting a little (or a lot) complicated. Instead of littering your code with a bunch of if or switch statements, a far better option is to create a small framework once that will allow you to setup your menus according to your business rules in a way that developers can understand and modify easily.

The framework will be available for download soon.