Monday 25 January 2016

Plugin Triggered When Share/Unshare CRM C#

Introduction

Hi everyone, I just want to share how Share/Unshare in CRM will trigger a plugin or just in case you need to do some logic after users has done the Share/Unshare records in CRM.

Especially if you want to Share the child records once you share the parent record, yes you can do the Cascading behaviour, but you might be aware that this parental and configurable cascading behaviour can only applied to 1 relationship, so imagine you have many entities, which this problem is the one i am facing right now.

The Problem

In the Plugin Registration Tool, there is no Share or Un-Share Messages and you see MSDN also does not have!



So meaning CRM does not allow the injection of the Share or Un-Share logic?

No..CRM does allow, just the Message Name is..

GrantAccess and RevokeAccess

Meanwhile you also can get the Parameters from that Request

The Code

This is the Code!

        //pass the context here from your common Execute function
        void Post_Message_GrantOrRevokeAccess(IServiceProvider serviceProvider, IPluginExecutionContext context)
        {
            if (context.MessageName == "GrantAccess")
            {
                //this GrantAccess is for sharing
                //for Unshare, messagename = "RevokeAccess" and will do the same passing parameters

                // Obtain the target entity from the input parameter.
                EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];
                //after get this EntityRef then will be easy to continue the logic

                //Obtain the principal access object from the input parameter
                Microsoft.Crm.Sdk.Messages.PrincipalAccess PrincipalAccess = (Microsoft.Crm.Sdk.Messages.PrincipalAccess)context.InputParameters["PrincipalAccess"];

                //Then got the User or Team and also Access Control that being Granted

                //***to Get User/Team that being Shared With
                var userOrTeam = PrincipalAccess.Principal;
                var userOrTeamId = userOrTeam.Id;
                var userOrTeamName = userOrTeam.Name;
//this userOrTeam.Name will be blank since entityReference only will give you ID
                var userOrTeamLogicalName = userOrTeam.LogicalName;

                //use the logical Name to know whether this is User or Team!
                if (userOrTeamLogicalName == "team")
                {
                    //what you are going to do if shared to Team?
                }
                if (userOrTeamLogicalName == "systemuser")
                {
                    //what you are going to do if shared to Team?
                }

                Trace(userOrTeamId.ToString());
                Trace(userOrTeamLogicalName);

                //***to Get the Principal Access
                var AccessMask = PrincipalAccess.AccessMask;
                Trace(AccessMask.ToString());

                throw new InvalidPluginExecutionException("to trigger the trace only"); //please remove later

                //your logic continue here after already have all of them

            }
            else if (context.MessageName == "RevokeAccess")
            {
                 // Obtain the target entity from the input parameter.
                EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];
                //after get this EntityRef then will be easy to continue the logic

                //Unshare does not have PrincipalAccess because it removes all, only can get the revokee
                //Obtain the principal access object from the input parameter
                var Revokee = (EntityReference)context.InputParameters["Revokee"];
                var RevokeeId = Revokee.Id;
                var RevokeeLogicalName = Revokee.LogicalName; //this one Team or User

                Trace(RevokeeId.ToString());
                Trace(RevokeeLogicalName);

                throw new InvalidPluginExecutionException("Unshared"); //please remove later
            }
        }

I just give you the concept that you can just continue from it..

Register the Plugin

Yup this is the last step, just using your favorite Plugin Registration Tool will do.



Result from the Trace

And here is the result that you can see, you can get the user or Team you have shared With and also What is the Access Mask

I try to share to a CRM User: Read and Write




So you can get the full Parameter in your plugin

Here is the Trace result



*As you can see you can get the System User ID as well Team ID if you share to Team and also the privilege, ReadAccess and also WriteAccess

How about UnShare?

Same as well!

I completely remove the CRMUser from all his previous shared access (Read & Write)



And this is the Trace result




*But, you need to Remember that Share here meaning performing Share to the user or Team that previously did not have any Access, it will trigger the GRANTACCESS event.
And also Unshare means Completely remove the Access, then it will trigger the REVOKEACCESS event.

If you want to only modify the Access (ex: from Read and Write to Read only), then you need to register your plugin to another message, so call MODIFYACCESS which i will tell you about this in the my next post..(I hope soon).

*Remember to remove the Trace, I use my own function to do trace in order let you know the Result and also you might not need it

And I hope this is helpful for you guys!
Thank you.

4 comments:

  1. I was trying to find info about how to run a plugin on sharing Account, found your article. Very helpful!

    ReplyDelete
  2. hi my self Sohail
    your artical help me more and more but one problem is that when I'm trying to RewokeAccess by mean i want Rewoke and also that Account related Contacts according account user/team then how can I update Contact entity Team/User???

    ReplyDelete
  3. private void Post_Message_GrantOrRevokeAccess(IServiceProvider serviceProvider, IOrganizationService service, IPluginExecutionContext context, ITracingService tracingService)
    {

    if (context.MessageName == "GrantAccess")
    {
    //this GrantAccess is for sharing
    //for Unshare, messagename = "RevokeAccess" and will do the same passing parameters

    EntityReference EntityRef = (EntityReference)context.InputParameters["Target"];

    EntityCollection contactEntityCollection = GetQuery(EntityRef.Id, service);

    //Obtain the principal access object from the input parameter
    PrincipalAccess PrincipalAccess = (PrincipalAccess)context.InputParameters["PrincipalAccess"];

    var userOrTeam = PrincipalAccess.Principal;
    var userOrTeamId = userOrTeam.Id;
    var userOrTeamName = userOrTeam.Name;
    //this userOrTeam.Name will be blank since entityReference only will give you ID

    var userOrTeamLogicalName = userOrTeam.LogicalName;


    foreach (var contcts in contactEntityCollection.Entities)
    {
    Guid contactEntityId = (Guid)contcts["contactid"];

    //use the logical Name to know whether this is User or Team!
    if (userOrTeamLogicalName == "team")
    {

    var CreatedReference = new EntityReference("team", userOrTeam.Id);
    var grantAccessRequest = new GrantAccessRequest
    {
    PrincipalAccess = new PrincipalAccess
    {
    AccessMask = AccessRights.ReadAccess | AccessRights.WriteAccess,
    Principal = CreatedReference
    },
    Target = new EntityReference("contact", contactEntityId)
    };
    service.Execute(grantAccessRequest);
    tracingService.Trace("get Team from account contex and share with contact");
    }

    if (userOrTeamLogicalName == "systemuser")
    {
    //what you are going to do if shared to User?

    var CreatedReference = new EntityReference("systemuser", userOrTeam.Id);

    var grantAccessRequest = new GrantAccessRequest
    {
    PrincipalAccess = new PrincipalAccess
    {
    AccessMask = AccessRights.ReadAccess | AccessRights.WriteAccess,
    Principal = CreatedReference
    },
    Target = new EntityReference("contact", contactEntityId)
    };
    service.Execute(grantAccessRequest);
    tracingService.Trace("get user from account contex and share with contact");

    }
    }
    }

    }


    private EntityCollection GetQuery(Guid entityId, IOrganizationService service)
    {
    QueryExpression query = new QueryExpression("contact");
    query.ColumnSet = new ColumnSet("contactid");
    query.Criteria.AddCondition("parentcustomerid", ConditionOperator.Equal, entityId);
    EntityCollection contactCollection = service.RetrieveMultiple(query);
    return contactCollection;
    }

    ReplyDelete

My Name is..