[ __CLASS__, 'sanitize_style' ] ] ); } public static function sanitize_style( mixed $input ): array { $defaults = self::get_style_defaults(); if ( ! is_array( $input ) ) { return $defaults; } $clean = []; $color_keys = [ 'text_color', 'bg_color', 'button_bg', 'button_text', 'button_hover_bg', 'button_hover_text', ]; foreach ( $color_keys as $key ) { $val = isset( $input[ $key ] ) ? sanitize_text_field( $input[ $key ] ) : ''; $clean[ $key ] = self::sanitize_hex_color( $val ) ?? $defaults[ $key ]; } $clean['custom_css'] = isset( $input['custom_css'] ) ? wp_strip_all_tags( $input['custom_css'] ) : ''; return $clean; } private static function sanitize_hex_color( string $color ): ?string { if ( preg_match( '/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/', $color ) ) { return $color; } return null; } public static function get_style_defaults(): array { return [ 'text_color' => '#ffffff', 'bg_color' => '#111111', 'button_bg' => '#2043B7', 'button_text' => '#ffffff', 'button_hover_bg' => '#1a369a', 'button_hover_text' => '#ffffff', 'custom_css' => '', ]; } public static function get_style(): array { $saved = get_option( CB_STYLE_OPTION, [] ); return wp_parse_args( is_array( $saved ) ? $saved : [], self::get_style_defaults() ); } /** Returns all services from the option, always as a list. */ public static function get_services(): array { $raw = get_option( CB_OPTION, [] ); return is_array( $raw ) ? array_values( $raw ) : []; } /** Returns a single service by id, or null. */ public static function get_service( string $id ): ?array { foreach ( self::get_services() as $svc ) { if ( isset( $svc['id'] ) && $svc['id'] === $id ) { return $svc; } } return null; } public static function save_services(): void { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Keine Berechtigung.', 'gdpr-content-blocker' ) ); } check_admin_referer( 'cb_save_services', 'cb_nonce' ); $raw_services = isset( $_POST['cb_services'] ) && is_array( $_POST['cb_services'] ) ? $_POST['cb_services'] : []; $services = []; foreach ( $raw_services as $item ) { if ( ! is_array( $item ) ) { continue; } $id = sanitize_key( $item['id'] ?? '' ); if ( $id === '' ) { continue; } $services[] = [ 'id' => $id, 'name' => sanitize_text_field( $item['name'] ?? '' ), 'enabled' => ! empty( $item['enabled'] ), 'match_pattern' => sanitize_text_field( $item['match_pattern'] ?? '' ), 'recipient' => sanitize_text_field( $item['recipient'] ?? '' ), 'third_country' => ! empty( $item['third_country'] ), 'sets_cookie' => ! empty( $item['sets_cookie'] ), 'loads_script' => ! empty( $item['loads_script'] ), 'purpose' => sanitize_textarea_field( $item['purpose'] ?? '' ), 'privacy_url' => esc_url_raw( $item['privacy_url'] ?? '' ), 'placeholder_text' => sanitize_textarea_field( $item['placeholder_text'] ?? '' ), ]; } update_option( CB_OPTION, $services ); wp_safe_redirect( admin_url( 'options-general.php?page=gdpr-content-blocker&cb_saved=1' ) ); exit; } public static function enqueue_admin_assets( string $hook ): void { if ( $hook !== 'settings_page_gdpr-content-blocker' ) { return; } wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_script( 'cb-admin', CB_URL . 'assets/admin.js', [ 'wp-color-picker', 'jquery' ], CB_VERSION, true ); wp_localize_script( 'cb-admin', 'cbAdmin', [ 'confirmRemove' => __( 'Dienst wirklich entfernen?', 'gdpr-content-blocker' ), 'presets' => self::get_presets(), 'newServiceLbl' => __( 'Neuer Dienst', 'gdpr-content-blocker' ), 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'scanNonce' => wp_create_nonce( 'cb_scan' ), 'i18n' => [ 'scanning' => __( 'Scanne Webseite …', 'gdpr-content-blocker' ), 'scanError' => __( 'Scan fehlgeschlagen:', 'gdpr-content-blocker' ), 'noFindings' => __( 'Keine externen Einbindungen gefunden.', 'gdpr-content-blocker' ), 'host' => __( 'Anbieter / Host', 'gdpr-content-blocker' ), 'type' => __( 'Typ', 'gdpr-content-blocker' ), 'count' => __( 'Anzahl', 'gdpr-content-blocker' ), 'example' => __( 'Beispiel-URL', 'gdpr-content-blocker' ), 'status' => __( 'Status', 'gdpr-content-blocker' ), 'action' => __( 'Aktion', 'gdpr-content-blocker' ), 'covered' => __( 'abgedeckt', 'gdpr-content-blocker' ), 'thirdParty' => __( 'Drittanbieter', 'gdpr-content-blocker' ), 'firstParty' => __( 'eigene Domain', 'gdpr-content-blocker' ), 'addService' => __( 'Als Dienst übernehmen', 'gdpr-content-blocker' ), 'scannedPages'=> __( 'Gescannte Seiten:', 'gdpr-content-blocker' ), 'foundOn' => __( 'Gefunden auf', 'gdpr-content-blocker' ), ], ] ); } /** * Ready-made templates for the most common third-party iframes. * Everything stays editable after insertion. */ public static function get_presets(): array { return [ 'google-maps' => [ 'id' => 'google-maps', 'name' => 'Google Maps', 'match_pattern' => 'google.com/maps', 'recipient' => 'Google Ireland Ltd., Irland / Google LLC, USA', 'third_country' => true, 'sets_cookie' => true, 'loads_script' => true, 'purpose' => __( 'Darstellung interaktiver Karten und Standortinformationen.', 'gdpr-content-blocker' ), 'privacy_url' => 'https://policies.google.com/privacy', ], 'youtube' => [ 'id' => 'youtube', 'name' => 'YouTube', 'match_pattern' => 'youtube', 'recipient' => 'Google Ireland Ltd., Irland / Google LLC, USA', 'third_country' => true, 'sets_cookie' => true, 'loads_script' => true, 'purpose' => __( 'Einbettung und Wiedergabe von Videos.', 'gdpr-content-blocker' ), 'privacy_url' => 'https://policies.google.com/privacy', ], 'openstreetmap' => [ 'id' => 'openstreetmap', 'name' => 'OpenStreetMap', 'match_pattern' => 'openstreetmap.org', 'recipient' => 'OpenStreetMap Foundation, Großbritannien', 'third_country' => true, 'sets_cookie' => false, 'loads_script' => true, 'purpose' => __( 'Darstellung interaktiver Karten.', 'gdpr-content-blocker' ), 'privacy_url' => 'https://wiki.osmfoundation.org/wiki/Privacy_Policy', ], 'vimeo' => [ 'id' => 'vimeo', 'name' => 'Vimeo', 'match_pattern' => 'player.vimeo.com', 'recipient' => 'Vimeo LLC, USA', 'third_country' => true, 'sets_cookie' => true, 'loads_script' => true, 'purpose' => __( 'Einbettung und Wiedergabe von Videos.', 'gdpr-content-blocker' ), 'privacy_url' => 'https://vimeo.com/privacy', ], ]; } public static function render_page(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } $services = self::get_services(); $style = self::get_style(); $saved = isset( $_GET['cb_saved'] ) && $_GET['cb_saved'] === '1'; $settings_saved = isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] === 'true'; ?>

$svc ) : ?>
esc_attr( $svc[ $key ] ?? $default ); $b = fn( string $key ) => ! empty( $svc[ $key ] ); $idx = esc_attr( (string) $index ); $name = $svc['name'] ?? ''; $id = $svc['id'] ?? ''; $enabled = $svc['enabled'] ?? true; // default ON (new + legacy services) // Header label: "interner-name — Anbietername" $headname = trim( ( $id !== '' ? $id : __( 'neu', 'gdpr-content-blocker' ) ) . ( $name !== '' ? ' — ' . $name : '' ) ); ?>
__( 'Textfarbe Platzhalter', 'gdpr-content-blocker' ), 'bg_color' => __( 'Hintergrundfarbe Platzhalter', 'gdpr-content-blocker' ), 'button_bg' => __( 'Button: Hintergrundfarbe', 'gdpr-content-blocker' ), 'button_text' => __( 'Button: Textfarbe', 'gdpr-content-blocker' ), 'button_hover_bg' => __( 'Button Hover: Hintergrundfarbe', 'gdpr-content-blocker' ), 'button_hover_text' => __( 'Button Hover: Textfarbe', 'gdpr-content-blocker' ), ]; echo ''; foreach ( $fields as $key => $label ) { $val = esc_attr( $style[ $key ] ); echo ''; echo ''; } echo ''; echo ''; echo ''; } /** * Help box explaining shortcodes, revoke, auto-detection and CSS classes. * Shown below "Darstellung speichern". */ private static function render_usage_help(): void { $code = static fn( string $s ): string => '' . esc_html( $s ) . ''; ?>




, , , , , ,

GDPR Content Blocker


Lucas Orth' ); ?>

[ 'page', 'post' ], 'post_status' => 'publish', 'numberposts' => 4, 'orderby' => 'comment_count', // roughly "most visited" 'order' => 'DESC', 'fields' => 'ids', 'no_found_rows' => true, ] ); foreach ( $posts as $pid ) { $link = get_permalink( $pid ); if ( $link ) { $urls[] = $link; } } return array_values( array_unique( $urls ) ); } public static function ajax_scan(): void { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => __( 'Keine Berechtigung.', 'gdpr-content-blocker' ) ], 403 ); } check_ajax_referer( 'cb_scan', 'nonce' ); $lic = CB_License::get_license(); if ( ( $lic['status'] ?? '' ) !== 'active' ) { wp_send_json_error( [ 'message' => __( 'Bitte zuerst eine Lizenz aktivieren (Tab „Lizenz").', 'gdpr-content-blocker' ) ] ); } $response = wp_remote_post( CB_License::api_url() . '/api/v1/scan', [ 'timeout' => 45, 'headers' => [ 'Content-Type' => 'application/json', 'Accept' => 'application/json' ], 'body' => wp_json_encode( [ 'key' => $lic['key'], 'product' => CB_PRODUCT_SLUG, 'domain' => CB_License::domain(), 'urls' => self::scan_urls(), ] ), ] ); if ( is_wp_error( $response ) ) { wp_send_json_error( [ 'message' => $response->get_error_message() ] ); } $data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $data ) || empty( $data['ok'] ) ) { $msg = is_array( $data ) && ! empty( $data['error'] ) ? $data['error'] : __( 'Unerwartete Antwort vom Lizenzserver.', 'gdpr-content-blocker' ); wp_send_json_error( [ 'message' => $msg ] ); } // Annotate each finding: is it already covered by a configured service? $services = self::get_services(); $findings = is_array( $data['findings'] ?? null ) ? $data['findings'] : []; foreach ( $findings as &$f ) { $f['covered'] = self::is_covered( $f, $services ); } unset( $f ); wp_send_json_success( [ 'findings' => $findings, 'scanned' => $data['scanned'] ?? [], ] ); } /** True if any configured service's match_pattern matches this finding. */ private static function is_covered( array $finding, array $services ): bool { $haystacks = array_merge( [ (string) ( $finding['host'] ?? '' ) ], array_map( 'strval', (array) ( $finding['sample_urls'] ?? [] ) ) ); foreach ( $services as $svc ) { $pattern = $svc['match_pattern'] ?? ''; if ( $pattern === '' ) { continue; } foreach ( $haystacks as $h ) { if ( $h !== '' && str_contains( $h, $pattern ) ) { return true; } } } return false; } }