[/content_blocker] */ public static function shortcode( array $atts, ?string $content = null ): string { $atts = shortcode_atts( [ 'id' => '' ], $atts, 'content_blocker' ); $id = sanitize_key( $atts['id'] ); if ( $id === '' ) { return ''; } $svc = CB_Settings::get_service( $id ); if ( $svc === null ) { return ''; } // Disabled blocker → let the embedded content load normally (no blocking). if ( ! ( $svc['enabled'] ?? true ) ) { return $content ?? ''; } // Extract src + dimensions from the inner iframe $src = ''; $dims = []; if ( $content !== null && $content !== '' ) { $attrs = self::extract_iframe_attrs( $content ); $src = $attrs['src']; $dims = [ 'width' => $attrs['width'], 'height' => $attrs['height'] ]; } return self::render_placeholder( $svc, $src, $dims ); } /** * Shortcode: [content_blocker_revoke] * Renders, by default, a visible text link (not a button) so it doesn't get * confused with a cookie banner's revoke control. Attributes: * text="…" custom link label * style="link|button" default "link" * note="yes|no" show the clarifying hint (default "yes") */ public static function revoke_shortcode( array|string $atts = [] ): string { $atts = shortcode_atts( [ 'text' => __( 'Einwilligung für externe Inhalte widerrufen', 'gdpr-content-blocker' ), 'style' => 'link', 'note' => 'yes', ], $atts, 'content_blocker_revoke' ); $class = $atts['style'] === 'button' ? 'cb-revoke-btn' : 'cb-revoke-link'; $out = '' . esc_html( $atts['text'] ) . ''; if ( $atts['note'] === 'yes' ) { $out .= '' . esc_html__( 'Betrifft nur die Freigabe externer Einbettungen (z. B. Karten, Videos). Cookie-Einstellungen werden separat verwaltet.', 'gdpr-content-blocker' ) . ''; } return $out; } /** * Shortcode: [content_blocker_services] * Lists every configured third-party service with the Art. 13 details * (provider, recipient, third-country note, purpose, privacy link). * Meant for embedding in the privacy policy. */ public static function services_shortcode(): string { $services = CB_Settings::get_services(); if ( empty( $services ) ) { return ''; } $out = ''; return $out; } /** * Build the placeholder HTML for a given service. * $src is the real iframe URL (empty string if unknown). * $dims may contain 'width' and 'height' (numbers or e.g. "100%") taken * from the original iframe so the placeholder reserves the same height. */ public static function render_placeholder( array $svc, string $src = '', array $dims = [] ): string { $id = esc_attr( $svc['id'] ); $name = esc_html( $svc['name'] ); $recipient = esc_html( $svc['recipient'] ); $purpose = esc_html( $svc['purpose'] ); $privacy_url = esc_url( $svc['privacy_url'] ?? '' ); $third = ! empty( $svc['third_country'] ); $custom_text = $svc['placeholder_text'] ?? ''; if ( $custom_text !== '' ) { $info_text = esc_html( $custom_text ); } else { $info_text = sprintf( /* translators: %s: provider name */ esc_html__( 'Um diesen Inhalt von %s zu laden, ist Ihre Einwilligung erforderlich. Dabei werden personenbezogene Daten (z. B. Ihre IP-Adresse) an den Anbieter übertragen.', 'gdpr-content-blocker' ), '' . $name . '' ); } $third_note = ''; if ( $third ) { $third_note = '' . esc_html__( '⚠ Datenübermittlung in ein Drittland außerhalb der EU/des EWR', 'gdpr-content-blocker' ) . ''; } $privacy_link = ''; if ( $privacy_url !== '' ) { $privacy_link = '' . esc_html__( 'Datenschutzerklärung des Anbieters', 'gdpr-content-blocker' ) . ''; } $data_src = $src !== '' ? ' data-src="' . esc_attr( $src ) . '"' : ''; // Reserve the embed's height so the layout doesn't jump, and remember the // original dimensions for the iframe that gets created on consent. $width = isset( $dims['width'] ) ? (string) $dims['width'] : ''; $height = isset( $dims['height'] ) ? (string) $dims['height'] : ''; $style_at = ''; if ( $height !== '' && ctype_digit( $height ) ) { $style_at = ' style="min-height:' . esc_attr( $height ) . 'px"'; } $data_dims = ''; if ( $width !== '' ) { $data_dims .= ' data-width="' . esc_attr( $width ) . '"'; } if ( $height !== '' ) { $data_dims .= ' data-height="' . esc_attr( $height ) . '"'; } return '
' . '
' . '

' . $info_text . '

' . '

' . '' . esc_html__( 'Empfänger:', 'gdpr-content-blocker' ) . ' ' . $recipient . ( $third_note !== '' ? ' — ' . $third_note : '' ) . '

' . '

' . '' . esc_html__( 'Zweck:', 'gdpr-content-blocker' ) . ' ' . $purpose . '

' . ( $privacy_link !== '' ? '

' . $privacy_link . '

' : '' ) . '' . '
' . '
'; } /** Pull the src attribute out of an iframe string. */ public static function extract_iframe_src( string $html ): string { return self::extract_iframe_attrs( $html )['src']; } /** * Pull src + width + height out of an iframe string. * Height is also read from an inline style="height:NNNpx" if no attribute. * Returns [ 'src' => string, 'width' => string, 'height' => string ]. */ public static function extract_iframe_attrs( string $html ): array { $src = ''; if ( preg_match( '/\bsrc=["\']([^"\']+)["\']/i', $html, $m ) ) { $src = esc_url_raw( $m[1] ); } $width = ''; if ( preg_match( '/\bwidth=["\']?(\d+(?:%|px)?)["\']?/i', $html, $m ) ) { $width = $m[1]; } $height = ''; if ( preg_match( '/\bheight=["\']?(\d+(?:%|px)?)["\']?/i', $html, $m ) ) { $height = $m[1]; } elseif ( preg_match( '/height\s*:\s*(\d+)px/i', $html, $m ) ) { $height = $m[1]; } // Normalise "450px" → "450" so it can be used as a numeric attribute. $width = preg_replace( '/px$/', '', $width ); $height = preg_replace( '/px$/', '', $height ); return [ 'src' => $src, 'width' => $width, 'height' => $height ]; } }