Configure GTM - Add personalization tags

Overview

Google Tag Manager (GTM) is a tag manager that can output tags based on defined rules. You can implement the GTM in combination with Google Analytics. You can configure Google Tag Manager to add Acoustic Personalization tags to your SPA or MPA.

Before you begin

  • You must have Google Analytics – Universal Analytics to use GTM tags.
  • You must have a GTM account configured with a Google Analytics container.
  • The Google Analytics settings variable should be created as a user-defined variable. In this topic, the Google Analytics setting variable is set as GA-Id.
  • Enable the Ecommerce option for the Google Analytics variable. You can enable these options in Google Analytics. Select the Enable Enhanced Ecommerce features and Use data layer checkboxes.
  • The event actions in the tags must use the standard Google nomenclature.

📘

Note:

If you are using the new exchange capture UI, set up the Exchange Capture on your website.

Create a page view tag

Page view tag is required for each page on your website to capture all the events.
To create a page view tag:

  1. Log in to Google Tag Manager and go to Tags.
  2. Click New and add the following configuration:
  • Tag Type: Google Analytics – Universal Analytics
  • Track Type: Page View
  • Google Analytics Settings > Select Settings Variable: Select the Google Analytics Settings variable.
  • Tag Firing Options: Once per page.
  • Triggering > Firing options: All Pages.

Add GTM tracking code to your SPA or MPA

The GTM tracking code enables Google Tag Manager on your website. Add the following tracking code at the beginning of the HEAD tag of each HTML page in your SPA and MPA.


  
<script> (function (w, d, s, l, i) { w[l] = w[l] || []; w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'}); var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'dataLayer', 'GTM_ID'); </script>

Replace `GTM_ID` with your own Google Tag Manager tracking ID.

📘

Note:

If you have GTM already configured on your website, the GTM tracking code is already present in the HEAD tag. There are two ways you can add Exchange Enablement code and GTM tracking code.

  1. You can add Exchange Enablement code directly into the website page before the GTM tracking snippet.
  2. You can add Exchange Enablement code to the website page and add the GTM Tracking Snippet into the Exchange Capture code snippet section.

You should not use the Exchange Enablement code within the GTM tag because both Exchange Capture and GTM are tag managers and can create conflicts on the website.

Complete the following steps to add the tracking code to Exchange Capture:

  1. Go to Acoustic Exchange >Tools > Exchange Capture.
  2. Click Code Snippets > Create Snippet.
  3. Provide a code snippet name. For example, GTM Container Snippet and add a description.
  4. In the code snippet details section, click Add snippet and copy and paste the GTM container code.

  
<script> (function (w, d, s, l, i) { w[l] = w[l] || []; w[l].push({'gtm.start': new Date().getTime(), event: 'gtm.js'}); var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'dataLayer', 'GTM_ID'); </script>
  1. Add this code snippet in the Deployment Group.
  2. Deploy the Group on Production, to reflect this change on your MPA or SPA channel.

Create custom variables in GTM

Create the following custom variables in Google Tag Manager.

  1. In Google Tag Manager, go to Variables and click New.
  2. Create a user-defined variable gtmDimensionVariable with the following configuration:
  • Variable Type: Data Layer Variable
  • Data Layer Variable Name: dimension1
  • Data Layer Version: Version 2
  1. Next, create another custom variable named SendHitTaskOverrideVariable of type Custom JavaScript.
  2. Add the following code snippet to this custom variable.

📘

Note:

The code snippet shown here uses gaClientID as an example. You must decide the identifier and use the same. Update the identifiers mapper array according to the Exchange unique identifier types.

function() 
{
  return function(model)
 {
  var globalSendTaskName = '_' + model.get('trackingId') + '_sendHitTask';
  var clientId = model.get('clientId');
  var identifiersMapperArray = [];
  identifiersMapperArray.push({ "name": "gaClientId", "value": clientId });
  var originalSendHitTask = window[globalSendTaskName] = window[globalSendTaskName] || model.get('sendHitTask');
  model.set('sendHitTask', function(sendModel) 
   {
    originalSendHitTask(sendModel);
    var payLoad = sendModel.get('hitPayload');
    var hitType = model.get("hitType");
    //var searchKeyword ; this must be declared globally on the website, and holds value of the search term 
    if (hitType=="pageview" && typeof searchKeyword !== 'undefined' &&  searchKeyword !== null)     
                {
                        var result = {};
                        payLoad.split("&").forEach(function (part) 
                        {
                             var item = part.split("=");
                             result[item[0]] = decodeURIComponent(item[1]);
                         });
                         var eveObj = google_ubx.mapToUBXEvent(result, google_ubx.googleToUBXPageViewEvent);
                         eveObj.attributes.push({"name":"searchTerms", "value": searchKeyword, "type": "string"});
                         eveObj.identifiers = identifiersMapperArray;
                         eveObj.eventCode = "ibmsearchedSite";
                         ibm_ubx.sendEvent(eveObj, google_ubx.host, google_ubx.authKey,"GOOGLEANALYTICS"); 
                         }
                         else{
                         google_ubx.sendEventFromPayload(payLoad, identifiersMapperArray);
                         }
   });
 };
}
  1. In the Google Analytics settings variable, set the custom variables as follows:
  • Fields to Set > Field name: customTask, Value: {{SendHitTaskOverrideVariable}}
  • Custom Dimensions > Index: 1, Dimension Value: {{gtmDimensionVariable}}

📘

Note:

These values are only for illustrative purposes. You must use the Index and Dimension Value as per your setup.

  1. Next, enable the following in-built GTM variables:
  • Container ID
  • HTML ID

GTM configuration

You must create and add a tag specific to personalization in GTM. Configure the tag based on the Personalization library version you are using.

📘

Note:

Personalization libraries v1.5.0 and v2.0.0 can only be configured with the Exchange Capture classic UI. If you use these versions with the Exchange Capture new UI, it may cause configuration issues.
If you have the Exchange Capture new UI, use the Personalization library v2.3.0.

  1. In Google Tag Manager, go to Tags and click New.
  2. Enter a name for the tag, for example, ExchangeIntegrationTag.
  3. Click Tag Configuration and choose Custom HTML as the tag type.
  4. Set the variables used to configure channel details for a personalized website. Then, add the code to the tag configuration HTML textbox.
<script> var channelID = "PROVIDE THE CHANNEL TENANT ID", channelDimension = "PROVIDE INDEX OF GOOGLE ANALYTICS CUSTOM DIMENSION USED FOR CHANNELID"; /*Do Not Modify Below Variables*/ var ubxEvents = []; var containerId = {{Container ID}} ; var htmlId = {{HTML ID}} ; var personalizationZoneMap = [], recommendationZoneMap = []; </script>
  1. Add the Exchange Capture Enablement Key to the tag configuration HTML textbox. To get the enablement key, go to Exchange Capture and click the Enablement Code icon:
<script type="text/javascript" src="<<//path/to/ubxCapture.js>>"></script> <script> ubxCapture.setTenantID("WRTP",channelID); </script> <script type="text/javascript"> ubxCapture.setID("ENABLEMENT KEY FROM UBX CAPTURE"); //The function setTenantID must be called before setID. </script>

Optional:
If you have more than one GA endpoint registered in Capture, then you need to select the endpoint to be used. Specify the endpoint authentication key to specify the endpoint. For example,

<script type="text/javascript" src="<<//path/to/ubxCapture.js>>"></script> <script> ubxCapture.setTenantID("WRTP",channelID); ubxCapture.setTenantID("GA","ENDPOINT AUTHENTICATION KEY FOR GOOGLE ANALYTICS ENDPOINT IN UBX"); </script> <script type="text/javascript"> ubxCapture.setID("ENABLEMENT KEY FROM UBX CAPTURE"); //The function setTenantID must be called before setID. </script>
  1. Then, copy and paste the following code snippet as-is to the HTML textbox for your tag.
<script type="text/javascript"> var ubxOleUtils = ({ loadAndPersonalizeContent: function(zoneIdToRenderingMap, personalizationObject) { personalizationObject.onReady(function() { ubxOleUtils.loadContent(zoneIdToRenderingMap, personalizationObject); }); }, loadRecommendationContent: function(recommendationZoneMap, personalizationObject) { personalizationObject.onRecommendationsReady(function (){ ubxOleUtils.loadContent(recommendationZoneMap, personalizationObject) }); }, loadContent: function(zoneMap, personalizationObject) { var retries = 200; var timerHandle = setInterval(function() { if (retries <= 0 || zoneMap.length == 0 || document.readyState == 'complete') { clearInterval(timerHandle); } for (var i = 0; i < zoneMap.length; i++) { var callbackFunction = zoneMap[i].renderingFunction; if (zoneMap[i].numberOfRecommendations) { var zoneArea = personalizationObject.getZoneArea(zoneMap[i].zoneId); if (zoneArea !== undefined) { var numberOfRecommendations = zoneMap[i].numberOfRecommendations; ubxOleUtils.callZoneRecommendation(zoneMap[i].zoneId, numberOfRecommendations, callbackFunction, personalizationObject, zoneArea); zoneMap.splice(i, 1); i--; continue; } } if (zoneMap[i].outletId) { var outletId = zoneMap[i].outletId; var outletArea = personalizationObject.getZoneArea(outletId); if (outletArea !== undefined) { var contentId = personalizationObject.getContentId(zoneMap[i].zoneId); zoneMap[i].renderingFunction(contentId, outletArea, personalizationObject, outletId, zoneMap[i].zoneId); zoneMap.splice(i, 1); i--; continue; } } } retries--; }, 50) }, callZoneRecommendation: function(zoneId, numberOfRecommendations, callbackFunction, personalizationObject, zoneArea) { var zonearea = zoneArea; personalizationObject.getRecommendedProducts(zoneId, numberOfRecommendations).then(function(response) { try { callbackFunction(zonearea, response, personalizationObject); } catch (error) { console.log('RTP: Please check HTML element or rendering for recommendations'); } }); }, setGtmEvents: function() { try { var gtmEvents = ['googleToUBXAddToCartEvent', 'googleToUBXCartPurchaseEvent', 'googleToUBXCartPurchaseItemEvent', 'googleToUBXFormErrorEvent', 'googleToUBXPageViewEvent', 'googleToUBXProductRatingEvent', 'googleToUBXProductReviewEvent', 'googleToUBXProductViewEvent', 'googleToUBXRemoveFromCartEvent', 'googleToUBXVideoCompletedEvent', 'googleToUBXVideoEvent', 'googleToUBXVideoLaunchedEvent', 'googleToUBXVideoPausedEvent', 'googleToUBXVideoPlayedEvent' ] for (var i = 0; i < gtmEvents.length; i++) { google_ubx[gtmEvents[i]] .attributesMapper .push({ "googleName": "cd" + channelDimension, "ubxName": "channeltenantId", "type": "string" }) } } catch (error) { console.error(error); } }, notifySuccess: function(htmlID, containerID) { try { var gtm = window.google_tag_manager[containerID]; gtm.onHtmlSuccess(htmlID); } catch (e) { gtm.onHtmlFailure(htmlID); } }, registerUbxEventCallbacks: function() { ibm_ubx.registerCallback("WRTP", function(eventPayload) { if (eventPayload) ubxEvents.push(eventPayload); }); }, setGtmDimension: function(containerID, channelDimension, channelID) { google_tag_manager[containerID] .dataLayer .set('dimension' + channelDimension, channelID); }, initializeRTPLib: function(channelData, personalizationZoneMap, recommendationZoneMap) { var OLEInitComplete = false; var mapperInitComplete = false; var documentReady = false; var retries = 3000; var timerHandle = setInterval(function() { if (retries <= 0 || (OLEInitComplete && mapperInitComplete)) { if (typeof channelData.customFunction !== 'undefined') { try { channelData.customFunction(); } catch (err) { console.log(err); } } clearInterval(timerHandle); ubxOleUtils.notifySuccess(channelData.htmlID, channelData.containerID); } if (google_ubx.googleToUBXAddToCartEvent && typeof(ibm_ubx.registerCallback) == "function" && !mapperInitComplete) { try { ubxOleUtils.setGtmEvents(channelData.channelDimension); ubxOleUtils.setGtmDimension(channelData.containerID, channelData.channelDimension, channelData.channelID); ubxOleUtils.registerUbxEventCallbacks(); mapperInitComplete = true; } catch (error) { console.log(error); } } if (ubxCapture.wrtpLoaded && typeof acoustic != 'undefined' && typeof(ibm_ubx.sendEvent) == "function" && !OLEInitComplete) { try { var personalizationObject = acoustic.personalization.JsWRTP.create(); if (typeof(personalizationZoneMap) != 'undefined' && personalizationZoneMap != null) ubxOleUtils.loadAndPersonalizeContent(personalizationZoneMap, personalizationObject); if (typeof(recommendationZoneMap) != 'undefined' && recommendationZoneMap != null) ubxOleUtils.loadRecommendationContent(recommendationZoneMap, personalizationObject); OLEInitComplete = true; } catch (error) { console.log(error); } } retries--; }, 10); }, }); </script>
  1. Add rendering logic to the GTM tag. The following is an example rendering logic. Examples show how to render personalized content or recommended products on the channel.

📘

Note:

You must write and add a rendering logic that best suits your implementation.

For content personalization

Create a function with the following signature:

function(contentId, domOutlet,personalizationObject,outletId,zoneId)

Where:

  • contentId – provides the actual personalized content as configured in Personalization UI.
  • domOutlet – is the HTML DOM element to be personalized.
  • personalizationObject – is the Personalization Library object.
  • outletId – is the ID selector for domOutlet.
  • zoneId – is the registered zone to be personalized.

The function renders the personalized content (contentId) on the personalized zone (domOutlet specified).

The following code snippet shows an example implementation for a single zone. If multiple zones use the same rendering logic, you can reuse the same rendering function. If different zones use different rendering logic, create multiple functions as applicable.

<script> var renderContentPersonalization = function(contentId, domOutlet,personalizationObject,outletId,zoneId) { //use this IF-ELSE block for content as image URL if (contentId == 'DCIDNF404') { domOutlet.style.opacity = 1; } else { domOutlet.style.background = "url(" + contentId + ") 0px 0px / cover no-repeat"; domOutlet.style.opacity = 1; } //use this IF-ELSE block for content as HTML if (contentId == 'DCIDNF404') { domOutlet.style.opacity = 1; } else { domOutlet.innerHTML = contentId; domOutlet.style.opacity = 1; } //This is optional, required only capturing the click events personalizationObject.trackEvent("ZONE ID","ELEMENT ID","EVENT"); } personalizationZoneMap.push({'zoneId':'ZONE ID','outletId':'OUTLETID', 'renderingFunction' : renderContentPersonalization}); </script>

After you create the rendering function, add it to the personalizationZoneMap array:

{'zoneId':'ZONE ID','outletId':'OUTLETID','renderingFunction' : renderContentPersonalization}

Where:

  • zoneId – is the registered zone to be personalized
  • outletId – is the ID selector for domOutlet
  • renderContentPersonalization – is the rendering function created. The function should apply to the Zone ID mentioned in the array.

For product recommendations

Create a function with the following signature:

function(zoneArea, RESPONSE, RTP)

Where:

  • zoneArea – is the zone on which product recommendations are to be displayed.
  • RESPONSE – is the list of recommended products.
  • personalizationObject – Personalization Library object

The following code snippet shows an example implementation for a single zone. If multiple zones use the same rendering logic, you can reuse the same rendering function. If different zones use different rendering logic, create multiple functions as applicable.

<script> var renderRecommendation = function(zoneArea, RESPONSE,personalizationObject){ if (RESPONSE !== 'PRNF404' && RESPONSE !== undefined) { // Rendering logic /** * Create an HTMLElement dynamically for each product * Add personalizationObject.trackProduct(RESPONSE[i].PRODUCT_ID, event); on click of each HTMLElement. **/ zoneArea.append(/*created element*/); zoneArea.style.opacity = 1; // Add code to display the content } else { console.log('WRTP:Error in getting the recommendation.'); } } recommendationZoneMap.push({'zoneId':'ZONE ID', 'numberOfRecommendations':<<Integer value for number of recommendations>>, 'renderingFunction' : renderRecommendation}); </script>

After you create the rendering function, add it to the personalizationZoneMap array:

{'zoneId':'ZONE ID', 'numberOfRecommendations':<<Integer value for number of recommendations>>, 'renderingFunction' : renderRecommendation}

Where:

  • zoneID – is the ID of the zone where you want to display product recommendations.
  • numberOfRecommendations – is the integer value for the number of recommendations, for example,10
  • renderRecommendation – is the rendering function created. The function should apply to the Zone ID mentioned in the array.
  1. Add the following code to trigger the script execution:
<script> var channelData = { channelID : channelID, channelDimension: channelDimension, htmlID : htmlId, containerID : containerId, customFunction : function (){ } }; ubxOleUtils.initializeRTPLib(channelData,personalizationZoneMap,recommendationZoneMap); </script>

Optional:
You can add a custom function in the 'customFunction` key value. For example, 'customFunction: function ()' {ADD YOUR CUSTOM FUNCTION HERE}. For more information, see Using custom mappers with Google Analytics.

In the previous code snippet:

  • Replace ZONE ID with the registered zone ID.
  • Replace OUTLET ID with the outlet ID (usually the div id) of the page that has the registered zone.
  • The trackEvent function is optional. It is used for tracking the click events on your personalized zones. In this function:
  • Replace ZONE ID with the ID of the zone to which you want to attribute the click events. Ensure that this zone ID is the same as that configured in the Acoustic Personalization.
  • Replace ELEMENTID with the ID of the HTML element for which you want to track the specified event.
  • Replace EVENT with the event type that you want to track for the given zone. In this case, replace "event" with “click” to track the click events on the specified zone. Ensure that you specify the parameters in the same order as specified in the code snippet. For more information about capturing click events, see Capture click events.
  • One custom dimension must be available for Personalization to send the channelTenantId information. You can create the custom dimension field with the User's scope in Google Analytics. Name the field as channelTenantId.
  • For the ExchangeIntegrationTag tag, choose the Tag Firing option as Once Per Page.
  • The error code DCIDNF404 is returned when no published rules are available. The error also occurs when none of the published rules match the visitor behavior on the channel. In such a case, Personalization does not return any personalized content. The channel must handle this scenario by specifying the default content to be displayed. For more information about the error codes, see Personalization Library response codes

This example code shows how to render product recommendations on your channel.

  1. Add the reference to CSS. Add the following script in the HEAD tag of your website page to use the example renderer widget.
<link rel="stylesheet" href="https://cdn-personalization-us- 1.goacoustic.com/acoustic/prod/utility/Renderer.css" type="text/css"/> <script type="text/javascript" src="https://cdn-personalization-us-1.goacoustic.com/acoustic/prod/utility/Renderer.js"></script>
  1. In the rendering logic, use the following code snippet.
<script> let init = (function () { if (acoustic != undefined || acoustic != {}) { const personalizationObject = acoustic.personalization.JsWRTP.create(); personalizationObject.onRecommendationsReady(() => { const zoneId = "ZONE_REG_ID"; personalizationObject.getRecommendedProducts(zoneId, <<NUMBER_OF_PRODUCTS>>) .then(<<RESPONSE>> => { let zoneArea = personalizationObject.getZoneArea(zoneId); if (zoneArea) { if (<<RESPONSE>> !== 'PRNF404' && <<RESPONSE>>!== undefined) { // Rendering logic <<RESPONSE>>.forEach(function(data) { // create the element dynamically to hold the product details here. let element = document.createElement('div'); element.id = data.<<PRODUCT_ID>>; element.classList.add("wrtp-slide-2fec40e4-091c-40ca-b1df-d45cf19144da", "wrtp-slide-2fec40e4-091c-40ca-b1df-d45cf19144da-" + ([i])); element.onclick = () => { // Add the function to track the product here. personalizationObject.trackProduct(data.<<PRODUCT_ID>>,event); // Add the redirection to product details page (if available in product object) window.location.href = 'REDIRECT_TO_PRODUCT_DETAILS_PAGE'; }; // Add the product image here (if available in product object) element.style.background = "url(" + <<LINK_TO_THE_IMAGE_SOURCE>> + ") 50% 50% / cover no-repeat"; zoneArea.append(element); }); // Example widget to render the products (optional) zoneArea.style.opacity = 1; wrtpRenderer(zoneArea); } else { console.log("Error in getting the recommendations."); } } else { console.log("Please add a valid element on HTML for displaying the recommended products."); } }); }) } else { console.log("Error in loading Personalization Library"); } }); window.onload = init; </script>

Next, perform the following steps:

  1. Go to the Page View tag, and make the following updates in the Tag Sequencing section:
    • Select the Fire a tag before Page View fires check box.
    • Select ExchangeIntegrationTag from the drop-down list.
  2. After you have set up your tags, click Submit. Then click Publish to publish your Google Tag Manager container.
  3. You can verify the flow of events by triggering events on your website. You can also verify the flow of events using Test Drive. For more information, see Testing the events flow in Acoustic Exchange
  1. In Google Tag Manager, go to Tags and click New.
  2. Enter a name for the tag, for example, ExchangeIntegrationTag.
  3. Click Tag Configuration and choose Custom HTML as the tag type.
  4. Set the variables used to configure channel details for a personalized website. Then, add the code to the tag configuration HTML textbox.
<script> var channelID = "PROVIDE THE CHANNEL TENANT ID", channelDimension = "PROVIDE INDEX OF GOOGLE ANALYTICS CUSTOM DIMENSION USED FOR CHANNELID"; </script>
  1. Add the Exchange Capture Enablement Key to the tag configuration HTML textbox. To get the enablement key, go to Exchange Capture and click the Enablement Code icon:
  2. Before the setID function in the code, specify the channel ID, GA ID, and the Events authentication key. This setting ensures that the Personalization and GA endpoints are paired in Exchange. The following code sample shows the script to add for providing the GA ID and the authentication key.
<script> ubxCapture.setTenantID("WRTP","CHANNEL TENANT ID"); ubxCapture.setTenantID("GA","EVENTS ONLY AUTHENTICATION KEY FOR GOOGLE ANALYTICS ENDPOINT IN UBX"); </script>

Example of the final Exchange code snippet added to your HTML files:

<script type="text/javascript" src="//path/to/ubxCapture.js"></script> <script> ubxCapture.setTenantID("WRTP","CHANNEL TENANT ID"); ubxCapture.setTenantID("GA","EVENTS ONLY AUTHENTICATION KEY FOR GOOGLE ANALYTICS ENDPOINT IN UBX"); </script> <script type="text/javascript"> ubxCapture.setID("UBXCapture_EnablementKey"); </script>
  1. Then, copy and paste the following code snippet as-is to the HTML textbox for your tag.
<script type="text/javascript"> var ubxEvents = []; var containerID = {{Container ID}}; var htmlID = {{HTML ID}}; var ubxUtils = ({ setGtmEvents: function() { try { var gtmEvents = ['googleToUBXAddToCartEvent', 'googleToUBXCartPurchaseEvent', 'googleToUBXCartPurchaseItemEvent', 'googleToUBXFormErrorEvent', 'googleToUBXPageViewEvent', 'googleToUBXProductRatingEvent', 'googleToUBXProductReviewEvent', 'googleToUBXProductViewEvent', 'googleToUBXRemoveFromCartEvent', 'googleToUBXVideoCompletedEvent', 'googleToUBXVideoEvent', 'googleToUBXVideoLaunchedEvent', 'googleToUBXVideoPausedEvent', 'googleToUBXVideoPlayedEvent' ] for (var i = 0; i < gtmEvents.length; i++) { google_ubx[gtmEvents[i]] .attributesMapper .push({ "googleName": "cd" + channelDimension, "ubxName": "channeltenantId", "type": "string" }) } } catch (error) { console.error(error); } }, notifySuccess: function(htmlID, containerID, retries) { try { var gtm = window.google_tag_manager[containerID]; if (retries > 0) { gtm.onHtmlSuccess(htmlID); } else { gtm.onHtmlFailure(htmlID); } } catch (e) { gtm.onHtmlFailure(htmlID); } }, registerUbxEventCallbacks: function() { ibm_ubx.registerCallback("WRTP", function(eventPayload) { if (eventPayload) ubxEvents.push(eventPayload); }); }, setGtmDimension: function(containerID, channelDimension, channelID) { google_tag_manager[containerID] .dataLayer .set('dimension' + channelDimension, channelID); }, initializeLib: function() { var OLEInitComplete = false; var mapperInitComplete = false; var retries = 2400; var timerHandle = setInterval(function() { if (retries <= 0 || (mapperInitComplete && OLEInitComplete)) { clearInterval(timerHandle); ubxUtils.notifySuccess(htmlID, containerID, retries); } try { if (google_ubx.googleToUBXAddToCartEvent && typeof(ibm_ubx.registerCallback) == "function" && !mapperInitComplete) { this.ubxUtils.setGtmEvents(channelDimension); this.ubxUtils.setGtmDimension(containerID, channelDimension, channelID); this.ubxUtils.registerUbxEventCallbacks(); mapperInitComplete = true; } } catch (error) {} if (ubxCapture.wrtpLoaded && (typeof acoustic != undefined || acoustic != {}) && typeof(ibm_ubx.sendEvent) == "function" && !OLEInitComplete) { try { initPZNLib(); OLEInitComplete = true; } catch (error) { console.log(error); } } retries--; }, 50); }, }); ubxUtils.initializeLib(); </script>

Configure personalization

  1. Add the personalization script. Copy and paste the following code snippet into your ExchangeIntegrationTag GTM tag.
<script type="text/javascript"> var initPZNLib = (function () { // get the personalization object if (acoustic != undefined || acoustic != {}) { // access the personalization object var personalizationObject = acoustic.personalization.PersonalizationLibrary.create(); // Create a DOM Element validator for the identified zones in the client application var validateDOMElement = function (outlet) { var outletDiv = document.getElementById(outlet); if (!(outletDiv instanceof Node)) { console.log(outlet, " is not a valid HTML element."); return null; } return outletDiv; }; // Helper functions for personalizationHandler var displayDefaultContent = function (result, targetArea) { // Display the default content targetArea.style.opacity = 1; } var displayCustomUrl = function (content, targetArea) { var contentId = content.value if (contentId && contentId !== 'CONNF404' && targetArea) { // Display the personalized content targetArea.style.background = "url(" + contentId + ") 0px 0px / cover no-repeat"; targetArea.style.opacity = 1; } else { displayDefaultContent(content, targetArea); } } var displayWchUrl = function (content, targetArea) { var contentId = content.value if (contentId && contentId !== 'CONNF404' && targetArea) { // Display the personalized content targetArea.style.background = "url(" + contentId + ") 0px 0px / cover no-repeat"; targetArea.style.opacity = 1; } else { displayDefaultContent(content, targetArea); } } var displayCustomHtml = function (content, targetArea) { var contentId = content.value if (contentId && contentId !== 'CONNF404' && targetArea) { targetArea.innerHTML = contentId; targetArea.style.opacity = 1; } else { displayDefaultContent(content, targetArea); } } var displayWchHtml = function (content, targetArea) { var contentId = content.value if (contentId && contentId !== 'CONNF404' && targetArea) { // Display the personalized content targetArea.innerHTML = contentId; targetArea.style.opacity = 1; } else { displayDefaultContent(content, targetArea); } } var displayRecommendations = function (products, targetArea) { var recommendations = products.value; if (recommendations && recommendations !== 'CONNF404' && targetArea) { // Display the personalized recommendations } else { // Display the default recommendations displayDefaultContent(products, targetArea); } } // A handler at the client side to process the results var personalizationHandler = function(result, targetArea) { switch(result.type) { case "recommendation": displayRecommendations(result, targetArea); break; case "custom-url": displayCustomUrl(result, targetArea); break; case "custom-html": displayCustomHtml(result, targetArea); break; case "wch-url": displayWchUrl(result, targetArea); break; case "wch-html": displayWchHtml(result, targetArea); break; case "": displayDefaultContent(result, targetArea); break; } } // Configure personalization personalizationObject.onReady(function(){ // Step 8 configure zones (Approach 1) var zones = [ { "zoneId": "ZONE_ID_1", "targetArea": "ELEMENT_ID_1", "num": "NUMBER_OF_PRODUCTS_[OPTIONAL]" }, { "zoneId": "ZONE_ID_2", "targetArea": "ELEMENT_ID_2", "num": "NUMBER_OF_PRODUCTS_[OPTIONAL]" } ] zones.forEach(function(zoneObj) { var validElement = validateDOMElement(zoneObj.targetArea) if(validElement) { personalizationObject.getContent(zoneObj) .then(function(result) { personalizationHandler(result, validElement) }); //Optional code to capture click events personalizationObject.collectBehavior("ELEMENTID","EVENT ","ZONEID"); } }); }); } else { console.log("Error in loading the Personalization library."); } }); </script>

Where:

  • ZONE_ID_1 – is the ID of the registered zone to be personalized.
  • ELEMENT_ID_1 – is the Element ID in which personalization and recommendation are shown.
  1. In the code snippet, you must modify the following functions in the personalizationHandler. Change the functions as per the rendering logic for both personalization and recommendations.
    • displayCustomUrl (),
    • displayCustomHtml (),
    • displayWchUrl(),
    • displayWchHtml(),
    • dislayRecommendations()
    • displayDefaultContent()
      For detailed information, see Zone configuration for MPA.
  2. One custom dimension must be available for Personalization to send the channelTenantId information. You can create the custom dimension field with the User's scope in Google Analytics. Name the field as channelTenantId.
  3. For the ExchangeIntegrationTag tag, choose the Tag Firing option as Once Per Page.
  4. Save the tag without adding the trigger.

The error code CONNF404 is returned when no published rules are available. The error also occurs when none of the published rules match the visitor behavior on the channel. In such a case, Personalization does not return any personalized content. The channel must handle this scenario by specifying the default content to be displayed. For more information about the error codes, see Personalization Library response codes.

Updates to HTML page

For the zone to render personalization and recommendations, update the page HTML file. In the case of multiple zones, repeat this snippet for each zone by changing the ZONE_ID.

<div> <div id="<ZONE_ID>" wrtp-recommended-zone="<ZONE_ID>"></div> </div>

Next, perform the following steps:

  1. Go to the Page View tag, and make the following updates in the Tag Sequencing section:
    • Select the Fire a tag before Page View fires check box.
    • Select ExchangeIntegrationTag from the drop-down list.
  2. After you have set up your tags, click Submit. Then click Publish to publish your Google Tag Manager container.

📘

Note:

You must deploy the deployment group on Exchange Capture before you proceed to configure GTM.

  1. In Google Tag Manager, go to Tags and click New.
  2. Enter a name for the tag, for example, ExchangeIntegrationTag.
  3. Click Tag Configuration and choose Custom HTML as the tag type.
  4. Set the variables used to configure channel details for a personalized website. Then, add the code to the tag configuration HTML textbox.
<script> var channelID = "PROVIDE THE CHANNEL TENANT ID", channelDimension = "PROVIDE INDEX OF GOOGLE ANALYTICS CUSTOM DIMENSION USED FOR CHANNELID"; </script>
  1. Copy and paste the following code snippet as-is to the HTML textbox for your tag.
<script type="text/javascript" > var ubxEvents = []; var containerID = {{Container ID}} ; var htmlID = {{HTML ID}} ; var ubxUtils = ({ setGtmEvents: function() { try { var gtmEvents = ['googleToUBXAddToCartEvent', 'googleToUBXCartPurchaseEvent', 'googleToUBXCartPurchaseItemEvent', 'googleToUBXFormErrorEvent', 'googleToUBXPageViewEvent', 'googleToUBXProductRatingEvent', 'googleToUBXProductReviewEvent', 'googleToUBXProductViewEvent', 'googleToUBXRemoveFromCartEvent', 'googleToUBXVideoCompletedEvent', 'googleToUBXVideoEvent', 'googleToUBXVideoLaunchedEvent', 'googleToUBXVideoPausedEvent', 'googleToUBXVideoPlayedEvent' ] for (var i = 0; i < gtmEvents.length; i++) { google_ubx[gtmEvents[i]] .attributesMapper .push({ "googleName": "cd" + channelDimension, "ubxName": "channeltenantId", "type": "string" }) } } catch (error) { console.error(error); } }, notifySuccess: function(htmlID, containerID, retries) { try { var gtm = window.google_tag_manager[containerID]; if(retries > 0){ gtm.onHtmlSuccess(htmlID); }else{ gtm.onHtmlFailure(htmlID); } } catch (e) { gtm.onHtmlFailure(htmlID); } }, registerUbxEventCallbacks: function() { ibm_ubx.registerCallback("WRTP", function(eventPayload) { if (eventPayload) ubxEvents.push(eventPayload); }); }, setGtmDimension: function(containerID, channelDimension, channelID) { google_tag_manager[containerID] .dataLayer .set('dimension' + channelDimension, channelID); }, initializeLib: function() { var OLEInitComplete = false; var mapperInitComplete = false; var retries = 40; //the value may vary depending upon the loading time of the scripts on website and you can change it according to website loading time. var timerHandle = setInterval(function() { if (retries <= 0 || (mapperInitComplete && OLEInitComplete)) { clearInterval(timerHandle); ubxUtils.notifySuccess(htmlID, containerID, retries); } try { if (google_ubx.googleToUBXAddToCartEvent && typeof(ibm_ubx.registerCallback) == "function" && !mapperInitComplete) { this.ubxUtils.setGtmEvents(channelDimension); this.ubxUtils.setGtmDimension(containerID, channelDimension, channelID); this.ubxUtils.registerUbxEventCallbacks(); mapperInitComplete = true; } } catch (error) { } if ((typeof exchangeCapture != "undefined") && (typeof acoustic != "undefined") && (typeof ibm_ubx != "undefined" && typeof ibm_ubx.sendEvent != "undefined") && !OLEInitComplete) { try { initPZNLib(); OLEInitComplete = true; } catch (error) { console.log(error); } } retries--; }, 50); }, }); ubxUtils.initializeLib(); </script>

📘

Note:

The value of the retries variable may vary depending on the website loading time. Ideally, it should be 40.

Configure personalization

  1. Add the personalization script. Copy and paste the following code snippet into your ExchangeIntegrationTag GTM tag.
<script type="text/javascript"> var initPZNLib = (function() { // get the personalization object if (acoustic != undefined || acoustic != {}) { // access the personalization object var personalizationObject = acoustic.personalization.PersonalizationLibrary.create(); // Create a DOM Element validator for the identified zones in the client application var validateDOMElement = function(outlet) { var outletDiv = document.getElementById(outlet); if (!(outletDiv instanceof Node)) { console.log(outlet, " is not a valid HTML element."); return null; } return outletDiv; }; // Helper functions for personalizationHandler var displayDefaultContent = function(result, targetArea) { // Display the default content targetArea.style.opacity = 1; } var displayCustomUrl = function(content, targetArea) { var contentId = content.value if (contentId && contentId !== 'CONNF404' && targetArea) { // Display the personalized content targetArea.style.background = "url(" + contentId + ") 0px 0px / cover no-repeat"; targetArea.style.opacity = 1; } else { displayDefaultContent(content, targetArea); } } var displayWchUrl = function(content, targetArea) { var contentId = content.value if (contentId && contentId !== 'CONNF404' && targetArea) { // Display the personalized content targetArea.style.background = "url(" + contentId + ") 0px 0px / cover no-repeat"; targetArea.style.opacity = 1; } else { displayDefaultContent(content, targetArea); } } var displayCustomHtml = function(content, targetArea) { var contentId = content.value if (contentId && contentId !== 'CONNF404' && targetArea) { targetArea.innerHTML = contentId; targetArea.style.opacity = 1; } else { displayDefaultContent(content, targetArea); } } var displayWchHtml = function(content, targetArea) { var contentId = content.value if (contentId && contentId !== 'CONNF404' && targetArea) { // Display the personalized content targetArea.innerHTML = contentId; targetArea.style.opacity = 1; } else { displayDefaultContent(content, targetArea); } } var displayRecommendations = function(products, targetArea) { var recommendations = products.value; if (recommendations && recommendations !== 'CONNF404' && targetArea) { // Display the personalized recommendations } else { // Display the default recommendations displayDefaultContent(products, targetArea); } } // A handler at the client side to process the results var personalizationHandler = function(result, targetArea) { switch (result.type) { case "recommendation": displayRecommendations(result, targetArea); break; case "custom-url": displayCustomUrl(result, targetArea); break; case "custom-html": displayCustomHtml(result, targetArea); break; case "wch-url": displayWchUrl(result, targetArea); break; case "wch-html": displayWchHtml(result, targetArea); break; case "": displayDefaultContent(result, targetArea); break; } } // Configure personalization personalizationObject.onReady(function() { // Step 8 configure zones (Approach 1) var zones = [{ "zoneId": "ZONE_ID_1", "targetArea": "ELEMENT_ID_1", "num": "NUMBER_OF_PRODUCTS_[OPTIONAL]" }, { "zoneId": "ZONE_ID_2", "targetArea": "ELEMENT_ID_2", "num": "NUMBER_OF_PRODUCTS_[OPTIONAL]" } ] zones.forEach(function(zoneObj) { var validElement = validateDOMElement(zoneObj.targetArea) if (validElement) { personalizationObject.getContent(zoneObj) .then(function(result) { personalizationHandler(result, validElement) }); //Optional code to capture click events personalizationObject.collectBehavior("ELEMENTID", "EVENT ", "ZONEID"); } }); }); } else { console.log("Error in loading the Personalization library."); } }); </script>

Where:

  • ZONE_ID_1 – is the ID of the registered zone to be personalized.
  • ELEMENT_ID_1 – is the Element ID in which personalization and recommendation are shown.
  1. In the code snippet, you must modify the following functions in the personalizationHandler. Change the functions as per the rendering logic for both personalization and recommendations.
    • displayCustomUrl (),
    • displayCustomHtml (),
    • displayWchUrl(),
    • displayWchHtml(),
    • dislayRecommendations()
    • displayDefaultContent()
      For detailed information, see Zone configuration for MPA.
  2. One custom dimension must be available for Personalization to send the channelTenantId information. You can create the custom dimension field with the User's scope in Google Analytics. Name the field as channelTenantId.
  3. For the ExchangeIntegrationTag tag, choose the Tag Firing option as Once Per Page.
  4. Save the tag without adding the trigger.

The error code CONNF404 is returned when no published rules are available. The error also occurs when none of the published rules match the visitor behavior on the channel. In such a case, Personalization does not return any personalized content. The channel must handle this scenario by specifying the default content to be displayed. For more information about the error codes, see Personalization Library response codes.

Updates to HTML page

For the zone to render personalization and recommendations, update the page HTML file. In the case of multiple zones, repeat this snippet for each zone by changing the ZONE_ID.

<div> <div id="<ZONE_ID>" wrtp-recommended-zone="<ZONE_ID>"></div> </div>

Next, perform the following steps:

  1. Go to the Page View tag, and make the following updates in the Tag Sequencing section:
    • Select the Fire a tag before Page View fires check box.
    • Select ExchangeIntegrationTag from the drop-down list.
  2. After you have set up your tags, click Submit. Then click Publish to publish your Google Tag Manager container.