GTM Integration
You can use Google Tag Manager to add Acoustic Personalization tags to your Multi-Page Application (MPA).
Prerequisites
- Before you begin with this section, ensure that you have completed the prerequisites.
- Ensure that you are using Personalization Library version >= 1.2.0. For more information about configuring the zones using Personalization Library, see Zone Configuration.
Adding a specific tag in GTM for Personalization
- Create and configure the personalization zones in your MPA channel. For more information, see Zone configuration
- In the Google Tag Manager (GTM), create a custom HTML tag, with name, for example, UbxWrtpIntegrationTag
- Add the following code to the UbxWrtpIntegrationTag custom HTML tag. Make the relevant changes to the code as instructed below before adding it to the tag.
Step 1: Initialize the variables
These variables are used to configure channel details for personalized website.
<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>
Step 2: Get Acoustic Exchange Capture Enablement Key
To get the Enablement Key: Log in to Acoustic Exchange. From the Tools menu, go to UBX Capture. Click Enablement Key icon. Copy the two lines with the enablement key and paste them in the UbxWrtpIntegrationTag. Make the necessary change as indicated in the code snippet.
<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 Google Analytics endpoints registered in the Acoustic Exchange
Capture, then you need to select the endpoint to be used by specifying its Endpoint
authentication key, as shown by the highlighted line of code in the following snippet.
<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>
Step 3: Add the following snippet in the GTM tag
Copy and paste the following code snippet as-is, into your UbxWrtpIntegrationTag GTM tag. No changes need to be made to this code snippet.
<script type="text/javascript">
var ubxOleUtils = ({
loadAndPersonalizeContent: function(zoneIdToRenderingMap, RTP) {
RTP.onReady(function() {
ubxOleUtils.loadContent(zoneIdToRenderingMap, RTP);
});
},
loadRecommendationContent: function(recommendationZoneMap, RTP) {
ubxOleUtils.loadContent(recommendationZoneMap, RTP)
},
loadContent: function(zoneMap, RTP) {
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 = RTP.getZoneArea(zoneMap[i].zoneId);
if (zoneArea !== undefined) {
var numberOfRecommendations = zoneMap[i].numberOfRecommendations;
ubxOleUtils.callZoneRecommendation(zoneMap[i].zoneId, numberOfRecommendations, callbackFunction, RTP, zoneArea);
zoneMap.splice(i, 1);
i--;
continue;
}
}
if (zoneMap[i].outletId) {
var outletId = zoneMap[i].outletId;
var outletArea = RTP.getZoneArea(outletId);
if (outletArea !== undefined) {
var contentId = RTP.getContentId(zoneMap[i].zoneId);
zoneMap[i].renderingFunction(contentId, outletArea, RTP, outletId, zoneMap[i].zoneId);
zoneMap.splice(i, 1);
i--;
continue;
}
}
}
retries--;
}, 50)
},
callZoneRecommendation: function(zoneId, numberOfRecommendations, callbackFunction, RTP, zoneArea) {
var zonearea = zoneArea;
RTP.getRecommendedProducts(zoneId, numberOfRecommendations).then(function(response) {
try {
callbackFunction(zonearea, response, RTP);
} 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 RTP = acoustic.personalization.JsWRTP.create();
if (typeof(personalizationZoneMap) != 'undefined' && personalizationZoneMap != null)
ubxOleUtils.loadAndPersonalizeContent(personalizationZoneMap, RTP);
if (typeof(recommendationZoneMap) != 'undefined' && recommendationZoneMap != null)
ubxOleUtils.loadRecommendationContent(recommendationZoneMap, RTP);
OLEInitComplete = true;
} catch (error) {
console.log(error);
}
}
retries--;
}, 10);
},
});
</script>
Step 4: Rendering logic
- For Content Personalization
-
Create a function with the following signature:
function(contentId, domOutlet,RTP,outletId,zoneId)Where:- contentId: Content ID that provides the actual personalized content as configured in Acoustic Personalization UI.
- domOutlet: HTML DOM element to be personalized
- RTP: Personalization Library object
- outletId: ID selector for domOutlet
- zoneId: Registered zone to be personalized
The above code snippet illustrates content personalization 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,RTP,outletId,zoneId) { if (contentId == 'DCIDNF404') { domOutlet.style.opacity = 1; } else { domOutlet.style.background = "url(" + contentId + ") 0px 0px / cover no-repeat"; domOutlet.style.opacity = 1; } RTP.trackEvent("<<zone ID>>","<<element ID>>","<<event>>"); } personalizationZoneMap.push({'zoneId':'<<Zone ID>>','outletId':'<<OutletID>>', 'renderingFunction' : renderContentPersonalization}); </script>After these rendering functions are created, populate the personalizationZoneMap array that holds objects in the format:
Where:{'zoneId':'<<Zone ID>>','outletId':'<<OutletID>>','renderingFunction' : renderContentPersonalization}- zoneId: Registered zone to be personalized
- outletId: ID selector for domOutlet
- renderContentPersonalization: The rendering function defined above and applicable to the Zone ID mentioned in the object.
- For Product Recommendations
Create a function with the following signature:
function(zoneArea, RESPONSE, RTP)Where:- zoneArea: Zone on which product recommendations are to be displayed
- RESPONSE: List of recommended products
- RTP: Personalization Library object
The above code snippet illustrates product recommendations 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 , RTP){ if (RESPONSE !== 'PRNF404' && RESPONSE !== undefined) { // Rendering logic /** * Create an HTMLElement dynamically for each product * Add RTP.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 these rendering functions are created, populate the recommendationZoneMap map. This map is an array that holds objects in the format:
Where:{'zoneId':'<<Zone ID>>', 'numberOfRecommendations':<<Integer value for number of recommendations>>, 'renderingFunction' : renderRecommendation}- zoneID: ID of the zone where product recommendations should be displayed
- numberOfRecommendations: Integer value for number of recommendations, for example: 10
- renderRecommendation: The rendering function defined above and applicable to the Zone ID mentioned in the object.
Step 5: Add code to trigger the script execution
Add the following code snippet as it is.
<script>
var channelData = {
channelID : channelID,
channelDimension: channelDimension,
htmlID : htmlId,
containerID : containerId,
customFunction : function (){
}
};
ubxOleUtils.initializeRTPLib(channelData,personalizationZoneMap,recommendationZoneMap);
</script>
Optional:
If you want to add any custom function, you can add it in the customFunction : function ()
{<<Add your custom function here>>} For more information, see
this Knowledge Center page.- 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 and 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.
- You must have one custom dimension available for Acoustic Personalization to send the channelTenantId information. To get this information, log in to Google Analytics and create a custom dimension field with User's scope. Name the field as channelTenantId.
- For the UbxWrtpIntegrationTag tag, choose the Tag Firing option as Once Per Page.
- The error code DCIDNF404 is returned when no published rules are available or when none of the published rules match the visitor behavior on the channel. In such case, Acoustic Personalization does not return any personalized content. The channel must handle this scenario by specifying a default content to be displayed. For more information about the error codes, see Personalization Library response codes
- 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 UbxWrtpIntegrationTag from the drop-down list.
- Select the Don't fire Page View tag if UbxWrtpIntegrationTag fails or is paused check box.
- After you have set up your tags, click the Submit button and then Publish button at the top of the page, to publish your Google Tag Manager container.
References
For an example code snippet to render product recommendations using Google Tag Manager, see Content rendering using GTM