Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add macro to track clicks #196

Merged
merged 3 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -58,7 +58,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 @@ -288,6 +302,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 @@ -374,6 +391,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 @@ -39,11 +39,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 @@ -85,8 +90,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 @@ -577,4 +585,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>`);
});
});
});