Customize the Pre-Chat Page UI with Lightning Components

Add Snap-ins Chat to your website so customers can quickly get answers to their questions by chatting with an agent while browsing your site. Snap-ins Chat uses a Live Agent deployment that you can quickly configure. Then, simply add the chat code to the web pages where you want the chat snap-in to be available. When agents chat with customers via Snap-ins Chat, the agents use Live Agent in their console. In this post, we will see how to create a pre-chat form by using the lightning components. You can able to Customize the fields, layout, buttons, images, validation, or any other part of the user interface for pre-chat using a custom Lightning component by using Pre chat API. I  am assuming that you have live agent deployments is available to use the snap-ins

prerequisites

To set up Snap-ins Chat, your org must meet these prerequisites:

  • Lightning Experience must be enabled to set up snap-ins
  • Service Cloud License
  • Live Agent License
  • Live Agent must be enabled in your Org
  • A Live Agent chat button and a Live Agent deployment must be set up and available in your Org
  • A Salesforce Community (preferable) or a Salesforce Site must be set up on your org and available for guest user access.

Create a Snap-Ins Deployment

we need to create a Snap-in deployment for each snap-in that you’re using on your website. In this example, the Snap-ins Chat setup uses a Salesforce Community. From Setup, find Snap-ins and click New Deployment and enter details as shown below In the Site Endpoint menu, select a Salesforce community or Salesforce Site where you want to host the snap-ins.

After saving the snap-ins deployment, you need to enable the live agent setting. Go to the snap-ins which you configured and select view. In the Snap-ins configuration page, go to the Live Agent settings section and click Start.

  1. In the Live Agent Deployment menu, select the Live Agent configuration that you want to use with the chat snap-in from the dropdown list.
  2. In the Live Agent Button menu, select the Live Agent chat button or automated invitation that you want to use with the chat snap-in from the dropdown list.
  3. Select Show queue position if you want to display the customer’s place in line while they wait for a support agent. Make sure that the Live Agent chat button you selected has Enable Queue selected in your Live Agent chat button settings.
  4. Click Save.

To set up the pre-chat form

From the snap-in deployment, you can able to add the pre-chat form that will be able to the user before the chat starts.

  1. In the Pre-chat section, move the radio button to Active.
  2. Click Edit.
  3. Select the use case for the pre-chat form.
After saving the pre-chat form go and override the pre-chat form with the below code. 
PreChatCmp.cmp
Here is the lightning component that we will use to override the pre-chat form.
<aura:component implements="lightningsnapin:prechatUI" description="Sample custom pre-chat component for Snap-ins. Implemented using Aura.">
    <!-- You must implement "lightningsnapin:prechatUI" for this component to appear in the "Pre-chat Component" customization dropdown in the Snap-ins setup -->
    
    <!-- Pre-chat field components to render -->
    <aura:attribute name="prechatFieldComponents" type="List" description="An array of objects representing the pre-chat fields specified in pre-chat setup."/>
    
    <!-- Handler for when this component is initialized -->
    <aura:handler name="init" value="{!this}" action="{!c.onInit}" />
    
    <!-- For Aura performance -->
    <aura:locator target="startButton" description="Pre-chat form submit button."/>
    
    <!-- Contains methods for getting pre-chat fields, starting a chat, and validating fields -->
    <lightningsnapin:prechatAPI aura:id="prechatAPI"/>
    
    <h2>Prechat form</h2>
    <div class="prechatUI">
        <div class="prechatContent">
            <ul class="fieldsList">
                <!-- Look in the controller's onInit function. This component dynamically creates the pre-chat field components -->
                {!v.prechatFieldComponents}
            </ul>
        </div>
        <div class="startButtonWrapper">
            <ui:button aura:id="startButton" class="startButton" label="{!$Label.LiveAgentPrechat.StartChat}" press="{!c.handleStartButtonClick}"/>
        </div>
    </div>
    
</aura:component>

Controller.js

({
    /**
     * On initialization of this component, set the prechatFields attribute and render pre-chat fields.
     * 
     * @param cmp - The component for this state.
     * @param evt - The Aura event.
     * @param hlp - The helper for this state.
     */
	onInit: function(cmp, evt, hlp) {
        // Get pre-chat fields defined in setup using the prechatAPI component
		var prechatFields = cmp.find("prechatAPI").getPrechatFields();
        // Get pre-chat field types and attributes to be rendered
        var prechatFieldComponentsArray = hlp.getPrechatFieldAttributesArray(prechatFields);
        
        // Make asynchronous Aura call to create pre-chat field components
        $A.createComponents(
            prechatFieldComponentsArray,
            function(components, status, errorMessage) {
                if(status === "SUCCESS") {
                    cmp.set("v.prechatFieldComponents", components);
                }
            }
        );
    },
    
    /**
     * Event which fires when start button is clicked in pre-chat
     * 
     * @param cmp - The component for this state.
     * @param evt - The Aura event.
     * @param hlp - The helper for this state.
     */
    handleStartButtonClick: function(cmp, evt, hlp) {
        hlp.onStartButtonClick(cmp);
    }
});

helper.js

({
    /**
	 * Map of pre-chat field label to pre-chat field name (can be found in Setup)
	 */
    fieldLabelToName: {
        "First Name": "FirstName",
        "Last Name": "LastName",
        "Email": "Email",
        "Subject": "Subject",
        "Type": "Type",
        "Priority": "Priority"
    },
    
    /**
	 * Event which fires the function to start a chat request (by accessing the chat API component)
	 *
	 * @param cmp - The component for this state.
	 */
    onStartButtonClick: function(cmp) {
        var prechatFieldComponents = cmp.find("prechatField");
        var fields;
        
        // Make an array of field objects for the library
        fields = this.createFieldsArray(prechatFieldComponents);
        
        // If the pre-chat fields pass validation, start a chat
        if(cmp.find("prechatAPI").validateFields(fields).valid) {
            cmp.find("prechatAPI").startChat(fields);
        } else {
            console.warn("Prechat fields did not pass validation!");
        }
    },
    
    /**
	 * Create an array of field objects to start a chat from an array of pre-chat fields
	 * 
	 * @param fields - Array of pre-chat field Objects.
	 * @returns An array of field objects.
	 */
    createFieldsArray: function(fields) {
        if(fields.length) {
            return fields.map(function(fieldCmp) {
                return {
                    label: fieldCmp.get("v.label"),
                    value: fieldCmp.get("v.value"),
                    name: this.fieldLabelToName[fieldCmp.get("v.label")]
                };
            }.bind(this));
        } else {
            return [];
        }
    },
    
    /**
     * Create an array in the format $A.createComponents expects
     * 
     * Example:
     * [["componentType", {attributeName: "attributeValue", ...}]]
     * 
	 * @param prechatFields - Array of pre-chat field Objects.
	 * @returns Array that can be passed to $A.createComponents
     */
    getPrechatFieldAttributesArray: function(prechatFields) {
        // $A.createComponents first parameter is an array of arrays. Each array contains the type of component being created, and an Object defining the attributes.
        var prechatFieldsInfoArray = [];
        
        // For each field, prepare the type and attributes to pass to $A.createComponents
        prechatFields.forEach(function(field) {
            var componentName = (field.type === "inputSplitName") ? "inputText" : field.type;
            var componentInfoArray = ["ui:" + componentName];
            var attributes = {
                "aura:id": "prechatField",
                required: field.required,
                label: field.label,
                disabled: field.readOnly,
                maxlength: field.maxLength,
                class: field.className,
                value: field.value
            };
            
            // Special handling for options for an input:select (picklist) component
            if(field.type === "inputSelect" && field.picklistOptions) attributes.options = field.picklistOptions;
            
            // Append the attributes Object containing the required attributes to render this pre-chat field
            componentInfoArray.push(attributes);
            
            // Append this componentInfoArray to the fieldAttributesArray
            prechatFieldsInfoArray.push(componentInfoArray);
        });
        
        return prechatFieldsInfoArray;
    }
});

Now override the Pre Chat component with this lightning component. 

Adding Your Snap-In to a Website

Now go to the community builder and add the out of box lightning component Snap-ins to the builder as show below.

Here we are configuring the Snap-ins Chat deployment and configure as shown below.

Add Your Website to the CORS Whitelist

Add the URLs of the web pages where you intend to add the snap-in to the CORS whitelist in your Org. The web page where you add the snap-in is the page that customers use to access chat.

Testing 

You can able to see the Pre chat form from the community as shown below. Before going to snap-ins, make sure at least one agent in on live agent with available status.

Once the user submits the form it’s going to create a case as shown below and the agent will able to chat with the user.

Salesforce Node.js Canvas Signed Request

Introduction

In this blog, I am going to explain how to set up salesforce canvas signed the request with node js. Signed Request is default authentication for canvas app. The signed request authorization flow varies depending on whether the canvas app’s Permitted Users field is set to “Admin approved users are pre-authorized” or “All users may self-authorize”. The signed request containing the consumer key, access token, and other contextual information is provided to the canvas app if the administrator has allowed access to the canvas app for the user or if the user has approved the canvas app via the approve/deny OAuth flow

Setup Connected App
First, we are going to create a connected app to obtain the consumer secret key which we will be used in the signed request.

  1. In Salesforce, from Setup, enter Apps in the Quick Find box, then select Apps.
  2.  In the Connected Apps related list, click New.
  3. In the Connected App Name field, enter NodeJsSignedRequest.
  4. In the Contact Email field, enter your email address.
  5.  In the API (Enable OAuth Settings) section, select the Enable OAuth Settings field.
  6. In the Callback URL field, enter https://localhost:8443/sdk/callback.html.
  7. In the Selected OAuth Scopes field, select Full Access.
  8. In the Canvas App Settings section, select Force.com Canvas.
  9. In the Canvas App URL field, enter https://localhost:8443/
  10. In the Access Method field, select the Signed Request (Post).
  11. In the Locations field, select Chatter Tab and save it.

On the detail page for the canvas app, next to the Consumer Secret field, click the link Click to reveal. The consumer secret is used in the app to authenticate. We will configure the this later in the Heroku apps.

Configure Who Can Access the Canvas App

Now you need to configure the canvas app for the user access

  1. go to “NodeJSSignedRequest” managing connected apps and click on manage and edit policies
  2.  In the Permitted Users field, select “Admin approved users are pre-authorized.” and then save it 
    3. In the Profiles related list, click Manage Profiles.
    4. Select the System Administrator profile and click Save.

Time to Code

1 .create a package.json file as shown below

{
  "name": "signedrequest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "express": "~4.8.1",
    "body-parser": "~1.2.2",
    "request": "~2.36.0",
    "ejs": "~1.0.0",
    "crypto-js": "^3.1.6"
    },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

2.create index.js file as shown below

var express = require('express'),
  bodyParser = require('body-parser'),
  path = require('path'),
  CryptoJS = require("crypto-js");
var app = express();
var consumerSecret = process.env.CANVAS_CONSUMER_SECRET;

app.use(express.static(path.join(__dirname, 'views')));
app.set('view engine', 'ejs');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ entended: true }));
app.get('/', function (req, res) {
  res.render('index');
});
app.post('/', function (req, res) {
  var signed_req = req.body.signed_request;
  var hashedContext = signed_req.split('.')[0];
  var context = signed_req.split('.')[1];
  var hash = CryptoJS.HmacSHA256(context, consumerSecret);
  var b64Hash = CryptoJS.enc.Base64.stringify(hash);
  if (hashedContext === b64Hash) {
    res.render('index', { req: req.body, res: res.data });
 } else {
    res.send("authentication failed");
  };
})
var port = process.env.PORT || 9000;
app.listen(port);
console.log('Listening on port ' + port);

3. create the index.ejs view inside the “views” folder.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
</head>

<body>
    <%- JSON.stringify(req) %>
</body>

</html>

4. Create Profile to host it to Heroku

web: node index.js

5. Run the following commands from the terminal.

heroku login 
git init
git add .
git commit -m "Commit"
heroku create 
git push heroku master

6. Set the environmental variable to Heroku app. we are going to set the environmental variable with consumer secret which we got in the connected app.

heroku config:set CANVAS_CONSUMER_SECRET=2781208380818866866

7. Now you will see Heroku app URL some think like https://arcane-woodland-93536.herokuapp.com/ .

8. Go to the connected app and edit and replace OAuth callback URL and canvas app URL with Heroku app URL.

9. Now if you can go to chatter page you see the canvas app is returning the response with SignedRequest.

What Signed Request Contains?

The signed request is a string of the following elements concatenated

  • The canvas app consumer secret encrypted with HMAC SHA–256 algorithm
  • A period (“.”)
  • The context and authorization token JSON encoded in Base64

When using a signed request, Salesforce delivers the user context and authentication information to your canvas app URL. To ensure that the signed request is valid, you must verify that the signed request was signed using your specific canvas app consumer secret as explained below.

  1. Receive the POST message that contains the initial signed request from Salesforce.
  2. Split the signed request in the first period. The result is two strings: the hashed Based64 context signed with the consumer secret and the Base64 encoded context itself.
  3. Use the HMAC SHA-256 algorithm to hash the Base64 encoded context and sign it using your consumer secret.
  4. Base64 encode the string created in point 3.
  5. Compare the Base64 encoded string with the hashed Base64 context signed with the consumer secret you received in point 2.

 

 

 

Salesforce Username and Password OAuth flow

Introduction

In this blog, I am going to explain how to setup and test OAuth username and password flow also called as  Resource Owner Password Credentials Flow.With this type of authorization, the credentials (and thus the password) are sent to the client and then to the authorization server along with the client and client secret. In this flow, the user’s credentials are used by the application to request an access token which you case use to access the data on behalf of the user.

Use Case

  • If you would like to access another salesforce instance data by using rest API
  • Resource Owner has a trust relationship with the client like if you are making calls from internal or secured on-premise application.

Create a Connected App

  1.  Navigate to App Setup > Create > Apps > Connected Apps > New
  2.  Enter  Connected App Name, API Name, Contact Email
  3.  In the API (Enable OAuth Settings) Section click the Enable OAuth Settings checkbox.
  4.  Enter an arbitrary Callback URL, such as https://login.salesforce.com/services/oauth2/callback.
  5. For Selected OAuth Scopes as full
  6.  Click the Save button.
  7. Navigate to Administration Setup > Manage Apps > Connected Apps
  8. Click on the link of the name of the Connected App created above.
  9. Click on the Edit button
  10. In the Permitted Users drop-down, select Admin approved users are pre-authorized and click OK on the popup.
  11. For the IP Restrictions dropdown, choose to Relax IP restrictions, or choose another option based up requirements.
  12. For the Require Users to Log in radio button, select Refresh Token is valid until revoked.
  13. Click Save
  14. On the Connected App page, in the Profiles section click on the Manage Profiles button and Add the profile

Requesting an Access Token

The client token request should be sent in an HTTP POST to the token endpoint with the following parameters.

  • grant_type— Value must be the password for this flow
  • client_id— Consumer key from the connected app definition
  • client_secret—  Consumer secret from the connected app definition.
  • username—User’s username
  • password—User’s password
  • format – Optional URLENCODED, JSON ,XML are supported

Here’s an example of the body of the out-of-band POST.

grant_type=password&client_id=3MVG9lKcPoNINVBIPJjdw1J9LLM82Hn
FVVX19KY1uA5mu0QqEWhqKpoW3svG3XHrXDiCQjK1mdgAvhCscA9GE&client_secret=
1955279925675241571&username=testuser%40salesforce.com&password=mypassword123456

Handling the Response

 After the request is verified, Salesforce sends a response to the client. The following parameters are in the body of the response
 Here is the sample response
{"access_token":"*****",
"instance_url":"https://fscttt-dev-ed.my.salesforce.com",
"id":"https://login.salesforce.com/id/00D6A000000vnuzUAA/0056A000000IpArQAK",
"token_type":"Bearer",
"issued_at":"1508299830083",
"signature":"btIf29MJg3CuzCG/fLm69Sn6CpL3AGt5ATmI0oJeJRM="}
  • access_token—Salesforce session ID that can be used with the web services API.
  • token_type—Value is Bearer for all responses that include an access token.
  • instance_url—A URL indicating the instance of the user’s Org. For example https://yourInstance.salesforce.com/.
  • id—Identity URL that can be used to both identify the user and query for more information about the user.
  • signature—Base64-encoded HMAC-SHA256 signature signed with the consumer’s private key containing the concatenated ID and issued_at. Use to verify that the identity URL hasn’t changed since the server sent it.
  • issued_at—When the signature was created.

Let’s test it

now the below code is used to send the OAuth details to salesforce endpoint URL which is going to return access token. you can use access token for subsequence calls.

 String endpoint='https://login.salesforce.com/services/oauth2/token';
        String username = '*****';
        String password = '**';
        String ClientId= '***';
        String ClientSecret = '**'; 
        Httprequest req = new HttpRequest();    
        req.setMethod('POST');    
        req.setHeader('Content-Type','application/x-www-form-urlencoded');
        req.setBody('grant_type=password' + 
                    '&client_id=' + ClientId + 
                    '&client_secret=' + ClientSecret + 
                    '&username=' + username +
                    '&password=' + password
                   );    
        req.setEndpoint(endpoint);         
        Http http = new Http();
        HttpResponse res= http.send(req);                
        system.debug('body:'+res.getBody());         
        JSONParser parser = JSON.createParser(res.getBody());
        String accessToken ;        
        while (parser.nextToken() != null) {
            if ((parser.getCurrentToken() == JSONToken.FIELD_NAME)) {
                String fieldName = parser.getText();
                parser.nextToken();
                System.debug('fieldName'+fieldName);
                if (fieldName == 'access_token') {
                    accessToken = parser.getText();
                } 
            }
        }

You can use the access token to authenticate sub sequence calls. you can use access token as similar to session id as shown below.

 HttpRequest request=new HttpRequest();
        request.setEndPoint('https://fscttt-dev-ed.my.salesforce.com/services/data/v40.0/sobjects/');
        request.setHeader('Authorization','Bearer '+accessToken);
        request.setHeader('Content-Type','application/json');
        request.setMethod('GET');
        Http hp=new Http();
        HttpResponse response=new HttpResponse();
        response=hp.send(request);
        System.debug('response'+response.getBody());

 

 

Salesforce Big Objects

Introduction

Salesforce Big objects let you store and manage massive amounts of data on
the Salesforce platform like storing the huge historical data.Big objects capture data for use within Force.com and are accessible via a standard set of APIs to clients and external systems. big objects are that they have been built to provide consistent performance whether there are 1 million records, 100 million, or even 1 billion records. This scale is what gives big objects their power and what defines the features that are provided.

Two Types of Big Objects 

Standard big objects — Objects defined by Salesforce and included in Salesforce products. FieldHistoryArchive is a standard big object that stores data as part of the Field Audit Trail product.
 Custom big objects— New objects that you create to store information unique to your Org.

How to Create a BigObject?

you need to define the big objects using metadata, not from the standard Salesforce UI. What we are going to do here is we are creating Sales Order Big Object to store the past historical orders. here is the metadata file you can download  Here. After downloading the zip file go to the workbench to create the big object. go to the workbench and deploy the above file as shown below

Once you can deploy the big objects  successfully, you can able to see the big objects that are created in Salesforce as shown below

Considerations

  • Big objects support only object and field permissions.
  • You must use the Metadata API to define a big object or add a field to a custom big object.Not from the UI
  •  SOQL relationship queries are based on a lookup field from a big object to a standard or custom object in the select field list
  • Big objects support custom Lightning and Visualforce components rather than standard UI elements home pages, detail pages, list views, and so on).
  •  You can create up to 100 big objects per org. The limits for big object fields are similar to the limits on custom objects and depend on your org’s license type.
  •  Big objects don’t support transactions including both big objects, standard object and custom objects.
  • At least you should have one indexed field
  • To support the scale of data in a big object, features like triggers, flows, processes, and Salesforce1, are not available.

Own Storage Space

Good to know that Salesforce big objects are come up with its own storage  space

Access Control

You can control  Read or create actions from the profile  

Insert Data into Big Object.

You can able to insert the data into the big object by using API  or  You can use a CSV file to load data into a custom big object. Here is the single record insert from the workbench. But even you can load data from CSV file.

Using Apex to insert the data 

You can create and update custom big object records in Apex using the insertImmediate method as shown below. Here in this code, I am inserting ten records for the sales order object. What we are going to do after inserting the Big Objects, we are going to create a visual force to show the custom Object and its Big object in the related list.

List<Sales_OrderBO__b> orderList = new List<Sales_OrderBO__b>();
for(integer i =1; i<10 ; i++){
    Sales_OrderBO__b  sOrder = new Sales_OrderBO__b();
    sOrder.Actual_Cost__c = 122.3 ; 
    sOrder.Saleschannel__c = 'Sales Channel' ; 
    sOrder.Created_From__c ='Internal' ; 
    sOrder.Expired_Date__c = System.now();
    sOrder.SalesorderBORel__c='a0B63000002qHXP' ; 
    orderList.add(sOrder);
}

Database.SaveResult[] sRes =  Database.insertImmediate(orderList);


Using Rest API

you can insert the data into the object by using rest api post method as shown below.

You can expose the data by using the visual force page or lightning component. Here are the apex class and page.

<apex:page standardController="Sales_Order__c" extensions="BigObjectData">
    <apex:form>
        
        <apex:pageBlock>
            <apex:pageBlockSection title="Big Objects" columns="1">
                <apex:pageBlockTable value="{!boData}" var="bo">
                    <apex:column headerValue="Actual Cost">
                        <apex:outputField value="{!bo.Actual_Cost__c}"/>
                    </apex:column>
                    <apex:column headerValue="Sales Channel">
                        <apex:outputField value="{!bo.Saleschannel__c}"/>
                    </apex:column>
                  
                      <apex:column headerValue="Created from ">
                        <apex:outputField value="{!bo.Created_From__c}"/>
                    </apex:column>
                  
                  
                </apex:pageBlockTable>
            </apex:pageBlockSection>
            
        </apex:pageBlock>
    </apex:form>
</apex:page>
public class BigObjectData {
    private  List<Sales_OrderBO__b> orderDetails; 
    public BigObjectData(ApexPages.StandardController con)
    {
        boData = fetchData();
    } 
    public List<Sales_OrderBO__b> fetchData()
    {
        return [Select Id ,Saleschannel__c ,Created_From__c,SalesorderBORel__c,Expired_Date__c,Actual_Cost__c
                from Sales_OrderBO__b ];
    }
    
    public List<Sales_OrderBO__b> boData{get;set;}
    
}