Share a SharePoint File with different Office365 users using Client Object Model C#

In my previous post, Get a list of all the Shared users for a ListItem/Document of SharePoint List, I had described how to get the list of all the SharePoint users with whom a given Document Library item, has been shared. Now, in this post, I am going to Share an existing ListItem with a different SP user using CSOM.This works in the following 2 simple steps:

  1. Get the user(principal) with whom the item has to be shared.
  2. Next, get/create the RoleDefinition which will have various Permission Rights for the user.

The 2nd point is important. SP has already defined various RoleDefinitions that we can use for our purpose like, Administrator, ContentEditior, Read, etc. So we can either use any one of them or can even create our own custom RoleDefinition. I am going to demonstrate both the functionalities here.

First get the User and the ListItem which is to be shared


ClientContext ctx = new ClientContext("targetURL");
ctx.Credentials = new SharePointOnlineCredentials(userName, passWord);

ctx.Load(ctx.Web);
SP.ListCollection listCol = web.Lists;
ctx.Load(listCol);

SP.List list = listCollection.GetByTitle("ListTitle");

SP.CamlQuery camlQuery = new SP.CamlQuery();
camlQuery.ViewXml = "";

SP.ListItemCollection listItemCollection = list.GetItems(camlQuery);
ctx.Load(listItemCollection);

ctx.ExecuteQuery();

//get the user you want to share the current item
User currentUser = ctx.Web.EnsureWeb("memberLoginName")
SP.ListItem listItem = listItemCollection.FirstOrDefault(t => t.DisplayName == "WordFile");
ctx.Load(listItem,
    li => li.HasUniqueRoleAssignments,
    li => li.RoleAssignments.Include(
        p => p.Member,
        p => p.RoleDefinitionBindings.Include(
            s => s.Name)));
ctx.Load(currentUser);
            
ctx.ExecuteQuery(); 

Share with User with Existing RoleDefinition

RoleDefinitionCollection roleDefCol = ctx.Web.RoleDefinitions;

ctx.Load(roleDefCol, r => r.Include(p => p.Name));
ctx.ExecuteQuery();

RoleDefinitionBindingCollection rDefBindCol = new RoleDefinitionBindingCollection(ctx);

//Could be Edit, Read, Contribute, etc.
string roleDefName = "Edit";
RoleDefinition rDef = roleDefCol.FirstOrDefault(p => p.Name == roleDefName);

if(rDef != null)
{
    //Add the current binding to the seleted listItems RoleAssignments
    rDefBindCol.Add(rDef);
}

ctx.ExecuteQuery();

Console.WriteLine("SharePoint Role Assigned successfully.");

Share with User with new Custom RoleDefinition

Site collSite = ctx.Site;

// Set up permissions.
BasePermissions permissions = new BasePermissions();
permissions.Set(PermissionKind.ViewListItems);
permissions.Set(PermissionKind.AddListItems);
permissions.Set(PermissionKind.EditListItems);
permissions.Set(PermissionKind.DeleteListItems);

// Create a new role definition.
RoleDefinitionCreationInformation rdcInfo = new RoleDefinitionCreationInformation();
rdcInfo.Name = "Manage List Items";
rdcInfo.Description = "Allows a user to manage this list items";
rdcInfo.BasePermissions = permissions;
RoleDefinition roleDef = collSite.RootWeb.RoleDefinitions.Add(rdcInfo);

// Create a new RoleDefinitionBindingCollection object.
RoleDefinitionBindingCollection collRDB = new RoleDefinitionBindingCollection(ctx);
// Add the role to the collection.
collRDB.Add(roleDef);

// Break permissions so its permissions can be managed directly.
listItem.BreakRoleInheritance(true, false);

// Get the RoleAssignmentCollection for the target listItem.
RoleAssignmentCollection collRoleAssign = listItem.RoleAssignments;
// Add the user to the target listItem and assign the user to the new //RoleDefinitionBindingCollection.
RoleAssignment rollAssign = collRoleAssign.Add(currentUser, collRDB);

ctx.ExecuteQuery();

Console.WriteLine("Custom Role Assigned successfully."); 

Get a list of all the Shared users for a ListItem/Document of SharePoint List using Client Object Model C#

In SharePoint, users have the option to share a document or a folder with another user with either Read or Edit permissions. So you can have a document or may be a folder that you have shared with a person, or many person, or even a group. So now the endevaour is to get a list of all the users with whom a given document is shared with their corresponding permission levels.

To do this, we’re take the help of Role Assignment class.
SharePoint stores this information in the ListItem’s RoleAssignments property. So have a look at the following code:


ClientContext ctx = new ClientContext(targetURL);
ctx.Credentials = new SharePointOnlineCredentials(userName, passWord);

ctx.Load(ctx.Web);
SP.ListCollection listCol = web.Lists;
ctx.Load(listCol);

SP.List list = listCollection.GetByTitle("ListTitle");

SP.CamlQuery camlQuery = new SP.CamlQuery();
camlQuery.ViewXml = "";

SP.ListItemCollection listItemCollection = list.GetItems(camlQuery);
ctx.Load(listItemCollection);

//Execute Query 
ctx.ExecuteQuery();

 
SP.ListItem listItem = listItemCollection.FirstOrDefault(t => t.DisplayName == "WordFileName");

ctx.Load(listItem,
    li => li.HasUniqueRoleAssignments,
    li => li.RoleAssignments.Include(
        p => p.Member,
        p => p.RoleDefinitionBindings.Include(
            s => s.Name)));
ctx.ExecuteQuery();

if (listItem.HasUniqueRoleAssignments)
{
    //Get all the RoleAssignments for this document
    foreach (RoleAssignment rAssignment in listItem.RoleAssignments)
    {
        //A single RA can have multiple bindings
        foreach (RoleDefinition rDef in rAssignment.RoleDefinitionBindings)
        {
            Console.WriteLine(String.Concat("Role assigned to the member, ", rAssignment.Member.LoginName, " is ", rDef.Name));
        }
    }
}
else
    Console.WriteLine("The current ListItem has no unique roles assigned to it.");

Here, what I am doing is that for a given SharePoint site, I am connecting to a Document Library named, “ListTitle”. Then I am trying to get an item, “WordFileName”  from that list as this item has got some unique roles.

The important things to note down here is:

  1. A single item can have multiple Role Assignments i.e., different types of roles can be assigned to it. Say, a given user, A, might be assigned with “Edit” permission while another user, B, can have only “Read” permissions. Each of this variations will come down as a single Role Assignment(RA).
  2. A single Role Assignment can have multiple bindings.

Following are the roles defined for the user, that you can get for an item:

  • Full Control
  • Design
  • Edit
  • Contribute
  • Read
  • Limited Access
  • View Only
  • Records Center Web Service Submitters

The above List may vary if there are some custom Role Definitions.

You might want to have a look about the Limited Access permission here, https://realmpksharepoint.wordpress.com/2014/07/29/you-cannot-grant-a-user-the-limited-access-permission-level/

Similarly, here’s the link to the blog where I have described how, through code, we can share Office365 file, https://realmpksharepoint.wordpress.com/2014/08/04/share-a-sharepoint-file-with-different-office365-users-using-client-object-model-c/
That’s it.

Remove/Delete an existing SharePoint File C# RPC

This one deals with the removal of a document from a SharePoint site. Though this can also be achieved using CSOM but there’s one situation where the CSOM won’t be of any help, i.e., deleting a file from the root folder of a Web(SiteCollection/SubSite). Since, the root files do not belong to any List, these files cannot be handled using standard CSOM logic. For this, I am going to rely on the RPC call to the author.dll. In my previous two posts, https://realmpksharepoint.wordpress.com/2014/04/29/upload-large-files-to-the-sharepoint-documentlibrary-using-rpc-from-a-desktop-application-c/https://realmpksharepoint.wordpress.com/2014/05/09/renamemove-an-existing-sharepoint-file-using-rpc-c/ , I have demonstrated how a document (residing in the root or anywhere else) can be uploaded, moved, or renamed using RPC.
private void RemoveDocument(string documentName)
{
    string requestUrl = this.ctx.Url + "/_vti_bin/_vti_aut/author.dll";
    string method = GetEncodedString("remove documents:15.0.0.4420");
    string serviceName = GetEncodedString(ctx.Web.ServerRelativeUrl);

    string url_List = GetEncodedString(String.Concat("[", documentName, "]"));
    rpcCallString = "method={0}&service_name={1}&url_list={2}\n";

    rpcCallString = String.Format(rpcCallString, method, serviceName, url_List).Replace("_", "%5f");

    HttpWebRequest wReq = WebRequest.Create(requestUrl) as HttpWebRequest;
    wReq.Method = "POST";
    wReq.Headers["Content"] = "application/x-vermeer-urlencoded";
    wReq.Headers["X-Vermeer-Content-Type"] = "application/x-vermeer-urlencoded";
    wReq.UserAgent = "FrontPage";
    wReq.UseDefaultCredentials = false;

    Uri targetSite = new Uri(this.ctx.Web.Url);
    SharePointOnlineCredentials spCredentials = (SharePointOnlineCredentials)this.ctx.Credentials;

    string authCookieValue = spCredentials.GetAuthenticationCookie(targetSite);
    wReq.CookieContainer = new CookieContainer();
    wReq.CookieContainer.Add(
        new Cookie("FedAuth",
            authCookieValue.TrimStart("SPOIDCRL=".ToCharArray()),
            String.Empty,
            targetSite.Authority));

    using (IO.Stream requestStream = wReq.GetRequestStream())
    {
        byte[] rpcHeader = Encoding.UTF8.GetBytes(rpcCallString);
        requestStream.Write(rpcHeader, 0, rpcHeader.Length);
        requestStream.Close();

        GetResponse(wReq);
    }
}

private string GetResponse(HttpWebRequest webRequest)
{
    string responseString = String.Empty;
    using (WebResponse webResponse = webRequest.GetResponse())
    {
        using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
        {
            responseString = reader.ReadToEnd();
            byte[] fileBuffer = Encoding.UTF8.GetBytes(responseString);
        }
    }
    
    if ((responseString.IndexOf("message=successfully") < 0) && (responseString.IndexOf("msg=Save Conflict") < 0))
    {
        throw new Exception(responseString);
    }
    return responseString;
}

Now, let’s evaluate this.

  • Here we are using method “remove documents” and, “15.0.0.4420” is server extension version.
  • Service name is server relative URL of your site.
  • documentName is the name of the document to be deleted. For ex. if the doc ToBeDeleted.aspx resides in the root folder of the site then, documentName will be ToBeDeleted.aspx.
  • For authentication, we’re using the CookieContainer of HTTPWebRequest.

Here is the example of how will you call this upload method.

RemoveDocument("ToBeDeleted.aspx");

One utility method has been used here for encoding of string. Here it is for your reference.

public string GetEncodedString(string sourceString)
{
    if (!String.IsNullOrEmpty(sourceString))
    { 
        return HttpUtility.UrlEncode(sourceString).Replace(".", "%2e").Replace("_", "%5f");
    }
    else
    {
        return sourceString;
    }
}

Check-In/Check-Out and Publish/UnPublish files in SharePoint 2013 using the Client Object Model C#


string siteUrl = "http://servername:12345/";
SP.ClientContext ctx = new SP.ClientContext(siteUrl);
ctx.Credentials = new SharePointOnlineCredentials(userName, passWord);
this.ctx.Load(this.ctx.Web);
SP.Web web = this.ctx.Web;
SP.File file = web.getFileByServerRelativeUrl("serverrelativeUrlOfTheFile");

//CheckIn the file
file.CheckIn(String.Concat("File CheckingIn at ", DateTime.Now.ToLongDateString()), SP.CheckinType.MajorCheckIn);

//CheckOut the File
file.CheckOut();

//Publish the file
file.Publish(String.Concat("File Publishing at ", DateTime.Now.ToLongDateString()));

//UnPublish the file
file.UnPublish(String.Concat("File UnPublishing at ", DateTime.Now.ToLongDateString()));

Remember, before making any changes to the state of the file it is always advisable to first check its current state/level. You can check the current state of the file i.e., if the file is currently CheckedOut, Drafted (CheckedIn), OR, Published. You can easily do that by checking the FileLevel value of the file you’re querying. FileLevel is an enum which specifies the publishing level for a file. MoreInfo

***

Create a ListItem with Attachment using Client Object Model

FieldValuesAsTexts is a Dictionary which has been populated with some values.
using System;
using IO = System.IO;
using System.Linq;
using System.Runtime.Remoting;
using System.Text;
using System.Threading;
using Microsoft.SharePoint.Client;
using SP = Microsoft.SharePoint.Client;


string siteUrl = "http://servername:12345/";
SP.ClientContext ctx = new SP.ClientContext(siteUrl);
SP.List list = ctx.Web.Lists.GetByTitle("ListTitle");

SP.ListItemCreationInformation listItemCreationInfo = new SP.ListItemCreationInformation();
SP.ListItem oListItem = list.AddItem(listItemCreationInfo);

foreach (string key in FieldValuesAsTexts.Keys)
{
    if (ValidateKey(key))
    {
        string strKey = fieldEntities.FirstOrDefault(t => t.FieldName == key).FieldInternalName;
        if (FieldValuesAsTexts[key] != String.Empty)
        {
            string strVal = FieldValuesAsTexts[key];
            
            //You can not have a Yes/No value for a field/column whose type is bit. Hence replacing it with 1/0
            strVal = (strVal.ToLower().Trim() == "yes") ? "1" : (strVal.ToLower().Trim() == "no") ? "0" : strVal;
            oListItem[strKey] = strVal;
        }
    }
}
                
//This will create ListItem before adding attachments
oListItem.Update();

//Let's say we have a custom class called AttachmentEntity and AttachmentsCollection is the //collection of the class Attachments.

if (listItemEntity.Attachments != null)
{
    foreach (AttachmentEntity attachEntity in AttachmentsCollection)
    {
        string strFilePath = GetAbsoluteFilePath(strDirectoryPath, attachEntity.FileName);
        strFilePath = String.Concat(strFilePath, "\\", attachEntity.FileName);

        byte[] bytes = IO.File.ReadAllBytes(strFilePath);

        IO.MemoryStream mStream = new IO.MemoryStream(bytes);

        SP.AttachmentCreationInformation aci = new SP.AttachmentCreationInformation();
        aci.ContentStream = mStream;
        aci.FileName = attachEntity.FileName;

        SP.Attachment attachment = oListItem.AttachmentFiles.Add(aci);

        oListItem.Update();
    }
}
ctx.ExecuteQuery();