PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.3.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.3.0
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
← All changes | classes/class-cartflows-analytics.php +191 -7 3.2.1 → 3.3.0 View file →
@@ -23,9 +23,9 @@
23 23 /**
24 24 * Instance
25 25 *
26 26 * @access private
27 - * @var object Class object.
27 + * @var Cartflows_Analytics|null Class object.
28 28 * @since 2.2.4
29 29 */
30 30 private static $instance;
31 31
@@ -39,9 +39,9 @@
39 39 /**
40 40 * Initiator
41 41 *
42 42 * @since 2.2.4
43 - * @return object initialized object of class.
43 + * @return Cartflows_Analytics initialized object of class.
44 44 */
45 45 public static function get_instance() {
46 46 if ( ! isset( self::$instance ) ) {
47 47 self::$instance = new self();
@@ -88,8 +88,47 @@
88 88 }
89 89 return self::$events;
90 90 }
91 91
92 + /**
93 + * Whether non-sensitive usage tracking is opted in.
94 + *
95 + * Reads the site option so multisite network opt-in resolves the same way BSF Analytics does.
96 + *
97 + * @since x.x.x
98 + * @return bool
99 + */
100 + public static function is_tracking_opted_in() {
101 + // Honour the BSF-wide kill switch and white-label first — BSF_Analytics::is_tracking_enabled() checks both.
102 + if ( ! apply_filters( 'bsf_usage_tracking_enabled', true ) ) {
103 + return false;
104 + }
105 +
106 + $white_label = apply_filters( 'cf_white_label_options', array() );
107 + if ( is_array( $white_label ) && in_array( true, $white_label, true ) ) {
108 + return false;
109 + }
110 +
111 + // BSF stores the literal string 'no' on opt-out, which a bare bool cast would treat as opted in.
112 + return (bool) apply_filters( 'cartflows_enable_non_sensitive_data_tracking', 'yes' === get_site_option( 'cf_usage_optin', false ) );
113 + }
114 +
115 + /**
116 + * Whole days elapsed since the plugin was installed, 0 when unknown.
117 + *
118 + * @since x.x.x
119 + * @return int
120 + */
121 + private function get_days_since_install() {
122 + $install_time = Cartflows_Helper::get_analytics_flag( 'usage_installed_time', 0 );
123 +
124 + if ( ! is_numeric( $install_time ) || (int) $install_time <= 0 ) {
125 + return 0;
126 + }
127 +
128 + return (int) floor( ( time() - (int) $install_time ) / DAY_IN_SECONDS );
129 + }
130 +
92 131 // -------------------------------------------------------------------------
93 132 // BSF Analytics stats payload
94 133 // -------------------------------------------------------------------------
95 134
@@ -103,11 +142,9 @@
103 142 * @param array $stats_data Existing analytics payload.
104 143 * @return array
105 144 */
106 145 public function get_stats( $stats_data ) {
107 - // Use get_site_option so multisite network opt-in flows correctly — matches
108 - // BSF_Analytics::is_tracking_enabled() which also reads the site option.
109 - if ( ! apply_filters( 'cartflows_enable_non_sensitive_data_tracking', get_site_option( 'cf_usage_optin', false ) ) ) {
146 + if ( ! self::is_tracking_opted_in() ) {
110 147 return $stats_data;
111 148 }
112 149
113 150 $theme_data = $this->get_active_theme();
@@ -176,10 +213,10 @@
176 213 'internal_referer' => ! empty( $bsf_internal_referer['cartflows'] ) ? $bsf_internal_referer['cartflows'] : '',
177 214 // Simplified: boolean flag only — no raw feedback object.
178 215 'nps_survey_submitted' => ! empty( get_option( 'nps-survey-cartflows', array() ) ),
179 216 'pro_license_key_exists' => $this->check_pro_license_key_exists() ? true : false,
180 - // Simplified: count only — no module ID list.
181 - 'learn_modules_completed' => count( (array) get_option( 'wcf_learn_data', array() ) ),
217 + // Resolved count — includes auto-completed modules the option never stores.
218 + 'learn_modules_completed' => Cartflows_Learn_Progress::get_instance()->get_completed_count(),
182 219 );
183 220 }
184 221
185 222 /**
@@ -910,8 +947,155 @@
910 947 }
911 948
912 949 // Value is the step ID, not a boolean — get_stats reads it back to resolve the layout.
913 950 Cartflows_Helper::set_analytics_flag( 'first_checkout_configured', $post_id );
951 + }
952 +
953 + // -------------------------------------------------------------------------
954 + // Learn section events
955 + // -------------------------------------------------------------------------
956 +
957 + /**
958 + * Learn event names the browser is allowed to record.
959 + *
960 + * @since x.x.x
961 + * @return array<int, string>
962 + */
963 + public static function get_trackable_learn_events() {
964 + return array( 'learn_page_viewed', 'learn_module_action', 'learn_action_failed', 'learn_video_opened' );
965 + }
966 +
967 + /**
968 + * Learn CTA action types accepted from the browser.
969 + *
970 + * @since x.x.x
971 + * @return array<int, string>
972 + */
973 + public static function get_learn_action_types() {
974 + return array( 'install_plugin', 'redirect', 'learn_how', 'upgrade', 'activate_pro' );
975 + }
976 +
977 + /**
978 + * Learn failure reasons accepted from the browser.
979 + *
980 + * @since x.x.x
981 + * @return array<int, string>
982 + */
983 + public static function get_learn_failure_reasons() {
984 + return array( 'install_failed', 'activate_failed', 'progress_save_failed' );
985 + }
986 +
987 + /**
988 + * Plugin slugs the Learn checklist can install.
989 + *
990 + * @since x.x.x
991 + * @return array<int, string>
992 + */
993 + public static function get_learn_plugin_slugs() {
994 + return array( 'woocommerce', 'woo-cart-abandonment-recovery', 'modern-cart' );
995 + }
996 +
997 + /**
998 + * Track the first Learn page view as a milestone and refresh the per-module progress snapshot.
999 + *
1000 + * Fired from the AJAX recorder, not the REST route, so a GET never writes options.
1001 + *
1002 + * @since x.x.x
1003 + * @return void
1004 + */
1005 + public function track_learn_page_view() {
1006 +
1007 + $events = self::events();
1008 + if ( null === $events || ! self::is_tracking_opted_in() ) {
1009 + return;
1010 + }
1011 +
1012 + $progress = Cartflows_Learn_Progress::get_instance();
1013 + $states = $progress->get_module_states();
1014 +
1015 + // Unforced on purpose: a one-time milestone keyed on time-to-value, like first_flow_published.
1016 + $events->track(
1017 + 'learn_page_viewed',
1018 + (string) $this->get_days_since_install(),
1019 + array(
1020 + 'total' => (string) $progress->get_total_count(),
1021 + 'completed' => (string) $progress->get_completed_count(),
1022 + )
1023 + );
1024 +
1025 + // Forced so the server always holds the latest snapshot — one row per site.
1026 + $events->track( 'learn_progress', $progress->get_progress_value(), $states, true );
1027 + }
1028 +
1029 + /**
1030 + * Track a Learn module CTA click.
1031 + *
1032 + * @since x.x.x
1033 + * @param string $module_id Whitelisted module identifier.
1034 + * @param string $action_type Whitelisted action type.
1035 + * @param bool $is_pro Whether the module is a Pro feature.
1036 + * @return void
1037 + */
1038 + public function track_learn_module_action( $module_id, $action_type, $is_pro ) {
1039 +
1040 + $events = self::events();
1041 + if ( null === $events || ! self::is_tracking_opted_in() ) {
1042 + return;
1043 + }
1044 +
1045 + // Forced events overwrite by name, so the module is folded in or clicks on two modules collapse to one.
1046 + $events->track(
1047 + 'learn_module_action_' . $module_id,
1048 + $module_id,
1049 + array(
1050 + 'action_type' => $action_type,
1051 + 'is_pro' => $is_pro ? 'yes' : 'no',
1052 + ),
1053 + true
1054 + );
1055 + }
1056 +
1057 + /**
1058 + * Track a failed Learn module action.
1059 + *
1060 + * @since x.x.x
1061 + * @param string $module_id Whitelisted module identifier.
1062 + * @param string $reason Whitelisted failure reason.
1063 + * @param string $plugin_slug Whitelisted plugin slug, empty when not plugin related.
1064 + * @return void
1065 + */
1066 + public function track_learn_action_failed( $module_id, $reason, $plugin_slug = '' ) {
1067 +
1068 + $events = self::events();
1069 + if ( null === $events || ! self::is_tracking_opted_in() ) {
1070 + return;
1071 + }
1072 +
1073 + $events->track(
1074 + 'learn_action_failed_' . $module_id,
1075 + $module_id,
1076 + array(
1077 + 'reason' => $reason,
1078 + 'plugin_slug' => $plugin_slug,
1079 + ),
1080 + true
1081 + );
1082 + }
1083 +
1084 + /**
1085 + * Track the Learn getting-started video being opened.
1086 + *
1087 + * @since x.x.x
1088 + * @return void
1089 + */
1090 + public function track_learn_video_opened() {
1091 +
1092 + $events = self::events();
1093 + if ( null === $events || ! self::is_tracking_opted_in() ) {
1094 + return;
1095 + }
1096 +
1097 + $events->track( 'learn_video_opened', CARTFLOWS_VER );
914 1098 }
915 1099 }
916 1100
917 1101 Cartflows_Analytics::get_instance();