diff --git a/AutoNumber-Old/AutoNumber.csproj b/AutoNumber-Old/AutoNumber.csproj
new file mode 100644
index 0000000..43a0d8e
--- /dev/null
+++ b/AutoNumber-Old/AutoNumber.csproj
@@ -0,0 +1,83 @@
+
+
+
+ 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
new file mode 100644
index 0000000..8052e93
--- /dev/null
+++ b/AutoNumber-Old/AutoNumberPluginConfig.cs
@@ -0,0 +1,17 @@
+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
new file mode 100644
index 0000000..b93e24d
--- /dev/null
+++ b/AutoNumber-Old/CeledonExtensions.cs
@@ -0,0 +1,154 @@
+// 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
new file mode 100644
index 0000000..f3b9ca3
--- /dev/null
+++ b/AutoNumber-Old/CeledonPlugin.cs
@@ -0,0 +1,409 @@
+// 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
new file mode 100644
index 0000000..84ac2fe
--- /dev/null
+++ b/AutoNumber-Old/CreateAutoNumber.cs
@@ -0,0 +1,119 @@
+// 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
new file mode 100644
index 0000000..e0c5a7a
--- /dev/null
+++ b/AutoNumber-Old/DeleteAutoNumber.cs
@@ -0,0 +1,94 @@
+// 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
new file mode 100644
index 0000000..7705fb2
--- /dev/null
+++ b/AutoNumber-Old/GetNextAutoNumber.cs
@@ -0,0 +1,154 @@
+// 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
new file mode 100644
index 0000000..c58809e
--- /dev/null
+++ b/AutoNumber-Old/Properties/AssemblyInfo.cs
@@ -0,0 +1,38 @@
+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
new file mode 100644
index 0000000..df313ec
--- /dev/null
+++ b/AutoNumber-Old/RuntimeParameter.cs
@@ -0,0 +1,436 @@
+// 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
new file mode 100644
index 0000000..07939e3
--- /dev/null
+++ b/AutoNumber-Old/ValidateAutoNumber.cs
@@ -0,0 +1,243 @@
+// 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
new file mode 100644
index 0000000..a8ebc20
--- /dev/null
+++ b/AutoNumber.Tests/App.config
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutoNumber.Tests/AutoNumber.Tests.csproj b/AutoNumber.Tests/AutoNumber.Tests.csproj
new file mode 100644
index 0000000..25c1bd7
--- /dev/null
+++ b/AutoNumber.Tests/AutoNumber.Tests.csproj
@@ -0,0 +1,193 @@
+
+
+
+ Debug
+ AnyCPU
+ {CBE6269C-2B9B-460C-9AE1-89DA739B30A8}
+ Library
+ Properties
+ AutoNumber.Tests
+ AutoNumber.Tests
+ v4.5
+ 512
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
+ False
+ UnitTest
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ 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\Microsoft.CrmSdk.CoreAssemblies.6.1.2\lib\net45\Microsoft.Crm.Sdk.Proxy.dll
+ True
+
+
+ ..\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.Deployment.6.1.1\lib\net45\Microsoft.Xrm.Sdk.Deployment.dll
+ True
+
+
+ ..\packages\Moq.4.7.63\lib\net45\Moq.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+ {56e63e6d-35e9-48b5-a549-1321610f197a}
+ AutoNumber
+
+
+
+
+
+
+ $projectId$
+ $projectName$
+
+
+
+
+
+
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AutoNumber.Tests/MoqExtensions.cs b/AutoNumber.Tests/MoqExtensions.cs
new file mode 100644
index 0000000..1e86583
--- /dev/null
+++ b/AutoNumber.Tests/MoqExtensions.cs
@@ -0,0 +1,30 @@
+using Moq.Language.Flow;
+using System;
+using System.Collections;
+using System.Diagnostics;
+
+namespace AutoNumber.Tests
+{
+ public static class MoqExtensions
+ {
+ public static void ReturnsInOrder(this ISetup setup,
+ params object[] results) where T : class
+ {
+ var queue = new Queue(results);
+ setup.Returns(() =>
+ {
+ var result = queue.Dequeue();
+ if (result is Exception)
+ {
+ throw result as Exception;
+ }
+ return (TResult)result;
+ });
+ }
+
+ public static void WriteTrace(string s, object[] o)
+ {
+ Debug.WriteLine(s);
+ }
+ }
+}
diff --git a/AutoNumber.Tests/Properties/AssemblyInfo.cs b/AutoNumber.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..78ec067
--- /dev/null
+++ b/AutoNumber.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+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.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AutoNumber.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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("ce5c64cf-6dcb-4fad-9158-2eeeb1f0c6fe")]
+
+// 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.Tests/Properties/Settings.Designer.cs b/AutoNumber.Tests/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..deb5f81
--- /dev/null
+++ b/AutoNumber.Tests/Properties/Settings.Designer.cs
@@ -0,0 +1,44 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AutoNumber.Tests.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("PLUGIN")]
+ public string CRMProjectType {
+ get {
+ return ((string)(this["CRMProjectType"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("UNIT")]
+ public string CRMTestType {
+ get {
+ return ((string)(this["CRMTestType"]));
+ }
+ }
+ }
+}
diff --git a/AutoNumber.Tests/Properties/Settings.settings b/AutoNumber.Tests/Properties/Settings.settings
new file mode 100644
index 0000000..f838cc8
--- /dev/null
+++ b/AutoNumber.Tests/Properties/Settings.settings
@@ -0,0 +1,12 @@
+
+
+
+
+
+ PLUGIN
+
+
+ UNIT
+
+
+
\ No newline at end of file
diff --git a/AutoNumber.Tests/UnitTests.cs b/AutoNumber.Tests/UnitTests.cs
new file mode 100644
index 0000000..5093d74
--- /dev/null
+++ b/AutoNumber.Tests/UnitTests.cs
@@ -0,0 +1,211 @@
+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/AutoNumber.Tests/packages.config b/AutoNumber.Tests/packages.config
new file mode 100644
index 0000000..de38e99
--- /dev/null
+++ b/AutoNumber.Tests/packages.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AutoNumber.WebResources/AutoNumber.WebResources.csproj b/AutoNumber.WebResources/AutoNumber.WebResources.csproj
new file mode 100644
index 0000000..74a8c32
--- /dev/null
+++ b/AutoNumber.WebResources/AutoNumber.WebResources.csproj
@@ -0,0 +1,105 @@
+
+
+
+
+ Debug
+ AnyCPU
+
+
+ 2.0
+ {68670149-40FE-4EA9-A624-572B2C595F69}
+ {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ AutoNumber.WebResources
+ AutoNumber.WebResources
+ v4.5
+ true
+
+ enabled
+ enabled
+ false
+
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ Web.config
+
+
+ Web.config
+
+
+
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+
+
+
+
+ True
+ True
+ 18176
+ /
+ http://localhost:18176/
+ False
+ False
+
+
+ False
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AutoNumber.WebResources/Properties/AssemblyInfo.cs b/AutoNumber.WebResources/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..1bb5990
--- /dev/null
+++ b/AutoNumber.WebResources/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+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.WebResources")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AutoNumber.WebResources")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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("7865dd68-63fc-444e-97e8-5bf9c2199dd2")]
+
+// 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 Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AutoNumber.WebResources/Properties/Settings.Designer.cs b/AutoNumber.WebResources/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..f165871
--- /dev/null
+++ b/AutoNumber.WebResources/Properties/Settings.Designer.cs
@@ -0,0 +1,41 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("WEBRESOURCE")]
+ public string CRMProjectType
+ {
+ get
+ {
+ return ((string)(this["CRMProjectType"]));
+ }
+ }
+ }
+}
diff --git a/AutoNumber.WebResources/Properties/Settings.settings b/AutoNumber.WebResources/Properties/Settings.settings
new file mode 100644
index 0000000..39f2a3e
--- /dev/null
+++ b/AutoNumber.WebResources/Properties/Settings.settings
@@ -0,0 +1,9 @@
+
+
+
+
+
+ WEBRESOURCE
+
+
+
\ No newline at end of file
diff --git a/AutoNumber.WebResources/Web.Debug.config b/AutoNumber.WebResources/Web.Debug.config
new file mode 100644
index 0000000..2e302f9
--- /dev/null
+++ b/AutoNumber.WebResources/Web.Debug.config
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AutoNumber.WebResources/Web.Release.config b/AutoNumber.WebResources/Web.Release.config
new file mode 100644
index 0000000..c358444
--- /dev/null
+++ b/AutoNumber.WebResources/Web.Release.config
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AutoNumber.WebResources/Web.config b/AutoNumber.WebResources/Web.config
new file mode 100644
index 0000000..bfb640d
--- /dev/null
+++ b/AutoNumber.WebResources/Web.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WebResources/cel_autonumber.js b/AutoNumber.WebResources/scripts/cel_autonumber.js
similarity index 100%
rename from WebResources/cel_autonumber.js
rename to AutoNumber.WebResources/scripts/cel_autonumber.js
diff --git a/AutoNumber.sln b/AutoNumber.sln
new file mode 100644
index 0000000..82cfa95
--- /dev/null
+++ b/AutoNumber.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+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.Tests", "AutoNumber.Tests\AutoNumber.Tests.csproj", "{CBE6269C-2B9B-460C-9AE1-89DA739B30A8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoNumber.WebResources", "AutoNumber.WebResources\AutoNumber.WebResources.csproj", "{68670149-40FE-4EA9-A624-572B2C595F69}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {56E63E6D-35E9-48B5-A549-1321610F197A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {56E63E6D-35E9-48B5-A549-1321610F197A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {56E63E6D-35E9-48B5-A549-1321610F197A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {56E63E6D-35E9-48B5-A549-1321610F197A}.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
+ {68670149-40FE-4EA9-A624-572B2C595F69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {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
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/AutoNumber/AutoNumber.csproj b/AutoNumber/AutoNumber.csproj
index 43a0d8e..93181d0 100644
--- a/AutoNumber/AutoNumber.csproj
+++ b/AutoNumber/AutoNumber.csproj
@@ -1,17 +1,15 @@
-
+
+
Debug
AnyCPU
- 8.0.30703
- 2.0
- {10E2B428-F093-400A-8F7F-38AA8BCBD434}
- {4C25E9B5-9FA6-436c-8E19-B395D2A65FAF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {56E63E6D-35E9-48B5-A549-1321610F197A}
Library
Properties
- Celedon
- CeledonPartners.AutoNumber
- v4.0
+ AutoNumber
+ AutoNumber
+ v4.5
512
@@ -23,6 +21,7 @@
DEBUG;TRACE
prompt
4
+ false
pdbonly
@@ -31,47 +30,70 @@
TRACE
prompt
4
+ false
true
- AutoNumberStrongKey.snk
+
+
- False
- ..\..\..\..\..\CRM 2013 SP1 R1 SDK\SDK\Bin\Microsoft.Crm.Sdk.Proxy.dll
+ ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.1.2\lib\net45\Microsoft.Crm.Sdk.Proxy.dll
+ True
+
+
+ ..\packages\Microsoft.IdentityModel.6.1.7600.16394\lib\net35\Microsoft.IdentityModel.dll
+ True
- False
- ..\..\..\..\..\CRM 2013 SP1 R1 SDK\SDK\Bin\Microsoft.Xrm.Sdk.dll
+ ..\packages\Microsoft.CrmSdk.CoreAssemblies.6.1.2\lib\net45\Microsoft.Xrm.Sdk.dll
+ True
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+ True
+ True
+ Settings.settings
+
-
-
-
+
+ Designer
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+