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 65 | 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 | @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.
Related posts:
- How to Unit Test Sending Mail in Apex
- Returning Contacts and Leads with Custom Wrapper Class
- Enhancing the Lead Conversion Process in Salesforce.com
- Salesforce.com ActionSupport Component Not Calling Setters
- Using PHP to call an Apex Web Service
Categories: Apex, Salesforce


