Saturday, March 8, 2014

Extending the Salesforce LiveAgent pre-chat form through javascript and VF Remoting

A client of Seahorce Solutions recently requested a LiveAgent preChat integration with the following requirements:

1)  The Chat Link be hosted within their non-Salesforce website.
2)  The preChat form capture first name, last name, and email (all required) and default the Agent greeting to include the provided first name (and Agent Name).
3)  The preChat form search for a matching Contact and a matching Lead with some specific conditions based on the outcome:
  • If a matching contact is found, DO NOT create a new Lead.
  • If a matching contact AND lead are found, bring up both.
  • If NO matching contact is found, do NOT create a new contact, but DO create a new Lead (as long as a matching Lead is not found).
I have included my solution below and highlighted in red those areas where I had to make tweaks to more common use cases.

Satisfying the Request:
     #1 was relatively easily satisfied using the documented ChatOnline and ChatOffline resources in the Chat configuration.  The LiveAgent configuration wizard provides deployment code for your non-Salesforce website.

     #2 turned out to be a little tricky (unless I missed something), because when I used the documented id="preChatField" to identify the element for "Visitor_Name", I could no longer get it included in the New Lead data.  My work-around was the write the first name to a hidden field dedicated to the greeting field (javascript slight-of-hand).

     #3 turned out to be the most difficult problem to solve.  There are no documented approaches for CONDITIONALLY creating new records, although this seems to be a reasonable requirement.  The client did not want chat requests from existing contacts (customers) to create leads unnecessarily.  My work-around was to come up with a way to render the "liveagent.prechat.findorcreate.map.doCreate:Lead" command ineffectual when a contact was found.  The trick ending up being to independently query the Contacts object to look for a match before actually submitting the form (and letting the LiveAgent api discover the same contact).  To do this, I changed the standard submit to a regular button element.  This allowed me to "preCheck" the contact based on the email provided.  I did not want to use SOQL in javascript (injection threat), so I settled on creating an apex class to support a VisualForce remoting call.  The solution works by removing all the required field arguments from the "doCreate:Lead" element, thus preventing the successful creation of a lead (no errors are triggered)!  

Note:  The fact that there is no error feedback on incorrectly configuring the doCreate:Lead set of elements is probably the biggest pitfall for many folks trying to implement a preChat.  Make sure that you provide a value for ALL required fields on the object for which you are creating a new record (see my hidden LeadStatus field).  If you don't get it just right, you'll get no new records with even less insight into what went wrong.


Using VF Remoting within the LiveAgent preChat form enables the developer to make significant customizations (like auto-filling optional data for returning contacts).  

Enjoy!

*****************************
PRE-CHAT VISUALFORCE PAGE
*****************************
<apex:page showHeader="false"  standardController="Account" extensions="preChatRemoting_Con">
<!-- This script takes the endpoint URL parameter passed from the deployment page and makes it the action for the form -->

<script type="text/javascript">
     (function() {
     function handlePageLoad() {
       var endpointMatcher = new RegExp("[\\?\\&]endpoint=([^&#]*)");
       document.getElementById('prechatForm').setAttribute('action',
       decodeURIComponent(endpointMatcher.exec(document.location.search)[1]));
     } if (window.addEventListener) {
              window.addEventListener('load', handlePageLoad, false);
   } else { window.attachEvent('onload', handlePageLoad, false);
              }})();

  function SubmitForm(createLead) {

      if (!createLead) {  //We found a matching contact based on email provided, so DO NOT send parameters to create a new lead.
          document.getElementById("optionA").value="";
          document.getElementById("optionB").value="false";
      }
      else {   //No matching contact was found, so send parameters required to create a new lead.
          document.getElementById("optionA").value="FirstName,true;LastName,true;Company,true;Email,true;Description,true;Status;true;Type,true;LeadSource,true;Source_Type__c,true;";
          document.getElementById("optionB").value="true";
      }
      document.getElementById("prechatForm").submit();
  }

  function getRemoteContact()
    {
        var contactEmail = document.getElementById('contactEmail').value;
        Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.preChatRemoting_Con.getcontact}', contactEmail, function(result, event){
                if (event.status) {
                    SubmitForm(false);  //contact found, don't create a lead
                } else if (event.type === 'exception') {
                    SubmitForm(true);  //contact NOT found, DO create a lead
                } else {
                    SubmitForm(false);  //unknown error, DON'T create a lead
                }
            },
            {escape: true}
        );
    }
</script>

<form method="post" id="prechatForm">

<!-- Detail inputs -->
First Name: <input type="text" name="liveagent.prechat:leadFirstName" onchange="javascript: document.getElementById('prechat_field').value=this.value;" required="required"/><br />
Last Name: <input type="text" name="liveagent.prechat:leadLastName"  required="required"/><br />
Email: <input type="text" id="contactEmail" name="liveagent.prechat:leadEmail"  required="required"/><br />

<!--greeting field, copies from FirstName input-->
<input type="hidden" name="liveagent.prechat.name"  id='prechat_field'/>

<!--hidden fields written to the new lead-->
<input type="hidden" name="liveagent.prechat:leadStatus" value="Open" />
<input type="hidden" name="liveagent.prechat:leadType" value="Prospect" />
<input type="hidden" name="liveagent.prechat:leadSource" value="Word of Mouth" />
<input type="hidden" name="liveagent.prechat:leadSourceType" value="Inbound Chat" />
<input type="hidden" name="liveagent.prechat:leadCompany" value="[[Unknown]]" />

<!-- Creates an auto-query for a matching Contact record’s Email field based on the value of the liveagent.prechat:leadEmail field -->
    <input type="hidden" name="liveagent.prechat.query:leadEmail" value="Contact,Contact.Email" />


<!-- Map the detail inputs to the Lead fields -->
<input type="hidden" name="liveagent.prechat.findorcreate.map:Lead" value="FirstName,leadFirstName;LastName,leadLastName;Company,leadCompany;Email,leadEmail;Status;leadStatus;Type,leadType;LeadSource,leadSource;Source_Type__c,leadSourceType;" />

<!-- Map the detail inputs to the Contact fields -->
<input type="hidden" name="liveagent.prechat.findorcreate.map:Contact" value="FirstName,leadFirstName;LastName,leadLastName;Email,leadEmail;" />


<!-- Try to find Contact by email (exact match) -->
<input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Contact" value="Email,true;" />
<input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Contact" value="Email,true;" />


<!-- Try to find the Lead by email (exact match) -->
<input type="hidden" name="liveagent.prechat.findorcreate.map.doFind:Lead" value="Email,true;" />
<input type="hidden" name="liveagent.prechat.findorcreate.map.isExactMatch:Lead" value="Email,true;" />

<!-- If the Lead is not found, then create one with the following fields set -->
<input type="hidden" id="optionA" name="liveagent.prechat.findorcreate.map.doCreate:Lead" value="FirstName,true;LastName,true;Company,true;Email,true;Description,true;Status;true;Type,true;LeadSource,true;Source_Type__c,true;" />

<!-- Save the Lead on the Live Chat Transcript -->
<input type="hidden" name="liveagent.prechat.findorcreate.saveToTranscript:Lead" value="Lead" />

<!-- Show the Lead when it is found or created -->
<input type="hidden" id="optionB" name="liveagent.prechat.findorcreate.showOnCreate:Lead" value="true" />

<!-- Show the Contact when it is found or created -->
<input type="hidden" name="liveagent.prechat.findorcreate.showOnCreate:Contact" value="true" />

<input type="button" value="Begin Chat Session" id="prechat_submit" onclick="javascript: getRemoteContact();"/>
</form>
</apex:page>

*****************************
APEX CONTROLLER
*****************************
public class preChatRemoting_Con 
{
    public preChatRemoting_Con(ApexPages.StandardController controller) 
    {

    }
    @RemoteAction
    public static contact getcontact(string contactemail)
    {
        Contact testContact=new Contact();
        testContact=[Select Id,Name from Contact where email=:contactemail limit 1];
        return testContact;
    }

}

18 comments:

  1. Andrew this is amazing. Thanks for sharing. We just went into production with Live Agent this morning on our site.

    ReplyDelete
  2. As someone who has worked on similar issue, I can definitely appreciate this solution. Great stuff and looking forward to more articles.

    ReplyDelete
  3. Hi Andrew, Thank you for posting this !
    My question like u created a Lead doCreate: Lead is it possible ti create an Event record similarly ?

    Thanks,
    Vaishali

    ReplyDelete
  4. Great article, thank you Andrew!

    One quick question, is it possible to search on multiple fields on the same record (i.e.) if email and account match on the contact record, then bring up that record else create a new contact record?

    Your help is much appreciated.

    Thanks!

    ReplyDelete
  5. This is absolutely spot on code! I do have a question... Can I also create a New Case as well as the Lead and Contact? I want to create a single survey and give the customer the option to select Sales (Lead) or Service (Case) both of which could also utilize the contact information.

    ReplyDelete
  6. any idea if this could be recreated in a custom HTML page vs. using VisualForce pages?

    ReplyDelete
  7. As I stated in an earlier comment, this is pretty perfect! I am having another issued with Live Agent that I can't seem to get a straight answer on and am hoping you might have some insight...

    I want to integrate my Google Analytics into Live Agent but the only information on that was from 2009 when it was an out of the box console and no longer applies. Salesforce Support says it isn't possible, do you know if it is possible?

    Thanks!

    ReplyDelete
  8. Thank you Andrew. This has saved my bacon (yummmmmmm bacon).

    ReplyDelete
  9. Thanks a ton!!
    could you please provide inputs to route the chats based on pre chat form entry ?

    ReplyDelete
  10. Is it possible to host the page on a third party website? If yes, what would be the process that is required to fulfill it.

    ReplyDelete
  11. Hi All,

    I ran into an issue recently with the Live Agent. So, I am creating a lead from the prechat form which is instantly getting converted by the backend apex trigger.So the showoncreate.lead does not seem to be able to open the converted lead as a subtab in console. The requirement is that leads that are created through live agent will get instantly converted and open up as a subtab in console. Any help is appreciated.

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Hi Andrew,

    My requirement is not to create case at all if contact(email and phone) doesn't match contact present in salesforce org.

    I tried your approach but Case is getting created still.

    Any help will be appreciated.

    Thanks.

    Yashita

    ReplyDelete
  14. Nowadays, most of the businesses rely on cloud based CRM tool to power their business process. They want to access the business from anywhere and anytime. In such scenarios, salesforce CRM will ensure massive advantage to the business owners.
    online form builder for salesforce

    ReplyDelete
  15. This really just saved my butt. Is there any way to not pop the "search results" screen in the console and just pop the contact (if found)?

    ReplyDelete
  16. Do you happen to have a test class for this? I am not a developer, but my understanding is we need a test class before you can move the APEX Class to production.

    ReplyDelete
  17. Validations are not working. Form is submitting without any data .

    ReplyDelete