Access and Manipulate Navigation Settings via SharePoint Client Object Model

Overview

SharePoint Server OM provides  PortalNavigation class that represents navigation for portal pages and other portal navigation objects. In turn the PublishingWeb class  is used to access the navigation settings (PortalNavigation type).

What about Client Object Model? Unfortunately it is not supported to access and manipulate Navigation settings via CSOM since there is no PublishingWeb Client Object  in SharePoint 2010. But still it possible to accomplish via Client Object Model.

Retrieving Web Client Object with Properties

PortalNavigation class uses internally SPWeb.AllProperties property to store Navigation settings and the same type also exist as a Client Object – Web.AllProperties

ClientObject provides a base class for objects on a remote client, the full list of  client objects available in SharePoint 2010 could be found on MSDN page.

When accessing client objects there are some differences that concern how object properties are initialized. According to the MSDN, in the server object model, when you return an SPWeb object, all of its properties become available for use in code. But to improve performance in the client object model, when you return a Web object (JavaScript: Web) certain properties are not included, and you must explicitly retrieve them.

The following example is aimed to demonstrate how to retrieve Web client object with it properties:

namespace Publishing.Navigation.Client.Samples
{
class PortalNavigation
{
/// <summary>
/// Read Web All Properties
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static PropertyValues LoadWebWithProperties(string url)
{
using (var clientContext = new ClientContext(url))
{
var web = clientContext.Web;
clientContext.Load(web,w => w.AllProperties);
clientContext.ExecuteQuery();
return web.AllProperties;
}
}
}
}

Access and manipulate Navigation settings via CSOM

The following class represents Client Object Model (CSOM) implementation of PortalNavigation class:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Microsoft.SharePoint.Client;
namespace SharePoint.Client.Publishing.Navigation
{
/// <summary>
/// Represents navigation for portal pages and other portal navigation objects
/// </summary>
class ClientPortalNavigation
{
public ClientPortalNavigation(Web web)
{
_web = web;
}
#region CRUD operations
private void EnsureLoaded()
{
if (!_web.IsObjectPropertyInstantiated("AllProperties"))
{
Context.Load(_web, w => w.AllProperties);
Context.ExecuteQuery();
}
}
public void SaveChanges()
{
_web.Update();
Context.ExecuteQuery();
}
#endregion
public void ExcludeFromNavigation(bool useGlobal, Guid item)
{
ModifyNavigationExclude(true, useGlobal, item);
}
public void IncludeInNavigation(bool useGlobal, Guid item)
{
ModifyNavigationExclude(false, useGlobal, item);
}
/// <summary>
/// Controls whether publishing pages in this site will be automatically included in global navigation.
/// </summary>
public bool GlobalIncludePages
{
get
{
return (GlobalIncludeTypes & NodeTypes.Page) == NodeTypes.Page;
}
set
{
GlobalIncludeTypes = !value ? GlobalIncludeTypes & ~NodeTypes.Page : GlobalIncludeTypes | NodeTypes.Page;
}
}
/// <summary>
/// Controls whether publishing pages in this site will be automatically included in current navigation.
/// </summary>
public bool CurrentIncludePages
{
get
{
return (CurrentIncludeTypes & NodeTypes.Page) == NodeTypes.Page;
}
set
{
CurrentIncludeTypes = !value ? CurrentIncludeTypes & ~NodeTypes.Page : CurrentIncludeTypes | NodeTypes.Page;
}
}
/// <summary>
/// Controls the ordering of navigation items owned by this site.
/// </summary>
public OrderingMethod OrderingMethod
{
get
{
return (OrderingMethod)GetProperty("__NavigationOrderingMethod", 2);
}
set
{
SetProperty("__NavigationOrderingMethod", (int)value);
}
}
/// <summary>
/// Controls the property to use when automatically sorting navigation items owned by this site.
/// </summary>
public AutomaticSortingMethod AutomaticSortingMethod
{
get
{
return (AutomaticSortingMethod)GetProperty("__NavigationAutomaticSortingMethod", 0);
}
set
{
SetProperty("__NavigationAutomaticSortingMethod", (int)value);
}
}
/// <summary>
/// Sorts items in a portal navigation in ascending alphanumeric order.
/// </summary>
public bool SortAscending
{
get
{
return GetProperty("__NavigationSortAscending", true);
}
set
{
SetProperty("__NavigationSortAscending", value);
}
}
/// <summary>
/// Controls whether sub-site's of this site will be automatically included in its global navigation.
/// </summary>
public bool GlobalIncludeSubSites
{
get
{
return (GlobalIncludeTypes & NodeTypes.Area) == NodeTypes.Area;
}
set
{
GlobalIncludeTypes = !value ? GlobalIncludeTypes & ~NodeTypes.Area : GlobalIncludeTypes | NodeTypes.Area;
}
}
public bool CurrentIncludeSubSites
{
get
{
return (CurrentIncludeTypes & NodeTypes.Area) == NodeTypes.Area;
}
set
{
CurrentIncludeTypes = !value ? CurrentIncludeTypes & ~NodeTypes.Area : CurrentIncludeTypes | NodeTypes.Area;
}
}
#region Private Properties
private NodeTypes CurrentIncludeTypes
{
get
{
return (NodeTypes)GetProperty("__CurrentNavigationIncludeTypes", 2);
}
set
{
SetProperty("__CurrentNavigationIncludeTypes", (int)value);
}
}
private NodeTypes GlobalIncludeTypes
{
get
{
return (NodeTypes)GetProperty("__GlobalNavigationIncludeTypes", 2);
}
set
{
SetProperty("__GlobalNavigationIncludeTypes", (int)value);
}
}
#endregion
#region Private methods
private void ModifyNavigationExclude(bool exclude, bool useGlobal, Guid item)
{
Dictionary<Guid, bool> excludes;
if (useGlobal)
{
if (_globalNavigationExcludes == null)
InitializeNavigationExcludes(true);
excludes = _globalNavigationExcludes;
}
else
{
if (_currentNavigationExcludes == null)
InitializeNavigationExcludes(false);
excludes = _currentNavigationExcludes;
}
excludes[item] = exclude;
SetProperty(useGlobal ? "__GlobalNavigationExcludes" : "__CurrentNavigationExcludes", ConcatenateGuidStrings(excludes));
}
internal static string ConcatenateGuidStrings(Dictionary<Guid, bool> excludes)
{
var guidStrings = new StringBuilder();
foreach (var exclude in excludes)
{
if (exclude.Value)
guidStrings.Append(exclude.Key + ";");
}
return guidStrings.ToString();
}
private void InitializeNavigationExcludes(bool useGlobal)
{
var excludesValue = GetProperty(useGlobal ? "__GlobalNavigationExcludes" : "__CurrentNavigationExcludes", "");
var excludeGuids = new string[0];
if (!string.IsNullOrEmpty(excludesValue))
excludeGuids = excludesValue.Split(new[] { ';' },StringSplitOptions.RemoveEmptyEntries);
Dictionary<Guid, bool> excludes;
if (useGlobal)
{
_globalNavigationExcludes = new Dictionary<Guid, bool>(excludeGuids.Length);
excludes = _globalNavigationExcludes;
}
else
{
_currentNavigationExcludes = new Dictionary<Guid, bool>(excludeGuids.Length);
excludes = _currentNavigationExcludes;
}
foreach (var g in excludeGuids)
{
if (!string.IsNullOrEmpty(g) && !g.Equals(true.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase))
excludes.Add(new Guid(g), true);
}
}
private T GetProperty<T>(string propName, T defaultPropValue)
{
EnsureLoaded();
if (!_web.AllProperties.FieldValues.ContainsKey(propName))
return defaultPropValue;
return (T)Convert.ChangeType(_web.AllProperties[propName], typeof(T));
}
private void SetProperty<T>(string propName, T propValue)
{
_web.AllProperties[propName] = propValue;
}
#endregion
private ClientRuntimeContext Context
{
get { return _web.Context; }
}
private readonly Web _web;
private Dictionary<Guid, bool> _globalNavigationExcludes;
private Dictionary<Guid, bool> _currentNavigationExcludes;
internal const string PropNameIncludePagesInNavigation = "__IncludePagesInNavigation";
}
#region NodeTypes
/// <summary>
/// Represents the various node types in Microsoft SharePoint Foundation.
/// </summary>
[Flags]
public enum NodeTypes
{
None = 0,
Area = 1,
Page = 2,
List = 4,
ListItem = 8,
PageLayout = 16,
Heading = 32,
AuthoredLinkToPage = 64,
AuthoredLinkToWeb = 128,
AuthoredLinkPlain = 256,
Custom = 512,
Error = 1024,
AuthoredLink = AuthoredLinkPlain | AuthoredLinkToWeb | AuthoredLinkToPage,
Default = AuthoredLink | Heading | Page | Area,
All = Default | Custom | PageLayout | ListItem | List,
}
#endregion
#region AutomaticSortingMethod
/// <summary>
/// Provides options that specify which property to use when automatically sorting navigation items.
/// </summary>
public enum AutomaticSortingMethod
{
Title,
CreatedDate,
LastModifiedDate,
}
#endregion
#region OrderingMethod
/// <summary>
/// Options that specify how navigation items are ordered.
/// </summary>
public enum OrderingMethod
{
Automatic,
ManualWithAutomaticPageSorting,
Manual,
}
#endregion
}

The following example  demonstrates how to print and update navigation settings using ClientPortalNavigation class :

namespace Publishing.Navigation.Client.Samples
{
class PortalNavigation
{
/// <summary>
/// Print Navigation settings
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static void PrintNavigationSettings(string url)
{
using (var clientContext = new ClientContext(url))
{
var navigation = new ClientPortalNavigation(clientContext.Web);
Console.WriteLine("CurrentIncludePages: {0}", navigation.CurrentIncludePages);
Console.WriteLine("GlobalIncludePages: {0}", navigation.GlobalIncludePages);
Console.WriteLine("OrderingMethod: {0}", navigation.OrderingMethod);
Console.WriteLine("AutomaticSortingMethod: {0}", navigation.AutomaticSortingMethod);
Console.WriteLine("SortAscending: {0}", navigation.SortAscending);
Console.WriteLine("GlobalIncludeSubSites: {0}", navigation.GlobalIncludeSubSites);
Console.WriteLine("CurrentIncludeSubSites: {0}", navigation.CurrentIncludeSubSites);
}
}
/// <summary>
/// Update Navigation settings
/// </summary>
/// <param name="url"></param>
public static void UpdateNavigationSettings(string url)
{
using (var clientContext = new ClientContext(url))
{
var navigation = new ClientPortalNavigation(clientContext.Web);
navigation.CurrentIncludePages = true;
navigation.GlobalIncludePages = false;
navigation.SaveChanges();
}
}
}
}

Summary

Since  Publishing API is not supported in Client Object Model of SharePoint 2010, there is no way to manage  the navigation settings for a publishing site the same way as in Server Object Model. But as was demonstrated in this article it could be accomplished via Web Client Object and Web.AllProperties property.

In turn, in SharePoint 2013 Microsoft.SharePoint.Client.Publishing.Navigation namespace was introduced, in particular WebNavigationSettings class for managing the navigation settings for a publishing site.

References

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.