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
Holly Havelka 10Holly Havelka 10 

Help with Apex Sharing Trigger on Custom Object

Hi all,

I have the below trigger, which is throwing this error: First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Contact, User/Group]: [Contact, User/Group]: Trigger.AffiliationMakePublicContactTrigger: line 64, column 1
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (before insert) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
    
    for(npe5__Affiliation__c aff : trigger.new)
    {
     
     if(aff.npe5__Organization__r != null)
     {
        AcctId.add(aff.npe5__Organization__r.Id);
     }

    }
     
     if(AcctId.size() > 0)
     {
         AccountLists  = [select Id,name from Account where Id IN: AcctId];
     }

    for(Account acc :AccountLists  )
    {
        AccountNameMap.put(acc.id,acc.Name);
    }
    
    // Get the Group Details
    List<Group> groups = [SELECT Email,Id,Name FROM Group];

    Map<String,Id> GroupMap = new MAp<String,Id>();
    for( Group grp:groups)
    {
    	if(grp.Name != null && grp.Name != '')
    	{
        	GroupMap.put(grp.Name,grp.Id);
    	}
    }
  
  // inserting new records
  if (Trigger.isInsert) {
             
    List<ContactShare> sharesToCreate = new List<ContactShare>();

    for (npe5__Affiliation__c affiliation : Trigger.new) {
        
      if (affiliation.Make_Public__c == true) 
      {

        // create the new share for group
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;        
        system.debug(cs.ContactId);
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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 (npe5__Affiliation__c affiliation : Trigger.new) {

      // if the record was public but is now private -- delete the existing share
      if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
        shareIdsToDelete.add(affiliation.Id);

      // if the record was private but now is public -- create the new share for the group
      } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
        
        // create the new share with read/write access
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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;

  }

}
I have the affiliation object (from NPSP), and it creates the following records with these fields: organization affiliation (account lookup), contact (contact lookup) and 'make public' field. 
I want to share the contact (contact lookup) from the affiliation record with a specified public group whether that happens on insert or on update.  
Any thoughts on what I am missing?
 
Best Answer chosen by Holly Havelka 10
Steven NsubugaSteven Nsubuga
Yes! Try this
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert, after update, after delete) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
	
    if (!Trigger.isDelete) {
		List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.newMap.keyset()];
		for(npe5__Affiliation__c aff : affiliations)
		{
		 
		 if(aff.npe5__Organization__r != null)
		 {
			AcctId.add(aff.npe5__Organization__r.Id);
		 }

		}
		 
		 if(AcctId.size() > 0)
		 {
			 AccountLists  = [select Id,name from Account where Id IN: AcctId];
		 }

		for(Account acc :AccountLists  )
		{
			AccountNameMap.put(acc.id,acc.Name);
		}
		
		// Get the Group Details
		List<Group> groups = [SELECT Email,Id,Name FROM Group];

		Map<String,Id> GroupMap = new MAp<String,Id>();
		for( Group grp:groups)
		{
			if(grp.Name != null && grp.Name != '')
			{
				GroupMap.put(grp.Name,grp.Id);
			}
		}
	  
	  // inserting new records
	  if (Trigger.isInsert) {
				 
		List<ContactShare> sharesToCreate = new List<ContactShare>();

		
		for (npe5__Affiliation__c affiliation : affiliations) {
			
		  if (affiliation.Make_Public__c == true) 
		  {

			// create the new share for group
			ContactShare cs = new ContactShare();
			cs.ContactAccessLevel = 'Edit';
			cs.ContactId = affiliation.npe5__Contact__r.Id;        
			system.debug(cs.ContactId);
			if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
					cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
			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 (npe5__Affiliation__c affiliation : affiliations) {

		  // if the record was public but is now private -- delete the existing share
		  if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
			shareIdsToDelete.add(affiliation.Id);

		  // if the record was private but now is public -- create the new share for the group
		  } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
			
			// create the new share with read/write access
			ContactShare cs = new ContactShare();
			cs.ContactAccessLevel = 'Edit';
			cs.ContactId = affiliation.npe5__Contact__r.Id;
			if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
					cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
			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;

	  }
  }
  else if (Trigger.isDelete) {

    List<ID> shareIdsToDelete = new List<ID>();

	List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.oldMap.keyset()];
    for (npe5__Affiliation__c affiliation : affiliations) {
        shareIdsToDelete.add(affiliation.npe5__Contact__r.Id);
    }

    // do the DML to delete shares
    if (!shareIdsToDelete.isEmpty())
      delete [select id from ContactShare where ContactId IN :shareIdsToDelete and RowCause = 'Manual'];
  }

}

 

All Answers

Raj VakatiRaj Vakati
you need to use after insert 
 
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
    
    for(npe5__Affiliation__c aff : trigger.new)
    {
     
     if(aff.npe5__Organization__r != null)
     {
        AcctId.add(aff.npe5__Organization__r.Id);
     }

    }
     
     if(AcctId.size() > 0)
     {
         AccountLists  = [select Id,name from Account where Id IN: AcctId];
     }

    for(Account acc :AccountLists  )
    {
        AccountNameMap.put(acc.id,acc.Name);
    }
    
    // Get the Group Details
    List<Group> groups = [SELECT Email,Id,Name FROM Group];

    Map<String,Id> GroupMap = new MAp<String,Id>();
    for( Group grp:groups)
    {
    	if(grp.Name != null && grp.Name != '')
    	{
        	GroupMap.put(grp.Name,grp.Id);
    	}
    }
  
  // inserting new records
  if (Trigger.isInsert) {
             
    List<ContactShare> sharesToCreate = new List<ContactShare>();

    for (npe5__Affiliation__c affiliation : Trigger.new) {
        
      if (affiliation.Make_Public__c == true) 
      {

        // create the new share for group
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;        
        system.debug(cs.ContactId);
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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 (npe5__Affiliation__c affiliation : Trigger.new) {

      // if the record was public but is now private -- delete the existing share
      if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
        shareIdsToDelete.add(affiliation.Id);

      // if the record was private but now is public -- create the new share for the group
      } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
        
        // create the new share with read/write access
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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;

  }

}

 
Holly Havelka 10Holly Havelka 10
Raj, 

Here is my updated code, which is still throwing the same error:
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert, after update) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
    
    for(npe5__Affiliation__c aff : trigger.new)
    {
     
     if(aff.npe5__Organization__r != null)
     {
        AcctId.add(aff.npe5__Organization__r.Id);
     }

    }
     
     if(AcctId.size() > 0)
     {
         AccountLists  = [select Id,name from Account where Id IN: AcctId];
     }

    for(Account acc :AccountLists  )
    {
        AccountNameMap.put(acc.id,acc.Name);
    }
    
    // Get the Group Details
    List<Group> groups = [SELECT Email,Id,Name FROM Group];

    Map<String,Id> GroupMap = new MAp<String,Id>();
    for( Group grp:groups)
    {
    	if(grp.Name != null && grp.Name != '')
    	{
        	GroupMap.put(grp.Name,grp.Id);
    	}
    }
  
  // inserting new records
  if (Trigger.isInsert) {
             
    List<ContactShare> sharesToCreate = new List<ContactShare>();

    for (npe5__Affiliation__c affiliation : Trigger.new) {
        
      if (affiliation.Make_Public__c == true) 
      {

        // create the new share for group
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;        
        system.debug(cs.ContactId);
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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 (npe5__Affiliation__c affiliation : Trigger.new) {

      // if the record was public but is now private -- delete the existing share
      if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
        shareIdsToDelete.add(affiliation.Id);

      // if the record was private but now is public -- create the new share for the group
      } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
        
        // create the new share with read/write access
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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;

  }

}

 
Steven NsubugaSteven Nsubuga
Try this Holly
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert, after update) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
    
    for(npe5__Affiliation__c aff : trigger.new)
    {
     
     if(aff.npe5__Organization__r != null)
     {
        AcctId.add(aff.npe5__Organization__r.Id);
     }

    }
     
     if(AcctId.size() > 0)
     {
         AccountLists  = [select Id,name from Account where Id IN: AcctId];
     }

    for(Account acc :AccountLists  )
    {
        AccountNameMap.put(acc.id,acc.Name);
    }
    
    // Get the Group Details
    List<Group> groups = [SELECT Email,Id,Name FROM Group];

    Map<String,Id> GroupMap = new MAp<String,Id>();
    for( Group grp:groups)
    {
    	if(grp.Name != null && grp.Name != '')
    	{
        	GroupMap.put(grp.Name,grp.Id);
    	}
    }
  
  // inserting new records
  if (Trigger.isInsert) {
             
    List<ContactShare> sharesToCreate = new List<ContactShare>();

	List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.newMap.keyset()];
    for (npe5__Affiliation__c affiliation : affiliations) {
        
      if (affiliation.Make_Public__c == true) 
      {

        // create the new share for group
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;        
        system.debug(cs.ContactId);
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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>();

	List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.newMap.keyset()];
    for (npe5__Affiliation__c affiliation : affiliations) {

      // if the record was public but is now private -- delete the existing share
      if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
        shareIdsToDelete.add(affiliation.Id);

      // if the record was private but now is public -- create the new share for the group
      } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
        
        // create the new share with read/write access
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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;

  }

}

 
Holly Havelka 10Holly Havelka 10
Steve, it's still throwing the same error.
Steven NsubugaSteven Nsubuga
Try this then
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert, after update) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
    
	List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.newMap.keyset()];
    for(npe5__Affiliation__c aff : affiliations)
    {
     
     if(aff.npe5__Organization__r != null)
     {
        AcctId.add(aff.npe5__Organization__r.Id);
     }

    }
     
     if(AcctId.size() > 0)
     {
         AccountLists  = [select Id,name from Account where Id IN: AcctId];
     }

    for(Account acc :AccountLists  )
    {
        AccountNameMap.put(acc.id,acc.Name);
    }
    
    // Get the Group Details
    List<Group> groups = [SELECT Email,Id,Name FROM Group];

    Map<String,Id> GroupMap = new MAp<String,Id>();
    for( Group grp:groups)
    {
    	if(grp.Name != null && grp.Name != '')
    	{
        	GroupMap.put(grp.Name,grp.Id);
    	}
    }
  
  // inserting new records
  if (Trigger.isInsert) {
             
    List<ContactShare> sharesToCreate = new List<ContactShare>();

	
    for (npe5__Affiliation__c affiliation : affiliations) {
        
      if (affiliation.Make_Public__c == true) 
      {

        // create the new share for group
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;        
        system.debug(cs.ContactId);
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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 (npe5__Affiliation__c affiliation : affiliations) {

      // if the record was public but is now private -- delete the existing share
      if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
        shareIdsToDelete.add(affiliation.Id);

      // if the record was private but now is public -- create the new share for the group
      } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
        
        // create the new share with read/write access
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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;

  }

}

 
Holly Havelka 10Holly Havelka 10
Steven, that cleared the error, but on delete of the affiliation record, it does not remove the manual sharing record?
Steven NsubugaSteven Nsubuga
Holly, my understanding is that if the affiliation record is deleted then the sharing record automatically gets deleted. I suggest you test this.
Holly Havelka 10Holly Havelka 10
Steve, I just tested in my sandbox, when I delete the affiliation record, the sharing record is not deleted.  The sharing record is on the contact not the affiliation record.
Steven NsubugaSteven Nsubuga
Here is an updated trigger, with after delete to handle the deletion.
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert, after update, after delete) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
    
	List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.newMap.keyset()];
    for(npe5__Affiliation__c aff : affiliations)
    {
     
     if(aff.npe5__Organization__r != null)
     {
        AcctId.add(aff.npe5__Organization__r.Id);
     }

    }
     
     if(AcctId.size() > 0)
     {
         AccountLists  = [select Id,name from Account where Id IN: AcctId];
     }

    for(Account acc :AccountLists  )
    {
        AccountNameMap.put(acc.id,acc.Name);
    }
    
    // Get the Group Details
    List<Group> groups = [SELECT Email,Id,Name FROM Group];

    Map<String,Id> GroupMap = new MAp<String,Id>();
    for( Group grp:groups)
    {
    	if(grp.Name != null && grp.Name != '')
    	{
        	GroupMap.put(grp.Name,grp.Id);
    	}
    }
  
  // inserting new records
  if (Trigger.isInsert) {
             
    List<ContactShare> sharesToCreate = new List<ContactShare>();

	
    for (npe5__Affiliation__c affiliation : affiliations) {
        
      if (affiliation.Make_Public__c == true) 
      {

        // create the new share for group
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;        
        system.debug(cs.ContactId);
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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 (npe5__Affiliation__c affiliation : affiliations) {

      // if the record was public but is now private -- delete the existing share
      if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
        shareIdsToDelete.add(affiliation.Id);

      // if the record was private but now is public -- create the new share for the group
      } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
        
        // create the new share with read/write access
        ContactShare cs = new ContactShare();
        cs.ContactAccessLevel = 'Edit';
        cs.ContactId = affiliation.npe5__Contact__r.Id;
        if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
        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;

  }
  
  else if (Trigger.isDelete) {

    List<ID> shareIdsToDelete = new List<ID>();

	List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.oldMap.keyset()];
    for (npe5__Affiliation__c affiliation : affiliations) {
        shareIdsToDelete.add(affiliation.npe5__Contact__r.Id);
    }

    // do the DML to delete shares
    if (!shareIdsToDelete.isEmpty())
      delete [select id from ContactShare where ContactId IN :shareIdsToDelete and RowCause = 'Manual'];
  }

}

 
Holly Havelka 10Holly Havelka 10
Steven, it is throwing this error:
Validation Errors While Saving Record(s)
There were custom validation error(s) encountered while saving the affected record(s). The first validation error encountered was "Apex trigger AffiliationMakePublicContactTrigger caused an unexpected exception, contact your administrator: AffiliationMakePublicContactTrigger: execution of BeforeDelete caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.AffiliationMakePublicContactTrigger: line 8, column 1". 

Is this because the trigger is trying to execute all of the logic before the isDelete section?
Steven NsubugaSteven Nsubuga
Yes! Try this
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert, after update, after delete) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
	
    if (!Trigger.isDelete) {
		List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.newMap.keyset()];
		for(npe5__Affiliation__c aff : affiliations)
		{
		 
		 if(aff.npe5__Organization__r != null)
		 {
			AcctId.add(aff.npe5__Organization__r.Id);
		 }

		}
		 
		 if(AcctId.size() > 0)
		 {
			 AccountLists  = [select Id,name from Account where Id IN: AcctId];
		 }

		for(Account acc :AccountLists  )
		{
			AccountNameMap.put(acc.id,acc.Name);
		}
		
		// Get the Group Details
		List<Group> groups = [SELECT Email,Id,Name FROM Group];

		Map<String,Id> GroupMap = new MAp<String,Id>();
		for( Group grp:groups)
		{
			if(grp.Name != null && grp.Name != '')
			{
				GroupMap.put(grp.Name,grp.Id);
			}
		}
	  
	  // inserting new records
	  if (Trigger.isInsert) {
				 
		List<ContactShare> sharesToCreate = new List<ContactShare>();

		
		for (npe5__Affiliation__c affiliation : affiliations) {
			
		  if (affiliation.Make_Public__c == true) 
		  {

			// create the new share for group
			ContactShare cs = new ContactShare();
			cs.ContactAccessLevel = 'Edit';
			cs.ContactId = affiliation.npe5__Contact__r.Id;        
			system.debug(cs.ContactId);
			if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
					cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
			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 (npe5__Affiliation__c affiliation : affiliations) {

		  // if the record was public but is now private -- delete the existing share
		  if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
			shareIdsToDelete.add(affiliation.Id);

		  // if the record was private but now is public -- create the new share for the group
		  } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
			
			// create the new share with read/write access
			ContactShare cs = new ContactShare();
			cs.ContactAccessLevel = 'Edit';
			cs.ContactId = affiliation.npe5__Contact__r.Id;
			if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
					cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
			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;

	  }
  }
  else if (Trigger.isDelete) {

    List<ID> shareIdsToDelete = new List<ID>();

	List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__r, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.oldMap.keyset()];
    for (npe5__Affiliation__c affiliation : affiliations) {
        shareIdsToDelete.add(affiliation.npe5__Contact__r.Id);
    }

    // do the DML to delete shares
    if (!shareIdsToDelete.isEmpty())
      delete [select id from ContactShare where ContactId IN :shareIdsToDelete and RowCause = 'Manual'];
  }

}

 
This was selected as the best answer
Holly Havelka 10Holly Havelka 10
Steven, thanks for all your help, I did have a few tweaks to add, but here is my updated code which works perfectly:
trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert, after update, before delete) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
	
    if (!Trigger.isDelete) {
		List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__c, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.newMap.keyset()];
		for(npe5__Affiliation__c aff : affiliations)
		{
		 
		 if(aff.npe5__Organization__r != null)
		 {
			AcctId.add(aff.npe5__Organization__r.Id);
		 }

		}
		 
		 if(AcctId.size() > 0)
		 {
			 AccountLists  = [select Id,name from Account where Id IN: AcctId];
		 }

		for(Account acc :AccountLists  )
		{
			AccountNameMap.put(acc.id,acc.Name);
		}
		
		// Get the Group Details
		List<Group> groups = [SELECT Email,Id,Name FROM Group];

		Map<String,Id> GroupMap = new MAp<String,Id>();
		for( Group grp:groups)
		{
			if(grp.Name != null && grp.Name != '')
			{
				GroupMap.put(grp.Name,grp.Id);
			}
		}
	  
	  // inserting new records
	  if (Trigger.isInsert) {
				 
		List<ContactShare> sharesToCreate = new List<ContactShare>();

		
		for (npe5__Affiliation__c affiliation : affiliations) {
			
		  if (affiliation.Make_Public__c == true) 
		  {

			// create the new share for group
			ContactShare cs = new ContactShare();
			cs.ContactAccessLevel = 'Edit';
			cs.ContactId = affiliation.npe5__Contact__r.Id;        
			system.debug(cs.ContactId);
			if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
					cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
			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 (npe5__Affiliation__c affiliation : affiliations) {

		  // if the record was public but is now private -- delete the existing share
		  if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
			shareIdsToDelete.add(affiliation.npe5__Contact__r.Id);

		  // if the record was private but now is public -- create the new share for the group
		  } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
			
			// create the new share with read/write access
			ContactShare cs = new ContactShare();
			cs.ContactAccessLevel = 'Edit';
			cs.ContactId = affiliation.npe5__Contact__r.Id;
			if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
					cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
			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;

	  }
  }
  else if (Trigger.isDelete) {

    List<ID> shareIdsToDelete = new List<ID>();

	List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__c, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.oldMap.keyset()];
    for (npe5__Affiliation__c affiliation : affiliations) {
        shareIdsToDelete.add(affiliation.npe5__Contact__r.Id);
        system.debug(shareIdsToDelete);
    }

    // do the DML to delete shares
    if (!shareIdsToDelete.isEmpty())
      delete [select id from ContactShare where ContactId IN :shareIdsToDelete and RowCause = 'Manual'];
  }

}

I changed it to 'before delete' and I fixed the '// if the record was public but is now private -- delete the existing share' line as it was not pulling in the contact id to remove the sharing if the 'Make Public' was removed.
Holly Havelka 10Holly Havelka 10
Steve,  I want to modify the Trigger.isDelete to only delete the ContactShare specified by the deletion of the affiliation record.  Right now, this isDelete trigger deletes all shares on the contact with row cause 'manual'.  For example, if there are (2) affiliation records on a contact, I want the isDelete trigger to only delete the 1st contactshare vs. removing both contactshares.  Any thoughts?
Steven NsubugaSteven Nsubuga
Hi Holly, apologies, I am only now just seeing this.  You said I want the isDelete trigger to only delete the 1st contactshare vs. removing both contactshares. It depends on what you mean by the 1st contactshare,  would it be the oldest in the system based on created date? Would it be the latest one? 
 
Holly Havelka 10Holly Havelka 10
Hi Steve,  thanks for getting back with me.  As an example, there are (2) affiliations created for (1) contact:
  • Affiliation 1 - Test Organization A, Make Public = True, Test Contact
    • Contact share record created for Test Organization A (Public Group)
  • Affiliation 2 - Test Organization B, Make Public = True, Test Contact
    • Contact share record created for Test Organization B (Public Group)
When I delete the record for Affiliation 1, I want the trigger to also delete the contact share record for Test Organization A (Public Group), but leave the contact share record for Test Organization B (Public Group) untouched.
Steven NsubugaSteven Nsubuga
The challenge is that there is no way to link a contactshare to an affiliation.
My suggestion would be to have a new object, a junction object to link contactshare and affiliation.
This would require the trigger to be updated such that this junction object is created in the insert, updated in the update and deleted in the delete.
Holly Havelka 10Holly Havelka 10
Steve are you saying to use this junction object to stage/store the relationship.  Then on delete you would reference that custom object to do the lookup for the affilation and contactshare?  
Steven NsubugaSteven Nsubuga
Yes Holly.
Use this junction object to stage/store the relationship.  Then on delete you would reference that custom object to do the lookup for the affilation and contactshare, and delete the specific contactshare.
Holly Havelka 10Holly Havelka 10
Steve,  I was able to write in the below code addition and it is executing as desired, can you take a look and let me know if I am missing something:
/*Created to give manual sharing to affiliated organizations with public groups(same name) if 'Make Public' field equals=true*/

trigger AffiliationMakePublicContactTrigger on npe5__Affiliation__c (after insert, after update, before delete) {
 
    // Get the Account Name Details
    Set<Id> AcctId = new Set<Id>();
    List<Account> AccountLists = new List<Account>();
    Map<Id,String> AccountNameMap = new Map<Id,String>();
    
    if (!Trigger.isDelete) {
        List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__c, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.newMap.keyset()];
        for(npe5__Affiliation__c aff : affiliations)
        {
         
         if(aff.npe5__Organization__r != null)
         {
            AcctId.add(aff.npe5__Organization__r.Id);
         }

        }
         
         if(AcctId.size() > 0)
         {
             AccountLists  = [select Id,name from Account where Id IN: AcctId];
         }

        for(Account acc :AccountLists  )
        {
            AccountNameMap.put(acc.id,acc.Name);
        }
        
        // Get the Group Details
        List<Group> groups = [SELECT Email,Id,Name FROM Group];

        Map<String,Id> GroupMap = new MAp<String,Id>();
        for( Group grp:groups)
        {
            if(grp.Name != null && grp.Name != '')
            {
                GroupMap.put(grp.Name,grp.Id);
            }
        }
      
      // inserting new records
      if (Trigger.isInsert) {
                 
        List<ContactShare> sharesToCreate = new List<ContactShare>();

        
          for (npe5__Affiliation__c affiliation : affiliations) {
            
          if (affiliation.Make_Public__c == true) 
          {

            // create the new share for group
            ContactShare cs = new ContactShare();
            cs.ContactAccessLevel = 'Edit';
            cs.ContactId = affiliation.npe5__Contact__r.Id;        
            system.debug(cs.ContactId);
            if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                    cs.UserOrGroupId = GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
            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 (npe5__Affiliation__c affiliation : affiliations) {

          // if the record was public but is now private -- delete the existing share
          if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == true && affiliation.Make_Public__c == false) {
            shareIdsToDelete.add(affiliation.npe5__Contact__r.Id);

          // if the record was private but now is public -- create the new share for the group
          } else if (Trigger.oldMap.get(affiliation.Id).Make_Public__c == false && affiliation.Make_Public__c == true) {
            
            // create the new share with read/write access
            ContactShare cs = new ContactShare();
            cs.ContactAccessLevel = 'Edit';
            cs.ContactId = affiliation.npe5__Contact__r.Id;
            if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
                    cs.UserOrGroupId =  GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id));   
            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;

      }
  }
  else if (Trigger.isDelete) {
        
        List<ID> shareIdsToDelete = new List<ID>();
      Set<Id> AcctId = new Set<Id>();
        List<npe5__Affiliation__c> affiliations = [select Id, npe5__Organization__c, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.oldMap.keyset()];
        for (npe5__Affiliation__c affiliation : affiliations) {
      shareIdsToDelete.add(affiliation.npe5__Contact__r.Id);
            
          if(affiliation.npe5__Organization__r != null)
            {
              AcctId.add(affiliation.npe5__Organization__r.Id);
            }
        }
          if(AcctId.size() > 0)
            {
              AccountLists  = [select Id,name from Account where Id IN: AcctId];
            }

           for(Account acc :AccountLists  )
            {
              AccountNameMap.put(acc.id,acc.Name);
            }
          
           // Get the Group Details
            List<Group> groups = [SELECT Email,Id,Name FROM Group];
    
            Map<String,Id> GroupMap = new MAp<String,Id>();
            for( Group grp:groups)
            {
                if(grp.Name != null && grp.Name != '')
                {
                    GroupMap.put(grp.Name,grp.Id);
                }
            }
          // Get the Affiliation Id
          List<ID> affiliategroupID = new List<ID>();
          List<npe5__Affiliation__c> possibleaffiliations = [select Id, npe5__Organization__c, npe5__Organization__r.Id, Make_Public__c, npe5__Contact__r.Id from npe5__Affiliation__c where Id IN: Trigger.oldMap.keyset()];
          for (npe5__Affiliation__c affiliation : possibleaffiliations){
              if(GroupMap.containsKey(AccountNameMap.get(affiliation.npe5__Organization__r.Id)))
              {
                   affiliategroupID.add(GroupMap.get(AccountNameMap.get(affiliation.npe5__Organization__r.Id)));
              }
          }

        // do the DML to delete shares
        if (!shareIdsToDelete.isEmpty())
          delete [select id from ContactShare where ContactId IN :shareIdsToDelete and RowCause = 'Manual' and UserOrGroupId IN :affiliategroupID];
    }
}

 
Steven NsubugaSteven Nsubuga
Hi Holly, 
This presupposes that a ContactShare can be uniquely identified by UserorGroupId and ContactId. If so, you are right on the money. 
The code looks fine to me (disclaimer: it is 23:58 hours over here). 
Holly Havelka 10Holly Havelka 10
Hi Steve, thanks for checking and giving that feedback.  The issue I now face is the following:

There are (2) affiliations created for (1) contact:
  • Affiliation 1 - Test Organization A, Make Public = True, Test Contact
    • Contact share record created for Test Organization A (Public Group)
  • Affiliation 2 - Test Organization A, Make Public = True, Test Contact
    • Contact share record already created because of Affiliation 1 record above
When I delete the record for Affiliation 1, I want the trigger to only delete the contact share record IF no other affiliation records exists for the test organization.  Meaning there is no other reason to continue sharing the record with that organization.  So, for the example above, I want the trigger to run but stop when it sees that Affiliation 2 exists.