How to exclude localhost session recordings on Microsoft Clarity? - ms-clarity

How to exclude development sessions from Clarity recordings? I couldn't find any options in their settings.
Also, is it possible to restrict session recordings to certain countries?

The easiest way is to not include the script on localhost with a simple check on the current hostname:
if (!window.location.host.includes('localhost')) {
// code
}
Full snippet like this:
<script type="text/javascript">
if (!window.location.host.includes('localhost')) {
(function (c, l, a, r, i, t, y) {
if(window.location.host.includes('localhost'))
c[a] = c[a] || function () { (c[a].q = c[a].q || []).push(arguments) };
t = l.createElement(r); t.async = 1; t.src = "https://www.clarity.ms/tag/" + i;
y = l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t, y);
})(window, document, "clarity", "script", "XXXXXXX");
}
</script>
(Replace XXXXX with your key/code from clarity)

I have since switched to using Google Tag Manager for managing all the tags. GTM has an option to fire a tag based on hostname pattern match. I have set all tags to fire on production host only.

Related

What does the `cx` parameter do in Gtag implementations?

I have been debugging an issue with Google Analytics 4 where when a GA4 property is connected to an existing GA3 gtag property, and the request to fetch the JavaScript from GTM contains the parameter cx=c, the resulting JavaScript does not contain the required child container for GA4.
https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXX-1&l=dataLayer&cx=c
The obvious fix is to remove the cx=c parameter and test, or to use another implementation of GA4 directly, but that is not the question.
After seeing the suggested implementation from Google, (e.g. no cx) and this version (with the cx) passed around in various online discussions, Github code and issues, etc. I am trying to figure out what it does. I have searched all Google docs, searched Github and Stack Overflow. I find it referenced with respect to Firebase, but nothing else. Does anyone know what this parameter does, officially?
Short answer: we have no clue what it does.
Longer answer: as you may know, GTM can deploy gtag. Since it can do that, it has the code for generating that function. Although that code is minified, it can still be useful:
var Fn = function (a, b, c) {
if (!En() && !sg(a)) {
var d = c ? "/gtag/js" : "/gtm.js", e = "?id=" + encodeURIComponent(a) + "&l=" + Cd.ba,
f = 0 === a.indexOf("GTM-");
f || (e += "&cx=c"); var g = Dn();
g && (e += "&sign=" + Cd.Xd);
var l = Bn(b, d + e);
if (!l) {
var m = Cd.uc + d; g && bb && f && (m = bb.replace(/^(?:https?:\/\/)?/i, "").split(/[?#]/)[0]);
l = Ri("https://", "http://", m + e)
} rg().container[a] = !0; hb(l)
}
}
See what it does? f indicates whether there is "GTM-" in a or not. And I didn't look up a, but a must be the fetch url. Now, f becomes a binary representation instead of the -1 returned by the indexOf.
Judging from this code, GTM deploys the cx parameter with a very hardcoded value whenever it deploys something that is not GTM, I imagine, from it's own family of fetch urls, which can include GTM itself. Why GTM doesn't want cx=c for its code? No idea. I have it loaded with it more often than without.
But here's another place where it's used:
Gn = function (a, b) {
var c; if (c = !En()) c = !rg().destination.hasOwnProperty(a); if (c) {
var d = "/gtag/destination?id=" + encodeURIComponent(a) + "&l=" + Cd.ba + "&cx=c";
Dn() && (d += "&sign=" + Cd.Xd); var e = Bn(b, d); e || (e = Ri("https://", "http://", Cd.uc + d)); rg().destination[a] = !0; hb(e)
}
};
En() is always false. It looks like it's some weird remnants from GTM's dev environments. Poor env variables management on GTM's side, but no one cares.
I stopped looking into the Gn function at this point. But if you want to persist digging, you're free to do so. I have a sneaking suspicion that the cx parameter does exactly no difference on the side of the consumer (our side). It may be something beneficial for Google's dev team, but I doubt it. Params, hardcoded like this are rarely a good idea. Looks to me like a fluke.

dataLayer with multiple rows is Possible?

Hi I need send multiple orders from a backoffice process. But Only send the last.
My code look like this
Best Regards And Thx in advance
<head>
...
<script type="text/javascript">
dataLayer = [];
dataLayer.push({"orderID":"6488jmk","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-382","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""});
dataLayer.push({"orderID":"7209all","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-385","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""});
dataLayer.push({"orderID":"6723drw","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-379","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""});
dataLayer.push({"orderID":"6488jmk","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-382","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""});
dataLayer.push({"orderID":"6723drw","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-379","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""});
</script>
<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-XXXXX99');</script>
</head>
It is "possible" in the sense that is does not cause errors. However, with identical keys each new row will overwrite the previous one, and you will only to be able the data that is pushed immediately before the first "event" key (in your example the native gtm.js/"container loaded" event). This is almost certainly not what you want.
You would need to push a nested array to the dataLayer:
items = [
{"orderID":"6488jmk","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-382","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""},
{"orderID":"7209all","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-385","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""},
{"orderID":"6723drw","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-379","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""},
{"orderID":"6488jmk","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-382","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""},
{"orderID":"6723drw","precio":"2.00","plataforma":"PAYMENTPLATFORM","moneda":"EUR","campana":"CLIENT","usuario":"TESTING-379","tipo_pago":"Pago TESTING","proveedor":"CLIENT","tracking":"","affiliateid":""}
];
dataLayer.push(items);
Within GTM, you'd need to iterate over the array in a custom HTML tag or something built via a custom template.

Robot Framework: Timed out waiting for page load

We have run our Robot Framework environment nearly one year without any problems, but now we get the error message:
TimeoutException: Message: Timed out waiting for page load.
Stacktrace:
at Utils.initWebLoadingListener/< (file:///tmp/tmp77bOby/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/driver-component.js:9089)
at WebLoadingListener/e (file:///tmp/tmp77bOby/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/driver-component.js:5145)
at WebLoadingListener/< (file:///tmp/tmp77bOby/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/driver-component.js:5153)
at fxdriver.Timer.prototype.setTimeout/<.notify (file:///tmp/tmp77bOby/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/driver-component.js:625)
What could be problem? I checked the disk space and there are disk space left.
A timeout error can have a laundry list of possible issues. Loading a page after a year of smooth operation narrows it down a little, but there are still many possibilities. Connection speeds sometimes change, sometimes the page you're loading up has added features that cause it to take longer to load or was just written poorly so it loads slowly... you get the idea. Without the code of the page or your code to look at, I can only suggest two fixes.
First, in the code of Selenium2Library, there is a timeout variable somewhere that can be set with Set Selenium Timeout. The default is 5 seconds, but if your page is taking longer to load, then increasing it might solve your problem. This assumes that your page loads at a reasonable rate in manual testing and that opening it is the least of your concerns.
Second, it's possible that you're testing an AngularJS application, not a normal website. If that's true, then you're going to want to use ExtendedSelenium2Library, not Selenium2Library. ExtendedSelenium2Library is better-equipped to deal with AngularJS applications and includes code to wait for Angular applications to load.
The old webdriver.xpi is buggy about page load handling. Timers are not correctly canceled, resulting in random windows switches and memory leaks. I copy here some replacement code that may be useful to anybody.
var WebLoadingListener = function(a, b, c, d) {
if ("none" == Utils.getPageLoadStrategy()) {
b(!1, !0);
} else {
this.logger = fxdriver.logging.getLogger("fxdriver.WebLoadingListener");
this.loadingListenerTimer = new fxdriver.Timer;
this.browser = a;
var self = this;
var e = function(a, c) {
self.destroy ();
b(a, c);
};
this.handler = buildHandler(a, e, d);
a.addProgressListener(this.handler);
-1 == c && (c = 18E5);
this.loadingListenerTimer.setTimeout(function() {
e(!0);
}, c);
WebLoadingListener.listeners [this.handler] = this;
goog.log.warning(this.logger, "WebLoadingListener created [" + Object.keys (WebLoadingListener.listeners).length + "] " + d.document.location);
}
};
WebLoadingListener.listeners = {};
WebLoadingListener.removeListener = function(a, b) {
if (b.constructor !== WebLoadingListener) {
b = WebLoadingListener.listeners [b];
}
b.destroy ();
};
WebLoadingListener.prototype.destroy = function() {
if (this.browser) {
this.loadingListenerTimer.cancel();
this.browser.removeProgressListener && this.handler && this.browser.removeProgressListener(this.handler);
delete WebLoadingListener.listeners [this.handler]
this.loadingListenerTimer = undefined;
this.browser = undefined;
goog.log.warning(this.logger, "WebLoadingListener destroyed [" + Object.keys (WebLoadingListener.listeners).length + "]");
}
};
enter code here

Getting errors when Browsery bundle with SquishIt

I am currently refactoring the javascript portions of a web site, and now I have bundled some scripts together using Browserify. The resulting script is bundled along with other resources using SquishIt. In Debug mode, when SquishIt is not bundling all the scripts together everything seems to work just fine, but when running in Production, and SquishIt bundles everything together I get errors from the Browserify part of my bundle. The error is complaining that r has no length property (see line 18) below. This part of the code is created by Browserify when bundling the scripts.
(function e(t, n, r) {
function s(o, u) {
if (!n[o]) {
if (!t[o]) {
var a = typeof require == "function" && require;
if (!u && a) return a(o, !0);
if (i) return i(o, !0);
var f = new Error("Cannot find module '" + o + "'");
throw f.code = "MODULE_NOT_FOUND", f
}
var l = n[o] = {
exports: {}
};
t[o][0].call(l.exports, function(e) {
var n = t[o][1][e];
return s(n ? n : e)
}, l, l.exports, e, t, n, r)
}
return n[o].exports
}
var i = typeof require == "function" && require;
for (var o = 0; o < r.length; o++) s(r[o]);
return s
})({
I really can't think of anything that using SquishIt to bundle all the scripts would break the logic of the browserified scripts. What could be the cause of this? This gist shows the entire source code, in case that is relevant.
I have not changed anything on the ASP.NET side (in the bundling), and the relevant part of my ´Head.ascx´ looks like this:
Bundle.JavaScript()
.Add(Assets.JavaScript.GetUrl("main.js").ToString())
.Add(Assets.JavaScript.GetUrl("Plugins/raphael-min.js").ToString())
.Add(Assets.JavaScript.GetUrl("Plugins/vector_map.js").ToString())
// more ...
.Render("~/Content/"+Assets.VersionString+"/Scripts/Combined/combined.js")
Have a look at this comment it may help https://github.com/jetheredge/SquishIt/issues/320#issuecomment-139921409
Is there a reason you need to use two different bundling solutions?

How to check for the existence of an IIS 7 web site via WiX 3.5?

Note: This question can also be found on the WiX mailing list.
I need to be able to check for the existence of an IIS7 website based on the website's description. If the website does not exist I need to cancel the installation. If the website exists I want to continue the installation. I also need to be able to save the site id of the website so that I may use it during an uninstall.
For debugging purposes I have hard coded the website's description. I do not see any indication that a check for the website is being made within the MSI log file. This is the code I am using:
<iis:WebSite Id="IISWEBSITE" Description="Default Web Site" SiteId="*">
<iis:WebAddress Id="IisWebAddress" Port="1"/>
</iis:WebSite>
<Condition Message="Website [IISWEBSITE] not found.">
<![CDATA[IISWEBSITE]]>
</Condition>
Using ORCA I can see that IIsWebAddress and IIsWebSite tables are added to the MSI. The values are:
IIsWebsite
WEB: IISWEBSITE
Description: Default Web Site
KeyAddress: IisWebAddress
Id: -1
IIsWebAddress
Address: IisWebAddress
Web_: IISWEBSITE
Port: 1
Secure: 0
With the above code, the installation is halted with the error message "Website not found". It appears that IISWEBSITE is never getting set. Though, I know that "Default Web Site" exists. I know that I must be missing something, but what?
How can I perform a simple check for the existence of a website in IIS 7?
I too had same problem.
I wrote a custom action to check the version of IIS from registry.
On the basis of registry value create virtual directory
I wrote a custom action in Javascript to do this. If you are assuming IIS7, then you can use the appcmd.exe tool, and just invoke it from within Javascript to get the list of sites. In theory, it's pretty simple to do. But in practice, there's a bunch of hoops you need to jump through.
Here's what I came up with:
function RunAppCmd(command, deleteOutput) {
var shell = new ActiveXObject("WScript.Shell"),
fso = new ActiveXObject("Scripting.FileSystemObject"),
tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder),
tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName()),
windir = fso.GetSpecialFolder(SpecialFolders.WindowsFolder),
appcmd = fso.BuildPath(windir,"system32\\inetsrv\\appcmd.exe") + " " + command,
rc;
deleteOutput = deleteOutput || false;
LogMessage("shell.Run("+appcmd+")");
// use cmd.exe to redirect the output
rc = shell.Run("%comspec% /c " + appcmd + "> " + tmpFileName, WindowStyle.Hidden, true);
LogMessage("shell.Run rc = " + rc);
if (deleteOutput) {
fso.DeleteFile(tmpFileName);
}
return {
rc : rc,
outputfile : (deleteOutput) ? null : tmpFileName
};
}
// GetWebSites_Appcmd()
//
// Gets website info using Appcmd.exe, only on IIS7+ .
//
// The return value is an array of JS objects, one per site.
//
function GetWebSites_Appcmd() {
var r, fso, textStream, sites, oneLine, record,
ParseOneLine = function(oneLine) {
// split the string: capture quoted strings, or a string surrounded
// by parens, or lastly, tokens separated by spaces,
var tokens = oneLine.match(/"[^"]+"|\(.+\)|[^ ]+/g),
// split the 3rd string: it is a set of properties separated by colons
props = tokens[2].slice(1,-1),
t2 = props.match(/\w+:.+?(?=,\w+:|$)/g),
bindingsString = t2[1],
ix1 = bindingsString.indexOf(':'),
t3 = bindingsString.substring(ix1+1).split(','),
L1 = t3.length,
bindings = {}, i, split, obj, p2;
for (i=0; i<L1; i++) {
split = t3[i].split('/');
obj = {};
if (split[0] == "net.tcp") {
p2 = split[1].split(':');
obj.port = p2[0];
}
else if (split[0] == "net.pipe") {
p2 = split[1].split(':');
obj.other = p2[0];
}
else if (split[0] == "http") {
p2 = split[1].split(':');
obj.ip = p2[0];
if (p2[1]) {
obj.port = p2[1];
}
obj.hostname = "";
}
else {
p2 = split[1].split(':');
obj.hostname = p2[0];
if (p2[1]) {
obj.port = p2[1];
}
}
bindings[split[0]] = obj;
}
// return the object describing the website
return {
id : t2[0].split(':')[1],
name : "W3SVC/" + t2[0].split(':')[1],
description : tokens[1].slice(1,-1),
bindings : bindings,
state : t2[2].split(':')[1] // started or not
};
};
LogMessage("GetWebSites_Appcmd() ENTER");
r = RunAppCmd("list sites");
if (r.rc !== 0) {
// 0x80004005 == E_FAIL
throw new Exception("ApplicationException", "exec appcmd.exe returned nonzero rc ("+r.rc+")", 0x80004005);
}
fso = new ActiveXObject("Scripting.FileSystemObject");
textStream = fso.OpenTextFile(r.outputfile, OpenMode.ForReading);
sites = [];
// Read from the file and parse the results.
while (!textStream.AtEndOfStream) {
oneLine = textStream.ReadLine();
record = ParseOneLine(oneLine);
LogMessage(" site: " + record.name);
sites.push(record);
}
textStream.Close();
fso.DeleteFile(r.outputfile);
LogMessage("GetWebSites_Appcmd() EXIT");
return sites;
}

Resources