| 1 |
# BSF Analytics Events — Full Session Context |
| 2 |
|
| 3 |
## Branch: `bsf-events` |
| 4 |
|
| 5 |
### PRs |
| 6 |
|
| 7 |
- **Free plugin:** brainstormforce/cartflows#2214 — "Add BSF Analytics Events tracking infrastructure" |
| 8 |
- **Pro plugin:** brainstormforce/cartflows-pro#1156 — "Add BSF Analytics event flag-setters for Pro features" |
| 9 |
|
| 10 |
--- |
| 11 |
|
| 12 |
## What Was Done |
| 13 |
|
| 14 |
### 1. Analytics Class Created (earlier commits) |
| 15 |
|
| 16 |
New file: `classes/class-cartflows-analytics.php` — single entry point for all BSF Analytics tracking. |
| 17 |
|
| 18 |
- Owns the complete `bsf_core_stats` payload: default stats, `numeric_values`, `boolean_values`, `kpi_records`, `events_record` |
| 19 |
- Replaced the former `Cartflows_Admin::get_specific_stats()` handler |
| 20 |
- `is_admin()` guard in constructor — no frontend overhead |
| 21 |
- Daily transient throttle (`cf_state_events_checked`) for state event detection |
| 22 |
- `BSF_Analytics_Events` instantiated with slug `cf` (options prefixed `cf_`) |
| 23 |
|
| 24 |
### 2. Events Added (28 total) |
| 25 |
|
| 26 |
**Activation & Setup (5):** |
| 27 |
| Event | event_value | Properties | Detection | |
| 28 |
|-------|------------|------------|-----------| |
| 29 |
| `plugin_activated` | Version | `{source}` | State — always fires, dedup handles it | |
| 30 |
| `plugin_updated` | New version | `{from_version}` | State — compares `cf_tracked_version` vs `CARTFLOWS_VER`, uses `flush_pushed()` to re-track | |
| 31 |
| `onboarding_completed` | — | — | State — reads existing `wcf_setup_complete` option | |
| 32 |
| `onboarding_skipped` | — | `{exit_step}` | State — reads existing `wcf_setup_skipped` + `wcf_exit_setup_step` | |
| 33 |
| `pro_license_activated` | — | — | State — `_is_cartflows_pro()` function check | |
| 34 |
|
| 35 |
**Feature Usage (9):** |
| 36 |
| Event | event_value | Properties | Detection | |
| 37 |
|-------|------------|------------|-----------| |
| 38 |
| `first_flow_published` ⭐ | Flow ID | `{days_since_install, source, step_count}` | Hook — `transition_post_status` when flow publishes | |
| 39 |
| `first_checkout_configured` | Step ID | `{layout}` | State — reads `cf_first_checkout_configured` flag | |
| 40 |
| `first_template_imported` | — | `{page_builder}` | State — reads `cf_first_template_imported` flag | |
| 41 |
| `first_order_bump_created` | — | — | State — reads `cf_first_order_bump_created` flag | |
| 42 |
| `first_upsell_accepted` | — | — | State — reads `cf_first_upsell_accepted` flag | |
| 43 |
| `first_downsell_accepted` | — | — | State — reads `cf_first_downsell_accepted` flag | |
| 44 |
| `first_ab_test_started` | — | — | State — reads `cf_first_ab_test_started` flag | |
| 45 |
| `first_ab_test_winner_declared` | — | — | State — reads `cf_first_ab_test_winner` flag | |
| 46 |
| `first_instant_layout_enabled` | — | — | State — reads `cf_first_instant_layout_enabled` flag | |
| 47 |
|
| 48 |
**Configuration Milestones (2):** |
| 49 |
| Event | Detection | |
| 50 |
|-------|-----------| |
| 51 |
| `first_store_checkout_set` | State — `Cartflows_Helper::get_global_setting('_cartflows_store_checkout') > 0` (existing option, no flag needed) | |
| 52 |
| `first_webhook_configured` | State — reads `cf_first_webhook_configured` flag | |
| 53 |
|
| 54 |
**Integrations (8):** |
| 55 |
| Event | Detection | |
| 56 |
|-------|-----------| |
| 57 |
| `fb_pixel_connected` | State — reads existing `_cartflows_facebook` setting | |
| 58 |
| `ga_connected` | State — reads existing `_cartflows_google_analytics` setting | |
| 59 |
| `tiktok_connected` | State — reads existing `_cartflows_tiktok` setting | |
| 60 |
| `pinterest_connected` | State — reads existing `_cartflows_pinterest` setting | |
| 61 |
| `gads_connected` | State — reads existing `_cartflows_google_ads` setting | |
| 62 |
| `snapchat_connected` | State — reads existing `_cartflows_snapchat` setting | |
| 63 |
| `stripe_gateway_enabled` | State — scans WC payment gateways for `stripe` in ID | |
| 64 |
| `paypal_gateway_enabled` | State — scans WC payment gateways for `paypal` in ID | |
| 65 |
|
| 66 |
**Engagement & UI (4):** |
| 67 |
| Event | Detection | |
| 68 |
|-------|-----------| |
| 69 |
| `pointer_accepted` | State — reads existing `cartflows_pointer_data['accepted']` | |
| 70 |
| `pointer_dismissed` | State — reads existing `cartflows_pointer_data['dismissed']` | |
| 71 |
| `weekly_report_notice_dismissed` | State — reads `cf_weekly_report_notice_dismissed` flag | |
| 72 |
| `instant_checkout_notice_dismissed` | State — reads existing `wcf-instant-checkout-notice-skipped` option | |
| 73 |
|
| 74 |
### 3. Daily KPIs (2) |
| 75 |
|
| 76 |
| KPI | Query | |
| 77 |
| -------------- | -------------------------------------------------------------------------------------------- | |
| 78 |
| `order_count` | Daily completed/processing WC orders with `_wcf_flow_id` or `_cartflows_parent_flow_id` meta | |
| 79 |
| `offer_orders` | Daily orders with `_cartflows_offer = 'yes'` meta (upsell/downsell) | |
| 80 |
|
| 81 |
Both support HPOS and classic order storage. Queried for last 2 days excluding today. |
| 82 |
|
| 83 |
### 4. Privacy Fixes Applied to Existing Payload |
| 84 |
|
| 85 |
| Field | Before | After | |
| 86 |
| ----------------------------- | ----------------------------------- | -------------------------------------------------------------- | |
| 87 |
| `nps-survey-status` | Raw NPS survey object | Boolean `nps_survey_submitted` | |
| 88 |
| `learn-tab-completed-modules` | Raw array of module IDs | Integer `learn_modules_completed` | |
| 89 |
| `active-gateways` | Full gateway name/title map | Integer `active_gateway_count` | |
| 90 |
| `social-tracking` | Full settings objects (6 platforms) | 6 booleans (`fb_pixel_enabled`, `ga_enabled`, etc.) | |
| 91 |
| `documentation-search-terms` | Kept as-is | User confirmed this is intentional (used to find missing docs) | |
| 92 |
|
| 93 |
### 5. Code Quality Fixes |
| 94 |
|
| 95 |
- Added `is_admin()` guard in analytics class constructor |
| 96 |
- Fixed `get_funnels_with_instant_layout()`: `posts_per_page => 1` + `found_posts` instead of `-1` |
| 97 |
- Replaced `gmdate` with `wp_date` in KPI method |
| 98 |
- Added daily transient throttle for state detection |
| 99 |
- Deactivation survey correctly configured as array-of-arrays in `set_entity()` |
| 100 |
|
| 101 |
--- |
| 102 |
|
| 103 |
## All New Options Introduced By This PR |
| 104 |
|
| 105 |
### Flag options (set by feature modules, read by detect_state_events) — 12 new rows: |
| 106 |
|
| 107 |
| # | Option key | Value type | Set in (free plugin) | Set in (Pro plugin) | |
| 108 |
| --- | ----------------------------------- | -------------- | --------------------------------------------------- | ---------------------------------------------- | |
| 109 |
| 1 | `cf_first_checkout_configured` | step ID (int) | `admin-core/inc/meta-ops.php:267` | — | |
| 110 |
| 2 | `cf_first_template_imported` | `true` | `admin-core/ajax/importer.php:860,1002` | — | |
| 111 |
| 3 | `cf_first_instant_layout_enabled` | `true` | `admin-core/ajax/flows.php:172`, `importer.php:671` | — | |
| 112 |
| 4 | `cf_first_webhook_configured` | `true` | `admin-core/api/webhooks.php:183` | — | |
| 113 |
| 5 | `cf_first_order_bump_created` | `true` | — | `admin-core/ajax/multiple-order-bump.php:720` | |
| 114 |
| 6 | `cf_first_upsell_accepted` | `true` | — | `modules/upsell/.../upsell-markup.php:138` | |
| 115 |
| 7 | `cf_first_downsell_accepted` | `true` | — | `modules/downsell/.../downsell-markup.php:131` | |
| 116 |
| 8 | `cf_first_ab_test_started` | `true` | — | `modules/ab-test/.../ab-test-meta.php:452` | |
| 117 |
| 9 | `cf_first_ab_test_winner` | `true` | — | `modules/ab-test/.../ab-test-meta.php:583` | |
| 118 |
| 10 | `cf_tracked_version` | version string | `class-cartflows-analytics.php:616` | — | |
| 119 |
| 11 | `cf_usage_installed_time` | timestamp | `class-cartflows-loader.php:690` | — | |
| 120 |
| 12 | `cf_weekly_report_notice_dismissed` | `true` | `class-cartflows-admin-notices.php` | — | |
| 121 |
|
| 122 |
### Library-managed options (BSF_Analytics_Events internal, cannot be consolidated): |
| 123 |
|
| 124 |
| # | Option key | Purpose | |
| 125 |
| --- | ------------------------- | --------------------------------------------------------- | |
| 126 |
| 13 | `cf_usage_events_pending` | Temporary event queue (cleared after each analytics send) | |
| 127 |
| 14 | `cf_usage_events_pushed` | Dedup array of event names already sent | |
| 128 |
|
| 129 |
--- |
| 130 |
|
| 131 |
## Pending Task: Consolidate Flag Options |
| 132 |
|
| 133 |
**Problem:** 12 separate `wp_options` rows for analytics flags. |
| 134 |
|
| 135 |
**Proposed solution:** Merge all 12 into a single `cf_analytics_flags` option (associative array). Reduces 12 rows to 1. |
| 136 |
|
| 137 |
**Files that need updating:** |
| 138 |
|
| 139 |
- `classes/class-cartflows-analytics.php` — change all `get_option('cf_first_*')` and `get_option('cf_tracked_version')` etc. to read from `cf_analytics_flags` array |
| 140 |
- `classes/class-cartflows-loader.php` — change `cf_usage_installed_time` setter |
| 141 |
- `classes/class-cartflows-admin-notices.php` — change `cf_weekly_report_notice_dismissed` setter |
| 142 |
- `admin-core/inc/meta-ops.php` — change `cf_first_checkout_configured` setter |
| 143 |
- `admin-core/ajax/importer.php` — change `cf_first_template_imported` + `cf_first_instant_layout_enabled` setters |
| 144 |
- `admin-core/ajax/flows.php` — change `cf_first_instant_layout_enabled` setter |
| 145 |
- `admin-core/api/webhooks.php` — change `cf_first_webhook_configured` setter |
| 146 |
- Pro `admin-core/ajax/multiple-order-bump.php` — change `cf_first_order_bump_created` setter |
| 147 |
- Pro `modules/upsell/.../upsell-markup.php` — change `cf_first_upsell_accepted` setter |
| 148 |
- Pro `modules/downsell/.../downsell-markup.php` — change `cf_first_downsell_accepted` setter |
| 149 |
- Pro `modules/ab-test/.../ab-test-meta.php` — change `cf_first_ab_test_started` + `cf_first_ab_test_winner` setters |
| 150 |
|
| 151 |
**Pattern:** |
| 152 |
|
| 153 |
```php |
| 154 |
// Setter (in feature modules): |
| 155 |
$flags = get_option( 'cf_analytics_flags', array() ); |
| 156 |
if ( empty( $flags['first_checkout_configured'] ) ) { |
| 157 |
$flags['first_checkout_configured'] = $post_id; |
| 158 |
update_option( 'cf_analytics_flags', $flags ); |
| 159 |
} |
| 160 |
|
| 161 |
// Reader (in detect_state_events): |
| 162 |
$flags = get_option( 'cf_analytics_flags', array() ); |
| 163 |
if ( ! empty( $flags['first_checkout_configured'] ) ) { |
| 164 |
$events->track( 'first_checkout_configured', strval( $flags['first_checkout_configured'] ), ... ); |
| 165 |
} |
| 166 |
``` |
| 167 |
|
| 168 |
--- |
| 169 |
|
| 170 |
## Test Results (Dry Run — 2026-04-07) |
| 171 |
|
| 172 |
**Environment:** wp-cartflows.test (Local by Flywheel) | **Result: PASS** |
| 173 |
|
| 174 |
- 27/28 events fired correctly (only `paypal_gateway_enabled` absent — PayPal not enabled on dev site, expected) |
| 175 |
- All 8 new events confirmed: `tiktok_connected`, `pinterest_connected`, `gads_connected`, `snapchat_connected`, `first_downsell_accepted`, `first_instant_layout_enabled`, `first_store_checkout_set`, `first_webhook_configured` |
| 176 |
- KPI structure valid (2 days, `order_count` + `offer_orders`) |
| 177 |
- Zero PHP fatal errors or warnings |
| 178 |
- No analytics-related JS console errors |
| 179 |
- Full `bsf_core_stats` payload generated cleanly |
| 180 |
|
| 181 |
--- |
| 182 |
|
| 183 |
## BSF Analytics Library |
| 184 |
|
| 185 |
- **Version:** 1.1.23 (latest, installed via Composer) |
| 186 |
- **Location:** `libraries/bsf-analytics/` |
| 187 |
- **Events class:** `class-bsf-analytics-events.php` — `BSF_Analytics_Events` with slug-based option storage, client-side dedup, `track()` / `flush_pending()` / `flush_pushed()` API |
| 188 |
- **Deactivation survey:** Correctly configured in `class-cartflows-loader.php:382` as array-of-arrays |
| 189 |
|