Showing posts with label Plugins. Show all posts
Showing posts with label Plugins. Show all posts

Friday, June 7, 2013

CRM 2011 - Auto assign a role to user when a New User is created/Updated

We had a recent requirement where we needed to assign a specific role to a user on user create using a plugin. The below is the complete source code of the plugin that had been written.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Messages; 


namespace AssignDefaultRole
{
    public class AssignRoleCS : IPlugin
    {        
        Guid systemUserGuid = Guid.Empty;
        EntityReference buLookup;
        Guid buid = Guid.Empty;
        EntityCollection userRolesColl = null;
        Entity entitySystemUser;
        string securityRole = string.Empty;

        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);

            
            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                Entity primaryEntity = (Entity)context.InputParameters["Target"]; // systemuser
                if (primaryEntity.LogicalName != "systemuser" && context.MessageName.ToLower()!="update")
                {
                    return;
                }
                else if (context.PostEntityImages.Contains("PostUserImage") && context.PostEntityImages["PostUserImage"] is Entity)
                {
                    entitySystemUser = (Entity)context.PostEntityImages["PostUserImage"];
                    buLookup = (EntityReference)entitySystemUser.Attributes["businessunitid"];
                    buid = buLookup.Id;                   
                    systemUserGuid = new Guid(entitySystemUser.Attributes["systemuserid"].ToString());

                    //Check if the user already has the Default Role
                    bool userHasRole = CheckRoleExists(service);

                    if (!userHasRole)//user does not have the Default role
                    {
                        //Get the Default Role
                        Guid DefaultSecurityRoleId = GetSecurityRoleId(service, buid, securityRole);

                        //Assign the role to the user.
                        AssignSecurityRole(systemUserGuid, DefaultSecurityRoleId, service);
                    }
                }
            }          

          
        }

        /// 
        /// Check if the user already has the default role assigned.
        ///   
        private bool CheckRoleExists(IOrganizationService service)
        {
            try
            {
                //UserGuid = context.InitiatingUserId;
                securityRole = "Default Privileges Role";
                QueryExpression Query = new QueryExpression()
                {
                    LinkEntities = 
                    {
                        new LinkEntity
                        {
                            LinkFromEntityName = "role",
                            LinkFromAttributeName = "roleid",
                            LinkToEntityName = "systemuserroles",
                            LinkToAttributeName = "roleid",
                            LinkCriteria = new FilterExpression
                            {
                                FilterOperator = LogicalOperator.And,
                                Conditions = 
                                {
                                    new ConditionExpression
                                    {
                                        AttributeName = "systemuserid",
                                        Operator = ConditionOperator.Equal,
                                        Values = { systemUserGuid }
                                    }
                                }
                            }
                        }
                    }
                };

                Query.ColumnSet = new ColumnSet(true);
                Query.EntityName = "role";

                userRolesColl = service.RetrieveMultiple(Query);
               
                //If the role exists, return true
                if (userRolesColl.Entities.Count > 0 && userRolesColl.Entities[0].Attributes["name"].ToString() == securityRole)
                {
                    return true;
                }

                return false;

            }
            catch (System.Web.Services.Protocols.SoapException ex)
            {
                throw new InvalidPluginExecutionException(ex.Detail.InnerText);
            }
            catch (Exception ex)
            {
                throw new InvalidPluginExecutionException(ex.Message);
            }
        }


        /// 
        /// Get the Role ID of the Default CRM 2011 role
        ///      
        private Guid GetSecurityRoleId(IOrganizationService service,Guid businessUnitID,string roleName)
        {
            Guid SecurityRoleId = Guid.Empty;
            EntityCollection ec = new EntityCollection();

            try
            {
                QueryExpression qe = new QueryExpression("role");
                ColumnSet set = new ColumnSet(new string[] { "roleid" });

                qe.ColumnSet = set;

                ConditionExpression cond = new ConditionExpression("name", ConditionOperator.Equal, roleName);
                ConditionExpression cond2 = new ConditionExpression("businessunitid", ConditionOperator.Equal, businessUnitID);
                FilterExpression fl = new FilterExpression(LogicalOperator.And);
                fl.Conditions.Add(cond);
                fl.Conditions.Add(cond2);

                qe.Criteria.AddFilter(fl);

                ec = service.RetrieveMultiple(qe);

                if (ec.Entities.Count > 0)
                {
                    SecurityRoleId = new Guid(ec.Entities[0].Attributes["roleid"].ToString());
                }
            }

            catch (System.Web.Services.Protocols.SoapException soapex)
            {
                throw new InvalidPluginExecutionException(soapex.Detail.InnerText);
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return SecurityRoleId;
        }


        /// 
        /// Assign the Default CRM 2011 Role to the user
        /// 
        public void AssignSecurityRole(Guid userID, Guid securityRoleID, IOrganizationService service)
        {
            try
            {
                service.Associate(
                               "role",
                               securityRoleID,
                               new Relationship("systemuserroles_association"),
                               new EntityReferenceCollection() { new EntityReference("systemuser", userID) });
            }
            catch (System.Web.Services.Protocols.SoapException soapex)
            {
                throw new InvalidPluginExecutionException(soapex.Detail.InnerText);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        
    }
}



The above code first checks if the user has any role assigned. If not, it goes ahead and assigns the Default Security Role to the user.

After building the plugin, I had it registered in the following manner:

Notice that for both "Create" and "Update" events, I have registered just the one step displayed above

I am not exactly sure why, but I noticed that even if I register my plugin for the Create event, the plugin also automatically fires another update event. So, I have just registered my plugin for the update event and this works for both the create and the update.

Another issue I was facing was when I tried to register this plugin in a synchronous manner. I would keep getting the error message "Error: usersettings With Id = 92bd1403-6ace-e211-a1c4-0800279f0ce3 Does Not Exist" in the trace logs.

In order to rid myself of the error and in order to ensure that the plugin only fires once per transaction, I have registered it ONLY for the "Update" message and in "Asynchronous" execution mode.

Cheers!

Friday, March 4, 2011

PostCreate/UpdatePlugin

Here, when a custom entity is created or updated, I am updating a varchar field in the related contact entity
public void Execute(IPluginExecutionContext context)
        {
            DynamicEntity entity=null;

            //Check if the input parameters contains a target of type Dynamic Entity
            if (context.InputParameters.Properties.Contains("Target") &&
                context.InputParameters.Properties["Target"] is DynamicEntity)
            {
                //obtain the entity from the input parameters
                entity = (DynamicEntity)context.InputParameters.Properties["Target"];

                //TextWriter log1 = TextWriter.Synchronized(File.AppendText(@"D:\Srihari Backup\Log.txt"));
                if (entity.Name == "new_testEntity")
                {
                    
                    try
                    {
                        //log1.WriteLine("inside try");                       

                        
                        DynamicEntity testEntity= (DynamicEntity)context.InputParameters[ParameterName.Target];
                        string clinicName = String.Empty;
                        string contactId= String.Empty;

                        if (testEntity.Properties.Contains("new_clinicname"))
                        {
                            clinicName = (string)testEntity["new_clinicname"];
                            //log1.WriteLine(clinicName);
                        }
                        if (context.MessageName == MessageName.Create)
                        {
                            if (testEntity.Properties.Contains("new_contactid"))
                            {
                                //log1.WriteLine("contains patient id");                                

              //Since the contact is a lookup, cast the object as a lookup.
                                //log1.WriteLine("create message :-");
                                Lookup contactLookup = (Lookup)testEntity["new_contactid"];
                                contactLookup.type = EntityName.contact.ToString();
                                contactId= contactLookup.Value.ToString(); //get the guid of the lookup   
                            }                   
                           
                            
                        }
                        else if (context.MessageName == MessageName.Update)
                        {
                            //log1.WriteLine("update message :-");
//Since this is an update, only the values updated on the form will be available in the context. Hence I am getting the value from an Image that I have registered.
                            DynamicEntity postTestEntity = (DynamicEntity)context.PostEntityImages["contactId"];//The name or 'entityalias' of the image
                            //log1.WriteLine(postCCProvider);
                            Lookup contactLookup = (Lookup)postTestEntity.Properties["new_contactid"];
                            //log1.WriteLine(patientLookup);
                            contactId = contactLookup.Value.ToString();
                            //log1.WriteLine(patientId);

                            //log1.Close();
                        }
                        
                        DynamicEntity contact = new DynamicEntity();
                        contact.Name = EntityName.contact.ToString();

                        Key contactKey = new Key();
                        contactKey.Value = new Guid(contactId);

                        KeyProperty contactGuid = new KeyProperty();
                        contactGuid.Name = "contactid";                 
                        contactGuid.Value = contactKey;
                        contact.Properties.Add(contactGuid);

                        //set the field that needs to be updated in the contact entity.
                        StringProperty ccProviderNameProperty = new StringProperty("new_communitycareprovider", clinicName);
                        contact.Properties.Add(ccProviderNameProperty);

                        ICrmService service = context.CreateCrmService(true);
                        service.Update(contact);


                        
                    }
                    catch (SoapException soapEx)
                    {
                        //log1.Close();
                        throw new InvalidPluginExecutionException("Soap error occured in the plugin", soapEx);                        
                        
                    }
                    catch (Exception ex)
                    {
                        //log1.WriteLine(ex.Message);
                        //log1.Close();
                        throw new InvalidPluginExecutionException("An error occured in the plugin", ex);
                    }
                }
            }
            else
            {
                return; 
            }

           
        }

Points about Pre-update plugin


1. A pre-update plugin can be used to update the field values of the same record/entity.
2. Only the values that we are changing in the entity form will be available in the entity context.
3. If we want to access the values of fields that we are not going to update, then we need to register the image for that entity and include the attributes whose value we need.
4. Finally, we need to directly update the entity context for the changes to get reflected.
Note: We cannot do a service.update(entity) as this will cause the code to go in a circular loop and finally the plugin will throw an error and the changes will not get saved!
Code Sample:
//Updating a boolean value to true or false using Pre-update
// Obtain the target business entity from the input parameters.
entity = (DynamicEntity)context.InputParameters.Properties["Target"];
//Instantiating the boolean property and passing the boolen field name and value to the property
CrmBooleanProperty boolProp = new CrmBooleanProperty("new_verified", new CrmBoolean(false));
//Adding the Property to the entity properties
entity.Properties.Add(boolProp);
-Srihari.