Apex based record sharing in Salesforce

Author posted by Jitendra on Posted on under category Categories Apex, Force.com, Salesforce and tagged as Tags , with 17 Comments on Apex based record sharing in Salesforce

Working with Apex based sharing in Salesforce

There are situations where the business requirement is too complex and standard sharing rules provided by the Salesforce will not work.

Example: On Opportunity, you want to give access to record to some users which are in related list.
One way is to manually share the record which will need the interference of opportunity owner. But everyone will love automated solution.

Apex managed sharing provides developers with the ability to support an application’s particular sharing requirements programmatically via Apex code.

Pre requisite:

The object’s organization-wide default access level must not be set to the most permissive access level. For custom objects, this is Public Read/Write. For more information, see Access Levels. This object is used for creating Apex based sharing.

The user to which the record going to be shared must have the object level permission. If you are trying to share the record with edit permission but user does not have the edit permission on that object, then it will not work.

To access sharing programmatically, you must use the share object associated with the standard or custom object for which you want to share. For example, AccountShare is the sharing object for the Account object, ContactShare is the sharing object for the Contact object, and so on. In addition, all custom object sharing objects are named as follows, where MyCustomObject is the name of the custom object: “MyCustomObject__Share”.

Objects on the detail side of a master-detail relationship do not have an associated sharing object. The detail record’s access is determined by the master’s sharing object and the relationship’s sharing setting.

In this article I want to share the custom object “Test__c” with students. Let’s consider that every “Test” has lookup to the “Student”.

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 object’s 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.
Salesforce Share Table
Salesforce Share Table

This information corresponds with the following fields in a share object:

ParentId
The Id of the record being shared. This field cannot be updated.
UserOrGroupIdThe 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.
AccessLevelThe 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 organization’s default access level for the parent object. For more information, see Access Levels.
RowCause (aka Sharing Reasons)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.

Apex sharing reason

Salesforce Apex Sharing Reason
Salesforce Apex Sharing Reason

Before we can start writing any Apex managed sharing code, we must create an Apex sharing reason. To do that:

  • Click “Setup | Create | Objects”.
  • Select the custom object. (In this case, the “Test” 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.

Now let’s start with Apex code:

trigger Test_Share on Test__c (after insert) {

 if(trigger.isInsert){

 /**
 * Test_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 Test_Share
* records.
 **/
 List<Test_Share> jobShares = new List<Test_Share>();

 /** For each of the Test records being inserted, do the following: **/
 for(Test__c t : trigger.new){

 /** Create a new Test_Share record to be inserted in to the Test_Share table. **/
 Test_Share studentRecord = new Test_Share();

 /** Populate the Test_Share record with the ID of the record to be shared. **/
 studentRecord.ParentId = t.Id;

 /** Then, set the ID of user or group being granted access. In this case,
 * we're setting the Id of the student__c that was specified by the Test
 * Result in the student__c lookup field on the Test record.
 **/

 studentRecord.UserOrGroupId = t.student__c;

 /** Specify that the Student should have edit access for this particular Test record. **/
 studentRecord.AccessLevel = 'edit';

 /** Specify that the reason the Student can edit the record is because its his test result
 * (Student_Access__c is the Apex Sharing Reason that we defined earlier.)
 **/
 studentRecord.RowCause = Schema.Test_Share.RowCause.Student_Access__c;

 /** Add the new Share record to the list of new Share records. **/
 jobShares.add(studentRecord);
 }

 /** Insert all of the newly created Share records and capture save result **/
 Database.SaveResult[] jobShareInsertResult = Database.insert(jobShares,false);
 }
}

Important Note: If the owner of record is changed, all the Apex based sharing will not be lost unlike manual sharing. You can read more at http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_bulk_sharing_understanding.htm

Related posts

17 thoughts on “Apex based record sharing in Salesforce”

  1. Save error: Entity is not org-accessible

    Hi Jitendra,
    I’m getting the above mentioned error.Can you please help me out with this

    Thaks in advance

      1. trigger Book on Book__c(after insert, after update)

        {

        // We only execute the trigger after a Book record has been inserted

        // because we need the Id of the Book record to already exist.

        if(trigger.isInsert || trigger.isUpdate){

        // Book_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 Book__Share records.

        List lstBookShares = new List();

        List lstNewBooks = new List();

        // For each of the Book records being inserted, do the following:

        for(Book__c book : trigger.new){

        lstNewBooks.add(book);

        // Create a new Book__Share record to be inserted in to the Book_Share table.

        Book__Share hiringManagerShare = new Book__Share();

        // Populate the Book__Share record with the ID of the record to be shared.

        hiringManagerShare.ParentId = book.Id;

        // Specify that the Hiring Manager should have edit access for

        // this particular Book record.

        hiringManagerShare.UserOrGroupId = book.CreatedById;

        hiringManagerShare.AccessLevel = ‘Read’;

        // Specify that the reason the Hiring Manager can edit the record is

        // because he’s the Hiring Manager.

        // (Hiring_Manager_Access__c is the Apex Sharing Reason that we defined earlier.)

        hiringManagerShare.RowCause = Schema.Book__Share.RowCause.Test_Sharing;

        // Add the new Share record to the list of new Share records.

        lstBookShares.add(hiringManagerShare);

        }

        // Insert all of the newly created Share records and capture save result

        Database.SaveResult[] jobShareInsertResult = Database.insert(lstBookShares,false);

        //delete lstNewBooks;

        // Error handling code omitted for readability.

        }

        }

        I just copied the code as it is and made the required changes as you guided above

        But still i’m getting this error, that

        Save error: Invalid share row cause:test_sharing

        hiringManagerShare.RowCause = Schema.Book__Share.RowCause.Test_Sharing;

        at this line

        Test_Sharing : is my Sharing Reason name

        Can you guide me through this..!

  2. hi,

    In account there is related list of opportunity and i have two record type in opportunity
    These records type assign with a different page layout and in the page layout we have different fields set.

    based on pick list(Interest PickList ) value on account when we create a new opportunity in related list can we redirect to particular record type or we can say particular page layout so that user will see different layout based on different picklist value in account.

    eg in account if he select “A” as interest then while creating opportunity it should go to Page layout A of opportunity

    if he select “B” as interest while creating opportunity it should go to Page layout B of opportunity

    DInesh SIngla

    1. HI Dinesh,
      You can Override Opportunity Create Button and try to do some checks on click event of that button by getting merge fields of Account.

      I have not tried like this, but you can give a shot on this

  3. Thank you so much for sharing
    this information….

    Whenever the salesforce code
    written by the developers is over-written or deleted…This tool will help you to
    take the backup of your code and attach as an attachment. Follow the below link
    and say byee to all the code-backup problems with the help of this app.

    I would also recommend for salesforce
    users to make use of it..

    https://appexchange.salesforce.com/listingDetail?listingId=a0N3000000B3XxLEAV

  4. Thanks alot for this article! Its very useful for me. I was trying to find an information about Share objects for custom objects and i got even more here! Thanks again.

  5. Hi Jitendra,

    I inserted the share records for the an custom object and I want to share the records to the opportunity owner and users above in the role hierarchy so I created one group having opportunity owner as member and check box of grand access using hierarchy is checked and inserted the share record on custom object but it is not working .Upper in role hierarchy user are not able to access the records .Can you please help me out with this.

      1. Hi Jitendra,
        Grant access using hierarchy is checked for the public group used in the share record. on my custom object it is unchecked. Is it necessary to Grant access using hierarchy to be checked on object level then it will work for an public group ?

  6. Hi ,
    I was reading out Apex developer’s guide. It says ” Apex managed sharing is maintained across record owner changes.”.I think it is just a contradictory to your note”Important Note: If the owner of record is changed, all the Apex based sharing will be lost. “.Have to try it out.Still thinking…

  7. Hi Jitendra,
    Can you please tell me how the “Student_Access__c” has been retrived from Apex sharing reason, which is used in the step below
    studentRecord.RowCause = Schema.Test_Share.RowCause.Student_Access__c;

  8. Hi what about detail records in master detail relationship. Can I share the records by any means without sharing parent records? Can manually sharing or Apex sharing solve the purpose?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.