Hi all,
Some times we come across the requirement to upload documents to SharePoint programatically from within a Dynamics CRM plugin. In my last post I had shown how to upload documents to SharePoint using a console application. We are going to reuse the same code here, however there are some additional steps that we need to do here that will ensure that the plugin works as expected without throwing any exception.
These are the steps before we even start developing the plugin:
1. If the source files to be uploaded to SharePoint are in a folder on the server or a shared location,m the Network Services account should have "read" privilege on this folder. The asynchronous service in which this plugin will run will run under the context of the "Network Service" account and hence it requires access to this folder. This can be achieved by right clicking the folder and going to the Security tab and add "Network Service" account by giving it read permission.
2. The assembly containing the SharePoint integration plugin should not be deployed in the Sandboxed mode. This will ensure that the plugin can read from the file system and access the SharePoint Dlls (later). Since plugins to Dynamics CRM Online can only be deployed in the Sandboxed mode so this plugin will not work for CRM Online.
3. The SharePoint Dlls that will be used in the code need to be GACed in the target server where CRM is installed. It is simply achieved by running the GacUtil.exe command in the Developer tools command prompt (running in the admin mode) pointing to the SharePoint Dlls below:
a. Microsoft.SharePoint.Client.dll
b. Microsoft.SharePoint.Client.Runtime.dll
Additional details on GacUtil can be found
here.
The actual plugin needs to contain this code (from the last post
here) and taking some concepts from
here:
/// <summary>
/// The sharepoint default site URL configuration key
/// </summary>
private const string SharepointDefaultSiteUrlConfigKey = "SharePointDefaultSiteUrl";
/// <summary>
/// The sharepoint username configuration key
/// </summary>
private const string SharepointUsernameConfigKey = "SharePointUserName";
/// <summary>
/// The sharepoint password configuration key
/// </summary>
private const string SharepointPasswordConfigKey = "SharePointPassword";
/// <summary>
/// The sharepoint domain configuration key
/// </summary>
private const string SharepointDomainConfigKey = "SharePointDomain";
// code to be included in Plugin
var completePath = "C:\\Users\\anurag\\Desktop";
var pricingDocuments = Directory.GetFiles(completePath, "Test*");
if (pricingDocuments.Length > 0)
{
// Attach the files obtained to the product
var defaultSiteUrl = Common.RetrieveConfigurationData(SharepointDefaultSiteUrlConfigKey, orgService);
var sharePointUser = Common.RetrieveConfigurationData(SharepointUsernameConfigKey, orgService);
var sharepointPassword = Common.RetrieveConfigurationData(SharepointPasswordConfigKey, orgService);
var sharepointDomain = Common.RetrieveConfigurationData(SharepointDomainConfigKey, orgService);
const string productLocationStr = "product";
using (var clientContext = new ClientContext(defaultSiteUrl))
{
clientContext.Credentials = new NetworkCredential(sharePointUser, sharepointPassword, sharepointDomain);
Web web = clientContext.Web;
var allProductsFolder = web.GetFolderByServerRelativeUrl(defaultSiteUrl + "product");
clientContext.Load(allProductsFolder, i => i.Folders);
clientContext.ExecuteQuery();
var productFolders = allProductsFolder.Folders;
if (productFolders != null)
{
var requiredFolderName = productEntity.GetAttributeValue<string>("name") + "_" + productEntity.Id.ToString().Replace("-", string.Empty).ToUpper();
Folder requiredFolder = null;
try
{
requiredFolder =
web.GetFolderByServerRelativeUrl(defaultSiteUrl + productLocationStr + "/" + requiredFolderName);
clientContext.Load(requiredFolder);
clientContext.ExecuteQuery();
}
catch (ServerException ex)
{
if (ex.ServerErrorTypeName == "System.IO.FileNotFoundException")
{
// the required folder does not exist - so create it
requiredFolder = allProductsFolder.Folders.Add(requiredFolderName);
clientContext.Load(requiredFolder);
clientContext.ExecuteQuery();
}
else
{
throw;
}
}
foreach (var pricingDocumentWithLocation in pricingDocuments)
{
var fileName = Utility.GetFileName(pricingDocumentWithLocation);
var fileContents = File.ReadAllBytes(pricingDocumentWithLocation);
var fci = new FileCreationInformation();
fci.Content = fileContents;
fci.Url = fileName;
fci.Overwrite = true;
var fileToUpload = requiredFolder.Files.Add(fci);
clientContext.Load(fileToUpload);
clientContext.ExecuteQuery();
}
}
}
// helper class
public static class Utility
{
public static string GetFileName(string fileLocation)
{
if (!string.IsNullOrWhiteSpace(fileLocation))
{
var indexOfLastSlash = fileLocation.LastIndexOf('\\');
if (indexOfLastSlash >= 0)
{
return fileLocation.Substring(indexOfLastSlash + 1);
}
}
return string.Empty;
}
}
The above plugin code creates a SharePoint folder for each "product" record in Dynamics CRM, if the folder for that product does not already exists and uploads the documents that are found in the filesystem folder to that SharePoint folder. Also, note the naming used for the folders, it is the same way in which Dynamics CRM names the folder for each of the product record out of the box.
Hope it helps make it easy to write your own plugin when you have a similar requirement to implement.