URL Hacks

Salesforce has re-introduced URL hacks.  This was something that was used a fair bit in Salesforce Classic.  With the release of Saleforce Lightning, these “hacks” were no longer supported.

But thanks to Spring 20, these have been added back.  The URL hack will only work with custom buttons or links.

To create a button or link which will create a new record with predefined values, use the following formula:


Creating Custom Tabs for Lightning Web Components

Create a Lightning web component and add it as a custom tab in the App Launcher and navigation menu.

  • Create a new Lightning web component:
    • Name: myComponent
  • Add a lightning-card to myComponent:
    • Title: Hello
  • Add a lightning-button to the Hello lightning-card (Note: make sure the button lives in the “actions” slot):
    • Label: New
  • Add a footer paragraph to the Hello lightning-card:
    • Text: Footnote
  • Add myComponent as a new custom tab:
    • Name: Words

Before you begin, you must update the configuration file to allow for custom tabs.

Copy the following code and paste it into notepad and save it as myComponent.js-meta.xml

<?xml version=”1.0″ encoding=”UTF-8″?>
<LightningComponentBundle xmlns=”http://soap.sforce.com/2006/04/metadata”&gt;

Use workbench to deploy the lightning component bundle to your org.

Editable Lightning Datatable Component

I wanted a nicer way to display related contacts on an account page layout.  This editable lightning datatable custom component does the trick!  I love it and it’s working out really well for us.

ContactDataTable Component

<aura:component implements=”force:appHostable,flexipage:availableForAllPageTypes,force:hasRecordId” access=”global” controller=”ContactDataTableController”>
<aura:attribute name=”data” type=”Object”/>
<aura:attribute name=”columns” type=”List”/>
<aura:attribute name=”recordId” type=”String”/>
<!– This attribute will hold the update records from data table–>
<aura:attribute name=”updatedRecord” type=”Object[]” />

<aura:handler name=”init” action=”{!c.doInit}” value=”{!this}”/>

<!– You must define keyField as ‘Id’ to save the record back in Salesforce ‘onsave’ attribute will executed when user clicks on save button –>
<!–<lightning:card >–>
columns=”{! v.columns }”
data=”{! v.data }”
onsave =”{!c.onSave}”
onrowaction=”{! c.handleRowAction }” />


* This finction defined column header
* and calls getContacts helper method for column data
* editable:’true’ will make the column editable
* */
doInit : function(component, event, helper) {
component.set(‘v.columns’, [
{label: ‘Name Range’, fieldName: ‘Name_Range__c’, editable:’true’, type: ‘text’},
//{label: ‘Name’, fieldName: ‘Name’, editable:’true’, type: ‘text’},
{label: ‘Contact Name’, fieldName: ‘linkName’, type: ‘url’,
typeAttributes: {label: { fieldName: ‘Name’ }}},
{label: ‘Title’, fieldName: ‘Title’, editable:’true’, type: ‘text’},
{label: ‘Email’, fieldName: ‘Email’, editable:’true’, type: ’email’},
{label: ‘Phone’, fieldName: ‘Phone’, editable:’true’, type: ‘tel’},
{label: ‘Board Contact’, fieldName: ‘Board_Contact__c’, editable:’true’, type: ‘text’},

helper.getContacts(component, helper);

* This function is calling saveDataTable helper function
* to save modified records
* */
onSave : function (component, event, helper) {
helper.saveDataTable(component, event, helper);


getContacts : function(component, event, helper) {
var action = component.get(“c.getContacts”);
accountId: component.get(“v.recordId”)
action.setCallback(this,function(response) {
var state = response.getState();
if (state === “SUCCESS”) {
var records =response.getReturnValue();
component.set(“v.data”, response.getReturnValue());
record.linkName = ‘/’+record.Id;
component.set(“v.data”, records);

* This function get called when user clicks on Save button
* user can get all modified records
* and pass them back to server side controller
* */
saveDataTable : function(component, event, helper) {
var editedRecords = component.find(“contactDataTable”).get(“v.draftValues”);
var totalRecordEdited = editedRecords.length;
var action = component.get(“c.updateContacts”);
‘editedContactList’ : editedRecords
action.setCallback(this,function(response) {
var state = response.getState();
if (state === “SUCCESS”) {
//if update is successful
if(response.getReturnValue() === true){
“title”: “Record Update”,
“type”: “success”,
“message”: totalRecordEdited+” Contact Records Updated”
} else{ //if update got failed
“title”: “Error!!”,
“type”: “error”,
“message”: “Error in update”

* Show toast with provided params
* */
showToast : function(params){
var toastEvent = $A.get(“e.force:showToast”);
} else{

* reload data table
* */
reloadDataTable : function(){
var refreshEvent = $A.get(“e.force:refreshView”);


public with sharing class ContactDataTableController {

public static List<Contact> getContacts(Id accountId)
return [SELECT Id, Name, title, Email, Phone,Name_Range__c, Board_Contact__c FROM Contact WHERE AccountId =: accountId];

public static boolean updateContacts(List<Contact> editedContactList){
update editedContactList;
return true;
} catch(Exception e){
return false;

Omni-Channel Lightning Component

Salesforce Omni-Channel is great, but the one downside in our organization is that you have to manually set your status to available in order to receive chat or work requests.

I am in the midst of creating a Background Utility Item which will auto log you into omni-channel.

Omni-Channel Lightning Component

<aura:component implements=”lightning:backgroundUtilityItem” access=”global” >
<aura:handler name=”init” value=”{!this}” action=”{!c.doInit}”/>
<lightning:omniToolkitAPI aura:id=”omniToolkit” />

Omni-Channel Lightning Component Controller

doInit: function(cmp, evt, hlp) {
$A.getCallback(function() {

var omniAPI = cmp.find(“omniToolkit”);
statusId: “0N561000000027Y”,
callback: function(result) {
if (result.success) {
console.log(‘Set status successful’);
console.log(‘Current statusId is: ‘ + result.statusId);
console.log(‘Channel list attached to this status is: ‘ + result.channels);
} else {
console.log(‘Set status failed’);

}), 1000

This works like 90% of the time. There are some kinks which I’m still trying to work out.  For example, sometimes when you open service console you will get an error that says “Error logging into Omni-Channel”.  Not too sure why yet as it seems sporadic.  Clearing broswer cache and re-logging into service console does the trick.  If you have any ideas as to why this is the case, feel free to shoot me a message.

Work with the Lightning Map Component and Apex Inherited Sharing

This is the hands on challenge from the Platform Developer 1 Winter 19 Certification Maintenance.


public inherited sharing class UtilityClass {
public static List<sObject> queryObjects(String theObject, List<String> theFields, List<String> theFilters, String sortField, String sortOrder) {
String theQuery = ‘SELECT ‘ + string.join(theFields, ‘,’);
theQuery += ‘ FROM ‘ + theObject;
boolean firstFilter = true;
for (String filter : theFilters) { //loop through the filters
String clauseToUse = (firstFilter) ? ‘ WHERE ‘ : ‘ AND ‘; //get the right clause
filter = filter.trim();
filter = filter.replaceAll(‘(\\s+)’, ‘ ‘); //remove white spaces
theQuery += clauseToUse + filter; //add the filter to the query
firstFilter = false; //changes the filter clause
if(!String.isEmpty(sortField)) {
theQuery += ‘ ORDER BY ‘ + sortField;
if(!String.isEmpty(sortOrder)) {
theQuery += ‘ ‘ + sortOrder;
String theQueryResult = string.escapeSingleQuotes(theQuery); //escapes the string
return database.query(theQueryResult);

TowerMapController Class

public inherited sharing class TowerMapController {
public static List<Tower__c> getAllTowers() {
String theObject = ‘Tower__c’;
List<String> theFields = new List<String>{‘Id’, ‘Name’, ‘State__r.Name’, ‘Location__Latitude__s’, ‘Location__Longitude__s’};
List<String> theFilters = new List<String>(); //empty filter list
String sortField = ‘Name’;
String sortOrder = ‘ASC’;
List<Tower__c> allTowers = UtilityClass.queryObjects(theObject, theFields, theFilters, sortField, sortOrder);
return allTowers;

Towermap Lightning Component

<aura:component implements=”flexipage:availableForAllPageTypes” controller=”TowerMapController” access=”global” >
<aura:attribute name=”mapMarkers” type=”Object” access=”PRIVATE” />
<aura:attribute name=”markersTitle” type=”String” access=”PRIVATE” />
<aura:handler name=”init” value=”{!this}” action=”{!c.handleInit}”/>
<aura:if isTrue=”{!!empty(v.mapMarkers)}” >
mapMarkers=”{! v.mapMarkers }”
markersTitle=”{! v.markersTitle }”
zoomLevel=”{!v.zoomLevel}” />

Lightning Component Controller

handleInit: function (component, event, helper) {
helper.initHelper(component, event, helper);

Lightning Component Helper

initHelper : function(component, event, helper) {
helper.utilSetMarkers(component, event, helper);
utilSetMarkers : function(component, event, helper) {
let action = component.get(“c.getAllTowers”);
action.setCallback(this, function(response) {
const data = response.getReturnValue();
const dataSize = data.length;
let markers = [];
for(let i=0; i < dataSize; i += 1) {
const Tower = data[i];
‘location’: {
‘Latitude’ : Tower.Location__Latitude__s,
‘Longitude’ : Tower.Location__Longitude__s
‘icon’: ‘utility:Tower’,
‘title’ : Tower.Name,
‘description’ : Tower.Name + ‘ Tower Location at ‘ + Tower.State__r.Name
component.set(‘v.markersTitle’, ‘Out and About Communications Tower Locations’);
component.set(‘v.mapMarkers’, markers);