Build-your-own Teams Feature for Custom Objects in Salesforce

Mehdi Maujood
Salesforce Zolo
Published in
6 min readSep 22, 2021

Teams for Custom Objects, similar to Accounts Teams and Case Teams, is an often-requested but yet-unavailable feature. Manual sharing can meet the needs to some extent, but there are two major limitations:

  • Manual sharing can be managed only by record owners, their higher-ups in the role hierarchy, and users with “modify all” permissions to the object
  • Manual sharing records are deleted when a record owner changes

While there are solutions on AppExchange available, it’s actually pretty simple to spin up your own Teams feature for custom objects. I built this as an example for my Pluralsight course on advanced sharing and visibility features, but ended up not using this example in the course so sharing it here:

Build-your-own Teams Feature

This example is based around a custom object named Class, and you can build something similar for any object you wish to add a teams feature to. We will build a Class Teams feature that allows users to add and remove Class Team Members from classes. Team members are then given access to class records through Apex Managed Sharing.

The solution is available on github (teams for custom objects example) and has the following big pieces:

  • Custom Object Class Team Member that has a lookup or master-detail to Class, a lookup to User, and picklist for choosing Edit or Read as access levels. Users can add team members to classes using this object.
  • An Apex Trigger on the Class Team Member object. Any time team members are added, removed, or updated, the trigger adds or removes sharing for the parent Class record through Apex Managed Sharing.
  • A sharing recalculation batch apex class that can be run to recalculate sharing records for all classes. Why do we need this? Several reasons: the system recalculates sharing and runs recalculation classes when OWD changes, we may need to recalculate sharing data in case our logic had bugs, and in some cases we may need to add sharing to pre-existing data although we do not have any such need in this example implementation as classes won’t have pre-existing team members.

Step 1: Create Apex Sharing Reason

Before we can share Class records through Apex, we need to create an Apex sharing reason on the Class object. Apex sharing reasons are currently available only in Classic.

In Salesforce Classic Setup, navigate the object settings for the Class object, and scroll down to the Apex Sharing Reasons related list. On this list, you can click New to create a new Apex sharing reason. I created the following:

Apex sharing reason on the Class object with API Name “Class_Team” and label “Class Team”

Step 2: Create Class Team Member object

In the Object Manager, create a Class Team Member object (pick Auto Number for Name) and add the following fields:

  • Team Member: Lookup to User.
  • Class: Lookup or Master-detail to parent. Whether you choose Lookup or Master-detail depends on your requirements. In my case, I chose to inherit record-level security from the parent Class record by using a Master-detail relationship.
  • Access Level: Picklist with picklist values Read and Edit.

This is what the fields on my Class Team Member object look like:

Fields defined on the object Class Team Member

You would want to add the Team Members related list to your object’s page layout and configure permissions and sharing for the Team Member object as needed.

Step 3: Automation on Class Team Member object

Next, we create an Apex Trigger on the Class Team Member object to manage sharing records in the Class__share object. If you’re unfamiliar with sharing objects, I recommend reading more about sharing. The trigger has the following logic:

  • If a Class Team Member is inserted, insert a new record into the Class__share object to give the selected user read or edit access to the parent class. This logic has to take care of one important fact: inserting a new record with the same ParentId, UserOrGroupId, and RowCause results in an overwrite of any existing record with the same ParentId, UserOrGroupId, and RowCause. This kind of behavior is unique to sharing objects. If we’re not careful, a team member who already has Edit access to Classes can have that access changed to Read just because someone added the same person twice as a team member with Read access. We should ensure the highest access is given to a team member.
  • If a Class Team Member is deleted, remove access from the sharing object unless the user should retain access through another Class Team Member record. If the user should retain a lower access level, reduce the access level in the sharing object.
  • If a Class Team Member is updated, perform the delete logic followed by insert logic outlined above.

The logic above could be simplified if we prevent duplicate Team Member/Parent Class combinations in the Class Team Member object, but the logic above is more flexible because it allows for both possibilities.

The trigger and classes are Class_Team_Member.trigger and ClassTeamMemberTriggerHandler.cls. You will also find an associated test class in the github project.

Step 4: Apex Sharing Recalculation

Like we discussed earlier, in certain circumstances apex sharing needs to be recalculated.

An example is when an object’s OWD is changed. If OWD is changed to Public Read/Write or Public Read, Salesforce deletes all unnecessary sharing records. If OWD is then switched back to Private, Salesforce would need to recalculate sharing for all records on the object, which can be done using batch apex.

Another example is if we discover that the logic we deployed was incorrect, and we need to delete all sharing records and insert new sharing records.

The batch apex class ClassTeamMemberSharingRecalculation.cls does exactly this: it goes over all Class records in batches, queries team members, deletes any existing sharing records associated with the classes, and inserts new sharing records.

An extra step for sharing recalculation classes is that they need to be selected in the object’s settings so Salesforce can automatically execute them when necessary. In the case of this example, the recalculation class needs to be selected for the Class Team Member object. Once again, this is available only in Salesforce Classic on the object page, right below Apex Sharing Reasons:

A batch apex class set as a Sharing Recalculation class for the Class object

This example does not include an implementation for default teams, but adding default teams into this solution should not be very hard.

If you find this solution difficult to follow, you can clone the github project and follow the instructions in the README file to deploy it to scratch org and run it. I would also recommend learning more about sharing through the links to documentation or through my course.

If you have any thoughts, comments, or questions, feel free to comment!

--

--

Mehdi Maujood
Salesforce Zolo

Software engineer, Salesforce enthusiast, Pluralsight author