Identity collectors - Gather contacts associated with a visitor

This guide covers the process of creating an identity collector for Contacts. Data protection laws such as the European Union’s GDPR often specify that visitors to a site must be able to see their personal data that has been collected, and they can request that it be removed. Identity collection is a key part of this process.

You can read more about GDPR compliance in the Xperience by Kentico Documentation.

Data protection series

This example is part of a series on data protection.

If you want to follow along, you can start here.

The example presented in this Data protection guide series is a valid implementation of data protection for Contacts in Xperience by Kentico. (Note that it does not cover the collection and erasure of Members and their associated data.)

You can copy-paste the code samples into your own solution.

However, if you choose to do so, make sure to consult your legal team to determine whether the implementation, texts, and consent levels meet the requirements of your region and market.

Before you start

This guide requires the following:

The examples in this guide require that you:

Code samples

You can find a project with completed, working versions of code samples from this guide and others in the finished branch of the Training guides repository.

The main branch of the repository provides a starting point to code along with the guides.

The code samples in this guide are for .NET 8 only.

They come from a project that uses implicit using directives. You may need to add additional using directives to your code if your project does not use this feature.

Understand the components

In the Configuration → Data Protection application in Xperience by Kentico, you’ll notice tabs for Data portability, Right to access, and Right to be forgotten.

These correspond to aspects of the GDPR but are applicable to other regulations as well, and each is used for dealing with visitors’ personal data.

The Data portability tab, once implemented, allows administrators to gather a visitor’s data in a machine-readable format. In contrast, the Right to access tab collects data in a human-readable format. Finally, the Right to be forgotten tab allows a visitor’s data to be wiped from the site.

However, the code that carries out these actions needs to be implemented, as there is no one-size-fits-all solution to the virtually limitless ways a site could collect and store personal information. This guide will cover just one example of a site with its own specific requirements.

Identify the first step

Data collection and erasure require an Identity collector as the first step.

Identity collectors are used to gather a collection of Xperience objects representing the visitor, called Identities. For example, real-world people could be represented by a User object, one or more Contact objects, or any custom objects, such as Customer, Author, Contractor, etc.

Once the identity collector gathers these identities, it passes them to other components. These components can use these identities to find objects that reference them, e.g. activities performed by a contact.

This guide will cover the process of creating an identity collector, the first step of this process.

Note: The system uses identifiers, such as an email address, to find Identity objects. It is critical that any data you collect and store always contains one of the following, so that it can be found.

  • a reference to one of the identity objects
  • the identifier itself

Consider an example where you use an email address as the identifier and create a form that collects the first and last name of a visitor but no email address. In such a case, there would be no way to find that information using a supplied email address, and GDPR compliance would be impossible when a visitor exercises their right to be forgotten.

Implement the identity collector

This training guide example site uses only contacts as identities — it does not capture visitor data as users or custom objects.

  1. Create a Collectors folder in ~/Features/DataProtection of the TrainingGuides.Web project.

  2. Add a ContactIdentityCollector.cs file to the folder, and add a namespace matching the folder structure (TrainingGuides.Web.Features.DataProtection.Collectors).

  3. Copy the example identity collector from the documentation, and add it to the same namespace.

  4. Add a constant to replace the hard-coded “email”  string, as it occurs more than once.

  5. Define additional logic that creates a new contact with the supplied email address if the query finds none.

    It is possible for form data containing an email address to exist even if there is no contact in the database with that email. Creating this dummy contact allows the Data collector, which will be covered in the next part of this series, to find form data associated with the provided email address in such a case.

C#
ContactIdentityCollector.cs


using CMS.ContactManagement;
using CMS.DataEngine;
using CMS.DataProtection;

namespace TrainingGuides.Web.Features.DataProtection.Collectors;

public class ContactIdentityCollector : IIdentityCollector
{
    private const string EMAIL_KEY = "email";

    private readonly IInfoProvider<ContactInfo> contactInfoProvider;

    public ContactIdentityCollector(IInfoProvider<ContactInfo> contactInfoProvider)
    {
        this.contactInfoProvider = contactInfoProvider;
    }

    public void Collect(IDictionary<string, object> dataSubjectFilter, List<BaseInfo> identities)
    {
        string? email = dataSubjectFilter.ContainsKey(EMAIL_KEY)
            ? dataSubjectFilter[EMAIL_KEY] as string
            : string.Empty;

        if (string.IsNullOrWhiteSpace(email))
        {
            return;
        }

        var contacts = contactInfoProvider
            .Get()
            .WhereEquals(nameof(ContactInfo.ContactEmail), email)
            .ToList();

        if (contacts.Count() == 0)
        {
            contacts.Add(new ContactInfo() { ContactEmail = email });
        }

        identities.AddRange(contacts);
    }
}

Register the Identity collector

Now that the Identity collector is in place, it can be registered.

  1. Create a new DataProtectionRegistrationModule class in ~/Features/DataProtection/Shared folder of the TrainingGuides.Web project.
  2. Inherit from Xperience’s Module class, and override OnInit to add ContactIdentityCollector to the IdentityCollectorRegister.
  3. Resolve an info provider for contacts, and pass it to the collector to fill the parameter we set earllier
  4. Register the module using assembly attribute.
C#
DataProtectionRegistrationModule.cs


using CMS;
using CMS.ContactManagement;
using CMS.Core;
using CMS.DataEngine;
using CMS.DataProtection;
using TrainingGuides.Web.Features.DataProtection.Collectors;
using TrainingGuides.Web.Features.DataProtection.Shared;

[assembly: RegisterModule(
    type: typeof(DataProtectionRegistrationModule))]

namespace TrainingGuides.Web.Features.DataProtection.Shared;

public class DataProtectionRegistrationModule : Module
{
    public DataProtectionRegistrationModule()
        : base("DataProtectionRegistration")
    {
    }

    // Contains initialization code that is executed when the application starts
    protected override void OnInit(ModuleInitParameters parameters)
    {
        var contactInfoProvider = parameters.Services.GetRequiredService<IInfoProvider<ContactInfo>>();

        base.OnInit(parameters);

        // Adds the ContactIdentityCollector to the collection of registered identity collectors
        IdentityCollectorRegister.Instance.Add(new ContactIdentityCollector(contactInfoProvider));
    }
}


Now, Xperience will know how to utilize the ContactIdentityCollector class for data portability, collection, and erasure requests. When you implement a Data collector, covered in the next guide, Xperience will use the identity collector class to gather a list of contacts and pass them to the data collector to retrieve personal data associated with them.

The IdentityCollectorRegister, like the other data protection registers, is a queue that can contain multiple collectors. If the same class is registered to this queue more than once, it will be called multiple times.

What’s next?

The following guide in this series will cover the process of creating a Data collector and some utility classes to help it run smoothly.