0

I have created a custom field(lookup field to opportunity) on Content version object that would denote the opportunity id that file is connected to. The custom field is Opportunity__c. Here is the apex trigger I worked on, but it gives me null in the custom field.

Trigger on Contentversion:

trigger populateOpportunityID on ContentVersion (after insert) {

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

 for(ContentVersion cv:trigger.new)
{
    if(cv.ContentDocumentId != null)
    {
        contentDocumentIdSet.add(cv.ContentDocumentId);
    }
}

ContentDocumentLink cdl = [SELECT ContentDocumentId, LinkedEntityId FROM ContentDocumentLink WHERE ContentDocumentId IN:contentDocumentIdSet Limit 1];

List<Opportunity> oppList = [SELECT Id, name FROM Opportunity where Id =:cdl.LinkedEntityId];   

List<ContentVersion> cvList = [SELECT Id, Opportunity__c, ContentDocumentId FROM ContentVersion where Id IN: Trigger.newMap.keySet()];

for(ContentVersion cv:cvList)
{
    if(oppList.size() > 0)
    {
        for(Opportunity a : oppList){
            cv.Opportunity__c = a.Id; 
        }
    }
    else{
        cv.Opportunity__c = null;
    }           
}
update cvList;
}

Trigger on ContentDocumentLink:

trigger OpportunityId on ContentDocumentLink (after insert) {


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

for(ContentDocumentLink temp:trigger.new)

{

    if(temp.LinkedEntity.Type=='Opportunity')

    {

       contentDocIds.add(temp.ContentDocumentId); 

    }

}

 List<sObject> cvList = [select id, Opportunity__c, Checksum, ContentBodyId, 
 ContentDocumentId, ContentLocation, ContentModifiedById,
 ContentModifiedDate, ContentSize, ContentUrl, Description, 
 ExternalDataSourceId, ExternalDocumentInfo1,
 ExternalDocumentInfo2, FeaturedContentBoost, FeaturedContentDate, 
 FileExtension, FileType, FirstPublishLocationId, IsAssetEnabled, 
 IsLatest,IsMajorVersion, NegativeRatingCount,  Origin, 
 OwnerId, PathOnClient, PositiveRatingCount, PublishStatus, RatingCount, 
 ReasonForChange, SharingOption,SharingPrivacy,
 TagCsv, TextPreview, Title, VersionNumber, VersionData, LinkedEntityId__c 
 FROM ContentVersion WHERE ContentDocumentId IN :contentDocIds AND IsLatest = 
 true]; 


  }
  • 1
    Are you inserting new ContentVersions on existing documents, or new ContentVersions that don't have documents yet? – David Reed Oct 7 at 22:09
  • 1
    I would write the trigger on CDL after insert ; filter on LinkedEntityId.SobjectType and fetch the most recent CV from the CDL's CD – cropredy Oct 7 at 22:21
  • @DavidReed It can be any case – Anonymous Oct 7 at 22:22
  • @cropredy yes that's where I was heading to, I just wrote the trigger on ContentDocumentLink and added in my question above, but I am still not sure how to populate opportunity Id from ContentDocumentLink to Contentversion, since ContentDocumentLink is related to ContentDocument not Contentversion. – Anonymous Oct 7 at 22:56
0

Your trigger should look something like this ....

Map<Id,Id> oppoIdsByContentDocumentId = new Map<Id,Id>();  // the xref
ContentVersion[] cvs = new List<ContentVersion>();         // for updating CV

for (ContentDocumentLink cdl: Trigger.new) {               // build xref for CDLs of Oppos only
  if (cdl.LinkedEntityId.getSobjectType() == Opportunity.SObjectType) {
    oppoIdsByContentDocumentId.put(cdl.ContentDocumentId,cdl.LinkedEntityId);
  }
}

if (!oppoIdsByContentDocumentId.keySet().isEmpty()) {
  for (ContentVersion cv : // Find the latest ContentVersion for the xref'd ContentDocumentIds
    [SELECT Id, ContentDocuemntId FROM ContentVersion WHERE ContentDocumentId IN :oppoIdsByContentDocumentId.keySet() 
                                       AND IsLatest = TRUE] ) {
    cvs.add(new ContentVersion (
       Id = cv.Id, Opportunity__c = oppoIdsByContentDocumentId.get(cv.ContentDocumentId));

  }
}

update cvs; // if empty, no DML is burned so no need to test for empty
  • Thank you so much for the help @Cropredy. I tried running the above code, it throws me the error when trying to save at the line if (!oppoIdsByContentDocumentId.keySet.isEmpty()). The error is Error: "Compile Error: Variable does not exist: keySet at line 13 column 33" – Anonymous Oct 8 at 14:42
  • Typo. Fixed. Keyset() – cropredy Oct 8 at 15:06
  • Thanks @Cropredy. I added the ContentDocumentId in the SOQL query [SELECT Id FROM ContentVersion WHERE ContentDocumentId IN :oppoIdsByContentDocumentId.keySet() as otherwise it was throwing me an error later. But other than that the code works amazingly. It does populate the opportunity__c field in case I upload a new file or upload a new version of an existing file. – Anonymous Oct 8 at 17:16
  • Thanks for the help again :) – Anonymous Oct 8 at 17:17
  • cool - sorry for the typo - clearly i didn't test it myself! (epistemic arrogance). As a courtesy to the Community mark either mine or David's answer as the solution that got you to the answer – cropredy Oct 8 at 17:20
2

When a ContentVersion is first inserted to create a new file, it has no ContentDocument or ContentDocumentLink associated with it. These are created later.

Hence this

if(cv.ContentDocumentId != null)
{
    contentDocumentIdSet.add(cv.ContentDocumentId);
}

will never execute on creation of a new file (only upon uploading a new version of an existing file), and these queries

ContentDocumentLink cdl = [SELECT ContentDocumentId, LinkedEntityId FROM ContentDocumentLink WHERE ContentDocumentId IN:contentDocumentIdSet Limit 1];
List<Opportunity> oppList = [SELECT Id, name FROM Opportunity where Id =:cdl.LinkedEntityId];   

will always be empty in that circumstance.

This code also has several key limitations:

  • It only processes one ContentDocumentLink per ContentDocument; sharing files to multiple entities, such as users or files, is common.
  • It in fact only processes one ContentDocumentLink per trigger invocation; i.e., it is not bulkified.
  • It does not react to new sharing events, i.e., adding a ContentDocumentLink to an existing ContentDocument, which would require another trigger.
  • It stores only one Opportunity Id per ContentVersion, regardless of to what (and to how many whats) the version and document are shared.

I believe you'll need at least two triggers (one on ContentVersion and one on ContentDocumentLink) to achieve your objective, but I remain unconvinced of the correctness of this approach. I'd strongly recommend you review the design of this large, complex integration system you're building with an experienced Salesforce architect.

  • I just wrote the trigger on ContentDocumentLink, updated in my question above. Still cannot figure out. All I want to do is to copy the opportunity id/LinkedEntityId to the contentversion so that I could know what opportunity the file is related to like we query parentid in notes and attachments. – Anonymous Oct 7 at 22:59
  • @Anonymous You'll need to write some more code to use Maps to correlate Version Ids back to Document Ids and thence to Linked Entity Ids. – David Reed Oct 7 at 23:28
  • I wrote the trigger on ContentDocumentLink and it seems to be populating the opportunity__c field in case of new file upload or uploading of a new version of the same file. Do I still need to write 2 triggers? The other one on content version? – Anonymous Oct 8 at 17:19

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.