Monday, 15 February 2016

Manual Sharing for Partner community using Apex



Salesforce

This topic is related to the sharing in communities. I need to setup the sharing on partner communities and there is no point and click option for sharing any object for partner communities object. First you should setup the communities in the ORG than go to the setupàcommunity settings and add a new sharing sets, see the below image 1


In above red highlighted box, you would not able to see the partner community profiles, so this is really a main point for which I used manual sharing by apex code. We can add a sharing rule for customer community user license. So if we are making customer community than this would be pretty mush simple to configure. Now moving forward, my task was to share a newly created case in the community only for same account, Means let’s say if I have created a Case1 and adding this case against any contact, obviously Account is there for that contact, so this Case1 should be share among all those community user which have that same Account Id and if another account user login to the community than that Case1 should not be seen for that User. So this is the user case which I need to follow here. So I have made a trigger on case and on that trigger, newly created case should be share with all those user which are of same account. Please keep this in mind if let’s say we update that case and link that case with another Account than we must un-share the case with previous users and that case would be share with new community user. So, I have just written a sample here which is perfect for the created case if we change the Account on updating than few more lines needs to be added here, please see the below trigger code here.

trigger trShareCase on Case (after insert, after update) 
{
    map<Id,Id> mCase_Account = new map<Id,Id>();
    map<Id,Id> mUser_Account = new map<Id,Id>();
    map<Id,List<Id>> mCase1 = new map<Id,List<Id>>();
    Map<Id,List<User>> mUser = new Map<Id,List<User>>();
            for(Case cs:trigger.new)
            {
                        if (cs.accountid != null)
                        {
                                    mCase_Account.put(cs.Id,cs.accountid);
                        }
            }

            for (user u : [select Id,AccountId from user where IsActive= true And AccountId in :mCase_Account.values()])
            {
              mUser_Account.put(u.Id,u.AccountId);
            }
           
            List<Id> liUser ;
            for(Id caseId: mCase_Account.keyset())
            {
                         for(Id userId: mUser_Account.keyset())
                         {
                                    if(mCase_Account.get(caseId) == mUser_Account.get(userId))
                                    {
                                        if (!mCase1.containsKey(caseId))
                                                {
                                                    liUser = new List<Id>();
                                                    liUser.add(userId);
                                                            mCase1.put(caseId,liUser);
                                                }
                                                else
                                                {
                                                    liUser = new List<Id>();
                                                            liUser = mCase1.get(caseId);
                                                            liUser.add(userId);
                                                            mCase1.put(caseId,liUser);
                                                }
                                    }
                         }

            }
            List<CaseShare> csShareList = new List<CaseShare>();
            Id TestId ;
            for(Case cs:trigger.new)
            {
              
               for (Id caseId: mCase1.keyset())
               {
                 if (cs.Id == caseId)
                         {
                            liUser = mCase1.get(caseId);
                                    for (Id UserId: liUser)
                                    {
                                                CaseShare csShare = new CaseShare();
                                                // Give Read write access to that user for this particular case record.
                                                csShare.CaseAccessLevel = 'Read';
                                                // Assign case Id of case record.
                                                csShare.CaseId = cs.id;
                                                // Assign user id to grant read write access to this particular case record.
                                                TestId = UserId;
                                                csShare.UserOrGroupId = TestId;
                                                //csShare.RowCause = Schema.Project__Share.RowCause.Manual;
                                                csShareList.add( csShare );
                                    }
                         
                         }

               }

              
            }
           
            insert csShareList;
}
In above code, first yellow highlighted code is showing a map which consist Case Id and Account Id, second green highlighted code showing map in which it is saving User Id and Account Id, and I used SOQL to fetched all those user which have Account Ids from first map value. In third gray highlighted code, it is than maintaining very important map. This map contains a signature of Case Id and List of user Ids. So I am using 2 for loops which actually maintain User list with single case, if the map do not contain any key than it would first add the value in the map with Case Id as key and List of User Id and in second if condition, if the same key exists than it is altering the map value against same key that is case id. Last but not least in the later part the case sharing object comes into play and adding the entries into the list having 1 to many relation means 1 case might contains one or multiple users.
The Community plus license also needs the same way of manual sharing but if you want to play the same game against customer community user license than go to setpàcommunity settings and define the sharing set and add the same rule means Case1 should be share against all those user which are under the umbrella of same Account, see below image for your help

So this is the case of adding any new case, let’s say if we are making any new partner community user than what should we do? Because we have earlier worked on Case object. So, for this we need to add a trigger on User as well but the important punch over here is that if we add a trigger on user and on that trigger if we are manually share the object like Case, so if User is added and then hit that trigger, it would give an exception of MIXED_DML_OPEARATION, reason being we are getting the exception because the same time we are using the System object that is user for DML operation and on the other way we are using non-system object which is CaseSharing object, so it would create an issue. So to handle this we should need some time difference between these 2 operation, so I have made a custom formula date time field in user and set the time 55 minutes before the created date. See below image


Why I added this field because I want to add a time base workflow and in time base workflow, it need minimum an hours to execute. I have also added a custom field in user object, a checkbox “Show in Community” , so if the workflow executes after 5 min than it would update this checkbox field and on update the user record than the trigger would execute on user. So the purpose of this so to give some time elapse between user object creation and case sharing object. See the below time dependent workflow snapshot.



The rule criteria is mentioned in red box which show that if the custom field is not checked than it would enter into the workflow. Secondly, the red box showing 1 hour after the custom date time formula field, we do not have any option to execute the workflow under an hour. Third one the field update, it would mark check on the custom checkbox field. Immediately if it mark the checkbox than it would hit the trigger and trigger will share the object with the newly created user. I am not writing the User trigger here as this trigger would be prettly kuch similar to the above trigger, only the difference would be that in this trigger, we should maintain the final map with User Id and list of case, the opposite logic to the above trigger, so happy coding guys J

Tuesday, 12 January 2016

Removing "unsaved changes" from MS dynamics online forms after saving



MS Dynamics CRM online

This topic is related to the dirty fields in MS dynamics CRM online forms, if user save any changes and after saving he would see the “Unsaved Changes” at the left bottom part of the page even if all the fields are saved, in my case I have used Javascript on both the load and save event also I have workflow activated behind every entity so the option in Administration area for Auto save is set to NO in my configuration. Because if we have workflows and JS behind the entity that is why we don’t allow Auto save functionality.
Now, it was real irritating for me to find the solution for this thing. At first stage, I tried to disable all the Javascript on my entity because I found this solution in most of the blogs and post. So I did this but that was useless for me. So, I found that there are some dirty fields in the entities which are causing the issue and due to these dirty fields, the form could not save and showing “Unsaved Changes” like below image


So, I have set the setSubmitMode to “never” in this case on page load Javascript. But the thing is that, if we set these fields as “never” submit mode than these fields won’t save into the database on saving event. And in my case, there are few fields which needs to be save. So what is observed during the task is that, if we set the submit mode to “never” on page load javascript and again just before saving through javascript if we again set the submit mode to “always” than it solve my issue because on page loading, MS dynamics page identify the dirty fields and if we set these fields to “never” than it won’t  affect the “unsaved changes” issue. I know this is trick but this is the only way I found the solution. So I have made a dynamic javascript which accept the submit mode and on load event, I set it to “never” and on saving it accept the “always” mode. See the below Javascript
function ClearDirtyFields(mode)
{
   var attributes = Xrm.Page.data.entity.attributes.get();
   for (var i=0;i<attributes.length ;i++)
   {
       if (attributes[i].getName() == "new_guid")
       {
          attributes[i].setSubmitMode(mode);
       }
               if (attributes[i].getName() == "custom_Membership")
       {
          attributes[i].setSubmitMode(mode);
       }
    }          
}

In above Javascript,, I explicitly set the field’s submit mode using for loop. Remember, set the “never” submit mode on page load javascript and “always” on save event javascript.