├── .eslintignore ├── .forceignore ├── .gitignore ├── .prettierignore ├── README.md ├── config └── project-scratch-def.json ├── force-app └── main │ └── default │ ├── applications │ └── PDFTron.app-meta.xml │ ├── classes │ ├── LookupSearchResult.cls │ ├── LookupSearchResult.cls-meta.xml │ ├── LookupSearchResultTests.cls │ ├── LookupSearchResultTests.cls-meta.xml │ ├── PDFTron_ContentVersionController.cls │ └── PDFTron_ContentVersionController.cls-meta.xml │ ├── contentassets │ ├── logoaprysesquare.asset │ └── logoaprysesquare.asset-meta.xml │ ├── flexipages │ ├── File_Browser.flexipage-meta.xml │ ├── PDFTron_UtilityBar.flexipage-meta.xml │ └── WebViewer.flexipage-meta.xml │ ├── lwc │ ├── .eslintrc.json │ ├── jsconfig.json │ ├── lookup │ │ ├── lookup.css │ │ ├── lookup.html │ │ ├── lookup.js │ │ └── lookup.js-meta.xml │ ├── pdftronAttachmentPickerCombobox │ │ ├── pdftronAttachmentPickerCombobox.html │ │ ├── pdftronAttachmentPickerCombobox.js │ │ └── pdftronAttachmentPickerCombobox.js-meta.xml │ ├── pdftronFilePickerCombobox │ │ ├── pdftronFilePickerCombobox.html │ │ ├── pdftronFilePickerCombobox.js │ │ └── pdftronFilePickerCombobox.js-meta.xml │ ├── pdftronTabViewer │ │ ├── pdftronTabViewer.html │ │ ├── pdftronTabViewer.js │ │ └── pdftronTabViewer.js-meta.xml │ ├── pdftronWebViewer │ │ ├── pdftronWebViewer.css │ │ ├── pdftronWebViewer.html │ │ ├── pdftronWebViewer.js │ │ └── pdftronWebViewer.js-meta.xml │ ├── pdftronWebviewerContainer │ │ ├── pdftronWebviewerContainer.html │ │ ├── pdftronWebviewerContainer.js │ │ └── pdftronWebviewerContainer.js-meta.xml │ ├── pdftronWvFileBrowserComponent │ │ ├── pdftronWvFileBrowserComponent.html │ │ ├── pdftronWvFileBrowserComponent.js │ │ └── pdftronWvFileBrowserComponent.js-meta.xml │ ├── pdftronWvInstance │ │ ├── mimeTypes.js │ │ ├── pdftronWvInstance.css │ │ ├── pdftronWvInstance.html │ │ ├── pdftronWvInstance.js │ │ └── pdftronWvInstance.js-meta.xml │ └── pubsub │ │ ├── pubsub.html │ │ ├── pubsub.js │ │ └── pubsub.js-meta.xml │ ├── messageChannels │ └── LMSWebViewer.messageChannel-meta.xml │ ├── profiles │ └── Admin.profile-meta.xml │ ├── staticresources │ ├── content_edit.resource-meta.xml │ ├── content_edit.zip │ ├── content_edit_resource.resource-meta.xml │ ├── content_edit_resource.zip │ ├── external.resource-meta.xml │ ├── external.zip │ ├── font_assets.resource-meta.xml │ ├── font_assets.zip │ ├── legacyOffice.resource-meta.xml │ ├── legacyOffice.zip │ ├── legacyOffice_asm.resource-meta.xml │ ├── legacyOffice_asm.zip │ ├── legacyOffice_resource.resource-meta.xml │ ├── legacyOffice_resource.zip │ ├── lib.resource-meta.xml │ ├── lib.zip │ ├── myfiles.resource-meta.xml │ ├── myfiles │ │ ├── compressed.tracemonkey-pldi-09.pdf │ │ ├── config.js │ │ ├── config_apex.js │ │ ├── visualforce-page-viewing.js │ │ ├── webviewer-demo-annotated.pdf │ │ ├── webviewer-demo-annotated.xod │ │ └── word.docx │ ├── office.resource-meta.xml │ ├── office.zip │ ├── office_asm.resource-meta.xml │ ├── office_asm.zip │ ├── office_edit.resource-meta.xml │ ├── office_edit.zip │ ├── office_resource.resource-meta.xml │ ├── office_resource.zip │ ├── pdf_full.resource-meta.xml │ ├── pdf_full.zip │ ├── resource.resource-meta.xml │ └── resource.zip │ └── tabs │ ├── File_Browser.tab-meta.xml │ └── WebViewer.tab-meta.xml ├── misc ├── app_launcher.png ├── files.png ├── pdftron_app.png └── webviewer.png └── sfdx-project.json /.eslintignore: -------------------------------------------------------------------------------- 1 | **/lwc/**/*.css 2 | **/lwc/**/*.html 3 | **/lwc/**/*.json 4 | **/lwc/**/*.svg 5 | **/lwc/**/*.xml 6 | **/aura/**/*.auradoc 7 | **/aura/**/*.cmp 8 | **/aura/**/*.css 9 | **/aura/**/*.design 10 | **/aura/**/*.evt 11 | **/aura/**/*.json 12 | **/aura/**/*.svg 13 | **/aura/**/*.tokens 14 | **/aura/**/*.xml 15 | .sfdx -------------------------------------------------------------------------------- /.forceignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status 2 | # More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm 3 | # 4 | 5 | package.xml 6 | 7 | # LWC configuration files 8 | **/jsconfig.json 9 | **/.eslintrc.json 10 | 11 | # LWC Jest 12 | **/__tests__/** 13 | **/tsconfig.json 14 | 15 | **/*.ts 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used for Git repositories to specify intentionally untracked files that Git should ignore. 2 | # If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore 3 | # For useful gitignore templates see: https://github.com/github/gitignore 4 | 5 | # Salesforce cache 6 | .sfdx/ 7 | .localdevserver/ 8 | 9 | # LWC VSCode autocomplete 10 | **/lwc/jsconfig.json 11 | 12 | # LWC Jest coverage reports 13 | coverage/ 14 | 15 | # SOQL Query Results 16 | **/scripts/soql/query-results 17 | 18 | # Logs 19 | logs 20 | *.log 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # Dependency directories 26 | node_modules/ 27 | 28 | # Eslint cache 29 | .eslintcache 30 | 31 | # MacOS system files 32 | .DS_Store 33 | 34 | # Windows system files 35 | Thumbs.db 36 | ehthumbs.db 37 | [Dd]esktop.ini 38 | $RECYCLE.BIN/ 39 | 40 | #salesforce local tracking 41 | .sf/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running prettier 2 | # More information: https://prettier.io/docs/en/ignore.html 3 | # 4 | 5 | **/staticresources/** 6 | .localdevserver 7 | .sfdx 8 | .vscode 9 | 10 | coverage/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to Add a PDFTron WebViewer to Salesforce as a Lightning Web Component 2 | 3 | ⚠️ This sample has been moved to the [webviewer-samples repo](https://github.com/ApryseSDK/webviewer-samples/tree/main/webviewer-salesforce). ⚠️ -------------------------------------------------------------------------------- /config/project-scratch-def.json: -------------------------------------------------------------------------------- 1 | { 2 | "orgName": "your Company", 3 | "edition": "Developer", 4 | "settings": { 5 | "lightningExperienceSettings": { 6 | "enableS1DesktopEnabled": true 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /force-app/main/default/applications/PDFTron.app-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #266DE7 5 | logoaprysesquare 6 | 1 7 | true 8 | 9 | Easy-to-digest code examples for Lightning Web Components 10 | Large 11 | false 12 | false 13 | 14 | Standard 15 | WebViewer 16 | File_Browser 17 | Lightning 18 | PDFTron_UtilityBar 19 | 20 | -------------------------------------------------------------------------------- /force-app/main/default/classes/LookupSearchResult.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * Class used to serialize a single Lookup search result item 3 | * The Lookup controller returns a List when sending search result back to Lightning 4 | */ 5 | public virtual class LookupSearchResult implements Comparable { 6 | protected Id id; 7 | protected String sObjectType; 8 | protected String icon; 9 | protected String title; 10 | protected String subtitle; 11 | 12 | protected LookupSearchResult() { 13 | } 14 | 15 | public LookupSearchResult(Id id, String sObjectType, String icon, String title, String subtitle) { 16 | this.id = id; 17 | this.sObjectType = sObjectType; 18 | this.icon = icon; 19 | this.title = title; 20 | this.subtitle = subtitle; 21 | } 22 | 23 | @AuraEnabled 24 | public Id getId() { 25 | return id; 26 | } 27 | 28 | @AuraEnabled 29 | public String getSObjectType() { 30 | return sObjectType; 31 | } 32 | 33 | @AuraEnabled 34 | public String getIcon() { 35 | return icon; 36 | } 37 | 38 | @AuraEnabled 39 | public String getTitle() { 40 | return title; 41 | } 42 | 43 | @AuraEnabled 44 | public String getSubtitle() { 45 | return subtitle; 46 | } 47 | 48 | /** 49 | * Allow to sort search results based on title 50 | */ 51 | public Integer compareTo(Object compareTo) { 52 | LookupSearchResult other = (LookupSearchResult) compareTo; 53 | if (this.getTitle() == null) { 54 | return (other.getTitle() == null) ? 0 : 1; 55 | } 56 | if (other.getTitle() == null) { 57 | return -1; 58 | } 59 | return this.getTitle().compareTo(other.getTitle()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /force-app/main/default/classes/LookupSearchResult.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 51.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/LookupSearchResultTests.cls: -------------------------------------------------------------------------------- 1 | @isTest(isParallel=true) 2 | public class LookupSearchResultTests { 3 | @isTest 4 | static void compareTo_should_work_with_two_null_titles() { 5 | LookupSearchResult r1 = getSearchResult(null); 6 | LookupSearchResult r2 = getSearchResult(null); 7 | 8 | Integer compareResult = r1.compareTo(r2); 9 | 10 | System.assertEquals(0, compareResult); 11 | } 12 | 13 | @isTest 14 | static void compareTo_should_work_with_this_null_title() { 15 | LookupSearchResult r1 = getSearchResult(null); 16 | LookupSearchResult r2 = getSearchResult('a'); 17 | 18 | Integer compareResult = r1.compareTo(r2); 19 | 20 | System.assertEquals(1, compareResult); 21 | } 22 | 23 | @isTest 24 | static void compareTo_should_work_with_other_null_title() { 25 | LookupSearchResult r1 = getSearchResult('a'); 26 | LookupSearchResult r2 = getSearchResult(null); 27 | 28 | Integer compareResult = r1.compareTo(r2); 29 | 30 | System.assertEquals(-1, compareResult); 31 | } 32 | 33 | @isTest 34 | static void compareTo_should_work_with_non_null_titles() { 35 | LookupSearchResult r1 = getSearchResult('a'); 36 | LookupSearchResult r2 = getSearchResult('b'); 37 | 38 | Integer compareResult = r1.compareTo(r2); 39 | 40 | System.assertEquals(-1, compareResult); 41 | } 42 | 43 | @isTest 44 | static void getters_should_work() { 45 | // For the sake of code coverage 46 | LookupSearchResult r = new LookupSearchResult('0010R00000yvEyRQAU', 'type', 'icon', 'title', 'subtitle'); 47 | 48 | System.assertEquals('0010R00000yvEyRQAU', r.getId()); 49 | System.assertEquals('type', r.getSObjectType()); 50 | System.assertEquals('icon', r.getIcon()); 51 | System.assertEquals('title', r.getTitle()); 52 | System.assertEquals('subtitle', r.getSubtitle()); 53 | } 54 | 55 | private static LookupSearchResult getSearchResult(String title) { 56 | return new LookupSearchResult(null, null, null, title, null); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /force-app/main/default/classes/LookupSearchResultTests.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 51.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/PDFTron_ContentVersionController.cls: -------------------------------------------------------------------------------- 1 | public with sharing class PDFTron_ContentVersionController { 2 | static Set supportedFileFormats = new Set { 3 | 'pdf', 4 | 'xfdf', 5 | 'fdf', 6 | 'doc', 7 | 'docx', 8 | 'xlsx', 9 | 'ppt', 10 | 'pptx', 11 | 'jpg', 12 | 'jpeg', 13 | 'png', 14 | 'mov', 15 | 'tif', 16 | 'tiff', 17 | 'xls', 18 | 'xlsx' 19 | }; 20 | 21 | @AuraEnabled(Cacheable=true) 22 | public static List getContentVersions() { 23 | return [SELECT Id, Title, FileExtension, IsMajorVersion, IsLatest, VersionData, IsAssetEnabled, LastModifiedDate 24 | FROM ContentVersion WHERE IsLatest = True]; 25 | } 26 | 27 | @RemoteAction 28 | @AuraEnabled(Cacheable=true) 29 | public static Map getFileBlobById(String Id) { 30 | ContentVersion cv = [SELECT Id, Title, FileExtension, IsMajorVersion, IsLatest, VersionData, IsAssetEnabled FROM ContentVersion WHERE Id = :Id]; 31 | // Returns string 32 | Map response = new Map(); 33 | response.put('Title', cv.Title); 34 | response.put('FileExtension', cv.FileExtension); 35 | response.put('Content', EncodingUtil.base64Encode(cv.VersionData)); 36 | 37 | return response; 38 | } 39 | 40 | @AuraEnabled 41 | public static string getUser() { 42 | try { 43 | User currentUser = [SELECT FirstName,LastName 44 | From User 45 | WHERE username = :UserInfo.getUsername()]; 46 | 47 | return currentUser.FirstName + ' ' + currentUser.LastName; 48 | } catch (Exception e) { 49 | throw new AuraHandledException(e.getMessage()); 50 | } 51 | } 52 | 53 | @AuraEnabled 54 | public static ContentVersionWrapper getBase64FromCv(String recordId) { 55 | try { 56 | ContentVersion cv = [SELECT Id, Title,FileExtension, VersionData FROM ContentVersion WHERE Id = :recordId AND IsLatest = true LIMIT 1]; 57 | return new ContentVersionWrapper(cv, true); 58 | } catch (Exception e) { 59 | throw new AuraHandledException(e.getMessage()); 60 | } 61 | } 62 | 63 | @AuraEnabled 64 | public static String saveDocument(String json, String recordId, String cvId) { 65 | try { 66 | //parse annotated document payload and create new ContentVersion 67 | PDFTron_ContentVersionPayload pl = new PDFTron_ContentVersionPayload(json); 68 | ContentVersion annotatedCv = new ContentVersion(); 69 | 70 | //retrieve original document before annotation 71 | List cvList = [SELECT ContentDocumentId FROM ContentVersion WHERE Id = :cvId]; 72 | 73 | if(cvList.size() == 1) { 74 | //retrieve and delete original document 75 | List originalDocument = [SELECT Id FROM ContentDocument WHERE Id = :cvList[0].ContentDocumentId]; 76 | 77 | if(originalDocument.size() > 0) delete originalDocument; 78 | } else { 79 | System.debug('Could not retrieve record for ' + cvId); 80 | } 81 | //build document blob 82 | annotatedCv.VersionData = EncodingUtil.base64Decode(pl.base64Data); 83 | annotatedCv.Title = pl.title; 84 | annotatedCv.PathOnClient = pl.filename; 85 | 86 | insert annotatedCv; 87 | 88 | if(annotatedCv.ContentDocumentId == null && String.isNotBlank(recordId)) { 89 | //get the content version since ContentDocumentId is defined after insert 90 | List contentVersions = [SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id =: annotatedCv.Id LIMIT 1]; 91 | 92 | //link record with document 93 | ContentDocumentLink contentDocumentLink = new ContentDocumentLink(LinkedEntityId = recordId, ContentDocumentId = contentVersions[0].ContentDocumentId, ShareType = 'V'); 94 | insert contentDocumentLink; 95 | } 96 | 97 | return annotatedCv.Id; 98 | } catch (Exception e) { 99 | system.debug('### ERROR: ' + e.getMessage()); 100 | throw new AuraHandledException(e.getMessage()); 101 | } 102 | } 103 | 104 | @AuraEnabled 105 | public static List getAttachments(String recordId){ 106 | try { 107 | List cdIdList = new List (); 108 | List cvwList = new List (); 109 | 110 | //Find links between record & document 111 | for(ContentDocumentLink cdl : 112 | [ SELECT id, ContentDocumentId, ContentDocument.LatestPublishedVersionId 113 | FROM ContentDocumentLink 114 | WHERE LinkedEntityId = :recordId ]) { 115 | cdIdList.add(cdl.ContentDocumentId); 116 | } 117 | //Use links to get attachments 118 | for(ContentVersion cv : 119 | [ SELECT Id, Title,FileExtension, VersionData, ContentDocumentId 120 | FROM ContentVersion 121 | WHERE ContentDocumentId IN :cdIdList 122 | AND IsLatest = true ]) { 123 | if(checkFileExtension(cv.FileExtension)) { 124 | cvwList.add(new ContentVersionWrapper(cv, false)); 125 | } 126 | } 127 | return cvwList; 128 | } catch (Exception e) { 129 | throw new AuraHandledException(e.getMessage()); 130 | } 131 | } 132 | 133 | //get single file from Id 134 | @AuraEnabled(Cacheable=true) 135 | public static ContentVersionWrapper getFileDataFromId(String Id) { 136 | try { 137 | Long max = 1024 * 1024 * 25; // kb - mb - 6 MB 138 | ContentVersion cv = [SELECT Title, FileExtension, ContentSize, VersionData, ContentDocumentId, LastModifiedDate FROM ContentVersion WHERE Id = :Id]; 139 | if(cv.ContentSize > max) { 140 | throw new PDFTronException('Your file size must not exceed ' + convertBytesToFormattedString(max) + ' - current file size: ' + convertBytesToFormattedString(cv.ContentSize)); 141 | } 142 | return new ContentVersionWrapper(cv, true); 143 | } catch(Exception e) { 144 | throw new AuraHandledException(e.getMessage()); 145 | } 146 | } 147 | 148 | @AuraEnabled 149 | public static List getExistingAttachments(String recordId){ 150 | try { 151 | 152 | List cdIdList = new List (); 153 | List results = new List(); 154 | 155 | //Find links between record & document for attachments 156 | for(ContentDocumentLink cdl : 157 | [ SELECT id, ContentDocumentId, ContentDocument.LatestPublishedVersionId 158 | FROM ContentDocumentLink 159 | WHERE LinkedEntityId = :recordId ]) { 160 | cdIdList.add(cdl.ContentDocumentId); 161 | } 162 | //Use links to get attachments 163 | for(ContentVersion cv : 164 | [ SELECT Id, Title,FileExtension, VersionData, ContentDocumentId, LastModifiedDate, LastModifiedBy.Alias, ContentSize 165 | FROM ContentVersion 166 | WHERE ContentDocumentId IN :cdIdList 167 | AND IsLatest = true 168 | ORDER BY LastModifiedDate DESC 169 | LIMIT 10 ]) 170 | { 171 | if(checkFileExtension(cv.FileExtension)) { 172 | String subtitle = 'Modified ' + cv.LastModifiedDate + ' by ' + cv.LastModifiedBy.Alias; 173 | results.add(new LookupSearchResult(cv.Id, 'ContentVersion', getFileLogo(cv.FileExtension), cv.Title + '.' + cv.FileExtension + ' (' + convertBytesToFormattedString(cv.ContentSize) + ')', subtitle)); 174 | } 175 | } 176 | return results; 177 | 178 | } catch (Exception e) { 179 | throw new AuraHandledException(e.getMessage()); 180 | } 181 | } 182 | 183 | @AuraEnabled(Cacheable=true) 184 | public static List search(String searchTerm, List selectedIds) { 185 | searchTerm = '%' + searchTerm + '%'; 186 | 187 | // Prepare results 188 | List results = new List(); 189 | 190 | for (ContentVersion cv : [ 191 | SELECT Title, FileExtension, LastModifiedDate, ContentSize, LastModifiedBy.Alias 192 | FROM ContentVersion 193 | WHERE Title LIKE :searchTerm 194 | ORDER BY LastModifiedDate DESC 195 | ]) { 196 | System.debug(cv); 197 | if(checkFileExtension(cv.FileExtension)) { 198 | String subtitle = 'Modified ' + cv.LastModifiedDate + ' by ' + cv.LastModifiedBy.Alias; 199 | results.add(new LookupSearchResult(cv.Id, 'ContentVersion', getFileLogo(cv.FileExtension), cv.Title + '.' + cv.FileExtension + ' (' + convertBytesToFormattedString(cv.ContentSize) + ')', subtitle)); 200 | } 201 | } 202 | 203 | //results.sort(); 204 | return results; 205 | } 206 | 207 | public static String getFileLogo(String extension) { 208 | String logo = 'doctype:unknown'; 209 | 210 | if(extension == 'pdf') { 211 | logo = 'doctype:pdf'; 212 | } else if(extension == 'docx' || extension == 'doc') { 213 | logo = 'doctype:word'; 214 | } else if(extension == 'xlsx' || extension == 'xls') { 215 | logo = 'doctype:excel'; 216 | } else if(extension == 'tif' || extension == 'jpg' || extension == 'jpeg' || extension == 'png') { 217 | logo = 'doctype:image'; 218 | } 219 | return logo; 220 | } 221 | 222 | //only process blobs from below file formats 223 | public static Boolean checkFileExtension(String extension) { 224 | return supportedFileFormats.contains(extension.toLowerCase()); //compare set of valid extensions with lower case input string 225 | } 226 | 227 | //take long value and output readable string 228 | public static String convertBytesToFormattedString(Long Value) { 229 | if (Value < 1024) { 230 | //no conversion needed 231 | return string.valueOf(Value) + ' Bytes'; 232 | } else if (Value >= 1024 && Value < (1024*1024)) { 233 | //KB 234 | Decimal kb = Decimal.valueOf(Value); 235 | kb = kb.divide(1024,2); 236 | return string.valueOf(kb) + ' KB'; 237 | } else if (Value >= (1024*1024) && Value < (1024*1024*1024)) { 238 | //MB 239 | Decimal mb = Decimal.valueOf(Value); 240 | mb = mb.divide((1024*1024),2); 241 | return string.valueOf(mb) + ' MB'; 242 | } else { 243 | //GB 244 | Decimal gb = Decimal.valueOf(Value); 245 | gb = gb.divide((1024*1024*1024),2); 246 | return string.valueOf(gb) + ' GB'; 247 | } 248 | } 249 | 250 | public class ContentVersionWrapper { 251 | @AuraEnabled 252 | public String name {get; set;} 253 | @AuraEnabled 254 | public String body {get; set;} 255 | @AuraEnabled 256 | public ContentVersion cv; 257 | 258 | public ContentVersionWrapper(ContentVersion contentVer, Boolean encode) { 259 | this.cv = contentVer; 260 | this.name = contentVer.Title + '.' + contentVer.FileExtension; 261 | if(encode) { 262 | this.body = EncodingUtil.base64Encode(contentVer.VersionData); 263 | } 264 | } 265 | } 266 | 267 | public class PDFTron_ContentVersionPayload { 268 | public String title {get; set;} 269 | public String filename {get; set;} 270 | public String base64Data {get; set;} 271 | public String contentDocumentId {get; set;} 272 | 273 | public PDFTron_ContentVersionPayload(String json) { 274 | PDFTron_ContentVersionPayload pl = (PDFTron_ContentVersionPayload) System.JSON.deserialize(json, PDFTron_ContentVersionPayload.class); 275 | this.title = pl.title; 276 | this.filename = pl.filename; 277 | this.base64Data = pl.base64Data; 278 | this.contentDocumentId = pl.contentDocumentId; 279 | } 280 | } 281 | 282 | public class PDFTronException extends Exception {} 283 | } 284 | -------------------------------------------------------------------------------- /force-app/main/default/classes/PDFTron_ContentVersionController.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/contentassets/logoaprysesquare.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/contentassets/logoaprysesquare.asset -------------------------------------------------------------------------------- /force-app/main/default/contentassets/logoaprysesquare.asset-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | en_US 5 | logoaprysesquare 6 | 7 | 8 | VIEWER 9 | 10 | 11 | 12 | 13 | 1 14 | logo-apryse-square.png 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /force-app/main/default/flexipages/File_Browser.flexipage-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | pdftronFilePickerCombobox 7 | 8 | 9 | main 10 | Region 11 | 12 | File Browser 13 | 16 | AppPage 17 | 18 | -------------------------------------------------------------------------------- /force-app/main/default/flexipages/PDFTron_UtilityBar.flexipage-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | utilityItems 5 | Region 6 | 7 | 8 | backgroundComponents 9 | Background 10 | 11 | PDFTron UtilityBar 12 | 15 | UtilityBar 16 | 17 | -------------------------------------------------------------------------------- /force-app/main/default/flexipages/WebViewer.flexipage-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | pdftronFilePickerCombobox 7 | 8 | 9 | region2 10 | Region 11 | 12 | 13 | 14 | 15 | pdftronWebViewerContainer 16 | 17 | 18 | region3 19 | Region 20 | 21 | WebViewer 22 | 25 | AppPage 26 | 27 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@salesforce/eslint-config-lwc/recommended" 3 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "baseUrl": ".", 5 | "paths": { 6 | "c/pdftron-file-picker-combobox": [ 7 | "pdftronFilePickerCombobox/pdftronFilePickerCombobox.js" 8 | ], 9 | "c/pubsub": [ 10 | "pubsub/pubsub.js" 11 | ], 12 | "c/pdftron-web-viewer": [ 13 | "pdftronWebViewer/pdftronWebViewer.js" 14 | ], 15 | "c/pdftronWvFileBrowserComponent": [ 16 | "pdfWvFileBrowserComponent/pdfWvFileBrowserComponent.js" 17 | ], 18 | "c/pdfWvInstance": [ 19 | "pdfWvInstance/pdfWvInstance.js" 20 | ], 21 | "c/pdfWebViewer": [ 22 | "pdfWebViewer/pdfWebViewer.js" 23 | ] 24 | } 25 | }, 26 | "include": [ 27 | "**/*", 28 | "../../../../.sfdx/typings/lwc/**/*.d.ts" 29 | ], 30 | "paths": { 31 | "c/*": [ 32 | "*" 33 | ] 34 | }, 35 | "typeAcquisition": { 36 | "include": [ 37 | "jest" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/lookup/lookup.css: -------------------------------------------------------------------------------- 1 | .slds-combobox__input, 2 | .slds-combobox_container { 3 | transition: border 0.1s linear, box-shadow 0.1 linear; 4 | } 5 | 6 | .slds-combobox__input { 7 | box-shadow: none; 8 | } 9 | 10 | .slds-combobox__input.has-custom-border { 11 | box-shadow: 0 0 0 2px #fff inset, 0 0 0 3px rgb(221, 219, 218) inset; 12 | } 13 | 14 | .slds-combobox__input.has-custom-error { 15 | border: 1px solid rgb(194, 57, 52); 16 | box-shadow: rgb(194, 57, 52) 0 0 0 1px inset; 17 | } 18 | 19 | .slds-combobox_container.has-custom-error { 20 | border: none !important; 21 | } 22 | 23 | .slds-combobox__input.has-custom-height { 24 | height: 32px !important; 25 | } 26 | 27 | .form-error { 28 | color: rgb(194, 57, 52); 29 | display: block; 30 | } 31 | 32 | .result-header{ 33 | font-size: 110%; 34 | padding: 25% 0; 35 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/lookup/lookup.html: -------------------------------------------------------------------------------- 1 | 196 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/lookup/lookup.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, api } from 'lwc'; 2 | import { NavigationMixin } from 'lightning/navigation'; 3 | 4 | const SEARCH_DELAY = 300; // Wait 300 ms after user stops typing then, peform search 5 | 6 | const KEY_ARROW_UP = 38; 7 | const KEY_ARROW_DOWN = 40; 8 | const KEY_ENTER = 13; 9 | 10 | const VARIANT_LABEL_STACKED = 'label-stacked'; 11 | const VARIANT_LABEL_INLINE = 'label-inline'; 12 | const VARIANT_LABEL_HIDDEN = 'label-hidden'; 13 | 14 | const REGEX_SOSL_RESERVED = /(\?|&|\||!|\{|\}|\[|\]|\(|\)|\^|~|\*|:|"|\+|-|\\)/g; 15 | const REGEX_EXTRA_TRAP = /(\$|\\)/g; 16 | 17 | export default class Lookup extends NavigationMixin(LightningElement) { 18 | // Public properties 19 | @api variant = VARIANT_LABEL_STACKED; 20 | @api label = ''; 21 | @api required = false; 22 | @api disabled = false; 23 | @api placeholder = ''; 24 | @api isMultiEntry = false; 25 | @api errors = []; 26 | @api scrollAfterNItems = null; 27 | @api newRecordOptions = []; 28 | @api minSearchTermLength = 2; 29 | 30 | // Template properties 31 | searchResultsLocalState = []; 32 | loading = false; 33 | 34 | // Private properties 35 | _hasFocus = false; 36 | _isDirty = false; 37 | _searchTerm = ''; 38 | _cleanSearchTerm; 39 | _cancelBlur = false; 40 | _searchThrottlingTimeout; 41 | _searchResults = []; 42 | _defaultSearchResults = []; 43 | _curSelection = []; 44 | _focusedResultIndex = null; 45 | 46 | // PUBLIC FUNCTIONS AND GETTERS/SETTERS 47 | @api 48 | set selection(initialSelection) { 49 | this._curSelection = Array.isArray(initialSelection) ? initialSelection : [initialSelection]; 50 | this.processSelectionUpdate(false); 51 | } 52 | 53 | get selection() { 54 | return this._curSelection; 55 | } 56 | 57 | @api 58 | setSearchResults(results) { 59 | // Reset the spinner 60 | this.loading = false; 61 | // Clone results before modifying them to avoid Locker restriction 62 | let resultsLocal = JSON.parse(JSON.stringify(results)); 63 | // Remove selected items from search results 64 | const selectedIds = this._curSelection.map((sel) => sel.id); 65 | resultsLocal = resultsLocal.filter((result) => selectedIds.indexOf(result.id) === -1); 66 | // Format results 67 | const cleanSearchTerm = this._searchTerm.replace(REGEX_SOSL_RESERVED, '.?').replace(REGEX_EXTRA_TRAP, '\\$1'); 68 | const regex = new RegExp(`(${cleanSearchTerm})`, 'gi'); 69 | this._searchResults = resultsLocal.map((result) => { 70 | // Format title and subtitle 71 | if (this._searchTerm.length > 0) { 72 | result.titleFormatted = result.title 73 | ? result.title.replace(regex, '$1') 74 | : result.title; 75 | result.subtitleFormatted = result.subtitle 76 | ? result.subtitle.replace(regex, '$1') 77 | : result.subtitle; 78 | } else { 79 | result.titleFormatted = result.title; 80 | result.subtitleFormatted = result.subtitle; 81 | } 82 | // Add icon if missing 83 | if (typeof result.icon === 'undefined') { 84 | result.icon = 'standard:default'; 85 | } 86 | 87 | result.titleFormatted = `${result.titleFormatted}`; 88 | result.subtitleFormatted = `${result.subtitleFormatted}`; 89 | return result; 90 | }); 91 | // Add local state and dynamic class to search results 92 | this._focusedResultIndex = null; 93 | const self = this; 94 | this.searchResultsLocalState = this._searchResults.map((result, i) => { 95 | return { 96 | result, 97 | state: {}, 98 | get classes() { 99 | let cls = 100 | 'slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta'; 101 | if (self._focusedResultIndex === i) { 102 | cls += ' slds-has-focus'; 103 | } 104 | return cls; 105 | } 106 | }; 107 | }); 108 | } 109 | 110 | @api 111 | getSelection() { 112 | return this._curSelection; 113 | } 114 | 115 | @api 116 | setDefaultResults(results) { 117 | this._defaultSearchResults = [...results]; 118 | if (this._searchResults.length === 0) { 119 | this.setSearchResults(this._defaultSearchResults); 120 | } 121 | } 122 | 123 | // INTERNAL FUNCTIONS 124 | 125 | updateSearchTerm(newSearchTerm) { 126 | this._searchTerm = newSearchTerm; 127 | 128 | // Compare clean new search term with current one and abort if identical 129 | const newCleanSearchTerm = newSearchTerm.trim().replace(REGEX_SOSL_RESERVED, '?').toLowerCase(); 130 | if (this._cleanSearchTerm === newCleanSearchTerm) { 131 | return; 132 | } 133 | 134 | // Save clean search term 135 | this._cleanSearchTerm = newCleanSearchTerm; 136 | 137 | // Ignore search terms that are too small after removing special characters 138 | if (newCleanSearchTerm.replace(/\?/g, '').length < this.minSearchTermLength) { 139 | this.setSearchResults(this._defaultSearchResults); 140 | return; 141 | } 142 | 143 | // Apply search throttling (prevents search if user is still typing) 144 | if (this._searchThrottlingTimeout) { 145 | clearTimeout(this._searchThrottlingTimeout); 146 | } 147 | // eslint-disable-next-line @lwc/lwc/no-async-operation 148 | this._searchThrottlingTimeout = setTimeout(() => { 149 | // Send search event if search term is long enougth 150 | if (this._cleanSearchTerm.length >= this.minSearchTermLength) { 151 | // Display spinner until results are returned 152 | this.loading = true; 153 | 154 | const searchEvent = new CustomEvent('search', { 155 | detail: { 156 | searchTerm: this._cleanSearchTerm, 157 | rawSearchTerm: newSearchTerm, 158 | selectedIds: this._curSelection.map((element) => element.id) 159 | } 160 | }); 161 | this.dispatchEvent(searchEvent); 162 | } 163 | this._searchThrottlingTimeout = null; 164 | }, SEARCH_DELAY); 165 | } 166 | 167 | isSelectionAllowed() { 168 | if (this.isMultiEntry) { 169 | return true; 170 | } 171 | return !this.hasSelection(); 172 | } 173 | 174 | hasSelection() { 175 | return this._curSelection.length > 0; 176 | } 177 | 178 | processSelectionUpdate(isUserInteraction) { 179 | // Reset search 180 | this._cleanSearchTerm = ''; 181 | this._searchTerm = ''; 182 | this.setSearchResults([...this._defaultSearchResults]); 183 | // Indicate that component was interacted with 184 | this._isDirty = isUserInteraction; 185 | // If selection was changed by user, notify parent components 186 | if (isUserInteraction) { 187 | const selectedIds = this._curSelection.map((sel) => sel.id); 188 | this.dispatchEvent(new CustomEvent('selectionchange', { detail: selectedIds })); 189 | } 190 | } 191 | 192 | // EVENT HANDLING 193 | 194 | handleInput(event) { 195 | // Prevent action if selection is not allowed 196 | if (!this.isSelectionAllowed()) { 197 | return; 198 | } 199 | this.updateSearchTerm(event.target.value); 200 | } 201 | 202 | handleKeyDown(event) { 203 | if (this._focusedResultIndex === null) { 204 | this._focusedResultIndex = -1; 205 | } 206 | if (event.keyCode === KEY_ARROW_DOWN) { 207 | // If we hit 'down', select the next item, or cycle over. 208 | this._focusedResultIndex++; 209 | if (this._focusedResultIndex >= this._searchResults.length) { 210 | this._focusedResultIndex = 0; 211 | } 212 | event.preventDefault(); 213 | } else if (event.keyCode === KEY_ARROW_UP) { 214 | // If we hit 'up', select the previous item, or cycle over. 215 | this._focusedResultIndex--; 216 | if (this._focusedResultIndex < 0) { 217 | this._focusedResultIndex = this._searchResults.length - 1; 218 | } 219 | event.preventDefault(); 220 | } else if (event.keyCode === KEY_ENTER && this._hasFocus && this._focusedResultIndex >= 0) { 221 | // If the user presses enter, and the box is open, and we have used arrows, 222 | // treat this just like a click on the listbox item 223 | const selectedId = this._searchResults[this._focusedResultIndex].id; 224 | this.template.querySelector(`[data-recordid="${selectedId}"]`).click(); 225 | event.preventDefault(); 226 | } 227 | } 228 | 229 | handleResultClick(event) { 230 | const recordId = event.currentTarget.dataset.recordid; 231 | 232 | // Save selection 233 | const selectedItem = this._searchResults.find((result) => result.id === recordId); 234 | if (!selectedItem) { 235 | return; 236 | } 237 | const newSelection = [...this._curSelection]; 238 | newSelection.push(selectedItem); 239 | this._curSelection = newSelection; 240 | 241 | // Process selection update 242 | this.processSelectionUpdate(true); 243 | } 244 | 245 | handleComboboxMouseDown(event) { 246 | const mainButton = 0; 247 | if (event.button === mainButton) { 248 | this._cancelBlur = true; 249 | } 250 | } 251 | 252 | handleComboboxMouseUp() { 253 | this._cancelBlur = false; 254 | // Re-focus to text input for the next blur event 255 | this.template.querySelector('input').focus(); 256 | } 257 | 258 | handleFocus() { 259 | // Prevent action if selection is not allowed 260 | if (!this.isSelectionAllowed()) { 261 | return; 262 | } 263 | this._hasFocus = true; 264 | this._focusedResultIndex = null; 265 | } 266 | 267 | handleBlur() { 268 | // Prevent action if selection is either not allowed or cancelled 269 | if (!this.isSelectionAllowed() || this._cancelBlur) { 270 | return; 271 | } 272 | this._hasFocus = false; 273 | } 274 | 275 | handleRemoveSelectedItem(event) { 276 | if (this.disabled) { 277 | return; 278 | } 279 | const recordId = event.currentTarget.name; 280 | this._curSelection = this._curSelection.filter((item) => item.id !== recordId); 281 | // Process selection update 282 | this.processSelectionUpdate(true); 283 | } 284 | 285 | handleClearSelection() { 286 | this._curSelection = []; 287 | this._hasFocus = false; 288 | // Process selection update 289 | this.processSelectionUpdate(true); 290 | } 291 | 292 | handleNewRecordClick(event) { 293 | const objectApiName = event.currentTarget.dataset.sobject; 294 | const selection = this.newRecordOptions.find((option) => option.value === objectApiName); 295 | 296 | const preNavigateCallback = selection.preNavigateCallback 297 | ? selection.preNavigateCallback 298 | : () => Promise.resolve(); 299 | preNavigateCallback(selection).then(() => { 300 | this[NavigationMixin.Navigate]({ 301 | type: 'standard__objectPage', 302 | attributes: { 303 | objectApiName, 304 | actionName: 'new' 305 | }, 306 | state: { 307 | defaultFieldValues: selection.defaults 308 | } 309 | }); 310 | }); 311 | } 312 | 313 | // STYLE EXPRESSIONS 314 | 315 | get hasResults() { 316 | return this._searchResults.length > 0; 317 | } 318 | 319 | get getFormElementClass() { 320 | return this.variant === VARIANT_LABEL_INLINE 321 | ? 'slds-form-element slds-form-element_horizontal' 322 | : 'slds-form-element'; 323 | } 324 | 325 | get getLabelClass() { 326 | return this.variant === VARIANT_LABEL_HIDDEN 327 | ? 'slds-form-element__label slds-assistive-text' 328 | : 'slds-form-element__label'; 329 | } 330 | 331 | get getContainerClass() { 332 | let css = 'slds-combobox_container slds-has-inline-listbox '; 333 | if (this._hasFocus && this.hasResults) { 334 | css += 'slds-has-input-focus '; 335 | } 336 | if (this.errors.length > 0) { 337 | css += 'has-custom-error'; 338 | } 339 | return css; 340 | } 341 | 342 | get getDropdownClass() { 343 | let css = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click '; 344 | const isSearchTermValid = this._cleanSearchTerm && this._cleanSearchTerm.length >= this.minSearchTermLength; 345 | if (this._hasFocus && this.isSelectionAllowed() && (isSearchTermValid || this.hasResults)) { 346 | css += 'slds-is-open'; 347 | } 348 | return css; 349 | } 350 | 351 | get getInputClass() { 352 | let css = 'slds-input slds-combobox__input has-custom-height '; 353 | if (this.errors.length > 0 || (this._isDirty && this.required && !this.hasSelection())) { 354 | css += 'has-custom-error '; 355 | } 356 | if (!this.isMultiEntry) { 357 | css += 'slds-combobox__input-value ' + (this.hasSelection() ? 'has-custom-border' : ''); 358 | } 359 | return css; 360 | } 361 | 362 | get getComboboxClass() { 363 | let css = 'slds-combobox__form-element slds-input-has-icon '; 364 | if (this.isMultiEntry) { 365 | css += 'slds-input-has-icon_right'; 366 | } else if (this.hasSelection()) { 367 | css += 'slds-input-has-icon_left-right' 368 | } else { 369 | css += 'slds-input-has-icon_right'; 370 | } 371 | return css; 372 | } 373 | 374 | get getSearchIconClass() { 375 | let css = 'slds-input__icon slds-input__icon_right '; 376 | if (!this.isMultiEntry && this.hasSelection()) { 377 | css += 'slds-hide'; 378 | } 379 | return css; 380 | } 381 | 382 | get getClearSelectionButtonClass() { 383 | return ( 384 | 'slds-button slds-button_icon slds-input__icon slds-input__icon_right ' + 385 | (this.hasSelection() ? '' : 'slds-hide') 386 | ); 387 | } 388 | 389 | get getSelectIconName() { 390 | return this.hasSelection() ? this._curSelection[0].icon : 'standard:default'; 391 | } 392 | 393 | get getSelectIconClass() { 394 | return 'slds-combobox__input-entity-icon ' + (this.hasSelection() ? '' : 'slds-hide'); 395 | } 396 | 397 | get getInputValue() { 398 | if (this.isMultiEntry) { 399 | return this._searchTerm; 400 | } 401 | return this.hasSelection() ? this._curSelection[0].title : this._searchTerm; 402 | } 403 | 404 | get getInputTitle() { 405 | if (this.isMultiEntry) { 406 | return ''; 407 | } 408 | return this.hasSelection() ? this._curSelection[0].title : ''; 409 | } 410 | 411 | get getListboxClass() { 412 | return ( 413 | 'slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid ' + 414 | (this.scrollAfterNItems ? 'slds-dropdown_length-with-icon-' + this.scrollAfterNItems : '') 415 | ); 416 | } 417 | 418 | get isInputReadonly() { 419 | if (this.isMultiEntry) { 420 | return false; 421 | } 422 | return this.hasSelection(); 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/lookup/lookup.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 51.0 4 | false 5 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronAttachmentPickerCombobox/pdftronAttachmentPickerCombobox.html: -------------------------------------------------------------------------------- 1 | 63 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronAttachmentPickerCombobox/pdftronAttachmentPickerCombobox.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, track, wire, api } from 'lwc' 2 | import { CurrentPageReference } from 'lightning/navigation' 3 | import { fireEvent, registerListener, unregisterAllListeners } from 'c/pubsub' 4 | import { ShowToastEvent } from 'lightning/platformShowToastEvent' 5 | import getAttachments from '@salesforce/apex/PDFTron_ContentVersionController.getExistingAttachments' 6 | import getBase64FromCv from '@salesforce/apex/PDFTron_ContentVersionController.getBase64FromCv' 7 | import apexSearch from '@salesforce/apex/PDFTron_ContentVersionController.search' 8 | import getFileDataFromId from '@salesforce/apex/PDFTron_ContentVersionController.getFileDataFromId' 9 | 10 | export default class PdftronAttachmentPickerCombobox extends LightningElement { 11 | error 12 | 13 | @track isModalOpen = false 14 | 15 | @track value = '' 16 | @track picklistOptions = [] 17 | @track isSaving = false 18 | @track loadFinished = false 19 | documentsRetrieved = false 20 | @api recordId 21 | @track attachments = [] 22 | @wire(CurrentPageReference) pageRef 23 | 24 | renderedCallback () { 25 | if(!this.recordId) { 26 | this.loadFinished = true; 27 | return 28 | } 29 | if (!this.documentsRetrieved) { 30 | getAttachments({ recordId: this.recordId }) 31 | .then(data => { 32 | this.attachments = data 33 | this.initLookupDefaultResults() 34 | 35 | this.error = undefined 36 | this.loadFinished = true 37 | this.documentsRetrieved = true 38 | }) 39 | .catch(error => { 40 | console.error(error) 41 | this.showNotification('Error', error, 'error') 42 | this.error = error 43 | }) 44 | } 45 | } 46 | 47 | connectedCallback () { 48 | registerListener('refreshOnSave', this.refreshOnSave, this) 49 | this.initLookupDefaultResults() 50 | } 51 | 52 | disconnectedCallback () { 53 | unregisterAllListeners(this) 54 | } 55 | 56 | showNotification (title, message, variant) { 57 | const evt = new ShowToastEvent({ 58 | title: title, 59 | message: message, 60 | variant: variant 61 | }) 62 | this.dispatchEvent(evt) 63 | } 64 | 65 | initLookupDefaultResults () { 66 | // Make sure that the lookup is present and if so, set its default results 67 | const lookup = this.template.querySelector('c-lookup') 68 | if (lookup) { 69 | lookup.setDefaultResults(this.attachments) 70 | } 71 | } 72 | 73 | handleSearch (event) { 74 | const lookupElement = event.target 75 | apexSearch(event.detail) 76 | .then(results => { 77 | lookupElement.setSearchResults(results) 78 | }) 79 | .catch(error => { 80 | // TODO: handle error 81 | this.error = error 82 | console.error(error) 83 | let def_message = 84 | 'We have encountered an error while searching for your file ' + 85 | event.detail + 86 | '\n' 87 | 88 | this.showNotification( 89 | 'Error', 90 | def_message + error.body.message, 91 | 'error' 92 | ) 93 | }) 94 | } 95 | 96 | handleSingleSelectionChange (event) { 97 | this.checkForErrors() 98 | 99 | if (event.detail.length < 1) { 100 | this.handleClose() 101 | return 102 | } 103 | 104 | this.isLoading = true 105 | 106 | getFileDataFromId({ Id: event.detail[0] }) 107 | .then(result => { 108 | fireEvent(this.pageRef, 'blobSelected', result) 109 | this.isLoading = false 110 | }) 111 | .catch(error => { 112 | // TODO: handle error 113 | this.error = error 114 | console.error(error) 115 | this.isLoading = false 116 | let def_message = 117 | 'We have encountered an error while handling your file. ' 118 | 119 | this.showNotification( 120 | 'Error', 121 | def_message + error.body.message, 122 | 'error' 123 | ) 124 | }) 125 | } 126 | 127 | //check for errors on selection 128 | checkForErrors () { 129 | this.errors = [] 130 | const selection = this.template.querySelector('c-lookup').getSelection() 131 | // Custom validation rule 132 | if (this.isMultiEntry && selection.length > this.maxSelectionSize) { 133 | this.errors.push({ 134 | message: `You may only select up to ${this.maxSelectionSize} items.` 135 | }) 136 | } 137 | // Enforcing required field 138 | if (selection.length === 0) { 139 | this.errors.push({ message: 'Please make a selection.' }) 140 | } 141 | } 142 | 143 | handleUploadFinished () { 144 | this.showNotification( 145 | 'Success', 146 | 'Your file has been attached to ' + this.recordId, 147 | 'success' 148 | ) 149 | this.refreshOnSave() 150 | } 151 | 152 | refreshOnSave () { 153 | this.loadFinished = false 154 | if(this.recordId){ 155 | getAttachments({ recordId: this.recordId }) 156 | .then(data => { 157 | this.attachments = data 158 | this.initLookupDefaultResults() 159 | 160 | this.error = undefined 161 | this.loadFinished = true 162 | this.documentsRetrieved = true 163 | }) 164 | .catch(error => { 165 | console.error(error) 166 | this.showNotification('Error', error, 'error') 167 | this.error = error 168 | }) 169 | } 170 | } 171 | 172 | handleDownload () { 173 | fireEvent(this.pageRef, 'downloadDocument', '*') 174 | } 175 | 176 | handleClose () { 177 | fireEvent(this.pageRef, 'closeDocument', '*') 178 | } 179 | 180 | openModal () { 181 | this.isModalOpen = true 182 | } 183 | 184 | closeModal () { 185 | this.isModalOpen = false 186 | } 187 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronAttachmentPickerCombobox/pdftronAttachmentPickerCombobox.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | true 5 | 6 | lightning__RecordPage 7 | 8 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronFilePickerCombobox/pdftronFilePickerCombobox.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronFilePickerCombobox/pdftronFilePickerCombobox.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, track, wire, api } from 'lwc'; 2 | import myfilesUrl from '@salesforce/resourceUrl/myfiles'; 3 | import { CurrentPageReference } from 'lightning/navigation'; 4 | import { fireEvent } from 'c/pubsub'; 5 | 6 | export default class PdftronFilePickerCombobox extends LightningElement { 7 | @track value = 'inProgress'; 8 | @track fileUrl = ''; 9 | @wire(CurrentPageReference) pageRef; 10 | @api myRecordId; 11 | 12 | get acceptedFormats() { 13 | return ['.pdf', '.png']; 14 | } 15 | handleButton(event) { 16 | fireEvent(this.pageRef, 'fileSelected', this.fileUrl); 17 | } 18 | 19 | handleFilePick(event) { 20 | console.log(event.target.files[0]) 21 | fireEvent(this.pageRef, 'fileSelected', event.target.files[0]); 22 | } 23 | 24 | handleUrlInput(event) { 25 | this.fileUrl = event.target.value; 26 | } 27 | 28 | handleUploadFinished(event) { 29 | // Get the list of uploaded files 30 | const uploadedFiles = event.detail.files; 31 | alert("No. of files uploaded : " + uploadedFiles.length); 32 | } 33 | 34 | get options() { 35 | return [ 36 | { label: 'webviewer-demo-annotated.pdf', value: myfilesUrl + '/webviewer-demo-annotated.pdf' }, 37 | { label: 'compressed.tracemonkey-pldi-09.pdf', value: myfilesUrl + '/compressed.tracemonkey-pldi-09.pdf' }, 38 | { label: 'webviewer-demo-annotated.xod', value: myfilesUrl + '/webviewer-demo-annotated.xod' }, 39 | { label: 'word.docx', value: myfilesUrl + '/word.docx' }, 40 | ]; 41 | } 42 | 43 | handleChange(event) { 44 | this.value = event.detail.value; 45 | fireEvent(this.pageRef, 'fileSelected', this.value); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronFilePickerCombobox/pdftronFilePickerCombobox.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 50.0 4 | true 5 | 6 | lightning__AppPage 7 | lightning__RecordPage 8 | lightning__HomePage 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronTabViewer/pdftronTabViewer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronTabViewer/pdftronTabViewer.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, track } from 'lwc'; 2 | import { publish, createMessageContext, releaseMessageContext, subscribe, unsubscribe } from 'lightning/messageService'; 3 | import lmsWebviewer from "@salesforce/messageChannel/LMSWebViewer__c"; 4 | 5 | export default class PdftronTabViewer extends LightningElement { 6 | //LMS 7 | @track receivedMessage = ''; 8 | currentLink; 9 | channel; 10 | context = createMessageContext(); 11 | 12 | @track tabs = []; //push {label:'',link:''} to this array 13 | showTabs = false; 14 | 15 | connectedCallback() { 16 | this.handleSubscribe(); 17 | if (this.showTabs === false) { 18 | if (typeof this.tabs.length !== '' && this.tabs.length) { 19 | 20 | } 21 | } 22 | } 23 | 24 | disconnectedCallback() { 25 | this.handleUnsubscribe(); 26 | } 27 | 28 | handleActive(event) { 29 | console.log(`Current doc: ${event.target.label}, link: ${event.target.value}`); 30 | //pass event.target.value to WebViewer here 31 | this.currentLink = event.target.value; 32 | } 33 | 34 | handleSubscribe() { 35 | const parentPage = this; 36 | this.channel = subscribe(this.context, lmsWebviewer, (event) => { 37 | if (event != null) { 38 | console.log(event); 39 | this.tabs = []; 40 | event.link.forEach((link) => { 41 | const label = link.substring(link.lastIndexOf("/") + 1); 42 | console.log(label); 43 | const option = { label: label, value: link }; 44 | if (!this.tabs.includes(option)) { 45 | this.tabs = [...this.tabs, option]; 46 | this.showTabs = true; 47 | } else { 48 | let idx = this.tabs.indexOf(option); 49 | this.tabs = this.tabs.splice(idx, 1); 50 | } 51 | }); 52 | } 53 | }); 54 | } 55 | 56 | handleUnsubscribe() { 57 | unsubscribe(this.channel); 58 | } 59 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronTabViewer/pdftronTabViewer.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 50.0 4 | true 5 | 6 | lightning__AppPage 7 | lightning__RecordPage 8 | lightning__HomePage 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWebViewer/pdftronWebViewer.css: -------------------------------------------------------------------------------- 1 | .pdf_viewer { 2 | height: 1000px; 3 | } 4 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWebViewer/pdftronWebViewer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWebViewer/pdftronWebViewer.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, track, wire } from 'lwc'; 2 | // import { ShowToastEvent } from 'lightning/platformShowToastEvent'; 3 | import { CurrentPageReference } from 'lightning/navigation'; 4 | import { loadScript } from 'lightning/platformResourceLoader'; 5 | import libUrl from '@salesforce/resourceUrl/lib'; 6 | import myfilesUrl from '@salesforce/resourceUrl/myfiles'; 7 | import { registerListener, unregisterAllListeners } from 'c/pubsub'; 8 | 9 | export default class PdftronWebViewer extends LightningElement { 10 | fullAPI = true; 11 | @wire(CurrentPageReference) pageRef; 12 | 13 | connectedCallback() { 14 | registerListener('fileSelected', this.handleFileSelected, this); 15 | } 16 | 17 | disconnectedCallback() { 18 | unregisterAllListeners(this); 19 | } 20 | 21 | handleFileSelected(file) { 22 | this.iframeWindow.postMessage({type: 'OPEN_DOCUMENT', file: file}, '*') 23 | } 24 | 25 | divHeight = 600; 26 | uiInitialized = false; 27 | renderedCallback() { 28 | if (this.uiInitialized) { 29 | return; 30 | } 31 | this.uiInitialized = true; 32 | 33 | Promise.all([ 34 | loadScript(this, libUrl + '/webviewer.min.js') 35 | ]) 36 | .then(() => { 37 | this.initUI(); 38 | }) 39 | .catch(console.error); 40 | } 41 | initUI() { 42 | const myObj = { 43 | libUrl: libUrl, 44 | fullAPI: this.fullAPI, 45 | namespacePrefix: '', 46 | }; 47 | 48 | const url = myfilesUrl + '/webviewer-demo-annotated.pdf'; 49 | // var url = myfilesUrl + '/webviewer-demo-annotated.xod'; 50 | // var url = myfilesUrl + '/word.docx'; 51 | 52 | const viewerElement = this.template.querySelector('div') 53 | const viewer = new WebViewer.Iframe({ 54 | path: libUrl, // path to the PDFTron 'lib' folder on your server 55 | custom: JSON.stringify(myObj), 56 | backendType: 'ems', 57 | config: myfilesUrl + '/config_apex.js', 58 | fullAPI: this.fullAPI, 59 | enableFilePicker: this.enableFilePicker, 60 | enableRedaction: this.enableRedaction, 61 | enableMeasurement: this.enableMeasurement, 62 | enableOptimizedWorkers: true, 63 | // l: 'YOUR_LICENSE_KEY_HERE', 64 | }, viewerElement); 65 | 66 | viewerElement.addEventListener('ready', () => { 67 | this.iframeWindow = viewerElement.querySelector('iframe').contentWindow 68 | }) 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWebViewer/pdftronWebViewer.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 50.0 4 | true 5 | 6 | lightning__AppPage 7 | lightning__RecordPage 8 | lightning__HomePage 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWebviewerContainer/pdftronWebviewerContainer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWebviewerContainer/pdftronWebviewerContainer.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, api } from 'lwc'; 2 | 3 | export default class PdftronWebviewerContainer extends LightningElement { 4 | @api recordId; 5 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWebviewerContainer/pdftronWebviewerContainer.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52.0 4 | true 5 | 6 | lightning__AppPage 7 | lightning__RecordPage 8 | lightning__HomePage 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWvFileBrowserComponent/pdftronWvFileBrowserComponent.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWvFileBrowserComponent/pdftronWvFileBrowserComponent.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, wire, track, api } from 'lwc'; 2 | import { NavigationMixin } from 'lightning/navigation'; 3 | /** ContentVersionController.getContentVersions() Apex method */ 4 | import getContentVersions from '@salesforce/apex/PDFTron_ContentVersionController.getContentVersions'; 5 | 6 | const actions = [ 7 | { label: 'Show details', name: 'show_details' }, 8 | { label: 'Open with WebViewer', name: 'open_with_webviewer' } 9 | ]; 10 | 11 | const columns = [ 12 | { label: 'Id', fieldName: 'Id' }, 13 | { label: 'Title', fieldName: 'Title', sortable: true }, 14 | { label: 'Extension', fieldName: 'FileExtension', sortable: true }, 15 | { label: 'Last Modified Date', fieldName: 'LastModifiedDate', type: "date", sortable: true, 16 | typeAttributes:{ 17 | hour12: true, 18 | year: "numeric", 19 | month: "long", 20 | day: "2-digit", 21 | hour: "2-digit", 22 | minute: "2-digit" 23 | } 24 | }, 25 | { type: 'action', typeAttributes: { rowActions: actions, menuAlignment: 'right' } }, 26 | ]; 27 | 28 | 29 | export default class PdftronWvFileBrowserComponent extends NavigationMixin(LightningElement) { 30 | @api tabName; 31 | @api label; 32 | @track contentVersions; 33 | columns = columns; 34 | 35 | @wire(getContentVersions, {}) 36 | handleGeRecords({error, data}){ 37 | if (error) { 38 | return; 39 | } 40 | 41 | if (data) { 42 | this.contentVersions = data; 43 | } 44 | } 45 | 46 | log(...str) { 47 | console.log(JSON.parse(JSON.stringify(str))); 48 | 49 | } 50 | 51 | connectedCallback() { 52 | console.log(`%c connectedCallback `, 'background: red; color: white;'); 53 | } 54 | 55 | handleRowAction(event) { 56 | console.log(`%c handleRowAction `, 'background: red; color: white;'); 57 | const action = event.detail.action; 58 | const row = event.detail.row; 59 | switch (action.name) { 60 | case 'show_details': 61 | alert('Showing Details: ' + JSON.stringify(row)); 62 | break; 63 | case 'open_with_webviewer': 64 | // open with wv 65 | this.navigateToWvInstance(row); 66 | break; 67 | } 68 | } 69 | 70 | navigateToWvInstance(row) { 71 | this[NavigationMixin.Navigate]({ 72 | type: 'standard__component', 73 | attributes: { 74 | componentName: 'c__WebViewerAura' 75 | }, 76 | state: { 77 | c__contentVersionId: row.Id, 78 | } 79 | }) 80 | } 81 | 82 | // The method onsort event handler 83 | updateColumnSorting(event) { 84 | 85 | var fieldName = event.detail.fieldName; 86 | var sortDirection = event.detail.sortDirection; 87 | // assign the latest attribute with the sorted column fieldName and sorted direction 88 | this.sortedBy = fieldName; 89 | this.sortedDirection = sortDirection; 90 | this.contentVersions = this.sortData(fieldName, sortDirection); 91 | } 92 | 93 | sortData(fieldName, sortDirection) { 94 | console.log(`%c sortData ${fieldName} ${sortDirection} `, 'background: red; color: white;'); 95 | let result; 96 | // switch (fieldName) { 97 | // case 'LastModifiedDate': 98 | // result = this.contentVersions.sort((a, b) => b.LastModifiedDate - a.LastModifiedDate) 99 | // break; 100 | // default: 101 | // result = this.contentVersions; 102 | // break; 103 | // } 104 | return this.contentVersions; 105 | } 106 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWvFileBrowserComponent/pdftronWvFileBrowserComponent.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 50.0 4 | true 5 | 6 | lightning__AppPage 7 | lightning__RecordPage 8 | lightning__HomePage 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWvInstance/mimeTypes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'a' : 'application/octet-stream', 3 | 'ai' : 'application/postscript', 4 | 'aif' : 'audio/x-aiff', 5 | 'aifc' : 'audio/x-aiff', 6 | 'aiff' : 'audio/x-aiff', 7 | 'au' : 'audio/basic', 8 | 'avi' : 'video/x-msvideo', 9 | 'bat' : 'text/plain', 10 | 'bin' : 'application/octet-stream', 11 | 'bmp' : 'image/x-ms-bmp', 12 | 'c' : 'text/plain', 13 | 'cdf' : 'application/x-cdf', 14 | 'csh' : 'application/x-csh', 15 | 'css' : 'text/css', 16 | 'dll' : 'application/octet-stream', 17 | 'doc' : 'application/msword', 18 | 'dot' : 'application/msword', 19 | 'dvi' : 'application/x-dvi', 20 | 'eml' : 'message/rfc822', 21 | 'eps' : 'application/postscript', 22 | 'etx' : 'text/x-setext', 23 | 'exe' : 'application/octet-stream', 24 | 'gif' : 'image/gif', 25 | 'gtar' : 'application/x-gtar', 26 | 'h' : 'text/plain', 27 | 'hdf' : 'application/x-hdf', 28 | 'htm' : 'text/html', 29 | 'html' : 'text/html', 30 | 'jpe' : 'image/jpeg', 31 | 'jpeg' : 'image/jpeg', 32 | 'jpg' : 'image/jpeg', 33 | 'js' : 'application/x-javascript', 34 | 'ksh' : 'text/plain', 35 | 'latex' : 'application/x-latex', 36 | 'm1v' : 'video/mpeg', 37 | 'man' : 'application/x-troff-man', 38 | 'me' : 'application/x-troff-me', 39 | 'mht' : 'message/rfc822', 40 | 'mhtml' : 'message/rfc822', 41 | 'mif' : 'application/x-mif', 42 | 'mov' : 'video/quicktime', 43 | 'movie' : 'video/x-sgi-movie', 44 | 'mp2' : 'audio/mpeg', 45 | 'mp3' : 'audio/mpeg', 46 | 'mp4' : 'video/mp4', 47 | 'mpa' : 'video/mpeg', 48 | 'mpe' : 'video/mpeg', 49 | 'mpeg' : 'video/mpeg', 50 | 'mpg' : 'video/mpeg', 51 | 'ms' : 'application/x-troff-ms', 52 | 'nc' : 'application/x-netcdf', 53 | 'nws' : 'message/rfc822', 54 | 'o' : 'application/octet-stream', 55 | 'obj' : 'application/octet-stream', 56 | 'oda' : 'application/oda', 57 | 'pbm' : 'image/x-portable-bitmap', 58 | 'pdf' : 'application/pdf', 59 | 'pfx' : 'application/x-pkcs12', 60 | 'pgm' : 'image/x-portable-graymap', 61 | 'png' : 'image/png', 62 | 'pnm' : 'image/x-portable-anymap', 63 | 'pot' : 'application/vnd.ms-powerpoint', 64 | 'ppa' : 'application/vnd.ms-powerpoint', 65 | 'ppm' : 'image/x-portable-pixmap', 66 | 'pps' : 'application/vnd.ms-powerpoint', 67 | 'ppt' : 'application/vnd.ms-powerpoint', 68 | 'pptx' : 'application/vnd.ms-powerpoint', 69 | 'ps' : 'application/postscript', 70 | 'pwz' : 'application/vnd.ms-powerpoint', 71 | 'py' : 'text/x-python', 72 | 'pyc' : 'application/x-python-code', 73 | 'pyo' : 'application/x-python-code', 74 | 'qt' : 'video/quicktime', 75 | 'ra' : 'audio/x-pn-realaudio', 76 | 'ram' : 'application/x-pn-realaudio', 77 | 'ras' : 'image/x-cmu-raster', 78 | 'rdf' : 'application/xml', 79 | 'rgb' : 'image/x-rgb', 80 | 'roff' : 'application/x-troff', 81 | 'rtx' : 'text/richtext', 82 | 'sgm' : 'text/x-sgml', 83 | 'sgml' : 'text/x-sgml', 84 | 'sh' : 'application/x-sh', 85 | 'shar' : 'application/x-shar', 86 | 'snd' : 'audio/basic', 87 | 'so' : 'application/octet-stream', 88 | 'src' : 'application/x-wais-source', 89 | 'swf' : 'application/x-shockwave-flash', 90 | 't' : 'application/x-troff', 91 | 'tar' : 'application/x-tar', 92 | 'tcl' : 'application/x-tcl', 93 | 'tex' : 'application/x-tex', 94 | 'texi' : 'application/x-texinfo', 95 | 'texinfo': 'application/x-texinfo', 96 | 'tif' : 'image/tiff', 97 | 'tiff' : 'image/tiff', 98 | 'tr' : 'application/x-troff', 99 | 'tsv' : 'text/tab-separated-values', 100 | 'txt' : 'text/plain', 101 | 'ustar' : 'application/x-ustar', 102 | 'vcf' : 'text/x-vcard', 103 | 'wav' : 'audio/x-wav', 104 | 'wiz' : 'application/msword', 105 | 'wsdl' : 'application/xml', 106 | 'xbm' : 'image/x-xbitmap', 107 | 'xlb' : 'application/vnd.ms-excel', 108 | 'xls' : 'application/vnd.ms-excel', 109 | 'xlsx' : 'application/vnd.ms-excel', 110 | 'xml' : 'text/xml', 111 | 'xpdl' : 'application/xml', 112 | 'xpm' : 'image/x-xpixmap', 113 | 'xsl' : 'application/xml', 114 | 'xwd' : 'image/x-xwindowdump', 115 | 'zip' : 'application/zip' 116 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWvInstance/pdftronWvInstance.css: -------------------------------------------------------------------------------- 1 | .pdf_viewer { 2 | height: 1000px; 3 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWvInstance/pdftronWvInstance.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWvInstance/pdftronWvInstance.js: -------------------------------------------------------------------------------- 1 | import getUser from '@salesforce/apex/PDFTron_ContentVersionController.getUser'; 2 | import saveDocument from '@salesforce/apex/PDFTron_ContentVersionController.saveDocument'; 3 | import libUrl from '@salesforce/resourceUrl/lib'; 4 | import myfilesUrl from '@salesforce/resourceUrl/myfiles'; 5 | import { fireEvent, registerListener, unregisterAllListeners } from 'c/pubsub'; 6 | import { CurrentPageReference } from 'lightning/navigation'; 7 | import { loadScript } from 'lightning/platformResourceLoader'; 8 | import { api, LightningElement, wire } from 'lwc'; 9 | import mimeTypes from './mimeTypes'; 10 | 11 | function _base64ToArrayBuffer(base64) { 12 | var binary_string = window.atob(base64); 13 | var len = binary_string.length; 14 | var bytes = new Uint8Array( len ); 15 | for (var i = 0; i < len; i++) { 16 | bytes[i] = binary_string.charCodeAt(i); 17 | } 18 | return bytes.buffer; 19 | } 20 | 21 | export default class PdftronWvInstance extends LightningElement { 22 | //initialization options 23 | fullAPI = true; 24 | enableRedaction = true; 25 | enableFilePicker = true; 26 | 27 | uiInitialized = false; 28 | 29 | source = 'My file'; 30 | @api recordId; 31 | 32 | @wire(CurrentPageReference) 33 | pageRef; 34 | 35 | username; 36 | 37 | connectedCallback() { 38 | registerListener('blobSelected', this.handleBlobSelected, this); 39 | registerListener('closeDocument', this.closeDocument, this); 40 | registerListener('downloadDocument', this.downloadDocument, this); 41 | registerListener('fileSelected', this.handleFileSelected, this); 42 | window.addEventListener('message', this.handleReceiveMessage.bind(this), false); 43 | } 44 | 45 | disconnectedCallback() { 46 | unregisterAllListeners(this); 47 | window.removeEventListener('message', this.handleReceiveMessage); 48 | } 49 | 50 | handleBlobSelected(record) { 51 | const blobby = new Blob([_base64ToArrayBuffer(record.body)], { 52 | type: mimeTypes[record.FileExtension] 53 | }); 54 | 55 | 56 | const payload = { 57 | blob: blobby, 58 | extension: record.cv.FileExtension, 59 | filename: record.cv.Title + "." + record.cv.FileExtension, 60 | documentId: record.cv.Id 61 | }; 62 | 63 | this.iframeWindow.postMessage({ type: 'OPEN_DOCUMENT_BLOB', payload }, '*'); 64 | } 65 | 66 | renderedCallback() { 67 | var self = this; 68 | 69 | if (this.uiInitialized) { 70 | return; 71 | } 72 | 73 | Promise.all([ 74 | loadScript(self, libUrl + '/webviewer.min.js') 75 | ]) 76 | .then(() => this.handleInitWithCurrentUser()) 77 | .catch(console.error); 78 | } 79 | 80 | handleFileSelected(file) { 81 | this.iframeWindow.postMessage({type: 'OPEN_DOCUMENT', file: file}, '*') 82 | } 83 | 84 | handleInitWithCurrentUser() { 85 | getUser() 86 | .then((result) => { 87 | this.username = result; 88 | this.error = undefined; 89 | 90 | this.initUI(); 91 | }) 92 | .catch((error) => { 93 | console.error(error); 94 | this.showNotification('Error', error.body.message, 'error'); 95 | }); 96 | } 97 | 98 | initUI() { 99 | var myObj = { 100 | libUrl: libUrl, 101 | fullAPI: this.fullAPI || false, 102 | namespacePrefix: '', 103 | username: this.username, 104 | }; 105 | var url = myfilesUrl + '/webviewer-demo-annotated.pdf'; 106 | 107 | const viewerElement = this.template.querySelector('div') 108 | // eslint-disable-next-line no-unused-vars 109 | const viewer = new WebViewer.Iframe({ 110 | path: libUrl, // path to the PDFTron 'lib' folder on your server 111 | custom: JSON.stringify(myObj), 112 | backendType: 'ems', 113 | config: myfilesUrl + '/config_apex.js', 114 | fullAPI: this.fullAPI, 115 | enableFilePicker: this.enableFilePicker, 116 | enableRedaction: this.enableRedaction, 117 | enableMeasurement: this.enableMeasurement, 118 | enableOptimizedWorkers: true, 119 | loadAsPDF: true, 120 | // l: 'YOUR_LICENSE_KEY_HERE', 121 | }, viewerElement); 122 | 123 | viewerElement.addEventListener('ready', () => { 124 | this.iframeWindow = viewerElement.querySelector('iframe').contentWindow; 125 | }) 126 | 127 | } 128 | 129 | handleReceiveMessage(event) { 130 | const me = this; 131 | if (event.isTrusted && typeof event.data === 'object') { 132 | switch (event.data.type) { 133 | case 'SAVE_DOCUMENT': 134 | const cvId = event.data.payload.contentDocumentId; 135 | saveDocument({ json: JSON.stringify(event.data.payload), recordId: this.recordId ? this.recordId : '', cvId: cvId }) 136 | .then((response) => { 137 | me.iframeWindow.postMessage({ type: 'DOCUMENT_SAVED', response }, '*') 138 | fireEvent(this.pageRef, 'refreshOnSave', response); 139 | }) 140 | .catch(error => { 141 | me.iframeWindow.postMessage({ type: 'DOCUMENT_SAVED', error }, '*') 142 | fireEvent(this.pageRef, 'refreshOnSave', error); 143 | console.error(event.data.payload.contentDocumentId); 144 | console.error(JSON.stringify(error)); 145 | this.showNotification('Error', error.body, 'error') 146 | }); 147 | break; 148 | default: 149 | break; 150 | } 151 | } 152 | } 153 | 154 | downloadDocument() { 155 | this.iframeWindow.postMessage({type: 'DOWNLOAD_DOCUMENT' }, '*') 156 | } 157 | 158 | @api 159 | closeDocument() { 160 | this.iframeWindow.postMessage({type: 'CLOSE_DOCUMENT' }, '*') 161 | } 162 | } -------------------------------------------------------------------------------- /force-app/main/default/lwc/pdftronWvInstance/pdftronWvInstance.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 50.0 4 | true 5 | 6 | lightning__AppPage 7 | lightning__RecordPage 8 | lightning__HomePage 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pubsub/pubsub.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/pubsub/pubsub.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A basic pub-sub mechanism for sibling component communication 3 | * 4 | * TODO - adopt standard flexipage sibling communication mechanism when it's available. 5 | */ 6 | 7 | const events = {}; 8 | 9 | const samePageRef = (pageRef1, pageRef2) => { 10 | const obj1 = pageRef1.attributes; 11 | const obj2 = pageRef2.attributes; 12 | return Object.keys(obj1) 13 | .concat(Object.keys(obj2)) 14 | .every(key => { 15 | return obj1[key] === obj2[key]; 16 | }); 17 | }; 18 | 19 | /** 20 | * Registers a callback for an event 21 | * @param {string} eventName - Name of the event to listen for. 22 | * @param {function} callback - Function to invoke when said event is fired. 23 | * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound. 24 | */ 25 | const registerListener = (eventName, callback, thisArg) => { 26 | // Checking that the listener has a pageRef property. We rely on that property for filtering purpose in fireEvent() 27 | if (!thisArg.pageRef) { 28 | throw new Error( 29 | 'pubsub listeners need a "@wire(CurrentPageReference) pageRef" property' 30 | ); 31 | } 32 | 33 | if (!events[eventName]) { 34 | events[eventName] = []; 35 | } 36 | const duplicate = events[eventName].find(listener => { 37 | return listener.callback === callback && listener.thisArg === thisArg; 38 | }); 39 | if (!duplicate) { 40 | events[eventName].push({ callback, thisArg }); 41 | } 42 | }; 43 | 44 | /** 45 | * Unregisters a callback for an event 46 | * @param {string} eventName - Name of the event to unregister from. 47 | * @param {function} callback - Function to unregister. 48 | * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound. 49 | */ 50 | const unregisterListener = (eventName, callback, thisArg) => { 51 | if (events[eventName]) { 52 | events[eventName] = events[eventName].filter( 53 | listener => 54 | listener.callback !== callback || listener.thisArg !== thisArg 55 | ); 56 | } 57 | }; 58 | 59 | /** 60 | * Unregisters all event listeners bound to an object. 61 | * @param {object} thisArg - All the callbacks bound to this object will be removed. 62 | */ 63 | const unregisterAllListeners = thisArg => { 64 | Object.keys(events).forEach(eventName => { 65 | events[eventName] = events[eventName].filter( 66 | listener => listener.thisArg !== thisArg 67 | ); 68 | }); 69 | }; 70 | 71 | /** 72 | * Fires an event to listeners. 73 | * @param {object} pageRef - Reference of the page that represents the event scope. 74 | * @param {string} eventName - Name of the event to fire. 75 | * @param {*} payload - Payload of the event to fire. 76 | */ 77 | const fireEvent = (pageRef, eventName, payload) => { 78 | if (events[eventName]) { 79 | const listeners = events[eventName]; 80 | listeners.forEach(listener => { 81 | if (samePageRef(pageRef, listener.thisArg.pageRef)) { 82 | try { 83 | listener.callback.call(listener.thisArg, payload); 84 | } catch (error) { 85 | // fail silently 86 | } 87 | } 88 | }); 89 | } 90 | }; 91 | 92 | export { 93 | registerListener, 94 | unregisterListener, 95 | unregisterAllListeners, 96 | fireEvent 97 | }; -------------------------------------------------------------------------------- /force-app/main/default/lwc/pubsub/pubsub.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 45.0 4 | false 5 | -------------------------------------------------------------------------------- /force-app/main/default/messageChannels/LMSWebViewer.messageChannel-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebViewer LMS 4 | true 5 | Message channel to enable communication between PDFTron's WebViewer and LWC, Aura, VF 6 | 7 | -------------------------------------------------------------------------------- /force-app/main/default/profiles/Admin.profile-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PDFTron 5 | false 6 | true 7 | 8 | 9 | PDFTron_ContentVersionController 10 | true 11 | 12 | false 13 | 14 | File_Browser 15 | DefaultOn 16 | 17 | 18 | WebViewer 19 | DefaultOn 20 | 21 | Salesforce 22 | 23 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/content_edit.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/content_edit.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/content_edit.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/content_edit_resource.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/content_edit_resource.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/content_edit_resource.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/external.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/external.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/external.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/font_assets.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/font_assets.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/font_assets.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/legacyOffice.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/legacyOffice.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/legacyOffice.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/legacyOffice_asm.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/legacyOffice_asm.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/legacyOffice_asm.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/legacyOffice_resource.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/legacyOffice_resource.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/legacyOffice_resource.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/lib.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/lib.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/lib.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/myfiles.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/myfiles/compressed.tracemonkey-pldi-09.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/myfiles/compressed.tracemonkey-pldi-09.pdf -------------------------------------------------------------------------------- /force-app/main/default/staticresources/myfiles/config.js: -------------------------------------------------------------------------------- 1 | let resourceURL = '/resource/' 2 | /** 3 | * @note If you are using WebViewer <= 7.x, please uncomment the line below, and 4 | * comment out the line below that 5 | */ 6 | // const Core = window.CoreControls; // WebViewer <= 7.x 7 | const Core = window.Core; // WebViewer >= 8.x 8 | Core.forceBackendType('ems'); 9 | 10 | const urlSearch = new URLSearchParams(location.hash) 11 | const custom = JSON.parse(urlSearch.get('custom')); 12 | resourceURL = resourceURL + custom.namespacePrefix; 13 | 14 | /** 15 | * The following `Core.set*` functions point WebViewer to the 16 | * optimized source code specific for the Salesforce platform, to ensure the 17 | * uploaded files stay under the 5mb limit 18 | */ 19 | // office workers 20 | Core.setOfficeWorkerPath(resourceURL + 'office') 21 | Core.setOfficeAsmPath(resourceURL + 'office_asm'); 22 | Core.setOfficeResourcePath(resourceURL + 'office_resource'); 23 | 24 | // pdf workers 25 | Core.setPDFResourcePath(resourceURL + 'resource') 26 | if (custom.fullAPI) { 27 | Core.setPDFWorkerPath(resourceURL+ 'pdf_full') 28 | Core.setPDFAsmPath(resourceURL +'asm_full'); 29 | } else { 30 | Core.setPDFWorkerPath(resourceURL+ 'pdf_lean') 31 | Core.setPDFAsmPath(resourceURL +'asm_lean'); 32 | } 33 | 34 | // external 3rd party libraries 35 | Core.setExternalPath(resourceURL + 'external') 36 | 37 | window.addEventListener("message", receiveMessage, false); 38 | 39 | function receiveMessage(event) { 40 | /** 41 | * @note If you are using WebViewer version <= 7.x, please uncomment the line 42 | * below 43 | */ 44 | // const instance = readerControl; 45 | if (event.isTrusted && typeof event.data === 'object') { 46 | switch (event.data.type) { 47 | case 'OPEN_DOCUMENT': 48 | instance.loadDocument(event.data.file, { 49 | officeOptions: { 50 | disableBrowserFontSubstitution: true, 51 | } 52 | }) 53 | break; 54 | case 'OPEN_DOCUMENT_BLOB': 55 | const { blob, extension, filename, documentId } = event.data.payload; 56 | instance.loadDocument(blob, { extension, filename, documentId, 57 | officeOptions: { 58 | disableBrowserFontSubstitution: true, 59 | } 60 | }) 61 | break; 62 | case 'CLOSE_DOCUMENT': 63 | instance.closeDocument() 64 | break; 65 | default: 66 | break; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /force-app/main/default/staticresources/myfiles/config_apex.js: -------------------------------------------------------------------------------- 1 | var resourceURL = '/resource/' 2 | window.Core.forceBackendType('ems'); 3 | 4 | var urlSearch = new URLSearchParams(location.hash) 5 | var custom = JSON.parse(urlSearch.get('custom')); 6 | resourceURL = resourceURL + custom.namespacePrefix; 7 | 8 | /** 9 | * The following `window.Core.set*` functions point WebViewer to the 10 | * optimized source code specific for the Salesforce platform, to ensure the 11 | * uploaded files stay under the 5mb limit 12 | */ 13 | window.instance.UI.setFontPath('../../../..' + resourceURL + 'font_assets/fonts'); 14 | 15 | // office workers 16 | window.Core.setOfficeWorkerPath(resourceURL + 'office') 17 | window.Core.setOfficeAsmPath(resourceURL + 'office_asm'); 18 | window.Core.setOfficeResourcePath(resourceURL + 'office_resource'); 19 | 20 | //office editing 21 | window.Core.setOfficeEditorWorkerPath(resourceURL + 'office_edit'); 22 | 23 | window.Core.ContentEdit.setWorkerPath(resourceURL + 'content_edit'); 24 | window.Core.ContentEdit.setResourcePath(resourceURL + 'content_edit_resource'); 25 | 26 | // pdf workers 27 | window.Core.setPDFResourcePath(resourceURL + 'resource') 28 | if (custom.fullAPI) { 29 | window.Core.setPDFWorkerPath(resourceURL + 'pdf_full') 30 | } else { 31 | window.Core.setPDFWorkerPath(resourceURL + 'pdf_lean') 32 | } 33 | 34 | // external 3rd party libraries 35 | window.Core.setExternalPath(resourceURL + 'external') 36 | 37 | let currentDocId; 38 | 39 | async function saveDocument() { 40 | // SF document file size limit 41 | const docLimit = 5 * Math.pow(1024, 2); 42 | const doc = instance.Core.documentViewer.getDocument(); 43 | if (!doc) { 44 | return; 45 | } 46 | instance.openElement('loadingModal'); 47 | const fileSize = await doc.getFileSize(); 48 | const fileType = doc.getType(); 49 | const filename = doc.getFilename(); 50 | const xfdfString = await instance.Core.documentViewer.getAnnotationManager().exportAnnotations(); 51 | const data = await doc.getFileData({ 52 | // Saves the document with annotations in it 53 | xfdfString 54 | }); 55 | 56 | let binary = ''; 57 | const bytes = new Uint8Array(data); 58 | for (let i = 0; i < bytes.byteLength; i++) { 59 | binary += String.fromCharCode(bytes[i]); 60 | } 61 | 62 | const base64Data = window.btoa(binary); 63 | 64 | const payload = { 65 | title: filename.replace(/\.[^/.]+$/, ""), 66 | filename, 67 | base64Data, 68 | contentDocumentId: currentDocId 69 | } 70 | // Post message to LWC 71 | fileSize < docLimit ? parent.postMessage({ type: 'SAVE_DOCUMENT', payload }, '*') : downloadWebViewerFile(); 72 | } 73 | 74 | const downloadWebViewerFile = async () => { 75 | const doc = instance.Core.documentViewer.getDocument(); 76 | 77 | if (!doc) { 78 | return; 79 | } 80 | 81 | const data = await doc.getFileData(); 82 | const arr = new Uint8Array(data); 83 | const blob = new Blob([arr], { type: 'application/pdf' }); 84 | 85 | const filename = doc.getFilename(); 86 | 87 | downloadFile(blob, filename) 88 | } 89 | 90 | const downloadFile = (blob, fileName) => { 91 | const link = document.createElement('a'); 92 | // create a blobURI pointing to our Blob 93 | link.href = URL.createObjectURL(blob); 94 | link.download = fileName; 95 | // some browser needs the anchor to be in the doc 96 | document.body.append(link); 97 | link.click(); 98 | link.remove(); 99 | // in case the Blob uses a lot of memory 100 | setTimeout(() => URL.revokeObjectURL(link.href), 7000); 101 | }; 102 | 103 | window.addEventListener('viewerLoaded', async function () { 104 | instance.UI.hotkeys.on('ctrl+s, command+s', e => { 105 | e.preventDefault(); 106 | saveDocument(); 107 | }); 108 | 109 | // Create a button, with a disk icon, to invoke the saveDocument function 110 | instance.UI.setHeaderItems(function (header) { 111 | var myCustomButton = { 112 | type: 'actionButton', 113 | dataElement: 'saveDocumentButton', 114 | title: 'tool.SaveDocument', 115 | img: '', 116 | onClick: function () { 117 | saveDocument(); 118 | } 119 | } 120 | header.get('viewControlsButton').insertBefore(myCustomButton); 121 | }); 122 | 123 | // When the viewer has loaded, this makes the necessary call to get the 124 | // pdftronWvInstance code to pass User Record information to this config file 125 | // to invoke annotManager.setCurrentUser 126 | instance.Core.documentViewer.getAnnotationManager().setCurrentUser(custom.username); 127 | instance.UI.enableFeatures([instance.UI.Feature.ContentEdit]); 128 | }); 129 | 130 | window.addEventListener("message", receiveMessage, false); 131 | 132 | function receiveMessage(event) { 133 | if (event.isTrusted && typeof event.data === 'object') { 134 | switch (event.data.type) { 135 | case 'OPEN_DOCUMENT': 136 | instance.UI.loadDocument(event.data.file) 137 | break; 138 | case 'OPEN_DOCUMENT_BLOB': 139 | const { blob, extension, filename, documentId } = event.data.payload; 140 | console.log("documentId", documentId); 141 | currentDocId = documentId; 142 | instance.UI.loadDocument(blob, { extension, filename, documentId }) 143 | break; 144 | case 'DOCUMENT_SAVED': 145 | console.log(`${JSON.stringify(event.data)}`); 146 | instance.showErrorMessage('Document saved ') 147 | setTimeout(() => { 148 | instance.UI.closeElements(['errorModal', 'loadingModal']) 149 | }, 2000) 150 | break; 151 | case 'LMS_RECEIVED': 152 | instance.UI.loadDocument(event.data.payload.message, { 153 | filename: event.data.payload.filename, 154 | withCredentials: false 155 | }); 156 | break; 157 | case 'DOWNLOAD_DOCUMENT': 158 | downloadWebViewerFile(); 159 | break; 160 | case 'CLOSE_DOCUMENT': 161 | instance.closeDocument() 162 | break; 163 | default: 164 | break; 165 | } 166 | } 167 | } -------------------------------------------------------------------------------- /force-app/main/default/staticresources/myfiles/visualforce-page-viewing.js: -------------------------------------------------------------------------------- 1 | function initWebViewer(options) { 2 | console.log(options); 3 | 4 | var myObj = { 5 | libUrl: options.lib, 6 | fullAPI: true, 7 | namespacePrefix: '', 8 | }; 9 | var resourceConfig = options.myfiles + '/config.js'; 10 | 11 | WebViewer( 12 | { 13 | path: options.lib, 14 | custom: JSON.stringify(myObj), 15 | config: resourceConfig, 16 | fullAPI: myObj.fullAPI, 17 | // initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf', 18 | }, 19 | document.getElementById('viewer') 20 | ).then(instance => { 21 | console.log(instance); 22 | instance.loadDocument('https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf') 23 | // Use WebViewer API here 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/myfiles/webviewer-demo-annotated.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/myfiles/webviewer-demo-annotated.pdf -------------------------------------------------------------------------------- /force-app/main/default/staticresources/myfiles/webviewer-demo-annotated.xod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/myfiles/webviewer-demo-annotated.xod -------------------------------------------------------------------------------- /force-app/main/default/staticresources/myfiles/word.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/myfiles/word.docx -------------------------------------------------------------------------------- /force-app/main/default/staticresources/office.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/office.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/office.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/office_asm.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/office_asm.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/office_asm.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/office_edit.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/office_edit.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/office_edit.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/office_resource.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/office_resource.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/office_resource.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/pdf_full.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/pdf_full.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/pdf_full.zip -------------------------------------------------------------------------------- /force-app/main/default/staticresources/resource.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Public 4 | application/zip 5 | -------------------------------------------------------------------------------- /force-app/main/default/staticresources/resource.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/force-app/main/default/staticresources/resource.zip -------------------------------------------------------------------------------- /force-app/main/default/tabs/File_Browser.tab-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created by Lightning App Builder 4 | File_Browser 5 | 6 | Custom13: Box 7 | 8 | -------------------------------------------------------------------------------- /force-app/main/default/tabs/WebViewer.tab-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created by Lightning App Builder 4 | WebViewer 5 | 6 | Custom27: Laptop 7 | 8 | -------------------------------------------------------------------------------- /misc/app_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/misc/app_launcher.png -------------------------------------------------------------------------------- /misc/files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/misc/files.png -------------------------------------------------------------------------------- /misc/pdftron_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/misc/pdftron_app.png -------------------------------------------------------------------------------- /misc/webviewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ApryseSDK/webviewer-salesforce/02d351fedec5e1ec3af463d9cc66f745bedba3be/misc/webviewer.png -------------------------------------------------------------------------------- /sfdx-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageDirectories": [ 3 | { 4 | "path": "force-app", 5 | "default": true 6 | } 7 | ], 8 | "namespace": "", 9 | "sfdcLoginUrl": "https://login.salesforce.com", 10 | "sourceApiVersion": "51.0" 11 | } 12 | --------------------------------------------------------------------------------