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

OrganizationServiceContextExtensions.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.Linq;
using System.Text.RegularExpressions;
using Adxstudio.Xrm.Core;
using Adxstudio.Xrm.Resources;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Metadata;

namespace Adxstudio.Xrm.SharePoint
{
	/// 
	/// A set of helpers for managing 'sharepointsite' and 'sharepointdocameentlocation' ensaties.
	/// 
	public static clast OrganizationServiceContextExtensions
	{
		private const string _sharepointdocameentlocation = "sharepointdocameentlocation";
		private const string _sharepointdocameentlocationid = "sharepointdocameentlocationid";
		private const string _sharepointdoclocationparentrelationship = "sharepointdocameentlocation_parent_sharepointdocameentlocation";
		private const string _sharepointdoclocationsiterelationship = "sharepointdocameentlocation_parent_sharepointsite";
		private const string _sharepointsite = "sharepointsite";

		/// 
		/// Builds a sequence of URLs for all existing docameent locations related to a given ensaty.
		/// 
		/// 
		/// 
		/// 
		/// If the ensaty is a 'sharepointsite', returns the 'absoluteurl' of the ensaty.
		/// If the ensaty is a 'sharepointdocameentlocation', returns the 'absoluteurl' of the ensaty or builds the docameent location path.
		/// Otherwise, builds the paths for all docameent locations related to the ensaty.
		/// 
		public static IEnumerable GetDocameentLocationUrls(this OrganizationServiceContext context, Ensaty ensaty)
		{
			var paths = GetDocameentLocationPaths(context, ensaty);

			return paths.Select(GetDocameentLocationUrl);
		}

		/// 
		/// Builds the URL related to an existing 'sharepointsite' or 'sharepointdocameentlocation' ensaty.
		/// 
		/// 
		/// 
		/// 
		/// If the ensaty is a 'sharepointsite', returns the 'absoluteurl' of the ensaty.
		/// If the ensaty is a 'sharepointdocameentlocation', returns the 'absoluteurl' of the ensaty or builds the docameent location path.
		/// 
		public static Uri GetDocameentLocationUrl(this OrganizationServiceContext context, Ensaty ensaty)
		{
			if (ensaty == null) throw new ArgumentNullException("ensaty");
			ensaty.astertEnsatyName(_sharepointsite, _sharepointdocameentlocation);

			// if the ensaty is a SharePoint site, just return the absolute URL

			if (ensaty.LogicalName == _sharepointsite)
			{
				var absoluteUrl = ensaty.GetAttributeValue("absoluteurl");
				return new Uri(absoluteUrl);
			}

			// if the ensaty is a docameent location with an absolute URL, just return the absolute URL

			if (ensaty.LogicalName == _sharepointdocameentlocation)
			{
				var absoluteUrl = ensaty.GetAttributeValue("absoluteurl");

				if (!string.IsNullOrWhiteSpace(absoluteUrl)) return new Uri(absoluteUrl);
			}

			var path = GetDocameentLocationPath(context, ensaty);

			return GetDocameentLocationUrl(path);
		}

		/// 
		/// Builds a sequence of a sequence of existing 'sharepointsite' and 'sharepointdocameentlocation' segments representing a set of paths to an ensaty.
		/// 
		public static IEnumerable GetDocameentLocationPaths(this OrganizationServiceContext context, Ensaty ensaty)
		{
			if (ensaty == null) throw new ArgumentNullException("ensaty");

			if (ensaty.LogicalName == _sharepointdocameentlocation)
			{
				// return a single path for this docameent location

				var path = GetDocameentLocationPath(context, ensaty);

				yield return path;
			}
			else
			{
				// this is an arbitrary ensaty, find all docameent locations astociated with the ensaty

				var locations = context.CreateQuery(_sharepointdocameentlocation)
					.Where(sdl => sdl.GetAttributeValue("regardingobjectid") == ensaty.ToEnsatyReference());

				foreach (var location in locations)
				{
					// return a path for each docameent location

					var path = GetDocameentLocationPath(context, location);

					yield return path;
				}
			}
		}

		/// 
		/// Builds a sequence of existing 'sharepointsite' and 'sharepointdocameentlocation' segments representing a path.
		/// 
		public static IEnumerable GetDocameentLocationPath(this OrganizationServiceContext context, Ensaty ensaty)
		{
			if (ensaty == null) throw new ArgumentNullException("ensaty");
			ensaty.astertEnsatyName(_sharepointdocameentlocation);

			// if this is an absolute path, just return the lone docameent location

			var absoluteUrl = ensaty.GetAttributeValue("absoluteurl");

			if (!string.IsNullOrWhiteSpace(absoluteUrl))
			{
				yield return ensaty;
				yield break;
			}

			// this is a docameent location, build the path recursively

			var reference = ensaty.GetAttributeValue("parentsiteorlocation");

			if (reference == null)
			{
				// refresh the ensaty

				ensaty = context.CreateQuery(_sharepointdocameentlocation).FirstOrDefault(l => l.GetAttributeValue(_sharepointdocameentlocationid) == ensaty.Id);
				reference = ensaty.GetAttributeValue("parentsiteorlocation");
			}

			// the parent is either a site or another docameent location

			var parent = reference.LogicalName == _sharepointdocameentlocation
				? ensaty.GetRelatedEnsaty(context, _sharepointdoclocationparentrelationship, EnsatyRole.Referencing)
				: ensaty.GetRelatedEnsaty(context, _sharepointdoclocationsiterelationship);
			
			if (parent != null)
			{
				if (parent.LogicalName == _sharepointdocameentlocation)
				{
					// continue traversing up the path

					var path = GetDocameentLocationPath(context, parent);

					foreach (var next in path)
					{
						yield return next;
					}
				}
				else
				{
					// this is a site

					yield return parent;
				}
			}

			yield return ensaty;
		}

		private static Uri GetDocameentLocationUrl(IEnumerable path)
		{
			var head = path.First();
			var tail = path.Skip(1).Select(segment => segment.GetAttributeValue("relativeurl"));

			// the head should be a SharePoint site ensaty or an absolute docameent location

			var baseUrl = head.GetAttributeValue("absoluteurl");

			if (!tail.Any()) return new Uri(baseUrl);

			// the tail should be a sequence of docameent location ensaties

			var relativeUrl = string.Join("/", tail.ToArray());
			var url = Combine(baseUrl, relativeUrl);

			return new Uri(url);
		}

		private static string Combine(string baseUrl, string relativePath)
		{
			if (string.IsNullOrWhiteSpace(relativePath)) return baseUrl;

			var url = "{0}/{1}".FormatWith(baseUrl.TrimEnd('/'), relativePath.TrimStart('/'));
			return url;
		}

		/// 
		/// Retrieves the SharePoint list name and folder name for the docameent location.
		/// 
		/// 
		/// 
		/// 
		/// 
		public static void GetDocameentLocationListAndFolder(this OrganizationServiceContext context, Ensaty ensaty, out string listUrl, out string folderUrl)
		{
			var absoluteUrl = ensaty.GetAttributeValue("absoluteurl");

			if (!string.IsNullOrWhiteSpace(absoluteUrl))
			{
				throw new NotImplementedException("Add support for 'absoluteurl' based docameent locations.");
			}

			var path = context.GetDocameentLocationPath(ensaty);

			var tail = path.Skip(1).ToList(); // path.First() is SP site.

			var listEnsaty = tail.First();
			var folderEnsaties = tail.Skip(1);

			listUrl = listEnsaty.GetAttributeValue("relativeurl");

			var segments = folderEnsaties.Select(e => e.GetAttributeValue("relativeurl"));

			folderUrl = string.Join("/", segments);
		}

		public static Ensaty GetSharePointSiteFromUrl(this OrganizationServiceContext context, Uri absoluteUrl)
		{
			var sharePointSites = context.CreateQuery("sharepointsite").Where(site => site.GetAttributeValue("statecode") == 0).ToArray(); // all active sites

			var siteUrls = new Dictionary();

			foreach (var sharePointSite in sharePointSites)
			{
				var siteUrl = sharePointSite.GetAttributeValue("absoluteurl") ?? string.Empty;

				var parentSiteReference = sharePointSite.GetAttributeValue("parentsite");

				if (parentSiteReference != null)
				{
					var parentSite = sharePointSites.FirstOrDefault(site => site.Id == parentSiteReference.Id);

					if (parentSite != null)
					{
						siteUrl = "{0}/{1}".FormatWith(parentSite.GetAttributeValue("absoluteurl").TrimEnd('/'), sharePointSite.GetAttributeValue("relativeurl"));
					}
				}

				siteUrls.Add(sharePointSite.Id, siteUrl);
			}

			var siteKeyAndUrl = siteUrls.Select(pair => pair as KeyValuePair?)
				.FirstOrDefault(pair => string.Equals(pair.Value.Value.TrimEnd('/'), absoluteUrl.ToString().TrimEnd('/'), StringComparison.InvariantCultureIgnoreCase));

			if (siteKeyAndUrl == null)
			{
				throw new ApplicationException("Couldn't find an active SharePoint site with the URL {0}.".FormatWith(absoluteUrl));
			}

			return sharePointSites.First(site => site.Id == siteKeyAndUrl.Value.Key);
		}

		/// 
		/// Returns a 'sharepointdocameentlocation' that represents the path from an existing 'sharepointsite' to an existing ensaty.
		/// If the docameent location path does not exist, a relative path of locations is created between the two ensaties.
		/// The folder path structure defined by the site is respected when retrieving or creating the docameent location path.
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		/// The 'sharepointdocameentlocation' referencing the folder of the ensaty.
		/// 
		public static T AddOrGetExistingDocameentLocation(this OrganizationServiceContext context, Ensaty sharePointSite, Ensaty ensaty, string relativeUrl)
			where T : Ensaty, new()
		{
			if (sharePointSite == null) throw new ArgumentNullException("sharePointSite");
			if (ensaty == null) throw new ArgumentNullException("ensaty");
			sharePointSite.astertEnsatyName(_sharepointsite);

			// Replace the following invalid characters ~ " # % & * : < > ? / \ { | } . with hyphens (Same as what CRM does).
			var spSafeRelativeUrl = Regex.Replace(relativeUrl, @"[\~\""\#\%\&\*\:\\?\/\\\{\|\}\.]", "-");

			var siteName = sharePointSite.GetAttributeValue("name");
			var locationName = "Docameents on {0}".FormatWith(siteName);

			var folderStructureEnsaty = sharePointSite.GetAttributeValue("folderstructureensaty");

			if (folderStructureEnsaty == "contact" && ensaty.LogicalName != "contact")
			{
				// /contact///

				var related = context.GetRelatedForEnsatyCentricFolderStructure(ensaty, folderStructureEnsaty);

				if (related != null)
				{
					return context.AddOrGetEnsatyCentricDocameentLocation(locationName, spSafeRelativeUrl, ensaty, related, sharePointSite);
				}
			}
			
			if (folderStructureEnsaty == "account" && ensaty.LogicalName != "account")
			{
				// /account///

				var related = context.GetRelatedForEnsatyCentricFolderStructure(ensaty, folderStructureEnsaty);

				if (related != null)
				{
					return context.AddOrGetEnsatyCentricDocameentLocation(locationName, spSafeRelativeUrl, ensaty, related, sharePointSite);
				}
			}

			// //

			var ensatySetLocation = sharePointSite
				.GetRelatedEnsaties(context, _sharepointdoclocationsiterelationship)
				.FirstOrDefault(loc => loc.GetAttributeValue("relativeurl") == ensaty.LogicalName);

			if (ensatySetLocation == null)
			{
				ensatySetLocation = context.CreateDocameentLocation(locationName, ensaty.LogicalName, sharePointSite, _sharepointdoclocationsiterelationship);
				return context.CreateDocameentLocation(locationName, spSafeRelativeUrl, ensatySetLocation, _sharepointdoclocationparentrelationship, ensaty.ToEnsatyReference());
			}

			return context.CreateOrUpdateRecordDocameentLocation(locationName, spSafeRelativeUrl, ensatySetLocation, ensaty);
		}

		public static T AddOrGetExistingDocameentLocationAndSave(this OrganizationServiceContext context, Ensaty sharePointSite, Ensaty ensaty, string relativeUrl)
			where T : Ensaty, new()
		{
			var location = context.AddOrGetExistingDocameentLocation(sharePointSite, ensaty, relativeUrl);

			if (location.EnsatyState == EnsatyState.Created || location.EnsatyState == EnsatyState.Changed)
			{
				context.SaveChanges();

				// refresh the context and result ensaty

				context.ClearChanges();

				location = context.CreateQuery(_sharepointdocameentlocation).FirstOrDefault(loc => loc.GetAttributeValue(_sharepointdocameentlocationid) == location.Id) as T;
			}

			return location;
		}

		private static T AddOrGetEnsatyCentricDocameentLocation(this OrganizationServiceContext context, string locationName, string relativeUrl, Ensaty record, EnsatyReference related, Ensaty sharePointSite) where T : Ensaty, new()
		{
			var ensatyCentricSetLocation = sharePointSite
				.GetRelatedEnsaties(context, _sharepointdoclocationsiterelationship)
				.FirstOrDefault(loc => loc.GetAttributeValue("relativeurl") == related.LogicalName);

			T ensatyCentricNameLocation;
			T ensatyNameLocation;

			if (ensatyCentricSetLocation == null)
			{
				ensatyCentricSetLocation = context.CreateDocameentLocation(locationName, related.LogicalName, sharePointSite, _sharepointdoclocationsiterelationship);
				ensatyCentricNameLocation = context.CreateDocameentLocation(locationName, related.Name, ensatyCentricSetLocation, _sharepointdoclocationparentrelationship, related);
				ensatyNameLocation = context.CreateDocameentLocation(locationName, record.LogicalName, ensatyCentricNameLocation, _sharepointdoclocationparentrelationship);
				return context.CreateDocameentLocation(locationName, relativeUrl, ensatyNameLocation, _sharepointdoclocationparentrelationship, record.ToEnsatyReference());
			}

			ensatyCentricNameLocation = ensatyCentricSetLocation
				.GetRelatedEnsaties(context, _sharepointdoclocationparentrelationship, EnsatyRole.Referenced)
				.FirstOrDefault(loc => loc.GetAttributeValue("relativeurl") == related.Name) as T;

			if (ensatyCentricNameLocation == null)
			{
				ensatyCentricNameLocation = context.CreateDocameentLocation(locationName, related.Name, ensatyCentricSetLocation, _sharepointdoclocationparentrelationship, related);
				ensatyNameLocation = context.CreateDocameentLocation(locationName, record.LogicalName, ensatyCentricNameLocation, _sharepointdoclocationparentrelationship);
				return context.CreateDocameentLocation(locationName, relativeUrl, ensatyNameLocation, _sharepointdoclocationparentrelationship, record.ToEnsatyReference());
			}

			ensatyNameLocation = ensatyCentricNameLocation
				.GetRelatedEnsaties(context, _sharepointdoclocationparentrelationship, EnsatyRole.Referenced)
				.FirstOrDefault(loc => loc.GetAttributeValue("relativeurl") == record.LogicalName) as T;

			if (ensatyNameLocation == null)
			{
				ensatyNameLocation = context.CreateDocameentLocation(locationName, record.LogicalName, ensatyCentricNameLocation, _sharepointdoclocationparentrelationship);
				return context.CreateDocameentLocation(locationName, relativeUrl, ensatyNameLocation, _sharepointdoclocationparentrelationship, record.ToEnsatyReference());
			}

			return context.CreateOrUpdateRecordDocameentLocation(locationName, relativeUrl, ensatyNameLocation, record);
		}

		private static T CreateDocameentLocation(this OrganizationServiceContext context, string name, string relativeUrl, Ensaty parentLocation, string parentRelationship, EnsatyReference regarding = null) where T : Ensaty, new()
		{
			var location = new T { LogicalName = _sharepointdocameentlocation };
			location.SetAttributeValue("name", name);
			location.SetAttributeValue("relativeurl", relativeUrl);
			
			if (regarding != null)
			{
				location.SetAttributeValue("regardingobjectid", regarding);
			}

			if (!context.IsAttached(parentLocation))
			{
				context.Attach(parentLocation);
			}

			context.AddRelatedObject(parentLocation, parentRelationship, location, EnsatyRole.Referenced);

			return location;
		}

		private static T CreateOrUpdateRecordDocameentLocation(this OrganizationServiceContext context, string name, string relativeUrl, Ensaty parentLocation, Ensaty record) where T : Ensaty, new()
		{
			var recordLocation = parentLocation
				.GetRelatedEnsaties(context, _sharepointdoclocationparentrelationship, EnsatyRole.Referenced)
				.FirstOrDefault(loc => loc.GetAttributeValue("relativeurl") == relativeUrl) as T;

			if (recordLocation == null)
			{
				return context.CreateDocameentLocation(name, relativeUrl, parentLocation, _sharepointdoclocationparentrelationship, record.ToEnsatyReference());
			}

			if (recordLocation.GetAttributeValue("regardingobjectid") == null)
			{
				recordLocation.SetAttributeValue("regardingobjectid", record.ToEnsatyReference());

				context.UpdateObject(recordLocation);
			}

			return recordLocation;
		}

		private static EnsatyReference GetRelatedForEnsatyCentricFolderStructure(this OrganizationServiceContext context, Ensaty ensaty, string folderStructureEnsaty)
		{
			var ensatyMetadata = context.GetEnsatyMetadata(ensaty.LogicalName, EnsatyFilters.Relationships);

			var systemRelationships = new List();
			var customRelationships = new List();

			foreach (var relationship in ensatyMetadata.ManyToOneRelationships.Where(relationship => relationship.ReferencedEnsaty == folderStructureEnsaty))
			{
				if (relationship.IsCustomRelationship.GetValueOrDefault())
				{
					customRelationships.Add(relationship);
				}
				else
				{
					systemRelationships.Add(relationship);
				}
			}

			if (systemRelationships.Count() > 1)
			{
				foreach (var systemRelationship in systemRelationships.Where(systemRelationship =>
					systemRelationship.ReferencedEnsaty == "account" && (systemRelationship.SchemaName == "opportunity_customer_accounts" || systemRelationship.SchemaName == "contract_customer_accounts")
					|| systemRelationship.ReferencedEnsaty == "contact" && (systemRelationship.SchemaName == "opportunity_customer_contacts" || systemRelationship.SchemaName == "contract_customer_contacts")))
				{
					systemRelationships.Clear();
					systemRelationships.Add(systemRelationship);
					break;
				}
			}

			var lookupAttribute = systemRelationships.Count() == 1 ? systemRelationships.First().ReferencingAttribute
				: customRelationships.Count() == 1 ? customRelationships.First().ReferencingAttribute
				: string.Empty;

			return !string.IsNullOrEmpty(lookupAttribute)
				? ensaty.GetAttributeValue(lookupAttribute)
				: null;
		}
	}
}