COMING SOON: Fall 2024 Release
By Thomas Winter | 2021 Jan 12
6 min
Tags
tutorial
salesforce
view
edit
redaction
At Apryse, many clients approached us looking for an end-to-end document solution for their Salesforce applications—one that could handle their most commonly used document file types and integrate seamlessly within their existing workflows.
Well, we've listened. Now you can view, edit, and annotate all your attached record files, including MS Office documents, right in a Salesforce application, using our expanded WebViewer integration. This article shows you how to easily integrate, customize, and extend such a solution.
Learn more about Apryse's PDF and DOCX editor functionality.
To get started with WebViewer and Salesforce, first clone our sample Lightning Web Component project from GitHub.
# Via HTTPS
git clone https://github.com/PDFTron/salesforce-webviewer-attachments.git
# Via SSH
git clone git@github.com:PDFTron/salesforce-webviewer-attachments.git
We recommend using Salesforce DX when deploying this application to your sandbox or scratch org. If you are unfamiliar with the tool, we recommend the Quick Start: Salesforce DX Trailhead Project or the App Development with Salesforce DX Trailhead module. Important steps include:
In this guide we are using the SFDX CLI, so here are some common parameters we will be using:
-a // sets the given alias name
-f // find a file using file path
-r // go to given url
-s // sets the scratch org as default org
Next, you need to clone our sample webviewer-salesforce-attachments
project, configure it accordingly, and get it up and running. Follow these steps:
1. Clone the salesforce-webviewer-attachments
GitHub repo:
# Via HTTPS
git clone https://github.com/PDFTron/salesforce-webviewer-attachments.git
# Via SSH
git clone git@github.com:PDFTron/salesforce-webviewer-attachments.git
cd webviewer-salesforce-attachments
2. You can add your WebViewer license key in staticresources/myfiles/config.js
file or add it in WebViewer constructor by passing l: "LICENSE_KEY"
option.
WebViewer({
l: ‘LICENSE_KEY’
})
3. If you haven’t done so, authenticate your org and provide it with an alias assigning DevHub as an alias reference and a url to direct to the org login page. Execute the following command as is from your terminal (Unix/macOS) or cmd
(Windows).
sfdx force:auth:web:login -a DevHub -r https://login.salesforce.com
Alternatively, you can authenticate from VS Code:
4. Enter your org credentials in the browser that opens (for sandboxes, you can advance to step 7). If you would like to use a scratch org, open Settings and type dev hub
in the quick find search. Make sure the toggle is set to enabled as shown in the picture below.
5. Create a scratch org using the config/project-scratch-def.json
file, sets the scratch org as default org, and assign it an alias [alias name]
. Alternatively, you can also deploy to your sandbox and skip this step.
sfdx force:org:create -s -f config/project-scratch-def.json -a [alias-name]
6. Push the app to your org:
sfdx force:source:push
7. Enter some dummy data:
sfdx force:data:record:create -s Account -v "Name='Marriott Marquis' BillingStreet='780 Mission St' BillingCity='San Francisco' BillingState='CA' BillingPostalCode='94103' Phone='(415) 896-1600' Website='www.marriott.com'"
8. Open the org:
sfdx force:org:open -u [alias name]
9. Navigate to the dummy data that you would like to use for editing your record files. Click on the gear wheel and select ‘Edit Page’, which opens the Lightning App Builder.
10. Now select where you would like to launch the Lightning Web Component from and drag and drop the pdftronWebviewerContainer component there.
Other Apryse LWC make up pdftronWebviewerContainer
To send the current record Id to your Lightning Web Component, include the following: <target>lightning__RecordPage</target>
in your js-meta.xml file like so:
<!-- pdftronWebviewerContainer.js-meta.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>50.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
Now you can use the record Id in your JS like so:
import { LightningElement, api } from 'lwc';
export default class PdftronWebviewerContainer extends LightningElement {
@api recordId;
}
You can also pass the record Id to any child components:
<!--- pdftronWebviewerContainer.html --->
<template>
<lightning-card>
<div id="cardContainer" class="slds-p-around_x-small">
<div id="comboboxContainer">
<c-pdftron-attachment-picker-combobox record-id={recordId}>
</c-pdftron-attachment-picker-combobox>
</div>
<div id="wvContainer">
<c-pdftron-wv-instance record-id={recordId}></c-pdftron-wv-instance>
</div>
</div>
</lightning-card>
</template>
We'll be retrieving the attachment imperatively:
//shortened for brevity
import { LightningElement, track, wire, api } from 'lwc';
import getAttachments from '@salesforce/apex/PDFTron_ContentVersionController.getExistingAttachments'
export default class PdftronAttachmentPickerCombobox extends LightningElement {
@api recordId;
@track attachments = []
getAttachments({ recordId: this.recordId })
.then(data => {
this.attachments = data
this.initLookupDefaultResults()
this.error = undefined
this.loadFinished = true
this.documentsRetrieved = true
})
.catch(error => {
console.error(error)
this.showNotification('Error', error, 'error')
this.error = error
})
}
Next, in your Apex Controller, you can use the following query to get the records' ContentDocumentLink
record, which in turn, can be used to gather the ContentVersion
records like so:
// PDFTron_ContentVersionController.cls - shortened for brevity
@AuraEnabled
public static List<ContentVersionWrapper> getAttachments(String recordId){
try {
List<String> cdIdList = new List<String> ();
List<ContentVersionWrapper> cvwList = new List<ContentVersionWrapper> ();
//Find links between record & document
for(ContentDocumentLink cdl :
[ SELECT id, ContentDocumentId, ContentDocument.LatestPublishedVersionId
FROM ContentDocumentLink
WHERE LinkedEntityId = :recordId ]) {
cdIdList.add(cdl.ContentDocumentId);
}
//Use links to get attachments
for(ContentVersion cv :
[ SELECT Id, Title,FileExtension, VersionData, ContentDocumentId
FROM ContentVersion
WHERE ContentDocumentId IN :cdIdList
AND IsLatest = true ]) {
if(checkFileExtension(cv.FileExtension)) {
cvwList.add(new ContentVersionWrapper(cv, false));
}
}
return cvwList;
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
You can include a wrapper class to pass relevant data to your LWC like so:
// shortened for brevity
public class ContentVersionWrapper {
@AuraEnabled
public String name {get; set;}
@AuraEnabled
public String body {get; set;}
public ContentVersionWrapper(ContentVersion contentVer) {
this.name = contentVer.Title + '.' + contentVer.FileExtension;
this.body = EncodingUtil.base64Encode(contentVer.VersionData);
}
}
@AuraEnabled
public static ContentVersionWrapper getBase64FromCv(String recordId) {
try {
ContentVersion cv = [SELECT Id, Title,FileExtension, VersionData FROM ContentVersion WHERE Id = :recordId AND IsLatest = true LIMIT 1];
return new ContentVersionWrapper(cv, true);
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
Check out the following guides to learn more about how to enable communication between your Lightning Web Component and WebViewer.
Salesforce’s multi-tenant architecture provisions each org with governor limits to ensure resources are shared appropriately. To maximize the effectiveness of your workflow, you should factor in these limits when designing your solution.
You may run into these limits depending on your workflow when using the client version of WebViewer. The most common limiting factors include the maximum heap size limit of 6MB(sync)/12MB(async) which are often exceeded when loading files larger than 30-50MB.
Also, make sure your organization meets your storage needs according to the data storage allocations:
To allow for larger file processing, you need to ensure your allocated heap memory capacity will not get exceeded. Check this link to look at a few ways of doing this recommended by Salesforce.
The sample repository uses these guidelines to ensure heap memory is used effectively. An example includes iterating through your SOQL queries like so:
// Find links between record & document
for (ContentDocumentLink cdl :
[ SELECT id, ContentDocumentId, ContentDocument.LatestPublishedVersionId
FROM ContentDocumentLink
WHERE LinkedEntityId = :recordId
]) {
cdIdList.add(cdl.ContentDocumentId);
}
Optimizing the original WebViewer source code for the Salesforce platform means that we will also have to set a few paths in config.js
in order for WebViewer to function properly.
You can now start adding different features to your viewer:
To learn more about this integration, visit our Salesforce documentation section or watch our previously recorded webinar on integrating WebViewer into Salesforce.
If you have any questions about implementing WebViewer in Salesforce, please feel free to get in touch and we will be happy to help!
Tags
tutorial
salesforce
view
edit
redaction
Thomas Winter
Related Products
Share this post
PRODUCTS
Enterprise
Small Business
Popular Content