cloudscribe/cloudscribe.Web.Navigation

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;
        }