I have an Apex class with sharing
where I get a list of every Pricebook2
available in the database. Considering the fact that we use sharing rules to prevent access to some of them, my SOQL shouldn't return a full list to every users.
Here's my super simple method:
public SelectOption[] getPricebookList() {
Pricebook2[] Records =
[
SELECT
Name
FROM
Pricebook2
WHERE
IsActive = true
ORDER BY
Name ASC
];
SelectOption[] Options = new SelectOption[]{};
for(Pricebook2 R : Records)
Options.add(new SelectOption(R.Id, R.Name));
return Options;
}
The data returned by this method is then outputted using a selectoption
component inside a Visualforce page.
The problem I have is that every price books are displayed to every single users. Also, any user can use the Visualforce module I created to create Quotes and QuoteLineItems with any Pricebook, even if they don't have access to them according to the sharing settings.
How can I prevent this from happening?
EDIT (@SF_Ninja)
I tried what you answer, here's my code:
// Requesting available price books
Map<ID, Pricebook2> Records = new Map<ID, Pricebook2>([
SELECT Name
FROM Pricebook2
WHERE IsActive = true
ORDER BY Name ASC
]);
SelectOption[] Options = new SelectOption[]{};
// Requesting price books user access
// NOTE: Workaround since Pricebook2 doesn't enforce Sharing Rules
UserRecordAccess[] UserRecordAccessList =
[
SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess
FROM UserRecordAccess
WHERE UserId = :UserInfo.getUserId() AND
RecordID IN :Records.keySet()
];
System.debug('User ID: ' + UserInfo.getUserId());
System.debug('Pricebook IDs size: ' + Records.keySet().size());
System.debug('IDs to search:');
for(Pricebook2 R : Records.values())
System.debug('----> ' + R.Name + ' (' + R.Id + ')');
System.debug('Result: ' + UserRecordAccessList); // <-- returning () as admin
According to the documentation, I'm requesting the right values, have the right WHERE
operations (http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_userrecordaccess.htm).
Here's the Developer console output:
UserID and Pricebook IDs are both returning values, but the last debug
is returning an empty list. Still trying to figure out whats going on here.
EDIT (2013-11-14 10:00)
Here's the final version of the code provided above. The reason why the version above wasn't working is because I passed a set
instead of a list
in the WHERE
condition. Normally, this would be working in a SOQL request, but not in this case. I guess there's an undocumented limitation in UserRecordAccessList
.
// Requesting available price books
Map<ID, Pricebook2> Records = new Map<ID, Pricebook2>([
SELECT Name
FROM Pricebook2
WHERE IsActive = true
ORDER BY Name ASC
]);
SelectOption[] Options = new SelectOption[]{};
Id[] PBIDs = new Id[]{};
PBIDs.addAll(Records.keySet());
// Requesting price books user access
// NOTE: Workaround since Pricebook2 doesn't enforce Sharing Rules
UserRecordAccess[] UserRecordAccessList =
[
SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess
FROM UserRecordAccess
WHERE UserId = :UserInfo.getUserId() AND
RecordID IN :PBIDs
];
System.debug('User ID: ' + UserInfo.getUserId());
System.debug('Pricebook IDs size: ' + Records.keySet().size());
System.debug('IDs to search:');
for(Pricebook2 R : Records.values())
System.debug('----> ' + R.Name + ' (' + R.Id + ')');
System.debug('Result: ' + UserRecordAccessList);
Hope this might fix others issue.
EDIT (2013-11-14 10:37)
The code above is working (we can get UserRecordAccessList
records), but every permissions are set to yes
, so it's pointless.
What is left to do?
Hack in Salesforce features to set the Pricebook:
https://[INSTANCE].salesforce.com/oppitm/choosepricebook.jsp?id=[OPP_ID]&retURL=[RET_URL]
This might break your flow, but it should be working and support sharing settings.
WHERE RecordID IN ('id', 'id2', 'id3'...)
), it is working and returning the right information. Maybe this special SOQL request is allergic to myRecords.keySet()
. Considering all the limitation mentioned in the documentation, I feel there's some magic done behind the scene. It is written like a regular SOQL request, but probably parsed before executing it. I might have to craft my request dynamically and populate theWHERE
condition using a loop.Set<ID>
doesn't work, whileList<ID>
does when requestingUserRecordAccess
object. It's strange, since they normally work in SOQL queries. Like I mentioned, it must be the magic done in the background that doesn't support this. There's not even an error returned!SELECT Id, Name, UserRecordAccess.HasReadAccess, UserRecordAccess.HasTransferAccess, UserRecordAccess.MaxAccessLevel FROM Pricebook2
returns alltrue
resp MaxAccessLvl:"Id":"01s0Y000005JVMyQAO","Name":"Not shared with anyone","UserRecordAccess":{"attributes":{"type":"UserRecordAccess"},"HasReadAccess":true,"HasTransferAccess":true,"MaxAccessLevel":"All"}
- taken from a vf page viewed as standard user with none shared pricebooks (dev org, can't test in production).