'; /** @var array|null */ private $remoteConfig = null; public static function instance(): self { static $i = null; return $i ?: ($i = new self()); } private function __construct() { add_action('admin_menu', [$this, 'adminMenu']); add_action('admin_init', [$this, 'registerSettings']); add_filter('plugin_action_links_' . plugin_basename(__FILE__), [$this, 'actionLinks']); add_filter('plugin_row_meta', [$this, 'rowMeta'], 10, 2); // Finché manca la chiave: avviso con il link alla configurazione add_action('admin_notices', [$this, 'setupNotice']); if ($this->isConfigured()) { // Il prima possibile nel , prima di GTM e degli altri tag add_action('wp_head', [$this, 'printLoader'], -1000); // Marca gli script accodati con wp_enqueue_script add_filter('script_loader_tag', [$this, 'filterScriptTag'], 999, 3); // Opzionale: riscrive anche script e iframe scritti direttamente nel tema. Usa il buffer della pagina // gestito da WordPress (dalla 6.9): WordPress lo apre e lo chiude, solo per le pagine HTML del sito. if (!empty($this->settings()['buffer']) && self::themeBlockingSupported()) { add_filter('wp_template_enhancement_output_buffer', [$this, 'rewriteHtml']); } } // WP Consent API (https://wordpress.org/plugins/wp-consent-api/) add_filter('wp_consent_api_registered_' . plugin_basename(__FILE__), '__return_true'); add_filter('wp_get_consent_type', function () { return 'optin'; }); } // ------------------------------------------------------------------------- // Impostazioni // ------------------------------------------------------------------------- public function settings(): array { return wp_parse_args(get_option(self::OPTION, []), [ 'server_url' => self::DEFAULT_SERVER, 'site_key' => '', 'buffer' => 0, ]); } private function isConfigured(): bool { return $this->settings()['site_key'] !== ''; } /** Indirizzo del loader indicato dal server (es. la CDN); altrimenti quello del server configurato. */ private function loaderUrl(): string { $cfg = $this->remoteConfig(); if (!empty($cfg['loaderUrl']) && is_string($cfg['loaderUrl']) && strpos($cfg['loaderUrl'], 'https://') === 0) { return $cfg['loaderUrl']; } return $this->serverUrl() . '/api/c/' . rawurlencode($this->settings()['site_key']) . '.js'; } public function adminMenu(): void { add_menu_page( 'Cookie Smart', 'Cookie Smart', 'manage_options', 'cookie-smart', [$this, 'renderSettingsPage'], 'data:image/svg+xml;base64,' . base64_encode(self::ICON_SVG), 81 // subito dopo Impostazioni ); } public function actionLinks(array $links): array { array_unshift($links, '' . esc_html__('Settings', 'cookie-smart') . ''); return $links; } /** * Server Cookie Smart: https://cookiesmart.it. Per lo sviluppo si può cambiare con la costante * COOKIESMART_SERVER_URL in wp-config.php; vale anche un indirizzo salvato dalle versioni precedenti. */ public function serverUrl(): string { $url = defined('COOKIESMART_SERVER_URL') ? (string) constant('COOKIESMART_SERVER_URL') : $this->settings()['server_url']; return untrailingslashit($url ?: self::DEFAULT_SERVER); } /** Indirizzo di una pagina del sito Cookie Smart (registrazione, accesso). */ private function siteUrl(string $path = ''): string { return $this->serverUrl() . $path; } /** Link sotto la descrizione del plugin, nella pagina Plugin. */ public function rowMeta(array $links, string $file): array { // Il link al sito lo aggiunge già WordPress («Visit plugin site», dal Plugin URI) if ($file === plugin_basename(__FILE__)) { if (!$this->isConfigured()) { $links[] = '' . esc_html__('Create an account', 'cookie-smart') . ''; } } return $links; } /** Avviso nella pagina Plugin e nella Bacheca finché il plugin non ha la chiave del sito. */ public function setupNotice(): void { $screen = function_exists('get_current_screen') ? get_current_screen() : null; if ($this->isConfigured() || !current_user_can('manage_options') || !$screen || !in_array($screen->id, ['plugins', 'dashboard'], true)) { return; } printf( '

%s %s

%s

', esc_html__('Cookie Smart is installed but not connected yet:', 'cookie-smart'), esc_html__('the cookie banner is not shown until you enter your site key.', 'cookie-smart'), esc_url(admin_url('admin.php?page=cookie-smart')), esc_html__('Connect Cookie Smart', 'cookie-smart') ); } /** Guida all'attivazione nella pagina del plugin, per chi lo ha installato senza passare dal sito. */ private function renderSetupGuide(): void { ?>

'array', 'sanitize_callback' => function ($in) { $in = is_array($in) ? $in : []; delete_transient(self::TRANSIENT); // ricarica la configurazione remota return [ // Non più nel modulo: si conserva l'eventuale indirizzo salvato in precedenza 'server_url' => esc_url_raw((string) ($this->settings()['server_url'] ?? '')), 'site_key' => preg_replace('/[^A-Za-z0-9]/', '', (string) ($in['site_key'] ?? '')), 'buffer' => empty($in['buffer']) ? 0 : 1, ]; }, ]); } public function renderSettingsPage(): void { if (!current_user_can('manage_options')) { return; } $s = $this->settings(); $cfg = $this->isConfigured() ? $this->remoteConfig(true) : null; ?>

Cookie Smart

isConfigured()) { $this->renderSetupGuide(); } ?>
isConfigured()) : ?>

✅ ' . (int) ($cfg['version'] ?? 0) . '', '' . count($cfg['blockPatterns'] ?? []) . '' ); ?>

⚠️

' . esc_html('' . __('Cookie preferences', 'cookie-smart') . '') . '', '[cookie_smart_preferences]' ); ?>

[cookie_smart_declaration]' ); ?>

remoteConfig !== null && !$fresh) { return $this->remoteConfig ?: null; } $cached = $fresh ? false : get_transient(self::TRANSIENT); if (is_array($cached)) { return $this->remoteConfig = $cached; } $url = $this->serverUrl() . '/api/v1/sites/' . rawurlencode($this->settings()['site_key']) . '/config'; $res = wp_remote_get($url, ['timeout' => 5, 'headers' => ['Accept' => 'application/json']]); $data = null; if (!is_wp_error($res) && wp_remote_retrieve_response_code($res) === 200) { $data = json_decode(wp_remote_retrieve_body($res), true); } if (is_array($data) && isset($data['blockPatterns'])) { set_transient(self::TRANSIENT, $data, 6 * HOUR_IN_SECONDS); update_option(self::LAST_CONFIG, $data, false); return $this->remoteConfig = $data; } // Server non raggiungibile: si continua con l'ultima configurazione ricevuta (pattern di blocco e // indirizzo del loader) e si riprova tra 10 minuti $last = get_option(self::LAST_CONFIG, []); $last = is_array($last) ? $last : []; set_transient(self::TRANSIENT, $last, 10 * MINUTE_IN_SECONDS); $this->remoteConfig = $last; return $last ?: null; } private function categoryForUrl(string $url) { $cfg = $this->remoteConfig(); foreach (($cfg['blockPatterns'] ?? []) as $p) { if (!empty($p['pattern']) && strpos($url, $p['pattern']) !== false) { return $p['category']; } } return null; } // ------------------------------------------------------------------------- // Front-end // ------------------------------------------------------------------------- public function printLoader(): void { // Lo script deve essere sincrono (niente async/defer): deve agire prima degli altri. // Gli attributi data-* chiedono ai plugin di ottimizzazione di non spostarlo né ritardarlo. wp_print_script_tag([ 'src' => esc_url($this->loaderUrl()), 'data-no-optimize' => '1', 'data-no-defer' => '1', 'data-cfasync' => 'false', 'data-pagespeed-no-defer' => true, ]); // Ponte verso la WP Consent API, se presente wp_print_inline_script_tag( "window.addEventListener('cookiesmart:consent',function(e){" . "if(typeof window.wp_set_consent!=='function')return;" . "var c=e.detail.categories,m={preferences:'functional',statistics:'analytics','statistics-anonymous':'analytics',marketing:'marketing'};" . "for(var k in m){window.wp_set_consent(k,c[m[k]]?'allow':'deny');}" . '});', ['data-no-optimize' => '1', 'data-cfasync' => 'false'] ); } public function filterScriptTag(string $tag, string $handle, string $src): string { if ($src === '' || strpos($tag, 'data-cookiesmart-category') !== false) { return $tag; } $cat = $this->categoryForUrl($src); return $cat ? $this->blockScriptTag($tag, $cat) : $tag; } private function blockScriptTag(string $tag, string $category): string { $attr = ' data-cookiesmart-category="' . esc_attr($category) . '"'; if (preg_match('/\stype=(["\'])(.*?)\1/i', $tag, $m)) { // conserva il type originale (es. module) per lo sblocco if (strtolower($m[2]) !== 'text/javascript') { $attr .= ' data-cookiesmart-type="' . esc_attr($m[2]) . '"'; } return preg_replace('/\stype=(["\']).*?\1/i', ' type="text/plain"' . $attr, $tag, 1); } return preg_replace('/ $html = preg_replace_callback('/]*\ssrc=(["\'])(.*?)\1[^>]*>/is', function ($m) { if (strpos($m[0], 'data-cookiesmart-category') !== false) { return $m[0]; } $cat = $this->categoryForUrl(html_entity_decode($m[2])); return $cat ? $this->blockScriptTag($m[0], $cat) : $m[0]; }, $html); //