Skip to content

Commit

Permalink
Add macro to track clicks (#196)
Browse files Browse the repository at this point in the history
* added support for GAM macro %%CLICK_URL_UNESC%%

* add tests for %%CLICK_URL_UNESC%%

* add info about clickUrlUnesc in readme

Co-authored-by: Michele Nasti <michele@rtk.io>
  • Loading branch information
musikele and Michele Nasti authored Jan 20, 2023
1 parent d7b728c commit 4a86a23
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 6 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ You can find a detailed explanations on the [Prebid Universal Creative](http://p
ucTagData.hbPb = "%%PATTERN:hb_pb%%";
ucTagData.hbFormat = "%%PATTERN:hb_format%%";
ucTagData.adId = "%%PATTERN:hb_adid%%";
// if you're using GAM and want to track outbound clicks on native ads you can add this line
ucTagData.clickUrlUnesc = "%%CLICK_URL_UNESC%%";
ucTagData.requestAllAssets = true;
try {
Expand Down
23 changes: 22 additions & 1 deletion src/nativeAssetManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,21 @@ const assetTypeMapping = {
const DEFAULT_CACHE_HOST = 'prebid.adnxs.com';
const DEFAULT_CACHE_PATH = '/pbc/v1/cache';

export function newNativeAssetManager(win, pubUrl) {
const CLICK_URL_UNESC = `%%CLICK_URL_UNESC%%`;

let clickUrlUnesc = '';

export function newNativeAssetManager(win, nativeTag) {
const { pubUrl } = nativeTag;


// clickUrlUnesc contains the url to track clicks in GAM. we check if it
// has been transformed, by GAM, in an URL.
// if CLICK_URL_UNESC is the string "%%CLICK_URL_UNESC%%", we're not in GAM.
if (nativeTag.clickUrlUnesc && nativeTag.clickUrlUnesc !== CLICK_URL_UNESC) {
clickUrlUnesc = nativeTag.clickUrlUnesc;
}

const sendMessage = prebidMessenger(pubUrl, win);
let callback;
let errorCountEscapeHatch = 0;
Expand Down Expand Up @@ -289,6 +303,9 @@ export function newNativeAssetManager(win, pubUrl) {
}

if (data.message === 'assetResponse') {
// add GAM %%CLICK_URL_UNESC%% to the data object to be eventually used in renderers
data.clickUrlUnesc = clickUrlUnesc;

const body = win.document.body.innerHTML;
const head = win.document.head.innerHTML;

Expand Down Expand Up @@ -375,6 +392,10 @@ export function newNativeAssetManager(win, pubUrl) {
}
}
}

//substitute CLICK_URL_UNESC with actual value
html = html.replaceAll(CLICK_URL_UNESC, bid.clickUrlUnesc || "");

win.document.body.innerHTML += html;
callback && callback();
win.removeEventListener('message', replaceAssets);
Expand Down
2 changes: 1 addition & 1 deletion src/nativeRenderManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function newNativeRenderManager(win) {
let renderNativeAd = function(doc, nativeTag) {
window.pbNativeData = nativeTag;
sendMessage = prebidMessenger(nativeTag.pubUrl, win);
const nativeAssetManager = newNativeAssetManager(window, nativeTag.pubUrl);
const nativeAssetManager = newNativeAssetManager(window, nativeTag);

if (nativeTag.hasOwnProperty('adId')) {

Expand Down
76 changes: 72 additions & 4 deletions test/spec/nativeAssetManager_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,16 @@ const mockDocument = {
};

// creates mock postmessage response from prebid's native.js:getAssetMessage
function createResponder(assets,url,template) {
function createResponder(assets,url,template, clickUrlUnesc = '') {
return function(type, listener) {
if (type !== 'message') { return; }

const data = { message: 'assetResponse', adId: AD_ID, assets, adTemplate:template, rendererUrl:url };
const data = {
message: 'assetResponse',
adId: AD_ID, assets,
adTemplate:template,
rendererUrl:url,
};
listener({ data: JSON.stringify(data), origin: ORIGIN});
};
}
Expand Down Expand Up @@ -86,8 +91,11 @@ function generateRenderer(assets) {
describe('nativeAssetManager', () => {
let win;

function makeManager() {
return newNativeAssetManager(win, ORIGIN);
function makeManager(args) {
return newNativeAssetManager(win, {
pubUrl: ORIGIN,
...args
});
}

beforeEach(() => {
Expand Down Expand Up @@ -578,4 +586,64 @@ describe('nativeAssetManager', () => {
expect(win.document.body.style.width).to.equal('600px');
});
});

describe('GAM macro %%CLICK_URL_UNESC%%', () => {
it("should remove %%CLICK_URL_UNESC%% if there's no variable set", () => {
const html = `<script>
let nativeTag = {};
nativeTag.adTemplate = "<div class=\"sponsored-post\">\r\n <div class=\"thumbnail\"><\/div>\r\n <div class=\"content\">\r\n <h1>\r\n <a href=\"%%CLICK_URL_UNESC%%##hb_native_linkurl##\" target=\"_blank\" class=\"pb-click\">##hb_native_title##<\/a>\r\n <\/h1>\r\n <p>##hb_native_body##<\/p>\r\n \t<div class=\"attribution\">\r\n \t<img class=\"pb-icon\" src=\"##hb_native_image##\" alt=\"icon\" height=\"150\" width=\"50\">\r\n \t\r\n \t<\/div>\r\n\t<\/div>\r\n<\/div>";
nativeTag.pubUrl = "https://www.url.com";
nativeTag.adId = "`+AD_ID+`";
nativeTag.requestAllAssets = true;
window.pbNativeTag.renderNativeAd(nativeTag);
</script>`;
win.pbNativeData = {
pubUrl : 'https://www.url.com',
adId : AD_ID,
adTemplate : '<div class=\"sponsored-post\">\r\n <div class=\"thumbnail\"><\/div>\r\n <div class=\"content\">\r\n <h1>\r\n <a href=\"%%CLICK_URL_UNESC%%##hb_native_linkurl##\" target=\"_blank\" class=\"pb-click\">##hb_native_title##<\/a>\r\n <\/h1>\r\n <p>##hb_native_body##<\/p>\r\n \t<div class=\"attribution\">\r\n \t<img class=\"pb-icon\" src=\"##hb_native_image##\" alt=\"icon\" height=\"150\" width=\"50\">\r\n \t\r\n \t<\/div>\r\n\t<\/div>\r\n<\/div>'
};

win.document.body.innerHTML = html;
win.addEventListener = createResponder([
{ key: 'body', value: 'Body content' },
{ key: 'title', value: 'new value' },
{ key: 'clickUrl', value: 'http://www.example.com' },
{ key: 'image', value: 'http://www.image.com/picture.jpg' },
]);

const nativeAssetManager = makeManager();
nativeAssetManager.loadAssets(AD_ID);

expect(win.document.body.innerHTML).to.include(`<a href="http://www.example.com" target="_blank" class="pb-click">new value</a>`);
});

it("should substitute %%CLICK_URL_UNESC%% with clickUrlUnesc value", () => {
const html = `<script>
let nativeTag = {};
nativeTag.adTemplate = "<div class=\"sponsored-post\">\r\n <div class=\"thumbnail\"><\/div>\r\n <div class=\"content\">\r\n <h1>\r\n <a href=\"%%CLICK_URL_UNESC%%##hb_native_linkurl##\" target=\"_blank\" class=\"pb-click\">##hb_native_title##<\/a>\r\n <\/h1>\r\n <p>##hb_native_body##<\/p>\r\n \t<div class=\"attribution\">\r\n \t<img class=\"pb-icon\" src=\"##hb_native_image##\" alt=\"icon\" height=\"150\" width=\"50\">\r\n \t\r\n \t<\/div>\r\n\t<\/div>\r\n<\/div>";
nativeTag.pubUrl = "https://www.url.com";
nativeTag.adId = "`+AD_ID+`";
nativeTag.requestAllAssets = true;
window.pbNativeTag.renderNativeAd(nativeTag);
</script>`;
win.pbNativeData = {
pubUrl : 'https://www.url.com',
adId : AD_ID,
adTemplate : '<div class=\"sponsored-post\">\r\n <div class=\"thumbnail\"><\/div>\r\n <div class=\"content\">\r\n <h1>\r\n <a href=\"%%CLICK_URL_UNESC%%##hb_native_linkurl##\" target=\"_blank\" class=\"pb-click\">##hb_native_title##<\/a>\r\n <\/h1>\r\n <p>##hb_native_body##<\/p>\r\n \t<div class=\"attribution\">\r\n \t<img class=\"pb-icon\" src=\"##hb_native_image##\" alt=\"icon\" height=\"150\" width=\"50\">\r\n \t\r\n \t<\/div>\r\n\t<\/div>\r\n<\/div>',
};

win.document.body.innerHTML = html;
win.addEventListener = createResponder([
{ key: 'body', value: 'Body content' },
{ key: 'title', value: 'new value' },
{ key: 'clickUrl', value: 'http://www.example.com' },
{ key: 'image', value: 'http://www.image.com/picture.jpg' },
], null, null, );

const nativeAssetManager = makeManager({ clickUrlUnesc: 'https://will.redirect/?to='});
nativeAssetManager.loadAssets(AD_ID);

expect(win.document.body.innerHTML).to.include(`<a href="https://will.redirect/?to=http://www.example.com" target="_blank" class="pb-click">new value</a>`);
});
});
});

0 comments on commit 4a86a23

Please # to comment.