Featured image of post Timeline Custom Connectors 1: Basics

Timeline Custom Connectors 1: Basics

Did you know you can bring your own data to the Dynamics timeline component? In general, the timeline is quite powerful with its configuration options in a no-code scenario already. But it’s also limited to the standard entities of the timeline: Notes, Posts and Activities. With these, we can work around, e.g. inserting posts for the creation of another entity or just defining an entity as an activity in the first place.

But sometimes this is not feasible, e.g. due to the security setup with activities or you are working with external data that you prefer to not persist. A good example is news articles. They just have less footprint in the system if they are queried the moment the seller opens the Account and compared to a nightly creation of post records, the news will always be up to date.

The Feature

But getting there will be quite a journey. First of all, I’m talking about a feature Microsoft calls Custom Connectors with the timeline control. This is a high-code extension that you will find in the Advanced section of the Timeline control settings. You enter the unique name of a webresource and the name of the primary class.

The section for custom connectors is found in Advanced The unique name of the webresource and the (full) name of the entrypoint is needed

This class you are specifying needs to implement a certain interface called IRecordSource which is found in Interfaces.d.ts in the sample linked at the bottom of the article. This requires you to implement 4 (+ 2 optional) methods.

init

This is the first bummer. The sample (at the time of writing) does not include a definition for the given context since the referenced mscrm.d.ts is missing. We will try to fix that later, but you could type it into any here and just save it to a class property/field.

async init(context: any, config?: JSON | undefined): Promise<void> {
    this.context = context;
    this.config = config;
};

The context contains a lot of functions, way too many to list here. But check the template repository linked at the end, the Interfaces.ts contains a partially defined IControlData interface, showing a lot of the capabilities.

Apart from that you have static namespaces like Xrm.WebApi or Xrm.Utility available.

getRecordSourceInfo

This is a very simple function, it just returns an object with a name property, done. No special requirements for the name, but it should correspond to the module aka the connector you are implementing. I’ve used the class name as the default.

getRecordSourceInfo(): IRecordSourceInfo {
    return {
        name: MyRecordSource.name
    };
};

getRecordsData

This function is called onLoad, on refresh and whenever the filters or search is used. It shall return the data based on the filters (if present). Here is the absolute minimal extract.

async getRecordsData(request: IRecordsDataRequest, filter?: IFilterRequest | undefined): Promise<IRecordsDataResponse> {
    this.records = this.records ?? [{
        id: "1",
        data: JSON.stringify({ name: "Record 1" }),
        sortDateValue: new Date()
    }]; 

    const response = {
        requestId: request.requestId,
        records: this.records
    }

    return response;
}

We will cover an actual sample in the next article about displaying data.

getRecordUX

This function is called for every record you return from getRecordsData. Here you transform the data into something displayable in the timeline. Important parts are Header, Body and Footer, composing the information in the card, an icon and the sortDateValue for ordering.

No code sample here, we cover this in the next article about displaying data. But the template contains the necessary parts for this in this class.

Also, you can add commands to each record, which is probably another topic in itself, so expect this series to grow quite a bit.

getFilterDetails

This function gets passed in the filters and shall return the (possibly manipulated) filters. Manipulations might for example be altering an existing filter by adding an option or creating a new filter that fits your extension.

Implementing this is quite complex since you need to consider the existing filters, need to prevent illegal combinations and calculate the count of records matching a filter in combination with all already selected filters. We will have to cover this in a dedicated article, but at least this is one of the optional functions, so we can just ignore it for now.

getRecordCreate

This function controls what is being shown at the + for creating new records. The objects you return contain an onClick function that is executed as soon as the added option is clicked. You only need to implement the buttons you want to add, options available by the regular configuration options of the timeline are shown regardless. So if you do not intend to add a button, you may skip the implementation of this function.

There is no code sample here, we will have another dedicated article for it.

The Template

An obvious problem with this feature (at the time of writing) is the lack of samples. The provided samples are compiled already, the definitions are not complete and the docs article lacks explanations on the required functions. I try to fix this on this blog by providing more context around the functions but we will also have samples.

These samples will be built on a template, which is found in this repository. The plan is that the main branch holds the template for easy cloning/forking/copying and each sample receives a separate branch.

Summary

Timeline Custom Connectors are a high-code extension configurable for the Dynamics timeline, shown on forms. It allows for a custom experience of records being shown in the timeline, with no constraint on what is shown, even custom commands are implementable. However, a considerable amount of code is required to leverage these capabilities!

The template in this repository tries to improve your time to “getting started” and you should check the series for more samples. If certain functionality is still missing on this blog you may be able to reverse engineer them from the MS samples.

This site is owned by Marius Wodtke. All rights reserved. Legal notice.
Built with Hugo. Theme Stack designed by Jimmy modified by Marius for GDPR conformity.