From d229cab7f9e6942739b1c6a471b4c86c1bf1e6db Mon Sep 17 00:00:00 2001 From: Travis Sharp Date: Fri, 27 Oct 2017 09:00:11 -0700 Subject: [PATCH] Cleanup and final packaging. --- AutoNumber-Old/AutoNumber.csproj | 83 ---- AutoNumber-Old/AutoNumberPluginConfig.cs | 17 - AutoNumber-Old/CeledonExtensions.cs | 154 ------- AutoNumber-Old/CeledonPlugin.cs | 409 ---------------- AutoNumber-Old/CreateAutoNumber.cs | 119 ----- AutoNumber-Old/DeleteAutoNumber.cs | 94 ---- AutoNumber-Old/GetNextAutoNumber.cs | 154 ------- AutoNumber-Old/Properties/AssemblyInfo.cs | 38 -- AutoNumber-Old/RuntimeParameter.cs | 436 ------------------ AutoNumber-Old/ValidateAutoNumber.cs | 243 ---------- AutoNumber.Tests/App.config | 40 +- AutoNumber.Tests/AutoNumber.Tests.csproj | 97 ++-- AutoNumber.Tests/Properties/AssemblyInfo.cs | 2 +- .../Properties/Settings.Designer.cs | 2 +- AutoNumber.Tests/UnitTests.cs | 40 +- AutoNumber.Tests/packages.config | 13 +- .../Properties/AssemblyInfo.cs | 2 +- AutoNumber.sln | 11 +- AutoNumber/AutoNumber.csproj | 45 +- AutoNumber/Properties/AssemblyInfo.cs | 2 +- AutoNumber/Properties/Settings.Designer.cs | 2 +- AutoNumber/packages.config | 4 +- AutoNumberUnitTest/AutoNumberUnitTest.csproj | 86 ---- AutoNumberUnitTest/UnitTests.cs | 211 --------- README.md | 9 +- Solutions/CeledonAutoNumber_1_2_0_0.zip | Bin 0 -> 39794 bytes .../CeledonAutoNumber_1_2_0_0_managed.zip | Bin 0 -> 39947 bytes Solutions/WARNING.MD | 1 + 28 files changed, 128 insertions(+), 2186 deletions(-) delete mode 100644 AutoNumber-Old/AutoNumber.csproj delete mode 100644 AutoNumber-Old/AutoNumberPluginConfig.cs delete mode 100644 AutoNumber-Old/CeledonExtensions.cs delete mode 100644 AutoNumber-Old/CeledonPlugin.cs delete mode 100644 AutoNumber-Old/CreateAutoNumber.cs delete mode 100644 AutoNumber-Old/DeleteAutoNumber.cs delete mode 100644 AutoNumber-Old/GetNextAutoNumber.cs delete mode 100644 AutoNumber-Old/Properties/AssemblyInfo.cs delete mode 100644 AutoNumber-Old/RuntimeParameter.cs delete mode 100644 AutoNumber-Old/ValidateAutoNumber.cs delete mode 100644 AutoNumberUnitTest/AutoNumberUnitTest.csproj delete mode 100644 AutoNumberUnitTest/UnitTests.cs create mode 100644 Solutions/CeledonAutoNumber_1_2_0_0.zip create mode 100644 Solutions/CeledonAutoNumber_1_2_0_0_managed.zip create mode 100644 Solutions/WARNING.MD diff --git a/AutoNumber-Old/AutoNumber.csproj b/AutoNumber-Old/AutoNumber.csproj deleted file mode 100644 index 43a0d8e..0000000 --- a/AutoNumber-Old/AutoNumber.csproj +++ /dev/null @@ -1,83 +0,0 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {10E2B428-F093-400A-8F7F-38AA8BCBD434} - {4C25E9B5-9FA6-436c-8E19-B395D2A65FAF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - Celedon - CeledonPartners.AutoNumber - v4.0 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true - - - AutoNumberStrongKey.snk - - - - False - ..\..\..\..\..\CRM 2013 SP1 R1 SDK\SDK\Bin\Microsoft.Crm.Sdk.Proxy.dll - - - False - ..\..\..\..\..\CRM 2013 SP1 R1 SDK\SDK\Bin\Microsoft.Xrm.Sdk.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/AutoNumber-Old/AutoNumberPluginConfig.cs b/AutoNumber-Old/AutoNumberPluginConfig.cs deleted file mode 100644 index 8052e93..0000000 --- a/AutoNumber-Old/AutoNumberPluginConfig.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace Celedon -{ - [DataContract] - public class AutoNumberPluginConfig - { - [DataMember] - public string EntityName; - - [DataMember] - public string EventName; - } -} diff --git a/AutoNumber-Old/CeledonExtensions.cs b/AutoNumber-Old/CeledonExtensions.cs deleted file mode 100644 index b93e24d..0000000 --- a/AutoNumber-Old/CeledonExtensions.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Author: Matt Barnes (matt.barnes@celedonpartners.com) -/*The MIT License (MIT) - -Copyright (c) 2015 Celedon Partners - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Collection of useful extension methods - -using System; -using System.IO; -using System.Text; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; - -using Microsoft.Xrm.Sdk; -using Microsoft.Xrm.Sdk.Query; - -namespace Celedon -{ - // Helpful extentions - // Use these and add to these to make our plugins a little shorter/cleaner/leaner - public static class Extensions - { - // Used to get values from context inputparameters and outputparameters, of a specific type rather than handling the Object type in our code. - public static bool TryGetValue(this ParameterCollection parameterCollection, string key, out T value) - { - object valueObj; - if (parameterCollection.TryGetValue(key, out valueObj)) - { - try - { - value = (T)valueObj; - return true; - } - catch (InvalidCastException) { } // Key exists, but cast failed. Let this fall through to the default return value. - } - - value = default(T); - return false; - } - - // NotNull because Plugins attached to custom Actions recieve ALL input parameters, even if they were not included in the original Action context - public static bool TryGetValueNotNull(this ParameterCollection parameterCollection, string key, out T value) - { - object valueObj; - if (parameterCollection.TryGetValue(key, out valueObj)) - { - if (valueObj != null) - { - try - { - value = (T)valueObj; - return true; - } - catch (InvalidCastException) { } // Key exists, but cast failed. Let this fall through to the default return value. - } - } - - value = default(T); - return false; - } - - // NotNull because Plugins attached to custom Actions recieve ALL input parameters, even if they were not included in the original Action context - public static bool ContainsNotNull(this ParameterCollection parameterCollection, string key) - { - return parameterCollection.Contains(key) && parameterCollection[key] != null; - } - - // Parse JSON string to object - CRM Online compatible - public static T ParseJSON(this string jsonString, bool useSimpleDictionaryFormat = true) - { - try - { - DataContractJsonSerializer JsonDeserializer = new DataContractJsonSerializer(typeof(T)); - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString))) - { - return (T)JsonDeserializer.ReadObject(stream); - } - } - catch - { - throw new InvalidDataContractException("JSON string is invalid, or could not be serialized to the specified type."); - } - } - - // Try Parse JSON string to object - CRM Online compatible - public static bool TryParseJSON(this string jsonString, out T obj, bool useSimpleDictionaryFormat = true) - { - try - { - obj = jsonString.ParseJSON(useSimpleDictionaryFormat); - return true; - } - catch - { - obj = default(T); - return false; - } - } - - // Convert object to JSON string - CRM Online compatible - public static string ToJSON(this object obj, bool useSimpleDictionaryFormat = true) - { - DataContractJsonSerializer JsonSerializer = new DataContractJsonSerializer(obj.GetType()); - using (MemoryStream stream = new MemoryStream()) - { - JsonSerializer.WriteObject(stream, obj); - return Encoding.UTF8.GetString(stream.ToArray()); - } - } - - // A slightly easier way to retreive all columns - public static Entity Retrieve(this IOrganizationService service, string entityName, Guid entityId, bool allColumns) - { - return service.Retrieve(entityName, entityId, new ColumnSet(allColumns)); - } - - // Easily convert Guid to EntityReference - public static EntityReference ToEntityReference(this Guid id, string entityType) - { - return new EntityReference(entityType, id); - } - - // Easily convert integer to OptionSetValue - public static OptionSetValue ToOptionSetValue(this int value) - { - return new OptionSetValue(value); - } - - // This is how the OOB GetService method should have been... - public static T GetService(this IServiceProvider serviceProvider) - { - return (T)serviceProvider.GetService(typeof(T)); - } - } -} diff --git a/AutoNumber-Old/CeledonPlugin.cs b/AutoNumber-Old/CeledonPlugin.cs deleted file mode 100644 index f3b9ca3..0000000 --- a/AutoNumber-Old/CeledonPlugin.cs +++ /dev/null @@ -1,409 +0,0 @@ -// Author: Matt Barnes (matt.barnes@celedonpartners.com) -/*The MIT License (MIT) - -Copyright (c) 2015 Celedon Partners - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Implements the Plugin Workflow Activity. - -using System; -using System.Linq; -using System.ServiceModel; -using System.Globalization; -using System.Collections.ObjectModel; -using Microsoft.Xrm.Sdk; -using Microsoft.Xrm.Sdk.Client; -using Microsoft.Xrm.Sdk.Query; - -namespace Celedon -{ - /// - /// Base class for all Plugins. - /// - /// Modified by Matt Barnes - "I've made a few special modifications myself" - /// - /// - Added the method RegisterEvent(), to simplify each child plugin constructor - /// - Removed the base class constructor with the child class argument (it wasn't needed) - /// - LocalPluginContext class now includes an OrganizationServiceContext object, so it doesn't need to be initialized within each plugin - /// - LocalPluginContext class now implements IDisposable, so the OrganizationServiceContext object gets properly disposed when it is done - /// - Added the Execution Stage constants - /// - Added GetInputParameters() method for early binding of InputParameters - /// - Added GetOutputParameters() method for early binding of OutputParameters - /// - Added PreImage and PostImage - returns the first available image (assumes there is only one) if you have multiple, then retrieve them normally - /// - /// - public class CeledonPlugin : IPlugin - { - public const int PREVALIDATION = 10; - public const int PREOPERATION = 20; - public const int POSTOPERATION = 40; - - public const string CREATEMESSAGE = "Create"; - public const string RETRIEVEMESSAGE = "Retrieve"; - public const string UPDATEMESSAGE = "Update"; - public const string DELETEMESSAGE = "Delete"; - public const string RETRIEVEMULTIPLEMESSAGE = "RetrieveMultiple"; - public const string ASSOCIATEMESSAGE = "Associate"; - public const string DISASSOCIATEMESSAGE = "Disassociate"; - public const string SETSTATEMESSAGE = "SetState"; - - protected class LocalPluginContext : IDisposable - { - internal IServiceProvider ServiceProvider - { - get; - - private set; - } - - internal IOrganizationService OrganizationService - { - get; - - private set; - } - - internal OrganizationServiceContext OrganizationDataContext - { - get; - - private set; - } - - internal IPluginExecutionContext PluginExecutionContext - { - get; - - private set; - } - - internal ITracingService TracingService - { - get; - - private set; - } - - private LocalPluginContext() { } - - internal LocalPluginContext(IServiceProvider serviceProvider) - { - if (serviceProvider == null) - { - throw new ArgumentNullException("serviceProvider"); - } - - // Obtain the execution context service from the service provider. - this.PluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); - - // Obtain the tracing service from the service provider. - this.TracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); - - // Obtain the Organization Service factory service from the service provider - IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); - - // Use the factory to generate the Organization Service. - this.OrganizationService = factory.CreateOrganizationService(this.PluginExecutionContext.UserId); - - // Generate the Organization Data Context - this.OrganizationDataContext = new OrganizationServiceContext(this.OrganizationService); - } - - internal Entity PreImage - { - get { try { return this.PluginExecutionContext.PreEntityImages.Values.First(); } catch { throw new InvalidPluginExecutionException("Pre Image Not Found"); } } - } - - internal Entity PostImage - { - get { try { return this.PluginExecutionContext.PostEntityImages.Values.First(); } catch { throw new InvalidPluginExecutionException("Post Image Not Found"); } } - } - - internal T GetInputParameters() where T : class, ICrmRequest - { - switch (this.PluginExecutionContext.MessageName) - { - case CREATEMESSAGE: - return new CreateInputParameters() { Target = this.PluginExecutionContext.InputParameters["Target"] as Entity } as T; - case UPDATEMESSAGE: - return new UpdateInputParameters() { Target = this.PluginExecutionContext.InputParameters["Target"] as Entity } as T; - case DELETEMESSAGE: - return new DeleteInputParameters() { Target = this.PluginExecutionContext.InputParameters["Target"] as EntityReference } as T; - case RETRIEVEMESSAGE: - return new RetrieveInputParameters() { Target = this.PluginExecutionContext.InputParameters["Target"] as EntityReference, ColumnSet = this.PluginExecutionContext.InputParameters["ColumnSet"] as ColumnSet, RelatedEntitiesQuery = this.PluginExecutionContext.InputParameters["RelatedEntitiesQuery"] as RelationshipQueryCollection } as T; - case RETRIEVEMULTIPLEMESSAGE: - return new RetrieveMultipleInputParameters() { Query = this.PluginExecutionContext.InputParameters["Query"] as QueryBase } as T; - case ASSOCIATEMESSAGE: - return new AssociateInputParameters() { Target = this.PluginExecutionContext.InputParameters["Target"] as EntityReference, Relationship = this.PluginExecutionContext.InputParameters["Relationship"] as Relationship, RelatedEntities = this.PluginExecutionContext.InputParameters["RelatedEntities"] as EntityReferenceCollection } as T; - case DISASSOCIATEMESSAGE: - return new DisassociateInputParameters() { Target = this.PluginExecutionContext.InputParameters["Target"] as EntityReference, Relationship = this.PluginExecutionContext.InputParameters["Relationship"] as Relationship, RelatedEntities = this.PluginExecutionContext.InputParameters["RelatedEntities"] as EntityReferenceCollection } as T; - case SETSTATEMESSAGE: - return new SetStateInputParameters() { EntityMoniker = this.PluginExecutionContext.InputParameters["EntityMoniker"] as EntityReference, State = this.PluginExecutionContext.InputParameters["State"] as OptionSetValue, Status = this.PluginExecutionContext.InputParameters["Status"] as OptionSetValue } as T; - default: - return default(T); - } - } - - internal T GetOutputParameters() where T : class, ICrmResponse - { - if (this.PluginExecutionContext.Stage < POSTOPERATION) - { - throw new InvalidOperationException("OutputParameters only exist during Post-Operation stage."); - } - - switch (this.PluginExecutionContext.MessageName) - { - case CREATEMESSAGE: - return new CreateOutputParameters() { Id = (Guid)this.PluginExecutionContext.OutputParameters["Id"] } as T; - case RETRIEVEMESSAGE: - return new RetrieveOutputParameters() { Entity = this.PluginExecutionContext.OutputParameters["Entity"] as Entity } as T; - case RETRIEVEMULTIPLEMESSAGE: - return new RetrieveMultipleOutputParameters() { EntityCollection = this.PluginExecutionContext.OutputParameters["BusinessEntityCollection"] as EntityCollection } as T; - default: - return default(T); - } - } - - internal void Trace(string message) - { - if (string.IsNullOrWhiteSpace(message) || this.TracingService == null) - { - return; - } - - if (this.PluginExecutionContext == null) - { - this.TracingService.Trace(message); - } - else - { - this.TracingService.Trace("{0} : (Correlation Id: {1}, Initiating User: {2})", message, this.PluginExecutionContext.CorrelationId, this.PluginExecutionContext.InitiatingUserId); - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - if (OrganizationDataContext != null) - { - OrganizationDataContext.Dispose(); - OrganizationDataContext = null; - } - } - } - } - - private Collection>> registeredEvents; - - /// - /// Gets the List of events that the plug-in should fire for. Each List - /// Item is a containing the Pipeline Stage, Message and (optionally) the Primary Entity. - /// In addition, the fourth parameter provide the delegate to invoke on a matching registration. - /// - protected Collection>> RegisteredEvents - { - get - { - if (this.registeredEvents == null) - { - this.registeredEvents = new Collection>>(); - } - - return this.registeredEvents; - } - } - - protected void RegisterEvent(int Stage, string EventName, string EntityName, Action ExecuteMethod) - { - this.RegisteredEvents.Add(new Tuple>(Stage, EventName, EntityName, ExecuteMethod)); - } - - protected delegate void TraceDelegate(string message); - protected TraceDelegate Trace; - - /// - /// Gets or sets the name of the child class. - /// - /// The name of the child class. - protected string ChildClassName - { - get { return this.GetType().Name; } - - //private set; - } - - /// - /// Initializes a new instance of the class. - /// - /// The of the derived class. - //internal Plugin(Type childClassName) - //{ - // this.ChildClassName = childClassName.ToString(); - //} - - /// - /// Executes the plug-in. - /// - /// The service provider. - /// - /// For improved performance, Microsoft Dynamics CRM caches plug-in instances. - /// The plug-in's Execute method should be written to be stateless as the constructor - /// is not called for every invocation of the plug-in. Also, multiple system threads - /// could execute the plug-in at the same time. All per invocation state information - /// is stored in the context. This means that you should not use global variables in plug-ins. - /// - public void Execute(IServiceProvider serviceProvider) - { - if (serviceProvider == null) - { - throw new ArgumentNullException("serviceProvider"); - } - - // Construct the Local plug-in context. - using (LocalPluginContext localcontext = new LocalPluginContext(serviceProvider)) - { - localcontext.Trace(String.Format(CultureInfo.InvariantCulture, "Entered {0}.Execute()", this.ChildClassName)); - - try - { - Trace = m => localcontext.Trace(m); - // Iterate over all of the expected registered events to ensure that the plugin - // has been invoked by an expected event - // For any given plug-in event at an instance in time, we would expect at most 1 result to match. - Action entityAction = - (from a in this.RegisteredEvents - where ( - a.Item1 == localcontext.PluginExecutionContext.Stage && - a.Item2 == localcontext.PluginExecutionContext.MessageName && - (String.IsNullOrWhiteSpace(a.Item3) ? true : a.Item3 == localcontext.PluginExecutionContext.PrimaryEntityName) - ) - select a.Item4).FirstOrDefault(); - - if (entityAction != null) - { - localcontext.Trace(String.Format(CultureInfo.InvariantCulture, "{0} is firing for Entity: {1}, Message: {2}", this.ChildClassName, localcontext.PluginExecutionContext.PrimaryEntityName, localcontext.PluginExecutionContext.MessageName)); - - entityAction.Invoke(localcontext); - - // now exit - if the derived plug-in has incorrectly registered overlapping event registrations, - // guard against multiple executions. - return; - } - } - catch (FaultException e) - { - localcontext.Trace(String.Format(CultureInfo.InvariantCulture, "Exception: {0}", e.ToString())); - - // Handle the exception. - throw; - } - finally - { - localcontext.Trace(String.Format(CultureInfo.InvariantCulture, "Exiting {0}.Execute()", this.ChildClassName)); - } - } - } - - internal interface ICrmRequest { } - - internal class CreateInputParameters : ICrmRequest - { - internal Entity Target; - } - - internal class UpdateInputParameters : ICrmRequest - { - internal Entity Target; - } - - internal class DeleteInputParameters : ICrmRequest - { - internal EntityReference Target; - } - - internal class RetrieveInputParameters : ICrmRequest - { - internal EntityReference Target; - internal ColumnSet ColumnSet; - internal RelationshipQueryCollection RelatedEntitiesQuery; - } - - internal class RetrieveMultipleInputParameters : ICrmRequest - { - internal QueryBase Query; - } - - internal class AssociateInputParameters : ICrmRequest - { - internal EntityReference Target; - internal Relationship Relationship; - internal EntityReferenceCollection RelatedEntities; - } - - internal class DisassociateInputParameters : ICrmRequest - { - internal EntityReference Target; - internal Relationship Relationship; - internal EntityReferenceCollection RelatedEntities; - } - - internal class SetStateInputParameters : ICrmRequest - { - internal EntityReference EntityMoniker; - internal OptionSetValue State; - internal OptionSetValue Status; - } - - internal interface ICrmResponse { } - - internal class CreateOutputParameters : ICrmResponse - { - internal Guid Id; - } - - internal class UpdateOutputParameters : ICrmResponse { } - - internal class DeleteOutputParameters : ICrmResponse { } - - internal class RetrieveOutputParameters : ICrmResponse - { - internal Entity Entity; - } - - internal class RetrieveMultipleOutputParameters : ICrmResponse - { - internal EntityCollection EntityCollection; - } - - internal class AssociateOutputParameters : ICrmResponse { } - - internal class DisassociateOutputParameters : ICrmResponse { } - - internal class SetStateOutputParameters : ICrmResponse { } - } -} \ No newline at end of file diff --git a/AutoNumber-Old/CreateAutoNumber.cs b/AutoNumber-Old/CreateAutoNumber.cs deleted file mode 100644 index 84ac2fe..0000000 --- a/AutoNumber-Old/CreateAutoNumber.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Author: Matt Barnes (matt.barnes@celedonpartners.com) -/*The MIT License (MIT) - -Copyright (c) 2015 Celedon Partners - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Generates a plugin step for an entity, when a new autonumber record is created - -using System; -using System.Linq; -using System.Collections.Generic; -using Microsoft.Xrm.Sdk; - -namespace Celedon -{ - public class CreateAutoNumber : CeledonPlugin - { - // - // This plugin is executed when a new AutoNumber record is created. It generates the plugin steps on the entity type to create each number - // - // Registration Details: - // Message: Create - // Primary Entity: cel_autonumber - // User Context: SYSTEM - // Event Pipeline: Post - // Mode: Async - // Config: none - // - - internal const string PLUGIN_NAME = "CeledonPartners.AutoNumber.{0}"; - - public CreateAutoNumber() - { - RegisterEvent(POSTOPERATION, CREATEMESSAGE, "cel_autonumber", Execute); - } - - protected void Execute(LocalPluginContext Context) - { - Trace("Get Target record"); - Entity Target = Context.GetInputParameters().Target; - string pluginName = String.Format(PLUGIN_NAME, Target.GetAttributeValue("cel_entityname")); - - if (Target.GetAttributeValue("cel_triggerevent").Value == 1) - { - pluginName += " Update"; - } - - Trace("Check for existing plugin step"); - if (Context.OrganizationDataContext.CreateQuery("sdkmessageprocessingstep").Where(s => s.GetAttributeValue("name").Equals(pluginName)).ToList().Any()) - { - return; // Step already exists, nothing to do here. - } - - Trace("Build the configuration"); - AutoNumberPluginConfig config = new AutoNumberPluginConfig() - { - EntityName = Target.GetAttributeValue("cel_entityname"), - EventName = Target.GetAttributeValue("cel_triggerevent").Value == 1 ? "Update" : "Create" - }; - - Trace("Get the Id of this plugin"); - Guid PluginTypeId = Context.OrganizationDataContext.CreateQuery("plugintype") - .Where(s => s.GetAttributeValue("name").Equals("Celedon.GetNextAutoNumber")) - .Select(s => s.GetAttributeValue("plugintypeid")) - .First(); - - Trace("Get the message id from this org"); - Guid messageId = Context.OrganizationDataContext.CreateQuery("sdkmessage") - .Where(s => s.GetAttributeValue("name").Equals(config.EventName)) - .Select(s => s.GetAttributeValue("sdkmessageid")) - .First(); - - Trace("Get the filterId for for the specific entity from this org"); - Guid filterId = Context.OrganizationDataContext.CreateQuery("sdkmessagefilter") - .Where(s => s.GetAttributeValue("primaryobjecttypecode").Equals(config.EntityName) - && s.GetAttributeValue("sdkmessageid").Id.Equals(messageId)) - .Select(s => s.GetAttributeValue("sdkmessagefilterid")) - .First(); - - Trace("Build new plugin step"); - Entity newPluginStep = new Entity("sdkmessageprocessingstep") - { - Attributes = new AttributeCollection() - { - { "name", pluginName }, - { "description", pluginName }, - { "plugintypeid", PluginTypeId.ToEntityReference("plugintype") }, // This plugin type - { "sdkmessageid", messageId.ToEntityReference("sdkmessage") }, // Create or Update Message - { "configuration", config.ToJSON() }, // EntityName and RegisteredEvent in the UnsecureConfig - { "stage", PREOPERATION.ToOptionSetValue() }, // Execution Stage: Pre-Operation - { "rank", 1 }, - { "impersonatinguserid", Context.PluginExecutionContext.UserId.ToEntityReference("systemuser") }, // Run as SYSTEM user. Assumes we are currently running as the SYSTEM user - { "sdkmessagefilterid", filterId.ToEntityReference("sdkmessagefilter") }, - } - }; - - Trace("Create new plugin step"); - Guid pluginStepId = Context.OrganizationService.Create(newPluginStep); - } - } -} diff --git a/AutoNumber-Old/DeleteAutoNumber.cs b/AutoNumber-Old/DeleteAutoNumber.cs deleted file mode 100644 index e0c5a7a..0000000 --- a/AutoNumber-Old/DeleteAutoNumber.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Author: Matt Barnes (matt.barnes@celedonpartners.com) -/*The MIT License (MIT) - -Copyright (c) 2015 Celedon Partners - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Removes the plugin step from an entity, if there are no registered autonumber records - -using System; -using System.Linq; -using System.Collections.Generic; - -using Microsoft.Xrm.Sdk; - -namespace Celedon -{ - public class DeleteAutoNumber : CeledonPlugin - { - // - // This plugin is executed when an AutoNumber record is deleted, it will remove the plugin steps from the associated entity - // - // Registration details: - // Message: Delete - // Primary Entity: cel_autonumber - // User Context: SYSTEM - // Event Pipeline: Post - // Mode: Async - // Config: none - // - // PreImage: - // Name: PreImage - // Alias: PreImage - // Attributes: cel_entityname, cel_attributename - // - public DeleteAutoNumber() - { - //this.RegisteredEvents.Add(new Tuple>(POSTOPERATION, DELETEMESSAGE, "entityname", new Action(Execute))); - RegisterEvent(POSTOPERATION, DELETEMESSAGE, "cel_autonumber", Execute); - } - - protected void Execute(LocalPluginContext Context) - { - int triggerEvent = Context.PreImage.Contains("cel_triggerevent") && Context.PreImage.GetAttributeValue("cel_triggerevent").Value == 1 ? 1 : 0; - - var remainingAutoNumberList = Context.OrganizationDataContext.CreateQuery("cel_autonumber") - .Where(s => s.GetAttributeValue("cel_entityname").Equals(Context.PreImage.GetAttributeValue("cel_entityname"))) - .Select(s => new { Id = s.GetAttributeValue("cel_autonumberid"), TriggerEvent = s.Contains("cel_triggerevent") ? s.GetAttributeValue("cel_triggerevent").Value : 0 }) - .ToList(); - - if (remainingAutoNumberList.Any(s => s.TriggerEvent == triggerEvent )) // If there are still other autonumber records on this entity, then do nothing. - { - return; - } - - // Find and remove the registerd plugin - string pluginName = String.Format(CreateAutoNumber.PLUGIN_NAME, Context.PreImage.GetAttributeValue("cel_entityname")); - if (Context.PreImage.Contains("cel_triggerevent") && Context.PreImage.GetAttributeValue("cel_triggerevent").Value == 1) - { - pluginName += " Update"; - } - - var pluginStepList = Context.OrganizationDataContext.CreateQuery("sdkmessageprocessingstep") - .Where(s => s.GetAttributeValue("name").Equals(pluginName)) - .Select(s => s.GetAttributeValue("sdkmessageprocessingstepid")) - .ToList(); - - if (!pluginStepList.Any()) // Plugin is already deleted, nothing to do here. - { - return; - } - - // Delete plugin step - Context.OrganizationService.Delete("sdkmessageprocessingstep", pluginStepList.First()); - } - } -} diff --git a/AutoNumber-Old/GetNextAutoNumber.cs b/AutoNumber-Old/GetNextAutoNumber.cs deleted file mode 100644 index 7705fb2..0000000 --- a/AutoNumber-Old/GetNextAutoNumber.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Author: Matt Barnes (matt.barnes@celedonpartners.com) -/*The MIT License (MIT) - -Copyright (c) 2015 Celedon Partners - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Gets the next available number and adds it to the Target - -using System; -using System.Linq; -using Microsoft.Xrm.Sdk; -using Microsoft.Xrm.Sdk.Query; - -namespace Celedon -{ - public class getNextAutoNumber : CeledonPlugin - { - // - // This is the main plugin that creates the numbers and adds them to new records - // This plugin is not registered by default. It is registered and unregistered dynamically by the CreateAutoNumber and DeleteAutoNumber plugins respectively - // - - private AutoNumberPluginConfig config; - - public getNextAutoNumber(string pluginConfig, string secureConfig) - { - // Need to support older version - if (pluginConfig.TryParseJSON(out config)) - { - RegisterEvent(PREOPERATION, config.EventName, config.EntityName, Execute); - } - else - { - RegisterEvent(PREOPERATION, CREATEMESSAGE, pluginConfig, Execute); - } - - - } - - protected void Execute(LocalPluginContext context) - { - #region Get the list of autonumber records applicable to the Target entity type - int triggerEvent = context.PluginExecutionContext.MessageName == "Update" ? 1 : 0; - var autoNumberIdList = context.OrganizationDataContext.CreateQuery("cel_autonumber") - .Where(a => a.GetAttributeValue("cel_entityname").Equals(context.PluginExecutionContext.PrimaryEntityName) && a.GetAttributeValue("statecode").Value == 0 && a.GetAttributeValue("cel_triggerevent").Value == triggerEvent) - .OrderBy(a => a.GetAttributeValue("cel_autonumberid")) // Insure they are ordered, to prevent deadlocks - .Select(a => a.GetAttributeValue("cel_autonumberid")); - #endregion - - #region This loop locks the autonumber record(s) so only THIS transaction can read/write it - foreach (Guid autoNumberId in autoNumberIdList) - { - Entity lockingUpdate = new Entity("cel_autonumber"); - lockingUpdate.Id = autoNumberId; - lockingUpdate["cel_preview"] = "555"; // Use the preview field as our "dummy" field - so we don't need a dedicated "dummy" - - context.OrganizationService.Update(lockingUpdate); - } - #endregion - - #region This loop populates the Target record, and updates the autonumber record(s) - Entity Target = context.PluginExecutionContext.InputParameters["Target"] as Entity; - - foreach (Guid autoNumberId in autoNumberIdList) - { - Entity autoNumber = context.OrganizationService.Retrieve("cel_autonumber", autoNumberId, true); - string targetAttribute = autoNumber.GetAttributeValue("cel_attributename"); - - #region Check conditions that prevent creating an autonumber - if (context.PluginExecutionContext.MessageName == "Update" && !Target.Contains(autoNumber.GetAttributeValue("cel_triggerattribute"))) - { - continue; // Continue, if this is an Update event and the Target does not contain the trigger value - } - else if ((autoNumber.Contains("cel_conditionaloptionset") && (!Target.Contains(autoNumber.GetAttributeValue("cel_conditionaloptionset")) || Target.GetAttributeValue(autoNumber.GetAttributeValue("cel_conditionaloptionset")).Value != autoNumber.GetAttributeValue("cel_conditionalvalue")))) - { - continue; // Continue, if this is a conditional optionset - } - else if (Target.Contains(targetAttribute) && !String.IsNullOrWhiteSpace(Target.GetAttributeValue(targetAttribute))) - { - continue; // Continue, so we don't overwrite an existing value - } - #endregion - - #region Create the AutoNumber - int numDigits = autoNumber.GetAttributeValue("cel_digits"); - - // Generate number and insert into Target Record - Target[targetAttribute] = String.Format("{0}{1}{2}", ReplaceParameters(autoNumber.GetAttributeValue("cel_prefix"), Target, context.OrganizationService), - numDigits == 0 ? "" : autoNumber.GetAttributeValue("cel_nextnumber").ToString("D" + numDigits), - ReplaceParameters(autoNumber.GetAttributeValue("cel_suffix"), Target, context.OrganizationService)); - - // Increment next number in db - Entity updatedAutoNumber = new Entity("cel_autonumber"); - updatedAutoNumber.Id = autoNumber.Id; - updatedAutoNumber["cel_nextnumber"] = autoNumber.GetAttributeValue("cel_nextnumber") + 1; - updatedAutoNumber["cel_preview"] = Target[targetAttribute]; // fix the preview - - context.OrganizationService.Update(updatedAutoNumber); - #endregion - } - #endregion - } - - #region Process Runtime Parameters, if any - private string ReplaceParameters(string text, Entity Target, IOrganizationService Service) - { - if (String.IsNullOrWhiteSpace(text)) - { - return ""; - } - - foreach (RuntimeParameter param in RuntimeParameter.GetParametersFromString(text)) - { - if (!param.IsParentParameter()) - { - text = text.Replace(param.ParameterText, param.GetParameterValue(Target)); - } - else - { - if (Target.Contains(param.ParentLookupName)) - { - var parentRecord = Service.Retrieve(Target.GetAttributeValue(param.ParentLookupName).LogicalName, Target.GetAttributeValue(param.ParentLookupName).Id, new ColumnSet(param.AttributeName)); - text = text.Replace(param.ParameterText, param.GetParameterValue(parentRecord)); - } - else // Target record has no parent, so use default value - { - text = text.Replace(param.ParameterText, param.DefaultValue); - } - } - } - - return text; - } - #endregion - } -} diff --git a/AutoNumber-Old/Properties/AssemblyInfo.cs b/AutoNumber-Old/Properties/AssemblyInfo.cs deleted file mode 100644 index c58809e..0000000 --- a/AutoNumber-Old/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AutoNumber")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AutoNumber")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: CLSCompliant(true)] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4dc971d5-7361-46ff-9df8-f6571ce6fc4c")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AutoNumber-Old/RuntimeParameter.cs b/AutoNumber-Old/RuntimeParameter.cs deleted file mode 100644 index df313ec..0000000 --- a/AutoNumber-Old/RuntimeParameter.cs +++ /dev/null @@ -1,436 +0,0 @@ -// Author: Matt Barnes (matt.barnes@celedonpartners.com) -/*The MIT License (MIT) - -Copyright (c) 2015 Celedon Partners - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Class for storing and processing Runtime Parameters in the Autonumber configuration - -using System; -using System.Linq; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using Microsoft.Xrm.Sdk; - -namespace Celedon -{ - public class RuntimeParameter - { - public string ParameterText { get; private set; } - public string AttributeName { get; private set; } - public string ParentLookupName { get; private set; } - public string DefaultValue { get; private set; } - public string StringFormatter { get; private set; } - public ConditionalFormatter Conditional { get; private set; } - - private RuntimeParameter() : this("", "", "", "", "", new ConditionalFormatter()) { } - public RuntimeParameter(string paramText, string attributeText, string parentLookup, string defaultValue, string stringFormat, ConditionalFormatter condition) - { - ParameterText = paramText; - AttributeName = attributeText; - ParentLookupName = parentLookup; - DefaultValue = defaultValue; - StringFormatter = stringFormat; - Conditional = condition; - } - - public static RuntimeParameter Parse(string input) - { - RuntimeParameter rp = new RuntimeParameter(); - rp.ParameterText = input; - rp.AttributeName = input.Trim('{', '}'); - - if (rp.AttributeName.Contains(':')) - { - string[] paramList = rp.AttributeName.Split(':'); - rp.AttributeName = paramList[0]; - - if (rp.AttributeName == "rand") - { - rp.StringFormatter = paramList[1]; - } - else - { - if (paramList[1].Contains('?')) - { - rp.Conditional = ConditionalFormatter.Parse(paramList[1]); - } - else if (paramList.Length > 2) - { - rp.StringFormatter = paramList[1]; - rp.Conditional = ConditionalFormatter.Parse(paramList[2]); - } - else - { - rp.StringFormatter = paramList[1]; - } - } - } - - if (rp.AttributeName.Contains('|')) - { - rp.DefaultValue = rp.AttributeName.Split('|')[1]; - rp.AttributeName = rp.AttributeName.Split('|')[0]; - } - - if (rp.AttributeName.Contains('.')) - { - rp.ParentLookupName = rp.AttributeName.Split('.')[0]; - rp.AttributeName = rp.AttributeName.Split('.')[1]; - } - - return rp; - } - - public static IEnumerable GetParametersFromString(string text) - { - foreach (string p in Regex.Matches(text, @"{(.*?)}").OfType().Select(m => m.Groups[0].Value).Distinct()) - { - yield return Parse(p); - } - } - - public string GetParameterValue(Entity Target) - { - if (Target.Contains(AttributeName)) - { - if (Target[AttributeName] is EntityReference) - { - // Lookup condition is based on GUID - return Conditional.HasCondition ? Conditional.GetResult(Target.GetAttributeValue(AttributeName).Id) : Target.GetAttributeValue(AttributeName).Name; - } - else if (Target[AttributeName] is OptionSetValue) - { - // Conditional OptionSetValue is based on the integer value - return Conditional.HasCondition ? Conditional.GetResult(Target.GetAttributeValue(AttributeName).Value.ToString()) : Target.FormattedValues[AttributeName]; - } - else if (Target[AttributeName] is bool) - { - // Note: Boolean values ignore the match value, they just use the attribute value itself as the condition - return Conditional.HasCondition ? Conditional.GetResult(Target.GetAttributeValue(AttributeName)) : Target.FormattedValues[AttributeName]; - } - else if (Target[AttributeName] is DateTime) - { - // If there is a format AND a condition, apply formatting first, then evaluate condition as a string - // If there is a condition without any format, evaluate condition as DateTime - return String.IsNullOrEmpty(StringFormatter) ? Conditional.GetResult(Target.GetAttributeValue(AttributeName)) : Conditional.GetResult(Target.GetAttributeValue(AttributeName).ToString(StringFormatter)); - } - else if (Target[AttributeName] is Money) - { - return Conditional.HasCondition ? Conditional.GetResult(Target.GetAttributeValue(AttributeName).Value) : Target.GetAttributeValue(AttributeName).Value.ToString(StringFormatter); - } - else if (Target[AttributeName] is int) - { - return Conditional.HasCondition ? Conditional.GetResult(Target.GetAttributeValue(AttributeName)) : Target.GetAttributeValue(AttributeName).ToString(StringFormatter); - } - else if (Target[AttributeName] is decimal) - { - return Conditional.HasCondition ? Conditional.GetResult(Target.GetAttributeValue(AttributeName)) : Target.GetAttributeValue(AttributeName).ToString(StringFormatter); - } - else if (Target[AttributeName] is double) - { - return Conditional.HasCondition ? Conditional.GetResult(Target.GetAttributeValue(AttributeName)) : Target.GetAttributeValue(AttributeName).ToString(StringFormatter); - } - else if (Target[AttributeName] is string) - { - return Conditional.GetResult(Target[AttributeName].ToString()); - } - } - else if (AttributeName.Equals("rand")) - { - string length = ""; - string stringStyle = "upper"; - int stringLength = 5; // Seems like reasonable default - - if (StringFormatter.Contains('?')) - { - length = StringFormatter.Split('?')[0]; - stringStyle = StringFormatter.Split('?')[1].ToLower(); - } - else - { - length = StringFormatter; - } - - if (!Int32.TryParse(length, out stringLength)) - { - stringLength = 5; - } - - string stringValues = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - if (stringStyle == "mix") - { - stringValues = stringValues + stringValues.ToLower(); - } - else if (stringStyle == "lower") - { - stringValues = stringValues.ToLower(); - } - - Random rnd = new Random(); - return String.Join("", Enumerable.Range(0, stringLength).Select(n => stringValues[rnd.Next(stringValues.Length)])); - } - - return DefaultValue; - } - - public bool IsParentParameter() - { - return !String.IsNullOrEmpty(ParentLookupName); - } - - public bool IsRandomParameter() - { - return AttributeName.Equals("rand"); - } - - public class ConditionalFormatter - { - private char Operator; - private ConditionalFormatter FalseCondition = null; - public string MatchValue { get; private set; } - public string TrueValue { get; private set; } - public string FalseValue { get; private set; } - - public ConditionalFormatter() : this("", "", "") { } - public ConditionalFormatter(string matchValue, string trueValue, string falseValue) - { - Operator = matchValue.StartsWith(">") || matchValue.StartsWith("<") ? matchValue[0] : '='; - MatchValue = matchValue.TrimStart('>', '<'); - TrueValue = trueValue; - FalseValue = falseValue; - - if (falseValue.Contains('?')) // if any nested conditions - { - string[] condition1 = falseValue.Split(new char[] { '?' }, 2); - string[] condition2 = condition1[1].Split(new char[] { '|' }, 2); - FalseCondition = new ConditionalFormatter(condition1[0], condition2[0], condition2[1]); - } - } - - public static ConditionalFormatter Parse(string conditionalString) - { - string[] condition1 = conditionalString.Split(new char[] { '?' }, 2); - string[] condition2 = condition1[1].Split(new char[] { '|' }, 2); - - return new ConditionalFormatter(condition1[0], condition2[0], condition2[1]); - } - - public bool HasCondition - { - get { return !String.IsNullOrEmpty(MatchValue); } - } - - public bool IsRecursive - { - get { return FalseCondition != null; } - } - - public string GetResult(string inputText) - { - if (!this.HasCondition) - { - return inputText; - } - - if (this.IsRecursive) - { - return inputText == MatchValue ? TrueValue : FalseCondition.GetResult(inputText); - } - else - { - return inputText == MatchValue ? TrueValue : FalseValue; - } - } - - public string GetResult(Guid inputGuid) - { - if (!this.HasCondition) - { - return inputGuid.ToString(); - } - - if (this.IsRecursive) - { - return CompareGuid(inputGuid) ? TrueValue : FalseCondition.GetResult(inputGuid); - } - else - { - return CompareGuid(inputGuid) ? TrueValue : FalseValue; - } - } - - public string GetResult(int inputInt) - { - if (!this.HasCondition) - { - return inputInt.ToString(); - } - - if (this.IsRecursive) - { - return CompareNumeric(inputInt) ? TrueValue : FalseCondition.GetResult(inputInt); - } - else - { - return CompareNumeric(inputInt) ? TrueValue : FalseValue; - } - } - - public string GetResult(double inputDouble) - { - if (!this.HasCondition) - { - return inputDouble.ToString(); - } - - if (this.IsRecursive) - { - return CompareNumeric((decimal)inputDouble) ? TrueValue : FalseCondition.GetResult(inputDouble); - } - else - { - return CompareNumeric((decimal)inputDouble) ? TrueValue : FalseValue; - } - } - - public string GetResult(decimal inputDecimal) - { - if (!this.HasCondition) - { - return inputDecimal.ToString(); - } - - if (this.IsRecursive) - { - return CompareNumeric((decimal)inputDecimal) ? TrueValue : FalseCondition.GetResult(inputDecimal); - } - else - { - return CompareNumeric((decimal)inputDecimal) ? TrueValue : FalseValue; - } - } - - public string GetResult(DateTime inputDate) - { - if (!this.HasCondition) - { - return inputDate.ToString(); - } - - if (this.IsRecursive) - { - return CompareDateTime(inputDate) ? TrueValue : FalseCondition.GetResult(inputDate); - } - else - { - return CompareDateTime(inputDate) ? TrueValue : FalseValue; - } - } - - private bool CompareGuid(Guid id) - { - Guid matchGuid; - if (Guid.TryParse(MatchValue, out matchGuid)) - { - return id.Equals(matchGuid); - } - else - { - return id.ToString().Equals(MatchValue); - } - } - - private bool CompareDateTime(DateTime date) - { - DateTime matchDate; - if (DateTime.TryParse(MatchValue, out matchDate)) - { - switch (Operator) - { - case '>': - return date > matchDate; - case '<': - return date < matchDate; - default: - return date == matchDate; - } - } - else - { - return date.ToShortDateString() == MatchValue; - } - } - - private bool CompareNumeric(decimal number) - { - decimal matchNumber; - if (Decimal.TryParse(MatchValue, out matchNumber)) - { - switch (Operator) - { - case '>': - return number > matchNumber; - case '<': - return number < matchNumber; - default: - return number == matchNumber; - } - } - else - { - return number.ToString() == MatchValue; - } - } - - public string GetResult(bool value) - { - // Boolean only has 2 possible values, so it doesn't support recursive conditions - return value ? TrueValue : FalseValue; - } - - public override bool Equals(Object obj) - { - return obj is ConditionalFormatter && this == (ConditionalFormatter)obj; - } - public override int GetHashCode() - { - return MatchValue.GetHashCode() ^ TrueValue.GetHashCode() ^ FalseValue.GetHashCode(); - } - public static bool operator ==(ConditionalFormatter x, ConditionalFormatter y) - { - if (Object.ReferenceEquals(null, x) && Object.ReferenceEquals(null, y)) - { - return true; - } - else if (Object.ReferenceEquals(null, x) || Object.ReferenceEquals(null, y)) - { - return false; - } - return x.MatchValue == y.MatchValue && x.TrueValue == y.TrueValue && x.FalseValue == y.FalseValue && x.FalseCondition == y.FalseCondition; - } - public static bool operator !=(ConditionalFormatter x, ConditionalFormatter y) - { - return !(x == y); - } - } - } -} diff --git a/AutoNumber-Old/ValidateAutoNumber.cs b/AutoNumber-Old/ValidateAutoNumber.cs deleted file mode 100644 index 07939e3..0000000 --- a/AutoNumber-Old/ValidateAutoNumber.cs +++ /dev/null @@ -1,243 +0,0 @@ -// Author: Matt Barnes (matt.barnes@celedonpartners.com) -/*The MIT License (MIT) - -Copyright (c) 2015 Celedon Partners - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -// Validation on pre-create of a new AutoNumber record - -#define VALIDATEPARAMETERS // temporarily disable this, until it can be made to work -#define DUPLICATECHECK - -using System; -using System.Linq; -using System.Collections.Generic; - -using Microsoft.Xrm.Sdk; -using Microsoft.Xrm.Sdk.Messages; -using Microsoft.Xrm.Sdk.Metadata; -using System.Text.RegularExpressions; - -namespace Celedon -{ - public class ValidateAutoNumber : CeledonPlugin - { - // - // This Plugin will validate the details of a new AutoNumber record before it is created - // - // Registration Details: - // Message: Create - // Primary Entity: cel_autonumber - // User Context: SYSTEM - // Event Pipeline: PreValidation - // Mode: Sync - // Config: none - // - private LocalPluginContext Context; - private Dictionary> EntityMetadata; - private Dictionary AttributeMetadata; - - public ValidateAutoNumber(string unsecureConfig, string secureConfig) - { - EntityMetadata = new Dictionary>(); - AttributeMetadata = new Dictionary(); - - RegisterEvent(PREVALIDATION, CREATEMESSAGE, "cel_autonumber", Execute); - } - - protected void Execute(LocalPluginContext context) - { - Context = context; - - Trace("Getting Target entity"); - Entity Target = Context.GetInputParameters().Target; - Trace("Validate the Entity name"); - Trace("Get Attribute List"); - List attributeList = GetEntityMetadata(Target.GetAttributeValue("cel_entityname")); - - Trace("Validate the Attribute name"); - if (!attributeList.Select(a => a.LogicalName).Contains(Target.GetAttributeValue("cel_attributename"))) - { - throw new InvalidPluginExecutionException("Specified Attribute does not exist."); - } - - Trace("Validate the Trigger Attribute (if any)"); - if (!String.IsNullOrEmpty(Target.GetAttributeValue("cel_triggerattribute")) && !attributeList.Select(a => a.LogicalName).Contains(Target.GetAttributeValue("cel_triggerattribute"))) - { - throw new InvalidPluginExecutionException("Specified Trigger Attribute does not exist."); - } - - Trace("Validate the Attribute type"); - if (attributeList.Single(a => a.LogicalName.Equals(Target.GetAttributeValue("cel_attributename"))).AttributeType != AttributeTypeCode.String && attributeList.Single(a => a.LogicalName.Equals(Target.GetAttributeValue("cel_attributename"))).AttributeType != AttributeTypeCode.Memo) - { - throw new InvalidPluginExecutionException("Attribute must be a text field."); - } - - #region test parameters -#if VALIDATEPARAMETERS - Dictionary fields = new Dictionary() { { "cel_prefix", "Prefix" }, { "cel_suffix", "Suffix" } }; - - foreach (string field in fields.Keys) - { - if (Target.Contains(field) && Target.GetAttributeValue(field).Contains('{')) - { - if (Target.GetAttributeValue(field).Count(c => c.Equals('{')) != Target.GetAttributeValue(field).Count(c => c.Equals('}'))) - { - throw new InvalidPluginExecutionException(String.Format("Invalid parameter formatting in {0}", fields[field])); - } - - foreach (string p in Regex.Matches(Target.GetAttributeValue(field), @"{(.*?)}").OfType().Select(m => m.Groups[0].Value).Distinct()) - { - if (p.Substring(1).Contains('{')) - { - throw new InvalidPluginExecutionException(String.Format("Invalid parameter formatting in {0}", fields[field])); - } - } - - try - { - foreach (RuntimeParameter param in RuntimeParameter.GetParametersFromString(Target.GetAttributeValue(field))) - { - if (!param.IsParentParameter()) - { - if (!attributeList.Select(a => a.LogicalName).Contains(param.AttributeName)) - { - throw new InvalidPluginExecutionException(String.Format("{0} is not a valid attribute name in {1} value", param.AttributeName, fields[field])); - } - } - else - { - if (!attributeList.Select(a => a.LogicalName).Contains(param.ParentLookupName)) - { - throw new InvalidPluginExecutionException(String.Format("{0} is not a valid attribute name in {1} value", param.ParentLookupName, fields[field])); - } - - if (attributeList.Single(a => a.LogicalName.Equals(param.ParentLookupName)).AttributeType != AttributeTypeCode.Lookup && attributeList.Single(a => a.LogicalName.Equals(param.ParentLookupName)).AttributeType != AttributeTypeCode.Customer && attributeList.Single(a => a.LogicalName.Equals(param.ParentLookupName)).AttributeType != AttributeTypeCode.Owner) - { - throw new InvalidPluginExecutionException(String.Format("{0} must be a Lookup attribute type in {1} value", param.ParentLookupName, fields[field])); - } - - var parentLookupAttribute = (LookupAttributeMetadata)GetAttributeMetadata(Target.GetAttributeValue("cel_entityname"), param.ParentLookupName); - if (!parentLookupAttribute.Targets.Any(e => GetEntityMetadata(e).Select(a => a.LogicalName).Contains(param.AttributeName))) - { - throw new InvalidPluginExecutionException(String.Format("invalid attribute on {0} parent entity, in {1} value", param.ParentLookupName, fields[field])); - } - - } - } - } - catch (InvalidPluginExecutionException) - { - throw; - } - catch - { - throw new InvalidPluginExecutionException(String.Format("Failed to parse Runtime Parameters in {0} value.", fields[field])); - } - } - } -#endif - #endregion - - if (Target.Contains("cel_conditionaloptionset")) - { - Trace("Validate Conditional OptionSet"); - if (!attributeList.Select(a => a.LogicalName).Contains(Target.GetAttributeValue("cel_conditionaloptionset"))) - { - throw new InvalidPluginExecutionException("Specified Conditional OptionSet does not exist"); - } - - if (attributeList.Single(a => a.LogicalName.Equals(Target.GetAttributeValue("cel_conditionaloptionset"))).AttributeType != AttributeTypeCode.Picklist) - { - throw new InvalidPluginExecutionException("Conditional Attribute must be an OptionSet"); - } - - Trace("Validate Conditional Value"); - PicklistAttributeMetadata optionSetMetadata = (PicklistAttributeMetadata)GetAttributeMetadata(Target.GetAttributeValue("cel_entityname"), Target.GetAttributeValue("cel_conditionaloptionset"));//attributeResponse.AttributeMetadata; - if (!optionSetMetadata.OptionSet.Options.Select(o => o.Value).Contains(Target.GetAttributeValue("cel_conditionalvalue"))) - { - throw new InvalidPluginExecutionException("Conditional Value does not exist in OptionSet"); - } - } - - #region Duplicate Check -#if DUPLICATECHECK - Trace("Validate there are no duplicates"); - // TODO: Fix this. duplicate detection works when all fields contain data, but fails when some fields are empty - var autoNumberList = Context.OrganizationDataContext.CreateQuery("cel_autonumber") - .Where(a => a.GetAttributeValue("cel_entityname").Equals(Target.GetAttributeValue("cel_entityname")) && a.GetAttributeValue("cel_attributename").Equals(Target.GetAttributeValue("cel_attributename"))) - .Select(a => new { Id = a.GetAttributeValue("cel_autonumberid"), ConditionalOption = a.GetAttributeValue("cel_conditionaloptionset"), ConditionalValue = a.GetAttributeValue("cel_conditionalvalue") }) - .ToList(); - - - if (!Target.Contains("cel_conditionaloptionset") && autoNumberList.Any()) - { - throw new InvalidPluginExecutionException("Duplicate AutoNumber record exists."); - } - else if (autoNumberList.Where(a => a.ConditionalOption.Equals(Target.GetAttributeValue("cel_conditionaloptionset")) && a.ConditionalValue.Equals(Target.GetAttributeValue("cel_conditionalvalue"))).Any()) - { - throw new InvalidPluginExecutionException("Duplicate AutoNumber record exists."); - } -#endif - #endregion - - Trace("Insert the autoNumber Name attribute"); - Target["cel_name"] = String.Format("AutoNumber for {0}, {1}", Target.GetAttributeValue("cel_entityname"), Target.GetAttributeValue("cel_attributename")); - } - - private AttributeMetadata GetAttributeMetadata(string entityName, string attributeName) - { - string attributeKey = entityName + attributeName; - if (!AttributeMetadata.ContainsKey(attributeKey)) - { - try - { - RetrieveAttributeResponse attributeResponse = (RetrieveAttributeResponse)Context.OrganizationService.Execute(new RetrieveAttributeRequest() { EntityLogicalName = entityName, LogicalName = attributeName }); - AttributeMetadata.Add(attributeKey, attributeResponse.AttributeMetadata); - } - catch - { - throw new InvalidPluginExecutionException(String.Format("{1} attribute does not exist on {0} entity, or entity does not exist.", entityName, attributeName)); - } - } - - return AttributeMetadata[attributeKey]; - } - - private List GetEntityMetadata(string entityName) - { - if (!EntityMetadata.ContainsKey(entityName)) - { - try - { - RetrieveEntityResponse response = (RetrieveEntityResponse)Context.OrganizationDataContext.Execute(new RetrieveEntityRequest() { EntityFilters = EntityFilters.Attributes, LogicalName = entityName }); - EntityMetadata.Add(entityName, response.EntityMetadata.Attributes.ToList()); // Keep the list of Attributes - } - catch - { - throw new InvalidPluginExecutionException(String.Format("{0} Entity does not exist.", entityName)); - } - } - - return EntityMetadata[entityName].ToList(); - } - } -} diff --git a/AutoNumber.Tests/App.config b/AutoNumber.Tests/App.config index a8ebc20..e8b889d 100644 --- a/AutoNumber.Tests/App.config +++ b/AutoNumber.Tests/App.config @@ -1,8 +1,8 @@ - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + diff --git a/AutoNumber.Tests/AutoNumber.Tests.csproj b/AutoNumber.Tests/AutoNumber.Tests.csproj index 25c1bd7..23777b8 100644 --- a/AutoNumber.Tests/AutoNumber.Tests.csproj +++ b/AutoNumber.Tests/AutoNumber.Tests.csproj @@ -8,7 +8,7 @@ Properties AutoNumber.Tests AutoNumber.Tests - v4.5 + v4.0 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 @@ -38,84 +38,38 @@ false - - ..\packages\Microsoft.CrmSdk.Extensions.6.0.4\lib\net40\AntiXSSLibrary.dll - True - - - ..\packages\Castle.Core.4.1.0\lib\net45\Castle.Core.dll - True + + ..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll - ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.1.2\lib\net45\Microsoft.Crm.Sdk.Proxy.dll - True + ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.0.0\lib\net40\Microsoft.Crm.Sdk.Proxy.dll ..\packages\Microsoft.IdentityModel.6.1.7600.16394\lib\net35\Microsoft.IdentityModel.dll True - - ..\packages\WindowsAzure.ServiceBus.2.1.2.0\lib\net40-full\Microsoft.ServiceBus.dll - True - - - ..\packages\Microsoft.WindowsAzure.ConfigurationManager.2.0.0.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll - True - - - ..\packages\Microsoft.CrmSdk.Extensions.6.0.4\lib\net40\Microsoft.Xrm.Client.dll - True - - - ..\packages\Microsoft.CrmSdk.Extensions.6.0.4\lib\net40\Microsoft.Xrm.Client.CodeGeneration.dll - True - - - ..\packages\Microsoft.CrmSdk.Extensions.6.0.4\lib\net40\Microsoft.Xrm.Portal.dll - True - - - ..\packages\Microsoft.CrmSdk.Extensions.6.0.4\lib\net40\Microsoft.Xrm.Portal.Files.dll - True - - ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.1.2\lib\net45\Microsoft.Xrm.Sdk.dll - True + ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.0.0\lib\net40\Microsoft.Xrm.Sdk.dll - - ..\packages\Microsoft.CrmSdk.Deployment.6.1.1\lib\net45\Microsoft.Xrm.Sdk.Deployment.dll - True + + ..\packages\Moq.4.7.142\lib\net45\Moq.dll - - ..\packages\Moq.4.7.63\lib\net45\Moq.dll - True + + ..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - - - - - - - - - - - - - @@ -123,11 +77,7 @@ - - - - - + @@ -141,6 +91,8 @@ + + SettingsSingleFileGenerator @@ -153,6 +105,31 @@ AutoNumber + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoNumber.Tests/Properties/AssemblyInfo.cs b/AutoNumber.Tests/Properties/AssemblyInfo.cs index 78ec067..4d1b783 100644 --- a/AutoNumber.Tests/Properties/AssemblyInfo.cs +++ b/AutoNumber.Tests/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AutoNumber.Tests")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © Celedon Partners 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/AutoNumber.Tests/Properties/Settings.Designer.cs b/AutoNumber.Tests/Properties/Settings.Designer.cs index deb5f81..210ea7d 100644 --- a/AutoNumber.Tests/Properties/Settings.Designer.cs +++ b/AutoNumber.Tests/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace AutoNumber.Tests.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/AutoNumber.Tests/UnitTests.cs b/AutoNumber.Tests/UnitTests.cs index 5093d74..01a3b25 100644 --- a/AutoNumber.Tests/UnitTests.cs +++ b/AutoNumber.Tests/UnitTests.cs @@ -1,13 +1,13 @@ using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Xrm.Sdk; +using NUnit.Framework; namespace Celedon { - [TestClass] + [TestFixture] public class AutoNumberUnitTest { - [TestMethod] + [Test] public void RuntimeParameterParseTest1() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName}"); @@ -22,7 +22,7 @@ public void RuntimeParameterParseTest1() Assert.AreEqual(rp.StringFormatter, String.Empty); } - [TestMethod] + [Test] public void RuntimeParameterParseTest2() { RuntimeParameter rp = RuntimeParameter.Parse("{parentLookup.parentAttribute}"); @@ -36,7 +36,8 @@ public void RuntimeParameterParseTest2() Assert.AreEqual(rp.DefaultValue, String.Empty); Assert.AreEqual(rp.StringFormatter, String.Empty); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest3() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName|defaultValue}"); @@ -50,7 +51,8 @@ public void RuntimeParameterParseTest3() Assert.AreEqual(rp.ParentLookupName, String.Empty); Assert.AreEqual(rp.StringFormatter, String.Empty); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest4() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:formatString}"); @@ -64,7 +66,8 @@ public void RuntimeParameterParseTest4() Assert.AreEqual(rp.DefaultValue, String.Empty); Assert.AreEqual(rp.ParentLookupName, String.Empty); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest5() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:matchValue?trueValue|falseValue}"); @@ -82,7 +85,8 @@ public void RuntimeParameterParseTest5() Assert.AreEqual(rp.Conditional.GetResult("matchValue"), "trueValue"); Assert.AreEqual(rp.Conditional.GetResult("other"), "falseValue"); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest6() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:match1?result1|match2?result2|match3?result3|elseResult}"); @@ -101,7 +105,8 @@ public void RuntimeParameterParseTest6() Assert.AreEqual(rp.Conditional.GetResult("match3"), "result3"); Assert.AreEqual(rp.Conditional.GetResult("other"), "elseResult"); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest7() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:formatString:matchValue?trueValue|falseValue}"); @@ -119,7 +124,8 @@ public void RuntimeParameterParseTest7() Assert.AreEqual(rp.Conditional.GetResult("other"), "falseValue"); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest8() { RuntimeParameter rp = RuntimeParameter.Parse("{parentLookup.parentAttribute:formatString:matchValue?trueValue|falseValue}"); @@ -136,7 +142,8 @@ public void RuntimeParameterParseTest8() Assert.AreEqual(rp.Conditional.GetResult("matchValue"), "trueValue"); Assert.AreEqual(rp.Conditional.GetResult("other"), "falseValue"); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest9() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName|defaultValue:formatString:matchValue?trueValue|falseValue}"); @@ -153,7 +160,8 @@ public void RuntimeParameterParseTest9() Assert.AreEqual(rp.Conditional.GetResult("matchValue"), "trueValue"); Assert.AreEqual(rp.Conditional.GetResult("other"), "falseValue"); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest10() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:>100?trueValue|falseValue}"); @@ -170,7 +178,8 @@ public void RuntimeParameterParseTest10() Assert.AreEqual(rp.Conditional.GetResult(101), "trueValue"); Assert.AreEqual(rp.Conditional.GetResult(99), "falseValue"); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest11() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:<2015-1-1?trueValue|falseValue}"); @@ -187,7 +196,8 @@ public void RuntimeParameterParseTest11() Assert.AreEqual(rp.Conditional.GetResult(new DateTime(2014,1,1)), "trueValue"); Assert.AreEqual(rp.Conditional.GetResult(new DateTime(2016,1,1)), "falseValue"); } - [TestMethod] + + [Test] public void RuntimeParameterParseTest12() { RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:yyyy:2015?trueValue|falseValue}"); @@ -199,7 +209,7 @@ public void RuntimeParameterParseTest12() // Stuff that should not be populated Assert.AreEqual(rp.DefaultValue, String.Empty); Assert.AreEqual(rp.ParentLookupName, String.Empty); - + // Conditional test cases Entity test = new Entity(); test["attributeName"] = new DateTime(2015,1,1); diff --git a/AutoNumber.Tests/packages.config b/AutoNumber.Tests/packages.config index de38e99..118a10b 100644 --- a/AutoNumber.Tests/packages.config +++ b/AutoNumber.Tests/packages.config @@ -1,11 +1,8 @@  - - - - - - - - + + + + + \ No newline at end of file diff --git a/AutoNumber.WebResources/Properties/AssemblyInfo.cs b/AutoNumber.WebResources/Properties/AssemblyInfo.cs index 1bb5990..242ba77 100644 --- a/AutoNumber.WebResources/Properties/AssemblyInfo.cs +++ b/AutoNumber.WebResources/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AutoNumber.WebResources")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © Celedon Partners 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/AutoNumber.sln b/AutoNumber.sln index 75f9773..c72b16a 100644 --- a/AutoNumber.sln +++ b/AutoNumber.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.16 +VisualStudioVersion = 15.0.27004.2002 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoNumber", "AutoNumber\AutoNumber.csproj", "{56E63E6D-35E9-48B5-A549-1321610F197A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoNumber.WebResources", "AutoNumber.WebResources\AutoNumber.WebResources.csproj", "{68670149-40FE-4EA9-A624-572B2C595F69}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoNumber.Tests", "AutoNumber.Tests\AutoNumber.Tests.csproj", "{CBE6269C-2B9B-460C-9AE1-89DA739B30A8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,8 +23,15 @@ Global {68670149-40FE-4EA9-A624-572B2C595F69}.Debug|Any CPU.Build.0 = Debug|Any CPU {68670149-40FE-4EA9-A624-572B2C595F69}.Release|Any CPU.ActiveCfg = Release|Any CPU {68670149-40FE-4EA9-A624-572B2C595F69}.Release|Any CPU.Build.0 = Release|Any CPU + {CBE6269C-2B9B-460C-9AE1-89DA739B30A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBE6269C-2B9B-460C-9AE1-89DA739B30A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBE6269C-2B9B-460C-9AE1-89DA739B30A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBE6269C-2B9B-460C-9AE1-89DA739B30A8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0FC25342-9536-4C7B-996B-F5A0EDFDBF99} + EndGlobalSection EndGlobal diff --git a/AutoNumber/AutoNumber.csproj b/AutoNumber/AutoNumber.csproj index 5b91633..f404a66 100644 --- a/AutoNumber/AutoNumber.csproj +++ b/AutoNumber/AutoNumber.csproj @@ -9,7 +9,7 @@ Properties Celedon Celedon.AutoNumber - v4.5 + v4.0 512 @@ -40,19 +40,17 @@ - ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.1.2\lib\net45\Microsoft.Crm.Sdk.Proxy.dll - True + ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.0.0\lib\net40\Microsoft.Crm.Sdk.Proxy.dll ..\packages\Microsoft.IdentityModel.6.1.7600.16394\lib\net35\Microsoft.IdentityModel.dll True - ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.1.2\lib\net45\Microsoft.Xrm.Sdk.dll - True + ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.0.0\lib\net40\Microsoft.Xrm.Sdk.dll - + @@ -61,11 +59,8 @@ - - - - + @@ -88,15 +83,37 @@ - - Designer - + SettingsSingleFileGenerator Settings.Designer.cs - + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/AutoNumberUnitTest/UnitTests.cs b/AutoNumberUnitTest/UnitTests.cs deleted file mode 100644 index 5093d74..0000000 --- a/AutoNumberUnitTest/UnitTests.cs +++ /dev/null @@ -1,211 +0,0 @@ -using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Microsoft.Xrm.Sdk; - -namespace Celedon -{ - [TestClass] - public class AutoNumberUnitTest - { - [TestMethod] - public void RuntimeParameterParseTest1() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - - // Stuff that should not be populated - Assert.AreEqual(rp.Conditional, new RuntimeParameter.ConditionalFormatter()); - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - Assert.AreEqual(rp.StringFormatter, String.Empty); - } - - [TestMethod] - public void RuntimeParameterParseTest2() - { - RuntimeParameter rp = RuntimeParameter.Parse("{parentLookup.parentAttribute}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "parentAttribute"); - Assert.AreEqual(rp.ParentLookupName, "parentLookup"); - - // Stuff that should not be populated - Assert.AreEqual(rp.Conditional, new RuntimeParameter.ConditionalFormatter()); - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.StringFormatter, String.Empty); - } - [TestMethod] - public void RuntimeParameterParseTest3() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName|defaultValue}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - Assert.AreEqual(rp.DefaultValue, "defaultValue"); - - // Stuff that should not be populated - Assert.AreEqual(rp.Conditional, new RuntimeParameter.ConditionalFormatter()); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - Assert.AreEqual(rp.StringFormatter, String.Empty); - } - [TestMethod] - public void RuntimeParameterParseTest4() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:formatString}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - Assert.AreEqual(rp.StringFormatter, "formatString"); - - // Stuff that should not be populated - Assert.AreEqual(rp.Conditional, new RuntimeParameter.ConditionalFormatter()); - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - } - [TestMethod] - public void RuntimeParameterParseTest5() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:matchValue?trueValue|falseValue}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - Assert.AreEqual(rp.Conditional.MatchValue, "matchValue"); - - // Stuff that should not be populated - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - Assert.AreEqual(rp.StringFormatter, String.Empty); - - // Conditional test cases - Assert.AreEqual(rp.Conditional.GetResult("matchValue"), "trueValue"); - Assert.AreEqual(rp.Conditional.GetResult("other"), "falseValue"); - } - [TestMethod] - public void RuntimeParameterParseTest6() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:match1?result1|match2?result2|match3?result3|elseResult}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - - // Stuff that should not be populated - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - Assert.AreEqual(rp.StringFormatter, String.Empty); - - // Conditional test cases - Assert.AreEqual(rp.Conditional.GetResult("match1"), "result1"); - Assert.AreEqual(rp.Conditional.GetResult("match2"), "result2"); - Assert.AreEqual(rp.Conditional.GetResult("match3"), "result3"); - Assert.AreEqual(rp.Conditional.GetResult("other"), "elseResult"); - } - [TestMethod] - public void RuntimeParameterParseTest7() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:formatString:matchValue?trueValue|falseValue}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - Assert.AreEqual(rp.StringFormatter, "formatString"); - - // Stuff that should not be populated - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - - // Conditional test cases - Assert.AreEqual(rp.Conditional.GetResult("matchValue"), "trueValue"); - Assert.AreEqual(rp.Conditional.GetResult("other"), "falseValue"); - - } - [TestMethod] - public void RuntimeParameterParseTest8() - { - RuntimeParameter rp = RuntimeParameter.Parse("{parentLookup.parentAttribute:formatString:matchValue?trueValue|falseValue}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "parentAttribute"); - Assert.AreEqual(rp.ParentLookupName, "parentLookup"); - Assert.AreEqual(rp.StringFormatter, "formatString"); - - // Stuff that should not be populated - Assert.AreEqual(rp.DefaultValue, String.Empty); - - // Conditional test cases - Assert.AreEqual(rp.Conditional.GetResult("matchValue"), "trueValue"); - Assert.AreEqual(rp.Conditional.GetResult("other"), "falseValue"); - } - [TestMethod] - public void RuntimeParameterParseTest9() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName|defaultValue:formatString:matchValue?trueValue|falseValue}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - Assert.AreEqual(rp.DefaultValue, "defaultValue"); - Assert.AreEqual(rp.StringFormatter, "formatString"); - - // Stuff that should not be populated - Assert.AreEqual(rp.ParentLookupName, String.Empty); - - // Conditional test cases - Assert.AreEqual(rp.Conditional.GetResult("matchValue"), "trueValue"); - Assert.AreEqual(rp.Conditional.GetResult("other"), "falseValue"); - } - [TestMethod] - public void RuntimeParameterParseTest10() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:>100?trueValue|falseValue}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - - // Stuff that should not be populated - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - Assert.AreEqual(rp.StringFormatter, String.Empty); - - // Conditional test cases - Assert.AreEqual(rp.Conditional.GetResult(101), "trueValue"); - Assert.AreEqual(rp.Conditional.GetResult(99), "falseValue"); - } - [TestMethod] - public void RuntimeParameterParseTest11() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:<2015-1-1?trueValue|falseValue}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - - // Stuff that should not be populated - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - Assert.AreEqual(rp.StringFormatter, String.Empty); - - // Conditional test cases - Assert.AreEqual(rp.Conditional.GetResult(new DateTime(2014,1,1)), "trueValue"); - Assert.AreEqual(rp.Conditional.GetResult(new DateTime(2016,1,1)), "falseValue"); - } - [TestMethod] - public void RuntimeParameterParseTest12() - { - RuntimeParameter rp = RuntimeParameter.Parse("{attributeName:yyyy:2015?trueValue|falseValue}"); - - // Stuff that should be populated - Assert.AreEqual(rp.AttributeName, "attributeName"); - Assert.AreEqual(rp.StringFormatter, "yyyy"); - - // Stuff that should not be populated - Assert.AreEqual(rp.DefaultValue, String.Empty); - Assert.AreEqual(rp.ParentLookupName, String.Empty); - - // Conditional test cases - Entity test = new Entity(); - test["attributeName"] = new DateTime(2015,1,1); - Assert.AreEqual(rp.GetParameterValue(test), "trueValue"); - test["attributeName"] = new DateTime(2016, 1, 1); - Assert.AreEqual(rp.GetParameterValue(test), "falseValue"); - } - } -} diff --git a/README.md b/README.md index 0638807..64dc2e8 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,16 @@ The following is required to build AutoNumber: * [Microsoft Visual Studio 2015 or 2017](https://www.visualstudio.com/vs/older-downloads/) * [CRM Developer Toolkit - by Jason Lattimer](https://github.com/jlattimer/CRMDeveloperExtensions) -The current version builds against the Dynamics CRM 2016 - v8.2.0.2 SDK +> The current version builds against the Dynamics CRM 2016 - v6.0 SDK and .Net 4.0. You can [look here](https://blogs.msdn.microsoft.com/crm/2017/02/01/dynamics-365-sdk-backwards-compatibility/) for more information on SDK compatibilities. Since this solution does not connect to CRM Via alternative methods we do not need to update the connectivity support that changed in the later versions of CRM-Online for OAuth support. ## v1.2 +> The plugin distributed with this version is *NOT* compatible with previous versions. You can import this Solution over the existing as an upgrade but you will need to convert the existing auto-number steps to the new plug-in in order to maintain support. + * Updated Code Formatting - *ReSharper* all the things! * Refactored code to be *Thread Safe* -* Updated solution to v8.2 -* Updated SDK to v8.2.0.2 +* Packaged Solution is exported for v8.0 (2016 RTM) - this means support for the v1.2 Solution is supported in 8.x + (Including 9.0) versions of CRM. +* SDK Version is set to 6.0.0 +* Added back the test cases and converted to NUnit so that travis-ci can build ## v1.1: * Supports custom prefix and suffix diff --git a/Solutions/CeledonAutoNumber_1_2_0_0.zip b/Solutions/CeledonAutoNumber_1_2_0_0.zip new file mode 100644 index 0000000000000000000000000000000000000000..28219319d55704e233a91a94c199163ee3e0b44f GIT binary patch literal 39794 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

m%75& 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 K8!+b|cmD-MLG1?s literal 0 HcmV?d00001 diff --git a/Solutions/WARNING.MD b/Solutions/WARNING.MD new file mode 100644 index 0000000..51e06ba --- /dev/null +++ b/Solutions/WARNING.MD @@ -0,0 +1 @@ +If you import the un-managed solution this will overwrite the site-map. This solution was intended for development purposes only! Backup your site-map if you intend to release it with your own internal solutions and restore after importing.