-tagin * type vaihdetaan arvoon "text/plain", jolloin selain ei aja sita. Banneri * vapauttaa sen kloonaamalla oikealla tyypilla kun lupa on annettu. * * Tarkoituksella varovainen: merkitaan vain tunnetut lahteet. Tuntematonta * skriptia ei kosketa, koska vaarin estetty skripti rikkoo sivuston ja * estamatta jaanyt tunnetaan sentaan terveystarkistuksessa. */ class Nanoconsent_Gating { /** * Ulkoiset lahteet luokittain. Jarjestys ratkaisee: ensimmainen osuma. */ public static function sources() { return array( 'analytics' => array( 'googletagmanager.com/gtag/js', 'google-analytics.com/analytics.js', 'google-analytics.com/ga.js', 'static.hotjar.com', 'clarity.ms/tag', 'matomo.js', 'piwik.js', 'cdn.mxpnl.com', 'cdn.heapanalytics.com', ), 'marketing' => array( 'connect.facebook.net', 'googleadservices.com', 'snap.licdn.com', 'analytics.tiktok.com', 'static.ads-twitter.com', 'ct.pinterest.com', 'sc-static.net/scevent', ), ); } /** * Inline-koodin tunnisteet. Nama ovat seurannan alustuskutsuja. */ public static function inline_markers() { return array( 'analytics' => array( "gtag('config'", 'gtag("config"', "ga('create'", 'ga("create"', '_paq.push', 'hj(', 'clarity(' ), 'marketing' => array( 'fbq(', 'ttq.load', 'snaptr(', 'lintrk(' ), ); } /** * Upotukset, jotka asettavat evasteita heti kun iframe latautuu. Jokainen * kilpailija korvaa nama paikanpitajalla; ilman tata valvonta loytaisi * YouTuben joka toiselta sivustolta. youtube-nocookie.com EI riita: se * kayttaa localStoragea, joka vaatii saman luvan. */ public static function embed_hosts() { return array( 'YouTube' => array( 'youtube.com/embed', 'youtube-nocookie.com/embed' ), 'Vimeo' => array( 'player.vimeo.com' ), 'Google Maps' => array( 'google.com/maps/embed', 'maps.google.com/maps' ), 'Facebook' => array( 'facebook.com/plugins' ), 'Instagram' => array( 'instagram.com/p/', 'instagram.com/reel/' ), 'TikTok' => array( 'tiktok.com/embed' ), 'Spotify' => array( 'open.spotify.com/embed' ), 'X' => array( 'platform.twitter.com' ), ); } /** * Puskurin taso jolla oma puskurimme aukesi. null = ei avattu. * Tallennetaan, jotta se voidaan sulkea tasmalleen omalta kohdaltaan * eika koskaan jonkun toisen puskuria. * * @var int|null */ private $taso = null; public function __construct() { add_action( 'template_redirect', array( $this, 'start' ), 1 ); // Suljetaan oma puskuri itse eika jateta sita PHP:n loppusiivouksen // varaan: jos joku muu avaa puskurin meidan jalkeemme eika sulje // sita, pinon purku menee vaarassa jarjestyksessa ja sivu voi jaada // vaillinaiseksi. Prioriteetti 0 = ennen muita shutdown-koukkuja. add_action( 'shutdown', array( $this, 'stop' ), 0 ); } public function start() { if ( is_admin() || is_feed() || is_embed() || is_robots() ) { return; } if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return; } $s = Nanoconsent_Settings::get(); if ( empty( $s['gating'] ) && empty( $s['embeds'] ) ) { return; } ob_start( array( $this, 'rewrite' ) ); $this->taso = ob_get_level(); } /** * Sulkee oman puskurin ja huuhtelee sen lapi rewrite()-kasittelijan. * Ei kosketa puskureihin jotka on avattu ennen meita. */ public function stop() { if ( null === $this->taso ) { return; } while ( ob_get_level() >= $this->taso ) { if ( ! ob_end_flush() ) { break; } } $this->taso = null; } /** * Koko sivu: ensin skriptit, sitten upotukset. Kumpikin vain jos asetus * on paalla ja sivulla on jotain kasiteltavaa. */ public function rewrite( $html ) { if ( ! is_string( $html ) ) { return $html; } $s = Nanoconsent_Settings::get(); if ( ! empty( $s['gating'] ) && false !== stripos( $html, ''; }, $html ); } public static function is_google( $src ) { return false !== strpos( $src, 'googletagmanager.com' ) || false !== strpos( $src, 'google-analytics.com' ); } public static function match_embed( $src ) { foreach ( self::embed_hosts() as $name => $needles ) { foreach ( $needles as $n ) { if ( false !== strpos( $src, $n ) ) { return $name; } } } return null; } /** * Korvaa tunnetun upotuksen paikanpitajalla: src -> data-nc-src, iframe * piiloon, eteen laatikko jossa kerrotaan mika odottaa ja miksi. * Selaimen puolella nanoconsent.onChange palauttaa src:n kun * markkinointi on sallittu (skripti nanoconsent.php:ssa). * * display:none inline-tyylina eika hidden-attribuuttina: teeman * "iframe{display:block}" voittaisi UA-tyylin [hidden]-saannon. */ public static function hold_embeds( $html ) { return preg_replace_callback( '/]*)>/i', function ( $m ) { $attrs = $m[1]; if ( preg_match( '/\bdata-nc(-src)?=/i', $attrs ) ) { return $m[0]; } if ( ! preg_match( '/\ssrc\s*=\s*["\']([^"\']+)["\']/i', $attrs, $src ) ) { return $m[0]; } $name = self::match_embed( $src[1] ); if ( null === $name ) { return $m[0]; } // Kuvasuhde iframen omista mitoista; prosentit ja puuttuvat -> 16:9. $w = 16; $h = 9; if ( preg_match( '/\swidth\s*=\s*["\']?(\d+)(%?)/i', $attrs, $mw ) && preg_match( '/\sheight\s*=\s*["\']?(\d+)(%?)/i', $attrs, $mh ) && '' === $mw[2] && '' === $mh[2] && (int) $mw[1] > 0 && (int) $mh[1] > 0 ) { $w = (int) $mw[1]; $h = (int) $mh[1]; } $attrs = preg_replace( '/\ssrc\s*=/i', ' data-nc-src=', $attrs, 1 ); if ( preg_match( '/\sstyle\s*=\s*["\']/i', $attrs ) ) { $attrs = preg_replace( '/(\sstyle\s*=\s*["\'])/i', '$1display:none;', $attrs, 1 ); } else { $attrs .= ' style="display:none"'; } $text = sprintf( /* translators: %s: service name such as YouTube */ __( '%s content loads after you allow marketing cookies.', 'nanoconsent' ), $name ); return '' . esc_html( $text ) . '' . '' . esc_html__( 'Allow and load', 'nanoconsent' ) . '' . ''; }, $html ); } public static function match_source( $src, $gate_gtm ) { if ( $gate_gtm && false !== strpos( $src, 'googletagmanager.com/gtm.js' ) ) { return 'analytics'; } foreach ( self::sources() as $cat => $needles ) { foreach ( $needles as $n ) { if ( false !== strpos( $src, $n ) ) { return $cat; } } } return null; } public static function match_inline( $body ) { foreach ( self::inline_markers() as $cat => $needles ) { foreach ( $needles as $n ) { if ( false !== strpos( $body, $n ) ) { return $cat; } } } return null; } }
' . esc_html( $text ) . '