Custom Object Creation and Object Sharing Table

When we create a custom object say Quote__c, the platform will default it's OWD to Public Read/Write.

UI:

View the custom objects: https://na34.salesforce.com/p/setup/custent/CustomObjectsPage

Create a custom object: https://na34.salesforce.com/01I/e?setupid=CustomObjects

OWD of the newly created Quote__c (Object Record Table)

Since OWD for this object is least restrictive, Object Sharing Table (Quote__share) is not yet created by the platform

Let us make it less restrictive by changing OWD for this object to Private:

This will make the platform to setup Object Sharing Table (Quote__share) along with other things. This is a background task. The Admin user who made this change will be notified when this background task is completed:

Let check the availability of the share table: Quote__share

Let us query Quote__share: No records yet!:

Let us create a record in Object Record Table: Quote__c:

$ cat newQuote.json 

{
  "name": "New Quote-1"
}

 $ curl https://na34.salesforce.com/services/data/v33.0/sobjects/Quote__c/ 

         -H "Content-Type: application/json" 

         -d @newQuote.json 

         -H 'Authorization: Bearer 00D61000000dcQB!AQ4AQPqcU.5ect3zyKjaSlf3ebSi.WTIWW4Np4W.B8SKgADRFSm9Aq.OmRf8MikJQukNrNtzVZ9iiM93mqPF4WjAWr.A3PaN'


$ {"id":"a0661000002hup1AAA","success":true,"errors":[]}

Now, let us query Quote__share:

You can see a tuple (record) with RowCase: Owner and AccessLevel: All This is the work of the platform's managed sharing: providing full access to the record owner.

Now we can create a Apex class to provide Read access to non-owner of this record.

Let us find the users in the org:

Here we will provide Read access to the user Joe Simple (id:00561000001VmT8AAK aka UserOrGroupId in Record Sharing table) for this record (id:a0661000002hup1AAA aka ParentId in Record Sharing table )

Create Apex class: QuoteSharing with method:

manualShareRead(ParentId, userOrGroupId)

UI: https://na34.salesforce.com/01p

public class QuoteSharing {

   public static boolean manualShareRead(Id recordId, Id userOrGroupId){
      // Create new sharing object for the custom object Quote.
      Quote__Share quoteShr  = new Quote__Share();

      // Set the ID of record being shared.
      quoteShr.ParentId = recordId;

      // Set the ID of user or group being granted access.
      quoteShr.UserOrGroupId = userOrGroupId;

      // Set the access level.
      quoteShr.AccessLevel = 'Read';

      // Set rowCause to 'manual' for manual sharing.
      // This line can be omitted as 'manual' is the default value for sharing objects.
      quoteShr.RowCause = Schema.Job__Share.RowCause.Manual;

      // Insert the sharing record and capture the save result. 
      // The false parameter allows for partial processing if multiple records passed 
      // into the operation.
      Database.SaveResult sr = Database.insert(quoteShr,false);

      // Process the save results.
      if(sr.isSuccess()){
         // Indicates success
         return true;
      }
      else {
         // Get first save result error.
         Database.Error err = sr.getErrors()[0];

         // Check if the error is related to trival access level.
         // Access levels equal or more permissive than the object's default 
         // access level are not allowed. 
         // These sharing records are not required and thus an insert exception is acceptable. 
         if(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION  &&  
                  err.getMessage().contains('AccessLevel')){
            // Indicates success.
            return true;
         }
         else{
            // Indicates failure.
            return false;
         }
       }
   }

}

Running using tooling API:

QuoteSharing.manualShareRead('a0661000002hup1AAA', '00561000001VmT8AAK');

Now, query Quote__share:

You can see new tuple (record) with RowCase: Manual and AccessLevel: Read

This is the result of our work: User Managed Sharing Using Apex.


Apex Managed Sharing:

Only users with “Modify All Data” permission can add or change Apex managed sharing on a record. Apex managed sharing is maintained across record owner changes.

Apex managed sharing uses Apex sharing reason (provide developers ability to track why they shared a record with a user or group of users)

Let us create an Apex sharing reason:

Check the Apex Sharing Reason:

Let us create an Apex class to do Apex Managed Sharing:

public class QuoteApexManagedSharing {

       public static boolean addShareEdit(Id recordId, Id userOrGroupId){
          // Create new sharing object for the custom object Quote.
          Quote__Share quoteShr  = new Quote__Share();

          // Set the ID of record being shared.
          quoteShr.ParentId = recordId;

          // Set the ID of user or group being granted access.
          quoteShr.UserOrGroupId = userOrGroupId;

          // Set the access level.
          quoteShr.AccessLevel = 'edit';

          // Set rowCause to Schema.Quote__Share.rowCause.Quote_Write_Sharing__c

          quoteShr.RowCause = Schema.Quote__Share.rowCause.Quote_Write_Sharing__c;

          // Insert the sharing record and capture the save result. 
          // The false parameter allows for partial processing if multiple records passed 
          // into the operation.
          Database.SaveResult sr = Database.insert(quoteShr,false);

          // Process the save results.
          if(sr.isSuccess()){
             // Indicates success
             return true;
          }
          else {
             // Get first save result error.
             Database.Error err = sr.getErrors()[0];

             // Check if the error is related to trival access level.
             // Access levels equal or more permissive than the object's default 
             // access level are not allowed. 
             // These sharing records are not required and thus an insert exception is acceptable. 
             if(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION  &&  
                      err.getMessage().contains('AccessLevel')){
                // Indicates success.
                return true;
             }
             else{
                // Indicates failure.
                return false;
             }
           }
       }

    }

Run using tooling API:

QuoteApexManagedSharing.addShareEdit('a0661000002hup1AAA', '00561000001VmT8AAK');

View Log:

Query Object Sharing Table: Quote__share:

You can see new tuple (record) with RowCase: Quote_Write_Sharing__c and AccessLevel: Edit

This is the result of our work: Apex Managed Sharing .

As you here, Object-id: a0661000002hup1AAA for the UserOrGroupId: 00561000001VmT8AAK has both Read and Edit, Platform will provide more permissive access (Edit, in this case) to this user.

Notes

If a manual share access level is set to Read and you insert a new one that’s set to Write, the original share rows are updated to Write, indicating the higher level of access

Testing this:

public class QuoteSharingGeneric {

   public static boolean manualShare(Id recordId, Id userOrGroupId, String accessLevel){
      // Create new sharing object for the custom object Quote.
      Quote__Share quoteShr  = new Quote__Share();

      // Set the ID of record being shared.
      quoteShr.ParentId = recordId;

      // Set the ID of user or group being granted access.
      quoteShr.UserOrGroupId = userOrGroupId;

      // Set the access level.
      quoteShr.AccessLevel = accessLevel;

      // Set rowCause to 'manual' for manual sharing.
      // This line can be omitted as 'manual' is the default value for sharing objects.
      quoteShr.RowCause = Schema.Job__Share.RowCause.Manual;

      // Insert the sharing record and capture the save result. 
      // The false parameter allows for partial processing if multiple records passed 
      // into the operation.
      Database.SaveResult sr = Database.insert(quoteShr,false);

      // Process the save results.
      if(sr.isSuccess()){
         // Indicates success
         return true;
      }
      else {
         // Get first save result error.
         Database.Error err = sr.getErrors()[0];

         // Check if the error is related to trival access level.
         // Access levels equal or more permissive than the object's default 
         // access level are not allowed. 
         // These sharing records are not required and thus an insert exception is acceptable. 
         if(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION  &&  
                  err.getMessage().contains('AccessLevel')){
            // Indicates success.
            return true;
         }
         else{
            // Indicates failure.
            return false;
         }
       }
   }

}

Run: QuoteSharingGeneric.manualShare('a0661000002hup1AAA', '00561000001VmT8AAK', 'Edit');