0

I have a requirement where I want to send email notification to User if Opportunity Close Date is last 10 days and send Chatter Feed notification when Opportunity Close Date is Last 3 days.

For this I have written a batch class.Batch class is working fine for Email Notification and display data correctly in table format but when I am trying to send Chatter notification, I chatter notification via email as well as Can see the same on chatter feeds but data is displayed like this:

<html><body>Dear Dhananjay Patil, <br><br>You have Total 1 List of Opportunity which will auto hard close soon: <br><br><table border="1" style="border-collapse: collapse"><tr><th>Opportunity Name</th><th>Stage</th><th>Vertical</th><th>Startegy</th><th>Geography</th><th>Account Name</th><th>Owner</th><th>Amount</th></tr><tr> <td><a href="https://tlogin.salesforce.com/123456789">Test Opportunity</a> <td>Present</td><td>Travel and Hospitality</td><td>Digital Integration</td> <td>North America</td> <td>Test Account</td><td>Dhananjay Patil</td> <td>120010.00</td> </tr></table><br><br>Please DO NOT reply to this email.</body></html>`

My expectation is I want to display the above data in tabular format when Posting it to chatter.

Can someone please tell me is it possible to display data in tabular format on chatter feed?

or any other possible workaround?

Batch Class:

 global class OpportunityCloseStgNotificationBatch implements Database.Batchable<AggregateResult> {
        global Integer recordsProcessed = 0;
        List<Opportunity> optList = new List<Opportunity>();
        global Iterable<AggregateResult> start(Database.BatchableContext BC)
        {
            return new OpportunityCloseStgNotificationIterable(); 
        }
        global void execute(Database.BatchableContext BC, List<sObject> scope){ 
            List<Messaging.SingleEmailMessage> EmailsToSend = new List<Messaging.SingleEmailMessage>();
            Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
            recordsProcessed = Integer.valueOf(scope[0].get('expr0')); 
            Id actorId = (Id)scope[0].get('ownerId');
            string receipentName = (String)scope[0].get('ownerName');
            // Map of Opportunities whose Auto Hard Close Date is Next 10 days.
            Map<Id,Opportunity> mapOfNextTenDaysOpportunity = new Map<Id,Opportunity>(
                [Select Id,Name,StageName,Vertical__c,Services__c,Regions__c,Account.Name,Auto_Hard_Closed_Date__c,Do_not_auto_close__c,Owner.Name,OwnerId,ABC_RuM__c FROM Opportunity 
                 where (Opportunity_RecordType_Dev_Name__c = 'ABC' OR Opportunity_RecordType_Dev_Name__c='ABC_Internal') AND Do_not_auto_close__c=false AND (Auto_Hard_Closed_Date__c<>null AND Auto_Hard_Closed_Date__c =NEXT_N_DAYS:10 AND Auto_Hard_Closed_Date__c<>NEXT_N_DAYS:3) AND IsClosed=false order by Auto_Hard_Closed_Date__c,Name]);
            // Map of Opportunities whose Auto Hard Close Date is Next 3 days
            Map<Id,Opportunity> mapOfNextThreeDaysOpportunity = new Map<Id,Opportunity>(
                [Select Id,Name,StageName,Vertical__c,Services__c,Regions__c,Account.Name,Auto_Hard_Closed_Date__c,Do_not_auto_close__c,Owner.Name,OwnerId,ABC_RuM__c FROM Opportunity 
                 where (Opportunity_RecordType_Dev_Name__c = 'ABC' OR Opportunity_RecordType_Dev_Name__c='ABC_Internal') AND Do_not_auto_close__c=false AND (Auto_Hard_Closed_Date__c<>null AND Auto_Hard_Closed_Date__c =NEXT_N_DAYS:3) AND IsClosed=false order by Auto_Hard_Closed_Date__c,Name]);

            message.setSaveAsActivity(false);
            message.setTargetObjectId(actorId);
            String tableStr = '';
            String bodyStr = '';
            string domain = URL.getSalesforceBaseUrl().toExternalForm();
            //open table
            tableStr = '<table border="1" style="border-collapse: collapse"><tr><th>Opportunity Name</th><th>Stage</th><th>Vertical</th><th>Offerings</th><th>Consulting Geography</th><th>Account Name</th><th>Owner</th><th>Amount</th></tr>';
            //For Opportunities with Auto Hard CLose Date Next 10 Days, Send email notification to user.
            if(!mapOfNextTenDaysOpportunity.isEmpty()){ 
                message.setSubject('Opportunity Closing within Next 10 days');      
                for(Opportunity opt : mapOfNextTenDaysOpportunity.values()){
                    String optName = opt.Name;
                    String stageName = Opt.StageName;
                    String vertical = opt.Vertical__c;
                    String offerings = opt.Services__c;
                    String geography = opt.Regions__c;
                    String accName = opt.Account.Name;
                    String ownerName = opt.Owner.Name;
                    decimal amount = opt.ABC_RuM__c;
                    string TargetLink = domain + '/' + opt.id;
                    if(opt.Auto_Hard_Closed_Date__c==System.Today()){
                        system.debug('Auto Hard Closing an Opportunity');
                        opt.Reason_opportunity_was_closed_Category__c = 'Stale';
                        opt.Opportunity_Close_Reason__c = 'Others';
                        opt.StageName='Closed';
                        optList.add(opt);
                    }
                    else{
                        tableStr += '<tr> <td><a href="'+TargetLink+'">' + optName +'</a> <td>' + stageName + '</td><td>' + vertical + '</td><td>' + offerings + '</td> <td>' + geography + '</td> <td>' + accName + '</td><td>' + ownerName + '</td> <td>' + amount + '</td> </tr>';   
                    }


                }
                tableStr+='</table>';
                if(string.isBlank(bodyStr)){
                    bodyStr += '<html><body>Dear ' + receipentName + ', ' +'<br><br>'+
                        'You have Total ' + recordsProcessed + ' List of Opportunity which will auto hard close soon: ' +'<br><br>'+
                        +tableStr+'<br><br>'+
                        'Please DO NOT reply to this email.</body></html>' ;        
                }
                message.setHTMLBody(bodyStr);
                EmailsToSend.add(message);    
                Messaging.sendEmail(EmailsToSend);
                if(!optList.isEmpty()){
                    update optList;
                }           
            }
         //For Opportunities with Auto Hard CLose Date Next 3 Days, Send email notification to user.
            if(!mapOfNextThreeDaysOpportunity.isEmpty()){
                //message.setSubject('Opportunity Closing within Next 3 days');         
                for(Opportunity opt : mapOfNextThreeDaysOpportunity.values()){
                    String optName = opt.Name;
                    String stageName = Opt.StageName;
                    String vertical = opt.Vertical__c;
                    String offerings = opt.Services__c;
                    String geography = opt.Regions__c;
                    String accName = opt.Account.Name;
                    String ownerName = opt.Owner.Name;
                    decimal amount = opt.ABC_RuM__c;
                    string TargetLink = domain + '/' + opt.id;

                    if(opt.Auto_Hard_Closed_Date__c==System.Today()){
                        opt.Reason_opportunity_was_closed_Category__c = 'Stale';
                        opt.Opportunity_Close_Reason__c = 'Others';
                        opt.StageName='Closed';
                        optList.add(opt);
                    }
                    else{
                        tableStr += '<tr> <td><a href="'+TargetLink+'">' + optName +'</a> <td>' + stageName + '</td><td>' + vertical + '</td><td>' + offerings + '</td> <td>' + geography + '</td> <td>' + accName + '</td><td>' + ownerName + '</td> <td>' + amount + '</td> </tr>';   
                    }

                }
                tableStr+='</table>';   
                if(string.isBlank(bodyStr)){
                    bodyStr += '<html><body>Dear ' + receipentName + ', ' +'<br><br>'+
                        'You have Total ' + recordsProcessed + ' List of Opportunity which will auto hard close soon: ' +'<br><br>'+
                        +tableStr+'<br><br>'+
                        'Please DO NOT reply to this email.</body></html>' ;        
                }
                //Calling MentionUtils Class
                MentionUtils.NotifyUser(actorId,bodyStr);

                if(!optList.isEmpty()){
                    update optList;
                }           
            }
        }   



        global void finish(Database.BatchableContext info) {

        } 
    }

MentionUtils Class:

public with sharing Class MentionUtils{
    public static void NotifyUser(Id userId,String Message){
        if(userId != UserInfo.getUserId()){
            system.debug('Inside User Block');
            system.debug('Message is:'+Message);
            ConnectApi.MessageBodyInput messageInput = new ConnectApi.MessageBodyInput();
            messageInput.messageSegments = new List<ConnectApi.MessageSegmentInput>();
            //create and add mention segments
            ConnectApi.MentionSegmentInput mentionSegment = new ConnectApi.MentionSegmentInput();
            mentionSegment.Id = userId;
            messageInput.messageSegments.add(mentionSegment);

            ConnectApi.MarkupBeginSegmentInput markupBeginSegment = new ConnectApi.MarkupBeginSegmentInput();
            markupBeginSegment.markupType = ConnectApi.MarkupType.Code;
             messageInput.messageSegments.add(markupBeginSegment);
            // create and add Message Body Segments
            ConnectApi.TextSegmentInput textSegment;
            textSegment = new ConnectApi.TextSegmentInput();
            textSegment.text = Message;

            ConnectApi.MarkupEndSegmentInput markupEndSegmentInput = new ConnectApi.MarkupEndSegmentInput();
            markupEndSegmentInput.markupType = ConnectApi.MarkupType.Code;
            //markupEndSegmentInput.markupType  = message;
            messageInput.messageSegments.add(markupEndSegmentInput);

            //create FeedItemInput and add the MessageInput to it
            ConnectApi.FeedItemInput input = new ConnectApi.FeedItemInput();
            input.body = messageInput;
            input.feedElementType = ConnectApi.FeedElementType.FeedItem;
            input.subjectId = 'me';
            //finally post the Current Users Feed
            //ConnectApi.FeedElement feedElement = ConnectApi.ChatterFeeds.postFeedElement(Network.getNetworkId(), input);
            ConnectApi.ChatterFeeds.postFeedElement(null, input);
        }   
    }   

}
  • You can do that via the ConnectAPI in Apex. – Pragati Jain Oct 16 '18 at 15:04
  • Checkout this for html table developer.salesforce.com/docs/atlas.en-us.apexcode.meta/… – Pragati Jain Oct 16 '18 at 15:07
  • I am already using Connect API but the problem is whenever i receive output, it is always display HTML format that I mentioned in description. It is not showing the data in the form of table. – Dhananjay Patil Oct 17 '18 at 9:36
  • Please paste your code as well. – Pragati Jain Oct 17 '18 at 9:37
  • There is an issue in your HTML string. I found a character (`) at the end of the string. – Pragati Jain Oct 17 '18 at 9:54
1

Currently ConnectApi.MarkupType supports following markups :

  1. Bold—Bold tag.
  2. Code—Code tag.
  3. Italic—Italic tag.
  4. ListItem—List item tag.
  5. OrderedList—Ordered list tag.
  6. Paragraph—Paragraph tag.
  7. Strikethrough—Strikethrough tag.
  8. Underline—Underline tag.
  9. UnorderedList—Unordered list tag.

So we can't achieve your requirement now.

  • Thanks for the info. As i can not display the data in tabular format on chatter. I found other possible solution to post the chatter message on individual records with the help of HttpRequest. – Dhananjay Patil Oct 24 '18 at 13:04
  • You can share alternative way here. It may be useful for others. – Pragati Jain Oct 24 '18 at 13:39
0

To Post the chatter message on individual records, I have created ChatterUtils Class & via Rest API I am posting chatter message to Individual Records.

public with sharing Class ChatterUtils{
    public static void NotifyUserOnRecordUpdate(Id userId,Id managerId,Id RecordId,string recordName,string modifyDate,string parentRecordName,integer difference){
        String salesforceHost = System.Url.getSalesforceBaseURL().toExternalForm();
        String fyiMessage;
        String ChatterPostText = 'This is to inform you that the '+recordName+' opportunity on the '+parentRecordName+' account has not been modified in '+difference+' days and will be closed automatically on '+modifyDate+'. To avoid auto-closure, please make the appropriate updates to the opportunity or close if appropriate.\\n';
        ChatterPostText+='\\n';
        ChatterPostText+='Thanks!\\n';
        ChatterPostText+='\\n';
        fyiMessage='FYI';
        system.debug('ChatterPostText is:'+ChatterPostText);
        String url =  salesforceHost + '/services/data/v26.0/chatter/feeds/record/' + RecordId  + '/feed-items';

        HttpRequest req = new HttpRequest();

        req.setMethod('POST');
        req.setEndpoint(url);
        req.setHeader('Content-type', 'application/json');
        req.setHeader('Authorization', 'OAuth ' + UserInfo.getSessionId());

        req.setBody('{ "body" : { "messageSegments" : [ { "type": "mention", "id" : "' + userId + '" }, { "type": "text",  "text" : "' + ' ' + ChatterPostText + ' " },{ "type": "mention", "id" : "' + managerId + '" },{ "type": "text",  "text" : "' + ' ' + fyiMessage + ' " } ] } }');

        Http http = new Http();
        if(!Test.isRunningTest()){
        HTTPResponse res = http.send(req);
        }

    }
}

I have referred the below link and created the above class accordingly.

https://developer.secure.force.com/cookbook/recipe/posting-a-chatter-mention-via-apex

Above class post chatter message on individual records & I am calling this class from Batch Class.

I want to know if the above solution is the best practice or not?

I can go with Connect API Solution also but the problem is how can I @ mention 2 users using connect API. IF you can see req.setBody(), I am mentioning Owner first and in the end of the Chatter Message, I am mentioning User's Manager.

Any comments or suggestions are welcome.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.