I don’t think I’ve come across any part of Salesforce that is so hard to understand, set up, manage and build a great user experience.

I may actually give up before I get very far with it. YEP, I GAVE UP. Wait! I’m back, and I think I’ve conquered it! But no, there are STILL issues!

I did a Video a few months ago for regular emails https://www.youtube.com/watch?v=nhhoGRyBnxU and an video on Emails in Cases before that https://www.youtube.com/watch?v=AKQruEHMZ5I to try to surface some of the issues. Now I’m trying to get it set up for real.

Video

00:00 - Intro - Expanding on my video 1 year ago

00:40 - The only answer is code

01:33 - Quick Action Overrides don't work

02:05 - Simple version of the code

02:34 - Understanding the requirements

04:53 - A decision tree document

07:42 - A review of Case setup in Lightning

12:04 - Email Buttons

15:23 - Quick Text

16:36 - Email Preview Issues

17:30 - Files on Cases

18:08 - Email to Client Button

21:16 - Email History and Activity issues

22:19 - Enhanced Email Lightning Page

22:49 - Third Party Email Button

23:40 - Rogue Email Addresses

24:41 - Third Party Email Button

26:17 - Some thread concerns

28:23 - Using the standard buttons on the Email

30:02 - Buttons, buttons everywhere

31:44 - Let's look at the code

35:45 - Validated From Address

36:30 - Setting up Email-to-Case

37:07 - Setting the Email Templates and Subject

38:27 - Get the right Email Template

39:57 - Setting the To Address

43:41 - No HTML Body

44:32 - One extra step to set up the code

45:34 - Weirdness with Templates

47:35 - Despair?

General Email Functionality in Cases

Email Issues

Interesting Things

The Salesforce Way

So basically what Salesforce is saying in the way they have set up Cases, is that when handling cases you only ever going to have a very simple case with a couple of fields on it and you only ever going to email the person who submitted the Case back and forth until the case of resolved and the Case can be closed.

If you have any other Case functionality other than that, then don’t bother trying to set up Cases with configuration only, you are going to need to do a heap of work to try to overcome what Salesforce thinks is the only way to handle Cases.

In my case the functionality I need is

Email Button / Send Email Quick Actions

Using Code to Send Emails

My Decision Tree

Name

When

Button Name

From Address

To Address

Signature

Template

Subject

Include Body

REF Included

Attachments

To Client

Any time

Email to Client

test@example.com

Primary Contact.Email

Sender.Title

BLANK

Your Case - {subject} {EmailThreadId}

No

Yes

Add any required

Reply to existing email

Any time

Reply or Reply All from Email

test@example.com

From Address

Sender.Title

BLANK

Re: Existing Subject

Yes

Yes

No

Reply

test@example.com

From Address

Sender.Title

BLANK

Re: Existing Subject

Yes

Yes

No

Forward an existing email

Any time

Forward from Email

test@example.com

Enter a valid address

Sender.Title

BLANK

FW: Existing Subject

Yes

Yes

Included

To Third Party

Any time

Third Party Email

test@example.com

ThirdParty.Email

Sender.Title

THIRD PARTY BLANK

Case for {ClientName} - {Subject} {EmailThreadId}

No

Yes

Add any required

Code Gotchas

My Code

global class EmailDefaults implements QuickAction.QuickActionDefaultsHandler {
    
    global void onInitDefaults(QuickAction.QuickActionDefaults[] defaultsList) {
    for (Integer i = 0; i < defaultsList.size(); i++) {
      QuickAction.QuickActionDefaults defaults = defaultsList.get(i);

      // Check if the quick action is the standard case feed `SendEmail` action
      if (
        defaults instanceof QuickAction.SendEmailQuickActionDefaults &&
        defaults.getTargetSObject().getSObjectType() == EmailMessage.sObjectType &&
        defaults.getActionType().equals('SendEmail')
      ) {
        String actionName = defaults.getActionName();
        Id contextId = defaults.getContextId();

        // check if the related object is a Case
        // and process it in the same way no matter if it's 
        // a `SendEmail`, `Reply`, or `Reply All` action
        if (
          (actionName.equals('Case.Send_Email') ||
           actionName.equals('Case.ReplyEmail') ||
           actionName.equals('Case.ThirdPartyEmail') ||
           actionName.equals('EmailMessage._Reply') ||
           actionName.equals('EmailMessage._Forward') ||
           actionName.equals('EmailMessage._ReplyAll')) &&
           contextId != null &&
           contextId.getSobjectType() == Case.sObjectType
        ) {
          applySendEmailDefaultsForCase((QuickAction.SendEmailQuickActionDefaults) defaults, actionName);
          break;
        }
      }
    }
  }
    
    private void applySendEmailDefaultsForCase(QuickAction.SendEmailQuickActionDefaults sendEmailDefaults, string sendType) {
    Case c = [
      SELECT ContactId, LinkedContID__c, ThirdParty__c
      FROM Case
      WHERE Id = :sendEmailDefaults.getContextId()
    ]; 
    
    EmailMessage emailMessage = (EmailMessage) sendEmailDefaults.getTargetSObject();
    
    if (sendType.contains('Reply')) {        
        sendEmailDefaults.setTemplateId(getTemplateId('developerName'));
        sendEmailDefaults.setInsertTemplateBody(true);
        sendEmailDefaults.setIgnoreTemplateSubject(true);
        emailMessage.ValidatedFromAddress = 'orgwidemail@example.com';
    }
    else if (sendType.contains('Forward')) {
        sendEmailDefaults.setTemplateId(getTemplateId('developerName'));
        sendEmailDefaults.setInsertTemplateBody(true);
        sendEmailDefaults.setIgnoreTemplateSubject(true);
        emailMessage.ValidatedFromAddress = 'orgwidemail@example.com';    
    }
    else if (sendType.contains('ThirdParty')) {
        String[] toIdsThirdParty = new String[]{c.ThirdParty__c}; 
        sendEmailDefaults.setTemplateId(getTemplateId('developerName'));
        sendEmailDefaults.setInsertTemplateBody(true);
        sendEmailDefaults.setIgnoreTemplateSubject(false);
        emailMessage.ToIds = toIdsThirdParty;
        emailMessage.ToAddress = '';
        emailMessage.ValidatedFromAddress = 'orgwidemail@example.com';
        emailMessage.HTMLBody = '';    
    }
    else{ 
        //This is the default. New Email to Case Contacts only. 
        String[] toIds = new String[]{c.ContactId, c.LinkedContID__c};    
        sendEmailDefaults.setTemplateId(getTemplateId('developerName'));
        sendEmailDefaults.setInsertTemplateBody(true);
        sendEmailDefaults.setIgnoreTemplateSubject(false);
        emailMessage.ToIds = toIds;
        emailMessage.ToAddress = '';
        emailMessage.ValidatedFromAddress = 'orgwidemail@example.com';  
        emailMessage.HTMLBody = ''; 
    }
  }
  
    private ID getTemplateId( String templateName ) {
        ID templateId = null;  
        List<EmailTemplate> templates = new List<EmailTemplate>([
            SELECT id
            FROM EmailTemplate
            WHERE developerName = :templateName
            LIMIT 1
        ]);
        
        if ( templates.size() > 0 ) {
            templateId = templates[0].id;
        } else {
            System.debug( LoggingLevel.ERROR, 'Unable to locate email template using name "' + templateName + '"' );
        } 
        return templateId;
    }
}

A Fix

I fixed it! I had to simplify my requirements a bit, and it’s the best of a really bad situation.

I changed

else if (sendType.contains('ThirdParty')) {

else if (c.Status.contains('Service')) {

and added Status to the SOQL query

OMG that took so much work and took so long to work through.

Bottom Line - NEVER USE MORE THAN ONE Email Button on Cases! EVER!

Testing This Code

I am SOO stuck on testing this code.

This is the extent of the Code Coverage I am getting following many of the examples of test classes found on Github. THIS SHOULD NOT BE THIS HARD. I am trying to install this solution in an org that has only this code and I can not get anywhere near the 75% code coverage.

The "suggested" way to test is https://salesforce.stackexchange.com/questions/102338/test-class-for-quickaction-quickactiondefaultshandler
My test Class currently creates a Closed Case and does a Reply Email and the code coverage is not even looking at the Status of the Case let alone hitting the bit of code that actually sets the default templates.