, keeps caching and * optimisation plugins away from it and passes the visitor's choice on to * the WP Consent API. * * Katalog: katalog/49-wordpress-plugin.md im Cookiepass-Repository. */ if ( ! defined( 'ABSPATH' ) ) { exit; } define( 'COOKIEPASS_VERSION', '1.0.3' ); define( 'COOKIEPASS_OPTION_ID', 'cookiepass_property_id' ); define( 'COOKIEPASS_OPTION_DOMAIN', 'cookiepass_domain' ); define( 'COOKIEPASS_OPTION_BLOCKING', 'cookiepass_blocking' ); // Overridable in wp-config.php (staging, tests). Not settings on purpose: // nobody should be able to point the banner somewhere else from the admin. if ( ! defined( 'COOKIEPASS_APP_URL' ) ) { define( 'COOKIEPASS_APP_URL', 'https://cookiepass.io' ); } if ( ! defined( 'COOKIEPASS_CDN_URL' ) ) { define( 'COOKIEPASS_CDN_URL', 'https://cdn.cookiepass.io' ); } /** * A property ID is a UUID. Anything else is not one of ours. * * @param mixed $id Value to check. * @return bool */ function cookiepass_is_valid_id( $id ) { return is_string( $id ) && 1 === preg_match( '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $id ); } /** * The stored property ID, or '' when the site is not connected. * * @return string */ function cookiepass_property_id() { $id = trim( (string) get_option( COOKIEPASS_OPTION_ID, '' ) ); return cookiepass_is_valid_id( $id ) ? strtolower( $id ) : ''; } /** * 'auto' blocks known trackers until consent, 'manual' only releases scripts * marked with type="text/plain". Same values as the snippet in the dashboard. * * @return string */ function cookiepass_blocking_mode() { return 'manual' === get_option( COOKIEPASS_OPTION_BLOCKING, 'auto' ) ? 'manual' : 'auto'; } /* ── Frontend ─────────────────────────────────────────────────────────── */ /** * Attributes that keep caching and optimisation plugins away from cp.js. * * cp.js must run before every tracker. Deferred, delayed, combined or moved * into the footer, the trackers above it run first – without consent. * * @return array */ function cookiepass_untouched_attributes() { return array( 'data-cfasync' => 'false', // Cloudflare Rocket Loader. 'data-no-optimize' => '1', // LiteSpeed Cache. 'data-no-defer' => '1', // LiteSpeed Cache. 'data-noptimize' => '1', // Autoptimize. 'nowprocket' => true, // WP Rocket. 'data-jetpack-boost' => 'ignore', // Jetpack Boost. 'data-wpmeteor-nooptimize' => 'true', // WP Meteor. 'data-pagespeed-no-defer' => true, // PageSpeed module. ); } /** * The banner script – the first thing in . * * Printed directly at wp_head priority -1000 instead of being enqueued: * enqueued scripts are printed at priority 9, and analytics plugins that * write their tags straight into wp_head would come first. No async, no * defer – either would let the browser run trackers before cp.js. */ function cookiepass_print_banner() { $id = cookiepass_property_id(); if ( '' === $id || is_admin() || is_feed() || cookiepass_printed() || cookiepass_is_builder_preview() ) { return; } if ( function_exists( 'amp_is_request' ) && amp_is_request() ) { return; // AMP pages cannot run custom scripts. } cookiepass_printed( true ); $attributes = array_merge( array( 'id' => 'cookiepass-banner', 'src' => COOKIEPASS_CDN_URL . '/cp.js', 'data-cbid' => $id, 'data-blocking' => cookiepass_blocking_mode(), ), cookiepass_untouched_attributes() ); /** * Filters the attributes of the Cookiepass script tag. * * @param array $attributes Script tag attributes. */ $attributes = apply_filters( 'cookiepass_script_attributes', $attributes ); wp_print_script_tag( $attributes ); if ( function_exists( 'wp_has_consent' ) ) { wp_print_inline_script_tag( cookiepass_consent_api_bridge(), array_merge( array( 'id' => 'cookiepass-wp-consent-api' ), cookiepass_untouched_attributes() ) ); } } add_action( 'wp_head', 'cookiepass_print_banner', -1000 ); // Themes that never call wp_head(): the banner still appears, printed at the // start of the body or in the footer. Later than ideal for blocking, but // better than no banner at all. cookiepass_printed() prevents a second copy. add_action( 'wp_body_open', 'cookiepass_print_banner', -1000 ); add_action( 'wp_footer', 'cookiepass_print_banner', -1000 ); /** * Whether the banner was already printed on this request. * * @param bool|null $set Pass true to mark it as printed. * @return bool */ function cookiepass_printed( $set = null ) { static $printed = false; if ( true === $set ) { $printed = true; } return $printed; } /** * Page builder and customizer previews: a consent dialog over the editor * canvas would block editing. The banner shows on the real page only. * * @return bool */ function cookiepass_is_builder_preview() { if ( is_customize_preview() ) { return true; } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- only checks whether an editor preview is open. foreach ( array( 'elementor-preview', 'fl_builder', 'et_fb', 'bricks', 'ct_builder', 'tve', 'brizy-edit-iframe', 'vcv-editable' ) as $param ) { if ( isset( $_GET[ $param ] ) ) { return true; } } // phpcs:enable WordPress.Security.NonceVerification.Recommended if ( function_exists( 'et_core_is_fb_enabled' ) && et_core_is_fb_enabled() ) { return true; } if ( function_exists( 'bricks_is_builder' ) && bricks_is_builder() ) { return true; } if ( class_exists( 'Elementor\\Plugin' ) && isset( \Elementor\Plugin::$instance->preview ) && is_object( \Elementor\Plugin::$instance->preview ) && method_exists( \Elementor\Plugin::$instance->preview, 'is_preview_mode' ) && \Elementor\Plugin::$instance->preview->is_preview_mode() ) { return true; } return false; } /* ── Caching and optimisation plugins ─────────────────────────────────── */ // Autoptimize: comma-separated list of strings that exclude a script. add_filter( 'autoptimize_filter_js_exclude', function ( $exclude ) { return trim( (string) $exclude . ', cdn.cookiepass.io, cookiepass-wp-consent-api', ', ' ); } ); // WP Rocket: delay, defer and external minification. foreach ( array( 'rocket_delay_js_exclusions', 'rocket_exclude_defer_js', 'rocket_minify_excluded_external_js' ) as $cookiepass_rocket_filter ) { add_filter( $cookiepass_rocket_filter, function ( $exclusions ) { $exclusions = is_array( $exclusions ) ? $exclusions : array(); $exclusions[] = 'cdn.cookiepass.io'; $exclusions[] = 'cookiepass-wp-consent-api'; return $exclusions; } ); } unset( $cookiepass_rocket_filter ); /* ── Page caches ──────────────────────────────────────────────────────── */ /** * Empties the page caches of the common caching plugins. * * Without this, a cached page keeps being served without the banner after * connecting – measured on 20 Oct 2026 on a customer site with WP-Optimize: * connected at 11:34, still no cp.js in the delivered HTML afterwards. * Every call is guarded; a plugin that is not installed is simply skipped. */ function cookiepass_purge_page_caches() { // WP-Optimize. if ( function_exists( 'wpo_cache_flush' ) ) { wpo_cache_flush(); } if ( function_exists( 'WP_Optimize' ) ) { $cookiepass_wpo = WP_Optimize(); if ( is_object( $cookiepass_wpo ) && method_exists( $cookiepass_wpo, 'get_page_cache' ) ) { $cookiepass_cache = $cookiepass_wpo->get_page_cache(); if ( is_object( $cookiepass_cache ) && method_exists( $cookiepass_cache, 'purge' ) ) { $cookiepass_cache->purge(); } } } // WP Rocket. if ( function_exists( 'rocket_clean_domain' ) ) { rocket_clean_domain(); } // W3 Total Cache. if ( function_exists( 'w3tc_flush_all' ) ) { w3tc_flush_all(); } // WP Super Cache. if ( function_exists( 'wp_cache_clear_cache' ) ) { wp_cache_clear_cache(); } // SiteGround Optimizer. if ( function_exists( 'sg_cachepress_purge_cache' ) ) { sg_cachepress_purge_cache(); } // Autoptimize (combined CSS/JS). if ( class_exists( 'autoptimizeCache' ) && method_exists( 'autoptimizeCache', 'clearall' ) ) { autoptimizeCache::clearall(); } // Plugins that listen to an action: LiteSpeed, WP Fastest Cache, Cache Enabler, // Breeze, Hummingbird, Nginx Helper. An action nobody listens to does nothing. foreach ( array( 'litespeed_purge_all', 'wpfc_clear_all_cache', 'cache_enabler_clear_complete_cache', 'breeze_clear_all_cache', 'wphb_clear_page_cache', 'rt_nginx_helper_purge_all' ) as $cookiepass_action ) { do_action( $cookiepass_action ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- hooks of other plugins. } wp_cache_flush(); } // Connecting, disconnecting, a new ID or blocking mode: every change to what // the page prints needs fresh pages. foreach ( array( COOKIEPASS_OPTION_ID, COOKIEPASS_OPTION_BLOCKING ) as $cookiepass_option ) { add_action( 'add_option_' . $cookiepass_option, 'cookiepass_purge_page_caches' ); add_action( 'update_option_' . $cookiepass_option, 'cookiepass_purge_page_caches' ); add_action( 'delete_option_' . $cookiepass_option, 'cookiepass_purge_page_caches' ); } unset( $cookiepass_option ); /* ── Self-check: does the homepage really deliver the banner? ────────── */ /** * Loads the homepage the way a visitor gets it and looks for the banner. * * Hosting caches (Varnish at the host, Cloudflare APO and the like) cannot * be emptied by any plugin – this check at least makes the problem visible * instead of leaving the site silently without a banner. The result is kept * for ten minutes. * * @param bool $fresh Ignore the stored result. * @return array{status: string, csp?: bool} */ function cookiepass_check_homepage( $fresh = false ) { $id = cookiepass_property_id(); if ( '' === $id ) { return array( 'status' => 'off' ); } $key = 'cookiepass_check_' . md5( $id . home_url( '/' ) ); if ( ! $fresh ) { $stored = get_transient( $key ); if ( is_array( $stored ) ) { return $stored; } } $response = wp_remote_get( home_url( '/' ), array( 'timeout' => 8, 'redirection' => 3, 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0 Safari/537.36 Cookiepass-Check', 'headers' => array( 'Cache-Control' => 'no-cache' ), ) ); $result = array( 'status' => 'unknown' ); if ( ! is_wp_error( $response ) && 200 === (int) wp_remote_retrieve_response_code( $response ) ) { $html = (string) wp_remote_retrieve_body( $response ); if ( false !== stripos( $html, 'data-cbid="' . $id . '"' ) ) { $result['status'] = 'ok'; } elseif ( false !== stripos( $html, 'cookiepass.io/cp.js' ) ) { $result['status'] = 'other'; // A different property ID, e.g. a script added by hand. } else { $result['status'] = 'missing'; } $csp = (string) wp_remote_retrieve_header( $response, 'content-security-policy' ); if ( '' !== $csp && preg_match( '/(?:^|;)\s*(script-src|default-src)\s+([^;]*)/i', $csp, $match ) ) { $result['csp'] = false === stripos( $match[2], 'cookiepass.io' ) && false === strpos( $match[2], '*' ); } } set_transient( $key, $result, 10 * MINUTE_IN_SECONDS ); return $result; } /** * Same site: equal hosts, or one is a subdomain of the other (www ignored). * * @param string $a Host. * @param string $b Host. * @return bool */ function cookiepass_same_site( $a, $b ) { $a = preg_replace( '/^www\./', '', strtolower( (string) $a ) ); $b = preg_replace( '/^www\./', '', strtolower( (string) $b ) ); if ( '' === $a || '' === $b ) { return true; // Nothing to compare. } return $a === $b || substr( $a, -strlen( '.' . $b ) ) === '.' . $b || substr( $b, -strlen( '.' . $a ) ) === '.' . $a; } /** * Status lines on the settings page, below “Connected”. */ function cookiepass_render_checks() { $domain = (string) get_option( COOKIEPASS_OPTION_DOMAIN, '' ); if ( '' !== $domain && ! cookiepass_same_site( cookiepass_site_host(), $domain ) ) { printf( '

%s

', esc_html( sprintf( /* translators: 1: current domain of the site, 2: connected domain */ __( 'This site runs on %1$s but is connected to %2$s. Consents from this site are rejected – please disconnect and connect again.', 'cookiepass' ), cookiepass_site_host(), $domain ) ) ); } $check = cookiepass_check_homepage(); $lines = array( 'ok' => array( 'success', __( 'Your homepage delivers the banner.', 'cookiepass' ) ), 'missing' => array( 'warning', __( 'Your homepage does not deliver the banner yet. Clear the page cache below – if your host or Cloudflare caches pages, clear that cache too.', 'cookiepass' ) ), 'other' => array( 'warning', __( 'Your homepage loads Cookiepass with a different property ID, probably a script added by hand. Remove it.', 'cookiepass' ) ), ); if ( isset( $lines[ $check['status'] ] ) ) { printf( '

%2$s

', esc_attr( $lines[ $check['status'] ][0] ), esc_html( $lines[ $check['status'] ][1] ) ); } if ( ! empty( $check['csp'] ) ) { printf( '

%s

', esc_html__( 'Your site sends a Content Security Policy that does not allow cdn.cookiepass.io. Allow it under script-src, style-src and connect-src, otherwise browsers block the banner.', 'cookiepass' ) ); } } /* ── Other cookie plugins ─────────────────────────────────────────────── */ /** * Active cookie consent plugins next to Cookiepass – two banners would be * shown, and two scripts blocking each other. Returns their names. * * @return string[] */ function cookiepass_other_consent_plugins() { $folders = array( 'complianz-gdpr', 'complianz-gdpr-premium', 'cookiebot', 'borlabs-cookie', 'real-cookie-banner', 'real-cookie-banner-pro', 'cookie-law-info', 'cookie-notice', 'gdpr-cookie-compliance', 'iubenda-cookie-law-solution', 'usercentrics-consent-management-platform', 'uk-cookie-consent', 'cookie-script-com', 'cookiefirst-plugin', 'consentmanager' ); $active = (array) get_option( 'active_plugins', array() ); if ( is_multisite() ) { $active = array_merge( $active, array_keys( (array) get_site_option( 'active_sitewide_plugins', array() ) ) ); } if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all = get_plugins(); $names = array(); foreach ( $active as $basename ) { if ( in_array( strtok( (string) $basename, '/' ), $folders, true ) ) { $names[] = isset( $all[ $basename ]['Name'] ) ? wp_strip_all_tags( $all[ $basename ]['Name'] ) : strtok( (string) $basename, '/' ); } } return array_values( array_unique( $names ) ); } /** * The warning, as a notice box. */ function cookiepass_render_conflict_notice() { $others = cookiepass_other_consent_plugins(); if ( empty( $others ) ) { return; } printf( '

%s

', esc_html( sprintf( /* translators: %s: names of other cookie plugins */ __( 'Another cookie plugin is active: %s. Deactivate it, otherwise visitors see two banners.', 'cookiepass' ), implode( ', ', $others ) ) ) ); } /* ── WP Consent API ───────────────────────────────────────────────────── */ // Tells the WP Consent API (and plugins like Site Kit and WooCommerce that // read it) that a consent plugin is present. add_filter( 'wp_consent_api_registered_' . plugin_basename( __FILE__ ), '__return_true' ); add_filter( 'wp_get_consent_type', function ( $type ) { return cookiepass_property_id() ? 'optin' : $type; } ); /** * Passes every decision from cp.js on to wp_set_consent(). * * cp.js categories (an object of booleans): necessary, preferences, statistics, marketing. * WP Consent API: functional, preferences, statistics(-anonymous), marketing. * The API script may load after cp.js has already decided – the last state * is kept and applied once wp_set_consent exists. * * @return string */ function cookiepass_consent_api_bridge() { return <<<'JS' (function () { var MAP = { necessary: ['functional'], preferences: ['preferences'], statistics: ['statistics', 'statistics-anonymous'], marketing: ['marketing'] }; var pending = null; // cp.js sends { necessary: true, statistics: false, ... }; arrays are accepted too. function granted(categories, c) { return Array.isArray(categories) ? categories.indexOf(c) !== -1 : !!(categories && categories[c]); } function apply(categories) { pending = categories; if (typeof window.wp_set_consent !== 'function') return; Object.keys(MAP).forEach(function (c) { var allowed = c === 'necessary' || granted(categories, c); MAP[c].forEach(function (w) { window.wp_set_consent(w, allowed ? 'allow' : 'deny'); }); }); pending = null; } function onDecision(e) { var d = e && e.detail; if (!d || d.entschieden === false) return; apply(d.kategorien || {}); } window.addEventListener('cookiepass:entscheidung', onDecision); window.addEventListener('cookiepass:bereit', onDecision); document.addEventListener('DOMContentLoaded', function () { if (pending) apply(pending); }); window.addEventListener('load', function () { if (pending) apply(pending); }); })(); JS; } /* ── Admin: connect, disconnect, settings ─────────────────────────────── */ /** * URL of the settings page. * * @param array $args Extra query arguments. * @return string */ function cookiepass_settings_url( $args = array() ) { return add_query_arg( $args, admin_url( 'options-general.php?page=cookiepass' ) ); } /** * Host of the site as visitors see it (home_url, not siteurl). * * @return string */ function cookiepass_site_host() { $host = wp_parse_url( home_url(), PHP_URL_HOST ); return is_string( $host ) ? strtolower( $host ) : ''; } /** * The privacy policy page for the banner footer. * * @return string */ function cookiepass_privacy_url() { $url = (string) get_privacy_policy_url(); if ( '' !== $url ) { return $url; } // No privacy page selected under Settings → Privacy: look for the usual slugs. return cookiepass_page_url( array( 'datenschutz', 'datenschutzerklaerung', 'privacy-policy', 'privacy', 'politique-de-confidentialite', 'politica-de-privacidad', 'informativa-privacy', 'privacyverklaring', 'polityka-prywatnosci', 'politica-de-privacidade' ) ); } /** * Permalink of the first published page with one of these slugs, or ''. * * @param string[] $slugs Page slugs to try. * @return string */ function cookiepass_page_url( $slugs ) { foreach ( $slugs as $slug ) { $page = get_page_by_path( $slug ); if ( $page instanceof WP_Post && 'publish' === $page->post_status ) { return (string) get_permalink( $page ); } } return ''; } /** * A published imprint page, if the site has one – German sites must. * WordPress has a setting for the privacy policy page, not for the imprint, * so this looks for the usual slugs. Only used to prefill the banner. * * @return string */ function cookiepass_imprint_url() { return cookiepass_page_url( array( 'impressum', 'imprint', 'legal-notice', 'mentions-legales', 'aviso-legal', 'note-legali', 'colofon' ) ); } /** * Lets wp_safe_redirect() go to Cookiepass, and only there. * * @param string[] $hosts Allowed hosts. * @return string[] */ function cookiepass_allowed_redirect_hosts( $hosts ) { $host = wp_parse_url( COOKIEPASS_APP_URL, PHP_URL_HOST ); if ( is_string( $host ) ) { $hosts[] = $host; } return $hosts; } /** * Handles the three admin actions before any output: start connecting, * return from Cookiepass, disconnect. */ function cookiepass_handle_admin_actions() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- only routes to handlers that verify their own nonce/state. $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; if ( 'cookiepass' !== $page || ! current_user_can( 'manage_options' ) ) { return; } $user_id = get_current_user_id(); // 1. Start: send the admin to Cookiepass with a one-time state. if ( isset( $_GET['cookiepass_action'] ) && 'connect' === sanitize_key( wp_unslash( $_GET['cookiepass_action'] ) ) ) { check_admin_referer( 'cookiepass_connect' ); $state = wp_generate_password( 32, false, false ); set_transient( 'cookiepass_state_' . $user_id, $state, 30 * MINUTE_IN_SECONDS ); $language = substr( (string) get_user_locale(), 0, 2 ); $args = array( 'quelle' => 'wordpress', 'site' => cookiepass_site_host(), 'rueck' => cookiepass_settings_url(), 'state' => $state, 'datenschutz' => cookiepass_privacy_url(), 'impressum' => cookiepass_imprint_url(), 'name' => wp_strip_all_tags( get_bloginfo( 'name' ) ), 'lang' => in_array( $language, array( 'de', 'en', 'es', 'fr', 'it', 'nl', 'pl', 'pt', 'ar', 'hi', 'ja', 'ko' ), true ) ? $language : 'en', 'utm_source' => 'wordpress', 'utm_medium' => 'plugin', 'utm_campaign' => 'verbinden', ); add_filter( 'allowed_redirect_hosts', 'cookiepass_allowed_redirect_hosts' ); wp_safe_redirect( add_query_arg( array_map( 'rawurlencode', array_filter( $args ) ), COOKIEPASS_APP_URL . '/verbinden' ) ); exit; } // 2. Return: Cookiepass sends the admin back with the same state. // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- verified against the one-time state stored in step 1. if ( isset( $_GET['cookiepass_state'] ) ) { $expected = get_transient( 'cookiepass_state_' . $user_id ); $received = sanitize_text_field( wp_unslash( $_GET['cookiepass_state'] ) ); delete_transient( 'cookiepass_state_' . $user_id ); $notice = 'state'; if ( is_string( $expected ) && '' !== $expected && hash_equals( $expected, $received ) ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- state verified above. $id = isset( $_GET['cookiepass_cbid'] ) ? sanitize_text_field( wp_unslash( $_GET['cookiepass_cbid'] ) ) : ''; $domain = isset( $_GET['cookiepass_domain'] ) ? sanitize_text_field( wp_unslash( $_GET['cookiepass_domain'] ) ) : ''; $error = isset( $_GET['cookiepass_fehler'] ) ? sanitize_key( wp_unslash( $_GET['cookiepass_fehler'] ) ) : ''; // phpcs:enable WordPress.Security.NonceVerification.Recommended if ( '' !== $error ) { $notice = 'cancelled'; } elseif ( cookiepass_is_valid_id( $id ) ) { update_option( COOKIEPASS_OPTION_ID, strtolower( $id ) ); update_option( COOKIEPASS_OPTION_DOMAIN, preg_replace( '/[^a-z0-9.\-]/', '', strtolower( $domain ) ) ); $notice = 'connected'; } else { $notice = 'invalid'; } } wp_safe_redirect( cookiepass_settings_url( array( 'cookiepass_notice' => $notice ) ) ); exit; } // 3. Clear page caches and check again. if ( isset( $_POST['cookiepass_purge'] ) ) { check_admin_referer( 'cookiepass_purge' ); cookiepass_purge_page_caches(); cookiepass_check_homepage( true ); wp_safe_redirect( cookiepass_settings_url( array( 'cookiepass_notice' => 'purged' ) ) ); exit; } // 4. Disconnect. if ( isset( $_POST['cookiepass_disconnect'] ) ) { check_admin_referer( 'cookiepass_disconnect' ); delete_option( COOKIEPASS_OPTION_ID ); delete_option( COOKIEPASS_OPTION_DOMAIN ); wp_safe_redirect( cookiepass_settings_url( array( 'cookiepass_notice' => 'disconnected' ) ) ); exit; } } add_action( 'admin_init', 'cookiepass_handle_admin_actions' ); /** * Settings: the manual property ID and the blocking mode. */ function cookiepass_register_settings() { register_setting( 'cookiepass_settings', COOKIEPASS_OPTION_ID, array( 'type' => 'string', 'default' => '', 'sanitize_callback' => 'cookiepass_sanitize_id', ) ); register_setting( 'cookiepass_settings', COOKIEPASS_OPTION_BLOCKING, array( 'type' => 'string', 'default' => 'auto', 'sanitize_callback' => function ( $value ) { return 'manual' === $value ? 'manual' : 'auto'; }, ) ); } add_action( 'admin_init', 'cookiepass_register_settings' ); /** * Keeps the old ID when the new one is not a property ID. * * @param mixed $value Submitted value. * @return string */ function cookiepass_sanitize_id( $value ) { $value = trim( sanitize_text_field( (string) $value ) ); if ( '' === $value || cookiepass_is_valid_id( $value ) ) { if ( '' === $value ) { delete_option( COOKIEPASS_OPTION_DOMAIN ); } return strtolower( $value ); } add_settings_error( COOKIEPASS_OPTION_ID, 'cookiepass_invalid_id', __( 'This is not a Cookiepass property ID. It looks like 7a0173d8-1f7e-4dfe-8f71-6b1e39ec0a57.', 'cookiepass' ) ); return cookiepass_property_id(); } function cookiepass_add_settings_page() { add_options_page( 'Cookiepass', 'Cookiepass', 'manage_options', 'cookiepass', 'cookiepass_render_settings_page' ); } add_action( 'admin_menu', 'cookiepass_add_settings_page' ); function cookiepass_admin_styles( $hook ) { if ( 'settings_page_cookiepass' !== $hook ) { return; } wp_register_style( 'cookiepass-admin', false, array(), COOKIEPASS_VERSION ); wp_enqueue_style( 'cookiepass-admin' ); wp_add_inline_style( 'cookiepass-admin', '.cookiepass-card{max-width:640px;background:#fff;border:1px solid #dcdcde;border-radius:8px;padding:20px 24px;margin-top:16px}' . '.cookiepass-card h2{margin-top:0}.cookiepass-status{display:flex;align-items:center;gap:8px;font-weight:600;color:#007a4d}' . '.cookiepass-status .dashicons{color:#00a32a}.cookiepass-actions{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0 0}' . '.cookiepass-actions form{margin:0}.cookiepass-card .notice.inline{margin:12px 0 0}.cookiepass-muted{color:#646970}.cookiepass-card details{margin-top:16px}' . '.cookiepass-card summary{cursor:pointer;color:#2271b1}' ); } add_action( 'admin_enqueue_scripts', 'cookiepass_admin_styles' ); function cookiepass_connect_url() { return wp_nonce_url( cookiepass_settings_url( array( 'cookiepass_action' => 'connect' ) ), 'cookiepass_connect' ); } function cookiepass_render_notice() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display only. $notice = isset( $_GET['cookiepass_notice'] ) ? sanitize_key( wp_unslash( $_GET['cookiepass_notice'] ) ) : ''; $messages = array( 'connected' => array( 'success', __( 'Connected. The banner is now live on your site – open it in a private window to see it.', 'cookiepass' ) ), 'disconnected' => array( 'success', __( 'Disconnected. Your site no longer loads the banner.', 'cookiepass' ) ), 'purged' => array( 'success', __( 'Page cache cleared and homepage checked again.', 'cookiepass' ) ), 'cancelled' => array( 'warning', __( 'Connecting was cancelled.', 'cookiepass' ) ), 'state' => array( 'error', __( 'This connection link has expired. Please click “Connect with Cookiepass” again.', 'cookiepass' ) ), 'invalid' => array( 'error', __( 'Cookiepass did not send a valid property ID. Please try again.', 'cookiepass' ) ), ); if ( isset( $messages[ $notice ] ) ) { printf( '

%2$s

', esc_attr( $messages[ $notice ][0] ), esc_html( $messages[ $notice ][1] ) ); } } function cookiepass_render_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $id = cookiepass_property_id(); $domain = (string) get_option( COOKIEPASS_OPTION_DOMAIN, '' ); ?>

Cookiepass

id || ! current_user_can( 'manage_options' ) ) { return; } cookiepass_render_conflict_notice(); if ( '' !== cookiepass_property_id() ) { return; } printf( '

%1$s %3$s

', esc_html__( 'Cookiepass is active but not connected yet – no banner is shown.', 'cookiepass' ), esc_url( cookiepass_connect_url() ), esc_html__( 'Connect with Cookiepass', 'cookiepass' ) ); } add_action( 'admin_notices', 'cookiepass_plugins_screen_notice' ); /** * "Settings" link in the plugin list. * * @param string[] $links Action links. * @return string[] */ function cookiepass_action_links( $links ) { array_unshift( $links, sprintf( '%s', esc_url( cookiepass_settings_url() ), esc_html__( 'Settings', 'cookiepass' ) ) ); return $links; } add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'cookiepass_action_links' );