Sharepoint 2013 CRM2011 Document Upload and Document Location Helper (CSOM and OM)

Hi all,

Playing around with SharePoint CRM 2011 integration, I managed to construct a helper function that would be useful for future projects. This is when you want to upload document to SharePoint as well as creating document location in CRM 2011.

There are several ways of uploading documents to SharePoint.
1. Object Model
2. Client Object Model
3. Rest API

In this post, I will show you the first two. If your code is sitting in SP server, you can use the traditional object model. Here you go:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//Object Model way
 public static void FileUpload(string siteURL, string docLibName, string folderName, string fileName, byte[] content, string entityName, string entityId)
 {
     try
     {
         UploadFile(siteURL, docLibName, folderName, fileName, content, entityName, entityId);
     }
     catch (Exception ex)
     {
         throw ex;
     }
 }
 
 private static bool UploadFile(string siteURL, string docLibName, string folderName, string fileName, byte[] content, string entityName, string entityId)
 {
     var success = false;
 
     using (SPSite site = new SPSite(siteURL))
     {
         using (SPWeb web = site.OpenWeb())
         {
 
             SPSecurity.RunWithElevatedPrivileges(delegate()
             {
                 SPDocumentLibrary documentLib = web.Lists[docLibName] as SPDocumentLibrary;
 
                 web.AllowUnsafeUpdates = true;
 
                 string fileUrl = String.Empty;
                 string docUrl = documentLib.RootFolder.Url;
 
                 if (!String.IsNullOrEmpty(folderName))
                 {
                     docUrl = docUrl + "/" + folderName;
                 }
 
                 if (!String.IsNullOrEmpty(fileName))
                 {
                     fileUrl = docUrl + "/" + fileName;
                 }
 
                 bool IsOverwriteFile = true;
 
                 EnsureParentFolder(web, fileUrl);
                 SPFile file = documentLib.RootFolder.Files.Add(fileUrl, content, IsOverwriteFile);
 
                 if (!String.IsNullOrEmpty(fileName))
                 {
                     SPListItem item = file.Item;
                     item["Title"] = fileName;
                     item.Update();
                     file.Update();
                 }
 
                 web.AllowUnsafeUpdates = false;
 
                 //Create Document Location
                 CreateCRMDocumentLocation(siteURL, docUrl, docLibName, folderName, entityName, entityId);
                 success = true;
             });
         }
     }
 
     return success;
 }
 
 //Ensure that the parent folder in the destination URL exists in the specified site, and it returns the site-relative URL of the parent folder.
 //The EnsureParentFolder method accepts two parameters: an SPWeb object that represents the parent site, and a string that contains the absolute URL
 //that is passed from the example’s UploadFile method. If the parent folder does not exist, the EnsureParentFolder method creates it.
 private static string EnsureParentFolder(SPWeb parentSite, string destinUrl)
 {
     destinUrl = parentSite.GetFile(destinUrl).Url;
 
     int index = destinUrl.LastIndexOf("/");
     string parentFolderUrl = string.Empty;
 
     if (index > -1)
     {
         parentFolderUrl = destinUrl.Substring(0, index);
 
         SPFolder parentFolder
             = parentSite.GetFolder(parentFolderUrl);
 
         if (!parentFolder.Exists)
         {
             SPFolder currentFolder = parentSite.RootFolder;
 
             foreach (string folder in parentFolderUrl.Split('/'))
             {
                 currentFolder
                     = currentFolder.SubFolders.Add(folder);
             }
         }
     }
     return parentFolderUrl;
 }

You might want to impersonate credentials when uploading to a different SP site. With Firefox, when I try to upload to secured SP site (https) I got prompted login box which is annoying (as opposed to single sign on with IE/Chrome).

So as a workaround we can use the Client Object Model way to upload to SharePoint in order to specify the credentials:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//We use client object model to assign custom credential when accessing SharePoint Document Site
public static void FileUpload(string siteURL, string docLibName, string folderName, string fileName, byte[] content, string entityName, string entityId)
{
    try
    {
        UploadFile(siteURL, docLibName, folderName, fileName, content, entityName, entityId);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}
 
private static bool UploadFile(string siteURL, string docLibName, string folderName, string fileName, byte[] content, string entityName, string entityId)
{
    var success = false;
 
    using (ClientContext clientContext = new ClientContext(siteURL))
    {
        clientContext.Credentials = new NetworkCredential("username", "password", "domain");
 
        string fileUrl = String.Empty;
        string docUrl = "/" + entityName;
 
        if (!String.IsNullOrEmpty(folderName))
        {
            docUrl = docUrl + "/" + folderName;
        }
 
        if (!String.IsNullOrEmpty(fileName))
        {
            fileUrl = docUrl + "/" + fileName;
        }
 
        using (MemoryStream memoryStream = new MemoryStream(content))
        {
            EnsureParentFolder(clientContext, docLibName, entityName, folderName, fileUrl);
            Microsoft.SharePoint.Client.File.SaveBinaryDirect(clientContext, fileUrl, memoryStream, true);
        }
 
        success = true;
    }
 
    return success;
}
 
////Ensure that the parent folder in the destination URL exists in the specified site, and it returns the site-relative URL of the parent folder.
////The EnsureParentFolder method accepts two parameters: an SPWeb object that represents the parent site, and a string that contains the absolute URL
////that is passed from the example’s UploadFile method. If the parent folder does not exist, the EnsureParentFolder method creates it.
private static string EnsureParentFolder(ClientContext clientContext, string docLibName, string entityName, string folderName, string destinUrl)
{
    int index = destinUrl.LastIndexOf("/");
    string parentFolderUrl = string.Empty;
 
    if (index > -1)
    {
        parentFolderUrl = destinUrl.Substring(0, index);
 
        Microsoft.SharePoint.Client.List list = GetListByTitleCI(clientContext, docLibName);
 
        if (list != null)
        {
            Folder parentFolder
                = GetFolderCI(clientContext, list, entityName, folderName);
 
            if (parentFolder == null)
            {
                Folder currentFolder = list.RootFolder;
 
                foreach (string folder in parentFolderUrl.Split('/'))
                {
                    currentFolder
                        = currentFolder.Folders.Add(folder);
                }
                clientContext.ExecuteQuery();
            }
        }
    }
    return parentFolderUrl;
}
 
private static Folder GetFolderCI(ClientContext clientContext, Microsoft.SharePoint.Client.List list, String listTitle, String folderName)
{
    Folder existingFolder = null;
     
    FolderCollection folders = list.RootFolder.Folders;
 
    String folderUrl = String.Format("/{0}/{1}", listTitle, folderName);
 
    IEnumerable<folder> existingFolders = clientContext.LoadQuery(
        folders.Include(
        folder => folder.ServerRelativeUrl)
        );
    clientContext.ExecuteQuery();
 
    existingFolder = existingFolders.FirstOrDefault(
        folder => folder.ServerRelativeUrl.ToLower() == folderUrl.ToLower());
    
 
    return existingFolder;
}
 
 
private static Microsoft.SharePoint.Client.List GetListByTitleCI(ClientContext clientContext, String listTitle)
{
    Microsoft.SharePoint.Client.List existingList;
 
    Web web = clientContext.Web;
    ListCollection lists = web.Lists;
    
    IEnumerable<Microsoft.SharePoint.Client.List> existingLists = clientContext.LoadQuery(
             lists.Include(
             list => list.Title)
             );
    clientContext.ExecuteQuery();
 
    existingList = existingLists.FirstOrDefault(list => list.Title.ToLower() == listTitle.ToLower());
 
    return existingList;
}

Here I also ensure that the folder exists before we upload document (otherwise create them!).

Lastly, the CRM 2011 document location can be created easily if they don't exist yet :)
You have 2 options (Absolute or Relative paths), I will show both functions:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//Create CRM Document Location Record (Absolute)
private static void CreateCRMDocumentLocation(string siteURL, string docUrl, string docLibName, string folderName, string entityName, string entityId)
{
    var crmService = CrmConnectionHelper.GetCrmService();
    OrgServiceContext orgService = new OrgServiceContext(crmService);
 
    //remove the "/" for CSOM
    string url = siteURL + "/" + docUrl;
 
    var docLocExist = orgService.SharePointDocumentLocationSet.Where(l => l.AbsoluteURL == url).ToList().Any();
 
    if (!docLocExist)
    {
        SharePointDocumentLocation docLocation = new SharePointDocumentLocation()
        {
            Name = "SharePoint Document Location for " + docLibName + " " + folderName,
            AbsoluteURL = url,
            RegardingObjectId = new EntityReference(entityName, new Guid(entityId))
        };
 
        orgService.AddObject(docLocation);
        Crm.SaveChanges(orgService);
    }
}
 
//Create CRM Document Location Record (Relative)
private static void CreateRelativeCRMDocumentLocation(string siteURL, string docUrl, string docLibName, string folderName, string entityName, string entityId)
{
    var crmService = CrmConnectionHelper.GetCrmService();
    OrgServiceContext orgService = new OrgServiceContext(crmService);
 
    //To match the formatting
    siteURL = siteURL + "/";
 
    var site = orgService.SharePointSiteSet.Where(s => s.AbsoluteURL == siteURL).FirstOrDefault();
 
    if (site != null)
    {
        SharePointDocumentLocation parentDocLocation = null;
        var urlString = docUrl.Split(new String[] {"/"}, StringSplitOptions.RemoveEmptyEntries);
 
        //Check first relative url (first location parent will always be an SP site)
        var firstDocLoc = (from l in orgService.SharePointDocumentLocationSet
                          where l.ParentSiteOrLocation.Id == site.Id &&
                                l.RelativeUrl == urlString[0]
                          select l).FirstOrDefault();
 
        //Create if not exist
        if (firstDocLoc == null)
        {
            parentDocLocation = new SharePointDocumentLocation()
            {
                Name = "SharePoint Document Location for " + docLibName + " " + urlString[0],
                ParentSiteOrLocation = new EntityReference(SharePointSite.EntityLogicalName, site.Id),
                RelativeUrl = urlString[0],
                Id = new Guid()
            };
 
            orgService.AddObject(parentDocLocation);
            Crm.SaveChanges(orgService);
            CreateRelativeChildDocumentLocation(parentDocLocation, orgService, docLibName, urlString, entityName, entityId);
        }
        else
        {
            CreateRelativeChildDocumentLocation(firstDocLoc, orgService, docLibName, urlString, entityName, entityId);
        }
    }
}
 
private static void CreateRelativeChildDocumentLocation(SharePointDocumentLocation parentDocLoc, OrgServiceContext orgService, string docLibName, string[] urlString, string entityName, string entityId)
{
 
    var parentDocLocId = parentDocLoc.Id;
    for (int i = 1; i < urlString.Length; i++)
    {
        var existingDocLoc = (from l in orgService.SharePointDocumentLocationSet
                              where l.ParentSiteOrLocation.Id == parentDocLocId &&
                                    l.RegardingObjectId.Id == new Guid(entityId) &&
                                    l.RelativeUrl == urlString[i]
                              select l).FirstOrDefault();
 
        if (existingDocLoc == null)
        {
            var docLoc = new SharePointDocumentLocation()
            {
                Name = "SharePoint Document Location for " + docLibName + " " + urlString[i],
                ParentSiteOrLocation = new EntityReference(SharePointDocumentLocation.EntityLogicalName, parentDocLocId),
                RegardingObjectId = new EntityReference(entityName, new Guid(entityId)),
                RelativeUrl = urlString[i],
                Id = new Guid()
            };
 
            orgService.AddObject(docLoc);
            Crm.SaveChanges(orgService);
            parentDocLocId = docLoc.Id;
        }
        else
        {
            parentDocLocId = existingDocLoc.Id;
        }
    }
}

There you go! Then you can just use this function in your handler class etc.

HTH,
Andreas

Comments

Popular posts from this blog

SharePoint 2013 anonymous access add attachments to list item

CRM Plugin - Parent and Child Pipeline