Current link in top and submenu - umbraco

I'm trying to set a "current" class on both top and sub menu items in an Umbraco installation.
Topmenu is like :
Home Products About Contact
Now when I click let's say Products, then i set the class name to "current". At the same time it loads a submenu like this :
Jeans
Sweeters
T-shirts
- Red
- Green
- Blue
And when I click let's say Sweeters then I wan't the products link and the sweeters link to have the the current class. How can I do this?
Code for topmenu
#{
<ul class="topnavigation">
#foreach (var c in Model.AncestorOrSelf(2).Children.Where("umbracoNaviHide!=true"))
{
<li class="#(Model.Id == c.Id ? "current" : "")">#c.Name</li>
}
</ul>
}
Code for submenu
#{
<ul>
#foreach (var page in #Model.AncestorOrSelf(3).Children)
{
string style = string.Empty;
if (Model.Id == page.Id) { style = "class=current"; }
<li #style><a href="#page.Url" #Html.Raw(style)>#page.Name</a></li>
if (page.Childen != null && page.Children.Count() > 0 && Model.AncestorsOrSelf().Where("Id == #0", page.Id).Count() > 0)
{
<ul>
#foreach (dynamic secondPage in page.Children.Where("!umbracoNaviHide"))
{
string style1 = string.Empty;
if (Model.Id == secondPage.Id) { style1 = "class=current"; }
<li #style1>
- #secondPage.Name
</li>
}
</ul>
}
}
</ul>
}

A page has a Path property which contains a comma-delimited list of IDs representing the ancestors of the page.
You could check whether the Products page's ID exists within the current page's Path property using something like #Model.Path.Contains(c.Id.ToString()).

Related

Populate Navigation Bar based from Login using ASP.NET Core MVC

I have a problem and I don't know if this is the correct forum.
I have to create a system using ASP.NET Core MVC and the system have 10 plans. And in order to know the plan it will be based on the login.
And this is the sample navigation bar for (there will be lots of navigation bar and only some can be found for some plans like the order and report).
Plan 1
a. Orders
b. Reprint Receipt
c. Report
d. Setting
Plan 2
a. Orders
b. Buy
c. Report
d. Setting
Plan 3
a. Orders
b. Buy
c. Cancel Purchase
There are 10 plans and some have different navigations (redirects to a different page).
My plan is to create the pages for each plan.
But how can I populate the navigation bar for each plan?
I don't want to have many if statement in the view to hide some navigation bar for each plan.
How can I solve this? I'm thinking of having a config file. Then after the login read the config file in order to know the navigation for each plan.
Is this a correct solution for this? Or is there a better solution?
Thank you
To populate the navigation menu, first, I suggest you create a NavigationModel model to store the navigation text and the url, the NavigationModel model as below:
public class NavigationModel
{
public int Id { get; set; }
public string NavigationName { get; set; }
public string NavigationUrl { get; set; }
public string PlanType { get; set; }
}
And the following DataRepository contains the test data, in your application, you could store the data into database, then get the navigations via the DbContext:
public class DataRepository : IDataRepository
{
public List<NavigationModel> GetNavigations()
{
return new List<NavigationModel>()
{
new NavigationModel(){ Id =1001, NavigationName="Orders", NavigationUrl="Home/Orders", PlanType="Plan1"},
new NavigationModel(){ Id =1002, NavigationName="Reprint Receipt", NavigationUrl="Home/Reprint", PlanType="Plan1"},
new NavigationModel(){ Id =1003, NavigationName="Report", NavigationUrl="Home/Report", PlanType="Plan1"},
new NavigationModel(){ Id =1004, NavigationName="Setting", NavigationUrl="Home/Setting", PlanType="Plan1"},
new NavigationModel(){ Id =1005, NavigationName="Orders", NavigationUrl="Home/Orders", PlanType="Plan2"},
new NavigationModel(){ Id =1006, NavigationName="Buy", NavigationUrl="Home/Buy", PlanType="Plan2"},
new NavigationModel(){ Id =1007, NavigationName="Report", NavigationUrl="Home/Report", PlanType="Plan2"},
new NavigationModel(){ Id =1008, NavigationName="Setting", NavigationUrl="Home/Setting", PlanType="Plan2"},
new NavigationModel(){ Id =1009, NavigationName="Orders", NavigationUrl="Home/Orders", PlanType="Plan3"},
new NavigationModel(){ Id =10010, NavigationName="Buy", NavigationUrl="Home/Buy", PlanType="Plan3"},
new NavigationModel(){ Id =10011, NavigationName="Cancel Purchase", NavigationUrl="Home/CancelPurchase", PlanType="Plan3"},
};
}
}
Then, you could try to use the following methods to populate the navigation menu.
Use session to store the navigation information.
First, configure the application to use session and add the SessionExtensions (Note the session expired time)
Second, after user login successfully, you could refer the following code to get the relate navigation menu, and store them in the session:
public IActionResult Index()
{
var isUserLogin = true;
var plantype = "Plan2";
if (isUserLogin && _dataRepository.GetNavigations().Any(c => c.PlanType.Contains(plantype)))
{
var navigations = _dataRepository.GetNavigations().Where(c => c.PlanType == plantype).ToList();
if (HttpContext.Session.Get<List<NavigationModel>>("Navigation") == default)
{
HttpContext.Session.Set<List<NavigationModel>>("Navigation", navigations);
}
}
return View();
}
In the _Layout.cshtml page, add the following code in the header:
#using Microsoft.AspNetCore.Http
#inject IHttpContextAccessor HttpContextAccessor
[Note] In the Startup.ConfigureServices method, use services.AddHttpContextAccessor(); to register the HttpContextAccessor.
Then, we could access the session and populate the navigation menu:
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
#if (HttpContextAccessor.HttpContext.Session.Get<List<NavigationModel>>("Navigation") != default)
{
var navigations = HttpContextAccessor.HttpContext.Session.Get<List<NavigationModel>>("Navigation");
foreach (var item in navigations)
{
<li class="nav-item">
<a class="nav-link text-dark" href="#item.NavigationUrl">#item.NavigationName</a>
</li>
}
}
</ul>
Use JQuery Ajax to call the action method and populate the navigation menu:
In the Home Controller, create an action method to get the navigation menu:
[HttpPost]
public IActionResult GetNavigation(string plantype)
{
//check if user login and get the relate navigation menus.
return Json(_dataRepository.GetNavigations().Where(c => c.PlanType == plantype).ToList());
}
In the _Layout.cshtml page, use the following code to add the navigation menu:
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
<script>
$(function () {
$.ajax({
url: "Home/GetNavigation",
method: "Post",
data: { "plantype": "Plan2" },
success: function (items) {
for (var i = 0; i < items.length; i++) {
var li = document.createElement('li');
li.setAttribute('class', 'nav-item');
var a = document.createElement('a');
a.setAttribute('class', 'nav-link text-dark');
a.href = items[i].navigationUrl;
var linkText = document.createTextNode(items[i].navigationName);
a.appendChild(linkText);
li.appendChild(a);
$(".navbar-nav")[0].appendChild(li);
}
}
})
});
</script>
The result as below (display plan 2):

NopCommerce Category Navigation Design

I am trying to edit this category navigation view. What I need is this:
We do not want to see all main categories or all sub categories as well.
When I click a Main Category (ParentCategoryId = 0), I should see only this one and it's sub categories.
When I click one of this sub categories, this sub category should be main category, and this time I should see it's sub categories only.
For ex, navigation system works like this right now:
Women (selected)
Bag
Top
Dress
Sound Systems
Home Decoration
Gardening
or
Women
Bag
Top (selected)
Tshirt
Sweatshirt
Tunic
Dress
Jeans
Suit
Sound Systems
Home Decoration
Gardening
What we want is that:
Women (selected)
Bag
Top
Dress
Jeans
Suit
or
Top (selected)
Tshirt
Sweatshirt
Tunic
#model CategoryNavigationModel
#using Nop.Web.Models.Catalog;
#using Nop.Core.Infrastructure;
#functions{
public bool BreadCrumbContainsCurrentCategoryId(CategorySimpleModel category)
{
if (Model.CurrentCategoryId == 0)
return false;
if (category.Id == Model.CurrentCategoryId)
return true;
foreach (var subCategory in category.SubCategories)
{
if (BreadCrumbContainsCurrentCategoryId(subCategory))
{
return true;
}
}
return false;
}
}
#helper RenderCategoryLine(CategorySimpleModel category)
{
var _categoryService = EngineContext.Current.Resolve<Nop.Services.Catalog.ICategoryService>();
// additional check on whether to set an active class on the parents of the current categories and above. Meaning all categories from the
// breadcrumb will have an "active" class
bool active = false;
if (category.Id == Model.CurrentCategoryId || category.SubCategories.Count(BreadCrumbContainsCurrentCategoryId) > 0)
{
active = true;
}
<li class="#(active ? "active" : "inactive")">
#if (category.ParentCategoryId == Model.CurrentCategoryId || category.Id == Model.CurrentCategoryId)
{
<a href="#Url.RouteUrl("Category", new { SeName = category.SeName })">#category.Name
#if (category.NumberOfProducts.HasValue)
{
<text> </text>#T("Categories.TotalProducts", category.NumberOfProducts.Value)
}
</a>
}
#{
if (category.Id == Model.CurrentCategoryId ||
category.SubCategories.Count(BreadCrumbContainsCurrentCategoryId) > 0)
{
if (category.SubCategories.Count > 0)
{
<ul class="sublist">
#foreach (var subCategory in category.SubCategories)
{
#RenderCategoryLine(subCategory)
}
</ul>
}
}
}
</li>
}
#if (Model.Categories.Count > 0)
{
<div class="block block-category-navigation">
<div class="title">
#if (Model.CurrentCategoryId == 1 || Model.CurrentCategoryId == 3 || Model.CurrentCategoryId == 4 || Model.CurrentCategoryId == 5)
{
<strong>#T("Categories")</strong>
}
else
{
<strong><< Geri Dön</strong>
}
</div>
<div class="listbox">
<ul class="list">
#foreach (var category in Model.Categories)
{
#RenderCategoryLine(category)
}
</ul>
</div>
</div>
}
My code is something like this, but there is a problem. Code is not working when selected category has no sub categories. On this situation it must do something like this:
PS: Sweatshirt has no sub categories.
Top
Tshirt
Sweatshirt (selected)
Tunic
Instead it shows something like this:
Sweatshirt (selected)
I'd hope I can explain myself. How can I solve this problem?
Thanks.

ASP.NET MVC5 dynamic submenu with links generated from DB

I need to have a submenu(Executive Sections) for a section called 'Executives'. This menu must show up when a user clicks Executives in the main menu and remain as long as any submenu link of Executives is clicked.
Catch is: the submenu links are dynamic and come from the DB/CMS system, so it's not a hard-coded list. Which is where my woe begins.
Currently, I have it in a PartialView that requires a model of IEnumerable so it can build dynamically.
But how I go about making this work as I need it too is a little twisted up in my mind.
#using xxx.Models
#model IEnumerable<xxx.Models.ExecutiveSection>
<ul class="nav navbar-nav">
#foreach (ExecutiveSection es in Model)
{
<li>#Html.ActionLink(es.SectionName, "Section", "Executive", new { id = es.ExecutiveSectionId })</li>
}
I solved it.
Did this in the _Layout
#{ // Sub-Menu for Executive Sections
if (HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString().Contains("Executive"))
{
Html.RenderAction("ExecutiveSubMenu", "Executive");
}
}
And in the controller:
public ActionResult ExecutiveSubMenu()
{
// get sections
var sections = db.ExecutiveSections.ToList();
return PartialView("ExecutiveSubMenuView", sections);
}
and finally the Partial:
#using xxx.Models
#model IEnumerable<xxx.Models.ExecutiveSection>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
#foreach (ExecutiveSection es in Model)
{
<li>#Html.ActionLink(es.SectionName, "Section", "Executive", new { id = es.SectionName.Replace(" ", "-") }, null)</li>
}
</ul>
(I 'replace' the spaces in any section name with dashes '-' and back again for SEO friendliness.)

Umbraco 5 ask if user has permission to node

Im working with Umbraco 5.1 beta. On the internet (this information is from previous versions, could not find recent documentation on it) I find that I could ask a node if the user has Access. In that way I want to build up my menu. The thing is, I cant get it to work, the HasAccess and IsProtected properties are not working. What am I doing wrong? Or does it work different in the newer versions of Umbraco? (I also tried it as method, still no result)
This is the code I'm now using:
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children) {
if(!item.IsProtected || (item.IsProtected && item.HasAccess)) {
if(#item.CurrentTemplate != null) {
var childName = item.Name ?? "(No name yet)";
<li>#childName </li>
}
}
}
</ul>
If you are just looking to suppress nodes that the user cannot access. Then you can use the WhereCanAccess() method.
Example: (This will hide all child nodes that the user doesn't have access to)
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children.WhereCanAccess())
{
if(#item.CurrentTemplate != null)
{
var childName = item.Name ?? "(No name yet)";
<li>#childName </li>
}
}
</ul>
Trying to find if a node IsProtected seems to be somewhat more complex (although only a couple of lines of code. Well the only way I found find to do it anyway!)
Example: (This just puts an * next to the name of protected menu items)
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
var appContext = DependencyResolver.Current.GetService<IUmbracoApplicationContext>();
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children)
{
var isProtected = appContext.Security.PublicAccess.IsProtected(item.Id);
if (#item.CurrentTemplate != null)
{
var childName = item.Name ?? "(No name yet)";
childName = (isProtected) ? "* " + childName : childName;
<li>#childName </li>
}
}
</ul>

select right menu on master page in MVC from child page

I have a couple of list items in a shared _layout.cshtm file (master page) in my MVC application.
something like:
<ul>
<li>Home</li>
<li>about</li>
<li>contact</li>
<li>blog</li>
</ul>
when the user is in a homepage, I want home li item to have class selected, like so:
<li class="selected">Home</li>
and so on. What is the best way to do this?
In regular asp.net website, I used to have a method in master page and call that method from child page but in MVC I am not sure what to do.
thanks.
You could write a custom helper method:
public static MvcHtmlString MenuItem(
this HtmlHelper htmlHelper,
string text,
string action,
string controller
)
{
var li = new TagBuilder("li");
var routeData = htmlHelper.ViewContext.RouteData;
var currentAction = routeData.GetRequiredString("action");
var currentController = routeData.GetRequiredString("controller");
if (string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase) &&
string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase))
{
li.AddCssClass("selected");
}
li.SetInnerText(text);
return MvcHtmlString.Create(li.ToString());
}
and then:
<ul>
#Html.MenuItem("Home", "home", "home")
#Html.MenuItem("About", "about", "home")
#Html.MenuItem("Contact", "contact", "home")
#Html.MenuItem("Blog", "blog", "home")
</ul>
The helper check the current action and controller and if they match the one passed as arguments to the helper it appends the selected CSS class to the li.
Just wanted to share what i do:
I create folder App_Code and add CustomHelpers.cshtml. In it i create something like this:
#helper MainMenu(string SelectedItem) {
<ul class="MainMenu">
<li><a href="/home" #if (SelectedItem == "Home") { <text>class="Active"</text> }>Home</a></li>
<li><a href="/about" #if (SelectedItem == "About") { <text>class="Active"</text> }>About</a></li>
<li><a href="/foo" #if (SelectedItem == "Foo") { <text>class="Active"</text> }>Foo</a></li>
</ul>
}
Than in my MasterPage (_Layout.cshtml) i add this where i want my menu to apear:
#CustomHelpers.MainMenu(ViewBag.SelectedMenu)
And than in my view, just like i change my page title, i change my selected menu:
#{
ViewBag.Title = "Welcome to my homepage";
ViewBag.SelectedMenu = "Home";
}
Got my idea from this tutorial: www.asp.net/mvc/videos/mvc-3/mvc-3-razor-helpers

Resources