Using multiple navigation.xml files
Closed this issue · 6 comments
Hi,
Is it possible to use multiple navigation.xml files with the same solution?
For instance, I if I use navigation.xml for the main application. In the Admin Area, I want to have a different set of Navigation, can I have navigation.admin.xml?
I know it can be configured in the appsetting file to use a different file:
"NavigationOptions": {
"RootTreeBuilderName": "cloudscribe.Web.Navigation.XmlNavigationTreeBuilder",
"NavigationMapXmlFileName": "navigation.xml",
"NavigationMapJsonFileName": "navigation.json"
}
but that sets it on a global level.
How do I get it at runtime to use a different navigation map file?
It can be done, you need to implement a custom
IOptions<NavigationOptions>
and inject your custom implementation. Then you can return different file names based on something about the request.
For example in my cloudscribe Core project I implement multi-tenancy, by default each tenant site uses the same navigation.xml file. But I wanted it to be possible to use a different one per tenant/site, so I implemented SiteNavigationOptionsResolver which first looks for a file named navigation.siteguid.xml where siteguid is the id of the current tenant/site for the request, if not found it just returns the default navigation.xml file.
So you could determine the section from the current url and return a different file as needed.
Using the core tenant example, I have implemented a custom IOptions<NavigationOptions>
and I injected like this:
services.AddScoped<NavOptionsResolver>();
But it is never called, do I need to do anything else?
You need to add it as singleton before calling .AddCloudscribeNavigation
services.AddSingleton<IOptions<NavigationOptions>, NavOptionsResolver>();
services.AddCloudscribeNavigation(configuration);
You probably also need to implement a custom ITreeCacheKeyResolver so you can cache the trees separately, you can see my custom implementation caches it separately per tenant/site by using a different cache key per tenant/site
services.AddScoped<ITreeCacheKeyResolver, YourImplementation>();
Thanks I did this:
public class RouteValueResourceFilter : IAsyncResourceFilter {
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) {
var value = context.RouteData.Values;
if(value != null) {
context.HttpContext.Items["RouteData"] = value;
}
await next();
}
}
public NavigationOptions Value {
get {
var context = httpContextAccessor.HttpContext;
var item = context.Items["RouteData"] as RouteValueDictionary;
string area = string.Empty;
if(item != null) {
area = item["area"] == null ? string.Empty : item["area"].ToString();
}
var options = new NavigationOptions();
var siteXmlFileName = "navigation." + area + ".xml";
var siteXmlNavigationFilePath = Path.Combine(environment.ContentRootPath, siteXmlFileName);
if(File.Exists(siteXmlNavigationFilePath)) {
options.NavigationMapXmlFileName = siteXmlFileName;
}
return options;
}
}
public string GetCacheKey(INavigationTreeBuilder builder) {
var context = httpContextAccessor.HttpContext;
var item = context.Items["RouteData"] as RouteValueDictionary;
string area = string.Empty;
if(item != null) {
area = item["area"] == null ? string.Empty : item["area"].ToString();
}
return string.IsNullOrEmpty(area) ? builder.Name : builder.Name + area;
}