Inconvenient programmatically working with Managed Navigation and anonymous users

InvalidOperationException caught in Visual Studio
SharePoint 2013 offers rich API for interacting with Managed Navigation. Unfortunately, some additional work is required if you need to use it with anonymous users.

Managed Navigation in SharePoint 2013

One of the new capabilities of SharePoint 2013 is Managed Navigation. Using the Managed Metadata Service it allows you to model the navigation and structure of your website decoupling it from its physical structure. The great benefit of using Managed Navigation is that it allows you to optimize your website for both visitors and content managers and that is simplifies the content management process.

Programmatically working with Managed Navigation

Managed Navigation just works out of the box what should be just fine for the most scenarios. In other cases however, when you need some additional capabilities, you might build a custom control to interact with Managed Navigation and render the navigation the way you want it.

When working programmatically with Managed Navigation all you need is a reference to the Microsoft.SharePoint.Publishing assembly. Optionally, if you need to interact with the underlying Taxonomy Terms, you will need a reference to the Microsoft.SharePoint.Taxonomy assembly as well. This is for example necessary if you need to access custom properties set on a Taxonomy Term, which aren’t exposed through the TaxonomySiteMapNode class.

You start working programmatically with Managed Navigation by getting a reference to the SiteMap Provider (either the default one using the SiteMap.Provider property or a specific one requested from the SiteMap.Providers collection). After that, you can retrieve the navigation nodes either by starting from the RootNode or the CurrentNode property. The returned nodes are of type SiteMapNode, but they can be casted to the TaxonomySiteMapNode type to give you access to the specific capabilities of Managed Navigation. Following is a simple code snippet that illustrates programmatically working with Managed Navigation:

SiteMapProvider provider = SiteMap.Provider;  
SiteMapNode currentNode = provider.CurrentNode;  
TaxonomySiteMapNode taxonomyNode = (TaxonomySiteMapNode)currentNode;  

So far everything works just as expected. What if however we wanted to get access to the underlying Term to read some of the custom properties it had configured? To do that we could use the following lines of code:

TaxonomySession taxonomySession = new TaxonomySession(SPContext.Current.Site);

string myProperty = null;  
if (taxonomyNode.NavigationTerm.GetAsEditable(taxonomySession).GetTaxonomyTerm().LocalCustomProperties.TryGetValue("MyProperty", out myProperty)) {  
    // do something with the property value
}
else {  
    // property not set
}

Using the NavigationTerm property we first get access to the underlying Navigation Term. Before we however will be able to access custom properties set on the underlying Taxonomy Term we need to convert it to an editable Navigation Term using the GetAsEditable(TaxonomySession) method. With this we are now able to access the underlying Taxonomy Term by calling the GetTaxonomyTerm() method. That gives us access to the Term object from which we can access the LocalCustomProperties property that contains all custom properties set on that Term.

The code snippet presented above will work just fine… unless the page is requested by an anonymous user.

Inconvenient programmatically working with Managed Navigation and anonymous users

If you try to execute the code snippet presented above as an anonymous user, you will get an InvalidOperationException with the “Term not found” message. The reason for this error is the fact, that by default anonymous users are not allowed to get Navigation Term as Editable Navigation Terms. So what are the available options to solve this issue?

Option #1: elevate permissions

The first thing that you could consider is elevating permissions for the purpose of this code snippet. This is a common solution to all kinds of permission-related issues with anonymous users in SharePoint.

Following is how the code snippet would look like:

Guid siteId = SPContext.Current.Site.ID;

SPSecurity.RunWithElevatedPrivileges(() => {  
    using (SPSite site = new SPSite(siteId)) {
        TaxonomySession taxonomySession = new TaxonomySession(site);

        string myProperty = null;
        if (taxonomyNode.NavigationTerm.GetAsEditable(taxonomySession).GetTaxonomyTerm().LocalCustomProperties.TryGetValue("MyProperty", out myProperty)) {
            // do something with the property value
        }
        else {
            // property not set
        }
    }
});

While this code would without a doubt do the job and work as expected it’s quite resource expensive to instantiate a new reference to SPSite each time you need to process a Navigation Term. And while you could keep the SPSite reference and reuse it for processing multiple Terms or apply some other kind of optimizations, it is still far from ideal. Luckily there is a less-intrusive way to solve the problem.

Option #2: allow for tagging

As mentioned before, by default anonymous users are not allowed to get Navigation Terms as Editable. That is by default, unless the Term Set has been made available for tagging.

The ‘Available for Tagging’ option checked in the Term Set settings

With this option enabled you will be able to run the code for all users without elevating privileges.

Summary

SharePoint 2013 offers a rich API that allows for programmatically interacting with Managed Navigation and building rich solutions. In some scenarios the code needs to run with elevated privileges in order to allow anonymous users to access the navigation objects. By allowing the Navigation Term Set to be used for tagging the need for elevating privileges can be avoided.

Comments

comments powered by Disqus