Browse Category

Development

Sharing a File in SharePoint Online or OneDrive with PowerShell

I have been diving into doing larger scale operations in SharePoint Online using the Client Side Object Model (CSOM) utilizing PowerShell and ran into a scenario that I couldn’t easily find documented anywhere. What I wanted to do was technically “share” a file with a specific user and have that user receive an email just like if it was done through the GUI. What I didn’t want to see is just the breaking of permissions. What I found was the Web.ShareObject method and this great blog post from Vesa Juvonen in 2015

Once I found this I started working on putting this into a useful PowerShell format. To get started with CSOM & PowerShell with SharePoint Online here is a good blog post from Chris O’Brien. You can get the latest version of SharePoint Online CSOM here. If you download the nuget file you can change the file extension to .zip and extract the .dlls.

Here is link to the GitHub rep and I will break it down below along with the script. Here are some key things to note:

  • The Web.ShareObject method has been updated since the Vesa blog post with a parameter called useSimplifiedRoles that can be used for utilizing modern sharing
  • SharePoint PnP has extended the sharing APIs and built a sample that can be used
  • This script is built to share a file based on filename within a site to a single user
  • This works on SharePoint Online and OneDrive for Business
  • It will share as the user who runs the script
  • This script could be updated to share a site or to multiple people
  • You can share with Edit or View permission based on the roleValue
  • It doesn’t replicate the modern sharing UI in capabilities exactly (more of what occurs details below)

To utilize the script make sure you fill out the appropriate variables and more information about what this will do is below the script. 

# Use this script to share a file via CSOM and PowerShell
# ShareObject https://msdn.microsoft.com/en-us/library/office/mt684216.aspx
# External sharing blog https://blogs.msdn.microsoft.com/vesku/2015/10/02/external-sharing-api-for-sharepoint-and-onedrive-for-business/

### ENTER YOU VARIABLES HERE ###

#path to the SP CSOM files 
$csomPath = "C:\...." 

#Email of person running the script
$adminEmail = "user@domain.com"

#Site collection to be connected to
$siteUrl = "https://site.sharepoint.com/sites/site"

#Library title where the file exists
$libraryTitle = "Documents" 

#Filename including file type
$fileName = "Test Document 1.docx"

#Email of who the document is being shared to
$emailSharedTo = "user2@domain.com"

#UNVALIDATED_EMAIL_ADDRESS if they are in AD or GUEST_USER if they are not
$principalType = "UNVALIDATED_EMAIL_ADDRESS"  

#role:1073741826 = View, role:1073741827 = Edit
$roleValue = "role:1073741827"

#A flag to determine if permissions should be pushed to items with unique permissions.
$propageAcl = $true

#Flag to determine if an e-mail notification should to sent, if e-mail is configured.
$sendEmail = $true  

#If an e-mail is being sent, this determines if an anonymous link should be added to the message.
$includedAnonymousLinkInEmail = $false  

#The ID of the group to be added to. Use zero if not adding to a permissions group. Not actually used by the code even when user is added to existing group. 
$groupId = 0

#Doesn't matter as it isn't sent in current email format
$emailSubject = ""

#Text for the body of the e-mail.
$emailBody = "Check out my email body"  

#Use modern sharing links instead of directly granting access
$useSimplifiedRoles = $true
################

# Get CSOM files
Add-type -Path "$csomPath\Microsoft.SharePoint.Client.dll"
Add-type -Path "$csomPath\Microsoft.SharePoint.Client.Runtime.dll"

# Connnect to site
$ss = Read-Host -Prompt "Enter admin password" -AsSecureString
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
$creds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($adminEmail, $ss)
$ctx.Credentials = $creds
if(!$ctx.ServerObjectIsNull.Value) { 
    Write-Host "Connected to site:" $siteUrl -ForegroundColor Green 
} 
# Get web
$web = $ctx.Web

# Connect to library
$list = $web.Lists.GetByTitle($libraryTitle)
$ctx.Load($web)
$ctx.Load($list)
$ctx.Load($list.RootFolder)
$ctx.ExecuteQuery()

# Get doc
$query = New-Object Microsoft.SharePoint.Client.CamlQuery
$caml ="<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='File'>" + $fileName + "</Value></Eq></Where></Query></View>"
$query.ViewXml = $caml
$item = $list.GetItems($query)
$ctx.Load($item)
$ctx.ExecuteQuery()
if (!$item) {
    Write-Host "Could not find the file:" $fileName -ForegroundColor Yellow 
} else {
    Write-Host "Sharing the the file:" $item.FieldValues.FileLeafRef -ForegroundColor Green 
}

# Get doc url
$itemUrl = $item.FieldValues.FileRef
$split = $web.Url -split '/'
$itemUrl = "https://" + $split[2] + $itemUrl

# Build user object to be shared to
$jsonPerson = "[{`"Key`":`"$emailSharedTo`",
`"Description`":`"$emailSharedTo`",
`"DisplayText`":`"$emailSharedTo`",
`"EntityType`":`"`",
`"ProviderDisplayName`":`"`",
`"ProviderName`":`"`",
`"IsResolved`":true,
`"EntityData`":{`"Email`":`"$emailSharedTo`",
    `"AccountName`":`"$emailSharedTo`",
    `"Title`":`"$emailSharedTo`",
    `"PrincipalType`":`"$principalType`"},
`"MultipleMatches`":[]}]"

# Initiate share
$result = [Microsoft.SharePoint.Client.Web]::ShareObject($web.Context,$itemUrl,$jsonPerson,$roleValue,$groupid,$propageAcl,$sendEmail,$includedAnonymousLinkInEmail,$emailSubject,$emailBody,$useSimplifiedRoles)
$web.Context.Load($result)
$web.Context.ExecuteQuery()

Write-Host "Status of the share:" $result.StatusCode -ForegroundColor Green

Starting from a non shared file this is what you will see based on different configurations:

Sharing with useSimplifiedRoles set to $true and sendEmail set to $true

  • The file does not have inheritance broken

  • After initiating the ShareObject, inheritance is broken but you don’t see any changes

  • The person being shared to receives an email that the person who ran the script wants to share a file with you and you will see the email subject is preset but the email body is included

  • Once the person being shared to clicks on the link you can see a new ‘Managed Links’ section in the item permissions

  • If you follow that link you will see the item is now shared with that individual

Sharing with useSimplifiedRoles set to $true and sendEmail set to $false

  • The file does not have inheritance broken
  • After initiating the ShareObject, inheritance is broken but you don’t see any changes if the user tries to access the file through the document library
  • There is a new link viewable in the modern manage access section showing a new sharing link and that someone can access via that link

  • If the user accesses the file via that link you can see a new ‘Managed Links’ section in the item permissions and you can see that user in the Shared with section

 

Sharing with useSimplifiedRoles set to $false and sendEmail set to $false

  • The file does not have inheritance broken
  • After initiating the ShareObject, inheritance is broken but you don’t see any changes even after a user accesses the file, that means this does nothing but break inheritance

Sharing with useSimplifiedRoles set to $false and sendEmail set to $true

  • The file does not have inheritance broken
  • After initiating the ShareObject, inheritance is broken but you don’t see any changes
  • Once a user accesses the file via the link in the email they are granted permissions directly to the file (contribute instead of edit)

Ending…

After putting this together I realized I don’t really have a great use case to actually use this. Either way it was a good learning experience for me as I am just getting started into this kind of CSOM & PowerShell work and maybe it will come in handy for someone else in the future. 

Interact with list fields based on SharePoint Group

A very common question that is asked around the SharePoint community is about the ability to interact with item forms. These forms are how users view and edit items in a list. Changes to these forms can accomplished in a few different ways.

  1. Use SharePoint Designer to create an editable New, Edit, or Display form
  2. Edit the default New, Edit, or Display form pages using web parts.  (The edit page link for these forms can be found in the site settings via the gear menu.)
  3. Using a customized Infopath form (for now…)

Using any of these options it is possible to edit the forms in both a basic and advanced way. If you are creating new forms with SP Designer you can edit the HTML of the form directly and have control over nearly everything that is displayed.  You can do some easy customizations that can be very helpful for the end user experience. One example involves the hiding of a column (such as status) on the default edit form. These are the steps to do this in SP designer:

1.  Create a new Edit Form using SP Designer on the corresponding list

Edit1 - 2014-09-08 11_16_41-

2.  Locate the row that contains the column you want to hide and use HTML comment tags (<!– & –>) to comment out the row.

Edit2 - 2014-09-08 11_19_16-.aspx

One big disadvantage of this approach is that as columns are added, removed, or renamed the form will not reflect these changes. You will need to manually go update the field changes on each custom form that you created. Also this scenario hides the field for everyone that goes to the list. 

What if we wanted to only hide the status column for users of a certain SharePoint group?

This can be accomplished using jQuery and one of the best libraries available, the jQuery SPServices library. The files that you need and the instructions to get started using this library can be found on codeplex. Some great examples of using these services can be found on the blog of the creator of this wonderful tool, Marc Anderson.

Continuing on the example above of hiding a status column, let’s work towards the requirement of hiding that column unless you are in the SharePoint group “Approvers”. 

Here is the script that you can put into a Script Editor web part on the default Edit Form:


<script language="javascript" src="https://sitecollection/SiteAssets/jquery-1.11.1.min.js">
    </script>

<script language="javascript" src="https://sitecollection/SiteAssets/jquery.SPServices-0.7.2.min.js">
</script>

<script>
$(document).ready(function () {

if(checkrole('Approvers')){
$("[id^=Status]").closest('tr').show();
}
else {
$("[id^=Status]").closest('tr').hide();
}

function checkrole(groupname) {
    var IsvalidRet = false;
    $().SPServices({
        operation: "GetGroupCollectionFromUser",
        userLoginName: $().SPServices.SPGetCurrentUser(),
        async: false,
        completefunc: function (xData, Status) {

            if ($(xData.responseXML).find("Group[Name='"+groupname+"']").length == 1) {
                IsvalidRet = true;
            }
        }
    });
    return IsvalidRet;
}

});
</script>


Script Breakdown

The first 3 javascript calls load the appropriate libraries on the page. These calls can also be placed at a higher level (such as the master page) so they do not need be called on every page. The download files for jQuery can be found here and the SPservices can be found in the link earlier in this post.

The next script section starts with the document.ready() which detects the state of readiness of the page for you. Code included inside of this will only run once the page Document Object Model (DOM) is ready for JavaScript code to execute.


if(checkrole('Approvers')){
$("[id^=Status]").closest('tr').show();
}
else {
$("[id^=Status]").closest('tr').hide();
}


The if statements calls the checkrole function and passes along the group name that you want to check to see if a user is a part of. If the statement returns true it shows the status column and if it returns false it hides the status column. The lookup checks the page for ID’s that contain the word Status. You can use a browser development tool to inspect elements on a page to get their generated IDs. You should not take the section of the ID that is the GUID.

Edit3 - 2014-09-08 20_33_57-Tasks - ..

The last section of the script is the checkrole function. This uses the SPServices library and the SPGetCurrentUser function to return the users data. It then scans through the responseXML to find the group name and sets the variable to true.


function checkrole(groupname) {
    var IsvalidRet = false;
    $().SPServices({
        operation: "GetGroupCollectionFromUser",
        userLoginName: $().SPServices.SPGetCurrentUser(),
        async: false,
        completefunc: function (xData, Status) {

            if ($(xData.responseXML).find("Group[Name='"+groupname+"']").length == 1) {
                IsvalidRet = true;
            }
        }
    });
    return IsvalidRet;
}


The big disadvantage of this option is that hiding a column does not actually secure the column from the user, it just hides it on the form. If this user has access to this column in quick edit or datasheet view directly in the list they would be able to change it.

These are some options to get started with interacting with list forms after the announcement of the death of InfoPath and a great tool in SPServices that can take the SharePoint user experience to a new level.