Privacy -> Policy Guide) with wp_add_privacy_policy_content(): * Google Analytics 4 when GA tracking is configured, Microsoft Clarity when * the Clarity tag is, and one paragraph for each ad platform that is on with * an ID. Nothing is added while none of them is set up. */ class PrivacyPolicy { /** * Settings store. * * @var Settings */ private Settings $settings; /** * Constructor. * * @param Settings $settings Settings store. */ public function __construct( Settings $settings ) { $this->settings = $settings; } /** * Hooks the text onto admin_init, where WordPress expects it. */ public function register(): void { add_action( 'admin_init', array( $this, 'add_policy_content' ) ); } /** * The admin_init callback: hands the suggested text to WordPress. */ public function add_policy_content(): void { if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) { return; } $content = $this->content(); if ( '' !== $content ) { wp_add_privacy_policy_content( 'Tagsmith', $content ); } } /** * The suggested policy text (HTML, every piece escaped), or '' when no * tag is configured. Notes for the site owner carry WordPress's * privacy-policy-tutorial class, so they are left out when the text is * copied. */ public function content(): string { $ga = (bool) $this->settings->get( 'tracking_enabled' ) && '' !== (string) $this->settings->get( 'measurement_id' ); $clarity = (bool) $this->settings->get( 'clarity_enabled' ) && '' !== (string) $this->settings->get( 'clarity_project_id' ); $pixels = Events::active_platforms( $this->settings->all() ); // The Google tag also gets the lead and contact events, but only while an ad pixel is on (PixelsOn). $ga4_events = array(); if ( $ga && array() !== $pixels ) { $ga4_events = Events::for_ga4( $this->settings->all() )['events'] ?? array(); } if ( ! $ga && ! $clarity && array() === $pixels ) { return ''; } $html = '

' . esc_html__( 'Tagsmith adds the analytics tools you turned on to your site, and the suggested text below describes them. Adjust it to match your setup, say how long the data is kept, and explain how visitors can give or withdraw consent with your consent banner. Tagsmith applies the same consent choice to every tool it adds (Tagsmith, Settings, Privacy).', 'tagsmith' ) . '

'; if ( $clarity ) { $html .= '

' . sprintf( /* translators: %s: link to Microsoft Clarity's terms of use. */ esc_html__( "Microsoft Clarity's terms of use require a privacy notice that tells visitors you use Clarity and how their data is collected and used. Check the current requirements at %s.", 'tagsmith' ), self::link( 'https://clarity.microsoft.com/terms' ) ) . '

'; } if ( array() !== $pixels ) { $html .= '

' . sprintf( /* translators: %s: links to the terms of the ad platforms in use, separated by commas. */ esc_html__( 'Each ad platform has its own terms for its pixel. Read them for what you have to tell your visitors: %s.', 'tagsmith' ), implode( ', ', array_map( static fn( string $key ): string => esc_html( Platforms::get( $key )['label'] ) . ' (' . self::link( Platforms::get( $key )['terms_url'] ) . ')', $pixels ) ) ) . '

'; } $label = '' . esc_html__( 'Suggested text:', 'tagsmith' ) . ' '; if ( $ga ) { $html .= '

' . $label . esc_html__( 'This site uses Google Analytics, a web analytics service from Google, to understand how visitors use it. When you visit, your browser loads a script from Google that may set cookies and sends Google details of your visit: the pages you view, the site that referred you, your device and browser, and your approximate location. Google uses your IP address to estimate your location, but Google Analytics does not log or store it.', 'tagsmith' ) . self::google_events( $ga4_events ) . ' ' . sprintf( /* translators: 1: link to Google's privacy policy. 2: link to Google's page on how it uses information from sites that use its services. */ esc_html__( "For more information, see Google's privacy policy (%1\$s) and how Google uses information from sites that use its services (%2\$s).", 'tagsmith' ), self::link( 'https://policies.google.com/privacy' ), self::link( 'https://policies.google.com/technologies/partner-sites' ) ) . '

'; $label = ''; } if ( $clarity ) { $html .= '

' . $label . sprintf( /* translators: %s: link to the Microsoft Privacy Statement. */ esc_html__( 'This site uses Microsoft Clarity to understand how visitors use it, through behavioral metrics, heatmaps and session replays, so we can improve it. When you visit, your browser loads a script from Microsoft (www.clarity.ms) that records how you interact with the pages, such as clicks, scrolling and mouse movements, together with details of your device and browser and your approximate location. This data is collected using first- and third-party cookies and similar technologies, and Microsoft processes it as described in the Microsoft Privacy Statement (%s).', 'tagsmith' ), self::link( 'https://privacy.microsoft.com/privacystatement' ) ); if ( $this->settings->get( 'clarity_wp_tags' ) ) { $html .= ' ' . esc_html__( 'Each recording is also labeled with the type, category and author of the page viewed, and whether you are logged in to this site.', 'tagsmith' ); } $html .= '

'; $label = ''; } // Whether the pixels wait for the visitor's agreement follows the consent mode (ADR-0024). $asks = 'off' !== (string) $this->settings->get( 'consent_mode' ); // In Basic mode, with the Google tag set up, Google Ads also waits for the tag's own grant (statistics cookies). $waits_for_tag = $ga && 'basic' === (string) $this->settings->get( 'consent_mode' ); foreach ( $pixels as $key ) { $html .= '

' . $label . $this->pixel_paragraph( $key, $asks, Events::for_platform( $key, $this->settings->all() ), 'gads' === $key && $waits_for_tag ) . '

'; $label = ''; } return $html; } /** * The suggested text for one ad platform: who runs it, where its script * comes from, what is sent to it, and a link to its privacy policy. It * says what is loaded and when, only names the events this platform gets, * and makes no claim about the law. * * @param string $key Platform key. * @param bool $asks Whether the pixel waits for the visitor to agree to marketing cookies. * @param array $events The events this platform sends (Events::for_platform()). * @param bool $waits_for_tag Whether it also waits for the Google tag's statistics consent (Google Ads in Basic mode). */ private function pixel_paragraph( string $key, bool $asks, array $events, bool $waits_for_tag ): string { $platform = Platforms::get( $key ); if ( null === $platform ) { return ''; } $when = $asks ? esc_html__( 'Once you have agreed to marketing cookies (an opt-out banner counts as agreed), where this site asks for it, your browser loads', 'tagsmith' ) : esc_html__( 'When you visit, your browser loads', 'tagsmith' ); $html = sprintf( /* translators: 1: name of the ad platform's pixel, e.g. "Meta Pixel". 2: the company that runs it, e.g. "Meta". */ esc_html__( 'This site uses the %1$s from %2$s to measure how its ads perform and to reach people who have visited it.', 'tagsmith' ), esc_html( $platform['label'] ), esc_html( $platform['company'] ) ) . ' ' . $when . ' ' . sprintf( /* translators: 1: the company that runs the pixel, e.g. "Meta". 2: the host the script comes from, e.g. "connect.facebook.net". 3: the company again. */ esc_html__( 'a script from %1$s (%2$s) and sends %3$s the address of the page you view, including its query string, the site that referred you, your IP address and browser details. %3$s also sets its own cookies.', 'tagsmith' ), esc_html( $platform['company'] ), esc_html( (string) wp_parse_url( $platform['script'], PHP_URL_HOST ) ), esc_html( $platform['company'] ) ); $company = esc_html( $platform['company'] ); // What it is told about, then what it is never told, then what the page events carry. $did = array(); $never = array(); if ( in_array( 'view_content', $events, true ) ) { $did[] = esc_html__( 'view a post or page', 'tagsmith' ); } if ( in_array( 'search', $events, true ) ) { $did[] = esc_html__( 'search this site', 'tagsmith' ); } if ( in_array( 'lead', $events, true ) ) { $did[] = esc_html__( 'send a form', 'tagsmith' ); $never[] = esc_html__( 'what you type into a form', 'tagsmith' ); } if ( in_array( 'contact', $events, true ) ) { $did[] = esc_html__( 'tap a contact link (phone, email, text message or WhatsApp)', 'tagsmith' ); $never[] = esc_html__( 'the number or address you tap', 'tagsmith' ); } if ( array() !== $did ) { $html .= ' ' . sprintf( /* translators: 1: the company that runs the pixel, e.g. "Meta". 2: what visitors do, e.g. "view a post or page, search this site or send a form". */ esc_html__( '%1$s is told when you %2$s.', 'tagsmith' ), $company, self::either( $did ) ); } if ( array() !== $never ) { $html .= ' ' . sprintf( /* translators: %s: what is never sent, e.g. "what you type into a form or the number or address you tap". */ esc_html__( 'It never gets %s.', 'tagsmith' ), self::either( $never ) ); } // What the page events carry. No adapter sends the page type, and Pinterest's content view carries neither title nor category. if ( in_array( 'view_content', $events, true ) && 'pinterest' !== $key ) { $html .= ' ' . sprintf( /* translators: %s: the company that runs the pixel, e.g. "Meta". */ esc_html__( '%s also gets the title and category of the page you view.', 'tagsmith' ), $company ); } if ( in_array( 'search', $events, true ) ) { $html .= ' ' . sprintf( /* translators: %s: the company that runs the pixel, e.g. "Meta". */ esc_html__( '%s also gets your search terms.', 'tagsmith' ), $company ); } if ( $waits_for_tag ) { $html .= ' ' . esc_html__( 'It also waits until you have agreed to statistics cookies, as Google Analytics does.', 'tagsmith' ); } // Microsoft's own documentation says UET data also reaches LinkedIn for conversion measurement (research section 3). if ( 'uet' === $key ) { $html .= ' ' . esc_html__( 'Microsoft may share this data with LinkedIn to measure conversions.', 'tagsmith' ); } return $html . ' ' . sprintf( /* translators: 1: the company that runs the pixel, e.g. "Meta". 2: link to its privacy policy. */ esc_html__( 'For more information, see the %1$s privacy policy (%2$s).', 'tagsmith' ), $company, self::link( $platform['privacy_url'] ) ); } /** * The sentences saying what the Google tag also records while an ad pixel * is on: a lead (the form plugin and form ID, as the browser sends them) * and a contact (the kind of link). Empty when it records neither. * * @param array $events The events the Google tag gets (Events::for_ga4()). */ private static function google_events( array $events ): string { $html = ''; if ( in_array( 'lead', $events, true ) ) { $html .= ' ' . esc_html__( 'It also records when you send a form, with the form plugin and the form ID but never what you type into it.', 'tagsmith' ); } if ( in_array( 'contact', $events, true ) ) { $html .= ' ' . esc_html__( 'It also records when you tap a contact link (phone, email, text message or WhatsApp), with the kind of link but never the number or address.', 'tagsmith' ); } return $html; } /** * Joins already-escaped phrases as "a, b or c". * * @param array $items The phrases. */ private static function either( array $items ): string { $last = array_pop( $items ); return array() === $items ? (string) $last : implode( ', ', $items ) . ' ' . esc_html__( 'or', 'tagsmith' ) . ' ' . $last; } /** * A link showing its own URL, so the text still reads well when copied * as plain text. * * @param string $url The URL. */ private static function link( string $url ): string { return '' . esc_html( $url ) . ''; } }