/// <summary>
        /// Initializes a new instance of the <see cref="Plugin"/> class.
        /// </summary>
        /// <param name="childClassName">The <see cref=" cred="Type"/> of the derived class.</param>
        //internal Plugin(Type childClassName)
        //{
        //	this.ChildClassName = childClassName.ToString();
        //}

        /// <summary>
        /// Executes the plug-in.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <remarks>
        /// 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.
        /// </remarks>
        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 <LocalPluginContext> 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 <OrganizationServiceFault> 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));
                }
            }
        }
        /// <inheritdoc />
        /// <summary>
        /// Executes the plug-in.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <remarks>
        /// 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.
        /// </remarks>
        public void Execute(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null)
            {
                throw new ArgumentNullException(nameof(serviceProvider));
            }

            // Construct the Local plug-in context.
            using (var localContext = new LocalPluginContext(serviceProvider))
            {
                localContext.Trace($"Entered {ChildClassName}.Execute()");

                try
                {
                    // 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.
                    var entityAction =
                        (from a in RegisteredEvents
                         where (
                             a.Item1 == localContext.PluginExecutionContext.Stage &&
                             a.Item2 == localContext.PluginExecutionContext.MessageName &&
                             (string.IsNullOrWhiteSpace(a.Item3) || a.Item3 == localContext.PluginExecutionContext.PrimaryEntityName)
                             )
                         select a.Item4).FirstOrDefault();

                    if (entityAction == null)
                    {
                        return;
                    }

                    localContext.Trace($"{ChildClassName} is firing for Entity: {localContext.PluginExecutionContext.PrimaryEntityName}, Message: {localContext.PluginExecutionContext.MessageName}");

                    entityAction.Invoke(localContext);
                }
                catch (FaultException <OrganizationServiceFault> e)
                {
                    localContext.Trace($"Exception: {e}");
                    throw;
                }
                finally
                {
                    localContext.Trace($"Exiting {ChildClassName}.Execute()");
                }
            }
        }
Example #3
0
        protected void Execute(LocalPluginContext context)
        {
            context.Trace("Get Target record");
            var target     = context.GetInputParameters <CreateInputParameters>().Target;
            var pluginName = string.Format(PluginName, target.GetAttributeValue <string>("cel_entityname"));

            if (target.GetAttributeValue <OptionSetValue>("cel_triggerevent").Value == 1)
            {
                pluginName += " Update";
            }

            context.Trace("Check for existing plugin step");
            if (context.OrganizationDataContext.CreateQuery("sdkmessageprocessingstep").Where(s => s.GetAttributeValue <string>("name").Equals(pluginName)).ToList().Any())
            {
                return;                  // Step already exists, nothing to do here.
            }

            context.Trace("Build the configuration");
            var config = new AutoNumberPluginConfig()
            {
                EntityName = target.GetAttributeValue <string>("cel_entityname"),
                EventName  = target.GetAttributeValue <OptionSetValue>("cel_triggerevent").Value == 1 ? "Update" : "Create"
            };

            context.Trace("Get the Id of this plugin");
            var pluginTypeId = context.OrganizationDataContext.CreateQuery("plugintype")
                               .Where(s => s.GetAttributeValue <string>("name").Equals(typeof(GetNextAutoNumber).FullName))
                               .Select(s => s.GetAttributeValue <Guid>("plugintypeid"))
                               .First();

            context.Trace("Get the message id from this org");
            var messageId = context.OrganizationDataContext.CreateQuery("sdkmessage")
                            .Where(s => s.GetAttributeValue <string>("name").Equals(config.EventName))
                            .Select(s => s.GetAttributeValue <Guid>("sdkmessageid"))
                            .First();

            context.Trace("Get the filterId for for the specific entity from this org");
            var filterId = context.OrganizationDataContext.CreateQuery("sdkmessagefilter")
                           .Where(s => s.GetAttributeValue <string>("primaryobjecttypecode").Equals(config.EntityName) &&
                                  s.GetAttributeValue <EntityReference>("sdkmessageid").Id.Equals(messageId))
                           .Select(s => s.GetAttributeValue <Guid>("sdkmessagefilterid"))
                           .First();

            context.Trace("Build new plugin step");
            var 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", PipelineStage.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") },
                }
            };

            context.Trace("Create new plugin step");
            var sdkmessageprocessingstepid = context.OrganizationService.Create(newPluginStep);

            // only add the image if the type is update, on create a value cannot be overridden
            if (target.GetAttributeValue <OptionSetValue>("cel_triggerevent").Value == 1)
            {
                context.Trace("Build new plugin step image");
                var newPluginStepImage = new Entity("sdkmessageprocessingstepimage")
                {
                    Attributes = new AttributeCollection()
                    {
                        { "sdkmessageprocessingstepid", sdkmessageprocessingstepid.ToEntityReference("sdkmessageprocessingstep") },
                        { "imagetype", 0.ToOptionSetValue() },       // PreImage
                        { "messagepropertyname", "Target" },
                        { "name", "Image" },
                        { "entityalias", "Image" },
                        { "attributes", target.GetAttributeValue <string>("cel_attributename") },      //Only incluce the one attribute we really need.
                    }
                };

                context.Trace("Create new plugin step image");
                context.OrganizationService.Create(newPluginStepImage);
            }
        }
        protected void Execute(LocalPluginContext context)
        {
            context.Trace("Getting Target entity");
            var target = context.GetInputParameters <CreateInputParameters>().Target;

            context.Trace("Validate the Entity name");
            context.Trace("Get Attribute List");

            var attributeList = GetEntityMetadata(context, target.GetAttributeValue <string>("cel_entityname"));

            context.Trace("Validate the Attribute name");
            if (!attributeList.Select(a => a.LogicalName).Contains(target.GetAttributeValue <string>("cel_attributename")))
            {
                throw new InvalidPluginExecutionException("Specified Attribute does not exist.");
            }

            context.Trace("Validate the Trigger Attribute (if any)");
            if (!string.IsNullOrEmpty(target.GetAttributeValue <string>("cel_triggerattribute")) && !attributeList.Select(a => a.LogicalName).Contains(target.GetAttributeValue <string>("cel_triggerattribute")))
            {
                throw new InvalidPluginExecutionException("Specified Trigger Attribute does not exist.");
            }

            context.Trace("Validate the Attribute type");
            if (attributeList.Single(a => a.LogicalName.Equals(target.GetAttributeValue <string>("cel_attributename"))).AttributeType != AttributeTypeCode.String && attributeList.Single(a => a.LogicalName.Equals(target.GetAttributeValue <string>("cel_attributename"))).AttributeType != AttributeTypeCode.Memo)
            {
                throw new InvalidPluginExecutionException("Attribute must be a text field.");
            }

            #region test parameters
#if VALIDATEPARAMETERS
            var fields = new Dictionary <string, string>()
            {
                { "cel_prefix", "Prefix" }, { "cel_suffix", "Suffix" }
            };

            foreach (var field in fields.Keys)
            {
                if (!target.Contains(field) || !target.GetAttributeValue <string>(field).Contains('{'))
                {
                    continue;
                }

                if (target.GetAttributeValue <string>(field).Count(c => c.Equals('{')) != target.GetAttributeValue <string>(field).Count(c => c.Equals('}')))
                {
                    throw new InvalidPluginExecutionException($"Invalid parameter formatting in {fields[field]}");
                }

                if (Regex.Matches(target.GetAttributeValue <string>(field), @"{(.*?)}").OfType <Match>().Select(m => m.Groups[0].Value).Distinct().Any(p => p.Substring(1).Contains('{')))
                {
                    throw new InvalidPluginExecutionException($"Invalid parameter formatting in {fields[field]}");
                }

                try
                {
                    foreach (var param in RuntimeParameter.GetParametersFromString(target.GetAttributeValue <string>(field)))
                    {
                        if (!param.IsParentParameter())
                        {
                            if (!attributeList.Select(a => a.LogicalName).Contains(param.AttributeName))
                            {
                                throw new InvalidPluginExecutionException($"{param.AttributeName} is not a valid attribute name in {fields[field]} value");
                            }
                        }
                        else
                        {
                            if (!attributeList.Select(a => a.LogicalName).Contains(param.ParentLookupName))
                            {
                                throw new InvalidPluginExecutionException($"{param.ParentLookupName} is not a valid attribute name in {fields[field]} value");
                            }

                            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($"{param.ParentLookupName} must be a Lookup attribute type in {fields[field]} value");
                            }

                            var parentLookupAttribute = (LookupAttributeMetadata)GetAttributeMetadata(context, target.GetAttributeValue <string>("cel_entityname"), param.ParentLookupName);

                            if (!parentLookupAttribute.Targets.Any(e => GetEntityMetadata(context, e).Select(a => a.LogicalName).Contains(param.AttributeName)))
                            {
                                throw new InvalidPluginExecutionException($"Invalid attribute on {param.ParentLookupName} parent entity, in {fields[field]} value");
                            }
                        }
                    }
                }
                catch (InvalidPluginExecutionException)
                {
                    throw;
                }
                catch
                {
                    throw new InvalidPluginExecutionException($"Failed to parse Runtime Parameters in {fields[field]} value.");
                }
            }
#endif
            #endregion

            if (target.Contains("cel_conditionaloptionset"))
            {
                context.Trace("Validate Conditional OptionSet");
                if (!attributeList.Select(a => a.LogicalName).Contains(target.GetAttributeValue <string>("cel_conditionaloptionset")))
                {
                    throw new InvalidPluginExecutionException("Specified Conditional OptionSet does not exist");
                }

                if (attributeList.Single(a => a.LogicalName.Equals(target.GetAttributeValue <string>("cel_conditionaloptionset"))).AttributeType != AttributeTypeCode.Picklist)
                {
                    throw new InvalidPluginExecutionException("Conditional Attribute must be an OptionSet");
                }

                context.Trace("Validate Conditional Value");
                var optionSetMetadata = (PicklistAttributeMetadata)GetAttributeMetadata(context, target.GetAttributeValue <string>("cel_entityname"), target.GetAttributeValue <string>("cel_conditionaloptionset"));              //attributeResponse.AttributeMetadata;
                if (!optionSetMetadata.OptionSet.Options.Select(o => o.Value).Contains(target.GetAttributeValue <int>("cel_conditionalvalue")))
                {
                    throw new InvalidPluginExecutionException("Conditional Value does not exist in OptionSet");
                }
            }

            #region Duplicate Check
#if DUPLICATECHECK
            context.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 <string>("cel_entityname").Equals(target.GetAttributeValue <string>("cel_entityname")) && a.GetAttributeValue <string>("cel_attributename").Equals(target.GetAttributeValue <string>("cel_attributename")))
                                 .Select(a => new { Id = a.GetAttributeValue <Guid>("cel_autonumberid"), ConditionalOption = a.GetAttributeValue <string>("cel_conditionaloptionset"), ConditionalValue = a.GetAttributeValue <int>("cel_conditionalvalue") })
                                 .ToList();


            if (!target.Contains("cel_conditionaloptionset") && autoNumberList.Any())
            {
                throw new InvalidPluginExecutionException("Duplicate AutoNumber record exists.");
            }

            if (autoNumberList.Any(a => a.ConditionalOption.Equals(target.GetAttributeValue <string>("cel_conditionaloptionset")) && a.ConditionalValue.Equals(target.GetAttributeValue <int>("cel_conditionalvalue"))))
            {
                throw new InvalidPluginExecutionException("Duplicate AutoNumber record exists.");
            }
#endif
            #endregion

            context.Trace("Insert the autoNumber Name attribute");
            target["cel_name"] = $"AutoNumber for {target.GetAttributeValue<string>("cel_entityname")}, {target.GetAttributeValue<string>("cel_attributename")}";
        }