You can implement consent tracking on a storefront based on SFRA. Merchants can track personal information about their shoppers to improve the shopping experience. The merchant can collect and honor shoppers’ consent preferences when they are using the site.
The following example shows one way to implement consent tracking on a storefront adapted from SFRA and is provided for informational purposes only. As always, merchants can consult their own legal department or advisors to review and consent management process.
The implementation uses:
To display a consent message to the shopper, a content
asset whose internal ID is tracking_hint
is used. You can
replace the placeholder text in the content asset with your own
message.
The Tracking site preference determines the default tracking behavior. When set to Opt-in, personal information is not tracked by default for all shoppers visiting the site; when not set to Opt-in, personal information is tracked.
To set this preference, select Merchant Tools > site > Site Preferences > Privacy.
The example assumes the Tracking preference is set to Opt-In.
The example presents a consent request message to the shopper, who can choose to allow tracking. If the shopper allows tracking, it is enabled during the shopper’s session. For more information, see Browser-Based Local Data Storage.
To enable tracking on a session:
dw.system.Session.setTrackingAllowed(boolean)
―
If the boolean value is true
, tracking is enabled for
the current session; if false
, tracking is
disabled.To check the current value of the session's tracking flag:
dw.system.Session.isTrackingAllowed()
― true
indicates
that tracking is enabled; false
, disabled.The sample implementation adds a
<span>
tag to every page in the storefront. The
<span>
tag stores information about the shopper’s consent
choices and makes the information available to client-side code running in the
browser. If the shopper has not yet given consent, the client-side code inspects the
<span>
tag and displays a Tracking Consent window.
The <span>
tag is created by the template
app_storefront_base/cartridge/templates/default/common/consent.isml.
<span class="api-${pdict.consentApi} ${pdict.tracking_consent == null ? '' : 'consented' } tracking-consent"
data-url="${URLUtils.url('ConsentTracking-GetContent', 'cid', 'tracking_hint')}"
data-reject="${URLUtils.url('ConsentTracking-SetSession', 'consent', 'false')}"
data-accept="${URLUtils.url('ConsentTracking-SetSession', 'consent', 'true')}"
data-acceptText="${Resource.msg('button.consentTracking.yes', 'common', null)}"
data-rejectText="${Resource.msg('button.consentTracking.no', 'common', null)}"
data-heading="${Resource.msg('heading.consentTracking.track.consent', 'common', null)}"
></span>
This
template is rendered by the ConsentTracking-Check
route in the
app_storefront_base/cartridge/controllers/ConsentTracking.js
controller.
server.get('Check', consentTracking.consent, function (req, res, next) {
res.render('/common/consent', {
consentApi: Object.prototype.hasOwnProperty.call(req.session.raw, 'setTrackingAllowed')
});
next();
});
This
route is called every time a storefront page is rendered. In the route, the
middleware step consentTracking.consent
is invoked before the
consent.isml template is rendered.
This middleware step is implemented in the server-side script app_storefront_base/cartridge/scripts/middleware/consentTracking.js.
'use strict';
/**
* Middleware to use consent tracking check
* @param {Object} req - Request object
* @param {Object} res - Response object
* @param {Function} next - Next call in the middleware chain
* @returns {void}
*/
function consent(req, res, next) {
var consented = req.session.privacyCache.get('consent');
if (consented === null || consented === undefined) {
req.session.privacyCache.set('consent', null);
} else if (consented === false) {
req.session.privacyCache.set('consent', false);
req.session.raw.setTrackingAllowed(false);
} else if (consented === true) {
req.session.privacyCache.set('consent', true);
req.session.raw.setTrackingAllowed(true);
}
res.setViewData({
tracking_consent: req.session.privacyCache.get('consent')
});
next();
}
module.exports = {
consent: consent
};
It
checks the value of the session's consent
property. Regardless if
the value of the property is either true
or false
,
it calls the setTrackingAllowed()
method on the session to record
the shopper’s choice.
It then adds the value of the consent
property to the response's view data, storing it in the
tracking_consent
property. The
tracking_consent
property is made available to the
consent.isml
template via the pdict
property.
The first time this code is executed in a session, the value of the
session's consent
property is null
, so the
tracking_consent
property is also null. Because the value of
the tracking_consent
property is null
, the first
line in the consent.isml
template:
<span class="api-${pdict.consentApi} ${pdict.tracking_consent == null ? '' : 'consented' } tracking-consent"
Outputs the following HTML:
<span class="api-true tracking-consent" ...
In
this output, the consented
attribute value is absent, and its
absence indicates to the client-side code that the shopper has not yet consented to
tracking. The absence of this attribute causes the client-side code to display the
Tracking Consent window, prompting the shopper to grant or deny consent.
consentTracking.consent
middleware step set the
tracking_consent
property to a non-null value. As a consequence,
the resulting HTML output looks like this code snippet: <span class="api-true consented tracking-consent" ...
In this output, the consented attribute value is present, which indicates to the client-side code that the shopper has either granted or denied access to tracking. Whichever choice the shopper made, it is no longer necessary to display the Tracking Consent window.
Every time the consent.isml template is
rendered, the consentApi
property is passed into the template via
the pdict
property. The value of the consentApi
property is the result of the following call:
Object.prototype.hasOwnProperty.call(req.session.raw, 'setTrackingAllowed')
The
call checks if the session has a setTrackingAllowed
property. If it
does, this call resolves to true
; otherwise,
false
. This call always evaluates to true
for
Salesforce B2C Commerce version 18.4 and later.
The client-side script,
app_storefront_base/cartridge/client/default/js/components/consentTracking.js,
inspects the <span>
tag created by the
consent.isml template.
'use strict';
/**
* Renders a modal window that tracks the shoppers consenting to accepting site
* tracking policy
*/
function showConsentModal() {
var urlContent = $('.tracking-consent').data('url');
var urlAccept = $('.tracking-consent').data('accept');
var urlReject = $('.tracking-consent').data('reject');
var textYes = $('.tracking-consent').data('accepttext');
var textNo = $('.tracking-consent').data('rejecttext');
var textHeader = $('.tracking-consent').data('heading');
var htmlString = '<!-- Modal -->'
+ '<div class="modal show" id="consent-tracking" role="dialog" style="display: block;">'
+ '<div class="modal-dialog">'
+ '<!-- Modal content-->'
+ '<div class="modal-content">'
+ '<div class="modal-header">'
+ textHeader
+ '</div>'
+ '<div class="modal-body"></div>'
+ '<div class="modal-footer">'
+ '<div class="button-wrapper">'
+ '<button class="affirm btn btn-primary" data-url="' + urlAccept + '">'
+ textYes
+ '</button>'
+ '<button class="decline btn btn-primary" data-url="' + urlReject + '">'
+ textNo
+ '</button>'
+ '</div>'
+ '</div>'
+ '</div>'
+ '</div>'
+ '</div>';
$.spinner().start();
$('body').append(htmlString);
$.ajax({
url: urlContent,
type: 'get',
dataType: 'html',
success: function (response) {
$('.modal-body').html(response);
},
error: function () {
$('#consent-tracking').remove();
}
});
$('#consent-tracking .button-wrapper button').click(function (e) {
e.preventDefault();
var url = $(this).data('url');
$.ajax({
url: url,
type: 'get',
dataType: 'json',
success: function () {
$('#consent-tracking .modal-body').remove();
$('#consent-tracking').remove();
$.spinner().stop();
},
error: function () {
$('#consent-tracking .modal-body').remove();
$('#consent-tracking').remove();
$.spinner().stop();
}
});
});
}
module.exports = function () {
if ($('.consented').length === 0 && $('.tracking-consent').hasClass('api-true')) {
showConsentModal();
}
if ($('.tracking-consent').hasClass('api-true')) {
$('.tracking-consent').click(function () {
showConsentModal();
});
}
};
This
script constructs a section of HTML, stores it in the variable
htmlString
, and appends it to the <body>
element of the DOM. The script then performs an Ajax call to get the value of the
tracking_hint
content asset. If the call is successful, the
script inserts the value of the content asset into the <div> tag whose class
attribute is modal-body
. If the call fails, the script removes the
entire section of appended HTML from the DOM.
The script then creates an
event listener on both <button>
elements in the appended
HTML. If the shopper clicks either button in the Tracking Consent window, the script
makes a second Ajax call, either enabling or disabling tracking on the session. The
script then removes the Tracking Consent window from the DOM.
Lastly, this
script exports a function whose purpose is to conditionally call the
showConsentModal()
function. The exported function is invoked
in two situations: after each storefront page is fully loaded in the browser window,
and when the shopper clicks Consent to Track in the Edit
Profile form on the My Account page. For more information, see the
app_storefront_base/cartridge/templates/default/account/editProfileForm.isml
template.