0

I've been trying to create a VF page for a customer community where the customer user can view all the Files on their own Account as a simple list. Internal users are adding these Files as Chatter posts with the access set to include the community. It seems like it should be simple, but the wonky (or wonky-appearing) access control is making my code really ugly.

The problem is that if I query ContentVersion or ContentDocument, I only get the Files that are shared to the current user directly, via Group-membership, or globally shared. But they really have access to the File if it is shared to the Community and posted on their Account.

Whereas, if I query FeedItem, I get all the FeedItems on the Account, whether the user can really see them or not (which seems like a bug in access control, but that's another story/question). If I provide the user with that list, they can get a load of links that will go through to permissions errors.

What I've ended up doing is getting the FeedItems and then using getContent() to find out whether or not the current user can see them. But it seems horrible.

Is there a better way?

VF:

<apex:pageMessages id="messages" />
  <apex:pageBlock title="Files">
    <apex:pageBlockTable value="{!feedItems}" var="thisFeedItem">
      <apex:column headerValue="Name">
        <apex:outputLink value="/{!thisFeedItem.RelatedRecordId}">{!thisFeedItem.ContentFileName}</apex:outputLink>
      </apex:column>
      <apex:column headerValue="Size (KB)" value="{!TEXT(ROUND(thisFeedItem.ContentSize/1024.0,0))} " />
      <apex:column headerValue="Created Date" value="{!thisFeedItem.CreatedDate} " />
    </apex:pageBlockTable>
  </apex:pageBlock>

</apex:page>

Controller:

public class ClientPortalFilesController {
    public List<FeedItem> feedItems {get; set;}

    public ClientPortalFilesController() {
        User thisUser = [SELECT Id, AccountId FROM User WHERE Id = :UserInfo.getUserId()];
        Id accountId;

        if(thisUser.AccountId == null) {
            List<Account> accs = [SELECT id FROM Account WHERE Name = 'ACME'];
            if(!accs.isEmpty()) {
                accountId = accs[0].id;
            }
        } else {
            accountId = thisUser.AccountId;
        }
        feedItems = [SELECT Id, ContentFileName, RelatedRecordId, CreatedDate, ContentSize 
                     FROM FeedItem 
                     WHERE ParentId = :accountId
                     ORDER BY CreatedDate DESC
                     LIMIT 100
                    ];

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

        List<FeedItem> accessibleFeedItems = new List<FeedItem>();
        for(FeedItem fi : feedItems) {
            try {
                (new PageReference('/' + fi.RelatedRecordId)).getContent();
                accessibleFeedItems.add(fi);
            } catch(Exception e) {
                System.debug('Failed to read ' + fi.ContentFileName);
                // OK, not accessible, don't care much
            }    
        }

        feedItems = accessibleFeedItems;

    }
}
1

Would it work for you to use the Content Document Link API? This will return documents shared with the record (which happens when the file is posted on the record) that the user has access to.

List<ContentDocumentLink> cdls = [SELECT ContentDocument.Title, ContentDocument.ContentSize, ContentDocument.ContentModifiedDate, ContentDocument.CreatedDate  FROM ContentDocumentLink WHERE LinkedEntityId = :accountId];

From here, you should be able to get the title and other fields such as cdls[0].ContentDocument.Title.

  • Great... that seems to be exactly what I needed! I could have sworn that I tried it already, but I must have tried the other Content* objects and missed that one. – Aidan Aug 3 '15 at 7:25

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.