*/ public static function cookie_meta(): array { $base = self::built_in_cookie_meta(); // What the owner wrote for their own services overrides the catalog: they are the // only one who knows what that cookie does on THEIR site. foreach ( COOKROCO_Blocker::custom_services() as $svc ) { foreach ( $svc['cookies'] as $c ) { if ( '' === $c['name'] ) { continue; } $base[ $c['name'] ] = [ $svc['label'], '' !== $c['purpose'] ? $c['purpose'] : __( 'Used by the provider listed for the purpose described by this category.', 'cookie-rocket' ), '' !== $c['duration'] ? $c['duration'] : __( 'Varies', 'cookie-rocket' ), ]; } } /** * Per-cookie metadata: [ provider, purpose, duration ]. * Cookie Rocket Pro hooks its extended catalog in here. * * @param array $base */ return (array) apply_filters( 'cookroco_cookie_meta', $base ); } /** The built-in catalog. */ private static function built_in_cookie_meta(): array { return [ // Google Analytics. '_ga' => [ 'Google Analytics', __( 'Registers a unique ID used to generate statistical data on how you use the site.', 'cookie-rocket' ), __( '2 years', 'cookie-rocket' ) ], '_ga_*' => [ 'Google Analytics', __( 'Persists the analytics session state.', 'cookie-rocket' ), __( '2 years', 'cookie-rocket' ) ], '_gid' => [ 'Google Analytics', __( 'Registers a unique ID used to generate statistical data on how you use the site.', 'cookie-rocket' ), __( '1 day', 'cookie-rocket' ) ], '_gat' => [ 'Google Analytics', __( 'Throttles the request rate to limit data collection on high-traffic sites.', 'cookie-rocket' ), __( '1 minute', 'cookie-rocket' ) ], '_gat_*' => [ 'Google Analytics', __( 'Throttles the request rate to limit data collection on high-traffic sites.', 'cookie-rocket' ), __( '1 minute', 'cookie-rocket' ) ], '__utma' => [ 'Google Analytics (legacy)', __( 'Distinguishes users and sessions.', 'cookie-rocket' ), __( '2 years', 'cookie-rocket' ) ], '__utmb' => [ 'Google Analytics (legacy)', __( 'Determines new sessions and visits.', 'cookie-rocket' ), __( '30 minutes', 'cookie-rocket' ) ], '__utmc' => [ 'Google Analytics (legacy)', __( 'Interoperates with older analytics code.', 'cookie-rocket' ), __( 'Session', 'cookie-rocket' ) ], '__utmt' => [ 'Google Analytics (legacy)', __( 'Throttles the request rate.', 'cookie-rocket' ), __( '10 minutes', 'cookie-rocket' ) ], '__utmz' => [ 'Google Analytics (legacy)', __( 'Stores the traffic source or campaign that explains how you reached the site.', 'cookie-rocket' ), __( '6 months', 'cookie-rocket' ) ], // Hotjar. '_hj*' => [ 'Hotjar', __( 'Collects analytics and session-recording data about your visit.', 'cookie-rocket' ), __( '1 year', 'cookie-rocket' ) ], // Microsoft Clarity / Bing. '_clck' => [ 'Microsoft Clarity', __( 'Persists the Clarity user ID and preferences.', 'cookie-rocket' ), __( '1 year', 'cookie-rocket' ) ], '_clsk' => [ 'Microsoft Clarity', __( 'Connects multiple page views into a single session recording.', 'cookie-rocket' ), __( '1 day', 'cookie-rocket' ) ], 'CLID' => [ 'Microsoft Clarity', __( 'Identifies the first time Clarity saw this user on any site using Clarity.', 'cookie-rocket' ), __( '1 year', 'cookie-rocket' ) ], 'MUID' => [ 'Microsoft', __( 'Identifies unique browsers visiting Microsoft sites; used for advertising and analytics.', 'cookie-rocket' ), __( '13 months', 'cookie-rocket' ) ], 'SM' => [ 'Microsoft', __( 'Used in synchronizing the MUID identifier across Microsoft domains.', 'cookie-rocket' ), __( 'Session', 'cookie-rocket' ) ], // Meta / Facebook. '_fbp' => [ 'Meta (Facebook)', __( 'Stores and tracks visits across websites to deliver advertising.', 'cookie-rocket' ), __( '3 months', 'cookie-rocket' ) ], '_fbc' => [ 'Meta (Facebook)', __( 'Stores the last ad click that led you to the site, for advertising.', 'cookie-rocket' ), __( '3 months', 'cookie-rocket' ) ], 'fr' => [ 'Meta (Facebook)', __( 'Delivers, measures and improves the relevance of advertising.', 'cookie-rocket' ), __( '3 months', 'cookie-rocket' ) ], 'personalization_id' => [ 'X (Twitter)', __( 'Stores a unique identifier used to personalize advertising on X.', 'cookie-rocket' ), __( '2 years', 'cookie-rocket' ) ], // Google Ads / DoubleClick. '_gcl_au' => [ 'Google Ads', __( 'Stores ad-click information to measure advertising conversions.', 'cookie-rocket' ), __( '3 months', 'cookie-rocket' ) ], '_gcl_aw' => [ 'Google Ads', __( 'Stores ad-click information to measure advertising conversions.', 'cookie-rocket' ), __( '3 months', 'cookie-rocket' ) ], 'IDE' => [ 'Google DoubleClick', __( 'Registers and reports your actions after viewing or clicking an ad, to measure its effectiveness.', 'cookie-rocket' ), __( '13 months', 'cookie-rocket' ) ], 'test_cookie' => [ 'Google DoubleClick', __( 'Checks whether your browser supports cookies.', 'cookie-rocket' ), __( '15 minutes', 'cookie-rocket' ) ], // TikTok. '_ttp' => [ 'TikTok', __( 'Measures and improves the performance of advertising campaigns.', 'cookie-rocket' ), __( '13 months', 'cookie-rocket' ) ], // LinkedIn. 'li_sugr' => [ 'LinkedIn', __( 'Builds a probabilistic identifier used for advertising.', 'cookie-rocket' ), __( '3 months', 'cookie-rocket' ) ], 'lidc' => [ 'LinkedIn', __( 'Facilitates data-center selection.', 'cookie-rocket' ), __( '1 day', 'cookie-rocket' ) ], 'bcookie' => [ 'LinkedIn', __( 'Stores a browser identifier for advertising and site features.', 'cookie-rocket' ), __( '1 year', 'cookie-rocket' ) ], 'bscookie' => [ 'LinkedIn', __( 'Stores a secure browser identifier for advertising and site features.', 'cookie-rocket' ), __( '1 year', 'cookie-rocket' ) ], 'UserMatchHistory' => [ 'LinkedIn', __( 'Synchronizes the LinkedIn Ads identifier.', 'cookie-rocket' ), __( '1 month', 'cookie-rocket' ) ], // Pinterest. '_pin_unauth' => [ 'Pinterest', __( 'Groups actions for users that cannot be identified, for advertising.', 'cookie-rocket' ), __( '1 year', 'cookie-rocket' ) ], '_routing_id' => [ 'Pinterest', __( 'Routes advertising requests.', 'cookie-rocket' ), __( 'Session', 'cookie-rocket' ) ], ]; } /** * Render the cookie declaration. * * @param array|string $atts Shortcode attributes (currently unused; reserved). * @return string Escaped HTML. */ public function render( $atts = [] ): string { $categories = get_option( 'cookroco_cookie_categories', [] ); if ( ! is_array( $categories ) || empty( $categories ) ) { return ''; } uasort( $categories, static function ( $a, $b ) { return ( $a['order'] ?? 99 ) <=> ( $b['order'] ?? 99 ); } ); $scan = get_option( 'cookroco_last_scan', [] ); $found = ( is_array( $scan ) && ! empty( $scan['found'] ) && is_array( $scan['found'] ) ) ? $scan['found'] : []; // is_array on the nested value too: a scalar 'found' would TypeError cookies_by_category(array). $by_category = $this->cookies_by_category( $found ); $this->enqueue_styles(); $out = ''; return $out; } /** * Build per-category rows from the scanner's detected vendors, enriched with the catalog. * * @param array $found Detected vendors from cookroco_last_scan ([ id => [label,cat,cookies] ]). * @return array category key => list of [ name, provider, purpose, duration ]. */ private function cookies_by_category( array $found ): array { $meta = self::cookie_meta(); $out = []; $seen = []; foreach ( $found as $vendor ) { $cat = $vendor['cat'] ?? ''; if ( '' === $cat || empty( $vendor['cookies'] ) ) { continue; } foreach ( (array) $vendor['cookies'] as $token ) { $dedupe = $cat . '|' . $token; if ( isset( $seen[ $dedupe ] ) ) { continue; } $seen[ $dedupe ] = true; if ( isset( $meta[ $token ] ) ) { $provider = $meta[ $token ][0]; $purpose = $meta[ $token ][1]; $duration = $meta[ $token ][2]; } else { $provider = $vendor['label'] ?? __( 'Third party', 'cookie-rocket' ); $purpose = __( 'Used by the provider listed for the purpose described by this category.', 'cookie-rocket' ); $duration = __( 'Varies', 'cookie-rocket' ); } $out[ $cat ][] = [ $token, $provider, $purpose, $duration ]; } } return $out; } /** * Strictly-necessary cookies that this site sets regardless of consent. * * @return array */ private function necessary_rows(): array { $rows = [ [ COOKROCO_COOKIE_NAME, get_bloginfo( 'name' ), __( 'Stores your cookie consent preferences so the banner is not shown again.', 'cookie-rocket' ), $this->consent_duration_label(), ], ]; $rows[] = [ 'cookroco_session', get_bloginfo( 'name' ), __( 'Links your later consent decisions to the same anonymous record, as proof of consent.', 'cookie-rocket' ), __( '1 year', 'cookie-rocket' ) ]; $rows[] = [ 'wordpress_*', 'WordPress', __( 'Maintains your authenticated session while you are signed in.', 'cookie-rocket' ), __( 'Session', 'cookie-rocket' ) ]; $rows[] = [ 'wp-settings-*', 'WordPress', __( 'Remembers your personal interface settings inside WordPress.', 'cookie-rocket' ), __( '1 year', 'cookie-rocket' ) ]; if ( class_exists( 'WooCommerce' ) ) { $rows[] = [ 'woocommerce_cart_hash', 'WooCommerce', __( 'Helps the store remember the contents of your cart.', 'cookie-rocket' ), __( 'Session', 'cookie-rocket' ) ]; $rows[] = [ 'woocommerce_items_in_cart', 'WooCommerce', __( 'Helps the store remember the contents of your cart.', 'cookie-rocket' ), __( 'Session', 'cookie-rocket' ) ]; $rows[] = [ 'wp_woocommerce_session_*', 'WooCommerce', __( 'Holds a unique reference to your cart and session data on the server.', 'cookie-rocket' ), __( '2 days', 'cookie-rocket' ) ]; } /** * Strictly-necessary cookies: [ name, provider, purpose, duration ]. * Cookie Rocket Pro adds the ones its own features set. * * @param array $rows */ return (array) apply_filters( 'cookroco_necessary_cookies', $rows ); } /** Human-readable label for the configured consent lifetime. */ private function consent_duration_label(): string { $settings = get_option( 'cookroco_settings', [] ); $days = isset( $settings['consent_duration_days'] ) ? (int) $settings['consent_duration_days'] : 365; if ( $days < 1 ) { $days = 365; } // The longest any visitor keeps it: Pro regions can keep consent longer than the general setting. $days = max( $days, absint( apply_filters( 'cookroco_longest_consent_days', $days ) ) ); return sprintf( /* translators: %d: number of days the consent choice is stored. */ _n( '%d day', '%d days', $days, 'cookie-rocket' ), $days ); } /** * Render one declaration table. * * @param array $rows List of [ name, provider, purpose, duration ]. * @return string Escaped HTML table. */ private function render_table( array $rows ): string { $html = ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; foreach ( $rows as $row ) { $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; } $html .= ''; return $html; } /** Register and enqueue the (inline) table styles. Loads only on pages with the shortcode. */ private function enqueue_styles(): void { $handle = 'cookie-rocket-policy'; if ( wp_style_is( $handle, 'enqueued' ) ) { return; } wp_register_style( $handle, false, [], COOKROCO_VERSION ); wp_enqueue_style( $handle ); wp_add_inline_style( $handle, '.cr-cookie-policy{font-size:15px;line-height:1.6}' . '.cr-cookie-policy__heading{margin:1.6em 0 .3em;font-size:1.15em}' . '.cr-cookie-policy__desc{margin:0 0 .8em;opacity:.8}' . '.cr-cookie-policy__table{width:100%;border-collapse:collapse;margin:0 0 1em;font-size:.92em}' . '.cr-cookie-policy__table th,.cr-cookie-policy__table td{border:1px solid rgba(0,0,0,.12);padding:.55em .7em;text-align:left;vertical-align:top}' . '.cr-cookie-policy__table th{background:rgba(0,0,0,.04);font-weight:600}' . '.cr-cookie-policy__table code{font-size:.92em;word-break:break-word}' . '.cr-cookie-policy__table tr:nth-child(even) td{background:rgba(0,0,0,.02)}' . '.cr-cookie-policy__updated{margin-top:1.2em;opacity:.7}' ); } }