Using Multiple MvcSiteMaps
Asked Answered
K

4

9

I've recently hit a road block trying to use the MvcSiteMapProvider.

In my application, I have three distinct areas: Landing, Application and Administration. I currently have implemented the MvcSiteMapProvider and it works amazingly, but what I'm trying to do now - is use the Html MvcSiteMap Helper and specify a different map provider depending on the area that I'm in.

So, when I'm:

  • In the "Admin" area - I want to use the provider named "AdminSiteMapProvider".
  • In the "Application" area - I want to use the provider named "AppSiteMapProvider".

I've tried the following:

Shared -> _AppLayout.cshtml

@Html.Partial("_Menu")

Shared -> _Menu.cshtml

@{
if (HttpContext.Current != null && HttpContext.Current.Handler is System.Web.Mvc.MvcHandler)
{
    var handler = HttpContext.Current.Handler as System.Web.Mvc.MvcHandler;
    var currentArea = handler.RequestContext.RouteData.Values["area"] ?? string.Empty;
    if (!string.IsNullOrEmpty(currentArea.ToString()))
    {
        <text>@Html.MvcSiteMap("AppSiteMapProvider").Menu()</text>
    }
    else if (currentArea.ToString() == "Admin")
    {
        <text>@Html.MvcSiteMap("AdminSiteMapProvider").Menu()</text>    
    }
}    

}

Any suggestions? I don't want to have to copy/paste the _AppLayout.cshtml content into a new master just for one area, I'd rather it select the right provider dynamically.

Kandicekandinsky answered 24/2, 2012 at 19:0 Comment(2)
Have you tried using the area attribute of mvcSiteMapNode? <mvcSiteMapNode title="Browse Store" controller="Store" action="Index" area="Area" />Warbler
That only changes the link that is generated (I use "area" currently, for the administration links). I'm looking to generate a completely different set of links that are literally in a separate file (admin.sitemap) depending on if I'm currently browsing in the "admin" area.Kandicekandinsky
W
8

Add something like this in your web.config file (a new provider for each area):

<siteMap defaultProvider="AppSiteMapProvider" enabled="true">
  <providers>
    <clear />
    <add name="AppSiteMapProvider" type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider" siteMapFile="~/Mvc.Sitemap" securityTrimmingEnabled="true" cacheDuration="5" enableLocalization="true" scanAssembliesForSiteMapNodes="true" includeAssembliesForScan="" excludeAssembliesForScan="" attributesToIgnore="visibility" nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider" controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider" actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider" aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider" siteMapNodeUrlResolver="MvcSiteMapProvider.DefaultSiteMapNodeUrlResolver, MvcSiteMapProvider" siteMapNodeVisibilityProvider="MvcSiteMapProvider.DefaultSiteMapNodeVisibilityProvider, MvcSiteMapProvider" siteMapProviderEventHandler="MvcSiteMapProvider.DefaultSiteMapProviderEventHandler, MvcSiteMapProvider" />
    <add name="AdminSiteMapProvider" type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider" siteMapFile="~/Areas/Admin/Mvc.Sitemap" securityTrimmingEnabled="true" cacheDuration="5" enableLocalization="true" scanAssembliesForSiteMapNodes="true" includeAssembliesForScan="" excludeAssembliesForScan="" attributesToIgnore="visibility" nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider" controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider" actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider" aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider" siteMapNodeUrlResolver="MvcSiteMapProvider.DefaultSiteMapNodeUrlResolver, MvcSiteMapProvider" siteMapNodeVisibilityProvider="MvcSiteMapProvider.DefaultSiteMapNodeVisibilityProvider, MvcSiteMapProvider" siteMapProviderEventHandler="MvcSiteMapProvider.DefaultSiteMapProviderEventHandler, MvcSiteMapProvider" />
  </providers>
</siteMap>

Put this in your common masterpage (same thing for the menu):

var currentArea = (string)ViewContext.RouteData.DataTokens["area"];
if (string.IsNullOrWhiteSpace(currentArea))
{
    <text>@Html.MvcSiteMap("AppSiteMapProvider").SiteMapTitle()</text>
}
else if (currentArea.ToString() == "Admin")
{
    <text>@Html.MvcSiteMap("AdminSiteMapProvider").SiteMapTitle()</text>
}

And finaly create a sitemap file for each area.

It works for me. Hope it helps.

Warbler answered 28/2, 2012 at 7:31 Comment(0)
E
6

I'm using version 4, for which the named providers apparently doesn't work. The prescribed way to have multiple sitemaps in v4 frankly scared the bejeebus out of me and was way more work than I wanted.

Per the @NightOwl888's suggestion comment on his own answer, I used the named helpers option in v4. I still only have one mvc.sitemap file, but I have mutually exclusive visibility options.

Step 1: add this setting to in web.config

<add key="MvcSiteMapProvider_DefaultSiteMapNodeVisibiltyProvider" value="MvcSiteMapProvider.FilteredSiteMapNodeVisibilityProvider, MvcSiteMapProvider"/>

Step 2: pick the names of your different "menus" and apply them to the visibility attribute on each node. In my case I had "Regular" and "Admin". Again, all of these are in the same mvc.sitemap file.

<mvcSiteMapNode title="Reports" controller="Report" action="List" visibility="Regular,!*"/>
<mvcSiteMapNode title="Downloads" controller="Download" action="List" visibility="Regular,!*"/>
<mvcSiteMapNode title="Documents" controller="Document" action="List" visibility="Regular,!*"/>

<mvcSiteMapNode title="Users" controller="User" action="List" visibility="Admin,!*"/>
<mvcSiteMapNode title="Projects" controller="Project" action="List" visibility="Admin,!*"/>
<mvcSiteMapNode title="Misc" clickable="false" visibility="Admin,!*">
    <mvcSiteMapNode title="Reports" controller="Report" action="List" visibility="Admin,!*"/>
    <mvcSiteMapNode title="Downloads" controller="Download" action="List" visibility="Admin,!*"/>
    <mvcSiteMapNode title="Documents" controller="Document" action="List" visibility="Admin,!*"/>
</mvcSiteMapNode>

You'll note, that the Reports, Downloads and Document links are available to both regular users and admin users, but since admin rarely uses these options, I wanted to put them in the Misc submenu.

Step 3: in your _Layout.cshtml, decide which menu you want to display.

@if(User.IsInRole("Admin"))
{
    @Html.MvcSiteMap().Menu("BootstrapMenuHelperModel", false, new { name = "Admin" })
}
else
{
    @Html.MvcSiteMap().Menu("BootstrapMenuHelperModel", false, new { name = "Regular" })
}

I used this bootstrap/sitemap tutorial, if you aren't I think you can just call @Html.MvcSiteMap().Menu(new { name = "MENUNAME" })

Enfilade answered 23/12, 2014 at 19:52 Comment(1)
What a nice and simple way to do things. I also agree that the DI approach is overkill and quite frankly ridiculous. Used your approach and it is working well for me.Forwarder
G
5

Multiple Sitemaps in One Application explains how this is done in v4, which has changed quite a bit from the accepted answer - which was for v3 and prior.

The primary difference is that now multiple sitemaps are configured with DI, and you need to implement ISiteMapCacheKeyGenerator and/or ISiteMapCacheKeyToBuilderSetMapper, which are small classes to tell MvcSiteMapProvider how to map the incoming HTTP requests to each sitemap.

Germinate answered 20/8, 2013 at 0:28 Comment(3)
Holy mother of DI batman, is there no simple way to use two sitemap files?Ybarra
Sorry Robin, there isn't at the present. Although, there are now more workarounds such as using "named" HTML helpers with advanced visibiltiy so they are not needed in as many cases.Germinate
Thanks - I ended up using only one. The reality was the other sitemap was simply a "Home, About, Contact, FAQ" which is front-facing and everyone can see. No reason for a sitemap for this.Ybarra
W
2

This is what I ended up doing (based on this question). it's a little simpler, hope it helps. I just named my site map provider after the area, or "default".

<ul id="menu">
@{
    // gets a different sitemap for each area (or the default one)
    var _siteMap = ViewContext.RouteData.DataTokens["area"] 
                        as string ?? "Default";
    var sm = Html.MvcSiteMap(_siteMap);
    @sm.Menu(sm.Provider.RootNode, true, true, 2);
}
</ul>
Woodhead answered 6/7, 2012 at 10:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.