Using Lightning Components In Visualforce page

In this blog, I am going to explain how to invoke the lightning component from the visualforce page by using Lightning out. Add Lightning components to your Visualforce pages to combine features you’ve built using both solutions. Implement new functionality using Lightning components and then use it with existing Visualforce pages. Lightning Components for Visualforce is based on Lightning Out, a powerful and flexible feature that lets you embed Lightning components into almost any web page.

Step 1: Create Sample Component

Create a lightning component with below code which contains two attributes

<aura:component access="global">
    <aura:attribute name="greeting" type="String" default="Hello" access="global" />
    <aura:attribute name="subject" type="String" default="World" access="global" />
    <div style="box">
        <span class="greeting">{!v.greeting}</span>, {!v.subject}!
    </div>
    
</aura:component>

Step 2: Create  Lightning Dependency App

To use Lightning Components for Visualforce, define component dependencies by referencing a Lightning dependency app. This app is globally accessible and extends ltng:outApp. The app declares dependencies on any Lightning definitions (like components) that it uses.

<aura:application access="GLOBAL" extends="ltng:outApp">
    <aura:dependency resource="c:helloworld"/>   
</aura:application>

Step 3: Using in Visualforce from the page

There are three steps to add Lightning components to a Visualforce page.

  1. Add the Lightning Components for Visualforce JavaScript library to your Visualforce page using the <apex:includeLightning/>  component.
    <apex:includeLightning/>
  2. To reference this app on your page, use the following JavaScript code.
    $Lightning.use("theNamespace:lcvfTest", function() {});
    
  3. Finally, add your top-level component to a page using $Lightning.createComponent(String type, Object attributes, String locator, function callback). This function is similar to $A.createComponent(), but includes an additional parameter, domLocator, which specifies the DOM element where you want the component inserted.

Here is the final code.

<apex:page docType="html-5.0" standardStylesheets="false" >
    <apex:includeLightning />
    
    <div id="lightning" />
    
    <script>
    $Lightning.use("c:helloworldApp", function() {
        $Lightning.createComponent("c:helloworld",
                                   {
                                       "greeting" : "Hello" ,
                                       "subject" : "World!" 
                                   },
                                   "lightning",
                                   function(cmp) {
                                       // do some stuff
                                   });
    });
    </script>
</apex:page>

 

 

Continuation Chaining Asynchronous Callouts

If the order of the callouts matters, or when a callout is conditional on the response of another callout, you can chain callout requests. Chaining callouts mean that the next callout is made only after the response of the previous callout returns. You can chain up to three callouts.The following Visualforce and Apex examples show how to chain one callout to another.

public with sharing class ChainedContinuationController {
    
    // Unique label for the initial callout request
    public String requestLabel1;
    // Unique label for the chained callout request
    public String requestLabel2;
    // Result of initial callout
    public String result1 {get;set;}
    // Result of chained callout
    public String result2 {get;set;}
    // Endpoint of long-running service
    private static final String LONG_RUNNING_SERVICE_URL1 = 'https://www.zipcodeapi.com/rest/XUdRusqAL97aO28KYyMzfhZmkBLzSl7Qs853mWwzTSlxLRktAmrNTfQ2kr9bd8BE/info.json/95051/degrees';
    private static  String LONG_RUNNING_SERVICE_URL2 ='http://openweathermap.org/data/2.5/weather?q=santa%20clara&appid=b6907d289e10d714a6e88b30761fae22';
    
    // Action method
    public Object invokeInitialRequest() {
        // Create continuation with a timeout
        Continuation con = new Continuation(10);
        // Set callback method
        con.continuationMethod='processInitialResponse';
        
        // Create first callout request
        HttpRequest req = new HttpRequest();
        req.setMethod('GET');
        req.setEndpoint(LONG_RUNNING_SERVICE_URL1);
        
        // Add initial callout request to continuation
        this.requestLabel1 = con.addHttpRequest(req);              
        
        // Return the continuation
        return con;  
    }
    
    // Callback method for initial request
    public Object processInitialResponse() {   
        // Get the response by using the unique label
        HttpResponse response = Continuation.getResponse(this.requestLabel1);
        // Set the result variable that is displayed on the Visualforce page
        this.result1 = response.getBody();
        
        ZipParser parser =  (ZipParser) System.JSON.deserialize(response.getBody(), ZipParser.class);
        System.debug('parser'+parser);
        
        Continuation chainedContinuation = null;
        // Chain continuation if some condition is met
        if (parser.city!=null) {
            // Create a second continuation 
            chainedContinuation = new Continuation(10);
            // Set callback method
            chainedContinuation.continuationMethod='processChainedResponse';
            
            // Create callout request
            HttpRequest req = new HttpRequest();
            req.setMethod('GET');
            
            // LONG_RUNNING_SERVICE_URL2 ='http://openweathermap.org/data/2.5/weather?q='+parser.city+',usa'+'&appid=b6907d289e10d714a6e88b30761fae22';
            req.setEndpoint(LONG_RUNNING_SERVICE_URL2);
            
            // Add callout request to continuation
            this.requestLabel2 = chainedContinuation.addHttpRequest(req); 
        }
        
        // Start another continuation 
        return chainedContinuation;  
    }    
    
    // Callback method for chained request
    public Object processChainedResponse() {   
        // Get the response for the chained request
        HttpResponse response = Continuation.getResponse(this.requestLabel2);
        // Set the result variable that is displayed on the Visualforce page
        this.result2 = response.getBody();
        System.debug('result2result2'+result2);
        // Return null to re-render the original Visualforce page
        return null;
    }
    
    
    public class ZipParser{
        public String zip_code;	//95051
        public Double lat;	//37.348656
        public Double lng;	//-121.984433
        public String city{get;set;}	//Santa Clara
        public String state{get;set;}	//CA
        public cls_timezone timezone;
        public cls_acceptable_city_names[] acceptable_city_names;
    }
    public class cls_timezone {
        public String timezone_identifier;	//America/Los_Angeles
        public String timezone_abbr;	//PST
        public Integer utc_offset_sec;	//-28800
        public String is_dst;	//F
    }
    public  class cls_acceptable_city_names {
    }
    
    
    
    
    
    
}

 

<apex:page controller="ChainedContinuationController" showChat="false" showHeader="false">
    <apex:form >
        <!-- Invokes the action method when the user clicks this button. -->
        <apex:commandButton action="{!invokeInitialRequest}" value="Start Request" reRender="panel" />  
    </apex:form>
    
    <apex:outputPanel id="panel">
        <!-- Displays the response body of the initial callout. -->   
        <apex:outputText value="{!result1}" />
        
        <br/>
        <!-- Displays the response body of the chained callout. -->
        <apex:outputText value="{!result2}" />
    </apex:outputPanel> 
    
</apex:page>

 

Visualforce Page Actions

Introduction

In this blog post, I am going to explain how to set up global actions and object-specific actions by using visualforce page.The global action which we are going to see in this blog is to send the message to slack channel and object specific action is to take the case ownership from the assigned queues.

Global Actions:-

As we are going to going to post the message to slack by using the global action, go and create a Slack app and get the slack token. Please refer this link

visualforce page is here below

<apex:page controller="GlobalSlackActions" lightningStylesheets="true" sidebar="false" showQuickActionVfHeader="false" docType="html-5.0">
    <apex:slds />
    /canvas/sdk/js/publisher.js
    
    <div class="slds-scope">
        <apex:form >
            <apex:actionFunction action="{!postMessagetoSlack}" name="postSlackMessage" rerender="out"
                                 oncomplete="refreshFeed();"/>
            <apex:outputPanel id="out">
                
                
                <div class="slds-form--compound slds-m-top--xxx-small">
                    <div class="slds-form-element">
                        <div class="slds-form-element__row slds-align_absolute-center">
                            <label class="slds-form-element__label" for="sample1">#Slack Channel</label>
                            <div class="slds-form-element__control slds-picklist">
                                <apex:selectList id="countries" value="{!slackChannel}" size="1" required="true" styleClass="slds-input">
                                    <apex:selectOptions value="{!ListOfSlackChannels}"/>
                                </apex:selectList>  
                            </div>
                        </div>
                    </div>
                    <div class="slds-form-element">
                        
                        <div class="slds-form-element__row slds-align_absolute-center ">
                            <label class="slds-form-element__label " for="sample2"> Name </label>
                            <div class="slds-form-element__control slds-picklist">
                                <apex:inputTextArea value="{!slackTextMessge}"  id="sample1" styleClass="slds-input"/>
                            </div>
                        </div>       
                    </div>
                </div>
                
                
            </apex:outputPanel>
            <div class="slds-form-element slds-align_absolute-center">
                <button  onclick="postSlackMessage();" styleClass="slds-button slds-button_neutral">Post to Slack </button>
                <button  onclick="closeAction();" styleClass="slds-button slds-button_neutral">Close  </button>
                
            </div>
        </apex:form>
    </div>
</apex:page>

 

And the controller is here.

public class GlobalSlackActions {
    public String slackChannel {get;set;}
    public List<SelectOption> channelNameOptions{get;set;}
    public String slackTextMessge {get;set;}
    public String slackToken {get;set;}
    public GlobalSlackActions(){
        slackToken ='xoxp-167092741267-167199659508-179751455458-6fd12a391bd432b735b4efcb1313193f';
    }
    public List<SelectOption> getListOfSlackChannels () {
        String slackEndpoint = 'https://slack.com/api/channels.list?token='+slackToken;
        HttpResponse res = buildHttp(slackEndpoint);
        
        if(res.getStatusCode() ==200){
            JSON2Apex cls =  (JSON2Apex)parse(res.getBody());
            list<Channels> c =cls.channels ; 
            System.debug('c'+c);
            List<SelectOption> options = new List<SelectOption>();
            for(Channels cName :c){
                options.add(new SelectOption(cName.name,cName.name));
            }
            return options ; 
        }else{
            return null ;
        }
    }
    
    
    
    public class JSON2Apex {
        public Boolean ok;
        public List<Channels> channels{get;set;}
    }
    
    
    
    public class Channels {
        public String id;
        public String name{get;set;}
        public Boolean is_channel;
        public Integer created;
        public String creator;
        public Boolean is_archived;
        public Boolean is_general;
        public Integer unlinked;
        public String name_normalized;
        public Boolean is_shared;
        public Boolean is_org_shared;
        public Boolean is_member;
        public Boolean is_private;
        public Boolean is_mpim;
        public List<String> members;
        public Integer num_members;
    }
    
    
    public static JSON2Apex parse(String json) {
        return (JSON2Apex) System.JSON.deserialize(json, JSON2Apex.class);
    }
    
    
    public PageReference postMessagetoSlack () {
        String slackEndpoint = 'https://slack.com/api/chat.postMessage?token='+slackToken+'&channel='+slackChannel+'&text='+slackTextMessge+'&pretty=1';
        buildHttp(slackEndpoint);
        return null ;
    }
    public HttpResponse buildHttp(String url){
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(url);
        req.setMethod('POST');
        HttpResponse res = new HttpResponse(); 
        res = h.send(req);
        return res ;
    }
    
}

Go to set up, find global actions and create a new one as shown below.

Add to the publisher layout as shown below.

After saving the publishers layout you can able to see the quick actions as shown below.

Object Specific actions

Now I am going to walk thru how to create the object specific action.To associate an action to object you need to user standardController attribute of the object. Here we are going to create a Case Quick actions which are allowing the user to take the ownership of the case from case queue.

Here is the visual force page

<apex:page standardController="Case" extensions="CaseTakeOwnership" showQuickActionVfHeader="false">
    
    /canvas/sdk/js/publisher.js
    
    
    <apex:form id="demo">
        <apex:actionFunction action="{!takeOwnership}" name="takeOwner" reRender="demo" oncomplete="refreshFeed();"/>
        <div style="align:center">
            <br/><br/><br/>
            <button  onclick="takeOwner();" class="sucessBtn">Take Ownership</button> <br/><br/><br/><br/>
            <button onclick="closeAction();" class="closebtn">Cancel</button>
        </div>
    </apex:form>
    
    
</apex:page>

The controller is here below.

public class CaseTakeOwnership {
    public Case careRecord {get; set;}
    public CaseTakeOwnership(ApexPages.StandardController controller) {
        careRecord =(Case)controller.getRecord();
    }
    public PageReference takeOwnership() {
        try {
            careRecord.OwnerId = UserInfo.getUserId();
            update careRecord;
        } catch(Exception ex){
        }
        return null ;
    }
    
    
}

Both in global action and Object actions, I set showQuickActionVfHeader to false. So that we will not able to see the Standard Submit and Cancel Global action header.

Now go to Case Object buttons and links and create a new action as shown below

Add the object to quick action, salesforce 1 and lightning action on page layout based on whether you wanted to show on salesforce class or salesforce 1 as shown below.

 

Publisher Events

Refresh feed (desktop) 

Sfdc.canvas.publisher.publish({ name: 'publisher.refresh',
 payload: {feed:true}
});

Submit hook (Salesforce1)

Sfdc.canvas.publisher.subscribe({name: "publisher.showPanel",
 onData:function(e) {
 Sfdc.canvas.publisher.publish({name:
 "publisher.setValidForSubmit", payload:"true"});
}});
Sfdc.canvas.publisher.subscribe({ name: "publisher.post",
 onData: function(e) {
 alert("call some remote action here");
 Sfdc.canvas.publisher.publish({ name: "publisher.close",
 payload:{ refresh:"true" }});
}});

Close publisher event

        Sfdc.canvas.publisher.publish({name: "publisher.close", payload:{ refresh:"true" , successMessage: 'Cancel!'}});  

Activate publish button 

Sfdc.canvas.publisher.publish({
    name: "publisher.setValidForSubmit", 
    payload:"true"});
Clear Panel State
Sfdc.canvas.publisher.subscribe(
             { name : "publisher.clearPanelState", onData:function(e) { alert('Fire ClearPanelState'); }}
        );

Show Panel State 

   Sfdc.canvas.publisher.subscribe(
             { name : "publisher.showPanel", onData:function(e) { alert('Fire ShowPanel'); }}
        );
Success State 
            Sfdc.canvas.publisher.publish({ name : "publisher.success", payload : { feed:true }});

 

Redirect (Salesforce1)

sforce.one.navigateToSObject(recordId,view)
sforce.one.navigateToURL(url)
sforce.one.navigateToFeed(subjectId, type)
sforce.one.navigateToFeedItemDetail(feedItemID)
sforce.one.navigateToRelatedList(relatedListId, parentRecordId)
sforce.one.navigateToList(listViewId, listViewName, scope)
sforce.one.createRecord(entityName, recordTypeId)
sforce.one.editRecord(recordId)

 

Sales force Platform Encryption API – APEX

In this blog, I am going to explain how to use the TenantSecret object in salesforce to generate the tenant secret key for Platform encryption.

We are going to build the visualforce page that used to create tenant secret key and view all the existing tenant secret key. Visualforce page looks as shown below

The Page controller is shown below

controller :-
public class TenantController {

public TenantSecret secretKey {get;set;}
public TenantController(ApexPages.StandardController controller){
this.secretKey = new TenantSecret();
}

public PageReference insertNewSecret(){
try{
system.debug(‘Key’+secretKey );
insert secretKey ;
}catch(Exception e ){
ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.ERROR,e.getMessage());
ApexPages.addMessage(myMsg);
}
return null ;
}

public List<TenantSecret> getKeyHistory(){

return [Select Id ,
Description ,
Source ,
Type ,
version,
status
from TenantSecret ];

}

}

Visual force page is shown below.

<apex:page standardController=”TenantSecret” extensions=”TenantController” docType=”html-5.0″ applyhtmltag=”true”
showheader=”true” sidebar=”false” standardstylesheets=”false”>

<apex:stylesheet value=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&#8221; />

<style>
.headerStyle{
background-color: PowderBlue!important;
background-image: none !important;
color: Black!important;
font-size:100% !important;
text-align:center;
font-family: Candara, Calibri, Segoe, ‘Segoe UI’, Optima, Arial, sans-serif;
font-size: 14px;
font-variant: normal;
font-weight: normal;
line-height: 17px;
}

.tdcls{
background-color:#f8f8ff!important;
background-image: none !important;
color: Black!important;
font-size:100% !important;
text-align:center;
font-family: Candara, Calibri, Segoe, ‘Segoe UI’, Optima, Arial, sans-serif;
font-size: 14px;
font-variant: normal;
font-weight: normal;
line-height: 17px;
}

</style>

<apex:form styleClass=”headerStyle”>
<apex:pageMessages ></apex:pageMessages>
<apex:pageBlock >
<apex:pageBlockSection columns=”3″>

<apex:inputField value=”{!secretKey.Description}” style=”width:80%;height:140%” />
<apex:inputField value=”{!secretKey.Type}” style=”width:40%;height:140%” />
<apex:commandButton value=”Save New Key” action=”{!insertNewSecret}”
style=”background:#4682b4;color:white;border-radius:7px” reRender=”existingkeys” />

</apex:pageBlockSection>

</apex:pageBlock>

<apex:pageBlock >
<apex:outputPanel id=”existingkeys”>

<table class=”table table-bordered table-inverse”>
<thead>
<tr>
<th class=”headerStyle”>Version </th>
<th class=”headerStyle”>status</th>
<th class=”headerStyle”>Type</th>
<th class=”headerStyle”>Description</th>
<th class=”headerStyle”>Source</th>
</tr>
</thead>
<tbody>
<apex:repeat value=”{!KeyHistory}” var=”key”>
<tr>
<td class=”tdcls”>{!key.Version} </td>
<td class=”tdcls”>{!key.status}</td>
<td class=”tdcls”>{!key.Type}</td>
<td class=”tdcls”>{!key.Description}</td>
<td class=”tdcls”>{!key.Source}</td>
</tr>
</apex:repeat>
</tbody>

</table>
</apex:outputPanel>

</apex:pageBlock>

</apex:form>

</apex:page>
Github URL for the code:-
https://github.com/rajamohanvakati/Platform-Encryption-