6

I created a trigger that copies all incoming email-to-case attachments to Files so that users can select these's files when forwarding emails etc. The code I have so for is:

trigger CaseAttachment on Attachment (after insert) {

    Set<Id> Parents = new Set<Id>();

    for (attachment a : Trigger.New) {
        Parents.add(a.parentId);
    }

    for (EmailMessage e : [SELECT Id, ParentId FROM EmailMessage WHERE Id in :Parents]) { 
        for (Attachment a : Trigger.New) {
            if (e.Id == a.ParentId) {

                Case c = [SELECT Id, CaseNumber FROM Case WHERE id =: e.ParentId LIMIT 1];

                ContentVersion cv = new ContentVersion(); 
                cv.ContentLocation = 'S'; 
                cv.PathOnClient = a.Name; 
                cv.Origin = 'H'; 
                cv.OwnerId = a.OwnerId; 
                cv.Title = a.Name+'-'+c.CaseNumber; 
                cv.VersionData = a.Body;

                insert cv;

                ContentVersion cov = [SELECT ContentDocumentId FROM ContentVersion WHERE Id =: cv.Id]; 

                ContentDocumentLink cl = new ContentDocumentLink(LinkedEntityId = e.ParentId, ContentDocumentId = cov.ContentDocumentId, ShareType = 'V'); 

                insert cl;
            }
        }    
    }

}

When creating Files and linking them to records we need to create a ContentVersion first, and then link it to a record by creating a ContentDocumentLink. Right now I'm doing all the inserts in my loop because I cant think of a way to ContentVersion Ids any other way. I tried doing something like this:

trigger CaseAttachment on Attachment (after insert) {

    Set<Id> Parents = new Set<Id>();
    List<ContentVersion> newFiles = new List<ContentVersion>();
    List<ContentDocumentLink> newFileLink = new List<ContentDocumentLink>();

    for (attachment a : Trigger.New) {
        Parents.add(a.parentId);
    }

    for (EmailMessage e : [SELECT Id, ParentId FROM EmailMessage WHERE Id in :Parents]) { 
        for (Attachment a : Trigger.New) {
            if (e.Id == a.ParentId) {
                ContentVersion cv = new ContentVersion(); 
                cv.ContentLocation = 'S'; 
                cv.PathOnClient = a.Name; 
                cv.Origin = 'H'; 
                cv.OwnerId = a.OwnerId; 
                cv.Title = a.Name; 
                cv.VersionData = a.Body;
                cv.ExternalDocumentInfo1 = e.ParentId;
                newFiles.add(cv); 
            }

        }
    }

    insert newFiles;

    for (ContentVersion cov : newFiles) {
        ContentDocumentLink cl = new ContentDocumentLink(LinkedEntityId = cov.ExternalDocumentInfo1, ContentDocumentId = cov.ContentDocumentId, ShareType = 'V');
        newFileLink.add(cl);
    }

    insert newFileLink;

}

I'm looking to fetch the correct ContentVersions by adding the Case Id to a field, in this case ExternalDocumentInfo1. This doesn't seem to work however.

  • This is a well-posed first question, I've upvoted it, and hope others do the same. – Derek F Dec 30 '17 at 13:46
2

In this line

ContentDocumentLink cl = new ContentDocumentLink(
                               LinkedEntityId = cov.ExternalDocumentInfo1, 
                               ContentDocumentId = cov.ContentDocumentId, ShareType = 'V');

the value of cov.ContentDocumentId is not known because you need to query for it after doing insert newFiles

SFDC automatically provides the value of the ContentVersion.Id when you do

insert newfiles;

but will not implicitly return the value of the behind-the-scenes-created ContentDocument.Id. The analogous way to think about this is normal SObject inserts that have auto-number or formula fields - those aren't available to the code that does the insert until a subsequent query is done.

So, your code after insert newFiles should be:

map<ID,ContentVersion> cvsById = new map<ID,ContentVersion>(
  [SELECT Id, ContentDocumentId 
    FROM ContentVersion
    WHERE ID = :newFiles]
  );
for (ContentVersion cv : newFiles) {
  newFileLinks.add(new ContentDocumentLink(
              LinkedEntityId = cv.ExternalDocumentInfo1, 
              ContentDocumentId = cvsById.get(cv.Id).ContentDocumentId,
                               ShareType = 'V'));
}
insert newFileLinks; // I renamed the variable to best practices (plural)
  • Thanks for having a look! So the issue is not the LinkedEntityId but the cov.ContentDocumentId not being known at this point? What would this subsequent query look like exactly? I'm struggling with the syntax. Do we update the newFiles list somehow? Or are we storing the Ids in a Set? And if so how do we reference them correctly in our next insert statement? – Ruud Dirksen Jan 1 '18 at 12:27
  • @RuudDirksen - updated answer – cropredy Jan 1 '18 at 17:42
  • Great! That did the trick :). Thanks for all the help, really appreciate it. – Ruud Dirksen Jan 3 '18 at 16:03
  • @RuudDirksen as a courtesy to the Community, mark the answer as Accepted. – cropredy Jan 3 '18 at 16:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.