[], 'version' => ABLOCKS_VERSION, ]; wp_enqueue_style( 'ablocks-cookie-consent', ABLOCKS_ASSETS_URL . 'build/cookie-consent.css', [], $asset['version'] ); wp_enqueue_script( 'ablocks-cookie-consent', ABLOCKS_ASSETS_URL . 'build/cookie-consent.js', [], $asset['version'], true ); // `wp_localize_script` casts every scalar to a string, which would turn // the policy version into "1" and break the strict comparison the // Consent Mode reader in makes against the integer. An inline // JSON assignment keeps the types. wp_add_inline_script( 'ablocks-cookie-consent', 'window.ABlocksConsentConfig = ' . wp_json_encode( $this->script_config() ) . ';', 'before' ); } /** * Everything the client needs to decide what to show and what to release. * * @return array */ private function script_config() { $categories = []; $known = Helper::known_cookies(); foreach ( Helper::active_categories() as $category ) { $cookies = isset( $known[ $category['slug'] ] ) ? (array) $known[ $category['slug'] ] : []; foreach ( isset( $category['cookies'] ) ? (array) $category['cookies'] : [] as $row ) { if ( ! empty( $row['name'] ) ) { $cookies[] = (string) $row['name']; } } $categories[] = [ 'slug' => $category['slug'], 'locked' => ! empty( $category['locked'] ), // Deleted when this category is withdrawn. Never for a locked // one: that list holds the consent cookie itself. 'cookies' => empty( $category['locked'] ) ? array_values( array_unique( $cookies ) ) : [], ]; } return [ 'cookie' => Helper::get( 'cookie_name', 'ablocks_consent' ), 'cookiePrev' => Helper::get( 'cookie_name_previous', '' ), 'days' => (int) Helper::get( 'cookie_days', 365 ), 'version' => (int) Helper::get( 'policy_version', 1 ), 'reconsentDays' => (int) Helper::get( 'reconsent_days', 0 ), 'mode' => Helper::get( 'mode', 'optin' ), 'categories' => $categories, 'consentMode' => (bool) Helper::get( 'consent_mode', true ), 'consentModeAds' => (bool) Helper::get( 'consent_mode_ads', true ), 'signals' => ConsentMode::signal_map(), 'record' => (bool) Helper::get( 'record_enabled', true ), 'endpoint' => rest_url( ABLOCKS_REST_NAMESPACE . '/consent' ), 'delay' => (int) Helper::banner( 'delay', 0 ), 'reopen' => self::shows_reopen(), // `heading` on a classic theme has nothing in theme.json to read, // so the client measures the page's own heading instead. 'fontSource' => Helper::banner( 'font_source', 'theme' ), 'canClose' => (bool) Helper::banner( 'show_banner_close', false ), 'closeAction' => Helper::banner( 'close_behaviour', 'dismiss' ), 'dismissDays' => (int) Helper::banner( 'dismiss_days', 0 ), 'respectGpc' => (bool) Helper::get( 'respect_gpc', true ), ]; } /** * The CSS custom properties the stylesheet reads. * * Emitted as variables rather than as rules so the whole banner can be * restyled from the settings screen without the stylesheet knowing anything * about a particular site's palette, and so a theme can override any one of * them without fighting specificity. * * @return string */ private function style_vars() { $banner = Helper::get( 'banner', [] ); $vars = [ '--ablocks-cc-bg' => $banner['bg'], '--ablocks-cc-text' => $banner['text'], '--ablocks-cc-muted' => $banner['muted'], '--ablocks-cc-border' => $banner['border'], '--ablocks-cc-accent' => $banner['accent'], '--ablocks-cc-accent-text' => $banner['accent_text'], '--ablocks-cc-secondary-bg' => $banner['secondary_bg'], '--ablocks-cc-secondary-text' => $banner['secondary_text'], '--ablocks-cc-save-bg' => $banner['save_bg'], '--ablocks-cc-save-text' => $banner['save_text'], '--ablocks-cc-locked-bg' => $banner['locked_bg'], '--ablocks-cc-locked-text' => $banner['locked_text'], '--ablocks-cc-radius' => (int) $banner['radius'] . 'px', '--ablocks-cc-max-width' => (int) $banner['max_width'] . 'px', '--ablocks-cc-prefs-max-width' => (int) $banner['prefs_max_width'] . 'px', '--ablocks-cc-font-size' => (int) $banner['font_size'] . 'px', '--ablocks-cc-weight' => (int) $banner['font_weight'], '--ablocks-cc-title-weight' => (int) $banner['title_weight'], '--ablocks-cc-btn-weight' => (int) $banner['button_weight'], ]; $font = self::font_family( $banner ); if ( '' !== $font ) { $vars['--ablocks-cc-font'] = $font; } $out = ''; foreach ( $vars as $name => $value ) { $out .= sprintf( '%s:%s;', $name, esc_attr( $value ) ); } return $out; } /** * Hold back trackers that are injected by JavaScript rather than printed. * * The two gating layers only see the HTML the server sends. A first-party * script that creates ` %s', esc_url( $url ), esc_html( $label ) ); } if ( '' === $out ) { return; } // Wrapped so two links get something between them. Adjacent inline // anchors with no separator run together into one word. echo '' . $out . ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- built from esc_url/esc_html above. } /** * The preferences body: heading, categories, and the buttons under them. * * Written once and called from either placement, so the inline panel and * the modal cannot drift apart. Only its container differs between the two. * * @param array $banner Banner settings. */ private function render_prefs( $banner ) { $accordion = ! empty( $banner['prefs_accordion'] ); $open_first = $accordion && ! empty( $banner['prefs_open_first'] ); $show_table = ! empty( $banner['show_cookie_table'] ); $checkbox = 'checkbox' === $banner['switch_style']; $badge = 'badge' === $banner['locked_style']; $index = 0; ?> Helper::banner( 'settings_label', __( 'Cookie preferences', 'ablocks' ) ), 'class' => '', ], $atts, 'ablocks_cookie_preferences' ); return sprintf( '', esc_attr( $atts['class'] ), esc_html( $atts['label'] ) ); } }