locked
Left side Menu with user info RRS feed

  • Question

  • User-833526072 posted

    Hi all. I Have an page i want to use a left menu.
    When I use regular render, I cant get user info.

    Header
    Left menu
    Here i need info of loged in user +++
    body
    Footer

    My purpose of this is that Left side menu stores the logged in user details.
    In body section, the page will be different. example, customers.chtml, orders, cshtml and so on.
    Anyone have a opinion of this?

    Monday, March 15, 2021 12:51 PM

Answers

  • User1686398519 posted

    Hi cbrAndy, 

    According to your needs, I wrote an example, you can refer to it.

    1. I suggest you use Identity to achieve authentication.
    2. The following example is modified in the ASP.NET MVC Identity template project, you can modify it according to your own needs.
    3. You need to modify the _Layout view, then create a Menu class, and implement dynamic menus based on roles.

    ApplicationUser

       public class ApplicationUser : IdentityUser
        {
            public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
            {
                // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
                var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
                ApplicationDbContext context = new ApplicationDbContext();
                var roleManager = new RoleManager<ApplicationUserRole>(new RoleStore<ApplicationUserRole>(context));
                var role = manager.GetRoles(userIdentity.GetUserId())[0].ToString();
                var roleid = roleManager.Roles.Where(m => m.Name == role).Select(m => m.Id).FirstOrDefault();
                var menulist = context.Menus.Include("ApplicationUserRoles").Include("SubMenus").Where(m => m.ApplicationUserRoles.Any(i => i.Id == roleid)).ToList();
                var menulistjsonstring = JsonConvert.SerializeObject(menulist, Formatting.Indented,
                new JsonSerializerSettings
                {
                    PreserveReferencesHandling = PreserveReferencesHandling.Objects
                });
                userIdentity.AddClaim(new Claim("menulist", menulistjsonstring));
                return userIdentity;
            }
        }
        public class ApplicationUserRole: IdentityRole
        {
            public List<Menus> Menus { get; set; }
        }
        public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
        {
            public ApplicationDbContext()
                : base("DefaultConnection", throwIfV1Schema: false)
            {
            }
            public DbSet<Menus> Menus { get; set; }
            public static ApplicationDbContext Create()
            {
                return new ApplicationDbContext();
            }
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                modelBuilder.Entity<ApplicationUserRole>()
                    .HasMany(m=>m.Menus)
                    .WithMany(m=>m.ApplicationUserRoles).Map(cs =>
                    {
                        cs.MapLeftKey("RoleId");
                        cs.MapRightKey("MainMenuId");
                        cs.ToTable("AspNetRolesMenus");
                    });
            }
        }

    Menus

        [JsonObject(IsReference = true)]
        public class Menus
        {
            [Key]
            public int MainMenuId { get; set; }
            public string MainMenuName { get; set; }
            public string MainMenuUrl { get; set; }
            public int? ParentMenuId { get; set; }
            [ForeignKey("ParentMenuId")]
            public List<Menus> SubMenus { get; set; }
            public List<ApplicationUserRole> ApplicationUserRoles { get; set; }
        }

    _Layout.cshtml

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>@ViewBag.Title - My ASP.NET Application</title>
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        @using Microsoft.AspNet.Identity
        <div class="wrapper">
            @if (Request.IsAuthenticated)
            {
                @Html.Partial("_LeftMenu")
            }
            <!-- Page Content Holder -->
            <div id="content">
                <nav class="navbar navbar-expand-lg navbar-light bg-light">
                    <div class="container-fluid">
                        @if (Request.IsAuthenticated)
                        {
                            <button type="button" id="sidebarCollapse" class="navbar-btn">
                                <span></span>
                                <span></span>
                                <span></span>
                            </button>
                        }
                        <button class="btn btn-dark d-inline-block d-lg-none ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                            <i class="fas fa-align-justify"></i>
                        </button>
                        <div class="collapse navbar-collapse" id="navbarSupportedContent">
                            @Html.Partial("_LoginPartial")
                        </div>
                    </div>
                </nav>
                @RenderBody()
            </div>
        </div>
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/bootstrap")
        <script src="~/Scripts/fontawesome/solid.js"></script>
        <script src="~/Scripts/fontawesome/fontawesome.js"></script>
        @RenderSection("scripts", required: false)
        <script type="text/javascript">
            $(document).ready(function () {
                $('#sidebarCollapse').on('click', function () {
                    $('#sidebar').toggleClass('active');
                    $(this).toggleClass('active');
                });
            });
        </script>
    </body>
    </html>

    _LeftMenu

    <!-- Sidebar Holder -->
    @using Microsoft.AspNet.Identity
    @using DynamicLeftMenuBasedRoleMVCIdentityDemo.Models
    <nav id="sidebar">
        <div class="sidebar-header">
            <h3>Sidebar Demo</h3>
        </div>
        @{
            var rootmenulist = User.Identity.GetMenusList().Where(m => m.ParentMenuId == null).ToList();
        }
        @ShowMenu(rootmenulist, null)
        @helper ShowMenu(List<Menus> list, string hrefname)
        {
            hrefname = String.IsNullOrEmpty(hrefname) ? "" : hrefname;
            <ul class="list-unstyled" id="@hrefname">
                @foreach (var item in list)
                {
                    <li>
                        <a href="#@item.MainMenuName" data-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
                            @item.MainMenuName
                        </a>
                        @if (item.SubMenus.Count() != 0)
                        {
                            @ShowMenu(item.SubMenus, @item.MainMenuName)
                        }
                    </li>
                }
            </ul>
        }
    </nav>

    IdentityExtensions

        public static class IdentityExtensions
        {
            public static List<Menus> GetMenusList(this IIdentity identity)
            {
                if (identity == null)
                {
                    throw new ArgumentNullException("identity");
                }
                var claimsidentity = identity as ClaimsIdentity;
                List<Menus> menulist = new List<Menus>();
                if (claimsidentity != null)
                {
                    var value = claimsidentity.FindFirstValue("menulist");
                    menulist = String.IsNullOrEmpty(value)?null: JsonConvert.DeserializeObject<List<Menus>>(value);
                }
                return menulist;
            }
        }

    Here is the result. 

    Best Regards,

    YihuiSun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, April 2, 2021 11:42 AM

All replies

  • User475983607 posted

    My purpose of this is that Left side menu stores the logged in user details.

    The user principal is very easy to get to if you are using standard authentication/authorization that comes with MVC.  

    User.Identity.Name

    The standard MVC 5 templates have sample code (_loginPartial.cshtml) that shows how to display user information in the main layout. 

    @using Microsoft.AspNet.Identity
    @if (Request.IsAuthenticated)
    {
        using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
        {
        @Html.AntiForgeryToken()
    
        <ul class="nav navbar-nav navbar-right">
            <li>
                @Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
            </li>
            <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
        </ul>
        }
    }
    else
    {
        <ul class="nav navbar-nav navbar-right">
            <li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
            <li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
        </ul>
    }

    At this point it is not clear why you are unable to get user to the user information or what user information you wish to show in the left menu.

    Monday, March 15, 2021 1:03 PM
  • User-833526072 posted

    I have not used the mvc authorization code

    I've wrote something when I wrote classic asp code.

    I should probably use the mvc script.

    Monday, March 15, 2021 9:07 PM
  • User475983607 posted

    I have not used the mvc authorization code

    I've wrote something when I wrote classic asp code.

    Seriously.  How do you expect the MVC community to help you if you wrote a custom authentication/authorization?  We cannot see your custom code and have no idea how code works.

    I should probably use the mvc script.

    I recommend using standard ASP.NET security.  You have to read documentation for the framework you are using ASP.NET or Core.

    https://docs.microsoft.com/en-us/aspnet/identity/overview/getting-started/introduction-to-aspnet-identity

     

    Monday, March 15, 2021 10:02 PM
  • User1686398519 posted

    Hi cbrAndy, 

    You can modify your layout view to achieve your needs.

    Best Regards,

    YihuiSun

    Tuesday, March 16, 2021 9:20 AM
  • User-833526072 posted

    Hello YihuiSun.
    I've been testing some today.
    I really can't get a grip for this.

    I made this in my CRMcontroller

                    Employee myEmployee = new Employee
                    {
                        EmployeeFirstName = employee.EmployeeFirstName,
                        EmployeeLastname = employee.EmployeeLastname,
                        EmployeeID = employee.EmployeeID
                    };
                    ViewBag.myEmployee = myEmployee;
    
                    var myCompany = db.Companies.Find(employee.CompanyID);
                    ViewBag.myCompany = myCompany;
    
                    var myRole = db.EmployeeRoles.Find(employee.EmployeeRoleID);
                    ViewBag.myRole = myRole;

    In my _leftMenu i got the information by doing this:

            var myEmployee = ViewBag.myEmployee;
            var myCompany = ViewBag.myCompany;
            var myRole = ViewBag.myRole;

    Which i coult post like this in layout view:

    var name = myEmployee.EmployeeFirstName + " " + myEmployee.EmployeeLastname;

    This works fine! Nothing problem here untill i press a link, that goes to another controller.
    Then the viewbag is empty and nothing works longer.

    So I'm back to start.

    Basicly what I'm trying to do is to have a crm page.
    Main page after login, i'm getting to a dashboard.
    Left menu I can choose several menus. The menu is not same for all user.
    Pressing on a link, the main menu is changed as the link you provided.
    Do you have a clue how I can do this?

    Friday, March 26, 2021 4:34 PM
  • User475983607 posted

    This works fine! Nothing problem here untill i press a link, that goes to another controller.

    Again, if you follow standard MVC security patterns then this is a non-issue.  User information like name, company, roles, etc are typically stored in claims and cached in an authentication cookie when the user logs in.  The following link illustrates how to cache claims in an authentication cookie without Identity.  

    Core MVC

    https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-5.0

    ASP.NET MVC

    https://stackoverflow.com/questions/31511386/owin-cookie-authentication-without-asp-net-identity

    Left menu I can choose several menus. The menu is not same for all user.

    Data driven menus are a very common web app feature.  Do a internet search for examples.  Typically the menu is cached for fast look ups.

    https://www.google.com/search?q=mvc+Data+driven+menu

    Friday, March 26, 2021 4:48 PM
  • User-833526072 posted

    Hello mgebhard.
    Ok, so this is my case.
    I have in my left menu, projects, customers, sales. This comes from a database, which each loged in user can only see.

    var myCompany = db.Companies.Find(employee.CompanyID);
    ViewBag.myCompany = myCompany;
     
    var myRole = db.EmployeeRoles.Find(employee.EmployeeRoleID);
    ViewBag.myRole = myRole;

    So if I use MVC security patterns i'll get these? just like reading a viewbag?

    var myCompany = ViewBag.myCompany;
    

    Friday, March 26, 2021 5:18 PM
  • User475983607 posted

    cbrAndy

    So if I use MVC security patterns i'll get these? just like reading a viewbag?

    Similar but not the same.  The user information is stored in claims.  The claims are cached in the authentication cookie as illustrated in the links provided above.  It is just a mater of fetching the claims which is very simple.

    Friday, March 26, 2021 7:01 PM
  • User-833526072 posted

    Ok. I'll try to i plent this tomorrow. Thanks for your tips

    Friday, March 26, 2021 10:17 PM
  • User-833526072 posted

    This was not a simple quick fix. I need to do all my code again.
    Or do you know a better way to do this?

    Thursday, April 1, 2021 9:08 AM
  • User475983607 posted

    cbrAndy

    This was not a simple quick fix. I need to do all my code again.
    Or do you know a better way to do this?

    Sometimes initial designs do not work out as expected.  You built a custom solution that must populate the ViewBag on each and every request. If you want to continue using your solution then it is up to you to come up with a design.   Perhaps a custom attribute or a base controller.

    I simply explained how to interface with MVC security by caching user claims in the standard authentication cookie.  This approach also allows you to use the [Authorize] attribute. 

    Thursday, April 1, 2021 10:13 AM
  • User-833526072 posted

    hehe...
    I'm actually doing the hard way and trying to understand the MVC security. It will take time, but first I'm trying to do the leftside menu.
    I'm going to make an @Html.Partial("_Sidemenu") for this.
    For this I'm now try to make an controller anv view model for this. Is this the correct way to do this?

    -André

    Friday, April 2, 2021 8:14 AM
  • User475983607 posted

    For this I'm now try to make an controller anv view model for this. Is this the correct way to do this?

    No.  Good luck.

    Friday, April 2, 2021 10:56 AM
  • User1686398519 posted

    Hi cbrAndy, 

    According to your needs, I wrote an example, you can refer to it.

    1. I suggest you use Identity to achieve authentication.
    2. The following example is modified in the ASP.NET MVC Identity template project, you can modify it according to your own needs.
    3. You need to modify the _Layout view, then create a Menu class, and implement dynamic menus based on roles.

    ApplicationUser

       public class ApplicationUser : IdentityUser
        {
            public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
            {
                // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
                var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
                ApplicationDbContext context = new ApplicationDbContext();
                var roleManager = new RoleManager<ApplicationUserRole>(new RoleStore<ApplicationUserRole>(context));
                var role = manager.GetRoles(userIdentity.GetUserId())[0].ToString();
                var roleid = roleManager.Roles.Where(m => m.Name == role).Select(m => m.Id).FirstOrDefault();
                var menulist = context.Menus.Include("ApplicationUserRoles").Include("SubMenus").Where(m => m.ApplicationUserRoles.Any(i => i.Id == roleid)).ToList();
                var menulistjsonstring = JsonConvert.SerializeObject(menulist, Formatting.Indented,
                new JsonSerializerSettings
                {
                    PreserveReferencesHandling = PreserveReferencesHandling.Objects
                });
                userIdentity.AddClaim(new Claim("menulist", menulistjsonstring));
                return userIdentity;
            }
        }
        public class ApplicationUserRole: IdentityRole
        {
            public List<Menus> Menus { get; set; }
        }
        public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
        {
            public ApplicationDbContext()
                : base("DefaultConnection", throwIfV1Schema: false)
            {
            }
            public DbSet<Menus> Menus { get; set; }
            public static ApplicationDbContext Create()
            {
                return new ApplicationDbContext();
            }
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                modelBuilder.Entity<ApplicationUserRole>()
                    .HasMany(m=>m.Menus)
                    .WithMany(m=>m.ApplicationUserRoles).Map(cs =>
                    {
                        cs.MapLeftKey("RoleId");
                        cs.MapRightKey("MainMenuId");
                        cs.ToTable("AspNetRolesMenus");
                    });
            }
        }

    Menus

        [JsonObject(IsReference = true)]
        public class Menus
        {
            [Key]
            public int MainMenuId { get; set; }
            public string MainMenuName { get; set; }
            public string MainMenuUrl { get; set; }
            public int? ParentMenuId { get; set; }
            [ForeignKey("ParentMenuId")]
            public List<Menus> SubMenus { get; set; }
            public List<ApplicationUserRole> ApplicationUserRoles { get; set; }
        }

    _Layout.cshtml

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>@ViewBag.Title - My ASP.NET Application</title>
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        @using Microsoft.AspNet.Identity
        <div class="wrapper">
            @if (Request.IsAuthenticated)
            {
                @Html.Partial("_LeftMenu")
            }
            <!-- Page Content Holder -->
            <div id="content">
                <nav class="navbar navbar-expand-lg navbar-light bg-light">
                    <div class="container-fluid">
                        @if (Request.IsAuthenticated)
                        {
                            <button type="button" id="sidebarCollapse" class="navbar-btn">
                                <span></span>
                                <span></span>
                                <span></span>
                            </button>
                        }
                        <button class="btn btn-dark d-inline-block d-lg-none ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                            <i class="fas fa-align-justify"></i>
                        </button>
                        <div class="collapse navbar-collapse" id="navbarSupportedContent">
                            @Html.Partial("_LoginPartial")
                        </div>
                    </div>
                </nav>
                @RenderBody()
            </div>
        </div>
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/bootstrap")
        <script src="~/Scripts/fontawesome/solid.js"></script>
        <script src="~/Scripts/fontawesome/fontawesome.js"></script>
        @RenderSection("scripts", required: false)
        <script type="text/javascript">
            $(document).ready(function () {
                $('#sidebarCollapse').on('click', function () {
                    $('#sidebar').toggleClass('active');
                    $(this).toggleClass('active');
                });
            });
        </script>
    </body>
    </html>

    _LeftMenu

    <!-- Sidebar Holder -->
    @using Microsoft.AspNet.Identity
    @using DynamicLeftMenuBasedRoleMVCIdentityDemo.Models
    <nav id="sidebar">
        <div class="sidebar-header">
            <h3>Sidebar Demo</h3>
        </div>
        @{
            var rootmenulist = User.Identity.GetMenusList().Where(m => m.ParentMenuId == null).ToList();
        }
        @ShowMenu(rootmenulist, null)
        @helper ShowMenu(List<Menus> list, string hrefname)
        {
            hrefname = String.IsNullOrEmpty(hrefname) ? "" : hrefname;
            <ul class="list-unstyled" id="@hrefname">
                @foreach (var item in list)
                {
                    <li>
                        <a href="#@item.MainMenuName" data-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
                            @item.MainMenuName
                        </a>
                        @if (item.SubMenus.Count() != 0)
                        {
                            @ShowMenu(item.SubMenus, @item.MainMenuName)
                        }
                    </li>
                }
            </ul>
        }
    </nav>

    IdentityExtensions

        public static class IdentityExtensions
        {
            public static List<Menus> GetMenusList(this IIdentity identity)
            {
                if (identity == null)
                {
                    throw new ArgumentNullException("identity");
                }
                var claimsidentity = identity as ClaimsIdentity;
                List<Menus> menulist = new List<Menus>();
                if (claimsidentity != null)
                {
                    var value = claimsidentity.FindFirstValue("menulist");
                    menulist = String.IsNullOrEmpty(value)?null: JsonConvert.DeserializeObject<List<Menus>>(value);
                }
                return menulist;
            }
        }

    Here is the result. 

    Best Regards,

    YihuiSun

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, April 2, 2021 11:42 AM
  • User-833526072 posted

    YihuiSun.
    Again, I need to take a bow for you.
    Thank you so much for your help.

    Tuesday, April 6, 2021 8:06 AM