←

' . esc_html__( 'The recommended setup is prepared from your scan results. It will be ready here once the scan finishes.', 'consiva-ai' ) . '

'; return; } $active = 'active' === $data['site_status']; if ( self::is_activated() !== $active || State::get( 'site_status' ) !== $data['site_status'] ) { State::update( array( 'activated' => $active, 'site_status' => $data['site_status'], ) ); // Consiva is the source of truth. } ?> render_woo_problems( $config ); $this->render_unclassified( $data['unclassified'] ); $this->render_summary( $config, $data['jurisdiction'] ); $this->render_actions( $config, $data, $active ); } /** * Plugin §7: warn before publishing a setup that would break the WooCommerce cart. * * @param array $config Config shown. * @return void */ private function render_woo_problems( array $config ) { $problems = EssentialGuard::problems( $config ); if ( ! $problems ) { return; } ?>

> $items Unclassified findings. * @return void */ private function render_unclassified( array $items ) { if ( ! $items ) { return; } $labels = self::class_labels(); ?> $config Draft (or published) config. * @param string $jurisdiction Site jurisdiction profile (lower case). * @return void */ private function render_summary( array $config, $jurisdiction ) { $layout = isset( $config['banner_layout'] ) && isset( self::layout_labels()[ $config['banner_layout'] ] ) ? self::layout_labels()[ $config['banner_layout'] ] : self::layout_labels()['bottom']; $rows = array( __( 'Where it applies', 'consiva-ai' ) => self::jurisdiction_text( $jurisdiction ), __( 'Blocking', 'consiva-ai' ) => self::blocking_text( $config ), __( 'Banner position', 'consiva-ai' ) => $layout, __( 'Google tags', 'consiva-ai' ) => InlineBootstrap::gcm_enabled() ? __( 'Google Consent Mode v2 is on: your Google tags are told the visitor\'s choice.', 'consiva-ai' ) : __( 'Google Consent Mode is off. Turn it on below if you use Google Analytics, Google Ads or Tag Manager.', 'consiva-ai' ), ); ?> $config Config shown. * @param array $data Loaded data. * @param bool $active Whether consent controls are active. * @return void */ private function render_actions( array $config, array $data, $active ) { $pending = count( $data['unclassified'] ); if ( ! $active ) : ?> $class ) { if ( ! is_string( $class ) || ! in_array( $class, self::CLASSES, true ) || ! preg_match( ScanProgress::ID_PATTERN, (string) $finding_id ) ) { continue; } $result = $client->classify_finding( (string) $finding_id, $class ); if ( is_wp_error( $result ) ) { ScanProgress::clear_cache(); self::finish( $result ); } ++$saved; } ScanProgress::clear_cache(); Dashboard::clear_cache(); if ( 0 === $saved ) { self::finish( new WP_Error( 'consiva_consent_nothing_chosen', __( 'Choose what each item is for, then save.', 'consiva-ai' ) ) ); } /* translators: %d: number of findings. */ self::finish( null, sprintf( _n( 'Saved your choice for %d item.', 'Saved your choices for %d items.', $saved, 'consiva-ai' ), $saved ) ); } /** * Saves "Customise first" changes to the draft, or publishes them when controls are already active. * * @return void */ public function handle_customise() { self::guard( self::ACTION_CUSTOMISE ); // phpcs:disable WordPress.Security.NonceVerification.Missing -- verified in guard(). $layout = isset( $_POST['banner_layout'] ) ? sanitize_key( wp_unslash( $_POST['banner_layout'] ) ) : ''; $mode = isset( $_POST['blocking_mode'] ) ? sanitize_key( wp_unslash( $_POST['blocking_mode'] ) ) : ''; $base = isset( $_POST['base_config_version'] ) ? absint( $_POST['base_config_version'] ) : null; $publish = ! empty( $_POST['publish'] ); $gcm = ! empty( $_POST['gcm'] ); // phpcs:enable // Plugin-side setting: saved whatever Consiva answers, and used from the next page view. if ( InlineBootstrap::gcm_enabled() !== $gcm ) { State::update( array( 'gcm' => $gcm ) ); } $changes = array(); if ( in_array( $layout, self::LAYOUTS, true ) ) { $changes['banner_layout'] = $layout; } if ( in_array( $mode, self::BLOCKING_MODES, true ) ) { $changes['blocking_mode'] = $mode; } $result = ( new ConsivaClient() )->update_consent_config( $changes, $base ? $base : null, $publish ); if ( is_wp_error( $result ) ) { self::finish( $result ); } $version = 0; if ( $publish ) { self::refresh_front_end(); $map = CategoryMap::get(); $version = null !== $map ? (int) $map['version'] : 0; $update = array( 'config_version' => $version ); // A publish on an active site queues a verification re-scan. $verify = isset( $result['body']['verificationScanId'] ) && is_string( $result['body']['verificationScanId'] ) ? $result['body']['verificationScanId'] : ''; if ( preg_match( ScanProgress::ID_PATTERN, $verify ) ) { $update['verification_scan_id'] = $verify; Verification::clear_cache(); } State::update( $update ); Dashboard::clear_cache(); } self::finish( null, $publish ? ( $version /* translators: %d: configuration version number. */ ? sprintf( __( 'Saved and published as configuration version %d.', 'consiva-ai' ), $version ) : __( 'Saved and published.', 'consiva-ai' ) ) : __( 'Saved. Your changes will be used when you activate consent controls.', 'consiva-ai' ), $result['request_id'] ); } /** * Activate Consent Controls: publishes the configuration and arms enforcement (§5.5). * * @return void */ public function handle_activate() { self::guard( self::ACTION_ACTIVATE ); $client = new ConsivaClient(); // Re-check on the server: the disabled button is only a convenience. $pending = $client->get_findings( array( 'state' => 'unclassified', 'limit' => '1', ) ); if ( is_wp_error( $pending ) ) { self::finish( $pending ); } if ( ! empty( $pending['body']['data'] ) ) { self::finish( new WP_Error( 'consiva_consent_unconfirmed', __( 'Confirm what the unclassified items are for before activating. Until then Consiva cannot tell whether to block them.', 'consiva-ai' ) ) ); } $result = $client->activate_site(); if ( self::is_conflict( $result ) ) { // 409: nothing published yet. Publish the current draft (or the default), then try once more. $published = $client->update_consent_config( array(), null, true ); if ( is_wp_error( $published ) ) { self::finish( $published ); } $result = $client->activate_site(); } if ( is_wp_error( $result ) ) { self::finish( $result ); } // The answer carries the status, the activation time and the id of the verification re-scan. $body = $result['body']; $verify = isset( $body['verification_scan_id'] ) && is_string( $body['verification_scan_id'] ) && preg_match( ScanProgress::ID_PATTERN, $body['verification_scan_id'] ) ? $body['verification_scan_id'] : null; State::update( array( 'activated' => true, 'site_status' => 'active', 'verification_scan_id' => $verify, ) ); self::refresh_front_end(); // Caches the script key and banner URL; the banner loads from the next page view. $map = CategoryMap::get(); $version = null !== $map ? (int) $map['version'] : 0; State::update( array( 'config_version' => $version ) ); ScanProgress::clear_cache(); Verification::clear_cache(); Dashboard::clear_cache(); $message = sprintf( /* translators: %d: configuration version number. */ __( 'Consent controls activated (configuration version %d).', 'consiva-ai' ), $version ); if ( null !== $verify ) { $message .= ' ' . __( 'Consiva is now re-scanning your site to check that trackers really stay blocked until visitors agree.', 'consiva-ai' ); } Notices::push( 'success', $message, $result['request_id'] ); wp_safe_redirect( Screens::url() ); exit; } // ------------------------------------------------------------------ Data /** * @return array|WP_Error */ private static function load() { $client = new ConsivaClient(); $config = $client->get_consent_config(); if ( is_wp_error( $config ) ) { return $config; } $site = $client->get_site(); if ( is_wp_error( $site ) ) { return $site; } $unclassified = $client->get_findings( array( 'state' => 'unclassified', 'limit' => (string) ScanProgress::FINDINGS_LIMIT, ) ); if ( is_wp_error( $unclassified ) ) { return $unclassified; } $body = $config['body']; $items = array(); foreach ( isset( $unclassified['body']['data'] ) && is_array( $unclassified['body']['data'] ) ? $unclassified['body']['data'] : array() as $f ) { if ( ! is_array( $f ) || ! isset( $f['finding_id'] ) || ! preg_match( ScanProgress::ID_PATTERN, (string) $f['finding_id'] ) ) { continue; } $items[] = array( 'id' => (string) $f['finding_id'], 'identifier' => isset( $f['identifier'] ) ? sanitize_text_field( (string) $f['identifier'] ) : '', 'type' => isset( $f['finding_type'] ) ? sanitize_key( (string) $f['finding_type'] ) : '', 'vendor' => isset( $f['vendor'] ) && is_string( $f['vendor'] ) ? sanitize_text_field( $f['vendor'] ) : '', 'note' => isset( $f['risk_note'] ) && is_string( $f['risk_note'] ) ? sanitize_text_field( $f['risk_note'] ) : '', ); } return array( // draft falls back to the published config when there is no draft. 'draft' => isset( $body['draft'] ) && is_array( $body['draft'] ) ? $body['draft'] : null, 'published' => isset( $body['config'] ) && is_array( $body['config'] ) ? $body['config'] : null, 'config_version' => isset( $body['configVersion'] ) && is_numeric( $body['configVersion'] ) ? (int) $body['configVersion'] : 0, 'site_status' => isset( $site['body']['status'] ) ? sanitize_key( (string) $site['body']['status'] ) : '', 'jurisdiction' => isset( $site['body']['jurisdiction_profile'] ) ? sanitize_key( (string) $site['body']['jurisdiction_profile'] ) : '', 'unclassified' => $items, ); } // ------------------------------------------------------------------ Wording /** * @return array */ private static function class_labels() { return array( 'necessary' => __( 'Necessary', 'consiva-ai' ), 'preferences' => __( 'Preferences', 'consiva-ai' ), 'statistics' => __( 'Statistics', 'consiva-ai' ), 'marketing' => __( 'Marketing', 'consiva-ai' ), ); } /** * @return array */ private static function layout_labels() { return array( 'bottom' => __( 'Bar at the bottom of the page', 'consiva-ai' ), 'top' => __( 'Bar at the top of the page', 'consiva-ai' ), 'modal' => __( 'Box in the middle of the page', 'consiva-ai' ), 'floating' => __( 'Small box in a corner', 'consiva-ai' ), 'slide-in' => __( 'Panel that slides in from the side', 'consiva-ai' ), ); } /** * @param string $type Finding type. * @return string */ private static function type_label( $type ) { $labels = array( 'cookie' => __( 'Cookie', 'consiva-ai' ), 'script' => __( 'Script', 'consiva-ai' ), 'tracker' => __( 'Tracker', 'consiva-ai' ), 'pixel' => __( 'Tracking pixel', 'consiva-ai' ), 'embed' => __( 'Embedded content', 'consiva-ai' ), 'storage' => __( 'Browser storage', 'consiva-ai' ), ); return isset( $labels[ $type ] ) ? $labels[ $type ] : __( 'Item', 'consiva-ai' ); } /** * @param string $profile Jurisdiction profile, lower case. * @return string */ private static function jurisdiction_text( $profile ) { switch ( $profile ) { case 'in': return __( 'India.', 'consiva-ai' ); case 'global': return __( 'India, the EU and UK, and the rest of the world, each with its own rules.', 'consiva-ai' ); default: return __( 'India, with EU and UK rules applied automatically to visitors from there.', 'consiva-ai' ); } } /** * @param array $config Config. * @return string */ private static function blocking_text( array $config ) { return isset( $config['blocking_mode'] ) && 'observe' === $config['blocking_mode'] ? __( 'Observe only: nothing is blocked. Trackers run before the visitor chooses, and Consiva records what would have been blocked.', 'consiva-ai' ) : __( 'Enforce: well-known trackers and embeds (analytics, advertising, social media, video and maps) are blocked until the visitor agrees.', 'consiva-ai' ); } // ------------------------------------------------------------------ Helpers /** * @param mixed $result Result of a ConsivaClient call. * @return bool Whether it is a 409. */ private static function is_conflict( $result ) { if ( ! is_wp_error( $result ) ) { return false; } $data = $result->get_error_data(); return is_array( $data ) && isset( $data['status'] ) && 409 === (int) $data['status']; } /** * Rebuilds the front-end cache now, so the published config applies from the next page view. * If Consiva cannot be reached, the front end fails closed and a background retry is queued. * * @return void */ public static function refresh_front_end() { if ( is_wp_error( CategoryMap::refresh() ) ) { CategoryMap::schedule_refresh(); } } /** * @param string $action Nonce action. * @return void */ private static function guard( $action ) { if ( ! current_user_can( Onboarding::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to change the Consiva setup.', 'consiva-ai' ), '', array( 'response' => 403 ) ); } check_admin_referer( $action ); } /** * Shows the outcome on the setup screen and stops. * * @param WP_Error|null $error Error, or null on success. * @param string $message Success message. * @param string $request_id X-Request-Id. * @return void */ private static function finish( $error, $message = '', $request_id = '' ) { if ( $error instanceof WP_Error ) { Notices::push_error( $error ); } else { Notices::push( 'success', $message, $request_id ); } wp_safe_redirect( self::url() ); exit; } }