← 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. |