Skip to content

Commit 0c1fbe4

Browse files
authored
Merge pull request #3674 from betagouv/matomo-opt-out
Possibilité de se désincrire au suivi Matomo
2 parents cb111b9 + 56c7432 commit 0c1fbe4

4 files changed

Lines changed: 185 additions & 4 deletions

File tree

frontend/src/router/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import PartnerPage from "@/views/PartnersPage/PartnerPage"
2626
import NewPartner from "@/views/NewPartner"
2727
import NotFound from "@/views/NotFound"
2828
import CGU from "@/views/CGU.vue"
29-
import PrivacyPolicy from "@/views/PrivacyPolicy.vue"
29+
import PrivacyPolicy from "@/views/PrivacyPolicy"
3030
import ManagementPage from "@/views/ManagementPage"
3131
import PendingActions from "@/views/PendingActions"
3232
import CanteenEditor from "@/views/CanteenEditor"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<template>
2+
<div class="pa-2 rounded-sm my-4" style="border: solid 1px #e0e0e0;">
3+
<div id="matomo-opt-out"></div>
4+
</div>
5+
</template>
6+
7+
<script>
8+
export default {
9+
mounted() {
10+
window.showContent(window.MatomoConsent.hasConsent())
11+
},
12+
}
13+
</script>
14+
15+
<style scoped>
16+
#matomo-opt-out >>> label {
17+
margin-left: 8px;
18+
}
19+
#matomo-opt-out >>> p {
20+
margin: 0;
21+
}
22+
</style>

frontend/src/views/PrivacyPolicy.vue renamed to frontend/src/views/PrivacyPolicy/index.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,9 @@
280280
<li>
281281
<span class="font-weight-bold">Matomo</span>
282282
, cookie de mesure d’audience, dans sa version exemptée de consentement préalable, telle que validée par la
283-
CNIL.
283+
CNIL. Vous pouvez gérer votre cookie Matomo ci-dessous :
284+
285+
<MatomoOptOut />
284286
</li>
285287
<li>
286288
<span class="font-weight-bold">Crisp</span>
@@ -318,10 +320,10 @@
318320

319321
<script>
320322
import BreadcrumbsNav from "@/components/BreadcrumbsNav"
321-
323+
import MatomoOptOut from "./MatomoOptOut"
322324
export default {
323325
name: "PrivacyPolicy",
324-
components: { BreadcrumbsNav },
326+
components: { BreadcrumbsNav, MatomoOptOut },
325327
}
326328
</script>
327329

web/templates/vue-app.html

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,163 @@
4040
window.TELEDECLARATION_END_DATE = "{% teledeclaration_end_date %}" || null
4141
window.IS_WIDGET = "{{ is_widget }}"
4242
</script>
43+
44+
<!-- Matomo opt-out settings -->
45+
<script>
46+
var settings = {
47+
showIntro: false,
48+
divId: "matomo-opt-out",
49+
useSecureCookies: true,
50+
cookiePath: null,
51+
cookieDomain: null,
52+
cookieSameSite: "Lax",
53+
OptOutComplete: "Désinscription prise en compte ; vos visites ne seront pas enregistrées par l'outil Web Analytics",
54+
OptOutCompleteBis:
55+
"Si vous effacez vos cookies, supprimez le cookie de désinscription ou changez d'ordinateur ou de navigateur Web, vous devrez refaire la procédure de désinscription.",
56+
YouMayOptOut2:
57+
"Vous pouvez choisir d'empêcher ce site d'analyser vos actions.",
58+
YouMayOptOut3:
59+
"Cela protégera votre vie privée, mais empêchera également le ou la propriétaire d'apprendre de vos actions et de créer une meilleure expérience.",
60+
OptOutErrorNoCookies: "La fonction de désinscription du suivi nécessite que les cookies soient activés.",
61+
OptOutErrorNotHttps:
62+
"La fonction de désinscription du suivi peut ne pas fonctionner correctement car ce site n'a pas été chargé via HTTPS. Veuillez recharger la page pour vérifier si votre statut de désinscription a changé.",
63+
YouAreNotOptedOut: "Vous n'êtes pas désinscrit ou désinscrite de notre suivi Matomo.",
64+
UncheckToOptOut: "Décochez cette case pour vous désinscrire.",
65+
YouAreOptedOut: "Vous êtes actuellement désinscrit ou désinscrite de notre suivi Matomo.",
66+
CheckToOptIn: "Cochez cette case pour vous inscrire.",
67+
}
68+
69+
function showContent(consent, errorMessage = null, useTracker = false) {
70+
var errorBlock = '<p style="font-style: italic;">'
71+
72+
var div = document.getElementById(settings.divId)
73+
if (!div) {
74+
const warningDiv = document.createElement("div")
75+
var msg = 'Le div conteneur de la désinscription est introuvable : "' + settings.divId + '"'
76+
warningDiv.id = settings.divId + "-warning"
77+
warningDiv.innerHTML = errorBlock + msg + "</p>"
78+
document.body.insertBefore(warningDiv, document.body.firstChild)
79+
console.log(msg)
80+
return
81+
}
82+
83+
if (!navigator || !navigator.cookieEnabled) {
84+
div.innerHTML = errorBlock + settings.OptOutErrorNoCookies + "</p>"
85+
return
86+
}
87+
if (location.protocol !== "https:") {
88+
div.innerHTML = errorBlock + settings.OptOutErrorNotHttps + "</p>"
89+
return
90+
}
91+
if (errorMessage !== null) {
92+
div.innerHTML = errorBlock + errorMessage + "</p>"
93+
return
94+
}
95+
var content = ""
96+
if (consent) {
97+
if (settings.showIntro) {
98+
content += "<p>" + settings.YouMayOptOut2 + " " + settings.YouMayOptOut3 + "</p>"
99+
}
100+
if (useTracker) {
101+
content +=
102+
'<input onclick="_paq.push([\'optUserOut\']);showContent(false, null, true);" id="trackVisits" type="checkbox" checked="checked" />'
103+
} else {
104+
content +=
105+
'<input onclick="window.MatomoConsent.consentRevoked();showContent(false);" id="trackVisits" type="checkbox" checked="checked" />'
106+
}
107+
content +=
108+
'<label for="trackVisits"><span>' +
109+
settings.YouAreNotOptedOut +
110+
" " +
111+
settings.UncheckToOptOut +
112+
"</span></label>"
113+
} else {
114+
if (settings.showIntro) {
115+
content += "<p>" + settings.OptOutComplete + " " + settings.OptOutCompleteBis + "</p>"
116+
}
117+
if (useTracker) {
118+
content +=
119+
'<input onclick="_paq.push([\'forgetUserOptOut\']);showContent(true, null, true);" id="trackVisits" type="checkbox" />'
120+
} else {
121+
content +=
122+
'<input onclick="window.MatomoConsent.consentGiven();showContent(true);" id="trackVisits" type="checkbox" />'
123+
}
124+
content +=
125+
'<label for="trackVisits"><span>' +
126+
settings.YouAreOptedOut +
127+
" " +
128+
settings.CheckToOptIn +
129+
"</span></label>"
130+
}
131+
div.innerHTML = content
132+
}
133+
134+
window.MatomoConsent = {
135+
cookiesDisabled: !navigator || !navigator.cookieEnabled,
136+
CONSENT_COOKIE_NAME: "mtm_consent",
137+
CONSENT_REMOVED_COOKIE_NAME: "mtm_consent_removed",
138+
cookieIsSecure: false,
139+
useSecureCookies: true,
140+
cookiePath: "",
141+
cookieDomain: "",
142+
cookieSameSite: "Lax",
143+
init: function(useSecureCookies, cookiePath, cookieDomain, cookieSameSite) {
144+
this.useSecureCookies = useSecureCookies
145+
this.cookiePath = cookiePath
146+
this.cookieDomain = cookieDomain
147+
this.cookieSameSite = cookieSameSite
148+
if (useSecureCookies && location.protocol !== "https:") {
149+
console.log("Error with setting useSecureCookies: You cannot use this option on http.")
150+
} else {
151+
this.cookieIsSecure = useSecureCookies
152+
}
153+
},
154+
hasConsent: function() {
155+
var consentCookie = this.getCookie(this.CONSENT_COOKIE_NAME)
156+
var removedCookie = this.getCookie(this.CONSENT_REMOVED_COOKIE_NAME)
157+
if (!consentCookie && !removedCookie) {
158+
return true // No cookies set, so opted in
159+
}
160+
if (removedCookie && consentCookie) {
161+
this.setCookie(this.CONSENT_COOKIE_NAME, "", -129600000)
162+
return false
163+
}
164+
return consentCookie || consentCookie !== 0
165+
},
166+
consentGiven: function() {
167+
this.setCookie(this.CONSENT_REMOVED_COOKIE_NAME, "", -129600000)
168+
this.setCookie(this.CONSENT_COOKIE_NAME, new Date().getTime(), 946080000000)
169+
},
170+
consentRevoked: function() {
171+
this.setCookie(this.CONSENT_COOKIE_NAME, "", -129600000)
172+
this.setCookie(this.CONSENT_REMOVED_COOKIE_NAME, new Date().getTime(), 946080000000)
173+
},
174+
getCookie: function(cookieName) {
175+
var cookiePattern = new RegExp("(^|;)[ ]*" + cookieName + "=([^;]*)"),
176+
cookieMatch = cookiePattern.exec(document.cookie)
177+
return cookieMatch ? window.decodeURIComponent(cookieMatch[2]) : 0
178+
},
179+
setCookie: function(cookieName, value, msToExpire) {
180+
var expiryDate = new Date()
181+
expiryDate.setTime(new Date().getTime() + msToExpire)
182+
document.cookie =
183+
cookieName +
184+
"=" +
185+
window.encodeURIComponent(value) +
186+
(msToExpire ? ";expires=" + expiryDate.toGMTString() : "") +
187+
";path=" +
188+
(this.cookiePath || "/") +
189+
(this.cookieDomain ? ";domain=" + this.cookieDomain : "") +
190+
(this.cookieIsSecure ? ";secure" : "") +
191+
";SameSite=" +
192+
this.cookieSameSite
193+
if ((!msToExpire || msToExpire >= 0) && this.getCookie(cookieName) !== String(value)) {
194+
console.log("There was an error setting cookie `" + cookieName + "`. Please check domain and path.")
195+
}
196+
},
197+
}
198+
</script>
199+
43200
{% if is_widget != True %}
44201
<script type="text/javascript">
45202
window.$crisp = [];

0 commit comments

Comments
 (0)