Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'js/piwik.js')
-rw-r--r--js/piwik.js200
1 files changed, 182 insertions, 18 deletions
diff --git a/js/piwik.js b/js/piwik.js
index d15eac4b1b..e6db6e4848 100644
--- a/js/piwik.js
+++ b/js/piwik.js
@@ -36,7 +36,8 @@
/*members Piwik, Matomo, encodeURIComponent, decodeURIComponent, getElementsByTagName,
shift, unshift, piwikAsyncInit, matomoAsyncInit, matomoPluginAsyncInit , frameElement, self, hasFocus,
createElement, appendChild, characterSet, charset, all,
- addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies,
+ addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies, setCookieConsentGiven,
+ areCookiesEnabled, getRememberedCookieConsent, rememberCookieConsentGiven, forgetCookieConsentGiven, requireCookieConsent,
cookie, domain, readyState, documentElement, doScroll, title, text, contentWindow, postMessage,
location, top, onerror, document, referrer, parent, links, href, protocol, name,
performance, mozPerformance, msPerformance, webkitPerformance, timing, connectEnd, requestStart, responseStart,
@@ -2136,6 +2137,7 @@ if (typeof window.Matomo !== 'object') {
// constants
CONSENT_COOKIE_NAME = 'mtm_consent',
+ COOKIE_CONSENT_COOKIE_NAME = 'mtm_cookie_consent',
CONSENT_REMOVED_COOKIE_NAME = 'mtm_consent_removed',
// Current URL and Referrer URL
@@ -2370,7 +2372,10 @@ if (typeof window.Matomo !== 'object') {
consentRequestsQueue = [],
// a unique ID for this tracker during this request
- uniqueTrackerId = trackerIdCounter++;
+ uniqueTrackerId = trackerIdCounter++,
+
+ // whether a tracking request has been sent yet during this page view
+ hasSentTrackingRequestYet = false;
// Document title
try {
@@ -2952,6 +2957,9 @@ if (typeof window.Matomo !== 'object') {
consentRequestsQueue.push(request);
return;
}
+
+ hasSentTrackingRequestYet = true;
+
if (!configDoNotTrack && request) {
if (configConsentRequired && configHasConsent) { // send a consent=1 when explicit consent is given for the apache logs
request += '&consent=1';
@@ -3018,6 +3026,8 @@ if (typeof window.Matomo !== 'object') {
return;
}
+ hasSentTrackingRequestYet = true;
+
makeSureThereIsAGapAfterFirstTrackingRequestToPreventMultipleVisitorCreation(function () {
var chunks = arrayChunk(requests, 50);
@@ -3126,8 +3136,12 @@ if (typeof window.Matomo !== 'object') {
browserFeatures.java = '1';
}
- // other browser features
- browserFeatures.cookie = hasCookies();
+ if (!isDefined(windowAlias.showModalDialog) && isDefined(navigatorAlias.cookieEnabled)) {
+ browserFeatures.cookie = navigatorAlias.cookieEnabled ? '1' : '0';
+ } else {
+ // Eg IE11 ... prevent error when cookieEnabled is requested within modal dialog. see #11507
+ browserFeatures.cookie = hasCookies();
+ }
}
var width = parseInt(screenAlias.width, 10);
@@ -3432,7 +3446,6 @@ if (typeof window.Matomo !== 'object') {
function setSiteId(siteId) {
configTrackerSiteId = siteId;
- setVisitorIdCookie();
}
function sortObjectByKeys(value) {
@@ -4818,9 +4831,6 @@ if (typeof window.Matomo !== 'object') {
this.getContent = function () {
return content;
};
- this.setVisitorId = function (visitorId) {
- visitorUUID = visitorId;
- };
this.buildContentImpressionRequest = buildContentImpressionRequest;
this.buildContentInteractionRequest = buildContentInteractionRequest;
@@ -5048,6 +5058,22 @@ if (typeof window.Matomo !== 'object') {
};
/**
+ * Sets a Visitor ID to this visitor. Should be a 16 digit hex string.
+ * The visitorId won't be persisted in a cookie or something similar and needs to be set every time.
+ *
+ * @param string User ID
+ */
+ this.setVisitorId = function (visitorId) {
+ var validation = /[0-9A-Fa-f]{16}/g;
+
+ if (isString(visitorId) && validation.test(visitorId)) {
+ visitorUUID = visitorId;
+ } else {
+ logConsoleError('Invalid visitorId set' + visitorId);
+ }
+ };
+
+ /**
* Gets the User ID if set.
*
* @returns string User ID
@@ -5755,7 +5781,6 @@ if (typeof window.Matomo !== 'object') {
*/
this.disableCookies = function () {
configCookiesDisabled = true;
- browserFeatures.cookie = '0';
if (configTrackerSiteId) {
deleteCookies();
@@ -5763,6 +5788,115 @@ if (typeof window.Matomo !== 'object') {
};
/**
+ * Detects if cookies are enabled or not
+ * @returns {boolean}
+ */
+ this.areCookiesEnabled = function () {
+ return !configCookiesDisabled;
+ };
+
+ /**
+ * Enables cookies if they were disabled previously.
+ */
+ this.setCookieConsentGiven = function () {
+ if (configCookiesDisabled && !configDoNotTrack) {
+ configCookiesDisabled = false;
+ if (configTrackerSiteId && hasSentTrackingRequestYet) {
+ setVisitorIdCookie();
+
+ // sets attribution cookie, and updates visitorId in the backend
+ // because hasSentTrackingRequestYet=true we assume there might not be another tracking
+ // request within this page view so we trigger one ourselves.
+ // if no tracking request has been sent yet, we don't set the attribution cookie cause Matomo
+ // sets the cookie only when there is a tracking request. It'll be set if the user sends
+ // a tracking request afterwards
+ var request = getRequest('ping=1', null, 'ping');
+ sendRequest(request, configTrackerPause);
+ }
+ }
+ };
+
+ /**
+ * When called, no cookies will be set until you have called `setCookieConsentGiven()`
+ * unless consent was given previously AND you called {@link rememberCookieConsentGiven()} when the user
+ * gave consent.
+ *
+ * This may be useful when you want to implement for example a popup to ask for cookie consent.
+ * Once the user has given consent, you should call {@link setCookieConsentGiven()}
+ * or {@link rememberCookieConsentGiven()}.
+ *
+ * If you require tracking consent for example because you are tracking personal data and GDPR applies to you,
+ * then have a look at `_paq.push(['requireConsent'])` instead.
+ *
+ * If the user has already given consent in the past, you can either decide to not call `requireCookieConsent` at all
+ * or call `_paq.push(['setCookieConsentGiven'])` on each page view at any time after calling `requireCookieConsent`.
+ *
+ * When the user gives you the consent to set cookies, you can also call `_paq.push(['rememberCookieConsentGiven', optionalTimeoutInHours])`
+ * and for the duration while the cookie consent is remembered, any call to `requireCoookieConsent` will be automatically ignored
+ * until you call `forgetCookieConsentGiven`.
+ * `forgetCookieConsentGiven` needs to be called when the user removes consent for using cookies. This means if you call `rememberCookieConsentGiven` at the
+ * time the user gives you consent, you do not need to ever call `_paq.push(['setCookieConsentGiven'])` as the consent
+ * will be detected automatically through cookies.
+ */
+ this.requireCookieConsent = function() {
+ if (this.getRememberedCookieConsent()) {
+ return false;
+ }
+ this.disableCookies();
+ return true;
+ };
+
+ /**
+ * If the user has given cookie consent previously and this consent was remembered, it will return the number
+ * in milliseconds since 1970/01/01 which is the date when the user has given cookie consent. Please note that
+ * the returned time depends on the users local time which may not always be correct.
+ *
+ * @returns number|string
+ */
+ this.getRememberedCookieConsent = function () {
+ return getCookie(COOKIE_CONSENT_COOKIE_NAME);
+ };
+
+ /**
+ * Calling this method will remove any previously given cookie consent and it disables cookies for subsequent
+ * page views. You may call this method if the user removes cookie consent manually, or if you
+ * want to re-ask for cookie consent after a specific time period.
+ */
+ this.forgetCookieConsentGiven = function () {
+ deleteCookie(COOKIE_CONSENT_COOKIE_NAME, configCookiePath, configCookieDomain);
+ this.disableCookies();
+ };
+
+ /**
+ * Calling this method will remember that the user has given cookie consent across multiple requests by setting
+ * a cookie named "mtm_cookie_consent". You can optionally define the lifetime of that cookie in hours
+ * using a parameter.
+ *
+ * When you call this method, we imply that the user has given cookie consent for this page view, and will also
+ * imply consent for all future page views unless the cookie expires or the user
+ * deletes all her or his cookies. Remembering cookie consent means even if you call {@link disableCookies()},
+ * then cookies will still be enabled and it won't disable cookies since the user has given consent for cookies.
+ *
+ * Please note that this feature requires you to set the `cookieDomain` and `cookiePath` correctly. Please
+ * also note that when you call this method, consent will be implied for all sites that match the configured
+ * cookieDomain and cookiePath. Depending on your website structure, you may need to restrict or widen the
+ * scope of the cookie domain/path to ensure the consent is applied to the sites you want.
+ *
+ * @param int hoursToExpire After how many hours the cookie consent should expire. By default the consent is valid
+ * for 30 years unless cookies are deleted by the user or the browser prior to this
+ */
+ this.rememberCookieConsentGiven = function (hoursToExpire) {
+ if (hoursToExpire) {
+ hoursToExpire = hoursToExpire * 60 * 60 * 1000;
+ } else {
+ hoursToExpire = 30 * 365 * 24 * 60 * 60 * 1000;
+ }
+ this.setCookieConsentGiven();
+ var now = new Date().getTime();
+ setCookie(COOKIE_CONSENT_COOKIE_NAME, now, hoursToExpire, configCookiePath, configCookieDomain, configCookieIsSecure);
+ };
+
+ /**
* One off cookies clearing. Useful to call this when you know for sure a new visitor is using the same browser,
* it maybe helps to "reset" tracking cookies to prevent data reuse for different users.
*/
@@ -6556,10 +6690,6 @@ if (typeof window.Matomo !== 'object') {
* This may be useful when you want to implement for example a popup to ask for consent before tracking the user.
* Once the user has given consent, you should call {@link setConsentGiven()} or {@link rememberConsentGiven()}.
*
- * Please note that when consent is required, we will temporarily set cookies but not track any data. Those
- * cookies will only exist during this page view and deleted as soon as the user navigates to a different page
- * or closes the browser.
- *
* If you require consent for tracking personal data for example, you should first call
* `_paq.push(['requireConsent'])`.
*
@@ -6574,6 +6704,11 @@ if (typeof window.Matomo !== 'object') {
this.requireConsent = function () {
configConsentRequired = true;
configHasConsent = this.hasRememberedConsent();
+ if (!configHasConsent) {
+ // we won't call this.disableCookies() since we don't want to delete any cookies just yet
+ // user might call `setConsentGiven` next
+ configCookiesDisabled = true;
+ }
// Matomo.addPlugin might not be defined at this point, we add the plugin directly also to make JSLint happy
// We also want to make sure to define an unload listener for each tracker, not only one tracker.
coreConsentCounter++;
@@ -6591,10 +6726,16 @@ if (typeof window.Matomo !== 'object') {
* Call this method once the user has given consent. This will cause all tracking requests from this
* page view to be sent. Please note that the given consent won't be remembered across page views. If you
* want to remember consent across page views, call {@link rememberConsentGiven()} instead.
+ *
+ * It will also automatically enable cookies if they were disabled previously.
+ *
+ * @param bool [setCookieConsent=true] Internal parameter. Defines whether cookies should be enabled or not.
*/
- this.setConsentGiven = function () {
+ this.setConsentGiven = function (setCookieConsent) {
configHasConsent = true;
+
deleteCookie(CONSENT_REMOVED_COOKIE_NAME, configCookiePath, configCookieDomain);
+
var i, requestType;
for (i = 0; i < consentRequestsQueue.length; i++) {
requestType = typeof consentRequestsQueue[i];
@@ -6605,11 +6746,23 @@ if (typeof window.Matomo !== 'object') {
}
}
consentRequestsQueue = [];
+
+ // we need to enable cookies after sending the previous requests as it will make sure that we send
+ // a ping request if needed. Cookies are only set once we call `getRequest`. Above only calls sendRequest
+ // meaning no cookies will be created unless we called enableCookies after at least one request has been sent.
+ // this will cause a ping request to be sent that sets the cookies and also updates the newly generated visitorId
+ // on the server.
+ // If the user calls setConsentGiven before sending any tracking request (which usually is the case) then
+ // nothing will need to be done as it only enables cookies and the next tracking request will set the cookies
+ // etc.
+ if (!isDefined(setCookieConsent) || setCookieConsent) {
+ this.setCookieConsentGiven();
+ }
};
/**
* Calling this method will remember that the user has given consent across multiple requests by setting
- * a cookie. You can optionally define the lifetime of that cookie in milliseconds using a parameter.
+ * a cookie. You can optionally define the lifetime of that cookie in hours using a parameter.
*
* When you call this method, we imply that the user has given consent for this page view, and will also
* imply consent for all future page views unless the cookie expires (if timeout defined) or the user
@@ -6621,6 +6774,9 @@ if (typeof window.Matomo !== 'object') {
* for all sites that match the configured cookieDomain and cookiePath. Depending on your website structure,
* you may need to restrict or widen the scope of the cookie domain/path to ensure the consent is applied
* to the sites you want.
+ *
+ * @param int hoursToExpire After how many hours the consent should expire. By default the consent is valid
+ * for 30 years unless cookies are deleted by the user or the browser prior to this
*/
this.rememberConsentGiven = function (hoursToExpire) {
if (hoursToExpire) {
@@ -6628,7 +6784,10 @@ if (typeof window.Matomo !== 'object') {
} else {
hoursToExpire = 30 * 365 * 24 * 60 * 60 * 1000;
}
- this.setConsentGiven();
+ var setCookieConsent = true;
+ // we currently always enable cookies if we remember consent cause we don't store across requests whether
+ // cookies should be automatically enabled or not.
+ this.setConsentGiven(setCookieConsent);
var now = new Date().getTime();
setCookie(CONSENT_COOKIE_NAME, now, hoursToExpire, configCookiePath, configCookieDomain, configCookieIsSecure);
};
@@ -6644,6 +6803,7 @@ if (typeof window.Matomo !== 'object') {
deleteCookie(CONSENT_COOKIE_NAME, configCookiePath, configCookieDomain);
setCookie(CONSENT_REMOVED_COOKIE_NAME, new Date().getTime(), thirtyYears, configCookiePath, configCookieDomain, configCookieIsSecure);
+ this.forgetCookieConsentGiven();
this.requireConsent();
};
@@ -6665,7 +6825,11 @@ if (typeof window.Matomo !== 'object') {
/**
* Alias for rememberConsentGiven(). After calling this function, the current user will be tracked.
*/
- this.forgetUserOptOut = this.rememberConsentGiven;
+ this.forgetUserOptOut = function () {
+ // we can't automatically enable cookies here as we don't know if user actually gave consent for cookies
+ var setCookieConsent = false;
+ this.rememberConsentGiven(0, setCookieConsent);
+ };
/**
* Mark performance metrics as available, once onload event has finished
@@ -6728,7 +6892,7 @@ if (typeof window.Matomo !== 'object') {
* Constructor
************************************************************/
- var applyFirst = ['addTracker', 'disableCookies', 'setTrackerUrl', 'setAPIUrl', 'enableCrossDomainLinking', 'setCrossDomainLinkingTimeout', 'setSessionCookieTimeout', 'setVisitorCookieTimeout', 'setCookieNamePrefix', 'setSecureCookie', 'setCookiePath', 'setCookieDomain', 'setDomains', 'setUserId', 'setSiteId', 'alwaysUseSendBeacon', 'enableLinkTracking', 'requireConsent', 'setConsentGiven'];
+ var applyFirst = ['addTracker', 'forgetCookieConsentGiven', 'requireCookieConsent', 'disableCookies', 'setTrackerUrl', 'setAPIUrl', 'enableCrossDomainLinking', 'setCrossDomainLinkingTimeout', 'setSessionCookieTimeout', 'setVisitorCookieTimeout', 'setCookieNamePrefix', 'setSecureCookie', 'setCookiePath', 'setCookieDomain', 'setDomains', 'setUserId', 'setVisitorId', 'setSiteId', 'alwaysUseSendBeacon', 'enableLinkTracking', 'setCookieConsentGiven', 'requireConsent', 'setConsentGiven'];
function createFirstTracker(matomoUrl, siteId)
{