2

I want to automatically change the name of a file as soon as it is created. To keep things simple, let's say I want to add "test-" before the file name.

I think this has to be done via a trigger, but if you have any other solution, please feel free to offer it.

There are several Salesforce objects that have to do with files: ContentDocument, ContentVersion and some others. By experimentation, I think that ContentDocument is the one that is most suitable for a trigger in my case.

So this is my experimental trigger, yes, if has not been bulkified, so please don't comment on that (although I know some people will do it anyway):

trigger ContentDocumentTrigger on ContentDocument(after insert) {
    List<Id> cdIds = new List<Id>();
    for (ContentDocument cd : Trigger.new) {
        System.debug('cd.Id = ' + cd.Id);
        System.debug('cd.Title = ' + cd.Title);
        cdIds.add(cd.Id);
    }
    System.debug('cdIds.size = ' + cdIds.size());
    List<ContentDocument> cdsToUpdate = new List<ContentDocument>();
    for (Id cdId : cdIds) {
        System.debug('cdId = ' + cdId);
        ContentDocument cd = [SELECT Id, Title FROM ContentDocument WHERE Id = :cdId][0];
        System.debug('cd.Id = ' + cd.Id);
        System.debug('cd.Title = ' + cd.Title);
        cd.Title = 'test-' + cd.Title;
        cdsToUpdate.add(cd);
    }
    update cdsToUpdate;
}

It is not possible to update the title in a before insert trigger, which would be the simplest solution. That will give you an error that title is not writeable.

So it must be an after insert trigger and since Trigger.new objects can not be updated directly (it results in a "object is read-only" error), I think it must be done in the way that I do it here.

So what goes wrong here? The Ids that I collect in the List turn out to be useless. The SELECT query results in zero returned records, even though I see in the debug log that the list contains seemingly valid Ids.

Am I making a mistake in my trigger? Is this even a good way of accomplishing what I want or do I have to do it in a completely different way?

2
  • IIRC you have to do triggers on ContentVersion
    – zaitsman
    Commented Aug 25, 2020 at 8:13
  • @zaitsman I tried that, I have a before+after insert trigger for ContentVersion, but it is not triggered at all. Commented Aug 25, 2020 at 8:44

2 Answers 2

3

I tried the solution and created a trigger on ContentDocumentLink and it works. Like whenever I upload a file directly it changes the name with "test-"before the file name.

Trigger on ContentDocumentLink

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

Handler Code-

public class ContentDocumentLinkTriggerHandler {
    public static void onAfterInsert(list<ContentDocumentLink> contDocList) {
        
        Set<Id> contDocId = new set<Id>();
        set<Id> accIds = new set<Id>();
        
        for(ContentDocumentLink cdl : contDocList) { 
            contDocId.add(cdl.ContentDocumentId);
        }
        
        map<Id, ContentDocument> mapContentDocuments = new map<Id, ContentDocument>([SELECT Id, Title, FileExtension FROM ContentDocument WHERE Id IN :contDocId]);
        
        list<ContentDocument> listUpdate = new list<ContentDocument>();
        
        for(ContentDocumentLink cdlIterator : contDocList) 
        {
            ContentDocument objCntDoc = mapContentDocuments.get(cdlIterator.ContentDocumentId);
            String strFilename = '';
            
            strFilename = 'test-' +objCntDoc.Title+'.'+ objCntDoc.FileExtension;
            if(!String.isBlank(strFilename)) {
                objCntDoc.Title = strFilename;
                listUpdate.add(objCntDoc);
            } 
        }
        if(!listUpdate.isEmpty()) {
            update listUpdate;
        }
        
    }
}

I believe this solves your query.

Cheers

1

So it must be an after update trigger and since Trigger.new objects can not be updated directly (it results in a "object is read-only" error), I think it must be done in the way that I do it here.

I guess you meant after insert trigger in the above statement?

Updating the file name (Title field) works in before update, but that's not your objective here.

So what goes wrong here? The Ids that I collect in the List turn out to be useless. The SELECT query results in zero returned records, even though I see in the debug log that the list contains seemingly valid Ids.

So, if you look at the trigger & order of execution details, committing all the DML operations to the database is the last (or rather before the last) step. In your case, while the trigger is still executing, the file (as well as the corresponding records such as ContentVersion, ContentDocument etc.) are not yet committed to the DB. And, in your code, you are intercepting this process in after trigger and trying to query for a file that is not committed to the DB yet. Hence, the zero query results.

It is not possible to update the title in a before insert trigger, which would be the simplest solution. That will give you an error that title is not writeable.

Insert is not allowed in ContentDocument object thru apex code, but update is. I guess you might be already aware of this.

I was able to write a simple before Insert trigger on ContentVersion to update the file name as soon as its uploaded. I wrote this code/ tested it only to validate the file title update DML and you possibly need improve it to match your requirements as well as incorporate best practices. This code worked in my dev org.

trigger ContentVersionTrigger on ContentVersion (before insert) {
    if(Trigger.isInsert && Trigger.IsBefore){
        for(ContentVersion cv: Trigger.new){
            Integer versionCount = 0; 
            // Query ContentVersion object to check that this is document/ file creation and not version update.
            versionCount = [SELECT count() FROM ContentVersion WHERE ContentDocumentId = :cv.Id];
            System.debug(versionCount);
            if(versionCount <= 0){
                cv.Title = 'bicvt_' + cv.Title;
            }
            System.debug(cv.Title);
        }        
    }
}

Hope this helps!

7
  • Yes, it should be after insert, not after update. I fixed my question. I will try this solution, thanks! Commented Aug 25, 2020 at 14:56
  • @SanderdeJong If you have existing triggers related to ContentDocument, ContentDocumentLink etc., ensure that none of them are running into error (or disable them temporarily for testing) because the triggers on the content related objects execute in a specific sequence and hence, failure in one trigger could completely stop the execution of subsequent related triggers.
    – arut
    Commented Aug 25, 2020 at 15:07
  • Unfortunately, in my case this trigger isn't called. It is because the file is created from within Salesforce (PDF creation in a quote object) and not uploaded to Salesforce. So a ContentVersion object is created, I can see that, but the trigger is not fired. I am going to try the other solution, because I know the ContentDocumentLink trigger is fired. Commented Aug 26, 2020 at 6:50
  • 1
    After several Google searches, I found out that apparently it is a feature that the ContentVersion trigger is not fired for documents created from inside Salesforce. Only Salesforce knows why that is. I have found at least one idea to request Salesforce to change this. Commented Aug 26, 2020 at 7:03
  • 1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .