Cookie Consent Banner [duplicate] - javascript

I am using React/Nextjs for my website and the react-cookie-consent library. It creates a pop up where the user can agree to the cookie policy. If agreed, a cookie is set: CookieConsent with value "true".
Additionally, I want to use Google Analytics to track users if they agree (click Accept on the popup).
But it doesnt work: The Google Analytic cookies _ga and G_ENABLED_IDPS are set before the user clicks on the pop up.
Funny thing is, I only realized that on the Microsoft Edge Browser. In Chrome, these cookies are not set before the user gives consent.
This is my current code in _document.js:
<Head>
{/* Global Site Tag (gtag.js) - Google Analytics */}
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
`}}
/>
<script type="text/javascript" src="/static/blockReactDevTools.js" />
<link href="https://fonts.googleapis.com/css?family=Cabin&display=swap" rel="stylesheet" />
</Head>
I played around using some hints from the internet, and came up with this version which also doesn't work:
<Head>
{/* Global Site Tag (gtag.js) - Google Analytics */}
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `
var gtagId = '${GA_TRACKING_ID}';
window['ga-disable-' + gtagId] = true;
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
window.addEventListener("load", function() {
var isCookieConsentTrue = getCookie("CookieConsent") == 'true';
if(isCookieConsentTrue){
window['ga-disable-' + gtagId] = false;
alert("Cookie Consent given!");
} else {
alert("Cookie Consent NOT given!");
window['ga-disable-' + gtagId] = true;
}
});
`}}
/>
<script type="text/javascript" src="/static/blockReactDevTools.js" />
<link href="https://fonts.googleapis.com/css?family=Cabin&display=swap" rel="stylesheet" />
</Head>
I don't know if this is a nextjs specific issue, or something plain stupid general.
Can anyone guide me to a working solution?
EDIT: I tried the "Universal Analytics" approach of the suggestion. Suggested Solution make my helper functions to log events and pageviews fail (see below). Do I need also the gtag manager?

Use gtag's consent config options. Currently, the following can be put before any data measurement commands (config or event) are run in the header:
gtag('consent', 'default', {
'ad_storage': 'denied',
'analytics_storage': 'denied'
});
Then run this once the user has approved or if a consent cookie is present:
gtag('consent', 'update', {
'ad_storage': 'granted',
'analytics_storage': 'granted'
});
If you use Facebook Pixel, it works in a similar way. E.g. fbq('consent', 'revoke'); then fbq('consent', 'grant').

The way you were doing it is Opt-out. The GA cookie is always set, as soon as the client requests the gtag.js. This however doesn't comply with GDPR. What you should look into is Opt-in, so that no GA cookie is set without consenting.
The general idea is to async load the gtag.js once the client has consented. For full functionality of gtag functions you have to load the gtag.js on every page-load if the client already consented. Best practice to do this is with a cookieconsent cookie set on consent.
There's a widely used js library for this, which generates a popup and sets the consent-cookie for you.
Reference:
https://www.osano.com/cookieconsent/documentation/javascript-api/
You can generate code for the layout of your cookie banner by clicking Start Coding here:
https://www.osano.com/cookieconsent/download/
https://github.com/osano/cookieconsent/blob/dev/examples/example-7-javascript-api.html
Following code has to be implemented on every page in the <head> section:
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.1/cookieconsent.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.1/cookieconsent.min.js" data-cfasync="false"></script>
<script>
var popup;
window.addEventListener('load', function(){
window.cookieconsent.initialise({
//set revokeBtn if you don't want to see a tiny pullup bar overlapping your website
//if revokeBtn is set, make sure to include a link to cookie settings in your footer
//you can open your banner again with: popup.open();
//revokeBtn: "<div class='cc-revoke'></div>",
type: "opt-in",
theme: "classic",
palette: {
popup: {
background: "#000",
text: "#fff"
},
button: {
background: "#fd0",
text: "#000"
}
},
onInitialise: function(status) {
// request gtag.js on page-load when the client already consented
if(status == cookieconsent.status.allow) setCookies();
},
onStatusChange: function(status) {
// resquest gtag cookies on a new consent
if (this.hasConsented()) setCookies();
else deleteCookies(this.options.cookie.name)
},
/* enable this to hide the cookie banner outside GDPR regions
law: {
regionalLaw: false,
},
location: true,
},
*/
function (p) {
popup = p;
})
});
//it is absolutely crucial to define gtag in the global scope
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}', {'anonymize_ip': true});
function setCookies() {
var s = document.createElement('script');
s.type = "text/javascript"
s.async = "true";
s.src = "https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}";
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
// you can add facebook-pixel and other cookies here
};
function deleteCookies(cookieconsent_name) {
var keep = [cookieconsent_name, "DYNSRV"];
document.cookie.split(';').forEach(function(c) {
c = c.split('=')[0].trim();
if (!~keep.indexOf(c))
document.cookie = c + '=;' + 'expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/';
});
};
</script>
Note:
Make sure that the gtag.js is loaded on every page-load once the consent-cookie was set to allow. Use event_callback to see if a gtag event was sent. You can use the gtag function without checking for the consent-cookie. If gtag.js is not present it just adds elements to the window.dataLayer without any functionality. To avoid errors, the function gtag() has to be declared in global scope and before use.
// cookie-check is not necessary when gtag is in global scope
//if(popup.hasConsented()) {
gtag('event', 'sign_up', {
'method': 'Google',
'event_callback': function(){alert('event was sent');}
});
//}
You don't have to send an extra pageview event, unless you want to manually specify the path. setCookies() already sends the current document path along with the config

Related

Blazor server javascript not inserting elements as an MVC app would

I'm facing a little challenge here.
I'm trying to implement ads in my blazor server solution, and i cannot get it to work. So i've come up with a small MVC app where it works.
I must say that the blazor app is for a client i'm working for, and I don't know who supplies these adds. They send the code that i must implement for it to work.
Here is the stuff that they have asked me to implement in the head of the webpage
<script type="text/javascript">
var utag_data = {
}
</script>
<!-- Loading script asynchronously -->
<script type="text/javascript">
(function(a,b,c,d){
a='//partnerurl';
b=document;c='script';d=b.createElement(c);d.src=a;d.type='text/java'+c;d.async=true;
a=b.getElementsByTagName(c)[0];a.parentNode.insertBefore(d,a);
})();
</script>
<script async='async' src='https://macro.adnami.io/macro/spec/adsm.macro.someId.js'></script>
<script>
var adsmtag = adsmtag || {};
adsmtag.cmd = adsmtag.cmd || [];
</script>
<link rel="preconnect" href="https://cdn.cookielaw.org">
<link rel="preload" href="https://cdn.cookielaw.org/consent/tcf.stub.js" as="script"/>
<script src="https://cdn.cookielaw.org/consent/tcf.stub.js" type="text/javascript" charset="UTF-8"></script>
<link rel="preconnect" href="https://securepubads.g.doubleclick.net">
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
<script>
var googletag = window.googletag || {};
googletag.cmd = googletag.cmd || [];
window.yb_configuration = {"domain":"clientdomain"};
(function (y, i, e, l, d, b, I, R, D) {
d.Yieldbird = d.Yieldbird || {};
d.Yieldbird.cmd = d.Yieldbird.cmd || [];
l.cmd.push(function () {l.pubads().disableInitialLoad();});
b = i.createElement('script'); b.async = true;
b.src = (e === 'https:' ? 'https:' : 'http:') + '//jscdn.yieldbird.com/' + y + '/yb.js';
I = i.getElementsByTagName('script')[0];
(I.parentNode || i.head).insertBefore(b, I);
})('950dff97-bb8d-4667-952b-d72ce8bbe88b', document, document.location.protocol, googletag, window);
</script>
<script>
googletag.cmd.push(function () {
googletag.pubads().enableLazyLoad({
fetchMarginPercent: 150,
renderMarginPercent: 75,
mobileScaling: 1.5
});
});
</script>
And for each add placement i've received some html/javascript i must place at the location.
Heres an example:
<!-- /x,y/domain/domain.dk/billboard_1 -->
<script async="async" src="https://securepubads.g.doubleclick.net/tag/js/gpt.js">
</script>
<script>
var googletag = googletag || {};googletag.cmd = googletag.cmd || [];
</script>
<script>
googletag.cmd.push(
function() {
var YBmapping = googletag.sizeMapping().addSize([931,0],[[930,180]]).addSize([0,0],[]).build();
googletag.defineSlot('/x,y/domain/domain.dk/billboard_1', [[930, 180]], 'div-gpt-ad-billboard_1').defineSizeMapping(YBmapping).addService(googletag.pubads());
googletag.enableServices();
});
</script>
<div id='div-gpt-ad-billboard_1'>
<script>
googletag.cmd.push(function() {
googletag.display('div-gpt-ad-billboard_1');});
</script>
</div>
I've obfuscted some variables. But i guess you get the hang of it..
This is from the MVC app, and it works. It places extra html inside my div with the div-gpt-ad-billboard_1 id. So on the website the HTML would look like this:
<div id="div-gpt-ad-billboard_1">
<script>
googletag.cmd.push(function() {
googletag.display('div-gpt-ad-billboard_1');});
</script>
<div id="google_ads_iframe_/x,y/domain/domain.dk/billboard_1_0__container__" style="border: 0pt none; width: 930px; height: 0px;"></div></div>
And if i deploy my MVC app to the live endpoint we have, an iFrame is inserted into the div with the google_ads_iframe Id, where the add is displayed.
Now on my blazor app, this doesn't happen.
I've created a blazor component which contains the ad i want to place. It looks like this:
<script async="async" suppress-error="BL9992" src="https://securepubads.g.doubleclick.net/tag/js/gpt.js">
</script>
<script suppress-error="BL9992">
var googletag = googletag || {};googletag.cmd = googletag.cmd || [];
</script>
<script suppress-error="BL9992">
googletag.cmd.push(
function() {
var YBmapping = googletag.sizeMapping().addSize([931,0],[[930,180]]).addSize([0,0],[]).build();
googletag.defineSlot('x,y/domain/domain.dk/billboard_1', [[930, 180]], 'div-gpt-ad-billboard_1').defineSizeMapping(YBmapping).addService(googletag.pubads());
googletag.enableServices();
console.log(googletag);
});
</script>
<div id='div-gpt-ad-billboard_1'>
<script suppress-error="BL9992">
googletag.cmd.push(function() {
googletag.display('div-gpt-ad-billboard_1');});
</script>
</div>
As i understand it you cannot directly call script tags in blazor components, but they work if you add the suppress-error, and i can atleast get it to console log from them.
The head is the same in the blazor app as in the MVC app, as script tag works fine in _Host.cshtml in blazor.
The issue is that for my blazor app the ad component looks like this on the live endpoint:
<div id="div-gpt-ad-billboard_1"><script><!--!-->
googletag.cmd.push(function() { googletag.display('div-gpt-ad-billboard_1');});
</script></div>
Notice that the entire div with the google iframe ID is missing. It's as if it doesn't update anything. I've tried to call statehaschanged from OnInitializedAsync. Nothing happens, and i guess it makes sense, since nothing is probably added to the render tree that statehaschanged activates the rerendering of.
Does anyone have any idea, how i can get it to work like the MVC app ?

Dynamically change the <script> tag src

Dynamically change the src tag
I am integrating Google analytics into my Angular app. It requires to add a google tracking id in the script-src and later. It needs to load at first before anything else.
While it works fine for one env, I wanted to have different Ids for different enviorment.
I am trying something like:
<script>
var googleTrackingId;
switch (window.location.hostname) {
case 'mycompany.com':
googleTrackingId = 'UA-123123-3'; // production
break;
case 'staging.mycompany.com':
googleTrackingId = 'UA-123123-2'; // staging
break;
default:
googleTrackingId = 'UA-123123-1'; // localhost
}
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=${googleTrackingId}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', googleTrackingId);
</script>
But the src tag is not changing.
You can find the desired tracking ID based on hostname first, then create the <script> tag.
<script>
var googleTrackingId;
switch (window.location.hostname) {
case 'mycompany.com':
googleTrackingId = 'UA-123123-3'; // production
break;
case 'staging.mycompany.com':
googleTrackingId = 'UA-123123-2'; // staging
break;
default:
googleTrackingId = 'UA-123123-1'; // localhost
}
document.write( '<scr' + 'ipt async src="https://www.googletagmanager.com/gtag/js?id=' + googleTrackingId + '"></scr' + 'ipt>' );
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', googleTrackingId);
</script>
Using Javascript, you can get the <script> element by ID, and append whatever you want to the element's src attribute.
Edit: You can alter the script element immediately when it is loaded, using the script's onload event handler:
var analyticsScript = document.getElementById('analytics');
analyticsScript.onload = function() {
analyticsScript.src += '?id=3wrwerf45r3t36y645y4';
console.log(analyticsScript.src);
}
<script src='https://some_source' id="analytics"></script>
Why your approach does not work
In your HTML code, you are trying to use template literals here:
<script async src="https://www.googletagmanager.com/gtag/js?id=${googleTrackingId}"></script>
However, template literals work in Javascript, not in HTML (at least not to my knowledge). Also, template literals are wrapped inside backticks (`), not double quotes (") !

Invalid credentials (missing or invalid oAuth token) on google plus share interactive post using javascript render method

Everything working fine.But while i click browse people icon in google plus share interactive post it just shows Invalid credentials (missing or invalid oAuth token). As mentioned in this image
And i have used following code for interactive post.
<html>
<head>
<title>Share Demo: Deferred execution with language code</title>
<link rel="canonical" href="http://www.example.com" />
</head>
<body>
<script>
window.___gcfg = {
lang: 'en-US',
parsetags: 'explicit'
};
</script>
<script type="text/javascript" src="http://apis.google.com/js/plusone.js"></script>
<div id ="sharePost">Share</div>
<script>
(function() {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'http://apis.google.com/js/client:platform.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
var options = {
contenturl: 'http://www.google.com',
contentdeeplinkid: '/pages',
clientid: 'xxxxxxxxxxxx.apps.googleusercontent.com',
cookiepolicy: 'single_host_origin',
prefilltext: 'Hai happy friday'+ Math.random(),
calltoactionlabel: 'INVITE',
calltoactionurl: 'http://www.google.com'
};
// Call the render method when appropriate within your app to display
// the button.
gapi.interactivepost.render('sharePost', options);
</script>
</body>
</html>
google's doc are up to date. Implementing interactive post is easy and well explain.
If it doesn't work :
it's a bug in Google code (i think it's that here)
It's a bug in Google doc which is not up to date
In all case the culprit is Google ! :)

inmobi javascript return empty banner

I want to get a test ad banner from inmobi. According to inmobi Developer Wiki
http://developer.inmobi.com/wiki/index.php?title=JavaScript
this script
<script type="text/javascript">
var inmobi_conf = {
siteid : "4028cba631d63df10131e1d3191d00cb",
slot : "15",
test: "true"
};
</script><script type="text/javascript" src="http://cf.cdn.inmobi.com/ad/inmobi.js"></script>
must return test banner 320x50, but it always returns an empty banner.
Please help. What am I doing wrong?
You're getting "No-Fill Response". From that link you provided:
For example, if a publisher faces an NFR (No-Fill Response) scenario,
a callback is sent notifying that there is an NFR. The publisher can
now take steps to address and blank real estate issue that was caused
by non-availability of ads.
<div id="my-ad-slot">
<script type="text/javascript">
var inmobi_conf = {
siteid : "your site id",
slot : "slot number",
test: true,
onError : function(code) {
if(code == "nfr") {
document.getElementById("my-ad-slot").style.display = "none";
// do something else. call to other ad network or logic to display in-house ads, etc.
}
}
};
</script>
<script type="text/javascript" src="http://cf.cdn.inmobi.com/ad/inmobi.js"></script>
</div>
In the above code example, the parameter code will give nfr when no ads are returned.

How can I change a JavaScript variable using Greasemonkey?

This is the page that I am trying to modify, I want to bypass the countdown timer, how should I write the script?
Is there a way that I can change the variable document.licenseform.btnSubmit.disabled to yes using Greasemonkey?
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>dsfsdf</title>
</head>
<body>
<form name="licenseform" method="post" action="">
<input name="btnSubmit" type="button" value="我同意">
</form>
<SCRIPT language=javascript type=text/javascript>
<!--
var secs = 9;
var wait = secs * 1000;
document.licenseform.btnSubmit.value = "我同意 [" + secs + "]";
document.licenseform.btnSubmit.disabled = true;
for(i = 1; i <= secs; i++)
{
window.setTimeout("Update(" + i + ")", i * 1000);
//这一句很关键,记得参数写法为("update("+i+")",i*1000)
}
window.setTimeout("Timer()", wait);
function Update(num)
{
if(num != secs)
{
printnr = (wait / 1000) - num;
document.licenseform.btnSubmit.value = "我同意 [" + printnr + "]";
}
}
function Timer()
{
document.licenseform.btnSubmit.disabled = false;
document.licenseform.btnSubmit.value = " 我同意 ";
}
-->
</SCRIPT>
</td>
<!--网页中部中栏代码结束-->
</body>
</html>
A more secure alternative to using unsafeWindow is to inject code into the document. The code that you inject will run in the same context as the page code, so it will have direct access to all of the variables there. But it will not have access to variables or functions in other parts of your user script code.
Another benefit of injecting code is that a user script written that way will work in Chrome as well as in Firefox. Chrome does not support unsafeWindow at all.
My favorite way to inject code is to write a function, then to use this reusable code to get back the source code for the function:
// Inject function so that in will run in the same context as other
// scripts on the page.
function inject(func) {
var source = func.toString();
var script = document.createElement('script');
// Put parenthesis after source so that it will be invoked.
script.innerHTML = "("+ source +")()";
document.body.appendChild(script);
}
To toggle btnSubmit you could write a script like this:
function enableBtnSubmit() {
document.licenseform.btnSubmit.disabled = false;
document.licenseform.btnSubmit.value = " 我同意 ";
// Or just invoke Timer()
}
function inject(func) {
var source = func.toString();
var script = document.createElement('script');
script.innerHTML = "("+ source +")()";
document.body.appendChild(script);
}
inject(enableBtnSubmit);
Remember that when you use the serialized form of a function in this way normal closure scope will not work. The function that you inject will not have access to variables in your script unless they are defined inside that function.
try calling the Timer() function since its what you want to happen anyway:
unsafeWindow.Timer();
while you are at it, change the Update function to do nothing:
unsafeWindow.update = function(){}
This is possible. The short answer is you can use the object unsafeWindow, for instance
unsafeWindow.document.licenseform.btnSubmit.disabled = true;
However it is not recomemended to do so, because it is unsecure. More information about this here:
http://wiki.greasespot.net/UnsafeWindow
Disregard anything said about "insecure", because script->document write operation IS perfectly secure.
unsafeWindow.document.licenseform.btnSubmit.disabled = false;
(Use mkoryak's method to suppress timeout callback)
That given form contains nothing but timeout, so you might want to bypass it completely:
// this example is INSECURE
unsafeWindow.document.licenseform.submit();
See?

Categories

Resources