Enterprise Keywords management in Office 365 via CSOM

Overview

SharePoint 2013 has introduced Microsoft.SharePoint.Client.Taxonomy namespace for metadata management via CSOM. Since Taxonomy API also allows to manage Keywords, i would like to demonstrate how to get/set Enterprise Keywords field values in this post.

Adding Enterprise Keywords column into List

The first example demonstrates how to add Enterprise Keywords column into List:

private static void AddTaxkeywordFieldInList(List list)
{
var ctx = list.Context as ClientContext;
var taxKeywordField = ctx.Site.RootWeb.Fields.GetByInternalNameOrTitle("TaxKeyword");
var taxKeywordFieldInList = list.Fields.Add(taxKeywordField);
ctx.ExecuteQuery();
}

Setting  Enterprise Keywords field value

The below example demonstrates how to set Enterprise Keywords field value:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Taxonomy;
namespace SharePoint.Client.Taxonomy
{
/// <summary>
/// Enterpise Keyword Manager
/// </summary>
public class KeywordsManager
{
/// <summary>
/// Set Enterprise Keyword Value
/// </summary>
/// <param name="item">List Item</param>
/// <param name="values">Keyword values</param>
public static void SetTaxKeywordValue(ListItem item,string[] values)
{
var ctx = item.Context;
var list = item.ParentList;
var field = list.Fields.GetByInternalNameOrTitle(TaxKeywordFieldName);
var taxKeywordField = ctx.CastTo<TaxonomyField>(field);
var keywords = values.Select(value => EnsureKeyword(taxKeywordField, value)).ToList();
taxKeywordField.SetFieldValueByValueCollection(item, new TaxonomyFieldValueCollection(ctx, GetTermsString(keywords), taxKeywordField));
}
/// <summary>
/// Ensure Keyword
/// </summary>
/// <param name="taxField"></param>
/// <param name="name"></param>
/// <returns></returns>
private static Term EnsureKeyword(TaxonomyField taxField, string name)
{
var ctx = taxField.Context;
var taxSession = TaxonomySession.GetTaxonomySession(ctx);
var termStore = taxSession.GetDefaultKeywordsTermStore();
var keywords = termStore.KeywordsTermSet.GetAllTerms();
var result = ctx.LoadQuery(keywords.Where(k => k.Name == name));
ctx.ExecuteQuery();
var keyword = result.FirstOrDefault();
if (keyword != null)
{
return keyword;
}
keyword = termStore.KeywordsTermSet.CreateTerm(name, DefaultLanguage, Guid.NewGuid());
ctx.Load(keyword);
ctx.ExecuteQuery();
return keyword;
}
/// <summary>
/// Retrieve formatted Term string
/// </summary>
/// <param name="term"></param>
/// <returns></returns>
private static string GetTermString(Term term)
{
return string.Format("-1;#{0}{1}{2}", term.Name, TaxonomyGuidLabelDelimiter,term.Id);
}
private static string GetTermsString(IEnumerable<Term> terms)
{
var termsString = terms.Select(GetTermString).ToList();
return string.Join(";#", termsString);
}
private const string TaxKeywordFieldName = "TaxKeyword";
private const int DefaultLanguage = 1033;
private const string TaxonomyGuidLabelDelimiter = "|";
}
}

view raw
KeywordsManager.cs
hosted with ❤ by GitHub

Key points:

  • The operation of setting Enterprise Keywords field value consists of two steps, first one to resolve Keyword in Managed Metadata service application (MMS), this what  EnsureKeyword method is intended for.  After Keyword has been retrieved or created if it not existed, the value of Enterprise Keyword field is set (TaxonomyFieldValueCollection type).
Usage
using (var ctx = new ClientContext(webUri))
{
ctx.Credentials = CreateSPOCredentials(userName, password);
var list = ctx.Web.Lists.GetByTitle(listTitle);
var item = list.GetItemById(itemId);
KeywordsManager.SetTaxKeywordValue(item,new []{"2013","2010"});
item.Update();
ctx.ExecuteQuery();
}
public static SharePointOnlineCredentials CreateSPOCredentials(string userName,string password)
{
var securePassword = new SecureString();
foreach (var ch in password)
{
securePassword.AppendChar(ch);
}
return new SharePointOnlineCredentials(userName, securePassword);
}

Results

DocumentsWithTaxKeyword TermStore_Keywords

Working with Discussions List via SharePoint Client Object Model

Overview

In this post  will be demonstrated how to perform a common CRUD operations when working with Discussions List via  Client Object Model (JavaScript CSOM) in SharePoint 2010/2013.

Create a Discussion

So, let’s get started with creating a Discussion item. SP.Utilities.Utility.createNewDiscussion method is used for creating a discussion item:

function createDiscussion(listTitle,properties,OnItemAdded,OnItemError)
{
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
context.load(list);
var discussionItem = SP.Utilities.Utility.createNewDiscussion(context, list, properties.Subject);
for(var propName in properties) {
if(propName == 'Subject') continue;
discussionItem.set_item(propName, properties[propName])
}
discussionItem.update();
context.load(discussionItem);
context.executeQueryAsync(
function() {
OnItemAdded(discussionItem);
},
OnItemError
);
}

view raw
createDiscussion.js
hosted with ❤ by GitHub

Load all Discussions

Since Discussion Content Type derives from Folder Content Type  we could utilize SP.CamlQuery.createAllFoldersQuery method to construct a query to retrieve all the discussions:

function getDiscussions(listTitle,OnItemsLoaded,OnError)
{
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
context.load(list);
var qry = SP.CamlQuery.createAllFoldersQuery();
var discussionItems = list.getItems(qry);
context.load(discussionItems);
context.executeQueryAsync(
function() {
OnItemsLoaded(discussionItems);
},
OnError
);
}

view raw
getDiscussions.js
hosted with ❤ by GitHub

Create a Message

The SP.Utilities.Utility.createNewDiscussionReply method is used to create a Message item (add reply to a discussion item):

function createMessage(discussionItem,properties,OnItemAdded,OnItemError)
{
var context = new SP.ClientContext.get_current();
var messageItem = SP.Utilities.Utility.createNewDiscussionReply(context, discussionItem);
for(var propName in properties) {
messageItem.set_item(propName, properties[propName])
}
messageItem.update();
context.executeQueryAsync(
function() {
OnItemAdded(messageItem);
},
OnItemError
);
}
function createMessages(discussionItem,messagesProperties,OnItemsAdded,OnItemsError)
{
var context = new SP.ClientContext.get_current();
var messageItems = [];
$.each(messagesProperties, function (i, properties) {
messageItems.push(SP.Utilities.Utility.createNewDiscussionReply(context, discussionItem));
for(var propName in properties) {
messageItems[i].set_item(propName, properties[propName])
}
messageItems[i].update();
});
context.executeQueryAsync(
function() {
OnItemsAdded(messageItems);
},
OnItemsError
);
}

view raw
createMessage.js
hosted with ❤ by GitHub

Load all Messages

Since message items  are contained within discussion container (Folder) , to identify messages by discussion we will use SPBuiltInFieldId.ParentFolderId field in CAML query:

function getMessages(listTitle,disscussionId,OnItemsLoaded,OnError)
{
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
context.load(list);
var qry = createAllMessagesByDisscussionIDQuery(disscussionId);
var messageItems = list.getItems(qry);
context.load(messageItems);
context.executeQueryAsync(
function() {
OnItemsLoaded(messageItems);
},
OnError
);
}
function createAllMessagesByDisscussionIDQuery(disscussionId) {
var qry = new SP.CamlQuery;
var viewXml = "<View Scope='Recursive'> \
<Query> \
<Where> \
<Eq> \
<FieldRef Name='ParentFolderId' /> \
<Value Type='Integer'>" + disscussionId + "</Value> \
</Eq> \
</Where> \
</Query> \
</View>";
qry.set_viewXml(viewXml);
return qry;
};

view raw
getMessages.js
hosted with ❤ by GitHub

Import Discussion List

And finally in order to see how to work with Discussions List in action,  let’s discuss one more example.  Suppose we need to import Discussions List content from an external source, let’s say from Stack Exchange. To access Stack Exchange content (Questions and Answers in our case) we will utilize Stack Exchange API:

function DiscussionBoardImporter()
{
this.sourceQuery = '/2.1/questions?order=desc&sort=votes&site=sharepoint&filter=!-.AG)sL*gPlV';
this.targetDiscussionsListName = 'Discussions List';
}
DiscussionBoardImporter.prototype = (function() {
var importData = function(data,discussionsListName)
{
$.each(data, function (i, question) {
var discussionProperties = {'Body': question.body,'Subject':question.title,'VoteNumber': question.score};
createDiscussion(discussionsListName,discussionProperties,
function(discussionItem){
console.log('Discussion ' + discussionItem.get_item('Title') + ' has been created successfully');
var messagesProperties = [];
$.each(question.answers, function (j, answer) {
messagesProperties.push({'Title': answer.title,'Body': answer.body});
});
createMessages(discussionItem,messagesProperties,
function(){
console.log('Messages have been created successfully');
},
function(sender,args){
console.log('Error occured while creating messages:' + args.get_message());
}
);
},
function(sender,args){
console.log('Error occured while creating disscussion:' + args.get_message());
});
});
};
var loadData = function(query,targetListName, loaded)
{
$.ajax({
url: 'http://api.stackexchange.com&#39; + query,
dataType: 'json',
success: function(data) {
loaded(data.items,targetListName);
}
});
};
return {
constructor:DiscussionBoardImporter,
import: function()
{
loadData(this.sourceQuery,this.targetDiscussionsListName,importData);
}
};
})();
var importer = new DiscussionBoardImporter();
importer.import();

How to populate a SharePoint List via CSOM

Overview

Populating a SharePoint List with data is  required for different purposes quite often. In this article I am going to demonstrate how to populate Contacts list with a fake data using CSOM.

Provision column to a List via SharePoint CSOM

We will utilize Contacts List for demonstration purposes. But before we  proceed, let’s add a Geolocation field to a list.

Since the Geolocation column is not available by default in SharePoint lists, it’s common practice to provision this field to a list programmatically. The code below demonstrates  how to add the Geolocation field  by using the SharePoint client object model:

function ProvisionField(fieldSchema, listTitle,OnSuccess,OnError)
{
var context = SP.ClientContext.get_current();
var list = context.get_web().get_lists().getByTitle(listTitle);
var fields = list.get_fields();
var fieldToAdd = fields.addFieldAsXml(fieldSchema, true, SP.AddFieldOptions.AddToDefaultContentType);
fieldToAdd.update();
context.load(fieldToAdd);
context.executeQueryAsync(OnSuccess(fieldToAdd),OnError);
}
function AddGeolocationFieldToContactsList()
{
ProvisionField("<Field Type='Geolocation' DisplayName='Location'/>","Contacts",
function(field){
console.log('Geolocation field has been added');
},
function(sender,args){
console.log('Error occured while adding Geolocation field:' + args.get_message());
})
}

Generate list data

For populating Contacts list we will utilize Faker.js JavaScript library that is intended for generating massive amounts of fake data, below is provided the complete code:

function AddListItem(listTitle,itemProps,OnItemAdded,OnItemError)
{
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
var itemCreateInfo = new SP.ListItemCreationInformation();
var listItem = list.addItem(itemCreateInfo);
for(var propName in itemProps) {
listItem.set_item(propName, itemProps[propName])
}
listItem.update();
context.load(listItem);
context.executeQueryAsync(
function() {
OnItemAdded(listItem);
},
OnItemError
);
}
function GenerateContacts()
{
var contactsCount = 108;
for(var i = 0; i < contactsCount; i++){
var contactEntry = CreateContactEntry();
AddListItem('Contacts',contactEntry,
function(contactItem){
console.log('Contact ' + contactItem.get_item('Title') + ' has been created successfully');
},
function(sender,args){
console.log('Error occured while creating contact:' + args.get_message());
});
}
}
function CreateContactEntry()
{
var contactCard = Faker.Helpers.createCard();
return {'Title': contactCard.username,
'FullName': contactCard.name,
'Email': contactCard.email,
'Company': contactCard.company.name,
'WorkPhone': contactCard.phone,
'WorkAddress': contactCard.address.streetA,
'WorkCity': contactCard.address.city,
'WorkZip': contactCard.address.zipcode,
'WorkCountry': contactCard.address.ukCountry,
'WebPage': 'http://&#39; + contactCard.website,
'Location': 'Point(' + contactCard.address.geo.lng + ' ' + contactCard.address.geo.lat + ')'};
}
var scriptbase = _spPageContextInfo.siteAbsoluteUrl + '/_layouts/15/';
$.getScript(scriptbase + 'SP.js',
function () {
$.getScript(_spPageContextInfo.siteAbsoluteUrl + '/SiteAssets/Faker.js', GenerateContacts);
}
);

view raw
GenerateListData.js
hosted with ❤ by GitHub

Results

After running the script the Contacts list will be populated with a fake data as demonstrated below.

Contacts default view

Contacts

Contacts map view

ContactsMap

References

Some tips and tricks of using SharePoint Client Object Model in PowerShell. Part 1

Overview

When it comes to using SharePoint 2010 Client Object Model (CSOM) we need to be ready for certain kind of  limitations  in PowerShell. First of all, it concerns the usage of Generics Methods, for the example ClientRuntimeContext.Load<T> method:

public void Load<T>(
T clientObject,
params Expression<Func<T, Object>>[] retrievals
)
where T : ClientObject

An attempt to call the method ClientRuntimeContext.Load<T> directly will result in the following error PSGenericMethods

This is a limitation of PowerShell  (V1, V2) AFIK. There are several options how to bypass this limitation but in this post I would like to concentrate only on one technique that was originally described in the post Invoking Generic Methods on Non-Generic Classes in PowerShell. The basic idea is to replace the call for ClientRuntimeContext.Load<T> method with the following one:

Function Invoke-LoadMethod() {
param(
$ClientObject = $(throw "Please provide an Client Object instance on which to invoke the generic method")
)
$ctx = $ClientObject.Context
$load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load")
$type = $ClientObject.GetType()
$clientObjectLoad = $load.MakeGenericMethod($type)
$clientObjectLoad.Invoke($ctx,@($ClientObject,$null))
}

For invoking a generic methods we utilize MethodInfo.MakeGenericMethod method. Below are demonstrated some examples of usage SharePoint 2010 Client Object Model (CSOM) in PowerShell.

Example: load Web client object

Let’s start with a simple example for loading Web Client Object:

Add-Type Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.dll'
Add-Type Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
function PrintWebProperties()
{
param(
[Parameter(Mandatory=$true)][string]$url,
[Parameter(Mandatory=$false)][System.Net.NetworkCredential]$credentials
)
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$ctx.Credentials = $credentials
$web = $ctx.Web
Invoke-LoadMethod ClientObject $web
$ctx.ExecuteQuery()
Write-Host "Web Properties:"
Write-Host "Title: $($web.Title)"
Write-Host "Url: $($web.ServerRelativeUrl)"
}
$credentials = New-Object System.Net.NetworkCredential('username', 'password','domain')
$url = 'http://contoso.intranet.com/'
PrintWebProperties $url $credentials

Example: create Wiki page via CSOM

The example below demonstrates how to create wiki page via CSOM.

C# version:

/// <summary>
/// Create Wiki page via CSOM
/// </summary>
/// <param name="webUrl"></param>
/// <param name="pageName"></param>
/// <param name="pageContent"></param>
public static void CreateWikiPage(string webUrl, string pageName,string pageContent)
{
const string templateRedirectionPageMarkup = "<%@ Page Inherits=\"Microsoft.SharePoint.Publishing.TemplateRedirectionPage,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c\" %> <%@ Reference VirtualPath=\"~TemplatePageUrl\" %> <%@ Reference VirtualPath=\"~masterurl/custom.master\" %>";
using (var ctx = new ClientContext(webUrl))
{
var wikiPages = ctx.Web.Lists.GetByTitle("Pages");
ctx.Load(wikiPages);
ctx.ExecuteQuery();
var file = new FileCreationInformation
{
Url = pageName,
Content = Encoding.UTF8.GetBytes(templateRedirectionPageMarkup),
Overwrite = true
};
var wikiFile = wikiPages.RootFolder.Files.Add(file);
ctx.Load(wikiFile);
ctx.ExecuteQuery();
var wikiPage = wikiFile.ListItemAllFields;
wikiPage["PublishingPageContent"] = pageContent;
wikiPage["PublishingPageLayout"] = "/_catalogs/masterpage/EnterpriseWiki.aspx, Basic Page";
wikiPage.Update();
ctx.ExecuteQuery();
}
}

view raw
CreateWikiCSOM.cs
hosted with ❤ by GitHub

PowerShell version:

Add-Type Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.dll'
Add-Type Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
function CreateWikiPage()
{
param(
[Parameter(Mandatory=$true)][string]$webUrl,
[Parameter(Mandatory=$false)][System.Net.NetworkCredential]$credentials,
[Parameter(Mandatory=$true)][string]$pageName,
[Parameter(Mandatory=$true)][string]$pageContent
)
$templateRedirectionPageMarkup = '<%@ Page Inherits="Microsoft.SharePoint.Publishing.TemplateRedirectionPage,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %> <%@ Reference VirtualPath="~TemplatePageUrl" %> <%@ Reference VirtualPath="~masterurl/custom.master" %>';
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
$ctx.Credentials = $credentials
$wikiPages = $ctx.Web.Lists.GetByTitle("Pages")
Invoke-LoadMethod ClientObject $wikiPages
$ctx.ExecuteQuery()
$file = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$file.Url = $pageName
$file.Content = [System.Text.Encoding]::UTF8.GetBytes($templateRedirectionPageMarkup)
$file.Overwrite = $true
$wikiFile = $wikiPages.RootFolder.Files.Add($file)
Invoke-LoadMethod ClientObject $wikiFile
$wikiPage = $wikiFile.ListItemAllFields
$wikiPage["PublishingPageContent"] = $pageContent
$wikiPage["PublishingPageLayout"] = "/_catalogs/masterpage/EnterpriseWiki.aspx, Basic Page"
$wikiPage.Update()
$ctx.ExecuteQuery();
}
$credentials = New-Object System.Net.NetworkCredential('username', 'password','domain')
$webUrl = 'http://contoso.intranet.com/knowledgebase/'
$pageName = 'MyFirstWikiPage.aspx'
$pageContent = '<h1>Welcome to the Knowledge Base!</h1>'
CreateWikiPage $webUrl $credentials $pageName $pageContent

view raw
CreateWikiPage.ps1
hosted with ❤ by GitHub

MyFirstWikiPage

Summary

In contrary to article Using PowerShell to Get Data from a SharePoint 2010 List that explains how to execute generic methods via inline C#  in PowerShell, this post demonstrates how to utilize Generics Methods in PowerShell natively.

References

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