key => [ full name, short name ]. */ public static function labels() { return array( 'necessary' => array( __( 'Strictly necessary', 'goconsent' ), __( 'Necessary', 'goconsent' ) ), 'analytics' => array( __( 'Analytics', 'goconsent' ), __( 'Analytics', 'goconsent' ) ), 'media' => array( __( 'Maps & videos', 'goconsent' ), __( 'Maps & videos', 'goconsent' ) ), 'marketing' => array( __( 'Marketing & advertising', 'goconsent' ), __( 'Marketing', 'goconsent' ) ), ); } /** * The declaration. * * @param Settings $settings Settings. * @return array{time:int,total:int,cats:array} */ public static function declaration( Settings $settings ) { if ( null !== self::$cache ) { return self::$cache; } $scan = get_option( Scanner::OPTION, array() ); $items = ( is_array( $scan ) && isset( $scan['items'] ) && is_array( $scan['items'] ) ) ? $scan['items'] : array(); $overrides = (array) $settings->get( 'overrides' ); $catalog = self::catalog(); $site = __( 'This site', 'goconsent' ); $cats = array_fill_keys( self::CATS, array() ); // GoConsent and WordPress itself. $cats['necessary'][] = self::row( 'goconsent', $site, __( 'Stores your privacy choices and your Consent ID.', 'goconsent' ), 'cookie', self::months( 12 ) ); $cats['necessary'][] = self::row( 'wordpress_test_cookie', $site, __( 'Checks that your browser accepts cookies (login page only).', 'goconsent' ), 'cookie', self::session() ); $cats['necessary'][] = self::row( 'wordpress_logged_in_*', $site, __( 'Keeps you logged in (only for people with an account).', 'goconsent' ), 'cookie', self::days( 14 ) ); if ( class_exists( 'WooCommerce' ) ) { $cats['necessary'][] = self::row( 'wp_woocommerce_session_*', $site, __( 'Keeps your shopping cart between pages.', 'goconsent' ), 'cookie', self::days( 2 ) ); $cats['necessary'][] = self::row( 'woocommerce_cart_hash', $site, __( 'Notices when your cart changes.', 'goconsent' ), 'cookie', self::session() ); } $maps = false; foreach ( $items as $id => $item ) { $id = (string) $id; $kind = isset( $item['kind'] ) ? (string) $item['kind'] : ''; if ( 'fonts-local' === $kind ) { continue; // Served from this site: nothing to declare. } if ( in_array( $id, self::MEDIA_IDS, true ) ) { $cat = 'media'; $maps = $maps || 'embed:gmaps' === $id || 'maps_js' === $id; } else { $cat = isset( $overrides[ $id ] ) ? (string) $overrides[ $id ] : ( isset( $item['default'] ) ? (string) $item['default'] : 'marketing' ); } if ( ! isset( $cats[ $cat ] ) ) { $cat = 'marketing'; } if ( isset( $catalog[ $id ] ) ) { foreach ( $catalog[ $id ] as $r ) { $cats[ $cat ][] = $r; } continue; } $host = 0 === strpos( $id, 'host:' ) ? substr( $id, 5 ) : ( 0 === strpos( $id, 'embed:' ) ? substr( $id, 6 ) : '' ); $name = isset( $item['label'] ) ? (string) $item['label'] : $id; if ( '' !== $host ) { $embed = 0 === strpos( $id, 'embed:' ); $cats[ $cat ][] = self::row( $host, $host, $embed ? __( 'Embedded content from another company; it may set its own cookies.', 'goconsent' ) : __( 'Script from another company; it may set its own cookies.', 'goconsent' ), $embed ? 'embed' : 'script', self::varies() ); } else { $cats[ $cat ][] = self::row( $name, $name, __( 'Third-party service; it may set its own cookies.', 'goconsent' ), 'script', self::varies() ); } } if ( $maps ) { $cats['media'][] = self::row( 'goconsent_maps', $site, __( 'Remembers that you loaded a map during this visit.', 'goconsent' ), 'session', self::session() ); } // One row per name and provider. $total = 0; foreach ( $cats as $k => $rows ) { $seen = array(); $out = array(); foreach ( $rows as $r ) { $key = strtolower( $r['name'] . '|' . $r['provider'] ); if ( isset( $seen[ $key ] ) ) { continue; } $seen[ $key ] = true; $out[] = $r; } $cats[ $k ] = $out; $total += count( $out ); } /** * Filter the cookie declaration before it is shown. * * @param array $cats category => rows ( name, provider, purpose, type, expires ). */ $cats = (array) apply_filters( 'goconsent_cookie_declaration', $cats ); self::$cache = array( 'time' => is_array( $scan ) && isset( $scan['time'] ) ? (int) $scan['time'] : 0, 'total' => $total, 'cats' => $cats, ); return self::$cache; } /** * Forget the cached declaration (after a scan or a category change). */ public static function flush() { self::$cache = null; } /** * "Cookie list from GoConsent™ scan, ." (empty before the first scan). * * @param int $time Scan time. * @return string */ public static function scan_line( $time ) { if ( ! $time ) { return ''; } /* translators: %s: date of the last scan. */ return sprintf( __( 'Cookie list from GoConsent™ scan, %s.', 'goconsent' ), wp_date( get_option( 'date_format' ), $time ) ); } /** * "N cookies". * * @param int $n Count. * @return string */ public static function count_label( $n ) { /* translators: %s: number of cookies. */ return sprintf( _n( '%s cookie', '%s cookies', $n, 'goconsent' ), number_format_i18n( $n ) ); } /** * Table of rows. * * @param array $rows Rows. * @param bool $type Include the Type column. * @param string $caption Optional caption (screen readers). * @return string */ public static function table_html( array $rows, $type = true, $caption = '' ) { $types = self::types(); $h = '' . esc_html__( 'Name', 'goconsent' ) . '' . esc_html__( 'Provider', 'goconsent' ) . '' . esc_html__( 'Purpose', 'goconsent' ) . ''; if ( $type ) { $h .= '' . esc_html__( 'Type', 'goconsent' ) . ''; } $h .= '' . esc_html__( 'Expires', 'goconsent' ) . ''; $b = ''; foreach ( $rows as $r ) { $b .= '' . esc_html( $r['name'] ) . '' . esc_html( $r['provider'] ) . '' . esc_html( $r['purpose'] ) . ''; if ( $type ) { $b .= '' . esc_html( isset( $types[ $r['type'] ] ) ? $types[ $r['type'] ] : $r['type'] ) . ''; } $b .= '' . esc_html( $r['expires'] ) . ''; } $cap = '' !== $caption ? '' . esc_html( $caption ) . '' : ''; // Focusable so keyboard users can scroll a wide table (WCAG 2.1.1). return ''; } /** * [goconsent_cookie_table] — the full declaration, for the Cookie Policy page. * * @param array $atts Attributes (category="analytics" limits it to one category). * @return string */ public static function shortcode( $atts ) { $atts = shortcode_atts( array( 'category' => '' ), $atts, 'goconsent_cookie_table' ); $decl = self::declaration( goconsent()->settings ); $labels = self::labels(); $only = sanitize_key( (string) $atts['category'] ); $out = ''; foreach ( $decl['cats'] as $key => $rows ) { if ( ( '' !== $only && $only !== $key ) || ! $rows || ! isset( $labels[ $key ] ) ) { continue; } $out .= ''; $out .= self::table_html( $rows, true, $labels[ $key ][0] ); } $line = self::scan_line( $decl['time'] ); if ( '' !== $line ) { $out .= ''; } return ''; } /** * Type labels. * * @return array */ private static function types() { return array( 'cookie' => __( 'Cookie', 'goconsent' ), 'local' => __( 'Local storage', 'goconsent' ), 'session' => __( 'Session storage', 'goconsent' ), 'pixel' => __( 'Pixel', 'goconsent' ), 'script' => __( 'Script', 'goconsent' ), 'embed' => __( 'Embed', 'goconsent' ), 'request' => __( 'Request', 'goconsent' ), ); } /** * One row. * * @param string $name Cookie name. * @param string $provider Provider. * @param string $purpose Purpose. * @param string $type cookie|local|session|pixel|script|embed|request. * @param string $expires How long it lasts. * @return array */ private static function row( $name, $provider, $purpose, $type, $expires ) { return array( 'name' => (string) $name, 'provider' => (string) $provider, 'purpose' => (string) $purpose, 'type' => (string) $type, 'expires' => (string) $expires, ); } /** * Duration helpers. * * @param int $n Number. * @return string */ private static function months( $n ) { /* translators: %d: number of months. */ return sprintf( _n( '%d month', '%d months', $n, 'goconsent' ), $n ); } /** * Days. * * @param int $n Number. * @return string */ private static function days( $n ) { /* translators: %d: number of days. */ return sprintf( _n( '%d day', '%d days', $n, 'goconsent' ), $n ); } /** * Years. * * @param int $n Number. * @return string */ private static function years( $n ) { /* translators: %d: number of years. */ return sprintf( _n( '%d year', '%d years', $n, 'goconsent' ), $n ); } /** * Minutes. * * @param int $n Number. * @return string */ private static function minutes( $n ) { /* translators: %d: number of minutes. */ return sprintf( _n( '%d minute', '%d minutes', $n, 'goconsent' ), $n ); } /** * Session. * * @return string */ private static function session() { return __( 'Session', 'goconsent' ); } /** * Varies. * * @return string */ private static function varies() { return __( 'Varies', 'goconsent' ); } /** * None (scripts and pixels that store nothing). * * @return string */ private static function none() { return __( 'n/a', 'goconsent' ); } /** * Known cookies for each service the scanner recognises. * * @return array> */ private static function catalog() { $c = array( 'ga4' => array( self::row( '_ga', 'Google Analytics', __( 'Tells visits apart with a random ID.', 'goconsent' ), 'cookie', self::years( 2 ) ), self::row( '_ga_*', 'Google Analytics', __( 'Keeps the current visit together.', 'goconsent' ), 'cookie', self::years( 2 ) ), ), 'gtag' => array( self::row( 'gtag.js', 'Google', __( 'Loads Google Analytics and Google Ads tags; sets no cookies itself.', 'goconsent' ), 'script', self::none() ), ), 'gtm' => array( self::row( 'gtm.js', 'Google Tag Manager', __( 'Loads the site’s tags; sets no cookies itself.', 'goconsent' ), 'script', self::none() ), ), 'gads' => array( self::row( '_gcl_au', 'Google Ads', __( 'Measures ad conversions.', 'goconsent' ), 'cookie', self::months( 3 ) ), self::row( '_gcl_aw', 'Google Ads', __( 'Remembers the ad click that brought you here.', 'goconsent' ), 'cookie', self::months( 3 ) ), ), 'dclick' => array( self::row( 'IDE', 'Google DoubleClick', __( 'Shows and measures ads across sites.', 'goconsent' ), 'cookie', self::months( 13 ) ), self::row( 'test_cookie', 'Google DoubleClick', __( 'Checks whether the browser accepts cookies.', 'goconsent' ), 'cookie', self::minutes( 15 ) ), ), 'meta' => array( self::row( '_fbp', 'Meta', __( 'Measures ads and builds ad audiences.', 'goconsent' ), 'cookie', self::months( 3 ) ), self::row( '_fbc', 'Meta', __( 'Remembers the Facebook ad click that brought you here.', 'goconsent' ), 'cookie', self::months( 3 ) ), self::row( 'Meta Pixel', 'Meta', __( 'Reports page views to Meta for ads.', 'goconsent' ), 'pixel', self::none() ), ), 'tiktok' => array( self::row( '_ttp', 'TikTok', __( 'Measures ads and builds ad audiences.', 'goconsent' ), 'cookie', self::months( 13 ) ), self::row( '_tt_enable_cookie', 'TikTok', __( 'Checks whether TikTok cookies can be set.', 'goconsent' ), 'cookie', self::months( 13 ) ), ), 'linkedin' => array( self::row( 'li_sugr', 'LinkedIn', __( 'Matches visitors to LinkedIn for ads.', 'goconsent' ), 'cookie', self::months( 3 ) ), self::row( 'bcookie', 'LinkedIn', __( 'Identifies the browser for LinkedIn ads.', 'goconsent' ), 'cookie', self::years( 1 ) ), self::row( 'lidc', 'LinkedIn', __( 'Routes requests to LinkedIn servers.', 'goconsent' ), 'cookie', self::days( 1 ) ), ), 'bing' => array( self::row( '_uetsid', 'Microsoft Ads', __( 'Measures ad conversions during the visit.', 'goconsent' ), 'cookie', self::days( 1 ) ), self::row( '_uetvid', 'Microsoft Ads', __( 'Recognises returning visitors for ads.', 'goconsent' ), 'cookie', self::months( 13 ) ), self::row( 'MUID', 'Microsoft', __( 'Identifies the browser across Microsoft sites.', 'goconsent' ), 'cookie', self::months( 13 ) ), ), 'pinterest' => array( self::row( '_pin_unauth', 'Pinterest', __( 'Measures ads and builds ad audiences.', 'goconsent' ), 'cookie', self::years( 1 ) ), self::row( '_derived_epik', 'Pinterest', __( 'Matches ad clicks to visits.', 'goconsent' ), 'cookie', self::years( 1 ) ), ), 'snap' => array( self::row( '_scid', 'Snap', __( 'Measures ads and builds ad audiences.', 'goconsent' ), 'cookie', self::months( 13 ) ), ), 'x' => array( self::row( 'muc_ads', 'X (Twitter)', __( 'Measures ads and builds ad audiences.', 'goconsent' ), 'cookie', self::months( 13 ) ), self::row( 'personalization_id', 'X (Twitter)', __( 'Personalises ads on X.', 'goconsent' ), 'cookie', self::months( 13 ) ), ), 'reddit' => array( self::row( '_rdt_uuid', 'Reddit', __( 'Measures ads and builds ad audiences.', 'goconsent' ), 'cookie', self::months( 3 ) ), ), 'adroll' => array( self::row( '__adroll_fpc', 'AdRoll', __( 'Recognises returning visitors for ads.', 'goconsent' ), 'cookie', self::months( 13 ) ), self::row( '__ar_v4', 'AdRoll', __( 'Measures ad conversions.', 'goconsent' ), 'cookie', self::months( 13 ) ), ), 'criteo' => array( self::row( 'cto_bundle', 'Criteo', __( 'Shows ads for products you viewed.', 'goconsent' ), 'cookie', self::months( 13 ) ), ), 'hubspot' => array( self::row( '__hstc', 'HubSpot', __( 'Tracks visits for marketing.', 'goconsent' ), 'cookie', self::months( 6 ) ), self::row( 'hubspotutk', 'HubSpot', __( 'Links form submissions to visits.', 'goconsent' ), 'cookie', self::months( 6 ) ), self::row( '__hssc', 'HubSpot', __( 'Counts pages in the current visit.', 'goconsent' ), 'cookie', self::minutes( 30 ) ), ), 'convertbox' => array( self::row( 'convertbox.com', 'ConvertBox', __( 'Shows pop-ups (offers, announcements and notices), measures how they perform and remembers which you saw.', 'goconsent' ), 'local', self::varies() ), ), 'clarity' => array( self::row( '_clck', 'Microsoft Clarity', __( 'Recognises returning visitors.', 'goconsent' ), 'cookie', self::years( 1 ) ), self::row( '_clsk', 'Microsoft Clarity', __( 'Connects page views in one visit.', 'goconsent' ), 'cookie', self::days( 1 ) ), ), 'hotjar' => array( self::row( '_hjSessionUser_*', 'Hotjar', __( 'Recognises returning visitors.', 'goconsent' ), 'cookie', self::years( 1 ) ), self::row( '_hjSession_*', 'Hotjar', __( 'Keeps the current visit together.', 'goconsent' ), 'cookie', self::minutes( 30 ) ), ), 'yandex' => array( self::row( '_ym_uid', 'Yandex Metrica', __( 'Recognises returning visitors.', 'goconsent' ), 'cookie', self::years( 1 ) ), self::row( '_ym_d', 'Yandex Metrica', __( 'Remembers the first visit date.', 'goconsent' ), 'cookie', self::years( 1 ) ), ), 'userway' => array( self::row( 'userway-*', 'UserWay', __( 'Remembers your accessibility settings.', 'goconsent' ), 'local', self::varies() ), ), 'recaptcha' => array( self::row( '_GRECAPTCHA', 'Google reCAPTCHA', __( 'Tells people from bots to protect forms.', 'goconsent' ), 'local', self::months( 6 ) ), ), 'turnstile' => array( self::row( 'Turnstile', 'Cloudflare', __( 'Tells people from bots to protect forms; sets no cookies.', 'goconsent' ), 'script', self::none() ), ), 'stripe' => array( self::row( '__stripe_mid', 'Stripe', __( 'Prevents payment fraud.', 'goconsent' ), 'cookie', self::years( 1 ) ), self::row( '__stripe_sid', 'Stripe', __( 'Prevents payment fraud during the visit.', 'goconsent' ), 'cookie', self::minutes( 30 ) ), ), 'paypal' => array( self::row( 'PayPal', 'PayPal', __( 'Processes payments and prevents fraud.', 'goconsent' ), 'script', self::varies() ), ), 'maps_js' => array( self::row( 'NID', 'Google Maps', __( 'Remembers map preferences.', 'goconsent' ), 'cookie', self::months( 6 ) ), ), 'embed:gmaps' => array( self::row( 'NID', 'Google Maps', __( 'Remembers map preferences.', 'goconsent' ), 'cookie', self::months( 6 ) ), ), 'embed:youtube' => array( self::row( 'YSC', 'YouTube', __( 'Counts video views.', 'goconsent' ), 'cookie', self::session() ), self::row( 'VISITOR_INFO1_LIVE', 'YouTube', __( 'Estimates your connection speed for playback.', 'goconsent' ), 'cookie', self::months( 6 ) ), self::row( 'VISITOR_PRIVACY_METADATA', 'YouTube', __( 'Stores your cookie choices on YouTube.', 'goconsent' ), 'cookie', self::months( 6 ) ), ), 'embed:vimeo' => array( self::row( 'vuid', 'Vimeo', __( 'Remembers what you watched.', 'goconsent' ), 'cookie', self::years( 2 ) ), ), 'fonts:google' => array( self::row( 'fonts.googleapis.com', 'Google Fonts', __( 'Loads web fonts; Google receives your IP address. Sets no cookies.', 'goconsent' ), 'request', self::none() ), ), 'hq_rental' => array( self::row( 'HQ Rental', 'HQ Rental Software', __( 'Runs online booking and keeps your booking session.', 'goconsent' ), 'script', self::session() ), ), 'resy' => array( self::row( 'Resy', 'Resy', __( 'Runs table reservations.', 'goconsent' ), 'embed', self::varies() ), ), 'opentable' => array( self::row( 'OpenTable', 'OpenTable', __( 'Runs table reservations.', 'goconsent' ), 'embed', self::varies() ), ), 'tock' => array( self::row( 'Tock', 'Tock', __( 'Runs reservations and ticketing.', 'goconsent' ), 'embed', self::varies() ), ), 'sevenrooms' => array( self::row( 'SevenRooms', 'SevenRooms', __( 'Runs table reservations.', 'goconsent' ), 'embed', self::varies() ), ), 'toast' => array( self::row( 'Toast', 'Toast', __( 'Runs online ordering.', 'goconsent' ), 'embed', self::varies() ), ), 'vendasta' => array( self::row( 'Vendasta', 'Vendasta', __( 'Runs website chat and reviews.', 'goconsent' ), 'script', self::varies() ), ), ); /** * Filter the known cookies for each scanned service. * * @param array $c Service id => rows ( name, provider, purpose, type, expires ). */ return (array) apply_filters( 'goconsent_cookie_catalog', $c ); } }