/
consent.js
152 lines (143 loc) · 5.21 KB
/
consent.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/**
* consent controller
* for gdpr
* @author adrienjoly, openwhyd
*/
var fs = require('fs');
var snip = require('../snip.js');
var mongodb = require('../models/mongodb.js');
var userModel = require('../models/user.js');
var analytics = require('../models/analytics.js');
var mainTemplate = require('../templates/mainTemplate.js');
var filePerLang = {
en: 'config/gdpr-consent-en.md',
fr: 'config/gdpr-consent-fr.md',
};
function removeEmptyLine(mdLine) {
return mdLine.length;
}
function renderMarkdownLine(mdLine) {
return ('<p>' + snip.htmlEntities(mdLine) + '</p>')
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
.replace(/\*([^*]+)\*/g, '<i>$1</i>')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
.replace(
/^<p>- \[ \] (.*)/g,
'<div class="consent-box"><input class="checkbox" type="checkbox"><p>$1</div>'
)
.replace(/^<p>- (.*)/g, '<li>$1</li>')
.replace(/^<p>#+ (.*)/g, '<h1>$1</h1>')
.replace(/<\/(li|h1)><\/p>$/, '</$1>');
}
const promisedTemplatePerLang = Object.entries(filePerLang).reduce(
(acc, [langId, langTemplate]) => {
const promisedHtml = fs.promises
.readFile(langTemplate, { encoding: 'utf8' })
.then((res) =>
res
.toString()
.split('\n')
.filter(removeEmptyLine)
.map(renderMarkdownLine)
.join('\n')
);
return {
...acc,
[langId]: promisedHtml,
};
},
{}
);
async function renderPageContent(params) {
var safeRedirect = snip.sanitizeJsStringInHtml(params.redirect || '/');
// credits: flag icons by Freepik, https://www.flaticon.com/packs/countrys-flags
return [
'<div class="container" id="consent-container" data-lang="lang-en">',
' <div class="language-flags">',
' <img alt="English / Anglais" id="lang-en" src="/images/lang-en.svg">',
' <img alt="French / Français" id="lang-fr" src="/images/lang-fr.svg">',
' </div>',
' <form class="whitePanel lang-en" action="/consent" method="POST">',
await promisedTemplatePerLang.en,
' <input type="hidden" name="lang" value="en">',
' <input type="hidden" name="redirect" value="' + safeRedirect + '">',
' <input disabled class="consent-submit" type="submit">',
' </form>',
' <form class="whitePanel lang-fr" action="/consent" method="POST">',
await promisedTemplatePerLang.fr,
' <input type="hidden" name="lang" value="fr">',
' <input type="hidden" name="redirect" value="' + safeRedirect + '">',
' <input disabled class="consent-submit" type="submit">',
' </form>',
'</div>',
'<script>',
' function changeLang(event) {',
' document.getElementById("consent-container").setAttribute("data-lang", event.currentTarget.id);',
' }',
' document.getElementById("lang-en").onclick = changeLang;',
' document.getElementById("lang-fr").onclick = changeLang;',
' function toggleConsent(event) {',
' var checked = event.currentTarget.checked;',
' document.getElementById("consent-container").setAttribute("data-checked", checked);',
' document.getElementsByClassName("consent-submit")[0].disabled = !checked;',
' document.getElementsByClassName("consent-submit")[1].disabled = !checked;',
' document.getElementsByClassName("checkbox")[0].checked = checked;',
' document.getElementsByClassName("checkbox")[1].checked = checked;',
' }',
' document.getElementsByClassName("checkbox")[0].onchange = toggleConsent;',
' document.getElementsByClassName("checkbox")[1].onchange = toggleConsent;',
'</script>',
].join('\n');
}
exports.controller = async function (request, getParams, response) {
var isPost = request.method.toLowerCase() === 'post';
var p = (isPost ? request.body : getParams) || {};
request.logToConsole('consent.controller ' + request.method, p);
// make sure user is logged in
if (!(p.loggedUser = request.checkLogin(response))) return;
function render(r) {
// content or error
if (!r || r.error) {
r = r || {};
console.log(r.error);
} else if (r.content) {
r.html = mainTemplate.renderWhydPage(r);
}
// call the adequate renderer
if (r.redirect) response.safeRedirect(r.redirect);
else if (r.html) response.renderHTML(r.html);
else response.renderJSON(r);
// and track visit to that page
analytics.addVisit(p.loggedUser, request.url);
}
if (isPost) {
userModel.updateAndFetch(
{ _id: mongodb.ObjectId('' + p.loggedUser.id) },
{
$set: {
'consent.lang': p.lang,
},
$currentDate: {
'consent.date': true, // => mongodb will store a ISODate in consent.date
},
},
null,
function onDone(err, user) {
if (user && user.consent)
console.log(
'user id',
p.loggedUser.id,
'consented to gdpr notice =>',
user.consent
);
render(err ? { error: err } : p); // should redirect to p.redirect, or display error
}
);
} else {
(p.css = p.css || []).push('consent.css');
p.bodyClass = 'pgConsent';
p.content = await renderPageContent(p);
p.redirect = ''; // to avoid redirection loops
render(p);
}
};