| @@ -512,12 +512,19 @@ | ||
| 512 | 512 | |
| 513 | 513 | /** |
| 514 | 514 | * Total page views counted across all published forms. |
| 515 | 515 | * |
| 516 | - * Paired with `forms_with_views` and the existing `total_entries`, this is what | |
| 517 | - * makes the conversion figure interpretable in the warehouse: a site with views | |
| 518 | - * but no entries reads very differently from one with neither. | |
| 516 | + * Read alongside `forms_with_views`, which is what makes this figure legible: a | |
| 517 | + * site with views but no forms recording any reads very differently from one | |
| 518 | + * with neither. | |
| 519 | 519 | * |
| 520 | + * Not a denominator for `total_entries`, and the two must not be divided. This | |
| 521 | + * total starts at the moment tracking opened and leaves out anyone who can edit | |
| 522 | + * the site, while `total_entries` is an unfiltered all-time count of every | |
| 523 | + * entry ever received. A rate built from the pair answers neither question and | |
| 524 | + * will not match the Conversion Rate column, which measures both halves over | |
| 525 | + * the same window with the same exclusion. | |
| 526 | + * | |
| 520 | 527 | * Counts only published forms, so views left behind by a trashed or draft form |
| 521 | 528 | * do not inflate the total against a denominator that no longer includes them. |
| 522 | 529 | * |
| 523 | 530 | * @since 2.12.6 |
| @@ -982,8 +989,16 @@ | ||
| 982 | 989 | if ( \SRFM\Inc\Onboarding::get_instance()->get_onboarding_status() ) { |
| 983 | 990 | $onboarding_props = []; |
| 984 | 991 | $onboarding_analytics = Helper::get_srfm_option( 'onboarding_analytics', [] ); |
| 985 | 992 | |
| 993 | + // Which wizard produced the blob. Outside the non-empty guard and not | |
| 994 | + // isset()-gated like the flags below, because absence IS the answer: a | |
| 995 | + // blob from the old wizard, or no blob at all, must still report 'no'. | |
| 996 | + // Without this the two wizards share one event name and can only be | |
| 997 | + // told apart by which properties happen to be present -- and a v2 run | |
| 998 | + // on Pro with no caching plugin emits none of the new ones. | |
| 999 | + $onboarding_props['onboarding_v2'] = ! empty( $onboarding_analytics['onboardingV2'] ) ? 'yes' : 'no'; | |
| 1000 | + | |
| 986 | 1001 | if ( ! empty( $onboarding_analytics ) && is_array( $onboarding_analytics ) ) { |
| 987 | 1002 | if ( ! empty( $onboarding_analytics['skippedSteps'] ) && is_array( $onboarding_analytics['skippedSteps'] ) ) { |
| 988 | 1003 | $onboarding_props['skipped_steps'] = implode( ',', $onboarding_analytics['skippedSteps'] ); |
| 989 | 1004 | } |
| @@ -1003,17 +1018,40 @@ | ||
| 1003 | 1018 | if ( isset( $onboarding_analytics['exitedEarly'] ) ) { |
| 1004 | 1019 | $onboarding_props['exited_early'] = (bool) $onboarding_analytics['exitedEarly'] ? 'yes' : 'no'; |
| 1005 | 1020 | } |
| 1006 | 1021 | |
| 1007 | - if ( ! empty( $onboarding_analytics['premiumFeatures']['selectedFeatures'] ) && is_array( $onboarding_analytics['premiumFeatures']['selectedFeatures'] ) ) { | |
| 1008 | - $premium = array_filter( | |
| 1009 | - $onboarding_analytics['premiumFeatures']['selectedFeatures'], | |
| 1010 | - static function( $f ) { | |
| 1011 | - return 'ai-form-generation' !== $f && 'entries' !== $f; | |
| 1012 | - } | |
| 1022 | + // Add-ons step: the wizard shows one feature tab at a time | |
| 1023 | + // instead of a checkbox list, so we report which tabs were opened and | |
| 1024 | + // whether Upgrade was clicked. Blobs written by older wizards carry | |
| 1025 | + // neither key and emit neither property. | |
| 1026 | + if ( ! empty( $onboarding_analytics['premiumFeatures']['viewedTabs'] ) && is_array( $onboarding_analytics['premiumFeatures']['viewedTabs'] ) ) { | |
| 1027 | + // The blob is whatever the wizard POSTed. The tabs are a closed | |
| 1028 | + // set of four, so intersect against it rather than filtering on | |
| 1029 | + // type: that drops anything unrecognised and caps both the | |
| 1030 | + // content and the length of the property in one step. | |
| 1031 | + $viewed_tabs = array_values( | |
| 1032 | + array_intersect( | |
| 1033 | + array_map( 'strval', array_filter( (array) $onboarding_analytics['premiumFeatures']['viewedTabs'], 'is_scalar' ) ), | |
| 1034 | + [ 'multistep', 'conditional', 'calculation', 'conversational' ] | |
| 1035 | + ) | |
| 1013 | 1036 | ); |
| 1014 | - $onboarding_props['selected_premium_features'] = implode( ',', $premium ); | |
| 1015 | - $onboarding_props['premium_features_count'] = (string) count( $premium ); | |
| 1037 | + $onboarding_props['viewed_premium_tabs'] = implode( ',', $viewed_tabs ); | |
| 1038 | + } | |
| 1039 | + | |
| 1040 | + if ( isset( $onboarding_analytics['premiumFeatures']['upgradeClicked'] ) ) { | |
| 1041 | + $onboarding_props['premium_upgrade_clicked'] = (bool) $onboarding_analytics['premiumFeatures']['upgradeClicked'] ? 'yes' : 'no'; | |
| 1042 | + } | |
| 1043 | + | |
| 1044 | + // Cache-conflict step: shown only when a recognised caching plugin | |
| 1045 | + // is active; "yes" means the user pressed "I've fixed this". | |
| 1046 | + // | |
| 1047 | + // Read this as an abandonment signal, not as intent. Acknowledging | |
| 1048 | + // is the only way past the step, so everyone who completes the | |
| 1049 | + // wizard reports "yes" and "no" only ever appears next to | |
| 1050 | + // exited_early. It cannot answer "did people act on the warning" -- | |
| 1051 | + // only "did they stop here". | |
| 1052 | + if ( isset( $onboarding_analytics['cacheConflictAcknowledged'] ) ) { | |
| 1053 | + $onboarding_props['cache_conflict_acknowledged'] = (bool) $onboarding_analytics['cacheConflictAcknowledged'] ? 'yes' : 'no'; | |
| 1016 | 1054 | } |
| 1017 | 1055 | } |
| 1018 | 1056 | |
| 1019 | 1057 | self::events()->track( 'onboarding_completed', '', $onboarding_props ); |