PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.3
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.3
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
← All changes | includes/core/rest-api/Controllers/class-setup-wizard-controller.php +177 -3 3.10.6 → 3.13.3 View file →
@@ -30,8 +30,86 @@
30 30 */
31 31 public function register_routes() {
32 32 register_rest_route(
33 33 $this->namespace,
34 + '/' . $this->rest_base . '/save-consent',
35 + array(
36 + array(
37 + 'methods' => WP_REST_Server::CREATABLE,
38 + 'callback' => array( $this, 'save_consent' ),
39 + 'permission_callback' => function () {
40 + return current_user_can( 'manage_options' );
41 + },
42 + 'args' => array(
43 + 'consented' => array(
44 + 'description' => __( 'Whether the user agreed to data sharing.', 'wpfnl' ),
45 + 'type' => 'boolean',
46 + 'required' => true,
47 + ),
48 + ),
49 + ),
50 + )
51 + );
52 +
53 + register_rest_route(
54 + $this->namespace,
55 + '/' . $this->rest_base . '/track-step',
56 + array(
57 + array(
58 + 'methods' => WP_REST_Server::CREATABLE,
59 + 'callback' => array( $this, 'track_step' ),
60 + 'permission_callback' => function () {
61 + return current_user_can( 'manage_options' );
62 + },
63 + 'args' => array(
64 + 'event_type' => array(
65 + 'description' => __( 'Step event type: viewed, completed, or abandoned.', 'wpfnl' ),
66 + 'type' => 'string',
67 + 'required' => true,
68 + 'sanitize_callback' => 'sanitize_key',
69 + 'enum' => array( 'viewed', 'completed', 'abandoned', 'skipped' ),
70 + ),
71 + 'step_name' => array(
72 + 'description' => __( 'Step slug identifier.', 'wpfnl' ),
73 + 'type' => 'string',
74 + 'required' => true,
75 + 'sanitize_callback' => 'sanitize_key',
76 + ),
77 + 'step_index' => array(
78 + 'description' => __( '1-based step position.', 'wpfnl' ),
79 + 'type' => 'integer',
80 + 'required' => false,
81 + 'sanitize_callback' => 'absint',
82 + 'default' => 0,
83 + ),
84 + 'goal' => array(
85 + 'description' => __( 'Selected goal slug.', 'wpfnl' ),
86 + 'type' => 'string',
87 + 'required' => false,
88 + 'sanitize_callback' => 'sanitize_text_field',
89 + 'default' => '',
90 + ),
91 + 'time_on_step' => array(
92 + 'description' => __( 'Seconds spent on this step.', 'wpfnl' ),
93 + 'type' => 'integer',
94 + 'required' => false,
95 + 'sanitize_callback' => 'absint',
96 + 'default' => 0,
97 + ),
98 + 'total_steps' => array(
99 + 'description' => __( 'Total number of steps in the wizard for this goal path.', 'wpfnl' ),
100 + 'type' => 'integer',
101 + 'required' => false,
102 + 'sanitize_callback' => 'absint',
103 + 'default' => 0,
104 + ),
105 + ),
106 + ),
107 + )
108 + );
109 +
110 + register_rest_route(
111 + $this->namespace,
34 112 '/' . $this->rest_base . '/complete-step',
35 113 array(
36 114 array(
37 115 'methods' => WP_REST_Server::CREATABLE,
@@ -51,8 +129,22 @@
51 129 'type' => 'string',
52 130 'required' => false,
53 131 'sanitize_callback' => 'sanitize_text_field',
54 132 ),
133 + 'goal' => array(
134 + 'description' => __( 'Selected goal slug.', 'wpfnl' ),
135 + 'type' => 'string',
136 + 'required' => false,
137 + 'sanitize_callback' => 'sanitize_text_field',
138 + 'default' => '',
139 + ),
140 + 'total_steps' => array(
141 + 'description' => __( 'Total number of steps in the wizard for this goal path.', 'wpfnl' ),
142 + 'type' => 'integer',
143 + 'required' => false,
144 + 'sanitize_callback' => 'absint',
145 + 'default' => 0,
146 + ),
55 147 ),
56 148 ),
57 149 )
58 150 );
@@ -94,11 +186,12 @@
94 186 *
95 187 * @return \WP_REST_Response
96 188 */
97 189 public function complete_step( WP_REST_Request $request ) {
98 - $funnel_id = isset( $request['funnelId'] ) ? absint( $request['funnelId'] ) : 0;
99 - $action = isset( $request['action'] ) ? sanitize_text_field( $request['action'] ) : '';
100 - $goal = isset( $request['goal'] ) ? sanitize_text_field( $request['goal'] ) : '';
190 + $funnel_id = isset( $request['funnelId'] ) ? absint( $request['funnelId'] ) : 0;
191 + $action = isset( $request['action'] ) ? sanitize_text_field( $request['action'] ) : '';
192 + $goal = isset( $request['goal'] ) ? sanitize_text_field( $request['goal'] ) : '';
193 + $total_steps = isset( $request['total_steps'] ) ? absint( $request['total_steps'] ) : 0;
101 194
102 195 /**
103 196 * Fires when setup wizard completes an action.
104 197 *
@@ -107,11 +200,92 @@
107 200 * @param string $goal Completion goal.
108 201 */
109 202 do_action( 'wpfunnels_setup_wizard_complete', $funnel_id, $action, $goal );
110 203
204 + // Fire the unified onboarding outcome event.
205 + $telemetry = \WPFunnels\Tracking\Telemetry::instance();
206 + if ( $telemetry ) {
207 + $telemetry->track_onboarding_progress( [
208 + 'outcome' => 'completed',
209 + 'last_step_name' => 'complete',
210 + 'last_step_index' => $total_steps ?: null,
211 + 'total_steps' => $total_steps,
212 + 'goal' => $goal,
213 + 'funnel_id' => $funnel_id,
214 + ] );
215 + }
216 +
111 217 return rest_ensure_response( array(
112 218 'success' => true,
113 219 ) );
220 + }
221 +
222 + /**
223 + * Persist the user's telemetry consent decision.
224 + *
225 + * Called from Welcome.vue when the user clicks "Start setup" so that
226 + * subsequent per-step tracking calls pass the SDK's opt-in gate.
227 + *
228 + * @param WP_REST_Request $request
229 + * @return \WP_REST_Response
230 + */
231 + public function save_consent( WP_REST_Request $request ) {
232 + $consented = (bool) $request['consented'];
233 + $telemetry = \WPFunnels\Tracking\Telemetry::instance();
234 + if ( $telemetry ) {
235 + $telemetry->save_consent( $consented );
236 + }
237 + return rest_ensure_response( array( 'success' => true ) );
238 + }
239 +
240 + /**
241 + * Receive an onboarding step event from the setup wizard frontend.
242 + *
243 + * For viewed/completed events, dispatches track_onboarding_step().
244 + * For abandoned events, dispatches track_onboarding_progress() with outcome 'exited'.
245 + * For skipped events (user reached template picker but didn't import),
246 + * dispatches track_onboarding_progress() with outcome 'skipped'.
247 + *
248 + * @param WP_REST_Request $request
249 + * @return \WP_REST_Response
250 + */
251 + public function track_step( WP_REST_Request $request ) {
252 + $data = array(
253 + 'event_type' => $request['event_type'],
254 + 'step_name' => $request['step_name'],
255 + 'step_index' => (int) $request['step_index'],
256 + 'goal' => (string) $request['goal'],
257 + 'time_on_step' => (int) $request['time_on_step'],
258 + 'total_steps' => (int) ( $request['total_steps'] ?? 0 ),
259 + );
260 +
261 + $telemetry = \WPFunnels\Tracking\Telemetry::instance();
262 + if ( $telemetry ) {
263 + if ( 'abandoned' === $data['event_type'] ) {
264 + $telemetry->track_onboarding_progress( [
265 + 'outcome' => 'exited',
266 + 'last_step_name' => $data['step_name'],
267 + 'last_step_index' => $data['step_index'],
268 + 'total_steps' => $data['total_steps'],
269 + 'goal' => $data['goal'],
270 + 'funnel_id' => 0,
271 + ] );
272 + } elseif ( 'skipped' === $data['event_type'] ) {
273 + // User reached the template selection screen but exited without importing
274 + $telemetry->track_onboarding_progress( [
275 + 'outcome' => 'skipped',
276 + 'last_step_name' => $data['step_name'],
277 + 'last_step_index' => $data['step_index'],
278 + 'total_steps' => $data['total_steps'],
279 + 'goal' => $data['goal'],
280 + 'funnel_id' => 0,
281 + ] );
282 + } else {
283 + $telemetry->track_onboarding_step( $data );
284 + }
285 + }
286 +
287 + return rest_ensure_response( array( 'success' => true ) );
114 288 }
115 289
116 290 /**
117 291 * Retrieve WooCommerce products for the setup wizard Product Sync step.