
Are you a Salesforce administrator or developer who often works with permission sets and needs an efficient way to explore and manage permissions? Look no further! In this blog post, we’ll introduce you to a powerful custom Lightning Web Component (LWC) called “Permission Set Explorer,” which can revolutionize how you handle Salesforce permission sets.
Exploring the Generate Excel Feature
One of the most exciting features of the Permission Set Explorer LWC component is the “Generate Excel” functionality. With just a single click of a button, you can export the permission set information into an Excel spreadsheet, providing a convenient and portable way to analyze and share the data with stakeholders. This feature comes in handy when you need to review or document the permission sets for compliance purposes or to analyze access levels across different objects.
Utilizing the Sheet JS Library for Excel Generation
The “Generate Excel” feature of the Permission Set Explorer is powered by the Sheet JS library, which is seamlessly integrated into the LWC component. Sheet JS is a JavaScript library that enables client-side Excel generation, making it easy to create, read, and modify Excel files right from within the browser.
The benefits of using the Sheet JS library for this feature are numerous:
- Client-Side Excel Generation: With Sheet JS, the Excel file is generated on the client-side, meaning the data doesn’t need to be sent back and forth to the server. This ensures a faster and more responsive experience for users.
- No Server-Side Code Required: Thanks to Sheet JS’s capabilities, the LWC component can handle the entire Excel generation process on its own. There’s no need for additional server-side code, reducing complexity and enhancing the component’s portability.
- Support for Various File Formats: Sheet JS supports not only Excel (XLSX) but also other popular file formats like CSV and ODS. This allows you to cater to different user preferences when exporting data.
- Ease of Use and Integration: Integrating Sheet JS into the LWC component is straightforward and intuitive, enabling developers to quickly add Excel generation capabilities without a steep learning curve.
Exploring the Permission Set Explorer Feature
The core functionality of the Permission Set Explorer LWC component revolves around exploring and presenting Salesforce permission set information in a user-friendly manner. Let’s understand how this feature can be beneficial for Salesforce admins and developers:
- Easy Selection of Permission Set: The component provides a user-friendly dropdown (combobox) that lists all the available permission sets. Users can easily select the desired permission set from the list, streamlining the exploration process.
- Permission Set Details Table: Once a permission set is selected, the component displays a tabular representation of the permission details. The table showcases various permissions for different objects, including Read, Create, Edit, Delete, View All, and Modify All.
- Visual Indicators for Permissions: To make the information more understandable at a glance, the component utilizes lightning-icons to represent permissions visually. A green checkmark icon indicates “TRUE” (permission granted), while a red “X” icon indicates “FALSE” (permission not granted).
- Dynamic Data Loading: The component dynamically fetches the permission details for the selected permission set, ensuring that users get the most up-to-date information without unnecessary delays.
Benefits for Salesforce Admins and Developers
The Permission Set Explorer LWC component offers several benefits for Salesforce admins and developers:
- Streamlined Permission Management: With a clear overview of permission sets and their details, admins can efficiently manage and fine-tune access levels for different user profiles.
- Efficient Auditing and Compliance: The ability to export permission set information to Excel facilitates auditing and compliance efforts, making it easier to review and share permission details with stakeholders.
- Enhanced User Experience: The client-side Excel generation and dynamic data loading ensure a smoother user experience, reducing wait times and improving overall usability.
- Customizable and Extendable: The LWC component can be easily customized and extended to add more features or integrate with other Salesforce functionalities.
The code
Prerequisites
To follow along with this tutorial, you’ll need the following prerequisites:
- Salesforce Developer Account: You need access to a Salesforce developer account to create custom LWC components and Apex classes.
- Basic Understanding of Salesforce: Familiarity with Salesforce platform concepts such as objects, fields, profiles, and permission sets will be helpful.
- Salesforce CLI: Install Salesforce CLI to create and deploy Lightning Web Components.
- Text Editor: Use your preferred text editor for writing and editing code.
Step 1: Add Sheet JS Static Resource in Salesforce Org
Download https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js
DO NOT “COPY AND PASTE”! The file should be explicitly downloaded. Copying and pasting corrupts the source code and the component will fail in subtle ways. The easiest approach is to right-click the link and select “Save Link As…”
Setup > Home > Search ‘Static Resource’ > Click on ‘New’ button > Add a static resource

Step 2: Creating the Permission Set Explorer Component
Let’s begin by creating the “Permission Set Explorer” component in Salesforce. Follow these steps:
- Open your Salesforce Developer Console or any text editor of your choice.
- Create a new Lightning Web Component with the name “PermissionSetExplorer.”
Step 3: HTML Structure
In the <strong>PermissionSetExplorer.html</strong>
file, we’ll define the HTML structure for our component. The component will consist of a Lightning Card containing a picklist to select a permission set and a button to generate an Excel file. We’ll also display the object-level permissions in a tabular format. Here’s the HTML code:
<template> | |
<lightning-card title="Permission Set Explorer" icon-name="custom:custom6"> | |
<div class="slds-grid slds-gutters"> | |
<div class="slds-col slds-size_6-of-12"> | |
<div class="picklist-container"> | |
<lightning-combobox | |
label="Select Permission Set" | |
options={permissionSetOptions} | |
value={selectedPermissionSet} | |
onchange={handlePermissionSetChange}> | |
</lightning-combobox> | |
</div> | |
</div> | |
<div class="slds-col slds-size_6-of-12 slds-align-end"> | |
<div class="button-container"> | |
<lightning-button | |
variant="brand" | |
label="Generate Excel" | |
title="Generate Excel" | |
onclick={handleGenerateExcel}> | |
</lightning-button> | |
</div> | |
</div> | |
</div> | |
<template if:true={isLoading}> | |
<div class="slds-align_absolute-center"> | |
<lightning-spinner alternative-text="Loading" size="small"></lightning-spinner> | |
</div> | |
</template> | |
<div class="slds-m-around_medium slds-table_header-fixed_container" style="height: 300px; overflow-y: auto;"> | |
<table class="slds-table slds-table_bordered slds-table_fixed-layout"> | |
<thead> | |
<tr class="slds-line-height_reset"> | |
<th class="slds-text-title_caps">Object Name</th> | |
<th class="slds-text-title_caps">Read</th> | |
<th class="slds-text-title_caps">Create</th> | |
<th class="slds-text-title_caps">Edit</th> | |
<th class="slds-text-title_caps">Delete</th> | |
<th class="slds-text-title_caps">View All</th> | |
<th class="slds-text-title_caps">Modify All</th> | |
</tr> | |
</thead> | |
</table> | |
<div style="max-height: 260px; overflow-y: auto;"> | |
<table class="slds-table slds-table_bordered slds-table_fixed-layout"> | |
<tbody> | |
<template for:each={objectPermissions} for:item="permission" for:index="index"> | |
<tr key={permission.Id}> | |
<td class="slds-truncate" title={permission.SObjectType}>{permission.SObjectType}</td> | |
<td> | |
<template if:true={permission.PermissionsRead}> | |
<lightning-icon icon-name="utility:success" alternative-text="TRUE" title="TRUE" size="xx-small"></lightning-icon> | |
</template> | |
<template if:false={permission.PermissionsRead}> | |
<lightning-icon icon-name="utility:close" alternative-text="FALSE" title="FALSE" size="xx-small"></lightning-icon> | |
</template> | |
</td> | |
<td> | |
<template if:true={permission.PermissionsCreate}> | |
<lightning-icon icon-name="utility:success" alternative-text="TRUE" title="TRUE" size="xx-small"></lightning-icon> | |
</template> | |
<template if:false={permission.PermissionsCreate}> | |
<lightning-icon icon-name="utility:close" alternative-text="FALSE" title="FALSE" size="xx-small"></lightning-icon> | |
</template> | |
</td> | |
<td> | |
<template if:true={permission.PermissionsEdit}> | |
<lightning-icon icon-name="utility:success" alternative-text="TRUE" title="TRUE" size="xx-small"></lightning-icon> | |
</template> | |
<template if:false={permission.PermissionsEdit}> | |
<lightning-icon icon-name="utility:close" alternative-text="FALSE" title="FALSE" size="xx-small"></lightning-icon> | |
</template> | |
</td> | |
<td> | |
<template if:true={permission.PermissionsDelete}> | |
<lightning-icon icon-name="utility:success" alternative-text="TRUE" title="TRUE" size="xx-small"></lightning-icon> | |
</template> | |
<template if:false={permission.PermissionsDelete}> | |
<lightning-icon icon-name="utility:close" alternative-text="FALSE" title="FALSE" size="xx-small"></lightning-icon> | |
</template> | |
</td> | |
<td> | |
<template if:true={permission.PermissionsViewlAll}> | |
<lightning-icon icon-name="utility:success" alternative-text="TRUE" title="TRUE" size="xx-small"></lightning-icon> | |
</template> | |
<template if:false={permission.PermissionsViewlAll}> | |
<lightning-icon icon-name="utility:close" alternative-text="FALSE" title="FALSE" size="xx-small"></lightning-icon> | |
</template> | |
</td> | |
<td> | |
<template if:true={permission.PermissionsModifyAll}> | |
<lightning-icon icon-name="utility:success" alternative-text="TRUE" title="TRUE" size="xx-small"></lightning-icon> | |
</template> | |
<template if:false={permission.PermissionsModifyAll}> | |
<lightning-icon icon-name="utility:close" alternative-text="FALSE" title="FALSE" size="xx-small"></lightning-icon> | |
</template> | |
</td> | |
</tr> | |
</template> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<!-- Footer section --> | |
<footer class="slds-m-around_medium"> | |
<p>Developed by <a href="https://sfdclesson.com/" target="_blank">SFDCLesson</a></p> | |
</footer> | |
</lightning-card> | |
</template> |
In this HTML code, we define the layout of the “Permission Set Explorer” component. It includes a Lightning Card that contains a picklist to select a permission set, a button to generate an Excel file, a spinner to indicate data loading, and a table to display object-level permissions.
Step 4: JavaScript Logic
Now, let’s add the JavaScript logic for our component in the <strong>PermissionSetExplorer.js</strong>
file. The JavaScript code will handle the following functionalities:
- Fetching the list of available permission sets using Apex method
getPermissionSets
. - Fetching the object-level permissions for the selected permission set using Apex method
getPermissions
. - Preparing and displaying the data in the table format.
- Generating an Excel file with the permission set details for download.
Here’s the JavaScript code:
import { LightningElement, wire ,track} from 'lwc'; | |
import getPermissionSets from '@salesforce/apex/PermissionSetController.getPermissionSets'; | |
import getPermissions from '@salesforce/apex/PermissionSetController.getPermissions'; | |
import { loadScript, loadStyle } from 'lightning/platformResourceLoader'; | |
import SheetJS from '@salesforce/resourceUrl/SheetJS'; // The static resource for SheetJS | |
export default class PermissionsExplorer extends LightningElement { | |
selectedPermissionSet; | |
isLoading = false; | |
permissionSetOptions = []; | |
@track objectPermissions = []; | |
async connectedCallback() { | |
await loadScript(this, SheetJS); // load the library | |
// At this point, the library is accessible with the `XLSX` variable | |
this.version = XLSX.version; | |
console.log('version: '+this.version); | |
} | |
@wire(getPermissionSets) | |
wiredPermissionSets({ error, data }) { | |
if (data) { | |
this.permissionSetOptions = data.map((item) => { | |
return { label: item.Name, value: item.Id }; | |
}); | |
} else if (error) { | |
console.error('Error fetching permission sets: ', error); | |
} | |
} | |
handlePermissionSetChange(event) { | |
this.selectedPermissionSet = event.detail.value; | |
if (this.selectedPermissionSet) { | |
this.isLoading = true; // Show the spinner when the user selects a permission set | |
this.fetchObjectPermissions(this.selectedPermissionSet); | |
} else { | |
this.objectPermissions = []; | |
} | |
} | |
fetchObjectPermissions(permissionSetId) { | |
getPermissions({ permissionSetId: permissionSetId }) | |
.then((data) => { | |
this.objectPermissions = this.prepareObjectPermissions(data); | |
this.isLoading = false; // Hide the spinner when data is fetched successfully | |
}) | |
.catch((error) => { | |
console.error('Error fetching permissions: ', error); | |
this.isLoading = false; // Hide the spinner when data is fetched successfully | |
}); | |
} | |
prepareObjectPermissions(data) { | |
let preparedData = []; | |
for (const objectName in data) { | |
if (data.hasOwnProperty(objectName)) { | |
const sheetData = data[objectName]; | |
for (const record of sheetData) { | |
preparedData.push({ | |
SObjectType: record.SObjectType, | |
PermissionsRead: record.PermissionsRead, | |
PermissionsCreate: record.PermissionsCreate, | |
PermissionsEdit: record.PermissionsEdit, | |
PermissionsDelete: record.PermissionsDelete, | |
PermissionsViewlAll: record.PermissionsViewlAll, | |
PermissionsModifyAll: record.PermissionsModifyAll, | |
}); | |
} | |
} | |
} | |
return preparedData; | |
} | |
handleGenerateExcel() { | |
getPermissions({ permissionSetId: this.selectedPermissionSet }) | |
.then((data) => { | |
this.downloadExcel(data); | |
}) | |
.catch((error) => { | |
console.error('Error fetching permissions: ', error); | |
}); | |
} | |
downloadExcel(data) { | |
const filename = 'PermissionSetPermissions.xlsx'; | |
const workbook = XLSX.utils.book_new(); | |
const headers = []; | |
const worksheetData = []; | |
for (const objectName in data) { | |
if (data.hasOwnProperty(objectName)) { | |
const sheetData = data[objectName]; | |
for (const record of sheetData) { | |
worksheetData.push({ | |
"Object": record.SObjectType, | |
"Read": record.PermissionsRead, | |
"Create":record.PermissionsCreate, | |
"Edit": record.PermissionsEdit, | |
"Delete": record.PermissionsDelete, | |
"View All": record.PermissionsViewlAll, | |
"Modify All": record.PermissionsModifyAll, | |
}); | |
} | |
} | |
} | |
const worksheet = XLSX.utils.json_to_sheet(worksheetData, { header: headers }); | |
XLSX.utils.book_append_sheet(workbook, worksheet, 'Object Permissions'); | |
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }); | |
const blob = new Blob([excelBuffer], { type: 'application/octet-stream' }); | |
// Create a download link and click it programmatically to initiate the download | |
const a = document.createElement('a'); | |
a.href = URL.createObjectURL(blob); | |
a.download = filename; | |
a.click(); | |
// Release the object URL to free up memory | |
URL.revokeObjectURL(a.href); | |
} | |
} |
In this JavaScript code, we have implemented methods to fetch permission sets, object-level permissions, and generate an Excel file for download.
Step 5: Apex Controller
To retrieve data from Salesforce, we need to create an Apex controller. Create an Apex class named <strong>PermissionSetController</strong>
with the following code:
public with sharing class PermissionSetController { | |
@AuraEnabled(cacheable=true) | |
public static List<PermissionSetInfo> getPermissionSets() { | |
List<PermissionSet> permissionSets = [SELECT Id, Name FROM PermissionSet ORDER BY Name]; | |
return convertToPermissionSetInfo(permissionSets); | |
} | |
@AuraEnabled | |
public static Map<String, List<PermissionSetPermissionInfo>> getPermissions(String permissionSetId) { | |
Map<String, List<PermissionSetPermissionInfo>> objectPermissionsMap = new Map<String, List<PermissionSetPermissionInfo>>(); | |
// Get the related ObjectPermissions for the given PermissionSet Id | |
List<ObjectPermissions> objectPermissions = [ | |
SELECT ParentId, Parent.Name, Parent.Label, SObjectType, PermissionsCreate, PermissionsDelete, | |
PermissionsModifyAllRecords, PermissionsEdit, PermissionsRead, PermissionsViewAllRecords | |
FROM ObjectPermissions | |
WHERE ParentId = :permissionSetId | |
]; | |
// Group the permissions by SObjectType (object name) | |
for (ObjectPermissions objPerm : objectPermissions) { | |
String objectName = objPerm.SObjectType; | |
if (!objectPermissionsMap.containsKey(objectName)) { | |
objectPermissionsMap.put(objectName, new List<PermissionSetPermissionInfo>()); | |
} | |
PermissionSetPermissionInfo permInfo = new PermissionSetPermissionInfo(); | |
permInfo.ParentName = objPerm.Parent.Name; | |
permInfo.ParentLabel = objPerm.Parent.Label; | |
permInfo.SObjectType = objectName; | |
permInfo.PermissionsCreate = objPerm.PermissionsCreate; | |
permInfo.PermissionsEdit = objPerm.PermissionsEdit; | |
permInfo.PermissionsRead = objPerm.PermissionsRead; | |
permInfo.PermissionsDelete = objPerm.PermissionsDelete; | |
permInfo.PermissionsViewlAll = objPerm.PermissionsViewAllRecords; | |
permInfo.PermissionsModifyAll = objPerm.PermissionsModifyAllRecords; | |
objectPermissionsMap.get(objectName).add(permInfo); | |
} | |
return objectPermissionsMap; | |
} | |
public static List<PermissionSetInfo> convertToPermissionSetInfo(List<PermissionSet> permissionSets) { | |
List<PermissionSetInfo> permissionSetInfoList = new List<PermissionSetInfo>(); | |
for (PermissionSet ps : permissionSets) { | |
PermissionSetInfo psi = new PermissionSetInfo(); | |
psi.Id = ps.Id; | |
psi.Name = ps.Name; | |
permissionSetInfoList.add(psi); | |
} | |
return permissionSetInfoList; | |
} | |
public class PermissionSetInfo { | |
@AuraEnabled public Id Id; | |
@AuraEnabled public String Name; | |
} | |
public class PermissionSetPermissionInfo { | |
@AuraEnabled public String ParentName; | |
@AuraEnabled public String ParentLabel; | |
@AuraEnabled public String SObjectType; | |
@AuraEnabled public Boolean PermissionsEdit; | |
@AuraEnabled public Boolean PermissionsCreate; | |
@AuraEnabled public Boolean PermissionsRead; | |
@AuraEnabled public Boolean PermissionsDelete; | |
@AuraEnabled public Boolean PermissionsViewlAll; | |
@AuraEnabled public Boolean PermissionsModifyAll; | |
} | |
} |
In this Apex class, we have implemented two methods: getPermissionSets
to retrieve the list of permission sets and getPermissions
to retrieve object-level permissions for the selected permission set.
Step 6: Styling
To make the component visually appealing, we can add CSS styling. Create a new file named <strong>PermissionSetExplorer.css</strong>
and add the following CSS code:
.picklist-container{ | |
left: 3%; | |
position: relative; | |
} | |
.button-container{ | |
top: 42%; | |
position: relative; | |
} |
Step 7: Deploying the Component
Now that we have built the “Permission Set Explorer” LWC component and the associated Apex controller, it’s time to deploy them to your Salesforce org.
- Use Salesforce CLI to deploy the component and Apex class to your org.
- Once deployed, create a Lightning App Page and add the “Permission Set Explorer” component to the page.
Conclusion
In this blog post, we have learned how to build a custom Lightning Web Component called “Permission Set Explorer” in Salesforce. This component provides an intuitive user interface to explore and manage permission sets efficiently. Users can select a permission set, view its object-level permissions, and generate an Excel file for download. By following this tutorial, you can empower administrators to easily manage user permissions in your Salesforce org.
Happy Salesforce development!
About the blog
SFDCLessons is a blog where you can find various Salesforce tutorials and tips that we have written to help beginners and experienced developers alike. we also share my experience and knowledge on Salesforce best practices, troubleshooting, and optimization. Don’t forget to follow us on:
Newsletter
Subscribe to our email newsletter to be notified when a new post is published.