Files
ms1inscription-v5/php/inc_cookie_consent.php
stephan ce7c430046 Enhance cookie consent management and UI improvements
This commit refines the cookie consent management system by introducing a new function for handling cookie preferences and updating the UI elements in `inc_footer.php` and `inc_header.php`. It ensures that analytics and marketing scripts are executed based on user consent, improving compliance with privacy regulations. Additionally, it enhances the cookie consent interface, providing users with clearer options for managing their cookie preferences, and updates related functions for better maintainability.
2026-06-27 14:48:48 -04:00

498 lines
16 KiB
PHP

<?php
function fxCookiesPreferences(): array
{
$defaults = ['analytics' => false, 'marketing' => false, 'set' => false];
if (!empty($_COOKIE['cookiesPreferences'])) {
$prefs = json_decode(stripslashes((string) $_COOKIE['cookiesPreferences']), true);
if (is_array($prefs)) {
return [
'analytics' => !empty($prefs['analytics']),
'marketing' => !empty($prefs['marketing']),
'set' => true,
];
}
}
if (isset($_COOKIE['cookiesAccepted']) && $_COOKIE['cookiesAccepted'] === 'true') {
return ['analytics' => true, 'marketing' => true, 'set' => true];
}
if (isset($_COOKIE['cookiesRejected']) && $_COOKIE['cookiesRejected'] === 'true') {
return ['analytics' => false, 'marketing' => false, 'set' => true];
}
return $defaults;
}
function fxCookiesConsentAccepted(): bool
{
$prefs = fxCookiesPreferences();
return $prefs['set'] && ($prefs['analytics'] || $prefs['marketing']);
}
function fxCookiesConsentRejected(): bool
{
$prefs = fxCookiesPreferences();
return $prefs['set'] && !$prefs['analytics'] && !$prefs['marketing'];
}
function fxCookiesConsentPending(): bool
{
return !fxCookiesPreferences()['set'];
}
function fxCookiesAllowAnalytics(): bool
{
return fxCookiesPreferences()['analytics'];
}
function fxCookiesAllowMarketing(): bool
{
return fxCookiesPreferences()['marketing'];
}
function fxCookiesConsentResetIfRequested(): void
{
if (!isset($_GET['cookies'])) {
return;
}
$past = time() - 3600;
$names = ['cookiesAccepted', 'cookiesRejected', 'cookiesPreferences'];
foreach ($names as $name) {
setcookie($name, '', $past, '/');
unset($_COOKIE[$name]);
}
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?: '/';
$query = [];
if (!empty($_SERVER['QUERY_STRING'])) {
parse_str($_SERVER['QUERY_STRING'], $query);
}
unset($query['cookies']);
$location = $path;
if ($query) {
$location .= '?' . http_build_query($query);
}
header('Location: ' . $location);
exit;
}
function fxPageNeedsPaypal(): bool
{
global $strPage;
if (empty($strPage)) {
return false;
}
static $paypalPages = [
'panier.php',
'dons.php',
'paiement_redirect.php',
'panier',
'cart',
'don',
'donate',
];
return in_array($strPage, $paypalPages, true);
}
function fxCookieConsentDefaults(): array
{
return [
'experience_cookies' => [
'fr' => 'Ce site utilise des témoins essentiels au fonctionnement de vos inscriptions. Avec votre permission, nous utilisons aussi des témoins de mesure d\'audience et de marketing. Vous pouvez accepter, refuser ou personnaliser vos choix.',
'en' => 'This site uses essential cookies for registration. With your permission, we also use analytics and marketing cookies. You may accept, refuse or customize your choices.',
],
'bouton_acceper_cookies' => [
'fr' => 'Tout accepter',
'en' => 'Accept all',
],
'bouton_refuser_cookies' => [
'fr' => 'Tout refuser',
'en' => 'Refuse all',
],
'bouton_personnaliser_cookies' => [
'fr' => 'Personnaliser',
'en' => 'Customize',
],
'bouton_enregistrer_cookies' => [
'fr' => 'Enregistrer mes choix',
'en' => 'Save my choices',
],
'cookie_pref_titre' => [
'fr' => 'Préférences de témoins',
'en' => 'Cookie preferences',
],
'cookie_pref_essentiels' => [
'fr' => 'Essentiels — requis pour le panier, la session et la sécurité. Toujours actifs.',
'en' => 'Essential — required for cart, session and security. Always active.',
],
'cookie_pref_analytique' => [
'fr' => 'Mesure d\'audience — statistiques anonymes de fréquentation (Google Analytics).',
'en' => 'Analytics — anonymous traffic statistics (Google Analytics).',
],
'cookie_pref_marketing' => [
'fr' => 'Marketing — mesure publicitaire et remarketing (Facebook / Meta, scripts événement).',
'en' => 'Marketing — advertising measurement and remarketing (Facebook / Meta, event scripts).',
],
'politique_cookie' => [
'fr' => 'Politique de témoins',
'en' => 'Cookie policy',
],
'politique_confidentialite' => [
'fr' => 'Politique de confidentialité',
'en' => 'Privacy policy',
],
];
}
function fxCookieConsentTextIsDraft(string $clef, string $text): bool
{
$text = trim($text);
if ($text === '' || $text === $clef || $text === '*' . $clef . '*') {
return true;
}
if (stripos($text, $clef) === 0) {
return true;
}
return false;
}
function fxEnsureCookieConsentTexts(): void
{
global $objDatabase;
if (!isset($objDatabase)) {
return;
}
static $done = false;
if ($done) {
return;
}
$done = true;
foreach (fxCookieConsentDefaults() as $clef => $texts) {
foreach (['fr', 'en'] as $lang) {
if (!isset($texts[$lang])) {
continue;
}
$sql = "SELECT pk_info, info_texte FROM info
WHERE info_clef = '" . $objDatabase->fxEscape($clef) . "'
AND info_langue = '" . $objDatabase->fxEscape($lang) . "'
AND info_prg = 'global'
LIMIT 1";
$row = $objDatabase->fxGetRow($sql);
if (!$row) {
if (function_exists('fxSetInfo')) {
fxSetInfo('add', [
'info_clef' => $clef,
'info_langue' => $lang,
'info_texte' => $texts[$lang],
'info_aide' => '',
'info_prg' => 'global',
'info_actif' => 1,
], $lang);
}
continue;
}
if (!fxCookieConsentTextIsDraft($clef, (string) $row['info_texte'])) {
continue;
}
$objDatabase->fxQuery(
"UPDATE info SET info_texte = '" . $objDatabase->fxEscape($texts[$lang]) . "', info_maj = NOW()
WHERE pk_info = " . intval($row['pk_info'])
);
}
}
}
function fxCookieConsentText(string $clef, string $strLangue): string
{
$defaults = fxCookieConsentDefaults();
$lang = ($strLangue === 'en') ? 'en' : 'fr';
$fallback = $defaults[$clef][$lang] ?? $clef;
if (!function_exists('afficheTexte')) {
return $fallback;
}
$text = trim((string) afficheTexte($clef, 0, 1, 1));
if (fxCookieConsentTextIsDraft($clef, $text)) {
return $fallback;
}
return $text;
}
function fxCookieLegalPlaceholders(string $html): string
{
global $vClient, $vDomaine;
$org = htmlspecialchars((string) $vClient, ENT_QUOTES, 'UTF-8');
$domain = htmlspecialchars((string) $vDomaine, ENT_QUOTES, 'UTF-8');
$email = 'confidentialite@' . preg_replace('/^https?:\/\//', '', (string) parse_url($vDomaine, PHP_URL_HOST));
$date = date('Y-m-d');
$replacements = [
'[ORGANISATION]' => $org,
'[ORGANIZATION]' => $org,
'[SITE]' => $domain,
'[DATE]' => $date,
'[COURRIEL]' => $email,
'[EMAIL]' => $email,
];
return str_replace(array_keys($replacements), array_values($replacements), $html);
}
function fxCookiePolicyHtml(string $strLangue): string
{
if ($strLangue === 'en') {
return fxCookieLegalPlaceholders(<<<'HTML'
<h2>Cookie policy</h2>
<p><strong>Last updated:</strong> [DATE]</p>
<p><strong>[ORGANIZATION]</strong> uses cookies on its registration site operated via MS1 Inscription (Progiweb inc.).</p>
<h3>1. Essential cookies (always active)</h3>
<ul>
<li><strong>PHP session</strong> — cart, login and forms (~3 h inactivity)</li>
<li><strong>no_panier</strong> — cart identifier (10 hours)</li>
<li><strong>cookiesPreferences</strong> — remembers your choices (12 months)</li>
<li><strong>reCAPTCHA (Google)</strong> — spam protection on forms</li>
</ul>
<h3>2. Analytics cookies (optional)</h3>
<p>Google Analytics (_ga, _gid) — only if you enable analytics in the banner.</p>
<h3>3. Marketing cookies (optional)</h3>
<p>Meta Pixel (_fbp) and event-specific scripts — only if you enable marketing in the banner.</p>
<h3>4. Payment cookies</h3>
<p>PayPal cookies may be placed during checkout only.</p>
<h3>5. Your rights</h3>
<p>Contact: [EMAIL]. You may reset your choices anytime using the cookie icon in the footer or by adding <code>?cookies=1</code> to the URL.</p>
<p>See also our <a href="[SITE]/page/confidentialite/en">privacy policy</a>.</p>
HTML
);
}
return fxCookieLegalPlaceholders(<<<'HTML'
<h2>Politique de témoins</h2>
<p><strong>Dernière mise à jour :</strong> [DATE]</p>
<p><strong>[ORGANISATION]</strong> utilise des témoins sur son site d'inscription exploité via MS1 Inscription (Progiweb inc.).</p>
<h3>1. Témoins essentiels (toujours actifs)</h3>
<ul>
<li><strong>Session PHP</strong> — panier, connexion et formulaires (~3 h d'inactivité)</li>
<li><strong>no_panier</strong> — identifiant du panier (10 heures)</li>
<li><strong>cookiesPreferences</strong> — mémorise vos choix (12 mois)</li>
<li><strong>reCAPTCHA (Google)</strong> — protection anti-pourriel sur les formulaires</li>
</ul>
<h3>2. Témoins analytiques (optionnels)</h3>
<p>Google Analytics (_ga, _gid) — uniquement si vous activez la mesure d'audience dans le bandeau.</p>
<h3>3. Témoins marketing (optionnels)</h3>
<p>Pixel Meta (_fbp) et scripts spécifiques à l'événement — uniquement si vous activez le marketing dans le bandeau.</p>
<h3>4. Témoins de paiement</h3>
<p>Des témoins PayPal peuvent être déposés lors du paiement seulement.</p>
<h3>5. Vos droits</h3>
<p>Contact : [COURRIEL]. Vous pouvez réinitialiser vos choix via l'icône témoins du pied de page ou en ajoutant <code>?cookies=1</code> à l'adresse.</p>
<p>Voir aussi notre <a href="[SITE]/page/confidentialite/fr">politique de confidentialité</a>.</p>
HTML
);
}
function fxPrivacyPolicyHtml(string $strLangue): string
{
if ($strLangue === 'en') {
return fxCookieLegalPlaceholders(<<<'HTML'
<h2>Privacy policy</h2>
<p><strong>Last updated:</strong> [DATE]</p>
<p><strong>[ORGANIZATION]</strong> collects personal information when you register for events on [SITE], in compliance with Quebec Law 25.</p>
<h3>Information collected</h3>
<p>Name, contact details, date of birth, registration choices, payment metadata (processed by PayPal — we do not store full card numbers), and technical data (IP, browser).</p>
<h3>Purposes</h3>
<ul>
<li>Process registrations and payments</li>
<li>Communicate about your order and the event</li>
<li>Secure the site and prevent fraud</li>
<li>Analytics and marketing only with your cookie consent</li>
</ul>
<h3>Third parties</h3>
<p>Progiweb (MS1 Inscription), PayPal, Google (reCAPTCHA, Analytics if consented), Meta (Pixel if consented), and the event organizer.</p>
<h3>Cross-border transfers</h3>
<p>Some providers may process data outside Quebec. Contractual safeguards are applied.</p>
<h3>Your rights</h3>
<p>Access, correction, deletion and withdrawal of consent: [EMAIL]. Response within 30 days when possible.</p>
<h3>Privacy officer</h3>
<p>Contact: [EMAIL]</p>
<p><a href="[SITE]/page/cookie/en">Cookie policy</a></p>
HTML
);
}
return fxCookieLegalPlaceholders(<<<'HTML'
<h2>Politique de confidentialité</h2>
<p><strong>Dernière mise à jour :</strong> [DATE]</p>
<p><strong>[ORGANISATION]</strong> collecte des renseignements personnels lors de vos inscriptions sur [SITE], conformément à la Loi 25.</p>
<h3>Renseignements collectés</h3>
<p>Nom, coordonnées, date de naissance, choix d'inscription, métadonnées de paiement (PayPal — aucun numéro de carte complet conservé), et données techniques (IP, navigateur).</p>
<h3>Finalités</h3>
<ul>
<li>Traiter les inscriptions et paiements</li>
<li>Communiquer au sujet de votre commande et de l'événement</li>
<li>Sécuriser le site et prévenir la fraude</li>
<li>Statistiques et marketing seulement avec votre consentement aux témoins</li>
</ul>
<h3>Tiers</h3>
<p>Progiweb (MS1 Inscription), PayPal, Google (reCAPTCHA, Analytics si consenti), Meta (Pixel si consenti), et l'organisateur de l'événement.</p>
<h3>Transferts hors Québec</h3>
<p>Certains mandataires peuvent traiter des données à l'extérieur du Québec. Des mesures contractuelles s'appliquent.</p>
<h3>Vos droits</h3>
<p>Accès, rectification, suppression et retrait du consentement : [COURRIEL]. Réponse dans un délai de 30 jours lorsque possible.</p>
<h3>Responsable de la protection des renseignements personnels</h3>
<p>Contact : [COURRIEL]</p>
<p><a href="[SITE]/page/cookie/fr">Politique de témoins</a></p>
HTML
);
}
function fxEnsureLegalPage(string $code, string $labelFr, string $labelEn, string $titleFr, string $titleEn, callable $bodyFr, callable $bodyEn, int $sort = 90): void
{
global $objDatabase;
if (!isset($objDatabase)) {
return;
}
$sql = "SELECT * FROM inscriptions_pages WHERE pag_code = '" . $objDatabase->fxEscape($code) . "' LIMIT 1";
$row = $objDatabase->fxGetRow($sql);
$textFr = $bodyFr();
$textEn = $bodyEn();
if (!$row) {
$sqlInsert = "INSERT INTO inscriptions_pages
(pag_code, pag_label_fr, pag_label_en, pag_titre_fr, pag_titre_en, pag_texte_fr, pag_texte_en, pag_tri, pag_maj, pag_menu, pag_actif)
VALUES (
'" . $objDatabase->fxEscape($code) . "',
'" . $objDatabase->fxEscape($labelFr) . "',
'" . $objDatabase->fxEscape($labelEn) . "',
'" . $objDatabase->fxEscape($titleFr) . "',
'" . $objDatabase->fxEscape($titleEn) . "',
'" . $objDatabase->fxEscape($textFr) . "',
'" . $objDatabase->fxEscape($textEn) . "',
" . intval($sort) . ",
NOW(),
1,
1
)";
$objDatabase->fxQuery($sqlInsert);
return;
}
$updates = [];
if (trim((string) $row['pag_texte_fr']) === '') {
$updates[] = "pag_texte_fr = '" . $objDatabase->fxEscape($textFr) . "'";
}
if (trim((string) $row['pag_texte_en']) === '') {
$updates[] = "pag_texte_en = '" . $objDatabase->fxEscape($textEn) . "'";
}
if (trim((string) $row['pag_titre_fr']) === '') {
$updates[] = "pag_titre_fr = '" . $objDatabase->fxEscape($titleFr) . "'";
}
if (trim((string) $row['pag_titre_en']) === '') {
$updates[] = "pag_titre_en = '" . $objDatabase->fxEscape($titleEn) . "'";
}
if ((int) $row['pag_menu'] !== 1) {
$updates[] = 'pag_menu = 1';
}
if ((int) $row['pag_actif'] !== 1) {
$updates[] = 'pag_actif = 1';
}
if ($updates) {
$updates[] = 'pag_maj = NOW()';
$objDatabase->fxQuery('UPDATE inscriptions_pages SET ' . implode(', ', $updates) . " WHERE pag_id = " . intval($row['pag_id']));
}
}
function fxEnsureLegalPages(): void
{
static $done = false;
if ($done) {
return;
}
$done = true;
fxEnsureCookieConsentTexts();
fxEnsureLegalPage(
'cookie',
'Témoins',
'Cookies',
'Politique de témoins',
'Cookie policy',
static function () {
return fxCookiePolicyHtml('fr');
},
static function () {
return fxCookiePolicyHtml('en');
},
98
);
fxEnsureLegalPage(
'confidentialite',
'Confidentialité',
'Privacy',
'Politique de confidentialité',
'Privacy policy',
static function () {
return fxPrivacyPolicyHtml('fr');
},
static function () {
return fxPrivacyPolicyHtml('en');
},
99
);
}
function fxCookiePolicyPageContent(string $strLangue): string
{
return fxCookiePolicyHtml($strLangue);
}
function fxPrivacyPolicyPageContent(string $strLangue): string
{
return fxPrivacyPolicyHtml($strLangue);
}