I want to retrieve File Ids for which new entry has been added or removed from child ContentDocumentLinks relation. Following is the parent to child relationship query I made for retrieving Ids, title and child records of all ContentDocuments using Admin token.


Is there any way to detect that a new entry has been made or removed from ContentDocumentLink object for each ContentDocument. Triggers are not allowed on ContentDocumentLink object else it would be an easy cookie to crack. Whereas while experimenting on ContentVersion Triggers, it turns out sharing with an entity(example User, record, Group) initiates after update trigger, sharing with a public link or internally with the entire company doesn't initiate the same after update trigger.

To sum up, in a real-time scenario, an added entry reflects share activity on a particular File and a removed entry reflects unshare activity in ContentDocumentLink object. Any clue will be helpful. Thanks.

2 Answers 2


Triggers can fire on ContentVersion when new ContentDocuments are created or updated. See my answer to Trigger on ContentDocument not working for more on this. It appears to me, based on what I've italicized below, that you need to be querying by getting the ContentDocumentId from the ContentVersion Object instead of the ContentDocument Object, especially since I'm guessing you're likely looking for a version of the document. Everything below is taken from the Object Reference.

The following is said about ContentDocumentLink:

Use this object to query the locations where a file is shared or query which files are linked to a particular location. For example, the following query returns a particular document shared with a Chatter group:

SELECT ContentDocument.title FROM ContentDocumentLink WHERE ContentDocumentId =
'069D00000000so2' AND LinkedEntityId = '0D5000000089123'

• You can't run a query without filters against ContentDocumentLink.

You can't filter on ContentDocument fields if you're filtering by ContentDocumentId. You can only filter on ContentDocument fields if you're filtering by LinkedEntityId.

• You can't filter on the related object fields. For example, you can't filter on the properties of the account to which a file is linked. You can filter on the properties of the file, such as the title field.

A SOQL query must filter on one of Id, ContentDocumentId, or LinkedEntityId.

Edit - More on ContentDocumentLink:

Special Access Rules

• Customer and Partner Portal users must have the “View Content in Portal” permission to query content in libraries where they have access.

• Users (including users with the “View All Data” permission) can only query files they have access to, including:

◊ All Salesforce CRM Content files in libraries they're a member of and in their personal library, regardless of library permissions (API version 17.0 and later).

◊ All Chatter files they own, posted on their profile, posted on groups they can see, and shared directly with them (API version 21.0 and later).

• For API version 25.0 and later, creation and deletion of ContentDocumentLink objects with a LinkedEntityId of types User, CollaborationGroup, or Organization are supported via the API.

For organizations with the Communities pilot enabled, a document can only be shared with users and groups that are a part of the community the file was created in.

Here's some of what you'll find under ContentVersion:

• Use this object to create, query, retrieve, search, edit, and update a specific version of a Salesforce CRM Content document or Chatter file. Use the ContentDocument object to retrieve, query, update, and delete the latest version of a document, but not a content pack, in a library or a file in Chatter.

• Use this object to create, query, retrieve, search, edit, and update a specific version of a Chatter file. Use the ContentDocument object to retrieve, query, update, and delete the latest version of a file in Chatter.

• If Chatter isn't enabled, Chatter files aren't included in query results.

• You can't query files that are only on records, whether they're public or not.

• Not all fields can be set for Chatter files.

• You can only update a version if it is the latest version and if it is published.

• You can't delete a version via the API.

• SOQL queries on the ContentVersion object return all versions of the document. SOSL searches on the ContentVersion object return only the most recent version of the document.

• Documents published into a personal library assume the default (General) content type.

Note: When Salesforce CRM Content is provisioned for an organization the administrator can create a General content type. Additional content types can be created to categorize documents. The administrator can rename the General content type.

And this is what's said about ContentDocument:

Use this object to retrieve, query, update, and delete the latest version of a document, but not a content pack, in a library or a file in Chatter. Use the ContentVersion object to create, query, retrieve, search, edit, and update a specific version of a Salesforce CRM Content document or Chatter file.

• The query() call doesn’t return archived documents. The queryAll() call returns archived documents.

• If Chatter isn't enabled, Chatter files aren't included in query results.

• You can't query files that are only on records, whether they're public or not.

To create a document, create a new version via the ContentVersion object without setting the ContentDocumentId. This automatically creates a parent document record. When adding a new version of the document, you must specify an existing ContentDocumentId which initiates the revision process for the document. When the latest version is published, the title, owner, and publish status fields are updated in the document.

When you delete a document, all versions of that document are deleted, including ratings, comments, and tags.

• You can't create, edit, or delete content packs via the API.

Note: Content metadata, such as tags, custom fields, and content owners are tracked at the version level rather than at the document level.

• If you query versions in the API, versions with a PublishStatus of Upload Interrupted are not returned.

A document record is a container for multiple version records. You create a new version to add a document to the system. The new version contains the actual file data which allows the document to have multiple versions. The version stores the body of the uploaded document.

Edit - Content Objects Schema:

Content Objects Schema

Edit - User Sharing Permissions Schema:

User Sharing Permissions Schema

Edit - In the Object Reference, under PermissionSet, I found the following and more:

Child Objects

When using the API, think of each permission set or related set of access controls as starting as an empty container that you fill with permission records.

In the API, a permission set can contain user, object, and field permissions, as well as setup entity access settings for other settings, such as Apex classes.

• ObjectPermissions and FieldPermissions objects are available in API version 24.0 and later.

• The SetupEntityAccess object is available in API version 25.0 and later.

Only user permissions are managed in the PermissionSet API object; all other permission types are managed in child API objects.

Profile and Permission Objects Schema

In these child objects, access is stored in a record, while the absence of a record indicates no access. In order to return a record in a SOQL query, a minimum permission or setting is required for each child object.

Because permissions are stored in related objects, it’s important to understand what questions to ask when using SOQL. For example, you may want to know which permission sets have “Delete” on an object or have the right to approve a return merchandise authorization (where the approval checkbox is controlled with field permissions). Asking the right questions when using SOQL with permission sets will ensure that you get the information you need to make an informed decision, such as whether to migrate permissions or assign a permission set to a user.

To set an object or field permission to no access, just delete the record that contains the permission. For example, to disable all object permissions in the Merchandise__c object for a particular permission set, first query to retrieve the ID of the object permission record.

FROM ObjectPermissions
WHERE SobjectType = 'Merchandise__c'

Then delete the IDs returned from the query.

Note: If you try to update the object or field permissions by setting all permissions to false, the permission record is automatically deleted. As a result, any subsequent queries for the record ID won’t return any results and you must add a new permission record to grant access.

Edit: Final Comments

The above note confirms your observation that record IDs are lost when once a document has been unshared. ContentDocumentLink only has 4 fields that aren't standard System Fields. Those fields tell very little and are mostly picklists. It appears to me that the ContentDocumentLink is going to tell you very little in terms of sharing.

If detecting changes in sharing is your primary objective, I recommend you study the example queries in PermissionSets section of the Object Reference Document to gain further insight into how to achieve your desired results. There were too many query examples for me to cite them all in this answer.

It's my sense that your solution will likely include some combination of Permission Sets, Document Libraries, Sharing Groups and ContentVersion/ContentDocument. I hope what I've provided helps provide some clarity for you.

  • Thanks for the detailed answer. Totally understood that creating a ContentDocument actually means one is creating a ContentVerison and we can query ContentVersion instead of ContentDocument. Yes, ContentDocumentLink has its limitations. Though, querying ContentVersion on 'LastReferencedDate' doesn't return File Ids who have been unshared(public-link/internal/collaborator removed). Can you guide which fields get updated to track this information ? The only thing that I have seen getting updated is the entry in ContentDocumentLink which gets hard-deleted. Commented May 14, 2015 at 12:37
  • See my edited response with much more on permissions; something which I neglected to address in my initial answer.
    – crmprogdev
    Commented May 14, 2015 at 16:20
  • Well, permission sets appears to be on organization level. Apparently, they would not hold permission setting on record-level (why record-level? I am interested in file sharing settings for each file). Will have to read-up more to explore if it is even possible. Commented May 17, 2015 at 12:32
  • Permission sets are assigned to Groups. Groups can be defined by Users, Users and Subordinates, Roles, Roles and Subordinates, Profiles, etc. The record sharing would then be determined by calculating Group membership and applying the Permissions to the User accordingly for each record. Object OWD sharing model, record ownership, etc would also be factored into those calculations. See Security Implementation Guide for more, plus Tips & Hints for Sharing Data.
    – crmprogdev
    Commented May 17, 2015 at 13:19

Its may be late but I think will be helpful You can query the documents based on lastModifiedDate column like below.

select Id, Title, Description, ContentSize, isDeleted, OwnerId, LastModifiedDate, CreatedDate, FileType, PublishStatus from ContentDocument where OwnerId = 'XXXXXXXXXXXXXXXXXX' and LastModifiedDate > 'CURRENT_DATE' order by LastModifiedDate

You must log in to answer this question.

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