Professional Documents
Culture Documents
com
http://w iki.developerforce.com/index.php/Using_Apex_Managed_Sharing_to_Create_Custom_Record_Sharing_Logic August 27, 2011
Abstract
Force.com allows developers and administrators to control access to data at many different levels. You can control access at the object-level, the record-level and even at the field-level. This article will focus on methods for controlling access to data at the record-level. In the vast majority of cases, the appropriate Force.com sharing settings can be defined declaratively by simply pointing and clicking. In some cases, developers may need the ability to define even more sophisticated sharing settings, and this is where Apex Managed Sharing comes in. Apex Managed Sharing allows you to use Apex Code to build sophisticated and dynamic sharing settings that arent otherwise possible. For example, a developer can use Apex Managed Sharing to write a trigger that will automatically share a custom object record with a user that has been specified in a lookup field. You can also use Apex Managed Sharing to write custom Visualforce controllers that implement your sharing logic After an introduction to sharing, this article looks at the components of Apex Managed Sharing and how you can use Apex Managed Sharing in your own applications. It also provides sample code for the trigger described above. If you need an introduction to these topics, see An Introduction to the Force.com Database and An Overview of Force.com Security.
Understanding Sharing
Organization Wide Defaults are the least granular level of sharing. Organization Wide Defaults allow an administrator to specify a users default access level to an object. From most restrictive to least restrictive, the available default settings are: "Private", "Public Read Only" and "Public Read/Write". All record-level sharing settings, including the ones created by Apex Managed Sharing, are exceptions to these Organization Wide Default sharing settings. Record-level sharing settings can only be used to grant more permissive access to records. Record-level sharing settings cannot be used to restrict access to records. This also means that sharing settings can only be implemented for objects whose object-level sharing defaults are restricted i.e. either Public Read Only or Private. It is not necessary to grant additional permissions on objects that have a default sharing access of Public Read/Write because users already have read/write default access. The declarative sharing settings that the Force.com platform provides are very easy to configure and are very powerful, so before writing any Apex Managed Sharing code, you should verify that the declarative sharing settings will not meet your requirements. Here's a look at the types of sharing settings available to you.
Sharing Rules: Sharing rules are defined by administrators. They automatically grant users within a given group/role access to records owned by a specific group of users.
Sharing Table
All objects that have a default sharing setting of the either "Private" or "Public Read Only" also have a related "Share" object that is similar to an access control list (ACL) found in other platforms. All share objects for custom objects are named as MyCustomObject__Share, where MyCustomObject__c is the name of the related custom object. A share object includes records supporting all three types of sharing: Force.com managed sharing, user managed sharing, and Apex managed sharing. A custom objects share object allows four pieces of information to be defined: The record being shared. The User or Group with whom the object is being shared. The permission level being granted to the User or Group. The reason why the User or Group has been granted sharing access. This information corresponds with the following fields in a share object:
ParentId The Id of the record being shared. This field cannot be updated. UserOrGroupId The Id of the User to whom you are granting access. May also be a Public Group Id, Role Id, or Territory Id. This field cannot be updated. AccessLevel The level of access that the specified User or Group has been granted. Valid values for Apex managed sharing are: Edit, Read. This field must be set to an access level that is higher than the organizations default access level for the parent object. For more information, see Access Levels. The reason why the user or group is being granted access. The reason determines the type of sharing, which in turn controls who can alter the sharing record. This field cannot be updated.
example, the Organization Wide Default Sharing Setting for our Job object is set to "Private". This means that when a recruiter at our company creates a new Job record, only the recruiter will have access to that record the hiring manager for the Job wont be able to edit or even view the record that corresponds with her job opening! As we can see if we click on the Job records "Sharing" button, only the recruiter that created the Job record has access to it:
Clearly the hiring manager should have access to her job record as well. This is the perfect use case for Apex managed sharing. To get started, we'll create an Apex sharing reason. Then, we'll write an Apex trigger that will automatically create the appropriate Job_Share record every time a new Job record is created.
reason. To do that: Click Setup | Create | Objects. Select the custom object. (In this case, the "Job" custom object.) Click New in the Apex Sharing Reasons related list. (If you don't see this related list, Apex managed sharing has not been configured for your Org - please contact your support representative.) Enter a label for the Apex sharing reason. Enter a name for the Apex sharing reason. Click Save. The end result will look like this:
Using the naming scheme explained previously, this Apex Sharing Reason will be called Hiring_Manager_Access__c. Let's now look at the code which uses this.
Example Code:
Our Apex Trigger will look like this:
trigger Hiring_Manager_Job_Share on Job__c (after insert) { // We only execute the trigger after a Job record has been inserted // because we need the Id of the Job record to already exist. if(trigger.isInsert){ // Job_Share is the "Share" table that was created when the // Organization Wide Default sharing setting was set to "Private". // Allocate storage for a list of Job__Share records. List<Job__Share> jobShares = new List<Job__Share>();
// For each of the Job records being inserted, do the following: for(Job__c job : trigger.new){ // Create a new Job__Share record to be inserted in to the Job_Share table. Job__Share hiringManagerShare = new Job__Share(); // Populate the Job__Share record with the ID of the record to be shared. hiringManagerShare.ParentId = job.Id; // Then, set the ID of user or group being granted access. In this case, // were setting the Id of the Hiring Manager that was specified by // the Recruiter in the Hiring_Manager__c lookup field on the Job record. // (See Image 1 to review the Job object's schema.) hiringManagerShare.UserOrGroupId = job.Hiring_Manager__c; // Specify that the Hiring Manager should have edit access for // this particular Job record. hiringManagerShare.AccessLevel = 'edit'; // Specify that the reason the Hiring Manager can edit the record is // because hes the Hiring Manager. // (Hiring_Manager_Access__c is the Apex Sharing Reason that we defined earlier.) hiringManagerShare.RowCause = Schema.Job__Share.RowCause.Hiring_Manager_Access__c; // Add the new Share record to the list of new Share records. jobShares.add(hiringManagerShare); } // Insert all of the newly created Share records and capture save result Database.SaveResult[] jobShareInsertResult = Database.insert(jobShares,false); // Error handling code omitted for readability. } }
We've just written a properly "bulkified" trigger that for each new Job record: Creates a Job_Share record. Assigns the Id of the Job record to the Job_Share record's ParentId field. Assigns the User Id of the Hiring Manager to the Job_Share record's UserOrGroupId field. Specifies that the Job_Share's AccessLevel field have a value of 'edit'. Assigns the Hiring_Manager_Access__c Apex Sharing Reason to the JobShare's RowCause field. Inserts the Job_Share record in to the Job_Share table. Now, the next time that a recruiter creates a new Job record, the Hiring Manager will automatically be given access to the record with a custom Sharing Reason of Hiring Manager: That's it!
Summary
This article describes Apex managed sharing, a technique that allows you to use Apex Code to build sophisticated and dynamic sharing settings for records on Force.com. After creating the Sharing Reasons, it's simply a matter of coding the creation of the share record. Besides the straightforward example shown in this article, there are any number of other use cases that Apex managed sharing can support as well - it gives you full control over the record sharing. You should be able to use this code as a starting point and modify it to implement your own custom sharing logic.
References
The following Help topics provide additional context. Search for them in the "Help" module in your Developer Edition environment. Sharing Considerations Managing the Sharing Settings Creating Apex Sharing Reasons The Force.com Platform Developer Guide contains a section that discusses Apex Managed Sharing. The Force.com Apex Code Developer's Guide describes how to share a record programmatically, as we have done here.