Set default page layout in SharePoint using CSOM

Oh Boy! After a long time I am writing a blog. Currently I am working on an Intranet Portal designed and developed on SharePoint 2013 On-Prem.

We use Office Dev PnP as a deployment strategy code. While deploying one of the Publishing Site, we missed to set the Default Page Layout and it went blank. Which resulted into error while Adding a new page, also the Page Layout and Site Template SettingsĀ was throwing error.

On the live environment I didn’t want to run the entire deployment script. So thought of finding some PowerShell code. I came across few blogs, but none of them worked smoothly but crafting out a script from all those references did the job for me.

So thought of posting this code for my reference and others as well. I have written this code in haste. People are free to correct me.

Try{
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll'
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Publishing.dll'
}
catch {
Throw "Unable to load SharePoint Client runtime"
}
Function Set-DefaultPageLayout
{
param
(
[string]$url,
[string]$defaultPageLayoutUrl
)
process
{
$Credentials = Get-Credential
#get Client Object
$context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$context.Credentials = $Credentials
#Load objects
$web = $context.Web
$allProps = $web.AllProperties
$context.Load($web)
$context.Load($context.Site.RootWeb)
$context.Load($allProps)
$context.ExecuteQuery()
Write-Host "Setting available page layouts and default page layout on " $web.Url -NoNewLine
$urlRelative = $context.Site.RootWeb.ServerRelativeUrl + "/" + $defaultPageLayoutUrl
if($context.Site.RootWeb.ServerRelativeUrl -eq "/")
{
$urlRelative = $context.Site.RootWeb.ServerRelativeUrl + $defaultPageLayoutUrl
}
$pageLayoutFile = $context.Site.RootWeb.GetFileByServerRelativeUrl($urlRelative)
$context.Load($pageLayoutFile)
$context.ExecuteQuery();
$listItemFields = $pageLayoutFile.ListItemAllFields
$context.Load($listItemFields)
$context.ExecuteQuery()
$pageGuid = $listItemFields.FieldValues["UniqueId"]
$xmlPageLayout = "<layout guid='{0}' url='{1}' />"
$xmlPageLayout = $xmlPageLayout.Replace("{0}", $pageGuid);
$xmlPageLayout = $xmlPageLayout.Replace("{1}", $defaultPageLayoutUrl);
$allProps["__DefaultPageLayout"] = $defaultPageLayoutXml
$web.Update()
$context.ExecuteQuery()
Write-Host " Done" -ForegroundColor Green
}
}

Large file upload in SharePoint Online with CSOM PowerShell

As a SharePoint developer one comes across many types of projects to work on. One of them is Migration of files from file system to SharePoint Document Libraries. The amount of migration data is always huge like 700GB or may be some TBs.

I am working on a similar project where I need to transfer the file system onto SharePoint Online Libraries. There can be numerous files whose size can be really large (100MB, 500MB or even 1.5GB). This was a big challenge for me.

I follow Microsoft’s OfficeDev Patterns and Practices. They have provided 4 options to upload large files in SharePoint Online. I referred an option which uploads file in slices.

Please refer OfficeDev PnP GitHub sample in detail at https://github.com/OfficeDev/PnP/tree/dev/Samples/Core.LargeFileUpload

PnP’s code is in C#, I have converted same function in PowerShell.

Try{
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll'
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
}
catch {
Write-Host $_.Exception.Message
Write-Host "No further parts of the migration will be completed"
}
Function UploadFileInSlice ($ctx, $libraryName, $fileName, $fileChunkSizeInMB) {
$fileChunkSizeInMB = 9
# Each sliced upload requires a unique ID.
$UploadId = [GUID]::NewGuid()
# Get the name of the file.
$UniqueFileName = [System.IO.Path]::GetFileName($fileName)
# Get the folder to upload into.
$Docs = $ctx.Web.Lists.GetByTitle($libraryName)
$ctx.Load($Docs)
$ctx.Load($Docs.RootFolder)
$ctx.ExecuteQuery()
# Get the information about the folder that will hold the file.
$ServerRelativeUrlOfRootFolder = $Docs.RootFolder.ServerRelativeUrl
# File object.
[Microsoft.SharePoint.Client.File] $upload
# Calculate block size in bytes.
$BlockSize = $fileChunkSizeInMB * 1024 * 1024
# Get the size of the file.
$FileSize = (Get-Item $fileName).length
if ($FileSize -le $BlockSize)
{
# Use regular approach.
$FileStream = New-Object IO.FileStream($fileName,[System.IO.FileMode]::Open)
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.ContentStream = $FileStream
$FileCreationInfo.URL = $UniqueFileName
$Upload = $Docs.RootFolder.Files.Add($FileCreationInfo)
$ctx.Load($Upload)
$ctx.ExecuteQuery()
return $Upload
}
else
{
# Use large file upload approach.
$BytesUploaded = $null
$Fs = $null
Try {
$Fs = [System.IO.File]::Open($fileName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$br = New-Object System.IO.BinaryReader($Fs)
$buffer = New-Object System.Byte[]($BlockSize)
$lastBuffer = $null
$fileoffset = 0
$totalBytesRead = 0
$bytesRead
$first = $true
$last = $false
# Read data from file system in blocks.
while(($bytesRead = $br.Read($buffer, 0, $buffer.Length)) -gt 0) {
$totalBytesRead = $totalBytesRead + $bytesRead
# You've reached the end of the file.
if($totalBytesRead -eq $FileSize) {
$last = $true
# Copy to a new buffer that has the correct size.
$lastBuffer = New-Object System.Byte[]($bytesRead)
[array]::Copy($buffer, 0, $lastBuffer, 0, $bytesRead)
}
If($first)
{
$ContentStream = New-Object System.IO.MemoryStream
# Add an empty file.
$fileInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$fileInfo.ContentStream = $ContentStream
$fileInfo.Url = $UniqueFileName
$fileInfo.Overwrite = $true
$Upload = $Docs.RootFolder.Files.Add($fileInfo)
$ctx.Load($Upload)
# Start upload by uploading the first slice.
$s = [System.IO.MemoryStream]::new($buffer)
# Call the start upload method on the first slice.
$BytesUploaded = $Upload.StartUpload($UploadId, $s)
$ctx.ExecuteQuery()
# fileoffset is the pointer where the next slice will be added.
$fileoffset = $BytesUploaded.Value
# You can only start the upload once.
$first = $false
}
Else
{
# Get a reference to your file.
$Upload = $ctx.Web.GetFileByServerRelativeUrl($Docs.RootFolder.ServerRelativeUrl + [System.IO.Path]::AltDirectorySeparatorChar + $UniqueFileName);
If($last) {
# Is this the last slice of data?
$s = [System.IO.MemoryStream]::new($lastBuffer)
# End sliced upload by calling FinishUpload.
$Upload = $Upload.FinishUpload($UploadId, $fileoffset, $s)
$ctx.ExecuteQuery()
Write-Host "File upload complete"
# Return the file object for the uploaded file.
return $Upload
}
else {
$s = [System.IO.MemoryStream]::new($buffer)
# Continue sliced upload.
$BytesUploaded = $Upload.ContinueUpload($UploadId, $fileoffset, $s)
$ctx.ExecuteQuery()
# Update fileoffset for the next slice.
$fileoffset = $BytesUploaded.Value
}
}
} #// while ((bytesRead = br.Read(buffer, 0, buffer.Length)) > 0)
}
Catch {
Write-Host $_.Exception.Message -ForegroundColor Red
}
Finally {
if ($Fs -ne $null)
{
$Fs.Dispose()
}
}
}
return $null
}
view raw LargeFileUpload.ps1 hosted with ❤ by GitHub

Usage

$credentials = Get-Credential
$SiteURL = "https://yoursite.sharepoint.com"
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL) 
$Context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($credentials.UserName, $credentials.Password)

$UpFile = UploadFileInSlice -ctx $Context -libraryName "YourLibName" -fileName "C:\LargeFiles\FileWithLargeSize.docx"

Change page layout of a SharePoint publishing page using CSOM PowerShell

If you are in a situation where you have already created a page and want to change the Page layout to another one, Also if you want the default welcome page of your Publishing Site to have a new branded page layout then I guess this function is helpful.

function ChangePageLayout()
{
param(
[Parameter(Mandatory=$true)][string]$siteurl,
[Parameter(Mandatory=$false)][System.Net.NetworkCredential]$credentials,
[Parameter(Mandatory=$false)][string]$PageName,
[Parameter(Mandatory=$false)][string]$PageLayoutName,
[Parameter(Mandatory=$false)][string]$PageLayoutDisplayName,
[Parameter(Mandatory=$false)][string]$Title,
[Parameter(Mandatory=$false)][bool]$isCustomPageLayout
)
try
{
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteurl)
$ctx.Credentials = $credentials
if($isCustomPageLayout -eq $false)
{
$PageLayoutName = "/_catalogs/masterpage/" + $PageLayoutName + "," + $PageLayoutDisplayName
}
else
{
#Here I have assumed that if its custom page layout, then it's placed inside some folder which is child to masterpage
#If that's not the case with you then you can use below line of code
#$PageLayoutName = "/_catalogs/masterpage/" + $PageLayoutName + ", " + $PageLayoutDisplayName
#
$PageLayoutName = "/_catalogs/masterpage/Custom Page Layouts/" + $PageLayoutName + ", " + $PageLayoutDisplayName
}
$Pages = $ctx.Web.Lists.GetByTitle('Pages')
$camlQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
$camlQuery.ViewXml = '<View><Query><Where><Eq><FieldRef Name="FileLeafRef" /><Value Type="Text">'+ $PageName +'</Value></Eq></Where></Query></View>'
$Page = $Pages.GetItems($camlQuery)
$ctx.Load($Page)
$ctx.ExecuteQuery()
$file = $Page.File
$ctx.Load($file)
$ctx.ExecuteQuery()
if ($file.CheckOutType -ne [Microsoft.SharePoint.Client.CheckOutType]::None) {
$file.UndoCheckOut()
$ctx.Load($file)
$ctx.ExecuteQuery()
}
$file.CheckOut()
$ctx.Load($file)
$ctx.ExecuteQuery()
$Page.Set_Item("PublishingPageLayout", $PageLayoutName)
$Page.Set_Item("Title", $Title)
$Page.Update()
$Page.File.CheckIn("", [Microsoft.SharePoint.Client.CheckinType]::MajorCheckIn)
$Page.File.Publish("")
#check for approval
$ctx.Load($Pages)
$ctx.ExecuteQuery()
if ($Pages.EnableModeration -eq $true) {
$Page.File.Approve("")
}
$ctx.Load($Page)
$ctx.ExecuteQuery()
Write-Host "Update Page Layout Complete"
Write-Host ""
}
catch
{
Write-Host ("Error while changing page layout. Error -->> " + $_.Exception.Message) -ForegroundColor Red
}
}
view raw ChangePageLayout.ps1 hosted with ❤ by GitHub

Usage:

$credentials = Get-Credential
ChangePageLayout "http:yoursite.com" $credentials "default.aspx" "YourCustomLayout.aspx" "Your Custom Page Layout" "Some Title" $true

SharePoint 2013 Office365 find source of a record in record centre using Document ID service

Yet again a QUEST is solved. How can we know the Source of a record in a library in Record Centre? I must admit that I was not the only one to solve it, my colleague and senior Nikhil Patel has huge contribution into it.

Platform: Office 365 (But I believe this should work in SharePoint 2013 on premise as well)

Scenario: There are more than 50 site collections and Record Centre is one of them. From the Site collections we are dumping documents as records in Record Centre. And in the view of a library in Record Centre we would like to know the source of the record(i.e. from which site collection it has come from).

Approach:

To understand the approach let us assume a normal site collection and a record centre site collection. And From a normal site collection we will be sending documents to record centre site collection.

  • Things to do at Site collection: First we need to activate the Document ID service feature. Goto site settings > Site Collection Features > Activate Document ID Service
  • Update Document ID settings: We can set the prefix of the Document ID generated for each site collection to maintain uniqueness. Goto Site Settings > Site Collection Administration > Document ID Settings

Document ID settings

Performing above steps when ever a document is uploaded it will have a unique Document Id. Refer below screen shot.

Record Library

If we try to break the Id N3SITE-424387581-13 we can notice that

1. N3SITE: is the prefix which we have in Document ID settings.
2. 424387581: Is the random number generated by SharePoint.
3. 13: is nothing but the normal ID of a document.

N3SITE and 13 are quite easy to understand and they are in our control to identify which site collection. But 424387581 puzzle us, and also from site collection which library it belongs we would also like to know that.

424387581 is nothing but the unique Id given to a document library. but from where we got to know it?

Cracking down curious case of 424387581: Open your office 365 site collection in SharePoint Designer. From ribbon control click on Site Options which is a property bag. Find the property docid_msft_hier_listidx, this contains the unique id for all the document libraries.

Note: The entry is placed only when a document is uploaded in a Document Library and not before that.

Property Bag

	<?xml version="1.0" encoding="utf-16"?>
	<DictionarySerializer>
	<dictionary>
		<item>
		<key>424387581</key>
		<webGuid>d4317398-252a-4ae2-9f09-90012f992554</webGuid>
		<listGuid>775632c2-489b-4cf6-876c-5947a38170c5</listGuid>
		</item>
	</dictionary>
	</DictionarySerializer>

This solves our problem of identifying the Document Source.

On Record Centre: On a view of a record library which contains records having Document ID, we can place a link which will open a new window passing Document ID in query string.

and on that window we can write our own logic first to break down the Document ID to get the prefix and identify which Site Collection is it. And second query that site collection’s property bag using JSOM and check 424387581 belongs to which document library.

Create Content Organizer Rule using Client Object Model CSOM

Well it took a bit of a time to figure out how I can create content organizer rule with CSOM as I dint wanna create some 100 manually on Office 365. But finally the QUEST is complete now and I have a wonderful csom c# and Powershell code to do the job.

What’s the trick: If we navigate to Content Organizer Rules from site settings, We can notice that its nothing but a list and all the Rules are stored as list items. Thankfully we have CSOM library available which allows us to save a list item using either c# or PowerShell. So the trick is to create ‘Rules’ as an item.

What to know: Now as we know that we create Rules as an item, But the big question remains what to store and which field/columns to set so that a proper well functioned Rule can be created.

Think Out of Box: Create a Content Organizer Rule through SharePoint UI. Now since its a list we can query it and get the values of all the fields. This is how we know SharePoint use which field/column and how are the values are stored, for example how SharePoint stores the condition.

Solution:—————–

First thing first: Get the value of a Rule which is added via SharePoint UI so that we get an idea which fields to store in and what type of values it needs.

CSOM c# code to get the Item:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint.Client;
namespace RecordCentreRule
{
class Program
{
static void Main(string[] args)
{
var targetSite = new Uri("https://YourSite.sharepoint.com");
var login = "SomeOne@SharePoint.onmicrosoft.com";
var password = "YourPassword";
var securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
var onlineCredentials = new SharePointOnlineCredentials(login, securePassword);
using (ClientContext clientCtx = new ClientContext(targetSite))
{
clientCtx.Credentials = onlineCredentials;
Web web = clientCtx.Web;
List routingRulesList = web.Lists.GetByTitle("Content Organizer Rules");
ListItem itm = routingRulesList.GetItemById(1);
clientCtx.Load(web);
clientCtx.Load(routingRulesList);
clientCtx.Load(itm);
clientCtx.ExecuteQuery();
Console.WriteLine("Title: " + itm["Title"] + "\n");
Console.WriteLine("RoutingConditions: " + itm["RoutingConditions"] + "\n");
Console.WriteLine("RoutingConditionProperties: " + itm["RoutingConditionProperties"] + "\n");
Console.WriteLine("RoutingContentType: " + itm["RoutingContentType"] + "\n");
Console.WriteLine("RoutingContentTypeInternal: " + itm["RoutingContentTypeInternal"] + "\n");
Console.WriteLine("RoutingConditions: " + itm["RoutingConditions"] + "\n");
Console.WriteLine("RoutingConditionProperties: " + itm["RoutingConditionProperties"] + "\n");
Console.WriteLine("RoutingAliases: " + itm["RoutingAliases"] + "\n");
Console.WriteLine("RoutingTargetLibrary: " + itm["RoutingTargetLibrary"] + "\n");
Console.WriteLine("RoutingTargetFolder: " + itm["RoutingTargetFolder"] + "\n");
Console.WriteLine("RoutingTargetPath: " + itm["RoutingTargetPath"] + "\n");
Console.WriteLine("RoutingAutoFolderProp: " + itm["RoutingAutoFolderProp"] + "\n");
Console.WriteLine("RoutingAutoFolderSettings: " + itm["RoutingAutoFolderSettings"] + "\n");
Console.WriteLine("RoutingCustomRouter: " + itm["RoutingCustomRouter"] + "\n");
Console.WriteLine("RoutingRuleExternal: " + itm["RoutingRuleExternal"] + "\n");
Console.Read();
}
}
}
}

CSOM PowerShell Code:

# replace these details (also consider using Get-Credential to enter password securely as script runs)..
$username = "SomeOne@SharePoint.onmicrosoft.com"
$password = "YourPassword"
$url = "https://YourSite.sharepoint.com"
$securePassword = ConvertTo-SecureString $Password -AsPlainText -Force
# the path here may need to change if you used e.g. C:\Lib..
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
# connect/authenticate to SharePoint Online and get ClientContext object..
$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword)
$clientContext.Credentials = $credentials
if (!$clientContext.ServerObjectIsNull.Value)
{
Write-Host "Connected to SharePoint Online site: '$Url'" -ForegroundColor Green
$web = $clientContext.Site.RootWeb
$listRoutingRules = $web.Lists.GetByTitle("Content Organizer Rules")
$item = $listRoutingRules.GetItemById(1)
$clientContext.Load($listRoutingRules)
$clientContext.Load($item)
$clientContext.ExecuteQuery()
Write-Host "Title: " $item["Title"]
Write-Host "RoutingConditions: " $item["RoutingConditions"]
Write-Host "RoutingConditionProperties: " $item["RoutingConditionProperties"]
Write-Host "RoutingContentType: " $item["RoutingContentType"]
Write-Host "RoutingContentTypeInternal: " $item["RoutingContentTypeInternal"]
Write-Host "RoutingConditions: " $item["RoutingConditions"]
Write-Host "RoutingConditionProperties: " $item["RoutingConditionProperties"]
Write-Host "RoutingAliases: " $item["RoutingAliases"]
Write-Host "RoutingTargetLibrary: " $item["RoutingTargetLibrary"]
Write-Host "RoutingTargetFolder: " $item["RoutingTargetFolder"]
Write-Host "RoutingTargetPath: " $item["RoutingTargetPath"]
Write-Host "RoutingAutoFolderProp: " $item["RoutingAutoFolderProp"]
Write-Host "RoutingAutoFolderSettings: " $item["RoutingAutoFolderSettings"]
Write-Host "RoutingCustomRouter: " $item["RoutingCustomRouter"]
Write-Host "RoutingRuleExternal: " $item["RoutingRuleExternal"]
}

So this is how we know what and How values are stored in a Rule.

Second Step would be just create a new item (Rule) with our values as inspired by above results.

CSOM c# code to create a Rule:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint.Client;
namespace RecordCentreRule
{
class Program
{
static void Main(string[] args)
{
var targetSite = new Uri("https://YourSite.sharepoint.com");
var login = "SomeOne@SharePoint.onmicrosoft.com";
var password = "YourPassword";
var securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
var onlineCredentials = new SharePointOnlineCredentials(login, securePassword);
using (ClientContext clientCtx = new ClientContext(targetSite))
{
clientCtx.Credentials = onlineCredentials;
Web web = clientCtx.Web;
List routingRulesList = web.Lists.GetByTitle("Content Organizer Rules");
clientCtx.Load(routingRulesList);
clientCtx.ExecuteQuery();
ListItemCreationInformation routingRuleInfo = new ListItemCreationInformation();
ListItem routingRule = routingRulesList.AddItem(routingRuleInfo);
routingRule["Title"] = "From Console";
routingRule["RoutingRuleName"] = "From Console";
routingRule["RoutingRuleDescription"] = "From Console";
routingRule["RoutingPriority"] = 1;
routingRule["RoutingEnabled"] = true;
routingRule["RoutingContentType"] = "Your Content Type Name";
routingRule["RoutingContentTypeInternal"] = "0x000000000000000000000000000000000000000000000000000000|Your Content Type Name";
routingRule["RoutingConditions"] = "<Conditions><Condition Column=\"xxxx-xxx-xxx-xxx-xxxxx|Column|Column Name\" Operator=\"EqualsOrIsAChildOf\" Value=\"1;#WhatEver|xxxx-xxx-xxx-xxx-xxxx\" /></Conditions>";
routingRule["RoutingConditionProperties"] = "Column Name on which you need condition";
routingRule["RoutingAliases"] = "Your Content Type Name";
routingRule["RoutingTargetLibrary"] = "Target Library";
routingRule["RoutingTargetFolder"] = "";
routingRule["RoutingTargetPath"] = "/sites/YourSite/Target Library";
routingRule["RoutingAutoFolderProp"] = "Folder Property";
routingRule["RoutingAutoFolderSettings"] = "<AutoFolder><Properties><Property Name=\"AutoFolderEnabled\" Value=\"True\" /><Property Name=\"AutoFolderPropertyName\" Value=\"Folder Property\" /><Property Name=\"AutoFolderPropertyInternalName\" Value=\"WhatEver\" /><Property Name=\"AutoFolderPropertyID\" Value=\"xxxx-xxxx-xxx-xxx-xxxx\" /><Property Name=\"AutoFolderPropertyFormat\" Value=\"%1 - %2\" /><Property Name=\"AutoFolderPropertyTypeAsString\" Value=\"TaxonomyFieldType\" /><Property Name=\"AutoFolderPropertyTermStore\" Value=\"xxxx-xxx-xxx-xxx-xxxxx\" /></Properties></AutoFolder>";
routingRule["RoutingCustomRouter"] = "";
routingRule["RoutingRuleExternal"] = false;
routingRule.Update();
clientCtx.ExecuteQuery();
Console.WriteLine("Rule created successfully");
Console.Read();
}
}
}
}

CSOM PowerShell to create Rule:

# replace these details (also consider using Get-Credential to enter password securely as script runs)..
$username = "SomeOne@SharePoint.onmicrosoft.com"
$password = "YourPassword"
$url = "https://YourSite.sharepoint.com"
$securePassword = ConvertTo-SecureString $Password -AsPlainText -Force
# the path here may need to change if you used e.g. C:\Lib..
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
# connect/authenticate to SharePoint Online and get ClientContext object..
$clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword)
$clientContext.Credentials = $credentials
if (!$clientContext.ServerObjectIsNull.Value)
{
Write-Host "Connected to SharePoint Online site: '$Url'" -ForegroundColor Green
$web = $clientContext.Site.RootWeb
$listRoutingRules = $web.Lists.GetByTitle("Content Organizer Rules")#RoutingRules
$clientContext.Load($listRoutingRules)
$clientContext.ExecuteQuery()
#Add an item to the list
$ListItemInfo = New-Object Microsoft.SharePoint.Client.ListItemCreationInformation
$Item1 = $listRoutingRules.AddItem($ListItemInfo)
$Item1["Title"] = "Asad Test Rule"
$Item1["RoutingRuleName"] = "Asad Test Rule"
$Item1["RoutingRuleDescription"] = "Asad Test Rule"
$Item1["RoutingPriority"] = 1
$Item1["RoutingEnabled"] = $true
$Item1["RoutingContentType"] = "Your Content Type Name"
$Item1["RoutingContentTypeInternal"] = "0x000000000000000000000000000000000000000000000000000000|Your Content Type Name"
$Item1["RoutingConditions"] = '<Conditions><Condition Column="xxxx-xxx-xxx-xxx-xxxxxx|ColumnName|Column Name" Operator="EqualsOrIsAChildOf" Value="1;#Value|xxxx-xxx-xxx-xxx-xxxxxx" /></Conditions>'
$Item1["RoutingConditionProperties"] = "Condition Property"
$Item1["RoutingAliases"] = "Alias Name"
$Item1["RoutingTargetLibrary"] = "Target Library"
$Item1["RoutingTargetFolder"] = ""
$Item1["RoutingTargetPath"] = "/sites/YourSite/Target Library"
$Item1["RoutingAutoFolderProp"] = "Folder Property"
$Item1["RoutingAutoFolderSettings"] = '<AutoFolder><Properties><Property Name="AutoFolderEnabled" Value="True" /><Property Name="AutoFolderPropertyName" Value="Your Value" /><Property Name="AutoFolderPropertyInternalName" Value="YourValue" /><Property Name="AutoFolderPropertyID" Value="xxxx-xxx-xxx-xx-xxxxx" /><Property Name="AutoFolderPropertyFormat" Value="%1 - %2" /><Property Name="AutoFolderPropertyTypeAsString" Value="TaxonomyFieldType" /><Property Name="AutoFolderPropertyTermStore" Value="xxxx-xxx-xxx-xxx-xxxxxx" /></Properties></AutoFolder>'
$Item1["RoutingCustomRouter"] = ""
$Item1["RoutingRuleExternal"] = $false
$Item1.Update()
$clientContext.ExecuteQuery()
Write-Host "Rule created successfully" -ForegroundColor Green
}

This is how we create rules using CSOM.

Set unique permissions to a SharePoint Group in SharePoint list using CSOM PowerShell

A SharePoint list when created inherits site’s permission, but as we move ahead in project and realise to maintain some unique permissions on the list, then below PowerShell function which uses client object model can be very helpful

For example:

There is a List called SUGGESTIONS and you would want even users from VISITORS group to contribute in it, then with the help of this function we can give contributors role to the users from VISITORS group.

function Set-PermissionsOnList()
{
param(
[Parameter(Mandatory=$true)][string]$url,
[Parameter(Mandatory=$true)][System.Net.NetworkCredential]$credentials,
[Parameter(Mandatory=$true)][string]$listName,
[Parameter(Mandatory=$true)][string]$GroupName,
[Parameter(Mandatory=$true)][string]$Roletype
)
begin{
try
{
#get Client Object
$context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$context.Credentials = $credentials
$web = $context.Web
$context.Load($web)
$context.ExecuteQuery()
# get root web
$RootWeb = $context.Site.RootWeb
$context.Load($RootWeb)
$context.ExecuteQuery()
# get list
#$lists = $web.Lists
#$context.Load($lists)
#$context.ExecuteQuery()
#$list = $lists | where {$_.Title -eq $listName}
#Retrieve List
$List = $context.Web.Lists.GetByTitle($listName)
$context.Load($List)
$context.ExecuteQuery()
$list.BreakRoleInheritance($true, $false)
$roleType = [Microsoft.SharePoint.Client.RoleType]$Roletype
# get group/principal
$groups = $web.SiteGroups
$context.Load($groups)
$context.ExecuteQuery()
$group = $groups | where {$_.Title -eq $RootWeb.Title + " " + $GroupName}
# get role definition
$roleDefs = $web.RoleDefinitions
$context.Load($roleDefs)
$context.ExecuteQuery()
$roleDef = $roleDefs | where {$_.RoleTypeKind -eq $Roletype}
}
catch
{
Write-Host "Error while getting context. Error -->> " + $_.Exception.Message -ForegroundColor Red
}
}
process{
try
{
# Assign permissions
$collRdb = new-object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($context)
$collRdb.Add($roleDef)
$collRoleAssign = $list.RoleAssignments
$rollAssign = $collRoleAssign.Add($group, $collRdb)
$context.ExecuteQuery()
Write-Host "Permissions assigned successfully." -ForegroundColor Green
}
catch
{
Write-Host "Error while setting permissions. Error -->> " + $_.Exception.Message -ForegroundColor Red
}
}
end{
$context.Dispose()
}
}

The above function can be called this way

$credentials = Get-Credential
$ListTitle = "Your List Name"
Set-PermissionsOnList "http://YourSite" $credentials $ListTitle "Visitors" "Contributor"

Customize SharePoint 2013 suite bar

The unbranded SharePoint 2013 site contains a SuiteBar which again contains a branding text on top left corner and number of links on top right corner.
Once we start branding our site we would like to get rid of the branding text, and may be add one more link on top right corner.

Customizing Branding Text using jQuery:

OOB branding text looks something like below screen shot.

UnBarndedSuiteBar

And if we study the source of it using developer tool we can see an html structure like below screen shot.

SourceOfUnbrandedSuiteBar

So to change the branding text below jQuery code can work wonders.

If we just need a simple text change then

jQuery('div#suiteBarLeft .ms-core-brandingText').text("Hello World");

Or if we need to add some html then

jQuery('div#suiteBarLeft .ms-core-brandingText').html("<span>Hello World</span> <img src='your img path'>");

The result of above JavaScript code will be

CustomizedSuitBar

Customizing SuiteBar Links:

OOB branded site has 3 links as shown in below screen shot.

UnBrandedSuiteBarLinks

The html source of these links looks something like below.

SourceOfUnBrandedSuiteBarLinks

So to add one more link below code can be helpful.

var customLi = "<li class='ms-core-suiteLink'><a class='ms-core-suiteLink-a' target='_blank' href='www.yourlink.com'>Your Custom Link</a></li>";
if(jQuery("div#suiteLinksBox").children("ul").length > 0){
      	jQuery("div#suiteLinksBox").children("ul").append(customLi);
     	
}
else {
      	jQuery("div#suiteLinksBox").html('<ul class="ms-core-suiteLinkList">' + customLi + '</ul>')
}

The result of above code will be

CustomizedSuiteBarLink

Send email in SharePoint 2013 using JavaScript

Following is the function which can used to send an email in SharePoint 2013 using JavaScript

function sendEmail(from, to, body, subject) {
    //Get the relative url of the site
    var siteurl = _spPageContextInfo.webServerRelativeUrl;
    var urlTemplate = siteurl + "/_api/SP.Utilities.Utility.SendEmail";
    $.ajax({
        contentType: 'application/json',
        url: urlTemplate,
        type: "POST",
        data: JSON.stringify({
            'properties': {
                '__metadata': {
                    'type': 'SP.Utilities.EmailProperties'
                },
                'From': from,
                'To': {
                    'results': [to]
                },
                'Body': body,
                'Subject': subject
            }
        }),
        headers: {
            "Accept": "application/json;odata=verbose",
            "content-type": "application/json;odata=verbose",
            "X-RequestDigest": jQuery("#__REQUESTDIGEST").val()
        },
        success: function(data) {
            alert('Email Sent Successfully');
        },
        error: function(err) {
            alert('Error in sending Email: ' + JSON.stringify(err));
        }
    });
}

Reorder content types in SharePoint list csom PowerShell

A SharePoint list can be associated to more than one content types, often there are situations where we would want to change the order they appear in new button. Content type which appears at first position will become the default content type for the list.

We can achieve this reordering of content types with the help of this small and simple csom powershell function

#Load SharePoint client assemblies
#
Try{
    Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll'
    Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
    Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Publishing.dll'
    Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Taxonomy.dll'
}

catch {
	Throw "Unable to load SharePoint Client runtime"
}


function Reorder-ContentTypesInList()
{
    param(
        [Parameter(Mandatory=$true)][string]$url,
        [Parameter(Mandatory=$true)][System.Net.NetworkCredential]$credentials,
        [Parameter(Mandatory=$true)][string]$listName,
        [Parameter(Mandatory=$true)][string[]]$ContentTypeNamesInOrder
    )
    
    begin{
        try
        {
            #get Client Object
			#
            $context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
            $context.Credentials = $credentials

            #Retrieve List
			#
            $list = $context.Web.Lists.GetByTitle($listName)
            $context.Load($list)
            $context.ExecuteQuery()


            #Get Content Types from a list
			#
            $contentTypes = $list.ContentTypes
            $context.Load($contentTypes)
            $context.ExecuteQuery()
        }
        catch{
            Write-Host "Error while getting context. Error -->> "  + $_.Exception.Message -ForegroundColor Red
        }
    }
    process{
        try
        {
            
            #Making generic list of content type ids in passed order
			#
            $ctList = New-Object System.Collections.Generic.List[Microsoft.SharePoint.Client.ContentTypeId]
            Foreach($ct in $ContentTypeNamesInOrder){
                $ctToInclude = $contentTypes | Where {$_.Name -eq $ct}
                $ctList.Add($ctToInclude.Id)
            }


            #Updating content type order
			#
            $list.RootFolder.UniqueContentTypeOrder = $ctList
            $list.Update()
            $context.Load($list)
            $context.ExecuteQuery()
            
            Write-Host "Content Types Reordered successfully" -ForegroundColor Green

        }
        catch
        {
            Write-Host ("Error while reordering content types in a list. Error -->> " + $_.Exception.Message) -ForegroundColor Red
        }
    }
    end{
        $context.Dispose()
    }
}

$credentials = Get-Credential 
$ContentTypesInOrder = "Content Type 1", "Content Type 2", "Content Type 3"
Reorder-ContentTypesInList 'http://YourSite' $credentials 'Your List Name' $ContentTypesInOrder

Add field into content type SharePoint 2013 using csom PowerShell

Once everything is deployed on production environment, and during implementing updates many a times we come across situations to add a column into existing content type. Instead of doing it manually below client side PowerShell function can be extremely handy

Try{
#Loading SharePoint Client side namespaces
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll'
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Publishing.dll'
Add-Type -Path 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Taxonomy.dll'
}
catch {
Throw "Unable to load SharePoint Client runtime"
}
function AddField-InContentType()
{
param(
[Parameter(Mandatory=$true)][string]$url,
[Parameter(Mandatory=$false)][System.Net.NetworkCredential]$credentials,
[Parameter(Mandatory=$true)][string]$ContentTypeName,
[Parameter(Mandatory=$true)][string]$FieldInternalName
)
begin{
try
{
#Get Client Object
#
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$Context.Credentials = $Credentials
#Load web, fields and site objects
#
$web = $Context.Web
$fields = $Context.Site.RootWeb.Fields;
$site = $context.Site
$Context.Load($web)
$Context.Load($site)
$Context.Load($fields)
$Context.ExecuteQuery()
}
catch
{
Write-Host "Error while getting context. Error -->> " + $_.Exception.Message -ForegroundColor Red
}
}
process{
try
{
#Check whether site column exists in site collection columns
#
Write-Host "Check if the site column" $FieldInternalName "is exist or not" -ForegroundColor Cyan
try
{
$field = $fields.GetByInternalNameOrTitle($FieldInternalName)
$Context.Load($field)
$Context.ExecuteQuery()
Write-Host "Site column" $FieldInternalName "exist" -ForegroundColor Cyan
}
catch
{
Write-Host "Site column" $FieldInternalName " does not exist in the list of site collection columns" -ForegroundColor Cyan
return
}
#Getting content types from the root site
#
$contentTypes = $Context.Site.RootWeb.ContentTypes
$Context.Load($contentTypes)
$Context.ExecuteQuery()
#Getting specified content type in which we need to add a column
#
$contentType = $contentTypes | Where {$_.Name -eq $ContentTypeName}
#Get Fields from the content type
#
$fieldCollection = $contentType.Fields
$Context.Load($fieldCollection);
$Context.ExecuteQuery();
#Check if field already exists in content type or not
#
$IsFieldExists = $false
foreach($fld in $fieldCollection)
{
Write-Host $fld.get_InternalName()
if($fld.get_InternalName() -eq $FieldInternalName){
$IsFieldExists = $true
}
}
if($IsFieldExists -eq $false)
{
#Get all the columns associated with content type
#
$fieldRefCollection = $contentType.FieldLinks;
$Context.Load($fieldRefCollection);
$Context.ExecuteQuery();
#Add a new column to the content type
#
Write-Host "Adding site column " $FieldInternalName "to the content type" -ForegroundColor Cyan
$fieldReferenceLink = New-Object Microsoft.SharePoint.Client.FieldLinkCreationInformation
$fieldReferenceLink.Field = $field;
$contentType.FieldLinks.Add($fieldReferenceLink);
$contentType.Update($true)
$Context.Load($contentType)
$Context.ExecuteQuery()
Write-Host "Field added successfully" -ForegroundColor Green
}
else
{
Write-Host "Site Column " $FieldInternalName " already exists in content type" -ForegroundColor Cyan
}
}
catch
{
Write-Host ("Error while adding field in contet type. Error -->> " + $_.Exception.Message) -ForegroundColor Red
}
}
end{
$Context.Dispose()
}
}
$credentials = Get-Credential
$Url = 'http://YourSite.com'
AddField-InContentType $Url $credentials "Your Content Type Name" "YourSiteColumn"