Hi Everyone,
In this blog post, we will see how we can convert Attachments to Files using Apex Code.
As we know that salesforce is using Lightning Environment and Attachments will be deprecated in near future. So, Converting the attachment to Files and Notes to Enhanced Notes is very crucial.
If you do not know about how the Files are stored in Salesforce, Please visit this Article.
In order to convert the attachments to files, we need to insert the new file record into Salesforce. But if we insert the new record we can lose Created Date, LastModified Date, CreatedById, and Last Modified By Id. So to keep these we need to enable few settings in salesforce org.
Enable the ‘Create Audit Fields’ permission
- From Setup, enter User in Quick Find box and select User Interface.
- Select the checkbox for Enable “Set Audit Fields upon Record Creation” and “Update Records with Inactive Owners” User Permissions.
- Click Save.
Once you have enabled the above permission, create a Custom Permission Set and Enabled below permissions under “System Permissions.“
- Set Audit Fields upon Record Creation – Allow the User to set audit fields (like ‘Created By’ or ‘Last Modified By’) when you create a record via API importing tools like Data Loader.
- Update Records with Inactive Owners – Allow the User to update record owner and sharing-based records with inactive owners.
Now, Assign this permission set to the user who will be converting the attachment to files.
Now, you are ready to convert your attachment or notes to the enhanced version of both.
Below is the full code for the same.
public class NotesAndAttachmentConverterHelper {
public static void convertAttachment(Set<Id> parentIdsSet){
Savepoint sp = Database.setSavepoint();
List<Attachment> lstAttachment = [SELECT Id, ParentId, Name,Description, CreatedById,
CreatedDate, LastModifiedById, LastModifiedDate,
OwnerId, isPrivate, Body
FROM Attachment where
ParentId IN:parentIdsSet
LIMIT 10000];
try{
Map<Id, List<Blob>> mapAttachmentIdLstBlob = new Map<Id, List<Blob>>();
Map<Id, Attachment> mapIdAttachment = new Map<Id, Attachment>();
for(Attachment attach: lstAttachment){
String bodyCont = attach.Body!=null ? String.valueOf(attach.Body).replace('&','&')
.replace('<','<').replace('>','>')
.replace('"','"').replace('\'','''):'.';
Blob tmpBlob = Blob.valueOf(bodyCont);
if(!mapAttachmentIdLstBlob.containsKey(attach.Id)){
mapAttachmentIdLstBlob.put(attach.Id, new List<Blob>());
}
mapAttachmentIdLstBlob.get(attach.Id).add(tmpBlob);
mapIdAttachment.put(attach.Id, attach);
}
Map<Id, List<ContentVersion>> mapActIdLstCV =
createContentVersionAttachment(mapAttachmentIdLstBlob, mapIdAttachment);
List<ContentDocumentLink> lstCVDL = updateContentDocLink(mapActIdLstCV);
}Catch(Exception ex){
Database.rollback(sp);
System.debug('Error-> '+ex.getLineNumber()+' Message : '+ex.getMessage());
throw ex;
}
}
public static void convertNotes(Set<Id> parentIdsSet){
List<Note> lstNot = [SELECT Id, ParentId, Title, Body FROM Note where
ParentId IN:parentIdsSet LIMIT 10000];
try{
Map<Id, List<Blob>> mapNoteIdLstBlob = new Map<Id, List<Blob>>();
Map<Id, Note> mapIdNote = new Map<Id, Note>();
for(Note n: lstNot){
String bodyCont = n.Body!=null ? String.valueOf(n.Body).replace('&','&').replace('<','<').replace('>','>').replace('"','"').replace('\'','''):'.';
Blob tmpBlob = Blob.valueOf(bodyCont);
if(!mapNoteIdLstBlob.containsKey(n.Id)){
mapNoteIdLstBlob.put(n.Id, new List<Blob>());
}
mapNoteIdLstBlob.get(n.Id).add(tmpBlob);
mapIdNote.put(n.Id, n);
}
Map<Id, List<ContentVersion>> mapActIdLstCV = createContentVersion(mapNoteIdLstBlob, mapIdNote);
List<ContentDocumentLink> lstCVDL = updateContentDocLink(mapActIdLstCV);
}Catch(Exception e){
}
}
public static List<ContentDocumentLink> updateContentDocLink(Map<Id, List<ContentVersion>> actIdLstCVMap){
Set<String> cvSet = new Set<String>();
Map<Id, Id> cvIdMap = new Map<Id, Id>();
for(Id accId: actIdLstCVMap.KeySet()){
for(ContentVersion cv: actIdLstCVMap.get(accId)){
cvSet.add(cv.Id);
cvIdMap.put(cv.Id, accId);
}
}
List<ContentVersion> cvLst = [SELECT ContentDocumentId FROM ContentVersion WHERE (Id IN :cvSet)];
List<ContentDocumentLink> cdlLst = new List<ContentDocumentLink>();
for(ContentVersion cv: cvLst){
ContentDocumentLink cdl = new ContentDocumentLink();
cdl.ContentDocumentId = cv.ContentDocumentId;
cdl.LinkedEntityId = cvIdMap.get(cv.Id);
cdl.ShareType = 'I';
cdlLst.add(cdl);
}
INSERT cdlLst;
return cdlLst;
}
public static Map<Id, List<ContentVersion>> createContentVersion(Map<Id, List<Blob>> mapNoteIdLstBlob,
Map<Id, Note> mapIdNote){
Map<Id, List<ContentVersion>> retMap = new Map<Id, List<ContentVersion>>();
List<ContentVersion> lstCV = new List<ContentVersion>();
for(Id noteId : mapNoteIdLstBlob.keySet()){
List<Blob> noteBlob = mapNoteIdLstBlob.get(noteId);
for(Blob b:noteBlob){
ContentVersion cv = new ContentVersion();
cv.ContentLocation = 'S';
cv.VersionData = b;
cv.CreatedById = mapIdNote.get(noteId).CreatedById;
cv.Title = mapIdNote.get(noteId).Title;
cv.PathOnClient = mapIdNote.get(noteId).Title+'.snote';
lstCV.add(cv);
if(!retMap.containsKey(mapIdNote.get(noteId).ParentId)){
retMap.put(mapIdNote.get(noteId).ParentId,new List<ContentVersion>());
}
retMap.get(mapIdNote.get(noteId).ParentId).add(cv);
}
}
INSERT lstCV;
return retMap;
}
public static Map<Id, List<ContentVersion>> createContentVersionAttachment(Map<Id, List<Blob>> mapNoteIdLstBlob,
Map<Id, Attachment> mapAttachment){
Savepoint sp = Database.setSavepoint();
Map<Id, List<ContentVersion>> contentVersionMap = new Map<Id, List<ContentVersion>>();
List<ContentVersion> newFileVersionList = new List<ContentVersion>();
for(Id attachmentId : mapNoteIdLstBlob.keySet()){
List<Blob> noteBlob = mapNoteIdLstBlob.get(attachmentId);
for(Blob b:noteBlob){
ContentVersion newFileVersion = new ContentVersion(
versionData = mapAttachment.get(attachmentId).body,
title = mapAttachment.get(attachmentId).name,
description = mapAttachment.get(attachmentId).description,
pathOnClient = '/' + mapAttachment.get(attachmentId).name,
firstPublishLocationId = mapAttachment.get(attachmentId).parentId,
sharingPrivacy = ( mapAttachment.get(attachmentId).isPrivate ? 'P' : 'N' ),
createdById = mapAttachment.get(attachmentId).ownerId,
createdDate = mapAttachment.get(attachmentId).createdDate,
lastModifiedById = mapAttachment.get(attachmentId).lastModifiedById,
lastModifiedDate = mapAttachment.get(attachmentId).lastModifiedDate
//Original_Record__c = mapAttachment.get(attachmentId).id,
//Parent_Id__c = mapAttachment.get(attachmentId).parentId
);
newFileVersionList.add(newFileVersion);
if(!contentVersionMap.containsKey(mapAttachment.get(attachmentId).ParentId)){
contentVersionMap.put(mapAttachment.get(attachmentId).ParentId,new List<ContentVersion>());
}
contentVersionMap.get(mapAttachment.get(attachmentId).ParentId).add(newFileVersion);
}
}
try{
Database.DMLOptions dmlOptions = new Database.DMLOptions();
dmlOptions.OptAllOrNone = false;
List<Database.SaveResult> saveReultRecords = Database.insert(newFileVersionList, dmlOptions);
For(Database.SaveResult sr : saveReultRecords){
if(sr.isSuccess()){
}else if(!sr.isSuccess()){
}
}
return contentVersionMap;
}catch(Exception ex){
Database.rollback(sp);
throw ex;
}
}
}
Method description
Sr.No | Method Name | Description |
1 | convertAttachment(Set parentIdsSet) | This method accepts the Set of Record Id of the parent Object which have Notes & Attachment and Convert all related attachment to files and associate those files to the record. |
2 | convertNotes(Set parentIdsSet) | This method accepts the Set of Record Id of the parent Object which has Notes & Attachment and Converts all related Notes to Content Note and associates those Notes to the record. |
Note: – Both Methods only converts 10000 Attachment or Notes at once. If you want to convert more then change the Limit Statement in the SOQL Query.
Thanks for reading 🙂 Sharing is caring 🙂
#DeveloperGeeks #Salesforce #SfdcPanther #AskPanther
Hi Amit,
Thanks for working hard on this and sharing a nice solution for a common challenge I believe many users have or will face.
I only wanted to check and see about the apex heap size limit issue here, because I believe that will be the biggest challenge rather than the number of attachments for any parent Id. Please share your thoughts.
Yes. Heap Size would be the issue. That is why the recommendations is to use the solution with batch apex.