=== Narrativva SEM Integration for Seznam ===
Contributors: bujnovskyf
Tags: seznam, sklik, server-side, woocommerce, conversions
Requires at least: 6.5
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 0.6.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Public beta: Independent, consent-aware Seznam Event Measurement integration with frontend events and server-side conversions.

== Description ==

**Public beta:** This plugin is in an early public testing phase. Test your setup on a staging site and verify event delivery and consent behavior before using it in production. Feedback and bug reports are welcome.

**Need help with implementation?** I can help you implement and configure the plugin on your website. [Contact me by email](mailto:bujnovskyf@gmail.com).

This plugin installs Seznam Event Measurement sul.js on the frontend and sends selected server-side events directly from WordPress to Seznam SEM.

Supported frontend events:

* PageView
* ViewContent
* Search
* Contact from contact clicks or optional copied contact text

Supported server-side events:

* WooCommerce AddToCart, InitiateCheckout, AddPaymentInfo, and Purchase by default, with optional developer dataLayer Purchase mode
* Lead events (Contact Form 7, Elementor Forms, WPForms, dataLayer)
* WordPress/Woo CompleteRegistration

The plugin also captures sid, udid, and sznaiid, bridges Google Consent Mode v2 to SEM updateConsent, hashes S2S user data server-side, deduplicates duplicate form leads, and keeps S2S delivery logs with retry handling. Multi-premise routing keeps each Zboží.cz premise's combined frontend SEM ID paired with its separate S2S SEM ID.

Optional features include Sklik Sandbox token support for S2S tests, frontend updateUserData for logged-in users after ad_user_data consent is granted, configurable WooCommerce product identifiers for matching Zboží.cz ITEM_ID, Action Scheduler support when available, and WP-Cron fallback for small sites.

Developers can connect any cookie banner/CMP through focused public hooks instead of relying on a plugin-specific cookie parser. Main integration points are `nva_sem_s2s_disable_by_gdpr`, `nva_sem_s2s_consent_mode`, `nva_sem_s2s_hidden_identity_form_selectors`, `nva_sem_s2s_purchase_contents_item`, `nva_sem_s2s_phone_country_prefix`, `nva_sem_s2s_delivery_result`, the `nva_sem_s2s_track()` PHP helper, and the browser object `window.NvaSemS2SBridge`.

Built-in CMP adapters are enabled for CookieYes and Cookiebot. CookieYes maps `advertisement` to ads consent, `analytics`/`performance` to analytics consent, and `functional` to functionality consent. Cookiebot maps `marketing`, `statistics`, and `preferences` to the matching Google Consent Mode v2 values. Developers can disable these adapters with `nva_sem_s2s_disable_cookieyes_bridge` and `nva_sem_s2s_disable_cookiebot_bridge`.

Plugin author: František Bujnovský, a web analyst and web developer who helps brands and businesses grow through web analytics, precise measurement, and development. Website: https://frantisekbujnovsky.com/. Email: bujnovskyf@gmail.com.

= Privacy / External Services =

This plugin depends on Seznam's Event Measurement service. It loads an external script from Seznam (`https://l.seznam.cz/sul.js`) on the frontend to manage identity cookies and collect browser events. It also performs Server-to-Server (S2S) HTTP requests to Seznam (`https://sem.seznam.cz/rtgconv`) to dispatch conversion events. Data is sent when tracking is enabled and the relevant consent and event conditions are met.

Service documentation: https://napoveda.sklik.cz/merici-skripty/seznam-event-measurement/implementace-sem/server-to-server-s2s-mereni/

Seznam advertiser terms: https://napoveda.sklik.cz/pravidla/smluvni-podminky-pro-inzerenty/

Seznam privacy information: https://o-seznam.cz/pravni-informace/ochrana-udaju/

This is an independent open-source integration. It is not developed, endorsed, sponsored, or supported by Seznam.cz. Seznam and related marks belong to their respective owner.

To respect user privacy and comply with GDPR regulations, the plugin:
* Uses `sid` and `udid` only when available from `sul.js`, which requires `ad_storage: granted`.
* Captures `sznaiid` from the Sklik click URL parameter. It may temporarily store it in the plugin's own first-party browser storage and sync it to WordPress/WooCommerce session/order metadata for later S2S attribution. Sklik SEM S2S documentation describes `sznaiid` as a click ID used for anonymous conversion modelling and states that it can be safely stored server-side without user consent.
* Sends `s2s_headers` with client IP address, User-Agent, and available `Sec-CH-UA*` Client Hints only when `ad_storage` consent is granted.
* Hashes personal user data (`em`, `ph`, `fn`, `ln`, etc.) using SHA-256 server-side before dispatching, and only sends it if `ad_user_data` consent is granted.
* Retains local S2S delivery logs for debugging. Payload previews are retained for diagnostics and are masked. Full payloads are temporarily stored while events are queued or retrying; after successful delivery or permanent failure they are cleared unless Debug Mode is enabled. Debug Mode caps log retention at 24 hours; turning Debug Mode off clears stored full payloads and debug-only log rows.
* Registers suggested Privacy Policy text in WordPress admin.
* Rate-limits the frontend Lead REST endpoint by source and client IP.

== For Developers ==

Most sites do not need custom code. For custom integrations, the main extension points are:

* `nva_sem_s2s_track_lead( $args )` for custom WordPress lead forms.
* `nva_sem_s2s_track( $event_name, $event_data )` for officially supported server-side SEM events, including valid non-WooCommerce Purchase payloads. Arbitrary event names are rejected.
* `window.NvaSemS2SBridge.trackLead(args)` for browser-triggered custom Lead tracking delivered through the plugin S2S endpoint.
* `nva_sem_s2s_consent_mode` for custom cookie banners or CMPs.
* `window.NvaSemS2SBridge.setConsent(consentMode)` for frontend consent changes from a custom banner.
* `nva_sem_s2s_disable_by_gdpr` to halt all plugin tracking from site privacy logic.
* `nva_sem_s2s_hidden_identity_form_selectors` to add exact custom forms that should receive sid/udid/sznaiid hidden fields.
* `nva_sem_s2s_purchase_contents_item` to customize WooCommerce product ITEM_ID mapping.
* `nva_sem_s2s_delivery_result` to react to S2S delivery results in custom monitoring or logging.
* `nva_sem_s2s_premise_key` to select a configured Zboží.cz premise when URL matching is not enough.
* `nva_sem_s2s_premise_route` to customize the final pair of frontend and S2S SEM IDs.

Hidden identity fields are intentionally limited to known form types by default. The broad all-forms mode is disabled in the admin UI for production safety.

== Installation ==

1. Upload the plugin folder to `/wp-content/plugins/`.
2. Activate the plugin in WordPress admin.
3. Open Narrativva SEM in the WordPress admin menu.
4. Enable tracking and fill in the default Frontend SEM ID and S2S SEM ID.
5. Configure the server-side event toggles and test the setup with the Sklik Sandbox.
6. For multiple Zboží.cz premises, add one route per line under Zboží.cz premise routes: `key | domain or URL prefix | frontend SEM ID | S2S SEM ID`.

Each route can match an exact domain, wildcard subdomain, path prefix, or full URL prefix. The global identifiers remain the fallback. WooCommerce snapshots the selected route into the order for stable delayed Purchase delivery.

== Frequently Asked Questions ==

= Does this replace sul.js? =

No. Seznam requires sul.js even for S2S measurement. The plugin loads sul.js and uses it as the browser gateway for identity cookies and frontend events.

= Does this work without WooCommerce? =

Frontend PageView, Contact Form 7, Elementor Forms, WPForms, dataLayer Lead tracking, and custom Lead helpers can work without WooCommerce. Built-in ViewContent, add-to-cart, checkout, payment, and WooCommerce order Purchase tracking require WooCommerce. Developers can enable dataLayer Purchase mode or send a valid custom Purchase S2S payload with `nva_sem_s2s_track()` when a non-WooCommerce flow provides the required event data.

= Can one installation serve multiple Zboží.cz premises? =

Yes. Add a route for each premise with its own combined frontend SEM ID and separate S2S SEM ID. Routes can match domains or URL prefixes. Custom integrations can select a route with `premise_key` or the `nva_sem_s2s_premise_key` filter. Seznam's documentation confirms that every connected Zboží.cz premise has its own combined frontend SEM ID and its own S2S SEM ID.

= Can I send ViewCart or another custom event name? =

No. The public helpers accept only event names from Seznam's official SEM event reference. ViewCart is not supported. To measure cart-page visitors, enable PageView and configure either a conversion or a retargeting list in Seznam based on PageView with a URL condition matching the cart page. Use AddToCart for additions and InitiateCheckout only when checkout begins.

= Can it send non-CZK purchase values? =

No. SEM requires CZK. Non-CZK WooCommerce orders are skipped and logged.

= Can I send the same conversion from the browser and S2S? =

Do not intentionally send the same conversion through both paths. Seznam's current SEM documentation does not provide frontend/S2S deduplication. The plugin disables WooCommerce Purchase S2S when its dataLayer Purchase mode is enabled, but other custom integrations remain your responsibility.

== Changelog ==

= 0.6.2 =
* Added public-beta guidance to the plugin description and settings page.
* Added an invitation to contact the author for implementation and configuration help.
* Made diagnostic test notices translatable and kept consent help localized after AJAX refresh.
* Measurement behavior and existing settings are unchanged.

= 0.6.1 =
* Renamed the plugin and distribution slug to Narrativva SEM Integration for Seznam following WordPress.org review feedback.
* Added direct nonce verification before processing Elementor Atomic Form AJAX submissions.
* Removed translation files from the WordPress.org distribution package in favor of translate.wordpress.org.

= 0.6.0 =
* Added multi-premise Zboží.cz routing with separate frontend and S2S SEM IDs per premise.
* Restricted developer helpers to event names in Seznam's official SEM reference; unsupported custom names such as ViewCart are rejected.
* Added a prominent frontend/S2S overlap warning and clarified that dataLayer Purchase is delivered through S2S; internal WooCommerce Purchase is disabled automatically, while external GTM or plugin tags must be disabled manually.
* Added exact-domain, wildcard-subdomain, path-prefix, and full-URL-prefix matchers with a global fallback route.
* Added `nva_sem_s2s_premise_key`, `nva_sem_s2s_premise_route`, and explicit `premise_key` support for custom integrations.
* Snapshots the resolved premise IDs into WooCommerce orders so delayed and retried Purchase events keep the checkout destination.

= 0.5.0 =
* Prepared the plugin for WordPress.org review with a production release version, concise directory metadata, and complete external-service disclosures.
* Removed obsolete cart-page tracking and settings to match Seznam's current official event reference.
* Documented single-premise scope and the need to avoid duplicate frontend and S2S conversions.

= 0.4.9-beta =
* Removed low-level/internal developer hooks from runtime and documentation, keeping the supported public extension points focused on custom consent, custom leads, privacy stop, form selectors, product ITEM_ID mapping, phone prefix, and delivery result handling.

= 0.4.8-beta =
* Reduced the Developers reference to practical public extension points and removed low-level/internal hooks from the admin UI and readme overview.
* Updated About feedback copy to invite bug reports and feature requests by email.

= 0.4.7-beta =
* Fixed critical error caused by class_exists() calls triggering CookieYes plugin's buggy autoloader.

= 0.4.6-beta =
* Added a clearer About CTA linking to František Bujnovský's other projects and a secondary email contact action.

= 0.4.5-beta =
* Added a contextual Developers link from consent diagnostics when a custom consent filter is detected or when no known CMP is found and a custom cookie banner may need hook configuration.

= 0.4.4-beta =
* Moved author and contact information out of Developers into a standalone About admin section.
* Added plugin purpose and current version to the About section.

= 0.4.3-beta =
* Improved CMP detection in the admin UI by checking active CookieYes/Cookiebot WordPress plugins in addition to current request cookies.
* Split consent reporting into detected integration and current values source so admin requests without visitor cookies are no longer misleading.
* Redesigned the Developers integration reference into full-width rows with wrapping hook labels and endpoints.
* Updated author description.

= 0.4.2-beta =
* Disabled the non-recommended "hidden identity fields on all forms" admin option for production safety. Default targeted form selectors remain active, and developers can extend them through `nva_sem_s2s_hidden_identity_form_selectors`.
* Added admin visibility for the currently detected consent source: Cookiebot, CookieYes, custom filter, or default fallback.
* Added stronger production warnings when Debug mode is enabled.
* Expanded the Developers tab with clearer hook groups and author contact details.

= 0.4.1-beta =
* Fixed admin settings persistence for unchecked checkboxes by submitting explicit 0/1 values and sanitizing checkbox values instead of only checking field presence.
* Added dependent admin states so irrelevant fields are visually disabled when parent tracking toggles are off, including cart detection, Purchase product identifiers, Registration value, and Lead integrations.

= 0.4.0-beta =
* Improved phone number hashing: numbers are now normalized to E.164 format before SHA-256 hashing for better match rate with Sklik (strips spaces, converts 00xx prefix to +xx, prefixes bare 9-digit Czech numbers with +420 by default). Country prefix overridable via `nva_sem_s2s_phone_country_prefix` filter.
* Replaced UUID v4 event_id generation with UUID v7 (time-ordered). Monotonically increasing IDs improve server-side deduplication reliability as recommended by Sklik S2S documentation.
* Improved consent-mode handling for S2S payloads.
* Secured lead REST endpoint with WordPress nonce verification (`X-WP-Nonce`) — previously the endpoint was publicly accessible.
* Added `do_action( 'nva_sem_s2s_track_lead', $args )` action hook (documented in README, was missing from previous release).
* Fixed identity extraction from Elementor Forms and WPForms hidden fields for sites without WooCommerce session.
* Documented developer filters for phone normalization, hidden identity form selectors, and delivery-result integrations.

= 0.3.0-beta =
* Added Sklik Sandbox token support for S2S test payloads.
* Added optional frontend `updateUserData` support for logged-in users after ad_user_data consent is granted.
* Added configurable WooCommerce product identifier source for matching Zboží.cz ITEM_ID.
* Added Action Scheduler support with WP-Cron fallback for queued S2S delivery.
* Added lead REST endpoint rate limiting and WordPress Privacy Policy helper text.
* Added custom log table schema version upgrade routine.
* Added S2S Lead tracking support for Contact Form 7, Elementor Forms, and WPForms.
* Added dataLayer Bridge to intercept GA4-like lead events on frontend and send them via S2S.
* Added public JS helper `window.nvaSemS2s.trackLead(args)` and PHP helper `nva_sem_s2s_track_lead(args)`.
* Added transient-based deduplication layer with customizable TTL window.
* Added POST REST endpoint `/wp-json/nva-sem-s2s/v1/lead` for custom lead tracking.
* Repositioned plugin from WooCommerce-only to general WordPress S2S tracking.
* Replaced old settings UI fields with an expanded "Lead Tracking" settings section and enhanced diagnostics check.

= 0.2.0 =
* Security: REST identity endpoint now properly verifies wp_rest nonce (permission_callback).
* Added WooCommerce HPOS (custom order tables) compatibility declaration.
* Removed runtime Composer dependency for distribution builds and switched to explicit class loading (manual requires) so the release ZIP works without Composer.
* Added proper uninstall.php + logs table cleanup.
* Added PHPCS (WordPress Coding Standards), PHPStan, and PHPUnit (basic unit tests for Options, EventBuilder, SemClient).
* GitHub Actions CI workflow (lint, phpcs, phpstan, tests, build).
* Major i18n work: admin UI strings converted to English source strings (ready for translations).
* Removed code duplication (product_category_path centralized).
* Unified result message helper.
* Light modernization of frontend.js and many supporting improvements (.distignore, phpunit.xml.dist, etc.).
* Bumped minimum WordPress requirement to 6.5.

= 0.1.0 =
* Initial implementation.
