settings = ATT_Consent::get_settings(); // Priority 1: consent defaults + attribution capture (must be first in
). add_action( 'wp_head', array( $this, 'output_consent_defaults' ), 1 ); // Priority 2: gtag.js or GTM (Advanced mode only). add_action( 'wp_head', array( $this, 'output_tracking_script' ), 2 ); // Custom scripts as inert type="text/plain" tags. add_action( 'wp_head', array( $this, 'output_custom_scripts_head' ), 99 ); add_action( 'wp_footer', array( $this, 'output_custom_scripts_footer' ), 99 ); // Footer: banner HTML. add_action( 'wp_footer', array( $this, 'output_banner_html' ), 5 ); // Enqueue banner assets. add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) ); } /** * Output the inline consent defaults script. * This MUST run before any gtag/GTM scripts. */ public function output_consent_defaults() { $s = $this->settings; $config = array( 'consent_mode' => $s['consent_mode'], 'cmp_mode' => $s['cmp_mode'], 'consent_expiry' => (int) $s['consent_expiry'], 'wait_for_update' => (int) $s['wait_for_update'], 'url_passthrough' => (bool) $s['url_passthrough'], 'ads_data_redaction' => (bool) $s['ads_data_redaction'], 'tracking_mode' => $s['tracking_mode'], ); // Cross-domain tracking domains. if ( ! empty( $s['cross_domain_domains'] ) ) { $domains = array_filter( array_map( 'trim', explode( "\n", $s['cross_domain_domains'] ) ) ); if ( ! empty( $domains ) ) { $config['linker_domains'] = array_values( $domains ); } } // In basic mode, pass the tracking snippet for deferred injection. // Basic mode only applies when this plugin owns consent (self mode). if ( 'basic' === $s['consent_mode'] && 'self' === $s['cmp_mode'] ) { $config['tracking_snippet'] = $this->get_tracking_snippet(); } $config_json = wp_json_encode( $config ); $is_tcf = ( 'tcf_bridge' === $s['cmp_mode'] ); ?> settings; // In basic mode, tracking is deferred until consent. if ( 'basic' === $s['consent_mode'] ) { return; } echo $this->get_tracking_snippet(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- contains script tags. } /** * Get the tracking script snippet HTML. * * @return string */ private function get_tracking_snippet() { $s = $this->settings; if ( 'gtm' === $s['tracking_mode'] && ! empty( $s['gtm_container_id'] ) ) { $id = esc_attr( $s['gtm_container_id'] ); return " "; } if ( 'gtag' === $s['tracking_mode'] && ! empty( $s['ga4_measurement_id'] ) ) { $id = esc_attr( $s['ga4_measurement_id'] ); return " "; } return ''; } /** * Output custom scripts in as inert type="text/plain" tags. */ public function output_custom_scripts_head() { echo ATT_Consent_Scripts_Manager::render_scripts_as_html( 'head' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- contains script/template tags. } /** * Output custom scripts in footer as inert type="text/plain" tags. */ public function output_custom_scripts_footer() { echo ATT_Consent_Scripts_Manager::render_scripts_as_html( 'footer' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- contains script/template tags. } /** * Enqueue frontend assets. */ public function enqueue_assets() { $is_tcf = ( 'tcf_bridge' === $this->settings['cmp_mode'] ); // Banner CSS is needed in both modes — the floating widget uses it. wp_enqueue_style( 'att-consent-banner', ATT_CC_PLUGIN_URL . 'public/css/banner.css', array(), ATT_CC_VERSION ); // consent-manager.js runs in both modes (script gating, cookie I/O, WP Consent API). wp_enqueue_script( 'att-consent-manager', ATT_CC_PLUGIN_URL . 'public/js/consent-manager.js', array(), ATT_CC_VERSION, true ); if ( $is_tcf ) { // In TCF bridge mode the banner.js UI is replaced by the TCF listener. wp_enqueue_script( 'att-consent-tcf-bridge', ATT_CC_PLUGIN_URL . 'public/js/tcf-bridge.js', array( 'att-consent-manager' ), ATT_CC_VERSION, true ); return; } wp_enqueue_script( 'att-consent-banner', ATT_CC_PLUGIN_URL . 'public/js/banner.js', array( 'att-consent-manager' ), ATT_CC_VERSION, true ); } /** * Output the banner and preferences modal HTML. * In TCF bridge mode the banner and modal are suppressed (an external CMP * renders its own UI); only the optional floating widget is emitted, and * its click handler is wired to the external CMP via tcf-bridge.js. */ public function output_banner_html() { $s = $this->settings; $is_tcf = ( 'tcf_bridge' === $s['cmp_mode'] ); do_action( 'att_consent_before_banner' ); $position_class = 'att-cc-position-' . esc_attr( $s['banner_position'] ); $style_vars = sprintf( '--att-cc-bg:%s;--att-cc-text:%s;--att-cc-accept-bg:%s;--att-cc-accept-text:%s;--att-cc-reject-bg:%s;--att-cc-reject-text:%s;--att-cc-pref-bg:%s;--att-cc-pref-text:%s;', esc_attr( $s['banner_bg_color'] ), esc_attr( $s['banner_text_color'] ), esc_attr( $s['btn_accept_bg'] ), esc_attr( $s['btn_accept_text'] ), esc_attr( $s['btn_reject_bg'] ), esc_attr( $s['btn_reject_text'] ), esc_attr( $s['btn_preferences_bg'] ), esc_attr( $s['btn_preferences_text'] ) ); $banner_html = ''; $modal_html = ''; if ( ! $is_tcf ) { $banner_html = ''; $modal_html = ''; } $widget_html = ''; if ( 'none' !== $s['floating_widget'] ) { $widget_position = 'right' === $s['floating_widget'] ? 'att-cc-widget--right' : 'att-cc-widget--bottom-right'; $widget_label = ! empty( $s['floating_widget_label'] ) ? $s['floating_widget_label'] : 'Cookie Settings'; // In TCF bridge mode the widget is shown immediately (no banner to wait for). $widget_display = $is_tcf ? '' : 'display:none;'; $widget_html = ''; } $html = apply_filters( 'att_consent_banner_html', $banner_html . $modal_html . $widget_html, $s ); echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above. do_action( 'att_consent_after_banner' ); } }