Programmatically Creating Sharing Rules with Apex

November 25th, 2009

Here’s a small Apex Trigger that demonstrates how to programmatically create sharing rules for objects with a private sharing model. It came in handy on a project about a year ago so I thought I’d post it.

So the scenario is that the object has a private sharing model (Contacts in this case) so that only the record owner and users higher in the role hierarchy have access to it. I added a checkbox to the Contact object called “Make Public” that when set to TRUE, creates a sharing record for a specific group (e.g., All Internal Users). When set to FALSE, it deletes all manual sharing rules for the record. You can modify this to add multiple groups or even make it operate on the reverse (public by default and then remove the sharing rules).

ContactMakePublicTrigger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
trigger ContactMakePublicTrigger on Contact (after insert, after update) {
 
  // get the id for the group for everyone in the org
  ID groupId = [select id from Group where Type = 'Organization'].id;
 
  // inserting new records
  if (Trigger.isInsert) {
 
    List<ContactShare> sharesToCreate = new List<ContactShare>();
 
    for (Contact contact : Trigger.new) {
      if (contact.Make_Public__c == true) {
 
        // create the new share for group
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = contact.Id;
        cs.UserOrGroupId =  groupId;
        sharesToCreate.add(cs);
 
      }
    }
 
    // do the DML to create shares
    if (!sharesToCreate.isEmpty())
      insert sharesToCreate;
 
  // updating existing records
  } else if (Trigger.isUpdate) {
 
    List<ContactShare> sharesToCreate = new List<ContactShare>();
    List<ID> shareIdsToDelete = new List<ID>();
 
    for (Contact contact : Trigger.new) {
 
      // if the record was public but is now private -- delete the existing share
      if (Trigger.oldMap.get(contact.id).Make_Public__c == true && contact.Make_Public__c == false) {
        shareIdsToDelete.add(contact.id);
 
      // if the record was private but now is public -- create the new share for the group
      } else if (Trigger.oldMap.get(contact.id).Make_Public__c == false && contact.Make_Public__c == true) {
 
        // create the new share with read/write access
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = contact.Id;
        cs.UserOrGroupId =  groupId;
        sharesToCreate.add(cs);
 
      }
 
    }
 
    // do the DML to delete shares
    if (!shareIdsToDelete.isEmpty())
      delete [select id from ContactShare where ContactId IN :shareIdsToDelete and RowCause = 'Manual'];
 
    // do the DML to create shares
    if (!sharesToCreate.isEmpty())
      insert sharesToCreate;
 
  }
 
}

TestContactMakePublicTrigger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@isTest
private class TestContactMakePublicTrigger {
 
    // test that newly inserted records marked as pubic=true have corresponding shares created
    static testMethod void testAddShares() {
 
    Set<ID> ids = new Set<ID>();
    List<Contact> contacts = new List<Contact>();
 
    for (Integer i=0;i<50;i++)
      contacts.add(new Contact(FirstName='First ',LastName='Name '+i,
        Email='email'+i+'@email.com',Make_Public__c=true));
 
    insert contacts;
 
    // get a set of all new created ids
    for (Contact c : contacts)
      ids.add(c.id);
 
    // assert that 50 shares were created
    List<ContactShare> shares = [select id from ContactShare where 
      ContactId IN :ids and RowCause = 'Manual'];
    System.assertEquals(shares.size(),50);
 
    }
 
    // insert records and switch them from public = true to public = false
    static testMethod void testUpdateContacts() {
 
    Set<ID> ids = new Set<ID>();
    List<Contact> contacts = new List<Contact>();
 
    for (Integer i=0;i<50;i++)
      contacts.add(new Contact(FirstName='First ',LastName='Name '+i,
        Email='email'+i+'@email.com',Make_Public__c=false));
 
    insert contacts;
 
    for (Contact c : contacts)
      ids.add(c.id);
 
    update contacts;
 
    // assert that 0 shares exist
    List<ContactShare> shares = [select id from ContactShare where 
      ContactId IN :ids and RowCause = 'Manual'];
    System.assertEquals(shares.size(),0);
 
    for (Contact c : contacts)
      c.Make_Public__c = true;
 
    update contacts;
 
    // assert that 50 shares were created
    shares = [select id from ContactShare where ContactId IN :ids and RowCause = 'Manual'];
    System.assertEquals(shares.size(),50);
 
    for (Contact c : contacts)
      c.Make_Public__c = false;
 
    update contacts;
 
    // assert that 0 shares exist
    shares = [select id from ContactShare where ContactId IN :ids and RowCause = 'Manual'];
    System.assertEquals(shares.size(),0);
 
    }
}

One thing to remember. When transferring accounts and their related data, all existing sharing rules will be removed. Any relevant sharing rules are then applied to the records based upon the new owners. You may need to manually share these accounts and opportunities to grant access to certain users and/or groups.

VN:F [1.9.3_1094]
Rating: 10.0/10 (1 vote cast)
VN:F [1.9.3_1094]
Rating: 0 (from 0 votes)
Programmatically Creating Sharing Rules with Apex, 10.0 out of 10 based on 1 rating

Categories: Apex, Code Sample, Salesforce

Leave a comment

Comments Feed5 Comments

  1. Nigel Folkes

    Hi Jeff

    I’ve got a customer with many contacts which are shared between a number of the users. They are heavy Outlook users. I have developed a custom object for additional contact sharing to associate other users with contacts and then an trigger on this object to insert a contactshare record based on this information. I assumed that by doing this it would include these shares in the sychronisation for outlook. but with Manual & Edit properties it doesn’t. do you know if there are settings we can set from Apex or via the API which can force the contact to be synchronised to more than one user. Don’t want to use the Accountshare or Account team mechanism as it generates far too many contact shares. Any thoughts ? Thanks. Nigel

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  2. Jeff Douglas

    Unfortunately I am not too familiar with the Outlook connector. I try to stay away from Outlook. You might want to post this on the Salesforce message boards as one of the PMs may pick it up. Good luck.

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  3. Rhonda Ross

    That’s for blogging on this and especially for including your test code. I found some sample triggers for this in Salesforce’s documentation but without the test code. As a administrator I can often figure out how to tweak code to meet my needs, but I can’t do it from scratch. Maybe someday.

    I’d love to be able to do this all through clicks instead of code. Vote for that here
    https://sites.secure.force.com/ideaexchange/ideaView?c=09a30000000D9xt&id=08730000000BrUvAAK

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  4. Sara Chieco

    Hello,

    We wrote very similar code (identical in the contact sharing syntax) and it throws a compile error saying the Contact ID is not writeable. Have you also had this error? We were looking on the boards and saw a reference to this code in someone else’s post so I figured I would see if you have any advice on this.

    Thanks!
    Sara

    VA:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  5. Jeff Douglas

    You may want to check your code again more closely. Not sure why you are trying to change the Contact ID but Salesforce record IDs are not writable.

    VN:F [1.9.3_1094]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.3_1094]
    Rating: 0 (from 0 votes)

Leave a comment

Feed

http://blog.jeffdouglas.com / Programmatically Creating Sharing Rules with Apex

WordPress Appliance - Powered by TurnKey Linux