/** * SiteForge Privacy Manager * Gestisce il consenso ai cookie e la privacy (GDPR/CCPA compliant basics). * * Utilizzo: * 1. Includi questo script: * 2. Inizializza: * PrivacyManager.init({ * policyUrl: '/privacy-policy', * cookiePolicyUrl: '/cookie-policy' * }); */ $( document ).ready(function() {PrivacyManager.init({policyUrl: '/privacy-policy.htm', cookiePolicyUrl: '/cookie-policy.html'});}); const PrivacyManager = (function () { const STORAGE_KEY = 'siteforge_privacy_consent'; // Default config let config = { policyUrl: '#', cookiePolicyUrl: '#', autoBlock: true // Blocca script src se non autorizzati (richiede gestione specifica) }; let consent = { necessary: true, // Sempre true analytics: false, marketing: false, timestamp: null }; // --- UI TEMPLATES --- const css = ` #sf-privacy-banner { position: fixed; bottom: 0; left: 0; width: 100%; background: #fff; box-shadow: 0 -2px 10px rgba(0,0,0,0.1); padding: 20px; font-family: sans-serif; font-size: 14px; z-index: 99999; display: none; flex-direction: column; gap: 15px; border-top: 1px solid #eee; } @media (min-width: 768px) { #sf-privacy-banner { flex-direction: row; align-items: center; justify-content: space-between; padding: 20px 40px; } } .sf-privacy-buttons { display: flex; gap: 10px; flex-wrap: wrap; } .sf-btn { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; font-weight: 600; font-size: 13px; } .sf-btn-primary { background: #1a73e8; color: white; } .sf-btn-secondary { background: #f1f3f4; color: #333; } .sf-btn-link { background: none; text-decoration: underline; color: #555; } #sf-privacy-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 100000; display: none; align-items: center; justify-content: center; } .sf-privacy-modal-content { background: white; padding: 30px; border-radius: 8px; width: 90%; max-width: 500px; max-height: 90vh; overflow-y: auto; font-family: sans-serif; } .sf-switch-group { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #eee; } .sf-switch-label { font-weight: bold; display: block; } .sf-switch-desc { font-size: 12px; color: #666; display: block; margin-top: 5px; } `; function init(userConfig) { config = { ...config, ...userConfig }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', runStartLogic); } else { runStartLogic(); } } function runStartLogic() { // Load saved consent const saved = localStorage.getItem(STORAGE_KEY); if (saved) { consent = JSON.parse(saved); // Apply saved consent (e.g. enable scripts) applyConsent(); } else { // Show Banner injectStyles(); injectBanner(); } } function injectStyles() { const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); } function injectBanner() { $('#sf-privacy-banner').remove(); if (document.getElementById('sf-privacy-banner')) return; const banner = document.createElement('div'); banner.id = 'sf-privacy-banner'; banner.innerHTML = `
Questo sito utilizza i cookie.

Utilizziamo cookie tecnici e, previo consenso, cookie di profilazione e analitici per migliorare la tua esperienza. Puoi gestire le tue preferenze o accettare tutti i cookie. Leggi la Privacy Policy.

`; document.body.appendChild(banner); // Animations setTimeout(() => banner.style.display = 'flex', 100); // Events document.getElementById('sf-privacy-accept').onclick = acceptAll; document.getElementById('sf-privacy-reject').onclick = rejectAll; document.getElementById('sf-privacy-settings').onclick = openSettings; } function injectSettingsModal() { if (document.getElementById('sf-privacy-modal')) { document.getElementById('sf-privacy-modal').style.display = 'flex'; return; } const modal = document.createElement('div'); modal.id = 'sf-privacy-modal'; modal.innerHTML = `

Preferenze Privacy

Gestisci il consenso per le diverse categorie di cookie.

Necessari Indispensabili per il funzionamento del sito. Non possono essere disattivati.
Analitici Ci aiutano a capire come visiti il sito (es. Google Analytics).
Marketing Usati per inviare pubblicità mirata (es. Facebook Pixel).
`; document.body.appendChild(modal); modal.style.display = 'flex'; // Events document.getElementById('sf-modal-cancel').onclick = () => { modal.style.display = 'none'; }; document.getElementById('sf-modal-save').onclick = () => { consent.analytics = document.getElementById('sf-consent-analytics').checked; consent.marketing = document.getElementById('sf-consent-marketing').checked; saveConsent(); modal.style.display = 'none'; }; } function acceptAll() { consent.analytics = true; consent.marketing = true; saveConsent(); } function rejectAll() { consent.analytics = false; consent.marketing = false; saveConsent(); } function openSettings() { injectSettingsModal(); } function saveConsent() { consent.timestamp = new Date().toISOString(); localStorage.setItem(STORAGE_KEY, JSON.stringify(consent)); const banner = document.getElementById('sf-privacy-banner'); if (banner) banner.style.display = 'none'; applyConsent(); } function applyConsent() { console.log("SiteForge Privacy: Consent Applied", consent); // Event for other scripts const event = new CustomEvent('siteforge-consent-update', { detail: consent }); window.dispatchEvent(event); // Example: Enable Google Analytics if accepted if (consent.analytics) { enableAnalytics(); } } function enableAnalytics() { // Here you would typically inject the GA script tag dynamically // or change types from text/plain to text/javascript // Example logic needed here based on user specific scripts } // Public API return { init: init, openSettings: openSettings, getConsent: () => consent }; })(); $(document).ready(function () { // Configuration const API_URL = 'blog_manager.php'; // 1. Check if we need to render the Blog Grid const $gridContainer = $('.siteforge-blog-grid-container'); if ($gridContainer.length) { loadBlogGrid($gridContainer); } // 2. Check if we need to render a Single Post const $postContainer = $('.siteforge-blog-post-container'); if ($postContainer.length) { const urlParams = new URLSearchParams(window.location.search); const slug = urlParams.get('post_slug'); const id = urlParams.get('post_id'); if (slug || id) { loadSinglePost($postContainer, slug, id); } else { $postContainer.html('

Seleziona un post dalla pagina del blog.

'); } } // Functions function loadBlogGrid($container) { $container.html('
'); $.get(API_URL, { action: 'list_posts' }, function (response) { if (response.status === 'success') { $container.empty(); if (response.posts.length === 0) { $container.html('

Nessun post trovato.

'); return; } // Filter only published if on public frontend? // For now show all or check status. ideal: show only published. const publicPosts = response.posts.filter(p => p.status === 'published'); if (publicPosts.length === 0) { $container.html('

Nessun post pubblicato.

'); return; } publicPosts.forEach(post => { const date = new Date(post.created_at).toLocaleDateString(); const excerpt = post.excerpt || (post.content ? post.content.substring(0, 100) + '...' : ''); // Strip HTML from excerpt if it came from content const cleanExcerpt = excerpt.replace(/<[^>]*>?/gm, ''); const cover = post.cover_image || 'https://via.placeholder.com/400x250?text=Blog'; // Link logic: We need to know the page that acts as "Single Post View". // For now, we assume the user might link to the SAME page with a query param // OR user sets up a specific page. // Let's assume the user will put the Single Post Strip on a page called 'post.htm' or similar. // BUT, to be generic, we can link to "post.htm?post_slug=..." if it exists, // or just current page? // Better: The user should probably configure the "Post Page Base URL". // For simplicity, we'll assume a standard 'blog-post.htm' or similar, // OR we just link to "?post_slug=..." and assume the *current* page can handle it? // No, usually grid is on 'blog.htm' and single is on 'post.htm'. // We'll use a data attribute on the container if set, else default. const basePostUrl = $container.data('post-page') || 'post.htm'; // Fallback const link = `${basePostUrl}?post_slug=${post.slug}`; const card = `
${post.title}
${date}

${post.title}

${cleanExcerpt}

Leggi tutto →
`; $container.append(card); }); } else { $container.html('

Errore caricamento blog.

'); } }, 'json').fail(function () { $container.html('

Errore di connessione.

'); }); } function loadSinglePost($container, slug, id) { $container.html('
'); const params = { action: 'get_post' }; if (slug) params.slug = slug; if (id) params.id = id; $.get(API_URL, params, function (response) { if (response.status === 'success') { const p = response.post; // Update Page Title if possible document.title = p.title + ' - ' + document.title; const date = new Date(p.created_at).toLocaleDateString(); const html = `

${p.title}

Pubblicato il ${date}
${p.cover_image ? `` : ''}
${p.content}
`; $container.html(html); } else { $container.html('

Post non trovato.

'); } }, 'json'); } }); (function () { const CONFIG = { formSelector: 'form[name="contactform"]', challengeEndpoint: "https://static.milanoprovider.net/customers/guard.challenge.php", receiverEndpoint: "https://static.milanoprovider.net/customers/form.receiver.php" }; // 🔍 Individua dominio reale (drafts + produzione) function detectDomain() { return window.location.hostname.toLowerCase(); } document.addEventListener("DOMContentLoaded", async function () { const form = document.querySelector(CONFIG.formSelector); if (!form) return; const domain = detectDomain(); if (!domain) { console.error("Impossibile determinare il dominio"); return; } let challengeData = null; // 🔐 Richiede challenge passando domain in GET try { const url = CONFIG.challengeEndpoint + "?domain=" + encodeURIComponent(domain); const res = await fetch(url, { method: "GET", credentials: "omit" }); if (!res.ok) throw new Error("Challenge HTTP error"); challengeData = await res.json(); if (!challengeData.signature) { console.error("Challenge non valida"); return; } } catch (e) { console.error("Challenge error", e); return; } form.addEventListener("submit", async function (e) { e.preventDefault(); if (!challengeData) return; const formData = new FormData(form); formData.append("_domain", challengeData.domain); formData.append("_nonce", challengeData.nonce); formData.append("_expires", challengeData.expires); formData.append("_signature", challengeData.signature); const submitBtn = form.querySelector('[type="submit"]'); if (submitBtn) submitBtn.disabled = true; try { const res = await fetch(CONFIG.receiverEndpoint, { method: "POST", body: formData }); const text = await res.text(); console.log("RAW RESPONSE:", text); const data = JSON.parse(text); if (data.success) { form.reset(); alert("Messaggio inviato"); } else { alert(data.message || "Errore invio"); } } catch (err) { alert("Errore di connessione"); } if (submitBtn) submitBtn.disabled = false; }); }); })();document.addEventListener("DOMContentLoaded", function () { const elements = document.querySelectorAll('.fade-in.visible'); elements.forEach(el => { el.classList.remove('visible'); }); // Section number badges — attivato con ?showsections nell'URL if (new URLSearchParams(window.location.search).has('showsections')) { document.querySelectorAll('[siteforge-section]').forEach(el => { const secId = el.getAttribute('siteforge-section'); if (!secId || secId.startsWith('nav-')) return; el.style.position = el.style.position || 'relative'; const badge = document.createElement('div'); badge.textContent = secId; badge.style.cssText = 'position:absolute; top:8px; left:8px; z-index:9999; ' + 'width:48px; height:48px; border-radius:50%; ' + 'background:rgba(0,0,0,0.55); color:#fff; ' + 'display:flex; align-items:center; justify-content:center; ' + 'font-family:Montserrat,sans-serif; font-size:18px; font-weight:700; ' + 'pointer-events:none; box-shadow:0 2px 6px rgba(0,0,0,0.3);'; el.appendChild(badge); }); } }); /* SiteForge Editor Disabled - Hide UI */ (function(){var s=document.createElement('style');s.textContent='.siteforge-section-gear,.siteforge-gear-btn,#siteforge-main-gear,.siteforge-modal{display:none!important}';document.head.appendChild(s);})();