Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to hide a menu item based on user's role in MVC app (html menu list)?

This question I've already been answered for webforms app, but can I hide my Administration menu item with this code ? Is it possible with sitemap binding or else ?

my menu in : Layout.cshtml

    <ul id="navlist">
        <li class="first"> <a href="@Url.Content("~")" id="current">Home</a> </li>

        <li>
            <a href="#">User</a>
            <ul class="second">
                <li> <a href="@Url.Content("~/Service/")">> Services</a></li>
            </ul>
        </li>

        <li> 
            <a href="#">Administration</a>
            <ul class="second">
                <li><a href="@Url.Content("~/ServiceManager")">> Services</a></li>
            </ul>
        </li>

        <li>@Html.Partial("_LogOnPartial") </li>
    </ul>
like image 573
Alternative Avatar asked Jul 31 '12 13:07

Alternative


1 Answers

The suitability of using a simple if statement, as described by Alternative, depends on:

-how many roles you have (in that you may wish to avoid multiple queries to the database, by avoiding multiple

if(User.IsInRole("whatever"))

-if you are using a custom role and membership provider, in which case you may wish to avoid using User.IsInRole all together, as it will open a new connection to the database every time.

Brij Mohan Dammani describes a solution which is has some nice features, but for my liking puts too much logic in the view. I also prefer strongly typed objects where possible in object oriented languages.

based on the code described by Brij Dammani, I now use a model:

namespace Whatever
{
  public class RoleMenuItem: MenuItem
  {
    public RoleMenuItem(){}
    public RoleMenuItem(string linkText, string actionName, string controllerName, string roleNames)
    {
        LinkText = linkText;
        ActionName = actionName;
        ControllerName = controllerName;
        RoleNames = roleNames;
    }
    public string RoleNames { set { Roles = value.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries); } }
    internal string[] Roles;
  }
  public class MenuItem
  {
    public string LinkText { get; set; }
    public string ActionName { get; set; }
    public string ControllerName { get; set; }
  }
  public class RoleMenu : System.Collections.Generic.IEnumerable<MenuItem>
  {
    private readonly List<RoleMenuItem> _roleMenuItems = new List<RoleMenuItem>();
    private readonly string[] _userRoleNames;
    public readonly bool _isAuthenticated;
    public RoleMenu()
    {
        if (_isAuthenticated = WebSecurity.User.Identity.IsAuthenticated)
        {
            _userRoleNames = Roles.GetRolesForUser();
        }
    }
    public RoleMenu(IDataContext context) 
    {
        if (_isAuthenticated = WebSecurity.User.Identity.IsAuthenticated)
        {
            string userName = HttpContext.Current.User.Identity.Name;
            User usr = context.Users.FirstOrDefault(Usr => Usr.UserName == userName) ;
            _userRoleNames = (usr==null)? new string[0]: usr.Roles.Select(r => r.RoleName).ToArray(); 

        }
    }
    public RoleMenu Add(RoleMenuItem menuItem)
    {
        string[] menuRoles = menuItem.Roles;
        if (
                menuRoles.Contains("All" ) ||
                (!_isAuthenticated && menuRoles.Contains("Anonymous")) ||
                (_isAuthenticated && (menuRoles.Contains("Authenticated") || menuRoles.Any(mr=>_userRoleNames.Contains(mr))))
            )
        {
            _roleMenuItems.Add(menuItem);
        }
        return this;
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    public System.Collections.Generic.IEnumerator<MenuItem> GetEnumerator()
    {
        return _roleMenuItems.GetEnumerator();
    } 
    public IEnumerable<MenuItem> ItemsForRole(string roleName)
    {
        return _roleMenuItems.Where(r => r.Roles.Contains(roleName));
    }
}

then in the constructor for each Controller (which is set up for dependency injection)

ViewBag.Menu = new RoleMenu(_db);

and in the _Layout view

@using WhateverNamespace
<nav>
    <ul id="menu">
    @{
       RoleMenu menu = (ViewBag.Menu ?? new RoleMenu())
          .Add(new RoleMenuItem("Home", "Index", "Home","Anonymous"))
          .Add(new RoleMenuItem("Home", "Auth", "Home","Authenticated"))
          .Add(new RoleMenuItem("About", "About", "Home","All"))
          .Add(new RoleMenuItem("Administer", "Index", "Administrators","Webmaster,Administrator"))...;
       foreach (var link in menu)
       {
          <li> 
              @Html.ActionLink(link.LinkText, link.ActionName,link.ControllerName)
            ....

or

    if((var adminMenuItems=menu.ItemsFoRole("admin")).Any()) {
       <li><ul>
       foreach (var link in adminMenuItems) {
           <li> ....
       }
like image 157
Brent Avatar answered Nov 15 '22 09:11

Brent