function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
hero zerohero zero 

ContentDocumentLink trigger change visibility of file with condition

Hi,

I am newbie in Salesforce, what i am trying to do is creating a trigger for image files that starts with 'ABC' to open them to community. So this is my trigger in below but it gives this error when i am trying to upload a file starts with 'ABC':

FATAL_ERROR System.FinalException: Record is read-only
FATAL_ERROR Trigger.ContentDocumentLinkTrigger: line 4, column 1

I have used after insert because i am trying to fetch DocumentContentID and even if i change my trigger to before trigger it works other way around. It doesn't work for other image files. It just works for files with 'ABC' and it changes its visibility to on before trigger.

Can you please help me out? I couldn't sort it out in some way. I am using files by the way not notes&attachments.

trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert) {
  for (ContentDocumentLink cdl : Trigger.new) {
            if (getFileExtensionAndTitle(cdl)) {
                cdl.Visibility = 'AllUsers';
            }
        }
    
     public static Boolean getFileExtensionAndTitle(ContentDocumentLink cdl) {
        String docId = cdl.ContentDocumentId;
        ContentDocument contentdocument = [SELECT FileExtension,Title FROM ContentDocument WHERE Id = :docId LIMIT 1];
        Boolean isExpected = contentdocument.Title.startsWith('ABC') && (contentdocument.FileExtension.equals('jpg') || contentdocument.FileExtension.equals('jpeg') || contentdocument.FileExtension.equals('png'));
        return isExpected;
    }
}
Best Answer chosen by hero zero
sfdcBahusfdcBahu
Hi Hero,

First of all, dont write the SOQL's inside the For loop. use collection variables to bulkify the logic. below should work, please try.

Set<id> AllDocids = new set<id>();
Set<id> EligibleDocIds = new set<id>();

// you can use this logic in before insert.
trigger ContentDocumentLinkTrigger on ContentDocumentLink (before insert) {
 
 //Get all ids
 for (ContentDocumentLink cdl : Trigger.new) {
           AllDocids.add(cdl.ContentDocumentId);
        }
        
   //get all the eligible contentdocument ids in a diff set
   
   for(ContentDocument CD: [Select id,FileExtension,title from ContentDocument where id in: AllDocids])
   {
        if(CD.Title.startsWith('ABC') && (CD.FileExtension.equals('jpg') || CD.FileExtension.equals('jpeg') || CD.FileExtension.equals('png')))
            EligibleDocIds.add(CD.Id);
   }
   
   // now reiterate the links to change the visibility where the doc is eligible
   
   for (ContentDocumentLink cdl : Trigger.new) {
           if(EligibleDocIds.contains(cdl.ContentDocumentId))
             cdl.Visibility = 'AllUsers';
        }
        
    }

All Answers

sfdcBahusfdcBahu
Hi Hero,

First of all, dont write the SOQL's inside the For loop. use collection variables to bulkify the logic. below should work, please try.

Set<id> AllDocids = new set<id>();
Set<id> EligibleDocIds = new set<id>();

// you can use this logic in before insert.
trigger ContentDocumentLinkTrigger on ContentDocumentLink (before insert) {
 
 //Get all ids
 for (ContentDocumentLink cdl : Trigger.new) {
           AllDocids.add(cdl.ContentDocumentId);
        }
        
   //get all the eligible contentdocument ids in a diff set
   
   for(ContentDocument CD: [Select id,FileExtension,title from ContentDocument where id in: AllDocids])
   {
        if(CD.Title.startsWith('ABC') && (CD.FileExtension.equals('jpg') || CD.FileExtension.equals('jpeg') || CD.FileExtension.equals('png')))
            EligibleDocIds.add(CD.Id);
   }
   
   // now reiterate the links to change the visibility where the doc is eligible
   
   for (ContentDocumentLink cdl : Trigger.new) {
           if(EligibleDocIds.contains(cdl.ContentDocumentId))
             cdl.Visibility = 'AllUsers';
        }
        
    }
This was selected as the best answer
hero zerohero zero
Thank you so much Bahu, it works how i want it to. I have a question about before insert here. Shouldn't we use after insert when we try to get id of any object? How did we get ContentDocumentId on before insert?
sfdcBahusfdcBahu
Thats correct only if you are looking for an id of the record itself.  You are running the trigger on ContentDocumentLink, and ContentDocumentID is a parent record's id which will be availble on before insert. so just in case of scenarios where we need the id of the ContentDocumentLink itself, then we need to run the trigger on After Insert Context. hope it makes sense? Please make the solution as Best answer if it helps.
hero zerohero zero
Hi again Bahu,
I am trying to write test class for ContentDocumenLink trigger that you showed it to me. This my method and my title doesn't start with 'ABC' so Visibility of ContentDocumentLink should be 'InternalUsers' but i am getting this error.

System.AssertException: Assertion Failed: Expected: InternalUsers, Actual: AllUsers
 
So it looks like Salesforce doesn't change Visibility to InternalUsers even community partner is enabled in org when creating document from test enviroment. What can i do to handle this situation?

@isTest
  public static void ImageTest() {
            ContentVersion docVersion = new ContentVersion();
            docVersion.Title = '24032020_12356';
            docVersion.PathOnClient = docVersion.Title + '.png';
            docVersion.VersionData = Blob.valueOf('Test Content');
            docVersion.IsMajorVersion = true;
            docVersion.ContentLocation = 'S';
            insert docVersion;
ContentVersion docQuery = [SELECT ContentDocumentId FROM ContentVersion WHERE Title = '24032020_12356' LIMIT 1];
ContentDocumentLink docLinkQuery = [SELECT Id,Visibility FROM ContentDocumentLink WHERE ContentDocumentId =: docQuery.ContentDocumentId LIMIT 1];
            System.assertEquals('InternalUsers', docLinkQuery.Visibility);
        }
sfdcBahusfdcBahu
Hi there,

I see that you are inserting the Contentversion, but linking to any sobject record. the Internalusers vs Allusers differentiation mainly comes in to picture when you actually attach the file to a record. So create an Account or any record, and  another contentdocumentlink to attach CV to the Record. Then check that CDL's visibility. 
hero zerohero zero
That worked like a charm. Thank you so much man!! Appreciated a lot!