Sunday, February 23, 2020

Lightning Web Component OnLoad

lWCOnLoad.html:

<template>
    <div class="slds-text-heading_large slds-text-align_center">
        {greeting}
</div>
</template>

lWCOnLoad.js:

import { LightningElement, track } from 'lwc';

export default class LWCOnLoad extends LightningElement {

    @track greeting;

    connectedCallback(){
        this.greeting ='Welcome to LWC';
    }

}


Get Current User Id in Lightning Web Component



UserDetails.html: 

<template>
<div class="slds-text-heading_large slds-text-align_center">
   User Id : {currentUserId}
    </div>
</template>

UserDetails.js: 

import { LightningElement } from 'lwc'; 
import strUserId from '@salesforce/user/Id'

export default class UserDetails extends LightningElement { 
 userId = strUserId 

}

Lightning CPQ button for Recall Approval

Lightning Component:

<aura:component controller="CpqRecallQuoteApprovalController" implements="flexipage:availableForAllPageTypes,force:appHostable,force:lightningQuickActionWithoutHeader,force:hasRecordId">
    <aura:attribute name="isSpinner" type="boolean" default="false"/>
    <aura:handler event="aura:waiting" action="{!c.handleShowSpinner}"/>
    <aura:handler event="aura:doneWaiting" action="{!c.handleHideSpinner}"/>
    <aura:html tag="style">
        .cuf-content {
        padding: 0 0rem !important;
        }
        .slds-p-around--medium {
        padding: 0rem !important;
        }       
        .slds-modal__content{
        overflow-y:hidden !important;
        height:unset !important;
        max-height:unset !important;
        }
    </aura:html>
    <div class="modal-header slds-modal__header slds-size_1-of-1">
        <h4 class="title slds-text-heading--medium" >Recall Approval</h4>
    </div>
    <!-- MODAL BODY / INPUT FORM -->    
    <div class="slds-modal__content slds-p-around--x-small slds-align_absolute-center slds-size_1-of-1 slds-is-relative" aura:id="modalbody" id="modalbody">
        <center>
            <b>This will allow you to edit the quote but will require the quote to be approved again.<br/> 
                Do you wish to continue?
            </b>
        </center>
    </div>
    <div class="modal-footer slds-modal__footer slds-size_1-of-1">
        <div class="forceChangeRecordTypeFooter">
            <ui:button class="slds-button slds-button_neutral" disabled="{!v.isSpinner}" label="Cancel" press="{!c.cancel}" /> 
            <ui:button class="slds-button slds-button--brand" disabled="{!v.isSpinner}" label="{!v.isSpinner == true ? 'Ok...' : 'Ok'}" press="{!c.proceedToRecall}"/>
        </div>
    </div>

</aura:component>

Lightning JS Controller:

({
    cancel: function(component, event, helper) {
        $A.get("e.force:closeQuickAction").fire();
    },
    proceedToRecall: function(component, event, helper) {
        var action = component.get("c.onRecall");
        action.setParams({"quoteId" : component.get("v.recordId")});
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                console.log(response.getReturnValue());
                $A.get('e.force:refreshView').fire();
                $A.get("e.force:closeQuickAction").fire();
            }
            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);
    },
    //Call by aura:waiting event  
    handleShowSpinner: function(component, event, helper) {
        component.set("v.isSpinner", true); 
    },
     
    //Call by aura:doneWaiting event 
    handleHideSpinner : function(component,event,helper){
        component.set("v.isSpinner", false);
    }

})

Apex Controller:

public class CpqRecallQuoteApprovalController {
    
/*
* This method will call get triggered on Recall approval button in Lightning Component
*/ 
    @AuraEnabled
    public static void onRecall(String quoteId) {
        if (quoteId != null) {
            SBAA.ApprovalAPI.recall(quoteId, SBAA__Approval__c.Quote__c);
        }
    }

}

Lightning CPQ button for Submit For Approval

Lightning Component:


<aura:component controller="CpqQuoteApprovalController" implements="flexipage:availableForAllPageTypes,force:appHostable,force:lightningQuickActionWithoutHeader,force:hasRecordId">
    <aura:attribute name="requiredValuesNotAvailable" type="boolean" default="true"/>
    <aura:attribute name="displaySecondConfirmation" type="boolean" default="false"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <aura:attribute name="isSpinner" type="boolean" default="false"/>
    <aura:handler event="aura:waiting" action="{!c.handleShowSpinner}"/>
    <aura:handler event="aura:doneWaiting" action="{!c.handleHideSpinner}"/>
     
    <aura:html tag="style">
        .cuf-content {
        padding: 0 0rem !important;
        }
        .slds-p-around--medium {
        padding: 0rem !important;
        }       
        .slds-modal__content{
        overflow-y:hidden !important;
        height:unset !important;
        max-height:unset !important;
        }
    </aura:html>
    <div class="modal-header slds-modal__header slds-size_1-of-1">
        <h4 class="title slds-text-heading--medium" >Submit For Approval</h4>
    </div>
    
    <!-- MODAL BODY / INPUT FORM -->    
    <div class="slds-modal__content slds-p-around--x-small slds-align_absolute-center slds-size_1-of-1 slds-is-relative" aura:id="modalbody" id="modalbody">
        <aura:if isTrue="{!!v.displaySecondConfirmation}">
            <center>
                <b>Do you want to submit quote for approval?<br/>
                    Once submitted for approval Quote will be locked.</b>
            </center>
        </aura:if>
        <aura:if isTrue="{!v.displaySecondConfirmation}">
            <center>
                <b>Please provide Quote Name and Discount Justification before Submitting for Approval</b>
            </center>
        </aura:if>
    </div>
    <!-- End of Modal Content -->  
    <!-- MODAL FOOTER -->
    <div class="modal-footer slds-modal__footer slds-size_1-of-1">
        <aura:if isTrue="{!!v.displaySecondConfirmation}">
            <div class="forceChangeRecordTypeFooter">
                <ui:button class="slds-button slds-button_neutral" disabled="{!v.isSpinner}" label="Cancel" press="{!c.cancel}" /> 
                <ui:button class="slds-button slds-button--brand" disabled="{!v.isSpinner}" label="{!v.isSpinner == true ? 'Ok...' : 'Ok'}" press="{!c.proceedToSubmit}"/>
            </div>

        </aura:if>
        <aura:if isTrue="{!v.displaySecondConfirmation}">
            <ui:button class="slds-button slds-button--brand" label="Ok" press="{!c.cancel}" />
        </aura:if>
    </div>

</aura:component>


Lightning JS Controller:

({
    doInit: function(component, event, helper) {
        var action = component.get("c.checkForRequiredFields");
        action.setParams({"recordId" : component.get("v.recordId")});
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                console.log(response.getReturnValue());
                component.set("v.requiredValuesNotAvailable", response.getReturnValue());
            }
            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);
    },
    cancel: function(component, event, helper) {
        $A.get("e.force:closeQuickAction").fire();
    },
    proceedToSubmit: function(component, event, helper) {
        var requiredValuesNotAvailable = component.get("v.requiredValuesNotAvailable");
   
        if(requiredValuesNotAvailable){
            component.set('v.displaySecondConfirmation', true);
        }
        else{
            var action = component.get("c.onSubmit");
            action.setParams({"quoteId" : component.get("v.recordId")});
            action.setCallback(this, function(response) {
                var state = response.getState();
                if (state === "SUCCESS") {
                    console.log(response.getReturnValue());
                    $A.get('e.force:refreshView').fire();
                    $A.get("e.force:closeQuickAction").fire();
                }
                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);
         
        }
    },

     //Call by aura:waiting event
    handleShowSpinner: function(component, event, helper) {
        component.set("v.isSpinner", true);
    },
   
    //Call by aura:doneWaiting event
    handleHideSpinner : function(component,event,helper){
        component.set("v.isSpinner", false);
    }
})

Apex Controller:

public class CpqQuoteApprovalController {
    @AuraEnabled
    public static boolean checkForRequiredFields(String recordId) {
        Boolean requiredFieldsNotAvailable = false;
        List<SBQQ__Quote__c> qt = [SELECT Id, SBQQ__Type__c, Quote_Name__c, Discount_Justification__c FROM SBQQ__Quote__c Where Id =: recordId];
        if((qt[0].SBQQ__Type__c == 'Amendment') && ((qt[0].Quote_Name__c == null) || (qt[0].Discount_Justification__c == null))){
            requiredFieldsNotAvailable = true;
        }
        return requiredFieldsNotAvailable;
    }
/*
* This method will call submit approval method
*/
    @AuraEnabled
    public static void onSubmit(String quoteId) {
        if (quoteId != null) {
            SBAA.ApprovalAPI.submit(quoteId, SBAA__Approval__c.Quote__c);
        }
    }
}

Sunday, October 20, 2019

Enforce Field-Level Security Permissions for SOQL Queries

This post will explain how to enforce field level security permissions for SOQL queries,

  • In Spring' 19 release salesforce introducing this fantastic feature, WITH_SECURITY_ENFORCED clause, you can use this to enable checking for field- and object-level security permissions on SOQL SELECT queries, including subqueries and cross-object relationships.
  • If you include a field in a SQOL query (Without WITH SECURITY_ENFORCED Clause) and a user doesn’t have access to that field, the field will be returned and can be used by the code without any exception, but the data to that user should not have access.
  • If you use WITH SECURITY_ENFORCED clause for same SOQL Select query, it will throw exception and no data will be returned.
  • The WITH SECURITY_ENFORCED clause is only available in Apex. Using WITH SECURITY_ENFORCED in Apex classes or triggers with an API version earlier than 45.0 is not recommended.

Below are some of the examples.

1. If on the Opportunity object, suppose field-level security for  Stage , Amount, and LeadSource is hidden, it will throw an exception insufficient permissions and no data will return.

String query = ’Select Id, Name, ‘;
if(Schema.sObjectType.Opportunity.fields.Amount.isAccessible()){
query += ‘Amount, ’;
} else{
//Exception handling code will go here
}
if(Schema.sObjectType.Opportunity.fields. LeadSource.isAccessible()){
query += ‘LeadSource, ’;
}
if(Schema.sObjectType.Opportunity.fields.StageName.isAccessible()){
query += ‘StageName, ’;
}
query = query.subString(0, query.length()-2);
query += ‘ FROM Opportunity’;
List<Opportunity> oppty = Database.query(query);
And after this awesome feature introduced, developers need to write below lone of code.
List<Opportunity> oppty = [SELECT Id, Name, LeadSource ,StageName, Amount FROM Opportunity WITH SECURITY_ENFORCED];

2. If field access for Website is hidden, this query throws an exception indicating insufficient permissions.

List<Account> acct = [SELECT Id, parent.Name, parent.Website
FROM Account WITH SECURITY_ENFORCED]

How to use in Apex Method

In below code example, the comparison can be understand that how the field
accessibility were getting checked before introduction of this awesome feature.

Checking field accessibility before this feature:

 if (Schema.SObjectType.Contact.isAccessible()
            && Schema.SObjectType.Contact.fields.Name.isAccessible()
            && Schema.SObjectType.Contact.fields.Secret_Key__c.isAccessible()){
            List results = [SELECT id, Name, Secret_Key__c FROM Contact WHERE Id = :recordId];
            if (!results.isEmpty()) {
                result = results[0];
            }
        } else{
        throw new SecurityException('You don\'t have access to all contact fields'); }

Checking field accessibility after this feature:

try
{
List results = [SELECT id, Name, Secret_Key__c FROM Contact WHERE Id = :recordId 
WITH SECURITY_ENFORCED];
if (!results.isEmpty()) {
result = results[0];
}
}
catch( System.QueryException exception)
{
     throw new SecurityException('You don\'t have access to all contact fields'); }