The requirements are quite simple:
- Create a SharePoint Global Navigation which can be displayed in different languages
- The navigation nodes schould be displayed based on the user’s permissions
As there are many ways in SharePoint to create a navigation, we figured out the best way to fulfill our needs was to write some code within a feature eventhandler that automatically creates our navigation. But there are several – undocumented – tripwires you need to watch out for. Let’s have a look and talk them through step by step.
Use the PublishingWeb to create the Navigation if you want audience targeting to work
If you want audience targeting to work with your navigation you need to activate the publishing feature on your SiteCollection.
Then get the PublishingWeb
from the current web and continue your work on that.
using (SPSite site = new SPSite(currentWeb.Url))
using (SPWeb web = site.OpenWeb())
{
web.AllowUnsafeUpdates = true;
PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
CreateNavigationMenu(publishingWeb);
web.AllowUnsafeUpdates = false;
web.Update();
}
DO NOT DELETE ALL NAVIGATION NODES PROGRAMMATICALLY
If you want to be sure that all current navigation items are deleted, you can iterate through the GlobalNavigationNodes
and delete them. But STOP! If you delete them all, then the GlobalNavigationNodes
will always remain null
within this SiteCollection
. This seems to be a bug and the only solution I found was to delete the SiteCollection
and re-create it.
A different way to clear the navigation would be to at first create a new SPNavigationNodeCollection
, add it to the GlobalNavigationNodes
. After that delete the old ones.
In this scenario I assume the global Navigation is empty and just add my new nodes.
Create Navigation Nodes
Let’s start off with the code:
private static void CreateNavigationMenu(PublishingWeb pubWeb)
{
pubWeb.Navigation.InheritGlobal = false;
pubWeb.Navigation.OrderingMethod = OrderingMethod.Manual;
pubWeb.Update();
// Get the Gloal Navigation Nodes
SPNavigationNodeCollection topNav = pubWeb.Navigation.GlobalNavigationNodes;
SPNavigationNode node = new SPNavigationNode("My New Node", "$Resources: MyResource, MyNodeName", pubWeb.Url);
node = topNav.AddAsLast(node); // Add the node to initialize it
node.Properties["NodeType"] = "AuthoredLinkPlain"; //important!
node.Update();
SetTargetAudience(node, "My SharePoint Group");
pubWeb.Update();
}
There are a few tricky things within the code that are not quite that obvious.
Multi-Language: You can add a Resource-String to a SPNavigationNode
. For that you can use different Resource-Files and get multiple languages for your navigation nodes. You can then switch the language e.g. through the user’s preferences settings and the global menu will switch its language. Awesome! Didn’t know that actually works. (This is not available if you are configuring the global navigation via UI.)
Node Creation: If you create a SPNavigationNode
it is not really created. You need to add it to the node collection first to really have it initiallized with all its properties. So, don’t forget to add the node to the collection before setting the properties.
Node Types: It is important to set the node.Properties["NodeType"]
. There are different types you can add. I chose the „AuthoredLinkPlain
„. If you do not set this property the node will be added to the collection but it will not be displayed on the UI.
Target Audience: Now let’s look at the target audiencing more closely:
SetTargetAudience(node, "My SharePoint Group");
private static void SetTargetAudience(SPNavigationNode node, string group)
{
if (node.Properties.Contains("Audience"))
{
node.Properties["Audience"] = ";;;;"+group;
}
else
{
node.Properties.Add("Audience", group);
}
node.Update();
}
To add the target audience you need to set the property „Audience
“ to contain the group you want to target. If the property is not available, you need to add it. So far so good! The remarkable thing is that you need to add the 4 semicolon before the group. If you forget them, the group will be inserted into the property but it will not work correctly. Why? – I honestly do not know – but it works 😉
And that’s it. Deploy the feature and activate! A lot of moving parts here and I had some hard time figuring out all the details. I hope this helps a bit – happy coding!