Consent development for profiles
Preview feature
The customer data platform feature is currently in preview mode. Expect the feature to be modified and expanded significantly in upcoming releases, including breaking changes.
The initial releases of the feature are primarily intended to gather early feedback. The scope of the feature is limited and much of the Xperience by Kentico digital marketing functionality is not supported yet.
All related API is marked as experimental and usage will result in warnings when compiling your project. The warnings are treated as errors for reporting purposes. To use the code, you need to suppress the warnings.
What should you do with this feature?
- DO try out this feature, for example, using the sample Dancing Goat project template or other demo sites.
- DO feel free to share your feedback with the Kentico Product team.
- DON’T use this feature in a production environment or invest heavily into development based on the current state.
This page describes how to set up consent functionality on Xperience websites when the customer data platform feature is enabled.
In Xperience, consents are records used for informing visitors about the means of collecting and manipulating their personal data by the system, administrators, marketers and anyone else who has access to said data, including third parties to whom this data is forwarded.
You can use consents to comply with the requirements of GDPR and other personal data regulations.
Whenever any personal data of a visitor is obtained, it is necessary to have a consent agreement from the visitor to legally process this data. This includes tracking of profiles and their activities. Every consent agreement of a visitor is directly connected to a corresponding contact (encapsulated by a profile). Deleting profiles from the system therefore also causes deletion of the related consent agreements.
To learn how to create consents in the Xperience administration, see Consent management.
Set up tracking consent
When creating a consent for the tracking of profiles and activities, you need to provide a way for visitors to adjust their allowed cookie level. The system only performs the tracking for visitors whose cookie level is Visitor or higher.
To ensure that visitors are not tracked until they give the tracking consent, set the default cookie level in the Xperience administration:
- Open Channel management application.
- Select your website channel.
- Open the Channel settings tab.
- In the Cookies section, choose a Default cookie level.
- For an overview of individual cookie levels, see Cookie levels.
- Select Save.
To allow visitors to give agreements with the tracking consent on your site, you need to develop corresponding components. For example, you can implement tracking consent functionality as a view component.
Create the consent
Start by defining the tracking consent in the Xperience administration interface (in the Data protection application on the Consents tab).
Create the model
We recommend creating a view model class in your project to represent data related to the tracking consent. Include the following properties:
- A
stringproperty for storing the consent’s short text - A
boolproperty to indicate whether an agreement with the consent was given - A
stringproperty for passing the current page URL as the return URL
public class TrackingConsentViewModel
{
public string ShortText { get; set; }
public bool IsAgreed { get; set; }
public string ReturnUrl { get; set; }
}
Create the view component
Create a new view component class in your project:
Initialize instances of services, which are necessary to manage the tracking consent:
IInfoProvider<ConsentInfo>– to load consent data.IProfileConsentAgreementService– to manage consent agreements.ICurrentCookieLevelProvider– to manage the current visitor’s cookie level.IWebPageDataContextRetrieverandIWebPageUrlRetriever– to obtain the URL of the current page.IPreferredLanguageRetriever– to get the currently selected content language.
Use dependency injection to initialize service instances.C#Servicespublic class TrackingConsentViewComponent : ViewComponent { private readonly IProfileConsentAgreementService profileConsentAgreementService; private readonly IInfoProvider<ConsentInfo> consentInfoProvider; private readonly IWebPageDataContextRetriever webPageDataContextRetriever; private readonly IWebPageUrlRetriever urlRetriever; private readonly IPreferredLanguageRetriever preferredLanguageRetriever; public TrackingConsentViewComponent(IProfileConsentAgreementService profileConsentAgreementService, IInfoProvider<ConsentInfo> consentInfoProvider, IWebPageDataContextRetriever webPageDataContextRetriever, IWebPageUrlRetriever urlRetriever, IPreferredLanguageRetriever preferredLanguageRetriever) { this.profileConsentAgreementService = profileConsentAgreementService; this.consentInfoProvider = consentInfoProvider; this.webPageDataContextRetriever = webPageDataContextRetriever; this.urlRetriever = urlRetriever; this.preferredLanguageRetriever = preferredLanguageRetriever; } }Implement the component’s
InvokeAsyncmethod to display information about the tracking consent:C#Invoke the componentpublic async Task<IViewComponentResult> InvokeAsync() { // Gets the related tracking consent // Fill in the code name of the appropriate consent in Xperience ConsentInfo consent = await consentInfoProvider.GetAsync("SampleTrackingConsent"); if (consent != null) { string consentName = consent.ConsentName; // Gets the code name of the currently selected language var preferredLanguage = preferredLanguageRetriever.Get(); var consentModel = new TrackingConsentViewModel { // Adds the consent's short text to the model ShortText = (await consent.GetConsentTextAsync(preferredLanguage)).ShortText, // Checks whether the current profile has given an agreement for the tracking consent IsAgreed = await profileConsentAgreementService.IsAgreed(consentName, cancellationToken: HttpContext.RequestAborted), // Adds the current page URL to the model ReturnUrl = webPageDataContextRetriever.TryRetrieve(out var currentWebPageContext) ? (await urlRetriever.Retrieve(currentWebPageContext.WebPage.WebPageItemID, preferredLanguage, cancellationToken: HttpContext.RequestAborted)).RelativePath // Gets the URL from the request context for pages using custom routing : (HttpContext.Request.PathBase + HttpContext.Request.Path).Value }; // Displays a view with tracking consent information and actions return View("~/Components/ViewComponents/TrackingConsent/_TrackingConsent.cshtml", consentModel); } return Content(string.Empty); }
Create the view
Add the view referenced in your view component’s invoke method. Display the text of the tracking consent and inputs for giving or revoking agreements. Depending on your preferences and legal requirements, you can hide the view’s output for visitors who have given the tracking consent or display alternative content (for example the option to revoke the consent).
@using Kentico.Content.Web.Mvc;
@model TrackingConsentViewModel
@* Displays content only if the current profile has not yet given a tracking consent agreement *@
@if (!Model.IsAgreed)
{
var routeData = new Dictionary<string, string> { { "returnUrl", Model.ReturnUrl } };
<div>
@* Displays the consent text*@
@Html.Raw(@Model.ShortText)
</div>
@* Displays an input for giving a consent agreement *@
@* Replace "TrackingConsent" with the name of your controller that handles consent agreement requests *@
<form asp-action="Agree"
asp-controller="TrackingConsent"
asp-antiforgery="true"
asp-all-route-data="routeData"
method="post">
<input type="submit" value="I agree">
</form>
}
Create the controller
Add a controller that handles requests for adding consent agreements:
Initialize instances of services required to manage the tracking consent (some of the same services described for the View component code).
Define a POST action for creating consent agreements.
Modify the visitor’s cookie level according to the performed action – call the
SetCurrentCookieLevelmethod of theICurrentCookieLevelProviderservice.- Set the All cookie level when creating tracking consent agreements.
Create consent agreements using the
TryAgreemethod of theIProfileConsentAgreementServiceinstance.C#Tracking consent controller with an Agree POST actionpublic class TrackingConsentController( ICurrentCookieLevelProvider currentCookieLevelProvider, IProfileConsentAgreementService profileConsentAgreementService, IInfoProvider<ConsentInfo> consentInfoProvider) : Controller { [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Agree(string returnUrl, CancellationToken cancellationToken) { // Gets the related tracking consent // Fill in the code name of the appropriate consent in Xperience ConsentInfo consent = await consentInfoProvider.GetAsync("SampleTrackingConsent"); if (consent != null) { string consentName = consent.ConsentName; // Sets the visitor's cookie level to 'All' (enables profile tracking) currentCookieLevelProvider.SetCurrentCookieLevel(Kentico.Web.Mvc.CookieLevel.All.Level); // Adds a tracking consent agreement for the current profile if (await profileConsentAgreementService.TryAgree(consentName, cancellationToken)) { return Redirect(returnUrl); } } return new StatusCodeResult(StatusCodes.Status400BadRequest); } }
Invoke the component
Invoke the view component from a suitable location in your website’s content. For example, use the main layout to ensure that the input to give the tracking consent is displayed on all pages.
...
<html>
<head >
...
</head>
<body>
<div>
@* Invokes 'TrackingConsentViewComponent' *@
<vc:tracking-consent />
...
Visitors on your website now have an option to give agreements with your tracking consent. New visitors are not tracked as profiles until they positively confirm that they agree with the tracking.
Create conditions based on consents
If you have any functionality that stores or processes personal data, you can create conditions to ensure that it only runs for profiles who have given consent.
- Initialize an instance of the
IProfileConsentAgreementServiceinterface and make it available in your code. - Obtain the code name of the related tracking consent.
- Evaluate whether the profile has given an agreement with the given consent by calling the
IsAgreedmethod of theIProfileConsentAgreementServiceinstance.
Revoke consents
To ensure compliance of your website with the GDPR or other data protection regulations, you need to give visitors the option to view and revoke their previously given consent agreements. We recommend creating a privacy page, where visitors can view the consents for which they have given agreements and potentially revoke them.
“It shall be as easy to withdraw as to give consent.”
(Source: GDPR Article 7, Paragraph 3)
The following sections describe how to create a privacy page using MVC architecture. The privacy page lists accepted consents for visitors, and allows them to view the full consent details or revoke their agreements.
- Create a model representing the list of consents for which visitors have given agreements.
- Create a model representing consent details.
- Create a controller that provides actions for displaying and revoking consents.
- Implement a handler for the
DataProtectionEvents.RevokeConsentAgreementglobal event to clear related cookies and personal data. - Create views for the consent list and details pages.
Create the models
Add a model class that represents the list of consents for which visitors have given agreements.
C#List of consentsusing System.Collections.Generic; using System.Linq; using CMS.DataProtection; public class ConsentListingModel { public IEnumerable<Consent> Consents { get; set; } = Enumerable.Empty<Consent>(); }ConsentandConsentInfoobjectsThe Xperience API uses
ConsentInfoobjects to work with consents in general. However, when you load consents for which a profile has given an agreement, theConsentAgreementService.GetAgreedConsentsmethod returns a collection ofConsentobjects. Instances of theConsentclass provide theGetConsentTextmethod, which automatically retrieves either the current texts of the given consent or the texts of the archived consent version for which the agreement was given.When displaying accepted consents to visitors, always work with
For more information, see the View archived consents section of the Consent management page.Consentobjects to ensure that the correct consent text version is used (for scenarios where the consent text was changed after agreements had been given).Tip: You can directly use anIEnumerable<Consent>collection as a model if you do not need to work with any other data in your controller or views. However, having a dedicated model class allows you to extend the model based on future requirements.Add a model class that represents the full details of a consent, including properties for the short text, full text, and any other information about the consent you may need.
C#Consent properties modelpublic class ConsentDetailsModel { public string ConsentShortText { get; set; } public string ConsentFullText { get; set; } public string ConsentDisplayName { get; set; } }
Create the controller
Create a new controller class or edit an existing one:
Initialize instances of the
IProfileConsentAgreementServiceandIInfoProvider<ConsentInfo>services (and any other required services), and make them available in the controller’s code:C#Controller class and required servicespublic class PrivacyPageController( IProfileConsentAgreementService profileConsentAgreementService, IInfoProvider<ConsentInfo> consentInfoProvider, IPreferredLanguageRetriever preferredLanguageRetriever) : Controller { // ... }Implement a GET action which retrieves a list of all consents accepted by the visitor:
- Get the consents for which the profile has given an agreement by calling the
GetAgreedConsentsmethod of theIProfileConsentAgreementServiceinstance. - Create an instance of the consent list model class and pass it to the view.
C#GET list of consents/// <summary> /// Loads and displays consents for which visitors have given agreements. /// </summary> public async Task<ActionResult> Index() { var consentListingModel = new ConsentListingModel(); // Gets all consents for which the current profile has given an agreement consentListingModel.Consents = await profileConsentAgreementService.GetAgreedConsents(HttpContext.RequestAborted); return View(consentListingModel); }- Get the consents for which the profile has given an agreement by calling the
Implement a GET action which retrieves the full details of a specified consent:
- Get the consents for which the current profile has given an agreement using the consent agreement service, and select the required consent based on the action’s parameter. This approach ensures that the correct consent text version is used in cases where the text was changed after the agreement had been given.
- Create an instance of the consent details model class, set its properties, and pass it to the view.
C#GET consent details/// <summary> /// Display details of the specified consent. /// </summary> public async Task<ActionResult> ConsentDetails(int consentId) { // Gets a list of consents for which the current profile has given an agreement IEnumerable<Consent> consents = await profileConsentAgreementService.GetAgreedConsents(HttpContext.RequestAborted); // Gets the consent matching the identifier for which the details were requested // Using this approach to get objects of the 'Consent' class is necessary to ensure that the correct consent text // is displayed, either from the current consent text or the archived consent version for which the agreement was given Consent consent = consents.FirstOrDefault(c => c.Id == consentId); // Displays the privacy page (consent list) if the specified consent identifier is not valid if (consent == null) { return View("Index"); } // Gets the consent text in the currently selected language ConsentText consentText = consent.GetConsentText(preferredLanguageRetriever.Get()); // Sets the consent's details into the view model var model = new ConsentDetailsModel { ConsentDisplayName = consent.DisplayName, ConsentShortText = consentText.ShortText, ConsentFullText = consentText.FullText }; return View(model); }Implement a POST action that revokes agreements for a specified consent:
- Retrieve the required consent (by calling the
IInfoProvider<ConsentInfo>().Getmethod). - Create a revocation for the consent agreement using the
TryRevokemethod of theIProfileConsentAgreementServiceinstance.
C#POST action/// <summary> /// Revokes the current profile's agreement with the specified consent. /// </summary> [HttpPost] [ValidateAntiForgeryToken] public async Task<ActionResult> Revoke(int consentId, CancellationToken cancellationToken) { // Gets the related consent object ConsentInfo consent = await consentInfoProvider.GetAsync(consentId); if (consent != null) { var consentName = consent.ConsentName; // Revokes the consent agreement await profileConsentAgreementService.TryRevoke(consentName, cancellationToken); } return RedirectToAction("Index"); }- Retrieve the required consent (by calling the
Add a RevokeConsentAgreement handler
Websites and their related functionality often contain multiple locations where visitors can revoke consents. To ensure that revocations are handled correctly in all cases, we recommend assigning a custom handler to the system’s DataProtectionEvents.RevokeConsentAgreement global event. The handler provides a central place where you can perform additional actions whenever a consent agreement is revoked.
The following are typical scenarios to handle in the RevokeConsentAgreementevent:
- Setting the cookie level – when revoking the tracking consent, you also need to lower the visitor’s cookie level to ensure that profile tracking is disabled. Call the
SetCurrentCookieLevelmethod of theICurrentCookieLevelProviderservice and set the default cookie level from the website channel’s settings. - Clearing personal data – when a visitor revokes a consent agreement, you may also need to delete or anonymize certain types of personal data stored by the system. See also: Personal data erasure
using System;
using CMS;
using CMS.Core;
using CMS.DataEngine;
using CMS.DataProtection;
using CMS.Helpers;
// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomDataProtectionModule))]
internal class CustomDataProtectionModule : Module
{
private ICurrentCookieLevelProvider currentCookieLevelProvider;
// Module class constructor, the system registers the module under the name "CustomDataProtection"
public CustomDataProtectionModule()
: base("CustomDataProtection")
{
}
// Contains initialization code that is executed when the application starts
protected override void OnInit(ModuleInitParameters parameters)
{
// Resolves service dependencies from the IoC container
currentCookieLevelProvider = parameters.Services.GetRequiredService<ICurrentCookieLevelProvider>();
// Assigns a handler to the RevokeConsentAgreement event
DataProtectionEvents.RevokeConsentAgreement.Execute += RevokeConsentHandler;
}
private void RevokeConsentHandler(object? sender, RevokeConsentAgreementEventArgs e)
{
// For the tracking consent, lowers the cookie level to the website channel's default in order to disable tracking
// Fill in the code name of the appropriate tracking consent in Xperience
if (e.Consent.ConsentName.Equals("SampleTrackingConsent", StringComparison.OrdinalIgnoreCase))
{
currentCookieLevelProvider.SetCurrentCookieLevel(currentCookieLevelProvider.GetDefaultCookieLevel());
}
}
}
Create the views
Add appropriate views to your project to display the consent list (privacy page), the consent details and revocation actions.
Html.Raw method.
<h3>Short text</h3>
<p>@Html.Raw(Model.ConsentShortText)</p>
<h3>Full text</h3>
<p>@Html.Raw(Model.ConsentFullText)</p>
Visitors on your site are now able to view a list of all consents for which they have given an agreement. They can also access the full details of the given consents and revoke their agreements if required.