Skip to content

Instrument connection, sync, settings, media, cache, features, and deactivation analytics events - #1226

Draft
gabrielcld2 wants to merge 11 commits into
developfrom
feature/custom-events-tracking
Draft

Instrument connection, sync, settings, media, cache, features, and deactivation analytics events#1226
gabrielcld2 wants to merge 11 commits into
developfrom
feature/custom-events-tracking

Conversation

@gabrielcld2

@gabrielcld2 gabrielcld2 commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Approach

Wires the remaining 7 event categories from the Analytics Event Tracking Spec POC's custom-events framework:
connection management, asset sync, settings & navigation, media & asset actions, non-media cache, extensions & gallery, and deactivation (30 events total).

No changes to the transport layer (Analytics::track() / Analytics.track()) — this is call-site wiring only.

Design decisions worth a second look

  • bulk_sync_started only covers the manual REST trigger (Push_Sync::rest_start_sync()). The auto_restart case inside Sync_Queue::stop_maybe() is treated as internal plumbing and isn't instrumented.
  • asset_sync_failed hooks the broad, already-generic Sync::log_sync_result() rather than the narrowerUpload_Sync::upload_asset(), trading a small asset_type lookup cost for covering every sync type in one place.
  • sync_completed needed new state (Sync_Queue::mark_run_started() / tally_run_result() / track_run_completed()) since no
    existing signal survives the queue's internal restart cycles.
  • account_switched is new comparison logic in Connect::verify_connection()Connect::switch_account() turned out to be dead code (no callers since 2021), not something to hook into.
  • Cache-category events target php/assets/class-rest-assets.php, not php/class-cache.php — the latter is never instantiated (dead code), confirmed via exploration before wiring anything.
  • gallery_configured's layout/media_count are parsed out of the gallery_config field's serialized JSON blob rather than being real submitted fields.

Unrelated fix included: duplicate page-view bug

While QA'ing settings_page_viewed, we found it firing twice per single page load on Image Settings / Video Settings. Root cause: Image_Preview::preview() set the sample <img>'s src="#", which resolves to the current page's own URL — so the browser fired a second real request back at the same admin page (as an image fetch) before global-transformations.js replaced it with a real preview URL. Video_Preview and Breakpoints_Preview both extend Image_Preview and inherited the same bug.

This is a genuine, pre-existing perf bug unrelated to the analytics work, but it directly corrupted the accuracy of the new settings_page_viewed event on exactly the pages it affects, so it's fixed here rather than filed separately: src="#" → an inert 1x1 data-URI placeholder. Confirmed via raw Apache access logs (down to one request) and the capture log (down to one event) that the duplicate is gone; full Playwright suite still green afterward.

QA notes

Setup

  1. npm run env:start — the capture mu-plugin (.wp-env/mu-plugins/analytics-capture.php) loads automatically and intercepts every outgoing event instead of hitting the real collector.
  2. Before each check below: npm run env:cli -- wp cloudinary analytics-events --clear
  3. After the action: npm run env:cli -- wp cloudinary analytics-events — confirm the expected event_name / event_category / params appear.
  4. Needs a real Cloudinary connection for the connection/sync checks — either connect via the wizard with real credentials, or fake it: npm run env:cli -- wp option update cloudinary_connect '{"cloudinary_url":"..."}' --format=json + set cloudinary_connection_signature to match (see tests/e2e/utils/connection.js:fakeCloudinaryConnected() for the exact snippet).

Connection management

  • General Settings → Connect page: submit a new connection string → connection_string_updated (status: success/error depending on
    validity)
  • Same page, switch to a different cloud → account_switched
  • Click "Disconnect" → connection_disconnected
  • connectivity_check_failed is cron-only (daily ping/REST-API self-check) — not user-triggerable, skip manual check

Asset sync

  • Media Library → "Start bulk sync" with at least one unsynced asset → bulk_sync_started, then sync_completed once the queue
    drains
  • Force a sync failure (e.g. temporarily break credentials mid-sync) → asset_sync_failed
  • Toggle "Auto sync" or the storage/offload setting → sync_settings_changed

Settings & navigation

  • Load any plugin admin page (dashboard, image/video settings, gallery, help, etc.) → settings_page_viewed
  • Change and save a setting on any page → settings_saved
  • Dismiss any admin notice → notice_dismissed
  • (Only visible on a free-plan account) click the special-offer promo link → special_offer_clicked

Media & asset actions

  • Open the asset editor for an image, apply a transformation, save → asset_edited + transformation_applied (scope: asset)
  • Change format/quality/freeform transformation on the Image or Video Settings page and save → transformation_applied (scope: global)

Non-media cache

  • Open "Non-media assets" cache management for a cache point → cache_items_viewed
  • Enable/disable a cached item → cache_items_toggled
  • Purge a single cache point → asset_cache_purged; "Purge all" → all_cache_purged
  • Push a non-media asset to Cloudinary → cache_uploaded

Extensions & gallery

  • Toggle an extension on/off → extension_toggled
  • Save gallery settings (layout + media selection) → gallery_configured

Deactivation

  • Plugins screen → click "Deactivate" on Cloudinary → modal opens → deactivation_modal_viewed (check is_connected matches actual state)
  • "Submit and deactivate" with a reason selected → deactivation_submitted
  • "Skip and deactivate" → deactivation_skipped
  • Reactivate, then deactivate again choosing "Remove all plugin data" → plugin_uninstalled (alongside deactivation_submitted)

Regression check: confirm no PHP notices/fatals in wp-content/debug.log and no new JS console errors across the above — none of this should be visible to a site admin who isn't looking for it.

  • Confirm Image Settings / Video Settings load with exactly one settings_page_viewed event (was firing twice before this PR's fix)

gabriel-detassigny and others added 11 commits July 20, 2026 14:18
Release process GH Action
…activation analytics events

Wires the remaining 7 event categories from the analytics tracking spec on
top of the existing WPP-1210 custom-events framework: connection management,
asset sync, settings & navigation, media & asset actions, non-media cache,
extensions & gallery, and deactivation. All call sites reuse Analytics::track()
/ Analytics.track() and were live-verified against wp-env via WP-CLI/REST
dispatch. Adds a permanent e2e analytics-capture mu-plugin and two Playwright
specs covering connection and deactivation events.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous commit only included src/js/* source changes. This repo ships
compiled js/* output directly, so the extension_toggled, special_offer_clicked,
deactivation_modal_viewed, and deactivation_skipped tracking calls weren't
actually live until this rebuild. Live-verified via the full Playwright e2e
suite against wp-env with real Cloudinary credentials (13/13 passing).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nts-tracking

# Conflicts:
#	php/class-admin.php
#	php/class-deactivation.php
develop bumped phpstan to level 5 after this branch was cut. The Analytics
component (added in WPP-1210) was missing from get_component()'s @return
union, so every ->track() call site read as "method.notFound" once merged.
Re-verified clean at level 5 and against the full Playwright e2e suite.
…e load

Image_Preview::preview() set the sample <img>'s src to a bare '#', which
resolves to the current document's own URL — every load of Image Settings,
Video Settings, or the Responsive/Breakpoints preview (Video_Preview and
Breakpoints_Preview both extend Image_Preview) fired a second real HTTP
request back at the same admin page before global-transformations.js
replaced it with a real preview URL.

Found while investigating why settings_page_viewed fired twice per page
load: confirmed via wp-env access logs that the second request carried an
image/* Accept header and a self-referencing Referer, matching this exact
placeholder-image bug. Fixed with an inert 1x1 data-URI placeholder instead.
Re-verified: single event per load, single request in the access log,
full Playwright suite still green (13/13).
…nts-tracking

# Conflicts:
#	js/cloudinary.js
#	js/syntax-highlight.js
#	src/js/main.js
The merge commit resolved js/cloudinary.js and js/syntax-highlight.js
conflicts by taking develop's compiled output, dropping the SpecialOffer
wiring and the deactivate.js Prettier reformat. Rebuilding restores both on
top of develop's dependency upgrades and ESLint flat-config migration.
Re-verified: phpcs/phpstan clean, full Playwright suite green (13/13).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants