RestrictCallHandlers

What It Does

Controls which call handlers — and therefore which calls — the running user is allowed to see.

The handler restriction is evaluated at the start of every timeline request and applied as a SOQL filter. It restricts visibility by the Salesforce User linked to each PhoneCallHandler__c record (PhoneCallHandler__c.User__c).

The default implementation allows users with the CallCore_ViewAllCalls permission to see all handlers. Users without that permission can only see calls where they are the matched handler.

Implement RestrictCallHandlers.IStrategy to introduce team-based, role-based, or any other custom restriction logic.

Interface

global interface IStrategy {
    void getAllowed(Context context);
}

Context

Input

The Context has no input fields. Use UserInfo.getUserId(), UserInfo.getProfileId(), or SOQL queries to determine what the running user is allowed to see.

Write Methods

Call exactly one of these before returning. If you call none, the default is allowAll().

Method Description
allowAll() No restriction — all handlers are visible.
allowSingle(Id id) Allow exactly one handler user ID. Throws if id is null.
allowSet(Set<Id> ids) Allow a specific set of handler user IDs. Throws if ids is null. An empty set means no handlers are allowed.
excludeAll() Allow no handlers — no calls will be visible to the user.

The Id values passed to allowSingle and allowSet are User IDs (the linked Salesforce user), not PhoneCallHandler__c record IDs.

Discriminator

This strategy has no SObject discriminator. There is one global implementation per org. Use default as the discriminator value.

Default Behaviour

  • If the user has CallCore_ViewAllCalls (or any permission that implies it): allowAll().
  • Otherwise: allowSingle(UserInfo.getUserId()) — the user sees only their own calls.

Example: Restrict by Salesforce Role

Allow users to see calls handled by anyone in their role or below in the role hierarchy.

public class RestrictHandlersToRoleHierarchy
    implements callcoreio.RestrictCallHandlers.IStrategy {

    public void getAllowed(callcoreio.RestrictCallHandlers.Context context) {
        if (hasViewAllPermission()) {
            context.allowAll();
            return;
        }

        Id roleId = UserInfo.getUserRoleId();
        if (roleId == null) {
            context.allowSingle(UserInfo.getUserId());
            return;
        }

        Set<Id> subordinateRoleIds = getSubordinateRoleIds(roleId);
        subordinateRoleIds.add(roleId);

        List<User> users = [
            SELECT Id FROM User
            WHERE IsActive = true AND UserRoleId IN :subordinateRoleIds
        ];

        Set<Id> allowedUserIds = new Set<Id>();
        for (User u : users) allowedUserIds.add(u.Id);

        context.allowSet(allowedUserIds);
    }

    private Boolean hasViewAllPermission() {
        return FeatureManagement.checkPermission('CallCore_ViewAllCalls');
    }

    private Set<Id> getSubordinateRoleIds(Id rootRoleId) {
        Set<Id> result = new Set<Id>();
        List<UserRole> roles = [SELECT Id FROM UserRole WHERE ParentRoleId = :rootRoleId];
        for (UserRole r : roles) {
            result.add(r.Id);
            result.addAll(getSubordinateRoleIds(r.Id));
        }
        return result;
    }
}

Example: Restrict by Custom Group Membership

Allow users to see calls handled by members of a custom CallTeam__c object that links to User.

public class RestrictHandlersToCallTeam
    implements callcoreio.RestrictCallHandlers.IStrategy {

    public void getAllowed(callcoreio.RestrictCallHandlers.Context context) {
        if (FeatureManagement.checkPermission('CallCore_ViewAllCalls')) {
            context.allowAll();
            return;
        }

        List<CallTeamMember__c> teammates = [
            SELECT TeamUser__c
            FROM CallTeamMember__c
            WHERE Team__r.Members__r.User__c = :UserInfo.getUserId()
        ];

        if (teammates.isEmpty()) {
            context.allowSingle(UserInfo.getUserId());
            return;
        }

        Set<Id> teamUserIds = new Set<Id>();
        for (CallTeamMember__c m : teammates) teamUserIds.add(m.TeamUser__c);
        teamUserIds.add(UserInfo.getUserId());

        context.allowSet(teamUserIds);
    }
}

Notes

  • Fail safely: if your query fails or returns unexpected results, consider whether allowSingle (own calls only) or excludeAll is the right fallback. The framework defaults to excludeAll on unhandled exceptions to prevent accidental over-exposure.
  • The restriction is evaluated once per timeline request, not once per call record. Keep the implementation efficient — avoid nested loops and limit SOQL queries.
  • The allowSet and allowSingle values are User IDs. There is no way to allow a PhoneCallHandler__c record that has no linked User — unlinking a handler from a User removes them from all restricted views.
  • If both RestrictCallHandlers and RestrictSources are configured, both filters are applied. A call must pass both restrictions to appear in the timeline.