Author Archives: Biswajeet

About Biswajeet

Biswajeet is my Name, Success is my Aim and Challenge is my Game. Risk & Riding is my Passion and Hard Work is my Occupation. Love is my Friend, Perfection is my Habit and Smartness is my Style. Smiling is my Hobby, Politeness is my Policy and Confidence is my Power.

Google reCAPTCHA in Lightning Component

Configure the Google reCAPTCHA and get the Site Key & Secret Key:

  • Login to Google reCAPTCHA
  • Register a new site
  • Add Label e.g. Salesforce.com
  • Select reCAPTCHA type “reCAPTCHA v2”
  • Select “I’m not a robot” Checkbox option.
  • Add a domain e.g. yourorgurl.com
  • Accept the reCAPTCHA Terms of Service
  • Submit and get the Site Key & Secret Key

Create a VF page to configure Google reCAPTCHA in it and we have to embed the VF page in our lightning component.

GoogleReCaptcha VF Page:

<apex:page sidebar="false" showHeader="false" standardStylesheets="false" cache="false" id="pg" applyBodyTag="false" applyHtmlTag="false">
    <html>
        <head>
            <script src='https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit' async='' defer=''/>
            <script type='text/javascript'>
                var verifyCallback = function(response){
                    parent.postMessage('VALID', 'https://biswajeet-dev-ed.lightning.force.com/');
                };
            
            var onloadCallback = function() {
                grecaptcha.render('reCAPTCHAWidget', {
                    'sitekey' : 'ADD_YOUR_GOOGLE_RECAPTCHA_SITE_KEY', 
                    'callback' : verifyCallback
                });
            };
            </script>
        </head>
        <body>
            <div id="reCAPTCHAWidget"></div>
        </body>
    </html>
</apex:page>

Embed the above created VF page in our lightning component to display Google reCAPTCHA.
Lightning Component:

<aura:component implements="flexipage:availableForAllPageTypes,force:appHostable,forceCommunity:availableForAllPageTypes" access="global">
    
    <!--Attributes-->
    <aura:attribute name="isDisable" type="Boolean" default="true"/>
    <aura:attribute name="firstName" type="String"/>
    <aura:attribute name="lastName" type="String"/>
    
     <!--Handlers-->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <div class="slds-m-around--xxx-large slds-align_absolute-center" style="width:70%;">
        <fieldset class="slds-box slds-align_absolute-center" >
            
            <legend id="newform" class="slds-text-heading--small login-heading">
                Registration
            </legend>
            
            <lightning:input name="fName" label="First Name" value="{!v.firstName}"/>
            <br/>
            <lightning:input name="lName" label="Last Name"  value="{!v.lastName}"/>
            <br/>
            <iframe src="/apex/GoogleReCaptcha" scrolling="no" frameborder="0" width="100%" allowtransparency="true"></iframe>
            
            <div class="slds-align_absolute-center">
                <lightning:button onclick="{!c.handleSubmit}" disabled="{!v.isDisable}" variant="brand" name="btnSubmit" label="Submit"   />
            </div>
        </fieldset>
    </div>
</aura:component>

Lightning JS Controller:

({
    doInit : function(component, event, helper) {
        var vfURL = 'https://biswajeet-dev-ed.lightning.force.com/';
        window.addEventListener('message', function(event){
            if(event.origin !== vfURL){
                return;
            }
            if(event.data === 'VALID'){
                component.set('v.isDisable', false); 
            }
        }, false);
    },
    
    handleSubmit : function(component, event, helper) {
        
    },
})

Salesforce Web to Lead form with Google reCAPTCHA

Configure the Google reCAPTCHA and get the Site Key & Secret Key:

  • Login to Google reCAPTCHA
  • Register a new site
  • Add Label e.g. Salesforce.com
  • Select reCAPTCHA type “reCAPTCHA v2”
  • Select “I’m not a robot” Checkbox option.
  • Add a domain e.g. yourorgurl.com
  • Accept the reCAPTCHA Terms of Service
  • Submit and get the Site Key & Secret Key

Remote Site Settings:
Create Remote Site settings for google.com site URL

Note: Make sure your org “Show Quick Create” is enabled.
Go to Setup | User Interface | Select the checkbox of “Show Quick Create” | Click on Save button at the bottom.

Setup Web-to-Lead:

1. Go to Setup | Leads | Web-to-lead | Select the checkbox of “Web-to-Lead Enabled” | Select the checkbox of “Require reCAPTCHA Verification” | Select Default Lead Creator | Click on Save button at the bottom.

2. Click on “Create Web-to-Lead Form” button | Select Lead fields you want to add on the form | Give the redirect URL | Click the lookup icon to select “reCAPTCHA API Key Pair”.

3. In the popup window, Click on the New Button | Enter the “API Key Pair Nickname” of your choice | Enter Google reCAPTCHA Site Key | Enter Google reCAPTCHA Secret Key | Click on Save.

4. Click on Generate button, and you will get the HTML code ready to put on your website.

Web-to-Lead Form:

How to use jQuery in Salesforce Lightning Aura Component?

Step 1: Upload the jQuery library as a static resource in your Org.

Step 2: Use ltng:require to load static resource JavaScript libraries on your component.

<aura:component>
    <ltng:require scripts="{!$Resource.YourStaticResourceName + '/jQueryFileName.js'}" />
</aura:component>

Step 3: To utilize the library include the afterScriptsLoaded event and add the method in your aura component JS controller.

Aura Component :

<aura:component>
    <ltng:require scripts="{!$Resource.YourStaticResourceName + '/jQueryFileName.js'}" afterScriptsLoaded="{!c.handleAfterScriptsLoaded}" />
</aura:component>

JS Controller:

({
    handleAfterScriptsLoaded : function(component, event, helper) {
        jQuery("document").ready(function(){
            console.log('jQuery Loaded');
        });
    }
})

Generic File Related List Lightning Component

Apex Class:

/*
@Author : Biswajeet Samal
@CreatedDate : 08th Aug 2020
@Description : Related Files Controller
*/
public class RelatedFilesController {
    
    @AuraEnabled
    public static List<ContentDocument> getRelatedDocs(Id recordId){
        List<ContentDocument> cdList = new List<ContentDocument>();
        List<ContentDocumentLink> cdlList = [SELECT ContentDocumentId FROM ContentDocumentLink
                                             WHERE LinkedEntityId = :recordId];
        Set<Id> cdIds = new Set<Id>();
        for (ContentDocumentLink cdl : cdlList) {
            cdIds.add(cdl.ContentDocumentId); 
        }        
        cdList = [SELECT Id, Title, FileType, OwnerId, Owner.Name, CreatedDate,
                  CreatedById, CreatedBy.Name, ContentSize
                  FROM ContentDocument WHERE Id IN :cdIds];
        return cdList;
    }
    
    @AuraEnabled
    public static string getDocURL(Id docId){
        ContentVersion cv = [SELECT Id FROM ContentVersion WHERE ContentDocumentId = :docId AND IsLatest = true];
        String cvDownloadURL = URL.getSalesforceBaseUrl().toExternalForm() + '/sfc/servlet.shepherd/version/download/' + cv.Id;
        return cvDownloadURL;
    }
    
    @AuraEnabled
    public static void deleteDoc(Id docId){
        ContentDocument conDoc = new ContentDocument(Id = docId);
        delete conDoc;
    }
}

Lightning Component:

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" controller="RelatedFilesController">
    
    <!--Attributes-->
    <aura:attribute name="cdList" type="List"/>
    
    <!--Handlers--> 
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <!--Component Start-->
    <table class="slds-table slds-table_cell-buffer slds-table_bordered">
        <thead>
            <tr class="slds-line-height_reset">
                <th class="slds-text-title_caps" scope="col">
                    <div class="slds-truncate" title="Title">Title</div>
                </th>
                <th class="slds-text-title_caps" scope="col">
                    <div class="slds-truncate" title="File Type">File Type</div>
                </th>
                <th class="slds-text-title_caps" scope="col">
                    <div class="slds-truncate" title="Created By">Created By</div>
                </th>
                <th class="slds-text-title_caps" scope="col">
                    <div class="slds-truncate" title="Created Date">Created Date</div>
                </th>
                <th class="slds-text-title_caps" scope="col">
                    
                </th>
            </tr>
        </thead>
        <tbody>
            <aura:iteration items="{!v.cdList}" var="cd">
                <tr>
                    <th scope="row">
                        <div class="slds-truncate" title="{!cd.Title}">
                            <a onclick="{!c.handleSelectedDocPreview}" data-Id="{!cd.Id}">{!cd.Title}</a>
                        </div>
                    </th>
                    <th scope="row">
                        <div class="slds-truncate" title="{!cd.FileType}">
                            {!cd.FileType}
                        </div>
                    </th>
                    <th scope="row">
                        <div class="slds-truncate" title="{!cd.CreatedBy.Name}">
                            <a onclick="{!c.handleRedirectToUserRecord}" data-Id="{!cd.CreatedById}">{!cd.CreatedBy.Name}</a>
                        </div>
                    </th>
                    <th scope="row">
                        <div class="slds-truncate" title="{!cd.CreatedDate}">
                            <lightning:formattedDateTime value="{!cd.CreatedDate}"/>
                        </div>
                    </th>
                    <th scope="row">
                        <lightning:buttonMenu alternativeText="Show menu" menuAlignment="auto" onselect="{!c.handleSelectedAction}" value="{!cd.Id}">
                            <lightning:menuItem value="Download" label="Download" iconName="utility:download" title="Download" />
                            <lightning:menuItem value="Delete" label="Delete" iconName="utility:delete" title="Delete"/>
                        </lightning:buttonMenu>
                    </th>
                </tr>  
            </aura:iteration>
        </tbody>
    </table>
    <!--Component End-->
</aura:component>

Lightning Component JS Controller:

({
    //Get Related Docs
    doInit : function(component, event, helper) {
        helper.getRelatedDocuments(component, event); 
    },
    
    //Redirect To User Record
    handleRedirectToUserRecord: function (component, event, helper) {
        var recordId = event.currentTarget.getAttribute("data-Id")
        var navEvt = $A.get("e.force:navigateToSObject");
        navEvt.setParams({
            "recordId": recordId,
            "slideDevName": "Detail"
        });
        navEvt.fire();
    },
    
    //Preview Selected File
    handleSelectedDocPreview : function(component,event,helper){ 
        $A.get('e.lightning:openFiles').fire({
            recordIds: [event.currentTarget.getAttribute("data-Id")]
        });
    },
    
    //Handle Selected Action
    handleSelectedAction: function(component, event, helper) {
        var docId = event.getSource().get("v.value");
        var selectedMenuValue = event.detail.menuItem.get("v.value");
        switch(selectedMenuValue) {
            case "Delete":
                helper.deleteDocument(component, event, docId);
                break;
            case "Download":
                helper.downloadDocument(component, event, docId);
                break;
        }
    }
})

Lightning Component JS Helper:

({
    getRelatedDocuments : function(component, event) {
        var action = component.get("c.getRelatedDocs");
        action.setParams({
            recordId : component.get("v.recordId")
        });
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(state === "SUCCESS"){
                component.set('v.cdList', response.getReturnValue());
            }else if(state === "INCOMPLETE") {
                console.log("INCOMPLETE");
            }else if(state === "ERROR"){
                var errors = response.getError();
                if(errors){
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " +  errors[0].message);
                    }
                }else{
                    console.log("Unknown error");
                }
            }
        });
        $A.enqueueAction(action);  
    },
    
    deleteDocument : function(component, event, docId) {
        var action = component.get("c.deleteDoc");
        action.setParams({
            docId : docId
        });
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(state === "SUCCESS"){
                this.getRelatedDocuments(component, event);
            }else if(state === "INCOMPLETE") {
                console.log("INCOMPLETE");
            }else if(state === "ERROR"){
                var errors = response.getError();
                if(errors){
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " +  errors[0].message);
                    }
                }else{
                    console.log("Unknown error");
                }
            }
        });
        $A.enqueueAction(action);  
    },
    
    downloadDocument : function(component, event, docId) {
        var action = component.get("c.getDocURL");
        action.setParams({
            docId : docId
        });
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(state === "SUCCESS"){
                var urlEvent = $A.get("e.force:navigateToURL");
                urlEvent.setParams({
                    "url": response.getReturnValue()
                });
                urlEvent.fire();
            }else if(state === "INCOMPLETE") {
                console.log("INCOMPLETE");
            }else if(state === "ERROR"){
                var errors = response.getError();
                if(errors){
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " +  errors[0].message);
                    }
                }else{
                    console.log("Unknown error");
                }
            }
        });
        $A.enqueueAction(action);  
    }
})

Google reCAPTCHA on Visualforce Page

Step 1: Configure the Google reCAPTCHA and get the Site Key & Secret Key

  • Login to Google reCAPTCHA
  • Register a new site
  • Add Label e.g. Salesforce.com
  • Select reCAPTCHA type “reCAPTCHA v2”
  • Select “I’m not a robot” Checkbox option.
  • Add a domain e.g. yourorgurl.com
  • Accept the reCAPTCHA Terms of Service
  • Submit and get the Site Key & Secret Key

Step 2: Add Google as a Remote Site in Salesforce

Step 3: Create apex controller

/*
@Author : Biswajeet Samal
@CreatedDate : 30th JUL 2020
@Description : Google ReCAPTCHA Controller
*/
public class GoogleReCAPTCHAController {
    
    public Boolean verified { get; private set; }
    public String response  { 
        get {
            return ApexPages.currentPage().getParameters().get('g-recaptcha-response');
        }
    }
    public String firstName{get;set;}
    public String lastName{get;set;}
    public String publicKey {get;set;}
    
    private String remoteHost{
        get {
            String ret = '127.0.0.1';
            //Also could use x-original-remote-host
            Map<String, String> hdrs = ApexPages.currentPage().getHeaders();
            if (hdrs.get('x-original-remote-addr')!= null)
                ret = hdrs.get('x-original-remote-addr');
            else if (hdrs.get('X-Salesforce-SIP')!= null)
                ret = hdrs.get('X-Salesforce-SIP');
            return ret;
        }
    }
    
    //Google Secret Key
    private static String secretKey = 'ADD YOUR GOOGLE RECAPTCHA SECRET KEY';
    private static String baseUrl = 'https://www.google.com/recaptcha/api/siteverify';
    
    //Constructor
    public GoogleReCAPTCHAController(){
        this.publicKey = 'ADD YOUR GOOGLE RECAPATCHA SITE KEY OR PUBLIC KEY';//Google Site Key or Public Key
        this.verified = false;
    }
    
    public PageReference submit(){
        if (response == null ){
            //Google recaptcha empty response
            return null;
        }
        HttpResponse res = getGoogleReCAPTCHAResponse(baseUrl,'secret=' + secretKey + '&remoteip=' + remoteHost + '&response=' + response);
        if (res != null ) {
            JSONParser parser = JSON.createParser(res.getBody());
            while (parser.nextToken() != null) {
                if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'success')) {
                    parser.nextToken();
                    this.verified = parser.getBooleanValue();
                    break;
                }
            }            
        }   
        if(this.verified){
            //Add your logic
            return null;
        }
        else{
            //Stay on page to re-try reCAPTCHA
            return null;
        }
    }
    
    //Get Google reCAPTCHA Service Response 
    private static HttpResponse getGoogleReCAPTCHAResponse(string requestURL, string body){
        HttpResponse response = null;
        HttpRequest req = new HttpRequest();
        req.setEndpoint(requestURL);
        req.setMethod('POST');
        req.setBody (body);
        try{
            Http http = new Http();
            response = http.send(req);
            System.debug('ReCAPTCHA Response-' + response);
            System.debug('ReCAPTCHA Body-' + response.getBody());
        }
        catch(System.Exception ex){
            System.debug('ERROR Message-' + ex.getMessage());
        }
        return response;
    }
}

Step 4: Create VF Page

<apex:page controller="GoogleReCAPTCHAController" sidebar="false" showHeader="false" cache="false" id="pg">
    <script type='text/javascript' src='https://www.google.com/recaptcha/api.js'/>
    <script type='text/javascript'>
        function recaptchaCallback() {
        var btnVerify = document.getElementById("pg:fm:pb:pbb:btnVerify");
        if (btnVerify.classList.contains("hideButton") ) {
            btnVerify.classList.remove("hideButton");
        }
    }
    </script> 
    
    <style type="text/CSS">
        .hideButton{
        display:none !important;
        }
    </style>
    <apex:form id="fm">
        <apex:pageBlock title="Google Captcha Example" id="pb">
            <apex:pageBlockSection columns="1" id="pbs"> 
                <apex:pageBlockSectionItem >
                    <apex:outputLabel for="fnField" value="First Name"/>
                    <apex:inputText value="{!firstName}" id="fnField"/>
                </apex:pageBlockSectionItem>
                <apex:pageBlockSectionItem >
                    <apex:outputLabel for="lnField" value="Last Name"/>
                    <apex:inputText value="{!lastName}" id="lnField"/>
                </apex:pageBlockSectionItem>
                <apex:pageBlockSectionItem >
                    <div data-type="image" class="g-recaptcha" data-sitekey="{!publicKey}" data-callback="recaptchaCallback"></div>
                </apex:pageBlockSectionItem>
                <apex:pageBlockSectionItem rendered="{!verified}">
                    <p>Google reCAPTCHA verified successfully.</p>
                </apex:pageBlockSectionItem>
            </apex:pageBlockSection>
            <apex:pageBlockButtons id="pbb" location="bottom">
                <apex:commandButton action="{!submit}" styleClass="hideButton" value="Verify" id="btnVerify"/>
            </apex:pageBlockButtons>
        </apex:pageBlock>
    </apex:form>
</apex:page>