Saturday, February 16, 2019

Salesforce ContentDocumentLink Trigger

Topics Covered in this trigger
1.   Changing Filename based on Parent Record.
2.   Avoiding Duplicate Files insertion.
3.   Checking File Name Size.

Note: Salesforce not supporting to show custom error message while uploading files. maybe in future salesforce resolve this issue.
For more information about issue check below link
Custom Error message Not Showing When File Uploading

Example:
In this Example, I am checking only account object you can extend this functionality as per your requirement.

ContentDocumentLinkTrigger
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert) {
    if(trigger.isAfter) {
        if(trigger.isInsert) {
            ContentDocumentLinkTriggerHandler.onAfterInsert(trigger.new);
        }
    }
}

ContentDocumentLinkTriggerHandler
public inherited sharing class ContentDocumentLinkTriggerHandler {
    
    public static void onAfterInsert(list<ContentDocumentLink> lstCntLinks) {
        String strObjPrefix;
        Set<Id> setCntDocIds = new set<Id>();
        set<Id> setAccIds = new set<Id>();
        map<Id, Account> mapAccs;
        
        for(ContentDocumentLink clIterator : lstCntLinks) {
            strObjPrefix = String.valueOf(clIterator.LinkedEntityId).substring(0, 3);
            if(strObjPrefix == Account.sObjectType.getDescribe().getKeyPrefix()) {
                setCntDocIds.add(clIterator.ContentDocumentId);
                setAccIds.add(clIterator.LinkedEntityId);
            }
        }
        
        if(!setCntDocIds.isEmpty()) {
            if(!setAccIds.isEmpty()) {
                mapAccs = new map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :setAccIds]);
            }
        }
        
        // Get content document object for current set of files
        map<Id, ContentDocument> mapContentDocuments = new map<Id, ContentDocument>([SELECT Id, Title, FileExtension FROM ContentDocument WHERE Id IN :setCntDocIds]);
        
        // Retrieve all the existing attachments associated with the parent records
        map<Id, set<String>> mapParentIdFilenames = new map<Id, set<String>>();
        for (ContentDocumentLink cdlIterator : [SELECT Id, ContentDocumentId, LinkedEntityId, ContentDocument.Title, ContentDocument.FileExtension FROM ContentDocumentLink WHERE LinkedEntityId IN :setAccIds AND ContentDocumentId NOT IN :setCntDocIds]) {
            if (!mapParentIdFilenames.containsKey(cdlIterator.LinkedEntityId)) {
                mapParentIdFilenames.put(cdlIterator.LinkedEntityId, new set<String>());
            }
            mapParentIdFilenames.get(cdlIterator.LinkedEntityId).add(cdlIterator.ContentDocument.Title + (String.isBlank(cdlIterator.ContentDocument.FileExtension) ? '' : '.' + cdlIterator.ContentDocument.FileExtension));
        }
        
        Account objAcc;
        list<ContentDocument> lstCntDocsToUpdate = new list<ContentDocument>();
        
        for(ContentDocumentLink cdlIterator : lstCntLinks) {
            ContentDocument objCntDoc = mapContentDocuments.get(cdlIterator.ContentDocumentId);
            String strFilename = '';
            if(mapAccs.containsKey(cdlIterator.LinkedEntityId)) {
                objAcc = mapAccs.get(cdlIterator.LinkedEntityId);
                strFilename = objAcc.Name + '_Account_' + objCntDoc.Title + '.' + objCntDoc.FileExtension;
            }
            
            if(!String.isBlank(strFilename)) {
                set<String> setExistingFilenames = mapParentIdFilenames.get(cdlIterator.LinkedEntityId);
                if(objCntDoc.Title.length() > 40) {
                    cdlIterator.addError('Filename to long.');
                }
                else if (setExistingFilenames != null && setExistingFilenames.contains(strFilename + (String.isBlank(objCntDoc.FileExtension) ? '' : '.' + objCntDoc.FileExtension))) {
                    cdlIterator.addError('Attaching duplicate file, please choose different file.');
                }
                else {
                    objCntDoc.Title = strFilename;
                    lstCntDocsToUpdate.add(objCntDoc);
                }
            } 
        }
        if(!lstCntDocsToUpdate.isEmpty()) {
            update lstCntDocsToUpdate;
        }
        
    }
}

Result

Resources
ContentDocumentLink
ContentVersion
ContentDocument

2 comments:

  1. I get error:

    FATAL_ERROR System.QueryException: Implementation restriction: ContentDocumentLink requires a filter by a single Id on ContentDocumentId or LinkedEntityId using the equals operator or multiple Id's using the IN operator.

    I believe its in Line 34. I am trying to perform this on Work Order instead of account but i didn't think that would matter

    ReplyDelete
  2. do you have test class ?

    ReplyDelete