Compare commits
1 Commits
main
...
extend/thi
Author | SHA1 | Date | |
---|---|---|---|
be9d00565d |
214
classes/GdprScriptDetector.php
Normal file
214
classes/GdprScriptDetector.php
Normal file
@ -0,0 +1,214 @@
|
||||
<?php
|
||||
/**
|
||||
* GDPR Cookie Consent Module for PrestaShop
|
||||
*
|
||||
* @author Walzen665
|
||||
* @copyright Copyright (c) 2025
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
*/
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for detecting third-party scripts and assigning them to cookie categories
|
||||
*/
|
||||
class GdprScriptDetector
|
||||
{
|
||||
// Common script signatures and their cookie categories
|
||||
private static $scriptPatterns = [
|
||||
'analytics' => [
|
||||
'/google-analytics\.com/i',
|
||||
'/googletagmanager\.com/i',
|
||||
'/gtag/i',
|
||||
'/analytics/i',
|
||||
'/matomo/i',
|
||||
'/piwik/i',
|
||||
'/statcounter/i',
|
||||
'/hotjar/i',
|
||||
'/clarity\.ms/i',
|
||||
'/stats/i'
|
||||
],
|
||||
'marketing' => [
|
||||
'/facebook\.net/i',
|
||||
'/connect\.facebook\.com/i',
|
||||
'/fbevents\.js/i',
|
||||
'/doubleclick\.net/i',
|
||||
'/googlesyndication\.com/i',
|
||||
'/google.*ads/i',
|
||||
'/twitter\.com\/widgets/i',
|
||||
'/platform\.twitter\.com/i',
|
||||
'/pixel/i',
|
||||
'/ads/i',
|
||||
'/adservice/i',
|
||||
'/criteo/i',
|
||||
'/pinterest/i',
|
||||
'/taboola/i',
|
||||
'/outbrain/i'
|
||||
],
|
||||
'functional' => [
|
||||
'/recaptcha/i',
|
||||
'/hcaptcha/i',
|
||||
'/fonts\.googleapis\.com/i',
|
||||
'/cloudflare/i',
|
||||
'/unpkg\.com/i',
|
||||
'/cdn/i',
|
||||
'/chat/i',
|
||||
'/live.*/i',
|
||||
'/support/i',
|
||||
'/feedback/i',
|
||||
'/maps\.google/i',
|
||||
'/disqus/i',
|
||||
'/zendesk/i',
|
||||
'/intercom/i'
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Check if a script URL or content matches any pattern
|
||||
*
|
||||
* @param string $scriptText The script URL or content to check
|
||||
* @return string The cookie category ('necessary', 'functional', 'analytics', 'marketing')
|
||||
*/
|
||||
public static function detectCategory($scriptText)
|
||||
{
|
||||
foreach (self::$scriptPatterns as $category => $patterns) {
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $scriptText)) {
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 'necessary'; // Default category if no match found
|
||||
}
|
||||
|
||||
/**
|
||||
* Get common third-party domains and their categories
|
||||
*
|
||||
* @return array Associative array of domains and their categories
|
||||
*/
|
||||
public static function getCommonThirdPartyDomains()
|
||||
{
|
||||
return [
|
||||
// Analytics
|
||||
'google-analytics.com' => 'analytics',
|
||||
'googletagmanager.com' => 'analytics',
|
||||
'analytics.google.com' => 'analytics',
|
||||
'matomo.org' => 'analytics',
|
||||
'matomo.cloud' => 'analytics',
|
||||
'piwik.pro' => 'analytics',
|
||||
'statcounter.com' => 'analytics',
|
||||
'hotjar.com' => 'analytics',
|
||||
'clarity.ms' => 'analytics',
|
||||
'mixpanel.com' => 'analytics',
|
||||
|
||||
// Marketing
|
||||
'facebook.com' => 'marketing',
|
||||
'facebook.net' => 'marketing',
|
||||
'fbcdn.net' => 'marketing',
|
||||
'doubleclick.net' => 'marketing',
|
||||
'googlesyndication.com' => 'marketing',
|
||||
'adservice.google.com' => 'marketing',
|
||||
'twitter.com' => 'marketing',
|
||||
'linkedin.com' => 'marketing',
|
||||
'pinterest.com' => 'marketing',
|
||||
'ads-twitter.com' => 'marketing',
|
||||
'criteo.com' => 'marketing',
|
||||
'criteo.net' => 'marketing',
|
||||
'taboola.com' => 'marketing',
|
||||
'outbrain.com' => 'marketing',
|
||||
|
||||
// Functional
|
||||
'google.com/recaptcha' => 'functional',
|
||||
'gstatic.com/recaptcha' => 'functional',
|
||||
'hcaptcha.com' => 'functional',
|
||||
'fonts.googleapis.com' => 'functional',
|
||||
'cloudflare.com' => 'functional',
|
||||
'cdn.jsdelivr.net' => 'functional',
|
||||
'unpkg.com' => 'functional',
|
||||
'cdnjs.cloudflare.com' => 'functional',
|
||||
'maps.google.com' => 'functional',
|
||||
'maps.googleapis.com' => 'functional',
|
||||
'disqus.com' => 'functional',
|
||||
'zendesk.com' => 'functional',
|
||||
'intercom.io' => 'functional'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a domain matches any known third-party domain
|
||||
*
|
||||
* @param string $domain The domain to check
|
||||
* @return string|false The cookie category or false if no match
|
||||
*/
|
||||
public static function matchDomain($domain)
|
||||
{
|
||||
$thirdPartyDomains = self::getCommonThirdPartyDomains();
|
||||
|
||||
foreach ($thirdPartyDomains as $thirdPartyDomain => $category) {
|
||||
if (strpos($domain, $thirdPartyDomain) !== false) {
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect inline script content category
|
||||
*
|
||||
* @param string $content The script content to analyze
|
||||
* @return string The cookie category
|
||||
*/
|
||||
public static function detectInlineScriptCategory($content)
|
||||
{
|
||||
// Common script signatures in inline scripts
|
||||
$inlinePatterns = [
|
||||
'analytics' => [
|
||||
'/googleAnalytics/i',
|
||||
'/gtag\s*\(/i',
|
||||
'/_gaq\s*\./i',
|
||||
'/ga\s*\(\s*[\'"]create/i',
|
||||
'/analytics/i',
|
||||
'/matomo/i',
|
||||
'/piwik/i',
|
||||
'/hotjar/i',
|
||||
'/clarity/i'
|
||||
],
|
||||
'marketing' => [
|
||||
'/fbq\s*\(/i',
|
||||
'/FB\.init/i',
|
||||
'/facebook-jssdk/i',
|
||||
'/twttr\s*\./i',
|
||||
'/twitter-widgets/i',
|
||||
'/pintrk/i',
|
||||
'/adsbygoogle/i',
|
||||
'/googletag/i',
|
||||
'/pixel/i',
|
||||
'/track/i'
|
||||
],
|
||||
'functional' => [
|
||||
'/grecaptcha/i',
|
||||
'/hcaptcha/i',
|
||||
'/maps\.googleapis/i',
|
||||
'/gapi\.load/i',
|
||||
'/disqus/i',
|
||||
'/LiveChat/i',
|
||||
'/intercom/i',
|
||||
'/zendesk/i'
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($inlinePatterns as $category => $patterns) {
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $content)) {
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 'necessary';
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ class GdprCookieConsent extends Module
|
||||
{
|
||||
$this->name = 'gdprcookieconsent';
|
||||
$this->tab = 'front_office_features';
|
||||
$this->version = '1.0.0';
|
||||
$this->version = '1.0.1';
|
||||
$this->author = 'Walzen665';
|
||||
$this->need_instance = 0;
|
||||
$this->ps_versions_compliancy = [
|
||||
@ -53,7 +53,11 @@ class GdprCookieConsent extends Module
|
||||
Configuration::updateValue('GDPR_COOKIE_RETENTION_PERIOD', '365 days') &&
|
||||
Configuration::updateValue('GDPR_COOKIE_THIRD_PARTIES', 'Google Analytics, Facebook, etc.') &&
|
||||
Configuration::updateValue('GDPR_COOKIE_MANAGE_TEXT', 'Manage Cookie Preferences') &&
|
||||
Configuration::updateValue('GDPR_COOKIE_ONLY_REQUIRED', 1);
|
||||
Configuration::updateValue('GDPR_COOKIE_ONLY_REQUIRED', 1) &&
|
||||
Configuration::updateValue('GDPR_COOKIE_NECESSARY_DESC', 'Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas. The website cannot function properly without these cookies.') &&
|
||||
Configuration::updateValue('GDPR_COOKIE_FUNCTIONAL_DESC', 'Functional cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region you are in.') &&
|
||||
Configuration::updateValue('GDPR_COOKIE_ANALYTICS_DESC', 'Analytics cookies help website owners understand how visitors interact with websites by collecting and reporting information anonymously.') &&
|
||||
Configuration::updateValue('GDPR_COOKIE_MARKETING_DESC', 'Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user.');
|
||||
}
|
||||
|
||||
|
||||
@ -74,7 +78,11 @@ class GdprCookieConsent extends Module
|
||||
Configuration::deleteByName('GDPR_COOKIE_RETENTION_PERIOD') &&
|
||||
Configuration::deleteByName('GDPR_COOKIE_THIRD_PARTIES') &&
|
||||
Configuration::deleteByName('GDPR_COOKIE_MANAGE_TEXT') &&
|
||||
Configuration::deleteByName('GDPR_COOKIE_ONLY_REQUIRED');
|
||||
Configuration::deleteByName('GDPR_COOKIE_ONLY_REQUIRED') &&
|
||||
Configuration::deleteByName('GDPR_COOKIE_NECESSARY_DESC') &&
|
||||
Configuration::deleteByName('GDPR_COOKIE_FUNCTIONAL_DESC') &&
|
||||
Configuration::deleteByName('GDPR_COOKIE_ANALYTICS_DESC') &&
|
||||
Configuration::deleteByName('GDPR_COOKIE_MARKETING_DESC');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,6 +107,10 @@ class GdprCookieConsent extends Module
|
||||
$thirdParties = Tools::getValue('GDPR_COOKIE_THIRD_PARTIES');
|
||||
$manageText = Tools::getValue('GDPR_COOKIE_MANAGE_TEXT');
|
||||
$onlyRequired = Tools::getValue('GDPR_COOKIE_ONLY_REQUIRED');
|
||||
$necessaryDesc = Tools::getValue('GDPR_COOKIE_NECESSARY_DESC');
|
||||
$functionalDesc = Tools::getValue('GDPR_COOKIE_FUNCTIONAL_DESC');
|
||||
$analyticsDesc = Tools::getValue('GDPR_COOKIE_ANALYTICS_DESC');
|
||||
$marketingDesc = Tools::getValue('GDPR_COOKIE_MARKETING_DESC');
|
||||
|
||||
// Update configuration values
|
||||
Configuration::updateValue('GDPR_COOKIE_ENABLED', $enabled);
|
||||
@ -113,6 +125,10 @@ class GdprCookieConsent extends Module
|
||||
Configuration::updateValue('GDPR_COOKIE_THIRD_PARTIES', $thirdParties);
|
||||
Configuration::updateValue('GDPR_COOKIE_MANAGE_TEXT', $manageText);
|
||||
Configuration::updateValue('GDPR_COOKIE_ONLY_REQUIRED', $onlyRequired);
|
||||
Configuration::updateValue('GDPR_COOKIE_NECESSARY_DESC', $necessaryDesc);
|
||||
Configuration::updateValue('GDPR_COOKIE_FUNCTIONAL_DESC', $functionalDesc);
|
||||
Configuration::updateValue('GDPR_COOKIE_ANALYTICS_DESC', $analyticsDesc);
|
||||
Configuration::updateValue('GDPR_COOKIE_MARKETING_DESC', $marketingDesc);
|
||||
|
||||
// Display confirmation
|
||||
$output .= $this->displayConfirmation($this->l('Settings updated'));
|
||||
@ -238,6 +254,34 @@ class GdprCookieConsent extends Module
|
||||
]
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => 'textarea',
|
||||
'label' => $this->l('Necessary Cookies Description'),
|
||||
'name' => 'GDPR_COOKIE_NECESSARY_DESC',
|
||||
'desc' => $this->l('Description for necessary cookies shown in the modal'),
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'type' => 'textarea',
|
||||
'label' => $this->l('Functional Cookies Description'),
|
||||
'name' => 'GDPR_COOKIE_FUNCTIONAL_DESC',
|
||||
'desc' => $this->l('Description for functional cookies shown in the modal'),
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'type' => 'textarea',
|
||||
'label' => $this->l('Analytics Cookies Description'),
|
||||
'name' => 'GDPR_COOKIE_ANALYTICS_DESC',
|
||||
'desc' => $this->l('Description for analytics cookies shown in the modal'),
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'type' => 'textarea',
|
||||
'label' => $this->l('Marketing Cookies Description'),
|
||||
'name' => 'GDPR_COOKIE_MARKETING_DESC',
|
||||
'desc' => $this->l('Description for marketing cookies shown in the modal'),
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'submit' => [
|
||||
'title' => $this->l('Save'),
|
||||
@ -277,6 +321,10 @@ class GdprCookieConsent extends Module
|
||||
$helper->fields_value['GDPR_COOKIE_THIRD_PARTIES'] = Configuration::get('GDPR_COOKIE_THIRD_PARTIES');
|
||||
$helper->fields_value['GDPR_COOKIE_MANAGE_TEXT'] = Configuration::get('GDPR_COOKIE_MANAGE_TEXT');
|
||||
$helper->fields_value['GDPR_COOKIE_ONLY_REQUIRED'] = Configuration::get('GDPR_COOKIE_ONLY_REQUIRED');
|
||||
$helper->fields_value['GDPR_COOKIE_NECESSARY_DESC'] = Configuration::get('GDPR_COOKIE_NECESSARY_DESC');
|
||||
$helper->fields_value['GDPR_COOKIE_FUNCTIONAL_DESC'] = Configuration::get('GDPR_COOKIE_FUNCTIONAL_DESC');
|
||||
$helper->fields_value['GDPR_COOKIE_ANALYTICS_DESC'] = Configuration::get('GDPR_COOKIE_ANALYTICS_DESC');
|
||||
$helper->fields_value['GDPR_COOKIE_MARKETING_DESC'] = Configuration::get('GDPR_COOKIE_MARKETING_DESC');
|
||||
|
||||
return $helper->generateForm([$form]);
|
||||
}
|
||||
@ -316,6 +364,11 @@ class GdprCookieConsent extends Module
|
||||
'gdprCookieManageText' => Configuration::get('GDPR_COOKIE_MANAGE_TEXT', 'Manage Cookies'),
|
||||
]);
|
||||
|
||||
// If not in required-only mode, add script for tagging known third-party scripts
|
||||
if (!Configuration::get('GDPR_COOKIE_ONLY_REQUIRED')) {
|
||||
$this->tagThirdPartyScripts();
|
||||
}
|
||||
|
||||
// Return the template content - but don't throw an error if it doesn't exist yet
|
||||
if (file_exists(_PS_MODULE_DIR_ . $this->name . '/views/templates/hook/manage_button.tpl')) {
|
||||
return $this->display(__FILE__, 'views/templates/hook/manage_button.tpl');
|
||||
@ -343,8 +396,80 @@ class GdprCookieConsent extends Module
|
||||
'gdprCookieThirdParties' => Configuration::get('GDPR_COOKIE_THIRD_PARTIES'),
|
||||
'gdprCookieManageText' => Configuration::get('GDPR_COOKIE_MANAGE_TEXT'),
|
||||
'gdprCookieOnlyRequired' => Configuration::get('GDPR_COOKIE_ONLY_REQUIRED'),
|
||||
'gdprCookieNecessaryDesc' => Configuration::get('GDPR_COOKIE_NECESSARY_DESC'),
|
||||
'gdprCookieFunctionalDesc' => Configuration::get('GDPR_COOKIE_FUNCTIONAL_DESC'),
|
||||
'gdprCookieAnalyticsDesc' => Configuration::get('GDPR_COOKIE_ANALYTICS_DESC'),
|
||||
'gdprCookieMarketingDesc' => Configuration::get('GDPR_COOKIE_MARKETING_DESC'),
|
||||
]);
|
||||
|
||||
return $this->display(__FILE__, 'views/templates/hook/footer.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to tag third-party scripts with appropriate consent categories
|
||||
*/
|
||||
protected function tagThirdPartyScripts()
|
||||
{
|
||||
// Require the script detector class
|
||||
require_once(dirname(__FILE__).'/classes/GdprScriptDetector.php');
|
||||
|
||||
// Get PrestaShop's currently registered JS files
|
||||
$jsFiles = $this->context->controller->js_files;
|
||||
|
||||
// Loop through JS files and add consent attributes based on patterns
|
||||
foreach ($jsFiles as $key => $jsFile) {
|
||||
// Use the detector class
|
||||
$category = GdprScriptDetector::detectCategory($jsFile);
|
||||
|
||||
// Add attribute to the script
|
||||
$this->context->smarty->assign([
|
||||
'js_' . md5($jsFile) . '_attributes' => 'data-cookieconsent="' . $category . '"'
|
||||
]);
|
||||
}
|
||||
|
||||
// Inject a helper script to tag inline scripts as well
|
||||
$this->context->controller->registerJavascript(
|
||||
'gdpr-cookie-helper',
|
||||
$this->_path . 'views/js/gdpr_cookie_helper.js',
|
||||
['position' => 'head', 'priority' => 1]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies scripts in the HTML head
|
||||
*/
|
||||
public function hookActionHtmlHeadFooter($params)
|
||||
{
|
||||
if (!Configuration::get('GDPR_COOKIE_ENABLED') || Configuration::get('GDPR_COOKIE_ONLY_REQUIRED')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current HTML content
|
||||
$html = $params['html_content'];
|
||||
|
||||
// Modify script tags to add data-cookieconsent attribute
|
||||
$patterns = [
|
||||
// Google Analytics pattern
|
||||
'/<script([^>]*)(gtag|googletagmanager|google-analytics)([^>]*)>/' => '<script$1$2$3 data-cookieconsent="analytics">',
|
||||
|
||||
// Facebook Pixel pattern
|
||||
'/<script([^>]*)(connect\.facebook\.net|fbevents\.js)([^>]*)>/' => '<script$1$2$3 data-cookieconsent="marketing">',
|
||||
|
||||
// Generic analytics patterns
|
||||
'/<script([^>]*)(analytics|piwik|matomo|stats)([^>]*)>/' => '<script$1$2$3 data-cookieconsent="analytics">',
|
||||
|
||||
// Marketing patterns
|
||||
'/<script([^>]*)(ads|adsbygoogle|doubleclick|googlesyndication)([^>]*)>/' => '<script$1$2$3 data-cookieconsent="marketing">',
|
||||
|
||||
// Functional patterns (more conservative, as these might be necessary)
|
||||
'/<script([^>]*)(recaptcha|chat)([^>]*)>/' => '<script$1$2$3 data-cookieconsent="functional">',
|
||||
];
|
||||
|
||||
foreach ($patterns as $pattern => $replacement) {
|
||||
$html = preg_replace($pattern, $replacement, $html);
|
||||
}
|
||||
|
||||
// Update the HTML content
|
||||
$params['html_content'] = $html;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,13 @@
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Store for blocked scripts
|
||||
const scriptStore = {
|
||||
functional: [],
|
||||
analytics: [],
|
||||
marketing: []
|
||||
};
|
||||
|
||||
// Cookie functions
|
||||
function setCookie(name, value, days) {
|
||||
var expires = '';
|
||||
@ -44,9 +51,161 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
showManageButton();
|
||||
}
|
||||
|
||||
// Block scripts based on type
|
||||
function blockScripts() {
|
||||
// Find and process script tags with data-cookieconsent attribute
|
||||
const scripts = document.querySelectorAll('script[data-cookieconsent]');
|
||||
|
||||
scripts.forEach(script => {
|
||||
const consentType = script.getAttribute('data-cookieconsent');
|
||||
|
||||
if (!consentType || consentType === 'necessary') {
|
||||
// Necessary scripts always run
|
||||
return;
|
||||
}
|
||||
|
||||
// Store script information
|
||||
const scriptData = {
|
||||
src: script.getAttribute('src'),
|
||||
content: script.innerHTML,
|
||||
type: script.getAttribute('type') || 'text/javascript',
|
||||
async: script.async,
|
||||
defer: script.defer
|
||||
};
|
||||
|
||||
// Add to appropriate store
|
||||
if (scriptStore[consentType]) {
|
||||
scriptStore[consentType].push(scriptData);
|
||||
}
|
||||
|
||||
// Remove the script from DOM
|
||||
script.parentNode.removeChild(script);
|
||||
});
|
||||
|
||||
// Also block iframe embeds with data-cookieconsent attribute
|
||||
const iframes = document.querySelectorAll('iframe[data-cookieconsent]');
|
||||
|
||||
iframes.forEach(iframe => {
|
||||
const consentType = iframe.getAttribute('data-cookieconsent');
|
||||
|
||||
if (!consentType || consentType === 'necessary') {
|
||||
// Necessary iframes always load
|
||||
return;
|
||||
}
|
||||
|
||||
// Create placeholder
|
||||
const placeholder = document.createElement('div');
|
||||
placeholder.className = 'gdpr-blocked-content-placeholder';
|
||||
placeholder.setAttribute('data-cookieconsent', consentType);
|
||||
placeholder.setAttribute('data-src', iframe.getAttribute('src'));
|
||||
placeholder.style.width = iframe.width + 'px' || '100%';
|
||||
placeholder.style.height = iframe.height + 'px' || '150px';
|
||||
placeholder.style.border = '1px dashed #ccc';
|
||||
placeholder.style.display = 'flex';
|
||||
placeholder.style.alignItems = 'center';
|
||||
placeholder.style.justifyContent = 'center';
|
||||
placeholder.style.backgroundColor = '#f9f9f9';
|
||||
placeholder.style.color = '#666';
|
||||
placeholder.innerHTML = `<div>
|
||||
<p>Content blocked due to ${consentType} cookies preferences</p>
|
||||
<button class="gdpr-load-blocked-content" data-type="${consentType}">Load content</button>
|
||||
</div>`;
|
||||
|
||||
// Replace iframe with placeholder
|
||||
iframe.parentNode.replaceChild(placeholder, iframe);
|
||||
});
|
||||
|
||||
// Add event listeners to load content buttons
|
||||
document.querySelectorAll('.gdpr-load-blocked-content').forEach(button => {
|
||||
button.addEventListener('click', function(e) {
|
||||
const type = this.getAttribute('data-type');
|
||||
const placeholder = this.closest('.gdpr-blocked-content-placeholder');
|
||||
|
||||
if (placeholder) {
|
||||
// Get iframe src
|
||||
const src = placeholder.getAttribute('data-src');
|
||||
|
||||
// Create iframe
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.src = src;
|
||||
iframe.width = placeholder.style.width;
|
||||
iframe.height = placeholder.style.height;
|
||||
iframe.frameBorder = '0';
|
||||
|
||||
// Replace placeholder with iframe
|
||||
placeholder.parentNode.replaceChild(iframe, placeholder);
|
||||
|
||||
// Update consent for this content type
|
||||
const preferences = JSON.parse(getCookie('gdpr_cookie_consent') || '{"necessary":true}');
|
||||
preferences[type] = true;
|
||||
setCookie('gdpr_cookie_consent', JSON.stringify(preferences), 365);
|
||||
|
||||
// Update checkboxes in modal if it exists
|
||||
const checkbox = document.querySelector(`#gdpr-cookie-${type}`);
|
||||
if (checkbox) {
|
||||
checkbox.checked = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Load scripts based on consent
|
||||
function loadConsentedScripts(preferences) {
|
||||
// For each script type
|
||||
Object.keys(scriptStore).forEach(type => {
|
||||
if (preferences[type]) {
|
||||
// Load all scripts of this type
|
||||
scriptStore[type].forEach(scriptData => {
|
||||
const script = document.createElement('script');
|
||||
|
||||
if (scriptData.src) {
|
||||
script.src = scriptData.src;
|
||||
}
|
||||
|
||||
script.type = scriptData.type;
|
||||
script.async = scriptData.async;
|
||||
script.defer = scriptData.defer;
|
||||
|
||||
if (scriptData.content) {
|
||||
script.innerHTML = scriptData.content;
|
||||
}
|
||||
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
|
||||
// Clear the store for this type
|
||||
scriptStore[type] = [];
|
||||
}
|
||||
});
|
||||
|
||||
// Also load blocked iframes
|
||||
document.querySelectorAll('.gdpr-blocked-content-placeholder').forEach(placeholder => {
|
||||
const type = placeholder.getAttribute('data-cookieconsent');
|
||||
|
||||
if (preferences[type]) {
|
||||
// Get iframe src
|
||||
const src = placeholder.getAttribute('data-src');
|
||||
|
||||
// Create iframe
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.src = src;
|
||||
iframe.width = placeholder.style.width;
|
||||
iframe.height = placeholder.style.height;
|
||||
iframe.frameBorder = '0';
|
||||
|
||||
// Replace placeholder with iframe
|
||||
placeholder.parentNode.replaceChild(iframe, placeholder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check if cookie consent is already set
|
||||
var cookieConsent = getCookie('gdpr_cookie_consent');
|
||||
|
||||
// Always block non-necessary scripts first
|
||||
blockScripts();
|
||||
|
||||
if (!cookieConsent) {
|
||||
// Show the cookie banner if consent is not set
|
||||
document.getElementById('gdpr-cookie-banner').style.display = 'block';
|
||||
@ -163,8 +322,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
|
||||
function applyConsentPreferences(preferences) {
|
||||
// Example implementation
|
||||
// You would need to adapt this based on your specific cookie usage
|
||||
// Load consented scripts
|
||||
loadConsentedScripts(preferences);
|
||||
|
||||
// Functional cookies
|
||||
if (!preferences.functional) {
|
||||
@ -194,31 +353,42 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Helper functions to implement consent preferences
|
||||
function removeFunctionalCookies() {
|
||||
// This is just an example - implement based on your specific needs
|
||||
var functionalCookies = ['prefs', 'language', 'theme'];
|
||||
var functionalCookies = ['prefs', 'language', 'theme', 'user_preferences'];
|
||||
var domains = [window.location.hostname, '.' + window.location.hostname];
|
||||
|
||||
functionalCookies.forEach(function(cookie) {
|
||||
document.cookie = cookie + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
|
||||
domains.forEach(function(domain) {
|
||||
document.cookie = cookie + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Domain=' + domain;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function disableAnalytics() {
|
||||
// Example: Disable Google Analytics
|
||||
// Google Analytics
|
||||
window['ga-disable-UA-XXXXXXXX-X'] = true;
|
||||
window['ga-disable-G-XXXXXXXX'] = true;
|
||||
|
||||
// Remove existing GA cookies
|
||||
document.cookie = '_ga=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Domain=.' + window.location.hostname;
|
||||
document.cookie = '_gid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Domain=.' + window.location.hostname;
|
||||
document.cookie = '_gat=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Domain=.' + window.location.hostname;
|
||||
var analyticsCookies = ['_ga', '_gid', '_gat', '__utma', '__utmb', '__utmc', '__utmt', '__utmz', '_hjid', '_hjAbsoluteSessionInProgress'];
|
||||
var domains = [window.location.hostname, '.' + window.location.hostname];
|
||||
|
||||
analyticsCookies.forEach(function(cookie) {
|
||||
domains.forEach(function(domain) {
|
||||
document.cookie = cookie + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Domain=' + domain;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function enableAnalytics() {
|
||||
// Example: Enable Google Analytics
|
||||
// Google Analytics
|
||||
window['ga-disable-UA-XXXXXXXX-X'] = false;
|
||||
window['ga-disable-G-XXXXXXXX'] = false;
|
||||
}
|
||||
|
||||
function disableMarketing() {
|
||||
// Example implementation for common marketing cookies
|
||||
var marketingCookies = ['_fbp', 'fr', 'IDE', 'MUID', 'personalization_id'];
|
||||
var domains = [window.location.hostname, '.' + window.location.hostname];
|
||||
// Common marketing cookies
|
||||
var marketingCookies = ['_fbp', 'fr', 'IDE', 'MUID', 'personalization_id', 'VISITOR_INFO1_LIVE', 'YSC', 'NID'];
|
||||
var domains = [window.location.hostname, '.' + window.location.hostname, '.google.com', '.facebook.com', '.youtube.com'];
|
||||
|
||||
marketingCookies.forEach(function(cookie) {
|
||||
domains.forEach(function(domain) {
|
||||
@ -228,8 +398,60 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
|
||||
function enableMarketing() {
|
||||
// For enabling marketing, we typically don't need to do anything special
|
||||
// The marketing scripts will set their cookies when they load
|
||||
// Just ensure the marketing scripts are loaded after checking consent
|
||||
// For enabling marketing, we load the scripts that were blocked
|
||||
// This happens in the loadConsentedScripts function
|
||||
}
|
||||
|
||||
// Function to detect which script is associated with each cookie category
|
||||
function detectThirdPartyScripts() {
|
||||
// This function would scan the page for known third-party scripts
|
||||
// and auto-assign data-cookieconsent attributes
|
||||
const knownScriptPatterns = {
|
||||
analytics: [
|
||||
/google-analytics\.com\/analytics\.js/i,
|
||||
/googletagmanager\.com\/gtag/i,
|
||||
/google-analytics\.com\/ga\.js/i,
|
||||
/hotjar\.com/i,
|
||||
/analytics/i,
|
||||
/matomo/i,
|
||||
/stats/i
|
||||
],
|
||||
marketing: [
|
||||
/facebook\.net/i,
|
||||
/doubleclick\.net/i,
|
||||
/googlesyndication\.com/i,
|
||||
/ads/i,
|
||||
/adservices/i,
|
||||
/pixel/i,
|
||||
/track/i
|
||||
],
|
||||
functional: [
|
||||
/recaptcha/i,
|
||||
/fonts\.googleapis\.com/i,
|
||||
/cloudflare/i,
|
||||
/cdn/i,
|
||||
/livechat/i,
|
||||
/chat/i,
|
||||
/support/i
|
||||
]
|
||||
};
|
||||
|
||||
const scripts = document.querySelectorAll('script:not([data-cookieconsent])');
|
||||
|
||||
scripts.forEach(script => {
|
||||
if (!script.src) return; // Skip inline scripts
|
||||
|
||||
for (const [category, patterns] of Object.entries(knownScriptPatterns)) {
|
||||
for (const pattern of patterns) {
|
||||
if (pattern.test(script.src)) {
|
||||
script.setAttribute('data-cookieconsent', category);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Run script detection
|
||||
detectThirdPartyScripts();
|
||||
});
|
211
views/js/gdpr_cookie_helper.js
Normal file
211
views/js/gdpr_cookie_helper.js
Normal file
@ -0,0 +1,211 @@
|
||||
/**
|
||||
* GDPR Cookie Consent Helper for PrestaShop
|
||||
*
|
||||
* @author Walzen665
|
||||
* @copyright Copyright (c) 2025
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
*/
|
||||
|
||||
// This script runs early in the page load to tag inline scripts with appropriate consent categories
|
||||
(function() {
|
||||
// Known script patterns to identify analytics, marketing and functional scripts
|
||||
const scriptPatterns = {
|
||||
analytics: [
|
||||
/google[\s\-_]?analytics/i,
|
||||
/ga\s*\(\s*['"]create/i,
|
||||
/googletagmanager/i,
|
||||
/gtag/i,
|
||||
/\_gaq/i,
|
||||
/matomo/i,
|
||||
/piwik/i,
|
||||
/mixpanel/i,
|
||||
/hotjar/i,
|
||||
/clarity/i
|
||||
],
|
||||
marketing: [
|
||||
/facebook[\s\-_]?pixel/i,
|
||||
/fbq\s*\(\s*['"]init/i,
|
||||
/doubleclick/i,
|
||||
/adwords/i,
|
||||
/google[\s\-_]?ad[\s\-_]?services/i,
|
||||
/google[\s\-_]?tag[\s\-_]?manager/i,
|
||||
/gtm/i,
|
||||
/twitter[\s\-_]?pixel/i,
|
||||
/pinterest[\s\-_]?tag/i
|
||||
],
|
||||
functional: [
|
||||
/recaptcha/i,
|
||||
/chat/i,
|
||||
/livechat/i,
|
||||
/support/i,
|
||||
/feedback/i,
|
||||
/preference/i,
|
||||
/usercentrics/i
|
||||
]
|
||||
};
|
||||
|
||||
// Function to process and tag a script element
|
||||
function processScript(script) {
|
||||
// Skip if it already has a consent attribute
|
||||
if (script.hasAttribute('data-cookieconsent')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip our own scripts
|
||||
if (script.src && script.src.indexOf('gdpr_cookie') !== -1) {
|
||||
script.setAttribute('data-cookieconsent', 'necessary');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check content for patterns
|
||||
const content = script.innerHTML || '';
|
||||
const src = script.src || '';
|
||||
|
||||
// Determine script category
|
||||
let category = 'necessary'; // Default category
|
||||
|
||||
for (const [cat, patterns] of Object.entries(scriptPatterns)) {
|
||||
for (const pattern of patterns) {
|
||||
if (pattern.test(content) || pattern.test(src)) {
|
||||
category = cat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (category !== 'necessary') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Tag the script
|
||||
script.setAttribute('data-cookieconsent', category);
|
||||
|
||||
// For non-necessary scripts, we'll create a duplicate with the correct attribute
|
||||
// but disabled until consent is given
|
||||
if (category !== 'necessary') {
|
||||
const originalScript = script;
|
||||
const newScript = document.createElement('script');
|
||||
|
||||
// Copy attributes
|
||||
Array.from(originalScript.attributes).forEach(attr => {
|
||||
if (attr.name !== 'data-cookieconsent') {
|
||||
newScript.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
});
|
||||
|
||||
// Set consent attribute
|
||||
newScript.setAttribute('data-cookieconsent', category);
|
||||
|
||||
// Copy content if it's an inline script
|
||||
if (!originalScript.src && originalScript.innerHTML) {
|
||||
newScript.innerHTML = originalScript.innerHTML;
|
||||
}
|
||||
|
||||
// Replace the original script
|
||||
originalScript.parentNode.replaceChild(newScript, originalScript);
|
||||
|
||||
// Prevent execution by removing src and content
|
||||
newScript.removeAttribute('src');
|
||||
newScript.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Process existing scripts
|
||||
document.querySelectorAll('script').forEach(processScript);
|
||||
|
||||
// Use a MutationObserver to catch dynamically added scripts
|
||||
const observer = new MutationObserver(mutations => {
|
||||
mutations.forEach(mutation => {
|
||||
mutation.addedNodes.forEach(node => {
|
||||
if (node.tagName === 'SCRIPT') {
|
||||
processScript(node);
|
||||
} else if (node.querySelectorAll) {
|
||||
node.querySelectorAll('script').forEach(processScript);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Start observing the document
|
||||
observer.observe(document.documentElement, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
// Also process iframes
|
||||
function processIframe(iframe) {
|
||||
// Skip if it already has a consent attribute
|
||||
if (iframe.hasAttribute('data-cookieconsent')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const src = iframe.src || '';
|
||||
|
||||
// Common third-party iframe sources
|
||||
const iframePatterns = {
|
||||
marketing: [
|
||||
/youtube/i,
|
||||
/vimeo/i,
|
||||
/facebook\.com\/plugins/i,
|
||||
/twitter\.com\/widgets/i,
|
||||
/instagram\.com/i,
|
||||
/pinterest\.com/i,
|
||||
/doubleclick/i,
|
||||
/ads/i
|
||||
],
|
||||
analytics: [
|
||||
/googletagmanager/i,
|
||||
/analytics/i
|
||||
],
|
||||
functional: [
|
||||
/recaptcha/i,
|
||||
/maps\.google/i,
|
||||
/google\.com\/maps/i,
|
||||
/disqus/i,
|
||||
/livechat/i,
|
||||
/chat/i
|
||||
]
|
||||
};
|
||||
|
||||
// Determine iframe category
|
||||
let category = 'necessary'; // Default category
|
||||
|
||||
for (const [cat, patterns] of Object.entries(iframePatterns)) {
|
||||
for (const pattern of patterns) {
|
||||
if (pattern.test(src)) {
|
||||
category = cat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (category !== 'necessary') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Tag the iframe
|
||||
iframe.setAttribute('data-cookieconsent', category);
|
||||
}
|
||||
|
||||
// Process existing iframes
|
||||
document.querySelectorAll('iframe').forEach(processIframe);
|
||||
|
||||
// Use the observer to catch dynamically added iframes
|
||||
const iframeObserver = new MutationObserver(mutations => {
|
||||
mutations.forEach(mutation => {
|
||||
mutation.addedNodes.forEach(node => {
|
||||
if (node.tagName === 'IFRAME') {
|
||||
processIframe(node);
|
||||
} else if (node.querySelectorAll) {
|
||||
node.querySelectorAll('iframe').forEach(processIframe);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Start observing for iframes
|
||||
iframeObserver.observe(document.documentElement, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
})();
|
@ -25,23 +25,23 @@
|
||||
<div class="gdpr-cookie-category">
|
||||
<input type="checkbox" id="gdpr-cookie-necessary" checked disabled>
|
||||
<label for="gdpr-cookie-necessary">{l s='Necessary' mod='gdprcookieconsent'}</label>
|
||||
<p>{l s='Necessary cookies help make a website usable by enabling basic functions like page navigation. The website cannot function properly without these cookies.' mod='gdprcookieconsent'}</p>
|
||||
<p>{$gdprCookieNecessaryDesc}</p>
|
||||
</div>
|
||||
{if !$gdprCookieOnlyRequired}
|
||||
<div class="gdpr-cookie-category">
|
||||
<input type="checkbox" id="gdpr-cookie-functional" class="gdpr-cookie-checkbox" data-cookie-category="functional">
|
||||
<label for="gdpr-cookie-functional">{l s='Functional' mod='gdprcookieconsent'}</label>
|
||||
<p>{l s='Functional cookies enable a website to remember information that changes the way the website behaves or looks, like your preferred language or the region you are in.' mod='gdprcookieconsent'}</p>
|
||||
<p>{$gdprCookieFunctionalDesc}</p>
|
||||
</div>
|
||||
<div class="gdpr-cookie-category">
|
||||
<input type="checkbox" id="gdpr-cookie-analytics" class="gdpr-cookie-checkbox" data-cookie-category="analytics">
|
||||
<label for="gdpr-cookie-analytics">{l s='Analytics' mod='gdprcookieconsent'}</label>
|
||||
<p>{l s='Analytics cookies help website owners understand how visitors interact with websites by collecting and reporting information anonymously.' mod='gdprcookieconsent'}</p>
|
||||
<p>{$gdprCookieAnalyticsDesc}</p>
|
||||
</div>
|
||||
<div class="gdpr-cookie-category">
|
||||
<input type="checkbox" id="gdpr-cookie-marketing" class="gdpr-cookie-checkbox" data-cookie-category="marketing">
|
||||
<label for="gdpr-cookie-marketing">{l s='Marketing' mod='gdprcookieconsent'}</label>
|
||||
<p>{l s='Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user.' mod='gdprcookieconsent'}</p>
|
||||
<p>{$gdprCookieMarketingDesc}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user