Extensibility

The Five Extensibility Points

CallCore TX ships with sensible defaults for every decision the package makes at runtime. Extensibility strategies let your org replace those defaults with logic that fits your specific data model, team structure, or business rules — without forking the package or waiting for a new release.

Strategy What it controls
BuildCommunicationScopeForRecord Which CRM records are "in scope" when the timeline is viewed from a given record page
GatherPhoneNumbersFromRecords How phone numbers are extracted from scope records
GetUserForCallHandler How a call handler's details are matched to a Salesforce User
RestrictCallHandlers Which call handlers the running user is allowed to see
RestrictSources Which call sources (phone lines) the running user is allowed to see

All five follow the same shape. You write an Apex class, implement the relevant interface, and register it via a custom metadata record. No code changes to the package are required.

How Strategy Resolution Works

When CallCore TX needs to invoke a strategy, it:

  1. Queries the callcoreio__ExtensibilityStrategy__mdt custom metadata type for a record matching the strategy type and discriminator.
  2. If a match is found, resolves the Apex class name from the callcoreio__ApexClassName__c field. Subscriber org classes are checked first; packaged classes are checked second.
  3. If no match is found in metadata, falls back to the packaged default implementation.

The discriminator narrows which metadata record is selected. For strategies that operate on a specific SObject type (BuildCommunicationScopeForRecord, GatherPhoneNumbersFromRecords), the discriminator is the SObject API name prefixed with RecordType: (e.g., RecordType:Contact). For strategies with a single global implementation (GetUserForCallHandler, RestrictCallHandlers, RestrictSources), use default as the discriminator.

A default record matches any discriminator that has no more-specific override. If you configure both RecordType:Contact and default, the Contact-specific record wins for Contact pages; all other record types use the default.

Resolution precedence (most specific wins):

RecordType-specific metadata record
        ↓ (fallback)
default metadata record
        ↓ (fallback)
packaged default implementation

The Context Pattern

Every strategy receives a Context object. The Context carries two things:

  • Input fields — read-only data the strategy uses to make its decision (e.g., the anchor record reference, the call handler's email address).
  • Write methods — the API the strategy calls to record its output (e.g., addToScope(id), found(userId), allowSet(ids)).

The strategy's job is to read the inputs, compute its output, and record that output via the write methods. It does not return a value. The framework reads the Context after the strategy returns.

This shape allows the contract to evolve — new input data can be added to the Context without changing the interface method signature, which would be a breaking change in a managed package.

Error Handling and Debug Mode

If a strategy throws an unhandled exception, CallCore TX logs the exception and falls back gracefully:

  • BuildCommunicationScopeForRecord — proceeds with an empty scope (no calls shown).
  • GatherPhoneNumbersFromRecords — proceeds with no phone numbers (no calls matched).
  • GetUserForCallHandler — treats the result as "no match" (handler is not linked to a user).
  • RestrictCallHandlers — fails closed: excludes all handlers (no calls shown).
  • RestrictSources — fails closed: excludes all sources (no calls shown).

When the CallCore_DebugMode custom permission is assigned to the running user, exceptions are rethrown instead of swallowed. Use the CallCore TX - Debug permission set during development.

Where to Start

  • To customise which CRM records appear in the timeline: see BuildCommunicationScopeForRecord.
  • To customise phone number extraction from non-standard objects: see GatherPhoneNumbersFromRecords.
  • To match call handlers to users by something other than email: see GetUserForCallHandler.
  • To restrict call visibility by team or department: see RestrictCallHandlers or RestrictSources.
  • For custom metadata registration: see Registering Strategies.
  • For testing your implementation: see Testing Strategies.