function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
bjzhubjzhu 

Apex Sharing - ContactShare not working

I'm writing a trigger that will automatically add sharing edit privileges for a contact to another user if that user is entered within a lookup field for that contact. I've been debugging a bit and it seems like the trigger is executing, but the sharing doesn't seem to be working properly.

 

If I set the org-wide sharing default to private, even if the trigger executes, I cannot view any contacts.

If I set the org-wide sharing default to public read only, if the trigger executes, I can edit contacts that i've shared, but not contacts that I haven't.

 

Any thoughts?

 

Here's the main code:

 

----------------------------

 

for (Contact contact : Trigger.new) {
// delete the old share
shareIdsToDelete.add(Trigger.oldMap.get(contact.id).id);
if (contact.Realtor__c != null) {
// create the new share with read/write access
ContactShare cs = new ContactShare();
cs.ContactAccessLevel = 'All';
cs.ContactId = contact.Id;

ID userId = [select id from User where Id =: contact.Realtor__c].id;

System.Debug('********USER ID**********************************************************');
System.Debug(userId);
cs.UserOrGroupId = userId;
System.Debug('********Realtor__c******');
System.Debug(contact.Realtor__c);

sharesToCreate.add(cs);
}
}
List<ContactShare> shares = [select id from ContactShare];
System.Debug('********SHARES PART 1**********************************************************');
System.Debug(shares.size());
// 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()) {
Database.SaveResult[] insertResults = Database.insert(sharesToCreate,false);

sfdcfoxsfdcfox

A few notes here:

 

1) Don't query for the user ID-- the value from Realtor__c is sufficient in and of itself (plus, you're in a loop, so a mass-update might fail with "too many queries").

 

2) You're using Database.insert's "allOrNone" parameter and setting it to false. This means that the record might not be created. You need to check your logs and see if it is failing for some unknown reason, or set it to true and see if you get an error.

 

3) Why are you using shareIdsToDelete, and even more so, why would you be using Trigger.oldMap to obtain the Id of the contact? Salesforce.com guarantees the ID will never change over the life of the object, so that's redundant. Instead, just do this:

 

delete [SELECT Id FROM ContactShare WHERE ContactId IN :Trigger.new AND RowCause = 'Manual'];

4) Avoid possiby confusing code. Like the statement that reads if(!shareidstodelete.isempty()). You will always have at least one ID in a trigger, and therefore you will always have at least one ID to delete with. Based on the style of the code being written, I presume that the author of the code is relatively new to programming in general and to Apex Code in particular. Code like this seems to suggest that shareIdsToDelete could ever be empty (which it can't) and that would therefore confuse other programmers; even I had to re-read your code just to make sure there wasn't some trick or extra logic I was missing.

 

5) Don't query shares in this code; this is a useless query, as its only purpose is for debugging. This code is wasting your precious SOQL Rows Returned and SOQL Queries Per Transaction limits. You will want to perform this logic inside of a "test method" later to make sure your logic works; this is the only acceptable time to query data that will only be used in a Debug statement (in fact, the "security review" documents suggest removing all debug statements within production code). As an extra, if you forget to remove this code, and later more stuff is tacked on, one day the query might cause a failure for no good reason, and it will be nigh impossible to track this behavior down. Better to write a proper test method to make sure it's working, and work from the debug logs.

 

I'd like to apolgize if I sound harsh or condesending in any way. Written text is so much harder for me to form into constructive criticism, and I really am just trying to be helpful.

 

Of course, do let us know if any of my tips were useful for figuring out the problem, or if you need any further assistance.

bjzhubjzhu

Thanks sfdcfox,

 

I made some the changes you suggested, the issue that I originally talked about still persists however,

 

If I set the org-wide sharing default to private, even if the trigger executes, I cannot view any contacts.

If I set the org-wide sharing default to public read only, if the trigger executes, I can edit contacts that i've shared, but not contacts that I haven't.

 

 

----------------

 

trigger ContactSharingTrigger on Contact (after insert, after update) {
    // inserting new records
    if (Trigger.isInsert) {
        List<ContactShare> sharesToCreate = new List<ContactShare>();

        for (Contact contact : Trigger.new) {
            // create the new share for group
            if (contact.Realtor__c != null) {
                ContactShare cs = new ContactShare();
                cs.ContactAccessLevel = 'All';
                cs.ContactId = contact.Id;
                cs.UserOrGroupId = contact.Realtor__c;
                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>();
        for (Contact contact : Trigger.new) {
            // delete the old share
            if (contact.Realtor__c != null) {
                // create the new share with read/write access
                ContactShare cs = new ContactShare();
                cs.ContactAccessLevel = 'All';
                cs.ContactId = contact.Id;
                cs.UserOrGroupId = contact.Realtor__c;
                sharesToCreate.add(cs);
            }
        }
        delete [SELECT Id FROM ContactShare WHERE ContactId IN :Trigger.new AND RowCause = 'Manual'];

        // do the DML to create shares
        if (!sharesToCreate.isEmpty()) {
            Database.SaveResult[] insertResults = Database.insert(sharesToCreate,true);
            //insert sharesToCreate;
        }
      }
}

sfdcfoxsfdcfox

Sharing might be optimizing away your share records, or silently discarding them because of the access level. Try using the Edit access level instead of All.