Powered By Blogger

Saturday 9 August 2014

Handling Inline Images in Salesforce Inbound Email

One of the Salesforce powerful feature is Inbound Email Services. Using this, you can send an email to Salesforce and process the email body as you want. Inbound email handler class allows you to access email’s plain text body as well as html body.

One issue that I came across was, none of the existing method can handle your inbound email’s inline images. I searched community a lot and realized that, too many people had the same issue but they never find any solution, so I thought to pick it from there.

Issue:
You want to write email’s HTML body in a Rich Text area field and your email has inline images. Now, when you send this, inbound email will lost those images references and your body will say “Inline Image XXXXX”.

More Detail:
I have created an email service in my org. It only listens inbound email and put the email’s HTML body in a rich text area field on lead.

global class POCEmailHandler implements Messaging.InboundEmailHandler {
    
    //Method to process email 
    global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
        Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
        
        //Create a New Lead record
        Lead lead = new Lead();
        lead.LastName = 'Inbound Lead';
        lead.Email = email.fromAddress;
        lead.Company = 'N/A';
        lead.HTML_Body__c = email.htmlBody;
        
        //Insert record
        insert lead;
    }
}


Now I sent below email to this email service. Inbound handler class processed it and a new lead was created in system.



This lead was created in system. Notice the HTML Body field.












Inline image didn’t came in the HTML body.

Reason:
Salesforce process inline images as binary attachments. When you send an email having inline images, it doesn’t come as a part of body, but it comes as binary attachment. From here, you will have to put your own logic to place the inline images back in HTML Body.

Resolution:

1.       Process binary attachments and see, which attachment came as a part on inline image. Create an attachment record for each image. Below code is to create a map with the inline image name and its related attachment record.


//Create a list of attachments
        Map< String, Attachment > mapAttachments = new Map< String, Attachment >(); 
        
        //Attachments
        for(Messaging.InboundEmail.BinaryAttachment bA : email.binaryAttachments) {
            System.debug(bA);
            for(integer i = 0; i < bA.headers.size(); i++) {
                
                //Header Value
                String headerValue = bA.headers[i].value;
                if(headerValue.startsWith('ii') || headerValue.startsWith('< image')) {
                    headerValue = headerValue.replaceAll('<', '').replaceAll('>', '');
                    mapAttachments.put(headerValue, new Attachment(Name = bA.fileName, body = bA.body, 
                                                    ParentId = lead.Id, ContentType = bA.mimeTypeSubType));
                }
            }
        }



2.       Now process HTML body content and get all the places where actual Inline Image was replaced with blank space and update these instances with the attachment link.
//Process inline images and update the HTML Body
        for(String headerValue : mapAttachments.keySet()) {
    
            //Reference Link
            String refLink = '/servlet/servlet.FileDownload?file=' + mapAttachments.get(headerValue).Id;
            lead.HTML_Body__c = lead.HTML_Body__c.replaceAll('cid:' + headerValue, refLink);
        }
        update lead;
 


Updated code will look like this.
global class POCEmailHandler implements Messaging.InboundEmailHandler {
    
    
global class POCEmailHandler implements Messaging.InboundEmailHandler {
    
    //Method to process email 
    global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
        Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
        
        //Create a New Lead record
        Lead lead = new Lead();
        lead.LastName = 'Inbound Lead';
        lead.Email = email.fromAddress;
        lead.Company = 'N/A';
        lead.HTML_Body__c = email.htmlBody;
        
        //Insert record
        insert lead;
        
        //Create a list of attachments
        Map< String, Attachment > mapAttachments = new Map< String, Attachment >(); 
        
        //Attachments
        for(Messaging.InboundEmail.BinaryAttachment bA : email.binaryAttachments) {
            System.debug(bA);
            for(integer i = 0; i < bA.headers.size(); i++) {
                
                //Header Value
                String headerValue = bA.headers[i].value;
                if(headerValue.startsWith('ii') || headerValue.startsWith('< image')) {
                    headerValue = headerValue.replaceAll('<', '').replaceAll('>', '');
                    mapAttachments.put(headerValue, new Attachment(Name = bA.fileName, body = bA.body, 
                                                    ParentId = lead.Id, ContentType = bA.mimeTypeSubType));
                }
            }
        }
        
        //Insert
        insert mapAttachments.values();
        
        //Process inline images and update the HTML Body
        for(String headerValue : mapAttachments.keySet()) {
    
            //Reference Link
            String refLink = '/servlet/servlet.FileDownload?file=' + mapAttachments.get(headerValue).Id;
            lead.HTML_Body__c = lead.HTML_Body__c.replaceAll('cid:' + headerValue, refLink);
        }
        update lead;
        
        return result;
    }
}




Now send the same email to Salesforce email address and see the result.













Don't  miss DF-14. See you there.
http://bit.ly/df14infblog
Join us in the Developer Zone at Dreamforce 2014 | Training, Talks & MoreSalesforce
Join Salesforce Developers from around the world in the Developer Zone at Dreamforce 2014. Learn new skills through interactive sessions and hands-on training. Immerse yourself in the tools and knowledge you need to build better, faster, and deliver more. For a limited time developers can register for Dreamforce at a special rate of $899.

Tuesday 29 July 2014

Approval Fields in Email Notification

Many of you have been worked with the approval processes. When you are using approval processes, you can define approval notification template. It’s too common when you need to address the approver in email notification who is going to receive this email like:

Hi BSS,

A deal has been submitted and assigned to you for your approval.
Thanks,
<<Assignee Name>>
<<Company Name>>

When you define the email template, you have choice to select the Approval fields.
Request_Approver
Request_Assignee

But unfortunately, none of the field is going to help when you want to include the Approver name in email who is going to handle this approval request.
Request_Approver is, who currently approved this request.
Request_Assignee is, who assigned this request.

I created this email template in Salesforce for testing.

 

Now let’s have a case when you have a multi-step approval process. User requested for the approval and email notification was sent to first level approver. So how will you get the approver name in email?

My approval process is:



I created an account and it sent the Approval Request to Lisa Hayden. Following email was sent to User Lisa.

See both the field says Bhavi Sharma.


How you can quickly fix this:
  1. Create a user lookup field on your object and call it “Next Approver”.
  2. In the approval process’s initial submission action, populate this field with the appropriate user (If you are using a queue, better is to create a Text field and get it populated with the Queue name).
  3. Now use this field in email approval submission notification email.
  4. In the first level approval actions, get this custom field populated with the name of the second level approver.