Send SMS from Chatter Feed with Action Link Template

In this blog, I am going to explain how to send SMS  from chatter feed by using action link templates. I am going to use Twilio rest API to send the SMS. Please refer this link for more information on action link templates Inside Action Link Templates

Step 1: –   Defining Action Link Group Templates

go to Setup, enter Action Link Templates in the Quick Find box, then select Action Link Templates create a new one with the below values

Field Value
Name Send Text Messages
Developer Name Send_Text_Messages
Category Primary action
Executions Allowed Once
Hours until Expiration  

10.PNG

 

Step 2: – Creating an  Action Link Temple 

Create a new Action link template under the above-created action link group template with the following details.

Field Value
Action Link Group Template Send Text Messages
Action Type Api API Async
Action URL https://api.twilio.com/2010-04-01/Accounts/{!Bindings.accountSID}/SMS/Messages.json
User Visibility Every one can see
HTTP Request Body Body={!Bindings.body}&To={!Bindings.toNumber}&From={!Bindings.fromNumber}
HTTP Headers Authorization: Basic {!Bindings.authToken}
Host: {!Bindings.host}
Content-Length: {!Bindings.lenght}
X-Target-URI: {!Bindings.uri}
Content-Type: {!Bindings.conenttype}
Position 0
Label Key None
Label Text Message
HTTP Method POST

after saving the Action link template its looks as shown below

11.PNG

Go back to the action link group template and publish it

Step 3: – Posting it to chatter feed 

Now I am going to use apex to instantiated action link template and post it to chatter.

go to developer console and paste the below code.

 

  // Query the Action link Groupd Id 
        ActionLinkGroupTemplate template = [SELECT Id FROM ActionLinkGroupTemplate WHERE
                                            DeveloperName='Send_Text_Messages'];
        Map<String, String> bindingMap = new Map<String, String>();
        // Map of Binding variables 
        bindingMap.put('accountSID', '<Your Account SID>');
        bindingMap.put('body', 'testing');
        bindingMap.put('toNumber','2159155090');
        bindingMap.put('fromNumber','2674600419');
        bindingMap.put('authToken', 'Auth token ==');
        bindingMap.put('host', 'api.twilio.com');
        bindingMap.put('lenght','113');
        bindingMap.put('uri','https://api.twilio.com');
        bindingMap.put('conenttype','application/x-www-form-urlencoded');

        List&lt;ConnectApi.ActionLinkTemplateBindingInput&gt; bindingInputs = new  List&lt;ConnectApi.ActionLinkTemplateBindingInput&gt;();
        for (String key : bindingMap.keySet()) {
            ConnectApi.ActionLinkTemplateBindingInput bindingInput = new
                ConnectApi.ActionLinkTemplateBindingInput();
            bindingInput.key = key;
            bindingInput.value = bindingMap.get(key);
            bindingInputs.add(bindingInput);
        }

        ConnectApi.ActionLinkGroupDefinitionInput actionLinkGroupDefinitionInput = new
            ConnectApi.ActionLinkGroupDefinitionInput();
        actionLinkGroupDefinitionInput.templateId = template.id;
        actionLinkGroupDefinitionInput.templateBindings = bindingInputs;
        // Action link Group Definition .
        ConnectApi.ActionLinkGroupDefinition actionLinkGroupDefinition = ConnectApi.ActionLinks.createActionLinkGroupDefinition(Network.getNetworkId(),
                                                                                                                                actionLinkGroupDefinitionInput);

        ConnectApi.FeedItemInput feedItemInput = new ConnectApi.FeedItemInput();
        ConnectApi.FeedElementCapabilitiesInput feedElementCapabilitiesInput = new
            ConnectApi.FeedElementCapabilitiesInput();
        ConnectApi.AssociatedActionsCapabilityInput associatedActionsCapabilityInput = new
            ConnectApi.AssociatedActionsCapabilityInput();
        ConnectApi.MessageBodyInput messageBodyInput = new ConnectApi.MessageBodyInput();
        ConnectApi.TextSegmentInput textSegmentInput = new ConnectApi.TextSegmentInput();
        feedItemInput.body = messageBodyInput;
        feedItemInput.capabilities = feedElementCapabilitiesInput;
        feedItemInput.subjectId = 'me';
        messageBodyInput.messageSegments = new List&lt;ConnectApi.MessageSegmentInput&gt;();
        textSegmentInput.text = 'Click to post a feed item.';
        messageBodyInput.messageSegments.add(textSegmentInput);

        feedElementCapabilitiesInput.associatedActions = associatedActionsCapabilityInput;
        associatedActionsCapabilityInput.actionLinkGroupIds = new List&lt;String&gt;();
        associatedActionsCapabilityInput.actionLinkGroupIds.add(actionLinkGroupDefinition.id);
         ConnectApi.FeedElement feedElement =
            ConnectApi.ChatterFeeds.postFeedElement(Network.getNetworkId(), feedItemInput);

 

Now you can see new action link on the chatter feed as shown below.

12.PNG Once you click on the Text message it will send an SMS and also update the Statis to chatter feed.

 

Inside Action Link Templates

In this blog post,  I am going to give an overview of the Action Link Templates and its conceptual knowledge. Action link is a button with some action on a feed element which is having a capability to a redirect Web page, initiate a file download, or invoke an API call to Salesforce or to an external server  like  when you want to post a survey link to salesforce chatter feed or you wanted to approve or reject travel or expense from the chatter feed are few use cases of action link templates .

Terminology :-  

Action Links: – An action link is a button on a feed element. Clicking an action link can take a user to a  Web page, initiate a file download, or invoke an API call to Salesforce or to an external server. Action links on chatter post can allow you to integrate your chatter feed with Salesforce and third-party services into the feed.

Action Link Templates: –  Action like the template is the place when you can define your server-side execute logic decoratively by using context variables and binding variables. You can use template binding keys in the Action URL, HTTP Request Body, and HTTP headers fields to make an APIcall. Suppose on click on button user need to send an SMS from the feed, in this case, you can defined the URL to send SMS and body and Header and other authentication parameters in Action link template by using binding variables and context variables.you can specify  binding variables in the template at the time of  instantiate the action link group. You can also specify context variables in the templates like who is executes the action link, Salesforce provides values for these variables, such as who executed the link and in which organization

Action Link Group Template: – To associate an action link with a feed element, you need to create an action link group template which is a collection of Action link templates. With the action link group template, you can able to control the Action links location on chatter feed like primary or overflow.

Types of Action Link: –With Action link template you can choose the different type of action as describes below .you can specify the action link type in the actionType property when you define an action link.

• Api—The action link calls a synchronous API at the action URL.

For example Approve Expenses, Travel request.

• ApiAsync—The action link calls an asynchronous API at the action URL.

For example  Upload Files, send SMS etc

• Download—The action link downloads a file from the action URL.

For example Download file from Dropbox, Box, etc.

• Ui—The action link takes the user to a Web page to the action URL.

For example View Map, open Visual Force Page, Canvas Page, etc.

Action link appearance: – While designing you action link templates you can specify the chatter feed item location by using Category property . it can be one of  Primary or Overflow.

If you specify the Category as Primary then action link will appear on Feed-item Body

If you specify the Category as  Overflow then Feed-item Detail Menu.

dtxY01494460173

Declarative Binding: –Not always you no need to pass the static data for the action link template while designing .you can identify the key areas where you need to make changes dynamically replace those by using Binding variables or context variables.

Use context variables to pass information about the user who executed the action link and the context in which it was invoked into the HTTP request made by invoking an action link. You can use context variables in the actionUrl, headers, and requestBody properties of the Action Link Definition Input request body or ConnectApi.ActionLinkDefinitionInput object.

Use binding variables in templates and set their values when you instantiate an action link group. Use binding variables to add sensitive information at run time.  You can define binding variables in the Action URL, HTTP Request Body, and HTTP Headers fields of an action link template. After a template is published, you can edit these fields, you can move binding variables between these fields, and you can delete binding variables. However, you can’t add new binding variables. Define a binding variable’s key in the template. When you instantiate the action link group, specify the key and its value.

Action link URL  with Bindings

https://www.example.com/!Bindings.ApiVersion/items/!Bindings.ItemId

Action Link Header with Bindings

Authorization: OAuth {!Bindings.OAuthToken}
Content-Type: {!Bindings.ContentType}

The lifetime of Action links: –  Setting up expiration data on action link template is easy to maintain suppose let’s consider you are posting a survey link to feed that should be available only specific time. these type of requirements you can set with hours until expiration value. The expiration date can be calculated based on a period provided in the template, or the action link group can be set not to expire at all. To set the hours until expiration in a template, enter a value in the Hours until Expiration field of the action link group template. This value is the number of hours from when the action link group is instantiated until it’s removed from associated feed elements and can no longer be executed. The maximum value is 8760, which is 365 days.

Who can Execute and how many times?

Choose a value from the User Visibility drop-down list to determine who can see the action link after it’s associated with a feed element. Among the available options are Only Custom User Can See and Everyone Except Custom User Can See. Choose one of these values to allow only a specific user to see the action link or to prevent a specific user from seeing it. Then enter a value in the Custom User Alias field. This value is a binding variable key. In the code that instantiates the action link group, use the key and specify the value as you would for any binding variable. You can decide how many time each user wanted to execute action link by setting execution allowed with any one of values as unlimited, Once Per User, Once.

 

 

 

Posting Survey Link to Feed with Action Link Template

In this blog, I am going to explain how to set up survey link to the chatter feed by using action link template.Here we are going to use Action Link Template Action type “UI”  .UI Action will take the user to a Web page to the action URL. Here I am using SurveyMonkey survey link URL.

Please refer this link for more information on Action link templates Inside Action Link Templates

Step 1: Create the Action Link Templates

Action link templates is a group of Actions that is going to be associated with the chatter feed. To create an Action link template From Setup, enter Action Link Templates in the Quick Find box, then select Action Link Templates. Click on New button then add the values in a new Action Link Group Template as shown below then save it.

Name Complete Survey
Developer Name Complete_Survey
Category Primary action
Executions Allowed Once per User

2

Now you create an Action Link Group Template . after that you can associate one or more action link templates.The action link template is automatically associated with an action link group template in a master-detail relationship. Add new action link template under the Action Link group template as shown below.

3.PNG

Action Type: UI
Action URL  https://www.surveymonkey.com/r/{!Bindings.surveylinkId}
User Visibility Everyone can see
Position 0
Label Key – none
HTTP Method GET
Label: Complete Survey

Now go to Action Link Group Template Detail Click on Edit then Check “Published” checkbox checked and save it. So that you can instantiate action line template group

Step 2: Get the Action Link Group Template ID

You need to get the previously defined action link template group Id. You can get in different ways like SOQL, Rest API or you can take from the URL. Action Link Template Group Object is Queriable so you can use simple SOQL as shown below

SELECT id FROM ActionLinkGroupTemplate WHERE DeveloperName=’Complete_Survey’

4.PNG

Step 3: Instantiate the Action Link Group

You can instantiate the action link group by Using rest API or Apex Code. But in this case, I am using rest API along with workbench for rest API.

Login into workbench by using https://workbench.developerforce.com/logout.php URL. Go to Utilities -> rest explorer
End Point URL :- /services/data/v39.0/connect/action-link-group-definitions
Select operation as POST and Use the Body as shown below

 {
"templateId":"07g41000000CaV9",
"templateBindings":[
{
"key":"surveylinkId",
"value":"M6DQ2WD"
}
]
}

If you can see above JSON is contacting the Action Linke Group Template Id and the array of TemplateBindings.  template bindings are contained key and values. Key should match with the action link template bindings variable reference and values is the corresponding values.{!Bindings.surveylinkId} === >  {

{!Bindings.surveylinkId} === >  {
“key”:”surveylinkId”,
“value”:”M6DQ2WD”
}

5

Click  on Execute now after that you see the JSON response as shown below which contains Action link id which you need to pass the Feed Element in next step

7.PNG

Step 4: Associate the Action Link Group with a Feed Element and Post It

Now in this step, you are going to create chatter feed and posting to the chatter. You can do it by using apex or rest API. here I am going to use rest API with workbench again.

Go to Utilities -> rest explorer
End Point URL :-/services/data/v39.0/chatter/feed-elements
Select operation as POST and Use the Body as shown below.

 

 {
"body": {
"messageSegments": [
{
"type": "Text",
"text": "Please complete this Survey"
}
]
},
"subjectId": "me",
"feedElementType": "FeedItem",
"capabilities": {
"associatedActions": {
"actionLinkGroupIds": ["0Ag410000004cDwCAI"]
}
}
}

 

Now you can see the above request body is contains action link group id instance which you got in earlier steps.

8.PNG

 

Click on Execute now. It will create a  new chatter feed post with action links as shown below.

9.PNG

On lick on of Complete Survey button, it will take you to the Survey link URL.

 

 

Handling “MIXED_DML_OPERATION” with future calls

 You can run “MIXED_DML_OPERATION” error when you are trying to perform DML on setup and non-setup objects in the same transaction.This restriction exists because some sObjects affect the user’s access to records in the org.Non-Setup objects are standard objects like Account or any custom object.  here is the few Setup Object
  • ObjectPermissions
  • PermissionSet
  • PermissionSetAssignment
  • QueueSObject
  • ObjectTerritory2AssignmentRule
  • ObjectTerritory2AssignmentRuleItem
  • RuleTerritory2Association
  • SetupEntityAccess
  • Territory2
  • Territory2Model
  • UserTerritory2Association
  • User
  • GroupMember
  • FieldPermissions

Use case: – Once “Customer Satisfaction Survey” is completed we need to assign it to the  “Internal_Employee” group. Here is the simple trigger


 trigger ServeyTrigger on Customer_Satisfaction_Survey__c (after insert) {
    Group g=[select Id from Group Where DeveloperName='Internal_Employee'];

    List&amp;amp;amp;amp;lt;GroupMember&amp;amp;amp;amp;gt;listGroupMember =new List&amp;amp;amp;amp;lt;GroupMember&amp;amp;amp;amp;gt;();
    for (Customer_Satisfaction_Survey__c cs  : Trigger.new){
        GroupMember gm= new GroupMember();
        gm.GroupId=g.id;
        gm.UserOrGroupId = UserInfo.getUserId();
        listGroupMember.add(gm);
    }
    insert listGroupMember;

}

 

Once you try to insert the new records you will get the below error message

Review all error messages below to correct your data.
Apex trigger ServeyTrigger caused an unexpected exception, contact your administrator: ServeyTrigger: execution of AfterInsert caused by: System.DmlException: Insert failed. First exception on row 0; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): GroupMember, original object: Customer_Satisfaction_Survey__c: []: Trigger.ServeyTrigger: line 11, column 1

To solve this problem simply you can add the Second DML into future call

  1. Create a method that performs a DML operation on one type of sObject.
  2. Create a second method that uses the future annotation to manipulate a second sObject type.

Now update the trigger as shown below

trigger ServeyTrigger on Customer_Satisfaction_Survey__c (after insert) {
  ServeyAsyncCall.assignToUser(trigger.newMap.keySet());
}

Apex Class:

 

 public class ServeyAsyncCall {
    @future
    public static void assignToUser(Set&amp;lt;Id&amp;gt; setofIds){
        Group g=[select Id from Group Where DeveloperName='Internal_Employee'];
        List&amp;lt;Customer_Satisfaction_Survey__c&amp;gt; records =[Select Id , Name from Customer_Satisfaction_Survey__c where id in:setofIds];
        List&amp;lt;GroupMember&amp;gt;listGroupMember =new List&amp;lt;GroupMember&amp;gt;();
        for (Customer_Satisfaction_Survey__c cs  :records){
            GroupMember gm= new GroupMember();
            gm.GroupId=g.id;
            gm.UserOrGroupId = UserInfo.getUserId();
            listGroupMember.add(gm);
        }
        if (!Test.isRunningTest()) {
            insert listGroupMember;
        }
    }
}

Apex Test Class:- 

@isTest
private class ServeyAsyncCall_test {

    private static testMethod  void bypassMixedDML(){
        // You can insert the user with @future  for this test method . But in this test class simply i queried
        // InsertUser.callInsertfuture()
        User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()];
        System.runAs (thisUser) {

            Customer_Satisfaction_Survey__c cs = new Customer_Satisfaction_Survey__c();
            cs.Comments__c='Hello' ;
            cs.Name='Test Class';
            insert cs ; 

        }
    }
}

Asynchronous calls with @Future

In this blog, I am going to how to make the asynchronous calls with @Future annotations .with the Future annotation you can make asynchronous Web service callout to an external service.A future method runs in the background, asynchronously. You can call a future method for executing long-running operations, such as callouts to external Web services or any operation you’d like to run in its own thread, on its own time.

What is Asynchronous Process in Salesforce 

An asynchronous process is a process or function which does not require interaction with a user. It can be used to execute a task “in the background” without the user having to wait for the task to finish. Force.com features such as Asynchronous Apex (@future), Batch Apex, Bulk API, Reports and other features use asynchronous processing to efficiently process requests.Each future method is queued and executes when system resources become available. That way, the execution of your code doesn’t have to wait for the completion of a long-running operation. A benefit of using future methods is that some governor limits are higher, such as SOQL query limits and heap size limits.

Use Case: – Upon Creation of Account Record, you need to validate the billing address of account and update latitude and longitude values.

Solution: – Create a trigger of account on after insert and after update.  Invoke  an asynchronous Webservice call with @future annotation to validate the address against google API and return latitude and longitude

Trigger :- 

trigger AccountTriger on Account (after insert) {

    AccountUpdateAsync.updateAccountAddress(Trigger.new[0].Id);

}

Apex Class:-

 public class AccountUpdateAsync {
    @future(callout=true)
    public static void updateAccountAddress(String accId){
        Account acc = [Select Id , Name ,Location__Latitude__s  , Location__Longitude__s, 
                       BillingStreet ,BillingCity ,BillingState ,BillingPostalCode,
                       BillingCountry from Account where id =:accId Limit 1];

        String httpReqURI = 'https://maps.googleapis.com/maps/api/geocode/json?address='+
            acc.BillingStreet+','+acc.BillingCity+','
            +acc.BillingState+','+acc.BillingPostalCode+','+
            acc.BillingCountry
            +'&amp;key=&lt;API_KEY&gt;';
        HttpResponse response = sendHttpReq(httpReqURI);

        if (response.getStatusCode() == 200) {
            JSONParser parser = JSON.createParser(response.getBody());
            Map &lt;String, Object&gt; root = (Map &lt;String, Object&gt;) JSON.deserializeUntyped(response.getBody());
            List &lt;Object&gt; childLevel = (List&lt;Object&gt;) root.get('results');
            for( Object o : childLevel){
                Map &lt;String, Object&gt; childLevel2 = (Map &lt;String, Object&gt;) o;
                Map &lt;String, Object&gt; grandChaildLevel= (Map &lt;String, Object&gt;) childLevel2.get('geometry');
                Object objFinal = grandChaildLevel.get('location');
                Map&lt;String, Object&gt; locs = (Map&lt;String, Object&gt;)objFinal;
                acc.Location__Latitude__s = (Decimal)locs.get('lat') ; 
                acc.Location__Longitude__s = (Decimal)locs.get('lng');
                update acc;
            }

        }

    }

    public static HttpResponse sendHttpReq(String url){
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(url);
        request.setMethod('GET');
        HttpResponse response = http.send(request);
        return response;

    }

    public static  void parseJson(String json){

    }

}

Future method Considerations:- 

Below are the future method considerations

  1. Methods with the future annotation must be static methods and can only return a void type.
  2. The specified parameters must be primitive data types, arrays of primitive data types, or collections of primitive data types. No sObjects or objects as arguments
  3. The future method does not necessarily execute in the same order it is called.
  4. Methods with the future annotation cannot be used in Visualforce controllers in either getMethodName or setMethodName methods, nor in the constructor.
  5. You cannot call a method annotated with future from a method that also has the future annotation
  6. future calls cannot be made from a batch job
  7. future calls can execute concurrently

 

Best Practices:-

future methods are having its own advantages.  Consider this below best practices while implementing the future calls

  1. Make @future  code is efficient as possible. Long execution times increase the chance of delays and extended delays.
  2.  Minimize the number of the asynchronous requests created to minimize the chance of delay and extended delays.
  3. Make sure you are not invoking too many future calls when you are invoking from the trigger . I would advice one future call for one trigger transaction.
  4. Avoid recursive behavior in future calls.Use a static variable to store the state of the trigger processing
  5. Consider using Batch Apex instead @future to process a large number of records asynchronously.
  6. asynchronous processing framework is queue-based, lower priority than real-time interaction Best practice – ensure @future requests execute as fast as possible and remember limits: 10 @futures each making at most 10 callouts.

 

Limits :-

Future calls have below limits

  1. No more than 10 @future method calls per Apex invocation.
  2. You’re limited to 50 future calls per Apex invocation, and there’s an additional limit on the number of calls in a 24-hour period.  The maximum number of future method invocations per a 24-hour period is 250,000 or the number of user licenses in your organization multiplied by 200, whichever is greater. Still, all salesforce licenses are not applicable fo this limits . for examples chatter licenses are not part of this.  More limits you can refer in salesforce limits guide.

 

 

Salesforce Apex Managed Sharing

In this blog post, I am going to explain how to use apex managed sharing. Apex managed sharing allows developers to programmatically share custom objects. When you use Apex managed sharing to share the custom object, only users with the “Modify All Data” permission can add or change the sharing on the custom object’s record, and the sharing access is maintained across record owner changes.

Apex Manager sharing can be enabled on the object that is having private and public read-only access.

1.Apex Managed Sharing can not be enabled on public read-write objects
2.Each custom object is having its own sharing table with __share name if the object sharing is private or public read-only
3.Apex sharing reasons and Apex managed sharing recalculation are only available for custom objects.
4.Objects on the detail side of a master-detail relationship do not have an associated sharing object. The detail record’s access is determined by the master’s sharing object and the relationship’s sharing setting

Understanding Sharing Reason:-
the Reason field on a custom object specifies the type of sharing used for a record. This field is called rowCause in Apex or the Force.com API. you can use the Sharing reason in Force.com Managed Sharing, User Managed Sharing, Apex Managed Sharing

RowCause values depend on the type of sharing .its can be Owner, team, share, manual etc depends on the type of sharing the reason.

Access Levels:-
Access level is determining a user’s access to records. Most share objects support the following access levels: Supported Access levels are Private, Read Only, ReadWrite, Full Access
Every share object has the following properties:-
AccessLevel – Can be any of the Edit, Read, All
ParentID – The ID of the object
RowCause – The reason why the user or group is being granted access
UserOrGroupId – The user or group IDs to which you are granting access

User Managed Sharing Using Apex:-
Now we are going to create an apex class that is used to share the data based on sharing.It is possible to manually share a record to a user or a group using Apex or the SOAP API. If the owner of the record changes, the sharing is automatically deleted


public with sharing class DeliverySharingCls {
    public static void shareDelivery(list&lt;Delivery__c&gt; delivery){
        List&lt;Delivery__Share&gt; totalShares = new List&lt;Delivery__Share&gt;() ; 
        for( Delivery__c d : delivery){
            // Create new sharing object for the custom object Delivery__c.           
            Delivery__Share share = new Delivery__Share() ; 
            // Set the ID of record being shared.
            share.ParentId = d.Id ;
            // Set the ID of user or group being granted access. Insted of the heard coded user id , you need to create fetch the 
            // id dynaically 
            share.UserOrGroupId = '00541000000RlIa';
            // Set the access level.
            share.AccessLevel = 'Edit';
            // Set rowCause to 'manual' for manual sharing.
            share.RowCause = Schema.Delivery__Share.RowCause.Manual;
            totalShares.add(share); 
        }

        if(totalShares.size()&gt;0){
            list&lt;Database.SaveResult&gt; saveres = Database.insert(totalShares) ; 

        }
    }

}

Invoke the above code from the trigger to share the data as shown below.


 trigger DeliveryShare on Delivery__c (after insert) {
    DeliverySharingCls.shareDelivery(trigger.new) ;

}

Creating Apex Managed Sharing:-

Apex managed sharing enables developers to programmatically manipulate sharing to support their application’s behavior through Apex or the SOAP API

Apex managed sharing must use an Apex sharing reason. Apex sharing reasons are a way for developers to track why they shared a record with a user or group of users.
Apex sharing reasons are defined on an object’s detail page.
To Create a Apex Managed Sharing reason on the custom Object
go to –> setup -> create ->object ->Delivery__c -> under Apex Managed Sharing Reason ->Click New and create a sharing reason as shown below .

Apex sharing reason name: Custom Sharing Model
In order to use it in the code, you have to use it as shown below
Schema.Delivery__share.rowCause.Custom_Sharing_Model__c
Now Update the above code with the rowCause as shown below.


public with sharing class DeliverySharingCls {
    public static void shareDelivery(list&lt;Delivery__c&gt; delivery){
        List&lt;Delivery__Share&gt; totalShares = new List&lt;Delivery__Share&gt;() ; 
        for( Delivery__c d : delivery){
            // Create new sharing object for the custom object Delivery__c.    
            Delivery__Share share = new Delivery__Share() ; 
            // Set the ID of record being shared.
            share.ParentId = d.Id ;
            // Set the ID of user or group being granted access. Insted of the heard coded user id , you need to create fetch the 
            // id dynaically 
            share.UserOrGroupId = '00541000000RlIa';
            // Set the access level.
            share.AccessLevel = 'Edit';
            // Set rowCause to 'Custom Sharing model' for manual sharing.
            share.RowCause = Schema.Delivery__Share.RowCause.Custom_Sharing_Model__c;
            totalShares.add(share); 
        }

        if(totalShares.size()&gt;0){
            list&lt;Database.SaveResult&gt; saveres = Database.insert(totalShares) ; 

        }
    }

}

 

Apex Managed Sharing Recalculating

In This blog, I am going to explain how to use apex managed sharing recalculation. You can use apex managed sharing recalculation to update the custom object sharing based on your own business logic whenever custom object OWD is changed. You can associate these classes with a custom object on its detail page, and execute them if a locking issue prevents Apex from granting access to a user as defined by the application’s logic

Step 1: Create Batch Class to update sharing recalculation
You can also run sharing update programmatically using the Database.executeBatch method.
In addition, Salesforce automatically runs Apex recalculation classes defined for a custom object every time a custom object’s organization-wide sharing default access level is updated.
Now let’s see the batch class code to update the Sharing recalculation. the final code looks as shown below.

global class DeliverySharingRecalculation implements Database.Batchable<sObject> {

global Database.QueryLocator start(Database.BatchableContext BC){
return Database.getQueryLocator([SELECT Id, Name FROM Delivery__c]);
}

global void execute(Database.BatchableContext BC, List<sObject> scope){
// In this case I am just simply sharing the data with one user. But you
//can adjust the data based on different conditions. Examples If Delivery mode is express you can assign to specific group id Delivery
// is normal you can assign to some other group
List<Delivery__Share> newDelivary = new List<Delivery__Share>();
List<Delivery__c> deli =(List<Delivery__c>)scope ;

for(Delivery__c de : deli){
Delivery__Share delSH = new Delivery__Share();
// Assign it to user or group
delSH.UserOrGroupId = '00541000000RlIa';
//Access level to read
delSH.AccessLevel = 'Read';
// Record Id
delSH.ParentId = de.Id;
// Row Cause
delSH.RowCause = Schema.Delivery__Share.RowCause.Custom_Sharing_Model__c;
newDelivary.add(delSH);
// You can add new sharing and delete the existing sharing based on requirementts
}
Database.SaveResult[] lsr = Database.insert(newDelivary,false);

}

global void finish(Database.BatchableContext BC){
}

}

Step 2: Invoking the batch

You can run the above batch class in different ways like you can schedule the class or run it as the one-time batch.

Another way is to go to Custom object –> Apex Sharing Recalculation –> Click New -> Update the above batch class as shown below.


Limitations :-

When working with Apex sharing recalculations, note the following.The apex code that extends the sharing recalculation can process a maximum of five million records. If this apex code affects more than five million records, the job fails immediately.
You can monitor the status of apex sharing recalculations in the apex job queue.
You can associate a maximum of five Apex sharing recalculations per custom object.
You cannot associate Apex sharing recalculations with standard objects
The class you choose must implement the Database.Batchable interface. You cannot associate the same Apex class multiple times with the same custom object.