Deleting Salesforce Contacts You Don’t Own

If you have a private sharing model for Accounts and Contacts in Salesforce, you might have run into this problem. You can only delete records you don’t own in Salesforce if you have been given Modify all Data permissions for the object or if you are in a Role above the owner. Our business requirements are a bit different from this. We wanted accounts to be private and shared by sharing rules to groups of other users. All of those users expect to be able to make changes to all contacts related to the account, including deleting contacts.

Let me digress here a minute: in my opinion, you shouldn’t delete contacts if they have any activities associated with them – they should be mark “Inactive” so we don’t lose all that history. We’ve added a before delete trigger to contacts to prevent them from being deleted in that case. In some cases, you might really need to delete a contact: it is a duplicate or there is no activity and it is just cluttering things up.

OK, back to the task at hand. Try as I might, I couldn’t figure out any way to accomplish deleting of contacts that were associated with accounts I could view and edit. I finally decided to roll my own solution with a custom button and some apex. It turned out to be pretty simple, but there is some room for improvement.

First the Apex code (complete with test!). In this code, I create a webservice that can be called by a custom button. It passes in the Contact Id and then deletes it. In the test, I create a contact and then try deleting the contact as a different user. Note that you might need to change the profile to get a valid user.

global without sharing class ContactUtil {
  webService static Boolean deleteContact(Id id) {
    Contact c = new Contact(id = id);
    try {
      delete c;
      return true;
    } catch (Exception e) {
      return false;
  }
}
 
  static testMethod void testDeleteContact() {
    Contact c = new Contact(LastName = 'Test');
    insert c;
    User u = [select id from user where Profile.Name = 'Sales User' and IsActive = true  limit 1];
    System.runAs(u) {
      deleteContact(c.Id);
    }
 
    Contact[] testContact = [select Id from Contact where Id = :c.Id];
    System.assert(testContact.isEmpty());
  }
}

Now that we have the webservice, we can create a button to add to the contact layout page. It will be a Detail Page Button that Executes JavaScript. Here’s what the javascript looks like. It prompts the user and if successful, redirects to the Contact tab.

{!REQUIRESCRIPT("/soap/ajax/22.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/22.0/apex.js")}
 
if (confirm("Are you sure?")) {
  if (sforce.apex.execute("ContactUtil","deleteContact", {id:"{!Contact.Id}"})) {
    window.location.replace('/003/o');
  }
}

Add this button to your page layout and remove the standard delete button. Now your users will be able to delete any contacts that they can view on the detail page.

A couple of words of caution. You might have noticed that there is no validation that the user should be able to delete the contact. Ideally, I’d like to test to see if the user has write access to that contact record. If so, then allow the delete. I’ve been unable to find a way to test record level access, so I’ve left it wide open. [UPDATE: I found a way! http://verticalcode.wordpress.com/2012/04/03/deleting-salesforce-contacts-you-dont-own-part-2/] Because of this, make sure that you only give permissions to the the ContactUtil class who you want to be able to delete all contacts.

10 thoughts on “Deleting Salesforce Contacts You Don’t Own”

  1. Wow, this is possible solution to my problem. However, I’m not a coder so I’m potentially may be too deep here. It seems rather straightforward, except for your last statement “make sure that you only give permissions to the the ContactUtil class who you want to be able to delete all contacts”. How exactly do I do that?

    My exact need is to allow users to delete any contact owned by one particular user. So I made a sharing rule that allows all users to read/write contacts owned by that user, but as you know, can’t grant them access to delete.

    If you can explain your last point on permissions, I think I may be able to do this.

    Thanks!

    1. You give permissions to classes by going to Setup->Develop->Apex Classes and clicking on ContactUtil then click on Security. You, of course need to have the class created first. Always test in a sandbox first and then deploy to production using Change Sets or the IDE.

  2. Daniel,

    Great finding and post. I would like to know if there is any way to update the case comments that the user hasnt created? I read in salesforce documentation that “Modify all” on case will help do this but we cannot give that.

    1. That would be more difficult to do since you need a screen for the user to edit case comments. What I think you’d need to do is create a custom Visualforce page that uses a controller with the without sharing keyword.

      1. Daniel,

        I am alredy doing this. I have a vf page and a standard controller of case and its extension class. But when I try to edit the case comment ceated by another user, I am geting ‘insufficient privileges’ page.

        Any ideas?

        1. I wonder if it has something to do with the standard controller? I just made a really crude page with custom controller to test this. I was able to edit a case comment.

  3. Daniel,

    Thanks for the reply and time. I just created a page without sharing, which calls a method in the class shown below (I am not using standard controller in the page).

    public PageReference pageConstructor(){

    cId = ApexPages.currentPage().getParameters().get(‘Id’);
    system.debug(‘value of current page: ‘+cId);

    cs = new List([SELECT Id,CaseNumber FROM Case WHERE Id =: cId]);

    return null;
    }

    Edit still says insufficient privileges.. Did you mean something else?

    1. Do you have a page that you use to edit the case comment? The code you provided retrieves the Case, but not the Case Comment. Here’s what I did with a quick hack:

      Controller (replace the ID with a real CaseComment Id):
      public class CaseCommentsController {

      public CaseComment cc {
      get {
      if (cc == null) {
      cc = [select Id, CommentBody from CaseComment where id = '00aU0000003K43b'];
      }
      return cc;
      }
      set;
      }

      public PageReference save() {
      update cc;
      return null;
      }
      }

  4. I have observed that any user is able to update the comments nomatter who created tham. so I am planning to not use the standard URL for redirection and use a custom text box and then taking it as input and then update case comments in the constructor class

Leave a Reply