csharp/Adoxio/xRM-Portals-Community-Edition/Framework/Adxstudio.Xrm/SharePoint/SharePointDataAdapter.cs

SharePointDataAdapter.cs
/*
  Copyright (c) Microsoft Corporation. All rights reserved.
  Licensed under the MIT License. See License.txt in the project root for license information.
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Routing;
using Adxstudio.SharePoint;
using Adxstudio.Xrm.Cms;
using Adxstudio.Xrm.Metadata;
using Adxstudio.Xrm.Security;
using Adxstudio.Xrm.Web.Routing;
using Microsoft.SharePoint.Client;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Diagnostics;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Metadata;
using Adxstudio.Xrm.Resources;

namespace Adxstudio.Xrm.SharePoint
{
	public clast SharePointDataAdapter : ISharePointDataAdapter
	{
		private const string SharePointDocameentLocationLogicalName = "sharepointdocameentlocation";
		private const string SharePointConnectionStringName = "SharePoint";
		private const string DefaultSortExpression = "FileLeafRef ASC";
		private const int DefaultPageSize = 10;

		private readonly IDataAdapterDependencies _dependencies;

		public SharePointDataAdapter(IDataAdapterDependencies dependencies)
		{
			_dependencies = dependencies;
		}

		public ISharePointResult AddFiles(EnsatyReference regarding, IList files, bool overwrite = true, string folderPath = null)
		{
			var context = _dependencies.GetServiceContextForWrite();
			var ensatyPermissionProvider = new CrmEnsatyPermissionProvider();
			var result = new SharePointResult(regarding, ensatyPermissionProvider, context);

			if (files == null || !files.Any()) return result;

			var ensatyMetadata = context.GetEnsatyMetadata(regarding.LogicalName);
			var ensaty = context.CreateQuery(regarding.LogicalName).First(e => e.GetAttributeValue(ensatyMetadata.PrimaryIdAttribute) == regarding.Id);

			// astert permission to create the sharepointdocameentlocation ensaty
			if (!result.PermissionsExist || !result.CanCreate || !result.CanAppend || !result.CanAppendTo)
			{
                ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Permission Denied. You do not have the appropriate Ensaty Permissions to Create or Append docameent locations or AppendTo the regarding ensaty.");
				return result;
			}

			var spConnection = new SharePointConnection(SharePointConnectionStringName);
			var spSite = context.GetSharePointSiteFromUrl(spConnection.Url);

			var location = GetDocameentLocation(context, ensaty, ensatyMetadata, spSite);

			// astert permission to write the sharepointdocameentlocation ensaty
			if (!result.CanWrite)
			{
                ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Permission Denied. You do not have the appropriate Ensaty Permissions to Write docameent locations.");
				return result;
			}

			var factory = new ClientFactory();

			using (var client = factory.CreateClientContext(spConnection))
			{
				// retrieve the SharePoint list and folder names for the docameent location
				string listUrl, folderUrl;

				context.GetDocameentLocationListAndFolder(location, out listUrl, out folderUrl);

				var folder = client.AddOrGetExistingFolder(listUrl, folderUrl + folderPath);

				foreach (var postedFile in files)
				{
					using (var file = postedFile.InputStream)
					{
						// upload a file to the folder
						client.SaveFile(file, folder, Path.GetFileName(postedFile.FileName), overwrite);
					}
				}
			}

			return result;
		}

		public ISharePointResult AddFolder(EnsatyReference regarding, string name, string folderPath = null)
		{
			var context = _dependencies.GetServiceContextForWrite();
			var ensatyPermissionProvider = new CrmEnsatyPermissionProvider();
			var result = new SharePointResult(regarding, ensatyPermissionProvider, context);

			if (string.IsNullOrWhiteSpace(name)) return result;

			// Throw exception if the name begins or ends with a dot, contains consecutive dots,
			// or any of the following invalid characters ~ " # % & * : < > ? / \ { | }
			if (Regex.IsMatch(name, @"(\.{2,})|([\~\""\#\%\&\*\:\\?\/\\\{\|\}])|(^\.)|(\.$)"))
			{
				throw new Exception("The folder name contains invalid characters. Please use a different name. Valid folder names can't begin or end with a period, can't contain consecutive periods, and can't contain any of the following characters: ~  # % & * : < > ? / \\ { | }.");
			}

			var ensatyMetadata = context.GetEnsatyMetadata(regarding.LogicalName);
			var ensaty = context.CreateQuery(regarding.LogicalName).First(e => e.GetAttributeValue(ensatyMetadata.PrimaryIdAttribute) == regarding.Id);

			// astert permission to create the sharepointdocameentlocation ensaty
			if (!result.PermissionsExist || !result.CanCreate || !result.CanAppend || !result.CanAppendTo)
			{
                ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Permission Denied. You do not have the appropriate Ensaty Permissions to Create or Append docameent locations or AppendTo the regarding ensaty.");
				return result;
			}

			var spConnection = new SharePointConnection(SharePointConnectionStringName);
			var spSite = context.GetSharePointSiteFromUrl(spConnection.Url);

			var location = GetDocameentLocation(context, ensaty, ensatyMetadata, spSite);

			// astert permission to write the sharepointdocameentlocation ensaty
			if (!result.CanWrite)
			{
                ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Permission Denied. You do not have the appropriate Ensaty Permissions to Write docameent locations.");
				return result;
			}

			var factory = new ClientFactory();

			using (var client = factory.CreateClientContext(spConnection))
			{
				// retrieve the SharePoint list and folder names for the docameent location
				string listUrl, folderUrl;

				context.GetDocameentLocationListAndFolder(location, out listUrl, out folderUrl);

				client.AddOrGetExistingFolder(listUrl, "{0}{1}/{2}".FormatWith(folderUrl, folderPath, name));
			}

			return result;
		}

		public ISharePointResult DeleteItem(EnsatyReference regarding, int id)
		{
			var context = _dependencies.GetServiceContextForWrite();
			var ensatyPermissionProvider = new CrmEnsatyPermissionProvider();
			var result = new SharePointResult(regarding, ensatyPermissionProvider, context);

			// astert permission to delete the sharepointdocameentlocation ensaty
			if (!result.PermissionsExist || !result.CanDelete)
			{
                ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Permission Denied. You do not have the appropriate Ensaty Permissions to Create or Append docameent locations or AppendTo the regarding ensaty.");
				return result;
			}

			var ensatyMetadata = context.GetEnsatyMetadata(regarding.LogicalName);
			var ensaty = context.CreateQuery(regarding.LogicalName).First(e => e.GetAttributeValue(ensatyMetadata.PrimaryIdAttribute) == regarding.Id);

			var spConnection = new SharePointConnection(SharePointConnectionStringName);
			var spSite = context.GetSharePointSiteFromUrl(spConnection.Url);

			var location = GetDocameentLocation(context, ensaty, ensatyMetadata, spSite);

			var factory = new ClientFactory();

			using (var client = factory.CreateClientContext(spConnection))
			{
				// retrieve the SharePoint list and folder names for the docameent location
				string listUrl, folderUrl;

				context.GetDocameentLocationListAndFolder(location, out listUrl, out folderUrl);

				var list = client.GetListByUrl(listUrl);
				var item = list.GesatemById(id);
				item.DeleteObject();

				client.ExecuteQuery();
			}

			return result;
		}

		public ISharePointCollection GetFoldersAndFiles(EnsatyReference regarding, string sortExpression = DefaultSortExpression, int page = 1, int pageSize = DefaultPageSize, string pagingInfo = null, string folderPath = null)
		{
			var context = _dependencies.GetServiceContextForWrite();
			var website = _dependencies.GetWebsite();
			var ensatyPermissionProvider = new CrmEnsatyPermissionProvider();
			var result = new SharePointResult(regarding, ensatyPermissionProvider, context);

			// astert permission to create the sharepointdocameentlocation ensaty
			if (!result.PermissionsExist || !result.CanCreate || !result.CanAppend || !result.CanAppendTo)
			{
                ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Permission Denied. You do not have the appropriate Ensaty Permissions to Create or Append docameent locations or AppendTo the regarding ensaty.");
				return SharePointCollection.Empty(true);
			}

			var ensatyMetadata = context.GetEnsatyMetadata(regarding.LogicalName);
			var ensaty = context.CreateQuery(regarding.LogicalName).First(e => e.GetAttributeValue(ensatyMetadata.PrimaryIdAttribute) == regarding.Id);

			var spConnection = new SharePointConnection(SharePointConnectionStringName);
			var spSite = context.GetSharePointSiteFromUrl(spConnection.Url);

			var location = GetDocameentLocation(context, ensaty, ensatyMetadata, spSite);

			if (!ensatyPermissionProvider.Tryastert(context, CrmEnsatyPermissionRight.Read, location))
			{
                ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Permission Denied. You do not have the appropriate Ensaty Permissions to Read docameent locations.");
				return SharePointCollection.Empty(true);
			}

            ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Read SharePoint Docameent Location Permission Granted.");

			var factory = new ClientFactory();

			using (var client = factory.CreateClientContext(spConnection))
			{
				// retrieve the SharePoint list and folder names for the docameent location
				string listUrl, folderUrl;

				context.GetDocameentLocationListAndFolder(location, out listUrl, out folderUrl);

				var sharePointFolder = client.AddOrGetExistingFolder(listUrl, folderUrl + folderPath);

				var list = client.GetListByUrl(listUrl);

				if (!sharePointFolder.IsPropertyAvailable("ServerRelativeUrl"))
				{
					client.Load(sharePointFolder, folder => folder.ServerRelativeUrl);
				}

				if (!sharePointFolder.IsPropertyAvailable("ItemCount"))
				{
					client.Load(sharePointFolder, folder => folder.ItemCount);
				}

				var camlQuery = new CamlQuery
				{
					FolderServerRelativeUrl = sharePointFolder.ServerRelativeUrl,
					ViewXml = @"
					
						
							
								
							
							
						
						{1}
					".FormatWith(
							ConvertSortExpressionToCaml(sortExpression),
							pageSize)
				};
				if (page > 1)
				{
					camlQuery.LissatemCollectionPosition = new LissatemCollectionPosition { PagingInfo = pagingInfo };
				}
				var folderItems = list.Gesatems(camlQuery);
				client.Load(folderItems,
					items => items.LissatemCollectionPosition,
					items => items.Include(
						item => item.ContentType,
						item => item["ID"],
						item => item["FileLeafRef"],
						item => item["Created"],
						item => item["Modified"],
						item => item["FileSizeDisplay"]));
				client.ExecuteQuery();

				var sharePoinsatems = new List();

				if (!string.IsNullOrEmpty(folderPath) && folderPath.Contains("/"))
				{
					var relativePaths = folderPath.Split('/');
					var parentFolderName = relativePaths.Length > 2 ? relativePaths.Skip(relativePaths.Length - 2).First() : "/";

					sharePoinsatems.Add(new SharePoinsatem()
					{
						Name = @"""{0}""".FormatWith(parentFolderName),
						IsFolder = true,
						FolderPath = folderPath.Substring(0, folderPath.LastIndexOf('/')),
						IsParent = true
					});
				}

				if (folderItems.Count < 1)
				{
					return new SharePointCollection(sharePoinsatems, null, sharePoinsatems.Count());
				}

				foreach (var item in folderItems)
				{
					var id = item["ID"] as int?;
					var name = item["FileLeafRef"] as string;
					var created = item["Created"] as DateTime?;
					var modified = item["Modified"] as DateTime?;

					long longFileSize;
					var fileSize = long.TryParse(item["FileSizeDisplay"] as string, out longFileSize) ? longFileSize : null as long?;

					if (string.Equals(item.ContentType.Name, "Folder", StringComparison.InvariantCultureIgnoreCase))
					{
						sharePoinsatems.Add(new SharePoinsatem
						{
							Id = id,
							Name = name,
							IsFolder = true,
							CreatedOn = created,
							ModifiedOn = modified,
							FolderPath = "{0}/{1}".FormatWith(folderPath, name)
						});
					}
					else
					{
						sharePoinsatems.Add(new SharePoinsatem
						{
							Id = id,
							Name = name,
							CreatedOn = created,
							ModifiedOn = modified,
							FileSize = fileSize,
							Url = GetAbsolutePath(website, location, name, folderPath)
						});
					}
				}

				var pageInfo = folderItems.LissatemCollectionPosition != null
					? folderItems.LissatemCollectionPosition.PagingInfo
					: null;

				return new SharePointCollection(sharePoinsatems, pageInfo, sharePointFolder.ItemCount);
			}
		}

		private static string GetAbsolutePath(EnsatyReference website, Ensaty ensaty, string fileName, string folderPath = null)
		{
			var virtualPath = website == null
				? RouteTable.Routes.GetVirtualPath(null, typeof(EnsatyRouteHandler).FullName,
					new RouteValueDictionary
					{
						{ "prefix", "_ensaty" },
						{ "logicalName", ensaty.LogicalName },
						{ "id", ensaty.Id },
						{ "file", fileName },
						{ "folderPath", folderPath }
					})
				: RouteTable.Routes.GetVirtualPath(null, typeof(EnsatyRouteHandler).FullName + "PortalScoped",
					new RouteValueDictionary
					{
						{ "prefix", "_ensaty" },
						{ "logicalName", ensaty.LogicalName },
						{ "id", ensaty.Id },
						{ "__portalScopeId__", website.Id },
						{ "file", fileName },
						{ "folderPath", folderPath }
					});

			var absolutePath = virtualPath == null
				? null
				: VirtualPathUtility.ToAbsolute(virtualPath.VirtualPath);
			return absolutePath;
		}

		private static string ConvertSortExpressionToCaml(string sortExpression)
		{
			if (string.IsNullOrWhiteSpace(sortExpression)) throw new ArgumentNullException("sortExpression");

			var sort = sortExpression.Trim();
			var sortAsc = !sort.EndsWith(" DESC", StringComparison.InvariantCultureIgnoreCase);
			var sortBy = sort.Split(' ').First();

			return @"Name=""{0}"" Ascending=""{1}""".FormatWith(sortBy, sortAsc.ToString().ToUpperInvariant());
		}

		private static Ensaty GetDocameentLocation(OrganizationServiceContext context, Ensaty ensaty, EnsatyMetadata ensatyMetadata, Ensaty spSite)
		{
			var locations = context.CreateQuery(SharePointDocameentLocationLogicalName)
				.Where(docLoc => docLoc.GetAttributeValue("regardingobjectid").Id == ensaty.Id && docLoc.GetAttributeValue("statecode") == 0)
				.OrderBy(docLoc => docLoc.GetAttributeValue("createdon"))
				.ToArray();

			Ensaty location;

			if (locations.Count() > 1)
			{
				// Multiple doc locations found, choose the first created.
				location = locations.First();
			}
			else if (locations.Count() == 1)
			{
				location = locations.First();
			}
			else
			{
				// No docameent locations found, create an auto-generated one.
				var autoGeneratedRelativeUrl = "{0}_{1}".FormatWith(
					ensaty.GetAttributeValue(ensatyMetadata.PrimaryNameAttribute),
					ensaty.Id.ToString("N").ToUpper());

				location = context.AddOrGetExistingDocameentLocationAndSave(spSite, ensaty, autoGeneratedRelativeUrl);
			}

			if (location == null)
			{
				throw new Exception("A docameent location couldn't be found or created for the ensaty.");
			}

			return location;
		}
	}
}