feat(tracking): ajout du tracking utilisateur

fix/logos
François Poulain 2020-08-14 19:03:29 +02:00 commité par François Poulain
Parent e5c4039eb8
révision a8fadf1e0a
16 fichiers modifiés avec 818 ajouts et 10 suppressions

Voir le fichier

@ -0,0 +1,18 @@
/**
* Appel une fonction après que le document est chargé.
* @param {Function} fn - La fonction à appeler
* @returns {void}
*/
export default (fn) => {
if (typeof fn !== 'function') {
return;
}
// si le document est déjà chargé, on appel la fonction
if (document.readyState === 'interactive' ||
document.readyState === 'complete') {
fn();
}
document.addEventListener('DOMContentLoaded', fn, false);
};

240
assets/js/opted-tracking.js Normal file
Voir le fichier

@ -0,0 +1,240 @@
import ready from './helpers/ready';
/**
* ------------------------------------------------------------------------
* Constantes
* ------------------------------------------------------------------------
*/
const Message = {
ACTIVE: 'Votre visite sur ce site est actuellement <b>suivie anonymement</b>.',
ACTIVE_HELP: 'Vous pouvez vous opposer au suivi en décochant cette case.',
INACTIVE: 'Votre visite sur ce site n\'est actuellement <b>pas suivie</b>.',
INACTIVE_HELP: 'Vous pouvez activer le suivi anonyme en cochant cette case afin de nous aider à améliorer notre site.',
DONOTTRACK: 'Votre visite sur ce site n\'est <b>pas suivie</b>, conformément aux préférences de votre navigateur.',
DONOTTRACK_HELP: 'La fonctionnalité <i>Do Not Track</i> étant activée dans les préférences de votre navigateur, elle prend le dessus sur le contrôle de votre suivi sur ce site.',
OLDBROWSER_HELP: 'Votre navigateur semble trop vieux pour vous permettre de contrôler votre suivi. Pour plus de sécurité et de confidentialité, veuillez mettre à jour votre navigateur.'
};
const Default = {
key: 'tracking',
initial: true
};
const Selector = {
CONTAINER: '.tracking-optout',
CHECKBOX: 'input[type="checkbox"]',
STATE: '.tracking-state',
HELP: '.tracking-help'
};
/**
* ------------------------------------------------------------------------
* Helpers
* ------------------------------------------------------------------------
*/
/**
* Detects whether localStorage is both supported and available.
* @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#Testing_for_availability
* @returns {Boolean} `true` if localStorage is usable.
*/
function hasLocalStorage() {
let storage;
try {
storage = window.localStorage;
const x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (e) {
return e instanceof DOMException && (
// everything except Firefox
e.code === 22 || // eslint-disable-line no-magic-numbers
// Firefox
e.code === 1014 || // eslint-disable-line no-magic-numbers
// test name field too, because code might not be present
// everything except Firefox
e.name === 'QuotaExceededError' ||
// Firefox
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
// acknowledge QuotaExceededError only if there's something already stored
(storage && storage.length !== 0);
}
}
/**
* Checks whether the user's do-not-track setting is enabled.
* @returns {Boolean} `true` if the user should not be tracked.
*/
function hasDoNotTrack() {
return navigator.doNotTrack === '1' ||
navigator.doNotTrack === 'yes' ||
navigator.msDoNotTrack === '1' ||
window.doNotTrack === '1';
}
/**
* ------------------------------------------------------------------------
* Classe
* ------------------------------------------------------------------------
*/
class OptedTracking {
constructor(fn, config) {
if (typeof fn !== 'function') {
throw new Error('Une fonction est attendue comme premier argument');
}
this._elements = [];
this._config = Object.assign({}, Default, config);
this._dnt = hasDoNotTrack();
this._localStorage = hasLocalStorage();
this._enabled = this.getState();
// exécute la fonction dès que possible
if (this._enabled) {
setTimeout(fn, 0);
}
window.addEventListener('storage', (e) => {
if (e.key === this._config.key) {
this._refresh();
}
});
// attends que la page soit chargée pour initialiser les éléments
ready(() => this._initElements());
}
// Méthodes publiques
getState() {
if (this._dnt) {
return false;
} else if (!this._localStorage) {
return this._config.initial;
}
const state = window.localStorage.getItem(this._config.key);
if (state === 'true') {
return true;
} else if (state === 'false') {
return false;
}
return this._config.initial;
}
setState(enabled) {
window.localStorage.setItem(this._config.key, enabled ? 'true' : 'false');
// l'état étant changé dans ce contexte, il faut rafraichir manuellement
this._refresh();
}
// Méthodes privées
_refresh() {
const enabled = this.getState();
if (this._dnt) {
this._adjustElements(
Message.DONOTTRACK,
Message.DONOTTRACK_HELP,
enabled,
true
);
} else if (!this._localStorage) {
this._adjustElements(
enabled ? Message.ACTIVE : Message.INACTIVE,
Message.OLDBROWSER_HELP,
enabled,
true
);
} else {
this._adjustElements(
enabled ? Message.ACTIVE : Message.INACTIVE,
enabled ? Message.ACTIVE_HELP : Message.INACTIVE_HELP,
enabled,
false
);
}
// si l'état a changé, il faut recharger la page
if (enabled !== this._enabled) {
window.location.reload();
}
}
_initElements() {
this._elements = [...document.querySelectorAll(Selector.CONTAINER)];
this._checkboxes = [];
this._states = [];
this._helps = [];
// parcours tous les conteneurs et ajoute leurs éléments
this._elements.forEach((container) => {
const checkbox = container.querySelector(Selector.CHECKBOX);
const state = container.querySelector(Selector.STATE);
const help = container.querySelector(Selector.HELP);
if (checkbox) {
this._checkboxes.push(checkbox);
}
if (state) {
this._states.push(state);
}
if (help) {
this._helps.push(help);
}
});
this._checkboxes.forEach((checkbox) => {
checkbox.addEventListener('click', () => this.setState(checkbox.checked));
});
this._refresh();
}
_adjustElements(stateText, helpText, checked, disabled) {
if (stateText) {
this._states.forEach((el) => {
el.innerHTML = stateText;
});
}
if (helpText) {
this._helps.forEach((el) => {
el.innerHTML = helpText;
});
}
if (typeof checked === 'boolean') {
this._checkboxes.forEach((el) => {
el.checked = checked;
});
}
if (disabled === true) {
this._checkboxes.forEach((el) => el.setAttribute('disabled', ''));
} else if (disabled === false) {
this._checkboxes.forEach((el) => el.removeAttribute('disabled'));
}
}
}
/**
* ------------------------------------------------------------------------
* Export
* ------------------------------------------------------------------------
*/
export default OptedTracking;
window.optedtracking = function (fn, config) {
return new OptedTracking(fn.bind(window), config);
};

Voir le fichier

@ -0,0 +1,28 @@
# Generated by Django 2.2.15 on 2020-08-14 15:52
from django.db import migrations, models
import django.db.models.deletion
import wagtail.core.fields
class Migration(migrations.Migration):
dependencies = [
('wagtailcore', '0041_group_collection_permissions_verbose_name_plural'),
('base', '0015_bouton_hors_zone_texte'),
]
operations = [
migrations.CreateModel(
name='MatomoSettings',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('privacy_policy', wagtail.core.fields.RichTextField(blank=True, help_text="Texte expliquant aux visiteurs les données qui sont collectées par le site et comment elles sont utilisées. Il est affiché sur la page <i>Politique de confidentialité</i>, qui permet aussi de s'opposer au suivi si le code est défini.", verbose_name='politique de confidentialité')),
('tracking_code', models.TextField(blank=True, help_text='Le code JavaScript à exécuter pour suivre les visiteurs sur le site, sans la balise <code>&lt;script&gt;</code>.', verbose_name='code de suivi')),
('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
],
options={
'verbose_name': 'Suivi des visiteurs',
},
),
]

Voir le fichier

@ -2,13 +2,31 @@ from collections import OrderedDict
from django.contrib import messages
from django.contrib.auth.models import User
from django.http.response import HttpResponseRedirect
from django.http.response import HttpResponseRedirect, Http404
from django.shortcuts import get_object_or_404
from django.views import generic
from taggit.models import Tag
from biohdf.base import annonces, emails, forms, mixins, models, taxonomy
from biohdf.base import annonces, emails, forms, mixins, models, taxonomy, wagtail_hooks
class PrivacyPolicyView(mixins.ThemeMixin, generic.TemplateView):
"""
Affiche la politique de confidentialité des données.
"""
template_name = 'privacy_policy.html'
def get(self, request, *args, **kwargs):
matomo_settings = wagtail_hooks.MatomoSettings.for_site(request.site)
if not matomo_settings.privacy_policy:
raise Http404("La politique de confidentialité n'est pas définie.")
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
root = models.SitePage.objects.first()
return {'page': root}
class CartoView(mixins.ThemeMixin, generic.ListView):

Voir le fichier

@ -1,8 +1,9 @@
from django.db import models
from django.utils.safestring import mark_safe
import wagtail.admin.rich_text.editors.draftail.features as draftail_features
from taggit.models import Tag
from wagtail.admin.edit_handlers import PageChooserPanel
from wagtail.admin.edit_handlers import PageChooserPanel, FieldPanel, StreamFieldPanel
from wagtail.admin.rich_text.converters.html_to_contentstate import (
InlineStyleElementHandler,
)
@ -13,6 +14,7 @@ from wagtail.contrib.modeladmin.options import (
)
from wagtail.contrib.settings.models import BaseSetting, register_setting
from wagtail.core import hooks
from wagtail.core.fields import RichTextField, StreamField
from biohdf.base import annonces, taxonomy
@ -33,6 +35,44 @@ class MentionsLegalesSettings(BaseSetting):
panels = [PageChooserPanel('page')]
@register_setting
class MatomoSettings(BaseSetting):
class Meta:
verbose_name = "Suivi des visiteurs"
privacy_policy = RichTextField(
"politique de confidentialité",
blank=True,
features=(
'h3',
'h4',
'bold',
'italic',
'ol',
'ul',
'hr',
'link',
'document-link',
),
help_text=mark_safe(
"Texte expliquant aux visiteurs les données qui sont collectées "
"par le site et comment elles sont utilisées. Il est affiché sur "
"la page <i>Politique de confidentialité</i>, qui permet aussi "
"de s'opposer au suivi si le code est défini."
),
)
tracking_code = models.TextField(
"code de suivi",
blank=True,
help_text=mark_safe(
"Le code JavaScript à exécuter pour suivre les visiteurs sur le "
"site, sans la balise <code>&lt;script&gt;</code>."
),
)
panels = [FieldPanel('privacy_policy'), FieldPanel('tracking_code')]
# Model Admins

Voir le fichier

@ -0,0 +1,395 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ({
/***/ "./assets/js/helpers/ready.js":
/*!************************************!*\
!*** ./assets/js/helpers/ready.js ***!
\************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/**
* Appel une fonction après que le document est chargé.
* @param {Function} fn - La fonction à appeler
* @returns {void}
*/
/* harmony default export */ __webpack_exports__["default"] = (function (fn) {
if (typeof fn !== 'function') {
return;
} // si le document est déjà chargé, on appel la fonction
if (document.readyState === 'interactive' || document.readyState === 'complete') {
fn();
}
document.addEventListener('DOMContentLoaded', fn, false);
});
/***/ }),
/***/ "./assets/js/opted-tracking.js":
/*!*************************************!*\
!*** ./assets/js/opted-tracking.js ***!
\*************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _helpers_ready__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./helpers/ready */ "./assets/js/helpers/ready.js");
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
/**
* ------------------------------------------------------------------------
* Constantes
* ------------------------------------------------------------------------
*/
var Message = {
ACTIVE: 'Votre visite sur ce site est actuellement <b>suivie anonymement</b>.',
ACTIVE_HELP: 'Vous pouvez vous opposer au suivi en décochant cette case.',
INACTIVE: 'Votre visite sur ce site n\'est actuellement <b>pas suivie</b>.',
INACTIVE_HELP: 'Vous pouvez activer le suivi anonyme en cochant cette case afin de nous aider à améliorer notre site.',
DONOTTRACK: 'Votre visite sur ce site n\'est <b>pas suivie</b>, conformément aux préférences de votre navigateur.',
DONOTTRACK_HELP: 'La fonctionnalité <i>Do Not Track</i> étant activée dans les préférences de votre navigateur, elle prend le dessus sur le contrôle de votre suivi sur ce site.',
OLDBROWSER_HELP: 'Votre navigateur semble trop vieux pour vous permettre de contrôler votre suivi. Pour plus de sécurité et de confidentialité, veuillez mettre à jour votre navigateur.'
};
var Default = {
key: 'tracking',
initial: true
};
var Selector = {
CONTAINER: '.tracking-optout',
CHECKBOX: 'input[type="checkbox"]',
STATE: '.tracking-state',
HELP: '.tracking-help'
};
/**
* ------------------------------------------------------------------------
* Helpers
* ------------------------------------------------------------------------
*/
/**
* Detects whether localStorage is both supported and available.
* @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#Testing_for_availability
* @returns {Boolean} `true` if localStorage is usable.
*/
function hasLocalStorage() {
var storage;
try {
storage = window.localStorage;
var x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
} catch (e) {
return e instanceof DOMException && ( // everything except Firefox
e.code === 22 || // eslint-disable-line no-magic-numbers
// Firefox
e.code === 1014 || // eslint-disable-line no-magic-numbers
// test name field too, because code might not be present
// everything except Firefox
e.name === 'QuotaExceededError' || // Firefox
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') && // acknowledge QuotaExceededError only if there's something already stored
storage && storage.length !== 0;
}
}
/**
* Checks whether the user's do-not-track setting is enabled.
* @returns {Boolean} `true` if the user should not be tracked.
*/
function hasDoNotTrack() {
return navigator.doNotTrack === '1' || navigator.doNotTrack === 'yes' || navigator.msDoNotTrack === '1' || window.doNotTrack === '1';
}
/**
* ------------------------------------------------------------------------
* Classe
* ------------------------------------------------------------------------
*/
var OptedTracking =
/*#__PURE__*/
function () {
function OptedTracking(fn, config) {
var _this = this;
_classCallCheck(this, OptedTracking);
if (typeof fn !== 'function') {
throw new Error('Une fonction est attendue comme premier argument');
}
this._elements = [];
this._config = Object.assign({}, Default, config);
this._dnt = hasDoNotTrack();
this._localStorage = hasLocalStorage();
this._enabled = this.getState(); // exécute la fonction dès que possible
if (this._enabled) {
setTimeout(fn, 0);
}
window.addEventListener('storage', function (e) {
if (e.key === _this._config.key) {
_this._refresh();
}
}); // attends que la page soit chargée pour initialiser les éléments
Object(_helpers_ready__WEBPACK_IMPORTED_MODULE_0__["default"])(function () {
return _this._initElements();
});
} // Méthodes publiques
_createClass(OptedTracking, [{
key: "getState",
value: function getState() {
if (this._dnt) {
return false;
} else if (!this._localStorage) {
return this._config.initial;
}
var state = window.localStorage.getItem(this._config.key);
if (state === 'true') {
return true;
} else if (state === 'false') {
return false;
}
return this._config.initial;
}
}, {
key: "setState",
value: function setState(enabled) {
window.localStorage.setItem(this._config.key, enabled ? 'true' : 'false'); // l'état étant changé dans ce contexte, il faut rafraichir manuellement
this._refresh();
} // Méthodes privées
}, {
key: "_refresh",
value: function _refresh() {
var enabled = this.getState();
if (this._dnt) {
this._adjustElements(Message.DONOTTRACK, Message.DONOTTRACK_HELP, enabled, true);
} else if (!this._localStorage) {
this._adjustElements(enabled ? Message.ACTIVE : Message.INACTIVE, Message.OLDBROWSER_HELP, enabled, true);
} else {
this._adjustElements(enabled ? Message.ACTIVE : Message.INACTIVE, enabled ? Message.ACTIVE_HELP : Message.INACTIVE_HELP, enabled, false);
} // si l'état a changé, il faut recharger la page
if (enabled !== this._enabled) {
window.location.reload();
}
}
}, {
key: "_initElements",
value: function _initElements() {
var _this2 = this;
this._elements = _toConsumableArray(document.querySelectorAll(Selector.CONTAINER));
this._checkboxes = [];
this._states = [];
this._helps = []; // parcours tous les conteneurs et ajoute leurs éléments
this._elements.forEach(function (container) {
var checkbox = container.querySelector(Selector.CHECKBOX);
var state = container.querySelector(Selector.STATE);
var help = container.querySelector(Selector.HELP);
if (checkbox) {
_this2._checkboxes.push(checkbox);
}
if (state) {
_this2._states.push(state);
}
if (help) {
_this2._helps.push(help);
}
});
this._checkboxes.forEach(function (checkbox) {
checkbox.addEventListener('click', function () {
return _this2.setState(checkbox.checked);
});
});
this._refresh();
}
}, {
key: "_adjustElements",
value: function _adjustElements(stateText, helpText, checked, disabled) {
if (stateText) {
this._states.forEach(function (el) {
el.innerHTML = stateText;
});
}
if (helpText) {
this._helps.forEach(function (el) {
el.innerHTML = helpText;
});
}
if (typeof checked === 'boolean') {
this._checkboxes.forEach(function (el) {
el.checked = checked;
});
}
if (disabled === true) {
this._checkboxes.forEach(function (el) {
return el.setAttribute('disabled', '');
});
} else if (disabled === false) {
this._checkboxes.forEach(function (el) {
return el.removeAttribute('disabled');
});
}
}
}]);
return OptedTracking;
}();
/**
* ------------------------------------------------------------------------
* Export
* ------------------------------------------------------------------------
*/
/* harmony default export */ __webpack_exports__["default"] = (OptedTracking);
window.optedtracking = function (fn, config) {
return new OptedTracking(fn.bind(window), config);
};
/***/ }),
/***/ 0:
/*!*******************************************!*\
!*** multi ./assets/js/opted-tracking.js ***!
\*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(/*! /home/francois/cliss_xxi/bio-hdf/biohdf/assets/js/opted-tracking.js */"./assets/js/opted-tracking.js");
/***/ })
/******/ });
//# sourceMappingURL=opted-tracking.js.map

Diff de fichier supprimé car une ou plusieurs lignes sont trop longues

Diff de fichier supprimé car une ou plusieurs lignes sont trop longues

Voir le fichier

@ -1,4 +1,4 @@
{% load staticfiles wagtailimages_tags wagtailcore_tags wagtailuserbar minified %}<!DOCTYPE html>
{% load staticfiles wagtailimages_tags wagtailcore_tags wagtailsettings_tags wagtailuserbar minified %}<!DOCTYPE html>
<html class="no-js" lang="fr">
<head>
<meta charset="utf-8">
@ -32,7 +32,16 @@
<link rel="stylesheet" href="{% minified "css/fork-awesome.css" %}">
{% endblock %}
{% block extra_head %}{% endblock %}
{% block extra_head %}
{% if settings.base.MatomoSettings.tracking_code %}
<script src="{% minified "js/opted-tracking.js" %}"></script>
<script type="text/javascript">
optedtracking(function() {
{{ settings.base.MatomoSettings.tracking_code|safe }}
});
</script>
{% endif %}
{% endblock %}
</head>
<body>
<div class="container">

Voir le fichier

@ -1,4 +1,4 @@
{% load staticfiles menu_tags wagtailimages_tags wagtailcore_tags wagtailuserbar minified %}
{% load staticfiles menu_tags wagtailimages_tags wagtailcore_tags wagtailsettings_tags wagtailuserbar minified %}
<!DOCTYPE html>
<html class="no-js" lang="fr">
<head>
@ -36,7 +36,16 @@
<link rel="stylesheet" href="{% minified "css/fork-awesome.css" %}">
{% endblock %}
{% block extra_head %}{% endblock %}
{% block extra_head %}
{% if settings.base.MatomoSettings.tracking_code %}
<script src="{% minified "js/opted-tracking.js" %}"></script>
<script type="text/javascript">
optedtracking(function() {
{{ settings.base.MatomoSettings.tracking_code|safe }}
});
</script>
{% endif %}
{% endblock %}
</head>
<body>
{% wagtailuserbar %}

Voir le fichier

@ -1,7 +1,9 @@
{% extends "./sitepage.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block extra_head %}{% include "./includes/style.html" with color=page.specific.action_color %}{% endblock %}
{% block extra_head %}{{ block.super }}
{% include "./includes/style.html" with color=page.specific.action_color %}
{% endblock %}
{% block title %}{{ page.title }}{% endblock %}

Voir le fichier

@ -0,0 +1,37 @@
{% extends "./sitepage.html" %}
{% load wagtailcore_tags wagtailsettings_tags %}
{% block extra_head %}{{ block.super }}
{% include "./includes/style.html" with color="primary" %}
{% endblock %}
{% block title %}Politique de confidentialité des données{% endblock %}
{% block inner_content %}
<div class="bg-white py-2 px-3">
<h2 class="page-title page-title-primary">
Politique de confidentialité des données
</h2>
{% if settings.base.MatomoSettings.privacy_policy %}
<div class="p-3 mt-5">
{{ settings.base.MatomoSettings.privacy_policy|richtext }}
</div>
{% endif %}
{% if settings.base.MatomoSettings.tracking_code %}
{% if not settings.base.MatomoSettings.privacy_policy %}
<div class="bg-white py-2 px-3">
<h2 class="mt-5">Suivi de votre visite</h2>
</div>
{% endif %}
<div class="form-group tracking-optout">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="tracking-optout" disabled>
<label class="custom-control-label tracking-state" for="tracking-optout">
<noscript>Le code JavaScript étant désactivé dans votre navigateur, votre visite sur ce site n'est <b>pas suivie</b>.</noscript>
</label>
</div>
<div class="small form-text tracking-help"></div>
</div>
{% endif %}
</div>
{% endblock %}

Voir le fichier

@ -3,7 +3,9 @@
{% block title %}Résultats de votre recherche&nbsp;: «&nbsp;{{ search_query }}&nbsp;»{% endblock %}
{% block extra_head %}{% include "./includes/style.html" with color="tertiary" %}{% endblock %}
{% block extra_head %}{{ block.super }}
{% include "./includes/style.html" with color="tertiary" %}
{% endblock %}
{% block search_value %}value="{{ search_query }}"{% endblock %}

Voir le fichier

@ -1,7 +1,9 @@
{% extends "./base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block extra_head %}{% include "./includes/style.html" with color=page.specific.get_theme_color %}{% endblock %}
{% block extra_head %}{{ block.super }}
{% include "./includes/style.html" with color=page.specific.get_theme_color %}
{% endblock %}
{% block title %}{{ page.get_seo_title }}{% endblock %}

Voir le fichier

@ -59,6 +59,11 @@ urlpatterns = [
'Sitemap: {}'.format(r.build_absolute_uri('sitemap.xml')),
content_type="text/plain",
)),
# Politique de confidentialité
path('politique-de-confidentialite/',
views.PrivacyPolicyView.as_view(),
name='privacy-policy'),
]
if settings.DEBUG:

Voir le fichier

@ -44,6 +44,7 @@ const CONFIG = {
// Paths to JavaScript entries which will be bundled
JS_ENTRIES: [
'assets/js/opted-tracking.js',
'assets/js/app.js'
],