Tag Archives: CSOM
VIDEO – Toggle Required Fields OFF
When making bulk data changes to SharePoint List or Library you may need to temporarily turn off Required fields. Opening this can allow us to import records, apply changes, and modify metadata without prompting that required fields are missing. PowerShell below can help disable all Required fields OFF. Script generates a CSV snapshot of prior configuration during run. CSV can be used to restore original configuration for which fields are Required.
VIDEO
PowerShell Code
[CmdletBinding()]
param (
[bool]$required,
[string]$restoreFilename
)
# Module
Import-Module "SharePointPnPPowerShellOnline" -ErrorAction "SilentlyContinue" | Out-Null
# Config
$appid = "APP ID HERE"
$appsecret = "APP SECRET HERE"
function Main() {
# Connect
Connect-PnPOnline -Url "https://tenant.sharepoint.com/" -AppId $appid -AppSecret $appsecret
$ctx = Get-PnPContext
$list = Get-PnPList "Test"
$list
if ($restoreFilename) {
# ENABLE Required Fields
$csv = Import-Csv $restoreFilename
$fields = Get-PnPField -List $list
foreach ($row in $csv) {
$row.Guid
$f = $fields | ? { $_.Id -eq $row.Guid }
$f.Required = $true
$f.Update()
}
$ctx.ExecuteQuery()
}
else {
# DISABLE Required Fields
$coll = @()
$guid = (New-Guid).ToString()
$fields = Get-PnPField -List $list
foreach ($f in $fields) {
if ($f.Required) {
Write-Host "CHANGED FIELD $($f.Title) NOT REQUIRED"
$f.Required = $false
$f.Update()
$coll += $f.Id
}
}
$ctx.ExecuteQuery()
$coll | Export-Csv "PNPToggleRequiredField-$guid.csv"
}
}
# Main
Main
GitHub Link
References
PowerShell – Create SharePoint Alert remotely (CSOM)
I want to migrate user alerts to Office 365 from SharePoint 2013 on-prem and was surprised to learn there is no client side API for creating Alerts. Not SOAP, not REST, nothing but the web browser ASPX web form GUI.
So I took Fiddler and inspected the HTTP POST traffic when you click “OK” on the SharePoint “SubNew.aspx” page in the browser. Lots of great detail! You can see all the ASPX web form controls and internal values. Clicking the “OK” button posts that event back to the server side Dot Net for processing with Request Digest, ViewState, and lots of cool stuff.
So …. could we simulate sending that same HTTP traffic? From somewhere else? Like PowerShell?? That’d be awesome!
Below is the working concept. PowerShell function with a handful of sample calls. Immediate alert, Daily, Weekly scheduled, etc. With various recipients and filters. Enjoy! ![]()
Screenshots
Code (CreateSPAlert.ps1)
Function CreateSPAlert($webURL, $listID, $userLogin, $userDisplayName, $alertTitle, $changeType, $sendAlertsFor, $whenToSend, $whenToSendDay, $whenToSendTime) {
<#
ASPNet WebForm Input Values
$changeType
-1 All changes
1 New items are added
2 Existing items are modified
4 Items are deleted
$sendAlertsFor
0 'Anything changes'
1 ' '
2 ' '
3 ' '
$whenToSend
0 immediate
1 daily
2 weekly
$whenToSendDay
0 Sunday
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
$whenToSendTime
12:00 AM
1:00 AM
..
11:00 AM
12:00 PM
1:00 PM
..
11:00 PM
#>
# Convert parameters
switch ($sendAlertsFor) {
'1' {$sendAlertsFor=' '}
'2' {$sendAlertsFor=' '}
'3' {$sendAlertsFor=' '}
default {$sendAlertsFor='Anything changes'}
}
# List Scope
$url = "$webURL/_layouts/15/SubNew.aspx?List=$listID"
$fields = Invoke-WebRequest -Uri $url -UseDefaultCredentials -UseBasicParsing | select -ExpandProperty inputfields | select name, value
# HTTP POST Body WeBforms
$postParams = @{
'MSOWebPartPage_PostbackSource'='';
'MSOTlPn_SelectedWpId'='';
'MSOTlPn_View'='0';
'MSOTlPn_ShowSettings'='False';
'MSOGallery_SelectedLibrary'='';
'MSOGallery_FilterString'='';
'MSOTlPn_Button'='none';
'__LASTFOCUS'='';
'__EVENTTARGET'='ctl00$PlaceHolderMain$ctl01$RptControls$BtnCreateAlert';
'__EVENTARGUMENT'='';
'MSOSPWebPartManager_DisplayModeName'='Browse';
'MSOSPWebPartManager_ExitingDesignMode'='false';
'MSOWebPartPage_Shared'='';
'MSOLayout_LayoutChanges'='';
'MSOLayout_InDesignMode'='';
'MSOSPWebPartManager_OldDisplayModeName'='Browse';
'MSOSPWebPartManager_StartWebPartEditingName'='false';
'MSOSPWebPartManager_EndWebPartEditing'='false';
'_maintainWorkspaceScrollPosition'='741';
'__REQUESTDIGEST' = ($fields|? {$_.Name -eq '__REQUESTDIGEST'}).Value;
'__VIEWSTATE'=($fields|? {$_.Name -eq '__VIEWSTATE'}).Value;
'__SCROLLPOSITIONX'='0';
'__SCROLLPOSITIONY'='0';
'__EVENTVALIDATION'=($fields|? {$_.Name -eq '__EVENTVALIDATION'}).Value;
'ctl00$PlaceHolderMain$ctl03$ctl01$TextTitle'=$alertTitle;
'ctl00$PlaceHolderMain$ctl04$ctl01$clientPeoplePicker'="[{""Key"":""$userLogin"",""DisplayText"":""$userDisplayName"",""IsResolved"":true,""Description"":""$userLogin"",""EntityType"":"""",""EntityGroupName"":"""",""HierarchyIdentifier"":null,""EntityData"":{""SPUserID"":""1"",""AccountName"":""$userLogin"",""PrincipalType"":""User""},""MultipleMatches"":[],""ProviderName"":"""",""ProviderDisplayName"":"""",""Resolved"":true}]";
'ctl00$PlaceHolderMain$ctl05$ctl02$rdoDC'='rdo_EmailDC';
'ctl00$PlaceHolderMain$ctl06$ctl01$RadioBtnEventType'=$changeType;
'ctl00$PlaceHolderMain$ctl07$ctl02$RadioBtnAlertFilter'=$sendAlertsFor;
'ctl00$PlaceHolderMain$hdnAlwaysNotify'='False';
'ctl00$PlaceHolderMain$ctl08$ctl02$RadioBtnAlertFreq'=$whenToSend}
# Append if needed
if ($whenToSendDay) {
$postParams.Add('ctl00$PlaceHolderMain$ctl08$ctl03$DdlWeekDay',$whenToSendDay);
}
if ($whenToSendTime) {
$postParams.Add('ctl00$PlaceHolderMain$ctl08$ctl03$DdlHour',$whenToSendTime);
}
# Execute HTTP
$response = Invoke-WebRequest -Uri $url -WebSession RequestForm -Method POST -Body $postParams -ContentType 'application/x-www-form-urlencoded' -UseBasicParsing
}
# Main
$start = Get-Date
# Create SP alerts
CreateSPAlert 'http://sharepoint/sites/test' '%7B0D568A08%2DD7AA%2D405B%2DADE1%2DF324216C0272%7D' 'i:0#.w|company\\jsmith' 'John Smith' 'Documents' '-1' 'Anything changes' '0'
CreateSPAlert 'http://sharepoint/sites/test' '%7B0D568A08%2DD7AA%2D405B%2DADE1%2DF324216C0272%7D' 'i:0#.w|company\\jsmith' 'John Smith' 'Documents' '4' 'Anything changes' '0'
CreateSPAlert 'http://sharepoint/sites/test' '%7B0D568A08%2DD7AA%2D405B%2DADE1%2DF324216C0272%7D' 'i:0#.w|company\\jsmith' 'John Smith' 'Daily-Documents' '4' 'Anything changes' '1' '0' '7:00 PM'
CreateSPAlert 'http://sharepoint/sites/test' '%7B0D568A08%2DD7AA%2D405B%2DADE1%2DF324216C0272%7D' 'i:0#.w|company\\jsmith' 'John Smith' 'Weekly-Documents' '4' 'Anything changes' '2' '5' '5:00 PM'
# Run duration
$end = (Get-Date) - $start
$ms = $end.TotalMilliseconds
"$ms MS duration"
References
- http://spalertme.codeplex.com/
- http://www.learningsharepoint.com/2013/09/17/client-object-model-doesnt-support-creating-sharepoint-2013-alerts/
- http://techtrainingnotes.blogspot.com/2011/10/sharepoint-create-alert-from-anywhere.html
- https://social.msdn.microsoft.com/forums/sharepoint/en-US/7a2a6447-cf92-427d-acb5-095067f4f9b1/set-up-alert-on-document-from-net-client-object-model
- https://social.technet.microsoft.com/Forums/en-US/11217809-9aaa-4d6f-8a53-4c1e9ab010a6/how-to-set-alert-me-on-documents-using-client-object-model-2010
- http://stackoverflow.com/questions/24416618/sending-sharepoint-alerts-programmatically-using-client-side-object-model
- http://toddbaginski.com/blog/how-to-create-office-365-sharepoint-alerts-with-the-client-side-object-model-csom/
FIXED – 403 ExecuteQuery CSOM from a SharePoint Server
I came across this error when running CSOM requests to Office 365. While troubleshooting the CSOM call worked beautifully from “powershell_ise” but not regular “powershell.” Fiddler monitoring showed the cmdlet making HTTP traffic correctly with login handshake from ISE. However, the regular “powershell” window did not attempt any login handshake.
Strange? Definitely.
The root cause was “$profile” loading the Server Object Model (SOM) cmdlets before CSOM DLL were loaded. Why would that matter? How could it cause a CSOM issue? There seems to be a DLL namespace overlap internally between these plugins (SOM, CSOM, SPO) so loading sequence matters. A lot.
By proactively loading the CSOM DLL in $profile before SOM, everything worked correctly from both ISE and the regular PowerShell console.
Symptom
- Run CSOM request in PowerShell
- From a SharePoint Server on premise (2013 here)
- Which also has Microsoft SharePoint Online (SPO) cmdlets installed (https://www.microsoft.com/en-us/download/details.aspx?id=35588)
- See this error
- Exception calling “ExecuteQuery” with “0” argument(s): “The remote server returned an error: (403) Forbidden.
Screenshot
Resolution
- Open PowerShell and type “notepad $profile”
- Ensure below code is present.
- NOTE – CSOM must load before SOM (Server Object Model) for requests to execute correctly. Workaround for an internal Microsoft naming overlap. Both are probably using the same object somewhere. Loading CSOM first allows CSOM to reserve the namespace first.
Code [$profile]
#CSOM first
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") | Out-Null
#SOM
Add-PSSnapIn Microsoft.SharePoint.PowerShell
Code [csom-only-test.ps1]
#dll
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") | Out-Null
#scope
$url = "https://tenant.sharepoint.com/sites/team"
$user = "admin@tenant.onmicrosoft.com"
$pass = "password"
$secpw = $pass | ConvertTo-SecureString -AsPlainText -Force
#connect
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$cred = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($user, $secpw)
$ctx.Credentials = $cred
#site
$site = $ctx.Site
$ctx.Load($site)
#update
if (!$site.TrimAuditLog) {
$site.TrimAuditLog = $true
$site.AuditLogTrimmingRetention = 180
}
#save
$ctx.ExecuteQuery()
$ctx.Dispose()
