September 19, 2021

How to get current logged user information using Lightning Web Component LWC in Salesforce?

Sample Code:

HTML:  
<template>
    <lightning-card title="User Details">
        <lightning-record-view-form object-api-name="User" record-id={userId}>
            <lightning-layout multiple-rows>
                <lightning-layout-item size="6" padding="around-small">
                    <lightning-output-field field-name="Name"></lightning-output-field>
                </lightning-layout-item>
                <lightning-layout-item size="6" padding="around-small">
                    <lightning-output-field field-name="CompanyName"></lightning-output-field>
                </lightning-layout-item>
                <lightning-layout-item size="6" padding="around-small">
                    <lightning-output-field field-name="IsActive"></lightning-output-field>
                </lightning-layout-item>
                <lightning-layout-item size="6" padding="around-small">
                    <lightning-output-field field-name="Alias"></lightning-output-field>
                </lightning-layout-item>
                <lightning-layout-item size="6" padding="around-small">
                    <lightning-output-field field-name="Title"></lightning-output-field>
                </lightning-layout-item>
            </lightning-layout>
        </lightning-record-view-form>
    </lightning-card>        
</template>

JavaScript:  
import { LightningElement } from 'lwc';
import USER_ID from '@salesforce/user/Id';

export default class UserDetails extends LightningElement {

    userId = USER_ID;

}

meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>

Output:

September 17, 2021

How to pass values from lightning:recordEditForm to apex class in Salesforce?

Sample Code:
CMP:
<aura:component implements="force:appHostable" controller="SampleAuraController">
    <lightning:card>
        <lightning:recordEditForm aura:id="recordEditForm" objectApiName="Lead" onsubmit="{!c.doSubmit}">
            <lightning:messages />
            <lightning:inputField fieldName="FirstName" />
            <lightning:inputField fieldName="LastName" />
            <lightning:inputField fieldName="Email" />
            <lightning:inputField fieldName="Phone" />
            <lightning:inputField fieldName="Company" />
            <div id="recaptchaCheckbox"></div>
            <br/>
            <lightning:button aura:id="myButton" label="Create Lead" type="submit"/>
        </lightning:recordEditForm>
    </lightning:card>
</aura:component>
 
JS:
({
    
    doSubmit: function ( component, event, helper ) {
        
        console.log( 'Inside Submit' );
        event.preventDefault();
        let fields = event.getParam( 'fields' );
        fields = Object.assign( { 'sobjectType': 'Lead' }, fields );
        console.log( 'Fields are ' + JSON.stringify( fields ) );
        let action = component.get( "c.insertRecord" );
        action.setParams( {            
            record: fields
            
        } );        
        action.setCallback( this, function( response ) {
            
            document.dispatchEvent(new Event( "grecaptchaReset" ) );
            let myButton = component.find( "myButton" );
            myButton.set( 'v.disabled', true );            
            let state = response.getState();
            
            if ( state === "SUCCESS" ) {
                
                let result = response.getReturnValue();
                console.log( 'Result is ' + JSON.stringify( result ) );
                
                if ( result === 'Success' ) {
                    
                    let showToast = $A.get( "e.force:showToast" );
                    showToast.setParams( {
                        
                        title : 'Lead Creation',
                        message : 'Lead Submitted Sucessfully.' ,
                        type : 'success',
                        mode : 'dismissible'
                        
                    } );
                    showToast.fire();
                    
                }
                
            } else {
                
                let errors = response.getError();
                if ( errors ) {
                    
                    console.log( errors[ 0 ] );
                    
                }
                    
                let showToast = $A.get( "e.force:showToast" );
                showToast.setParams( {
                    
                    title : 'Lead Creation',
                    message : 'Lead is not Submitted due to some error.' ,
                    type : 'error',
                    mode : 'dismissible'
                    
                } );
                showToast.fire();
                
            }
            
        });        
        $A.enqueueAction(action);
        
    }
    
})
 
Apex Class:
public class SampleAuraController {
    
    @AuraEnabled
    public static String insertRecord( SObject record ) {   
        
        try {
            
            insert record;
            return 'Success';
            
        } Catch( Exception e ) {
            
            return 'Failed';
            
        }
        
    }
    
}
 
Output:
 

Lead Creation with Captcha in Salesforce Experience Cloud using Lightning Web Component LWC

1. Complete steps 1 through 10 from the following.
 
2. Use the same Apex Class from the above link.
 
Solution:
1. googleCaptcha is a re-usable LWC. This will fire events when the users click I'm not a robot and when the Captcha expires.
2. leadCreationForm is the parent LWC. This will handle the events from the googleCaptcha child component. When the Captcha verification is successful, the Create Lead Button will be enabled. If the Captcha verification fails or expires, the Create Lead Button will be disabled.
 

Sample Code:

googleCaptcha LWC:
HTML:
<template>
    <div class="recaptchaCheckbox"></div>
</template>
 
JS:
import { LightningElement } from 'lwc';

export default class GoogleCaptcha extends LightningElement {

    connectedCallback() {

        console.log( 'Inside Google Captcha Connected Callback' );
        document.addEventListener( "grecaptchaVerified", ( e ) => {

            console.log( 'Captcha Response from Verification is ' + e.detail.response );
            let detailPayload = { value : false, response : e.detail.response };
            this.dispatchEvent( new CustomEvent( 'captcha', { detail : detailPayload } ) );

        });

        document.addEventListener( "grecaptchaExpired", () => {

            console.log( 'Listener Expired' );
            this.dispatchEvent( new CustomEvent( 'captcha', { detail : { value : true } } ) );

        } );
        
    }

    renderedCallback() {

        console.log( 'Inside Google Captcha Rendered Callback' );
        let divElement = this.template.querySelector( 'div.recaptchaCheckbox' );
        console.log( 'Div Element is ' + JSON.stringify( divElement ) );
        let payload = { element: divElement };
        document.dispatchEvent(new CustomEvent( "grecaptchaRender",  { "detail": payload } ) );
        
    }

}

 
JS-meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>
</LightningComponentBundle>

 
leadCreationForm LWC:
HTML:
<template>
    <lightning-card title="Lead Creation">
        <lightning-record-edit-form object-api-name="Lead" onsubmit={handleSubmit}>
            <lightning-messages></lightning-messages>
            <lightning-input-field field-name="FirstName"></lightning-input-field>
            <lightning-input-field field-name="LastName"></lightning-input-field>
            <lightning-input-field field-name="Email"></lightning-input-field>
            <lightning-input-field field-name="Phone"></lightning-input-field>
            <lightning-input-field field-name="Company"></lightning-input-field>
            <c-google-captcha oncaptcha={handleUpdate}></c-google-captcha><br/>
            <lightning-button type="submit" label="Create Lead" variant="brand" disabled={verifiedBool}></lightning-button>        
        </lightning-record-edit-form>
    </lightning-card>
</template>

 
JS:
import { LightningElement } from 'lwc';
import insertRecord from '@salesforce/apex/RecaptchaController.insertRecord';
import {ShowToastEvent} from 'lightning/platformShowToastEvent';

export default class LeadCreationForm extends LightningElement {

    verifiedBool = true;
    captchaResponse
    
    handleUpdate( event ) {

        console.log( 'Updated value is ' + JSON.stringify( event.detail ) );
        this.verifiedBool = event.detail.value;

        if ( event.detail.response ) {

            console.log( 'Response is ' + event.detail.response );
            this.captchaResponse = event.detail.response;

        }

    }

    handleSubmit( event ) {

        let fields = event.detail.fields;
        fields = Object.assign( { 'sobjectType': 'Lead' }, fields );
        console.log( 'Fields are ' + JSON.stringify( fields ) );
        console.log( 'Captcha Response is ' + JSON.stringify( this.captchaResponse ) );
        event.preventDefault();

        insertRecord( { record : fields, recaptchaResponse : this.captchaResponse } )
        .then( result => {

            this.isLoaded = false;
            window.console.log('result ===> '+result);
            this.strMessage = result;
            this.dispatchEvent(
                new ShowToastEvent( {
                    title: 'Lead Submission Result',
                    message: 'Lead ' + ( result.includes( 'Success' ) ? '' : 'not ' ) + 'Submitted Successfully',
                    variant: result.includes( 'Success' ) ? 'success' : 'error',
                    mode: 'sticky'
                } )
            );

        })
        .catch( error => {

            this.dispatchEvent(
                new ShowToastEvent( {
                    title: 'Error',
                    message: JSON.stringify( error ),
                    variant: 'error',
                    mode: 'sticky'
                } )
            );     

        } )

    }

}

 
JS-meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Lead Creation Form</masterLabel>
    <targets>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>
</LightningComponentBundle>

 
Output:
 

September 16, 2021

Hmm, looks like there's no connection. Try reconnecting or come back later. Exception in Salesforce

If you get "Hmm, looks like there's no connection. Try reconnecting or come back later." exception in Salesforce, it might be due to the storage limit.

Check your data storage.

Use the following link to find the Data Storage in your org.

"The sharing calculation you requested can't be processed right now, because it interferes with another operation already in progress. Please try again later." Exception in Salesforce

Please check the following document for reference.
https://help.salesforce.com/s/articleView?id=sf.deploy_special_behavior.htm&type=5

1. Simultaneously inserting a custom object, updating the sharingModel field for an object, and adding a new owner-based sharing rule isn’t supported.
2. Simultaneously updating the sharingModel field for an object and adding a new owner-based sharing rule isn’t supported.
3. Using API version 29.0, you can’t change the sharingModel of an object using Metadata API. Manually change the target org through the user interface.
4. Starting with API version 30.0, you can change the sharingModel of an object for internal users using Metadata API and the user interface.
5. Simultaneously updating the sharingModel field for an object and adding a new owner-based sharing rule isn’t supported in Metadata API. You can add an owner-based sharing rule when the org-wide default is public, and then update the sharingModel, which would result in a single sharing recalculation. You can deploy a criteria-based or guest user sharing rule and changes to the sharingModel field together using the Metadata API.

September 15, 2021

Lead Creation with Captcha in Salesforce Experience Cloud

 
Note:
I have used Google reCAPTCHA v2 Checkbox in the below implementation. 

 
2. Click on the "V3 Admin console" button.
 
3. Click the ‘+’ create icon.
 
4. Enter a Label and select reCAPTCHA v2 Checkbox. 
 
5. Add your Experience Cloud domain. 
 
 
6. Accept the terms of service and click the Submit button.

7. Get the Site Key and Secret Key.

8. Create Remote Site Settings in Salesforce for https://www.google.com.

9. In the Experience Site Builder, update and add the CSPs.
 
 
10. Add the following code in the Head Markup.

<!--reCaptcha v2 Checkbox-->
<script>
    var grecaptchaReady = false;
    var onloadCallback = function(){ grecaptchaReady = true; };
    var verifyCallback = function(token) {
        document.dispatchEvent(new CustomEvent('grecaptchaVerified', {'detail': {response: token}}));
    };
    var expireCallback = function() {
        document.dispatchEvent(new Event('grecaptchaExpired'));
    };
    var errorCallback = function() {
        document.dispatchEvent(new Event('grecaptchaError'));
    };
    document.addEventListener('grecaptchaRender', function(e) {
        onloadCallback = function() {
            grecaptchaReady = true;
            grecaptcha.render(e.detail.element, {
                'sitekey': '<Add your Site Key>',
                'callback': verifyCallback,
                'expired-callback': expireCallback,
                'error-callback': errorCallback
            });
        };
        if (grecaptchaReady) {
            onloadCallback();
        }
    });
    document.addEventListener('grecaptchaReset', function() {
        grecaptcha.reset();
    });
</script>
<script src='https://www.google.com/recaptcha/api.js?render=explicit&onload=onloadCallback' async defer></script>
 
Sample Code:

Apex:

public with sharing class RecaptchaController {   
    
    private static String recaptchaSecretKey = '<Add Your Secret Key Here>';
        
    @AuraEnabled
    public static String insertRecord( SObject record, String recaptchaResponse ) {
        
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://www.google.com/recaptcha/api/siteverify' );
        request.setMethod( 'POST');
        request.setBody( 'secret=' + recaptchaSecretKey + '&response=' + recaptchaResponse );
        HttpResponse response = http.send( request );

        if ( response.getStatusCode() == 200 ) {
            
            System.debug( 'Response Body is ' + response.getBody() );
            Map < String, Object > result = ( Map< String, Object > ) JSON.deserializeUntyped( response.getBody() );

            if ( result.containsKey( 'success' ) && result.get( 'success' ) == true) {

                if ( result.containsKey( 'score' ) ) {
                    
                    String action = ( String )result.get( 'action' );
                    Decimal threshold = .4;
                    Decimal score = ( Decimal )result.get( 'score' );

                    if ( score > threshold ) {
                       
                        insert record;
                        return 'Success - v3';

                    }

                } else {
                    
                    insert record;
                    return 'Success - v2';

                }
                
            } else {

                return 'Invalid Verification';

            }
            
        }
        
        return 'Invalid Verification Request';

    }

}

Aura Component:

<aura:component controller="RecaptchaController" implements="forceCommunity:availableForAllPageTypes" access="global" >
    <aura:handler name="init" value="{!this}" action="{!c.onInit}" />
    <aura:handler name="render" value="{!this}" action="{!c.onRender}" />
    <aura:attribute name="recaptchaResponse" type="String" />
    <lightning:recordEditForm aura:id="recordEditForm" objectApiName="Lead" onsubmit="{!c.doSubmit}">
        <lightning:messages />
        <lightning:inputField fieldName="FirstName" />
        <lightning:inputField fieldName="LastName" />
        <lightning:inputField fieldName="Email" />
        <lightning:inputField fieldName="Phone" />
        <lightning:inputField fieldName="Company" />
        <div id="recaptchaCheckbox"></div>
        <br/>
        <lightning:button aura:id="myButton" label="Create Lead" type="submit" disabled="true" />
    </lightning:recordEditForm>
</aura:component>

Aura JavaScript Controller:

({
    
    onInit: function (component, event, helper){
        document.addEventListener("grecaptchaVerified", function(e) {
            component.set('v.recaptchaResponse', e.detail.response);
            let myButton = component.find("myButton");
            myButton.set('v.disabled', false);
        });
        
        document.addEventListener("grecaptchaExpired", function() {
            
            let myButton = component.find( "myButton" );
            myButton.set( 'v.disabled', true );
            
        });
    },
    
    onRender: function ( component, event, helper ) {
        
        document.dispatchEvent(new CustomEvent( "grecaptchaRender", { "detail" : { element: 'recaptchaCheckbox'} } ) );
        
    },
    
    doSubmit: function ( component, event, helper ) {
        
        console.log( 'Inside Submit' );
        event.preventDefault();
        let fields = event.getParam( 'fields' );
        fields = Object.assign( { 'sobjectType': 'Lead' }, fields );
        console.log( 'Fields are ' + JSON.stringify( fields ) );
        let action = component.get( "c.insertRecord" );
        action.setParams( {
            
            record: fields,
            recaptchaResponse: component.get( 'v.recaptchaResponse')
            
        } );        
        action.setCallback( this, function( response ) {
            document.dispatchEvent(new Event( "grecaptchaReset" ) );
            let myButton = component.find( "myButton" );
            myButton.set( 'v.disabled', true );            
            let state = response.getState();
            
            if ( state === "SUCCESS" ) {
                
                let result = response.getReturnValue();
                console.log( 'Result is ' + JSON.stringify( result ) );
                
                if ( result.includes( 'Success' ) ) {
                    
                    let showToast = $A.get( "e.force:showToast" );
                    showToast.setParams( {
                        
                        title : 'Lead Creation',
                        message : 'Lead Submitted Sucessfully.' ,
                        type : 'success',
                        mode : 'dismissible'
                        
                    } );
                    showToast.fire();
                    
                }
                
            } else {
                
                let errors = response.getError();
                if ( errors ) {
                    
                    console.log( errors[ 0 ] );
                    
                }
                    
                let showToast = $A.get( "e.force:showToast" );
                showToast.setParams( {
                    
                    title : 'Lead Creation',
                    message : 'Lead is not Submitted due to some error.' ,
                    type : 'error',
                    mode : 'dismissible'
                    
                } );
                showToast.fire();
                
            }
            
        });
        
        $A.enqueueAction(action);
        
    }
    
})

Output:
 

September 10, 2021

How to Query Salesforce Einstein Bot Event Logs?

1. Get the session id.

2. Use Data Loader.


Sample SOQL:
SELECT ConversationDefinitionId, CreatedById, CreatedDate, DialogDefinitionId, EventDateTime, EventLabel, EventTarget, EventTargetType, EventTimestamp, EventUuid, Id, IsDeleted, LastModifiedById, LastModifiedDate, LogLevel, LogType, ParentId, PreviousEventLogId, PreviousEventLogUuid, StepType, SystemModstamp FROM ConversationDefinitionEventLog WHERE ParentId = '5CB5f000000sZ05GAE'
 
Workbench:
 
 

September 9, 2021

September 8, 2021

Salesforce Inbound REST API with Param

Sample Code:
@RestResource(urlMapping='/AccountRESTAPI/*')
global class AccountRESTAPIController {
    
    @HttpGet
    global static Account fetchAccount() {
        
        Account objAccount = new Account();   
        RestRequest req = RestContext.request;
        String accNum = req.params.get( 'accNum' );                                                
        return [ SELECT Id, Name, AccountNumber, Industry FROM Account WHERE AccountNumber =: accNum ];
        
    } 

}

Connected App:
 
 
Output:
Workbench:
 

 
Postman Client:
 
 
Test Class:
@isTest
private class AccountRESTAPIControllerTest {
    
    testMethod static void testAccountAPI() {
        
        Account objAcc = new Account( Name = 'Testing12345', AccountNumber = '12345' );
        insert objAcc;
        RestRequest req = new RestRequest(); 
        RestResponse res = new RestResponse();             
        req.requestURI = '/services/apexrest/AccountRESTAPI';
        req.addParameter( 'accNum', '12345' );
        req.httpMethod = 'GET';
        RestContext.request = req;
        RestContext.response = res;
        Account objFetchedAcc = AccountRESTAPIController.fetchAccount();
        system.assertEquals( '12345', objFetchedAcc.AccountNumber );
        
    }
}

September 5, 2021

Error: User does not have access to this service provider Salesforce

If you face "Error: User does not have access to this service provider" exception, then the Profile doesn't have access to the Connected App create as part of the Single Sign On.
 

In the SAML tracer, you will find "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed".

You can use SAML Tracer extension and check SAMLStatusMessage and SAMLStatusCode values.

September 3, 2021

Record Type assigned via Permission Set not working in Salesforce

Default Record Types: 
A user’s default record type is specified in the user’s profile. Users can view their default record type and edit record type selection in personal settings. You can’t specify a default record type in permission sets.
 
Master Record Types:
In Profiles: You can assign the master record type in profiles, but you can’t include custom record types in the profile.
In Permission Sets:You can assign only custom record types in permission sets, not master record types.
 
 
Record Types:


Permission Set: 


Setup 1:
 
Profile:
 
Output:
Execute the below code in Anonymous Window.
insert new Contact( LastName = 'Testing' );


Record Type is Master since the Default as per profile is Master record type.

Setup 2:
Profile:


Output:
Execute the below code in Anonymous Window.
insert new Contact( LastName = 'Testing' );
 

Custom Fields for Task and Event Objects Salesforce

Task and Event objects are part of Activity. Activity is available in Object Manager to add fields for Task and Event objects. You cannot create fields directly in Task and Event object. You have to create in Activity.
 
When you navigate to Task or Event fields in Setup, you  may notice that there is not a New button to create new fields for that object. Instead, custom fields for Tasks and Events are created at the Activity level.
 
Since Events and Tasks are both part of the Activities object, you will need to create your custom fields at the Activities level. Check the following article for detailed steps and instructions.
 
 

Accessing Custom Permission and Standard Permission in Salesforce LWC

Custom Permission:
 

HTML:
<template>
    <lightning-card>
        <lightning-input
            type="toggle"
            label="Test Permission"
            checked={isTestPermEnabled}
            disabled>
        </lightning-input>
        <lightning-input
            type="toggle"
            label="View All Data Permission"
            checked={isViewAllDataEnabled}
            disabled>
        </lightning-input>
    </lightning-card>
</template>
 
JavaScript:
import { LightningElement } from 'lwc';
import hasTestPermission from '@salesforce/customPermission/Test';
import hasViewAllDataPermission from '@salesforce/userPermission/ViewAllData';

export default class PermissionCheck extends LightningElement {

    get isTestPermEnabled() {

        console.log( 'hasTestPermission is ' + hasTestPermission );
        return hasTestPermission;

    }

    get isViewAllDataEnabled() {

        console.log( 'hasViewAllDataPermission is ' + hasViewAllDataPermission );
        return hasViewAllDataPermission;

    }

}

Output:
 

September 2, 2021

How to Salesforce supported certificates?

To review a current list of supported certificates you can append /cacerts.jsp to any instance URL (Only Winter '19 Instances will work with this endpoint) - https://INSTANCE.salesforce.com/cacerts.jsp (replace 'INSTANCE' with any Winter'19 upgraded instance, e.g. https://cs32.salesforce.com/cacerts.jsp)

Reference Article - https://help.salesforce.com/s/articleView?id=000326722&type=1
 
Note:
Salesforce trusts only root certificate authority (CA) certificates, with few historical exceptions. Salesforce's certificate trust policy is to require server and client certificate chains to include all intermediate certificates that exist between the server or client certificate and the chain's root certificate. Salesforce will not honor requests to add intermediate certificates to its trust list. Salesforce trusts many generally trusted root certificates, but not all.

September 1, 2021

You've exceeded the limit of 100 jobs in the flex queue for org - Salesforce Exception

Salesforce allows up to five batch jobs to run at a time, i.e. to be in a “Processing” status. When this limit is hit, jobs that are submitted are held in the Apex Flex Queue until resources become available to process them. Jobs in the flex queue sit in a “Holding” status until they are released for processing. The flex queue will only hold up to 100 jobs at a time. Once this limit is reached, you will see this error when a batch apex job is submitted.
 
Apex Governor Limits
Maximum number of batch Apex jobs in the Apex flex queue that are in Holding status - 100
https://developer.salesforce.com/docs/atlas.en-us.232.0.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_apexgov.htm

Up to 100 batch jobs can be in the holding status. When system resources become available, the system picks up jobs from the Apex flex queue and moves them to the batch job queue. The status of these moved jobs changes from Holding to Queued. Queued jobs get executed when the system is ready to process new jobs and they will then go to InProgress status.
https://help.salesforce.com/s/articleView?id=000334068&type=1

August 28, 2021

Quick Actions not showing even after adding it to the page layout in Salesforce

1. If it is a record creation specific Quick Action, 
a. Make sure the user have edit access to the record.
b. Make sure the user's profile have Read and Edit Field Level Permission to Parent field. 
c. Check the whether the user's profile have access to the Record Type selected.
 
2.  If Quick Actions on Cases and Work Orders are not visible, then kindly check https://www.infallibletechie.com/2018/12/quick-actions-on-cases-and-work-orders.html.

3. Check the page layout assignment and confirm whether it is added to the assigned page layout.

4. If Dynamic Actions are use, then edit the Lightning Record Page, check the Highlight Panel Component and confirm whether the quick action is hidden with condition.

August 26, 2021

Salesforce User Login IP

 1. Create a Report with Users as the Report Type.


2. Add Source IP Address to the Report.


Dynamically Change Language for Salesforce Embedded Service Chat

Apex Class:
public class EmbeddedChatController {

    public String selectedOption { get; set; }
    
    public EmbeddedChatController() {
    
        selectedOption = ApexPages.currentPage().getParameters().containsKey( 'language' ) ? ApexPages.currentPage().getParameters().get( 'language' ) : 'da';
        
    }
    
    public PageReference changeLanguage() {
    
        system.debug( 'Changed Language is ' + selectedOption );
        PageReference pg = new PageReference( '/apex/EmbeddedChat' );
        pg.getParameters().put( 'language', selectedOption );
        pg.setRedirect(true);
        return pg;
    
    }

}

Visualforce Page:


Output:


August 25, 2021

Custom message Salesforce Chat Window

Sample code:
<apex:page showHeader="false">
    <style>
        #liveAgentClientChat.liveAgentStateWaiting {
            font-weight: bold;
            font-size: 30px;
        }
        body { overflow: hidden; width: 100%; height: 100%; padding: 0; margin: 0 }
        #waitingMessage { height: 100%; width: 100%; vertical-align: middle; text-align: center;
        display: none; }
        #liveAgentClientChat.liveAgentStateWaiting #waitingMessage { display: table; }
        #liveAgentSaveButton, #liveAgentEndButton { z-index: 2; }
        .liveAgentChatInput {
            height: 25px;
            border-width: 1px;
            border-style: solid;
            border-color: #000;
            padding: 2px 0 2px 4px;
            background: #fff;
            display: block;
            width: 99%;
        }
        .liveAgentSendButton {
            display: block;
            width: 60px;
            height: 31px;
            padding: 0 0 3px;
            position: absolute;
            top: 0;
            right: -67px;
        }
        #liveAgentChatLog {
            width: auto;
            height: auto;
            top: 0px;
            position: absolute;
            overflow-y: auto;
            left: 0;
            right: 0;
            bottom: 0;
        }
    </style>
    <div style="top: 0; left: 0; right: 0; bottom: 0; position: absolute;">
        <liveAgent:clientChat>
            <liveAgent:clientChatSaveButton />
            <liveAgent:clientChatEndButton />
            <div style="top: 25px; left: 5px; right: 5px; bottom: 5px; position: absolute; z-index:0;">
                <liveAgent:clientChatAlertMessage />
                <liveAgent:clientChatStatusMessage />
                <table id="waitingMessage" cellpadding="0" cellspacing="0">
                    <tr>
                        <td>Please wait!!! We are connecting you to an available agent to address your issue.</td>
                    </tr>
                </table>
                <div style="top: 0; right: 0; bottom: 41px; left: 0; padding: 0; position: absolute;word-wrap: break-word; z-index: 0;">
                    <liveAgent:clientChatLog />
                </div>
                <div style="position: absolute; height: auto; right: 0; bottom: 0; left: 0; margin-right:67px;">
                    <liveagent:clientChatInput /><liveAgent:clientChatSendButton />
                </div>
            </div>
        </liveAgent:clientChat>
    </div>
</apex:page>
 
Chat button Configuration:
 

Output:
 

How to invoke Embedded Service Chat on click of a button in Salesforce?

embedded_svc.bootstrapEmbeddedService() can be used. Check the following article for additional information.  
 
Sample Code:
<apex:commandButton value="Start Chat" onclick="embedded_svc.bootstrapEmbeddedService();" reRender="chatDetails"/> 

Salesforce Chat Transfer

Agents can click Transfer icon to transfer the Chat.
 


August 24, 2021

How to debug JavaScript Custom Button in Salesforce?

Sample JavaScript Custom Button 
 

 Button on the Record Page:
 

Output: