How to convert Attachment to Files in Salesforce

on

|

views

and

comments

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

  1. From Setup, enter User in Quick Find box and select User Interface.
  2. Select the checkbox for Enable “Set Audit Fields upon Record Creation” and “Update Records with Inactive Owners” User Permissions.
  3. 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('&','&amp;')
                                                 .replace('<','&lt;').replace('>','&gt;')
                                                 .replace('"','&quot;').replace('\'','&#39;'):'.';
                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('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('"','&quot;').replace('\'','&#39;'):'.';
                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

Amit Singh
Amit Singhhttps://www.pantherschools.com/
Amit Singh aka @sfdcpanther/pantherschools, a Salesforce Technical Architect, Consultant with over 8+ years of experience in Salesforce technology. 21x Certified. Blogger, Speaker, and Instructor. DevSecOps Champion
Share this

Leave a review

Excellent

SUBSCRIBE-US

Book a 1:1 Call

Must-read

How to Utilize Salesforce CLI sf (v2)

The Salesforce CLI is not just a tool; it’s the cornerstone of development on the Salesforce Platform. It’s your go-to for building, testing, deploying, and more. As one of the most important development tools in our ecosystem

Save the day of a Developer with Apex Log Analyzer

Table of Contents What is Apex Log Analyzer? Apex Log Analyzer, a tool designed with Salesforce developers in mind, is here to simplify and accelerate your...

Salesforce PodCast

Introduction Hey Everyone, Welcome to my podcast, the first-ever podcast in India for Salesforce professionals. Achievement We are happy to announce that we have been selected as Top...

Recent articles

More like this

2 COMMENTS

  1. 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.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

5/5

Stuck in coding limbo?

Our courses unlock your tech potential