+ Start a Discussion
Rick MacGuiganRick MacGuigan 

contentversion trigger not preventing delete

I started to build out a simple trigger from the  to prevent a File record from being deleted from SalesForce Files | Triggers. These file records can be deleted from a related list on a visualforce page. The trigger is active but I can still delete an uploaded File . Any ideas ? 
 
trigger CREContentDocVersion on ContentVersion (before delete) {
for(ContentVersion cv : trigger.old){
        cv.adderror('Document Cannot be deleted');
    }
}

 
Best Answer chosen by Rick MacGuigan
Jason HardyJason Hardy
You can't use javascript to alert the user. You should be able to add a <apex:pageMessages/> to your visualforce page to display the error you define in your trigger.

As far as the cascated delete, you can use the ContentDocumentId on the ContentDocumentLink object to find the appropriate related Id to delete on the ContentDocument object.

If you do want to do that, then I would recommending moving it into the after conext of your delete trigger. It's best practice to do any additional DML operations after the database commission has been completed.

Apex validations (which is what you're doing here) will execute in either the before or after context. Salesforce generally recommends doing trigger validations in the after context (mainly because it will allow any DML errors, such as required fields, to surface first).

All Answers

Nayana KNayana K
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_ignoring_operations.htm

In this documentation, it is stated that "You can't use before or after delete triggers with the ContentVersion object"
Jason HardyJason Hardy
Hi Rick, as Nayana stated you can't use the convent version object; however, for what you're trying to accomplish you can use the ContentDocument object. I justed tested it and using it will prevent deletion of files. 
Rick MacGuiganRick MacGuigan
Thanks Jason and Nayana for the replies. @Jason Hardy could you post your ContentDocument trigger. I had tried this trigger as well which still does not prevent me from deleting any File record from the objects related list.
 
Trigger CREContentDocument on ContentDocument (before delete) {
for(ContentDocument cd : trigger.old){
        cd.adderror('Document Cannot be deleted');
    }
}

 
Rick MacGuiganRick MacGuigan
I am on a Salesforce classic platform as well. 
 
Jason HardyJason Hardy
I misunderstood what you were looking for. The ContentDocument will work if you're going directly to the Files object and working with them there. To double check, when you're on an object record you're speaking of the items in notes and attachments? 
User-added image

If that's the case you'll need to write the trigger on the Attachment object. 
Jason HardyJason Hardy
Just as an FYI, an attachment created as I outlined above will also appear in the ContentDocument object. It would exist in both area's. 
Rick MacGuiganRick MacGuigan
Jason, thanks but no. I have been using the new Notes and Files objects for a while.Abandoned and migrated from the Notes&Attachments. To clarify more I have a related list that displays the Files . This related list is associated with a custom object rendered in Visualforce. So I am using the tag: <apex:relatedList list="AttachedContentDocuments"  id="FilesList" /> (pic example below.) 

What I need to do is limit 'who' can delete these File records from the related list. Right now I can't even get the trigger to respond when I delete one of these File records. 

User-added image
Rick MacGuiganRick MacGuigan
I also tried to delete a File from a standard object in Force.com and same result. I was able to delete the File without the ContentDocument trigger firing. It is active and there are no other triggers active on the File object. 
Jason HardyJason Hardy
I was able to block it with the following trigger on the object
trigger ContentDocumentTrigger on ContentDocument (before delete) {
    for (ContentDocument cd : (List<ContentDocument>) Trigger.old) {
        cd.addError('Can\'t delete!');
    }
}
When I hit delete from the files tab, here's what I get:
User-added image
Rick MacGuiganRick MacGuigan
Jason, thanks. I lifted your trigger replacing mine and still allows me to delete any File. This if I upload a file thru the Visualforce page's related list or thru Force.com. I'm doing this in my sandbox if that helps. Are ther additional ContentDocument settings that may be getting in the way ? 
Rick MacGuiganRick MacGuigan
I tried to delete from the Files Tab as you did as well. Interestingly, I don't have a delete option from here ? I normally do not use the tab for files but rather work directly with the related list of Files for an object. How are you deleting Files from the tab ? 
User-added image
Rick MacGuiganRick MacGuigan
If I open the File and click Delete I see the trigger response preventing the deletion. But if I delete the same File from the File related list the trigger does not respond and I am able to delete the File. Could this be a DocumentLink that needs the trigger to prevent it from being deleted on the related list ? 


User-added image
Jason HardyJason Hardy
I uploaded the file through the files tab directly, so that may be where the difference. When you add them to the object directly, what related list are you loading them into?
Jason HardyJason Hardy
Which related list are you using? If you're defining it custom on visualforce, what are you refereicng? 
Rick MacGuiganRick MacGuigan
I'm uploading the files on the Apex related list tag (via the default upload button) that is rendered on te custom visualforce page. 
<apex:relatedList list="AttachedContentDocuments"  id="FilesList" />

 
Jason HardyJason Hardy
Ok got it working with this
trigger ContentDocumentLinkTrigger on ContentDocumentLink (before delete) {
    for (ContentDocumentLink cdl : (List<ContentDocumentLink>) Trigger.old) {
        cdl.addError('Can\'t delete!');
    }
}
Jason HardyJason Hardy
Also while I have included logic in a trigger as outlined above, it's generally not best practice to have logic running in the trigger itself. It's best to have a single trigger per object which calls a handler. This is the general pattern I follow for all triggers, it makes life so much simpler since you have a general pattern that is followed for all triggers:
http://developer.force.com/cookbook/recipe/trigger-pattern-for-tidy-streamlined-bulkified-triggers
Rick MacGuiganRick MacGuigan
Jason, thanks for the suggestion. My suspicion that the Apex tag for related list 'delete' action was working on the ContentDocumentLink. How would I just act on the single ContentDocumentLink and ContentDocument that is being selected for deletion (from the link on the related list. The challenges I now have are to delete the ContentDocumentLink, the ContentDocument 

Also, since my users would delete the document from the Apex related list tag I also need to redirect the user to the page in order for the refresh to show the complete Visualforce page. Or should I simply embed javascript to the trigger to inform the user they are not authorized to delete the document ? 

Some testing: If I deactivate ContentDocumentLinkTrigger and keep ContentDocumentTrigger active I can deleted the link and the related list no longer shows the File record. However, If I open the document from the link on the related list and then click 'Delete" from the Document Content I am prohibited from deleting it as expected. 

If I deactivate ContentDocumentLinkTrigger AND ContentDocumentTrigger then I can delete the link as above but the Document remains on the Files Tab. 

So this requirement will require a deletion made to the ContentDocument if the ContentDocumentLink is deleted (by an authorized user.) 

Any thoughts on the best approach to prevent deletion (i.e. use Javascript to alert the user) and how to best delete the contentdocumentlink and the contentdocument by an authorized user ? 
 
//prevent the document link from being deleted.
trigger ContentDocumentLinkTrigger on ContentDocumentLink (before delete) {
    for (ContentDocumentLink cdl : Trigger.old) {
        cdl.addError('Document LINK Cannot be deleted');
    }
}
// prevent the document from being deleted.
trigger ContentDocumentTrigger on ContentDocument (before delete) {
for(ContentDocument cd : Trigger.old){
        cd.adderror('The Whole Document Cannot be deleted');
    }
}













 
Jason HardyJason Hardy
You can't use javascript to alert the user. You should be able to add a <apex:pageMessages/> to your visualforce page to display the error you define in your trigger.

As far as the cascated delete, you can use the ContentDocumentId on the ContentDocumentLink object to find the appropriate related Id to delete on the ContentDocument object.

If you do want to do that, then I would recommending moving it into the after conext of your delete trigger. It's best practice to do any additional DML operations after the database commission has been completed.

Apex validations (which is what you're doing here) will execute in either the before or after context. Salesforce generally recommends doing trigger validations in the after context (mainly because it will allow any DML errors, such as required fields, to surface first).
This was selected as the best answer
Rick MacGuiganRick MacGuigan
Thanks Jason. This has been helpful. I do implement apex:pageMessages  and will see how the page refresh handles that.
Dale WorthingtonDale Worthington
Sorry to hijack an old thread. I have used the guidance here from Rick and Jason and i have two triggers that work famously well in a sandbox now however i cannot deply them to production because i dont have 1% of code coverage. upon reading i see that i need to create a test class however my efforts have not been rewarded was success. Could you share with me what test class you used?