PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.10.6
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.10.6
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
wpfunnels / includes / core / rest-api / Controllers / DashboardController.php

DashboardController.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.10.6, at includes/core/rest-api/Controllers/DashboardController.php

468 lines 11.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 namespace WPFunnels\Rest\Controllers;
5
6 use WPFunnels\Report\ReportGenerator;
7 use WPFunnels\Wpfnl_functions;
8
9 /**
10 *
11 *
12 * Class DashboardController
13 * @package WPFunnels\Rest\Controllers
14 * @since 3.2.0
15 */
16 class DashboardController extends Wpfnl_REST_Controller
17 {
18
19 /**
20 * Endpoint namespace.
21 *
22 * @var string
23 * @since 3.2.0
24 */
25 protected $namespace = 'wpfunnels/v1';
26
27
28 /**
29 * Route base.
30 *
31 * @var string
32 * @since 3.2.0
33 */
34 protected $rest_base = 'report';
35
36
37 /**
38 * Makes sure the current user has access to READ the settings APIs.
39 *
40 *
41 * @param $request
42 * @return \WP_Error|boolean
43 * @since 3.2.0
44 */
45 public function get_items_permissions_check($request)
46 {
47 if (!Wpfnl_functions::wpfnl_rest_check_manager_permissions('settings')) {
48 return new \WP_Error('wpfunnels_rest_cannot_edit', __('Sorry, you cannot list resources.', 'wpfnl'), array('status' => rest_authorization_required_code()));
49 }
50 return true;
51 }
52
53
54 /**
55 * Register rest routes
56 *
57 * @since 3.2.0
58 */
59 public function register_routes()
60 {
61 register_rest_route(
62 $this->namespace,
63 '/' . $this->rest_base . '/overview',
64 array(
65 array(
66 'methods' => \WP_REST_Server::READABLE,
67 'args' => $this->get_stats_args(),
68 'callback' => array($this, 'get_overview'),
69 'permission_callback' => array($this, 'get_items_permissions_check'),
70 ),
71 )
72 );
73
74 register_rest_route(
75 $this->namespace,
76 '/' . $this->rest_base . '/stats',
77 array(
78 array(
79 'methods' => \WP_REST_Server::READABLE,
80 'args' => $this->get_stats_args(),
81 'callback' => array($this, 'get_stats'),
82 'permission_callback' => array($this, 'get_items_permissions_check'),
83 ),
84 )
85 );
86
87 register_rest_route(
88 $this->namespace,
89 '/' . $this->rest_base . '/top-funnels',
90 array(
91 array(
92 'methods' => \WP_REST_Server::READABLE,
93 'args' => $this->get_stats_args(),
94 'callback' => array($this, 'get_top_funnels'),
95 'permission_callback' => array($this, 'get_items_permissions_check'),
96 ),
97 )
98 );
99
100 register_rest_route(
101 $this->namespace,
102 '/' . $this->rest_base . '/onboarding-status',
103 array(
104 array(
105 'methods' => \WP_REST_Server::READABLE,
106 'callback' => array($this, 'get_onboarding_status'),
107 'permission_callback' => array($this, 'get_items_permissions_check'),
108 ),
109 )
110 );
111
112 register_rest_route(
113 $this->namespace,
114 '/' . $this->rest_base . '/has-funnels',
115 array(
116 array(
117 'methods' => \WP_REST_Server::READABLE,
118 'callback' => array($this, 'check_has_funnels'),
119 'permission_callback' => array( $this, 'get_items_permissions_check' ),
120 ),
121 )
122 );
123
124 register_rest_route(
125 $this->namespace,
126 '/' . $this->rest_base . '/dismiss-onboarding',
127 array(
128 array(
129 'methods' => \WP_REST_Server::CREATABLE,
130 'callback' => array($this, 'dismiss_onboarding'),
131 'permission_callback' => array( $this, 'get_items_permissions_check' ),
132 ),
133 )
134 );
135 }
136
137 /**
138 * Get stats arguments
139 *
140 * @return mixed|void
141 * @since 3.2.0
142 */
143 public function get_stats_args()
144 {
145 return array(
146 'after' => array(
147 'type' => 'string',
148 'format' => 'date-time',
149 'validate_callback' => 'rest_validate_request_arg',
150 ),
151 'before' => array(
152 'type' => 'string',
153 'format' => 'date-time',
154 'validate_callback' => 'rest_validate_request_arg',
155 ),
156 'interval' => array(
157 'type' => 'string',
158 'default' => 'week',
159 'enum' => array(
160 'hour',
161 'day',
162 'week',
163 'month',
164 'quarter',
165 'year',
166 ),
167 ),
168 );
169 }
170
171
172 /**
173 * Get overview data of funnels
174 *
175 * @param \WP_REST_Request $request
176 * @return \WP_Error|\WP_REST_Response
177 * @throws \Exception
178 *
179 * @since 3.2.0
180 */
181 public function get_overview(\WP_REST_Request $request)
182 {
183 $params = $request->get_params();
184 $start_date = isset($params['after']) ? $params['after'] : $this->default_after()->format('Y-m-d H:i:s');
185 $end_date = isset($params['before']) ? $params['before'] : $this->default_before()->format('Y-m-d H:i:s');
186 $response = ReportGenerator::get_overview($start_date, $end_date);
187 return rest_ensure_response($response);
188 }
189
190
191 /**
192 * Get stats of the funnels with intervals period
193 *
194 * @param \WP_REST_Request $request
195 * @return \WP_Error|\WP_REST_Response
196 * @throws \Exception
197 *
198 * @since 3.2.0
199 */
200 public function get_stats(\WP_REST_Request $request)
201 {
202 $params = $request->get_params();
203 $start_date = isset($params['after']) ? $params['after'] : $this->default_after()->format('Y-m-d H:i:s');
204 $end_date = isset($params['before']) ? $params['before'] : $this->default_before()->format('Y-m-d H:i:s');
205 $interval = isset($params['interval']) ? $params['interval'] : 'day';
206 $response = ReportGenerator::get_stats($start_date, $end_date, $interval);
207 return rest_ensure_response($response);
208 }
209
210
211 /**
212 * Get 3 top performing funnels
213 *
214 * @param \WP_REST_Request $request
215 * @return \WP_Error|\WP_REST_Response
216 *
217 * @since 3.2.0
218 */
219 public function get_top_funnels(\WP_REST_Request $request)
220 {
221 $response['status'] = true;
222 $response['data'] = ReportGenerator::get_top_funnels();
223 return rest_ensure_response($response);
224 }
225
226
227 /**
228 * Get onboarding status.
229 *
230 * Determines whether the onboarding section should be shown on the dashboard.
231 * The onboarding is hidden when ALL three conditions are met:
232 * 1. At least one funnel is published (live)
233 * 2. At least one funnel contains an order bump (upsell is optional/bonus)
234 * 3. At least one real order has been placed through any funnel
235 *
236 * Also returns individual step completion states for the checklist.
237 *
238 * @return \WP_REST_Response
239 * @since 3.9.5
240 */
241 public function get_onboarding_status()
242 {
243 // ── Check if onboarding has been dismissed ──
244 $is_dismissed = get_transient( 'wpfnl_onboarding_dismissed' );
245 if ( $is_dismissed ) {
246 return rest_ensure_response( array(
247 'success' => true,
248 'show_onboarding' => false,
249 'steps' => array(),
250 'completed_count' => 0,
251 'total_steps' => 0,
252 'progress_percent' => 0,
253 ) );
254 }
255
256 $is_wc_active = class_exists('WooCommerce');
257 $has_live_funnel = false;
258 $has_order_bump = false;
259 $has_upsell = false;
260 $has_real_order = false;
261 $has_funnel_created = false;
262 $has_store_checkout_created = false;
263
264 // Meta query to exclude store checkout funnels
265 $exclude_sc_meta = array(
266 'relation' => 'OR',
267 array(
268 'key' => '_wpfnl_funnel_type',
269 'compare' => 'NOT EXISTS',
270 ),
271 array(
272 'key' => '_wpfnl_funnel_type',
273 'value' => 'store_checkout',
274 'compare' => '!=',
275 ),
276 );
277
278 // ── Step 1: Check for published funnels (excluding store checkouts) ──
279 $funnels = get_posts(array(
280 'post_type' => WPFNL_FUNNELS_POST_TYPE,
281 'post_status' => 'publish',
282 'numberposts' => -1,
283 'fields' => 'ids',
284 'meta_query' => $exclude_sc_meta,
285 ));
286
287 if (!empty($funnels)) {
288 $has_funnel_created = true;
289 $has_live_funnel = true;
290 }
291
292 // Also check for draft funnels (counts as "created" but not "live")
293 if (!$has_funnel_created) {
294 $draft_funnels = get_posts(array(
295 'post_type' => WPFNL_FUNNELS_POST_TYPE,
296 'post_status' => array('publish', 'draft'),
297 'numberposts' => 1,
298 'fields' => 'ids',
299 'meta_query' => $exclude_sc_meta,
300 ));
301 $has_funnel_created = !empty($draft_funnels);
302 }
303
304 // ── Check for store checkout funnels separately ──
305 $sc_funnels = get_posts(array(
306 'post_type' => WPFNL_FUNNELS_POST_TYPE,
307 'post_status' => array('publish', 'draft'),
308 'numberposts' => 1,
309 'fields' => 'ids',
310 'meta_query' => array(
311 array(
312 'key' => '_wpfnl_funnel_type',
313 'value' => 'store_checkout',
314 ),
315 ),
316 ));
317 $has_store_checkout_created = !empty($sc_funnels);
318
319 // ── Step 2: Check for order bump & upsell across all published funnels ──
320 if (!empty($funnels)) {
321 foreach ($funnels as $funnel_id) {
322 $steps = Wpfnl_functions::get_steps($funnel_id);
323
324 if (!is_array($steps) || empty($steps)) {
325 continue;
326 }
327
328 foreach ($steps as $step) {
329 $step_id = isset($step['id']) ? $step['id'] : 0;
330 $step_type = isset($step['step_type']) ? $step['step_type'] : '';
331
332 // Check for order bump on checkout steps
333 if ('checkout' === $step_type && !$has_order_bump) {
334 $ob_settings = get_post_meta($step_id, 'order-bump-settings', true);
335 if (!empty($ob_settings) && is_array($ob_settings)) {
336 $is_multidimensional = Wpfnl_functions::check_array_is_multidimentional($ob_settings);
337 if ($is_multidimensional) {
338 foreach ($ob_settings as $ob) {
339 if (!empty($ob['product']) && !empty($ob['isEnabled'])) {
340 $has_order_bump = true;
341 break;
342 }
343 }
344 }
345 else {
346 // Single OB (legacy format)
347 if (!empty($ob_settings['product'])) {
348 $has_order_bump = true;
349 }
350 }
351 }
352 }
353
354 // Check for upsell step
355 if ('upsell' === $step_type) {
356 $has_upsell = true;
357 }
358 }
359
360 // If we already found both, no need to check more funnels
361 if ($has_order_bump && $has_upsell) {
362 break;
363 }
364 }
365 }
366
367 // ── Step 3: Check for real orders through funnels ──
368 if ($is_wc_active && !empty($funnels)) {
369 global $wpdb;
370 $stats_table = $wpdb->prefix . 'wpfnl_stats';
371
372 // Check if stats table exists
373 $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $stats_table));
374 if ($table_exists) {
375 $order_count = $wpdb->get_var(
376 "SELECT COUNT(id) FROM {$stats_table} WHERE status IN ('completed', 'processing') LIMIT 1"
377 );
378 $has_real_order = (int)$order_count > 0;
379 }
380 }
381
382 // ── Determine if onboarding should be shown ──
383 // Hide onboarding only when ALL conditions are met:
384 // - At least 1 live funnel
385 // - Has an order bump (upsell is optional)
386 // - Has at least 1 real order
387 $should_hide_onboarding = $has_live_funnel && $has_order_bump && $has_real_order;
388
389 // ── Build checklist step completion data ──
390 $completed_count = 0;
391 $steps_status = array(
392 'woocommerce_connected' => $is_wc_active,
393 'funnel_created' => $has_funnel_created,
394 'store_checkout_created' => $has_store_checkout_created,
395 'order_bump_added' => $has_order_bump,
396 'upsell_added' => $has_upsell,
397 'test_order_placed' => $has_real_order,
398 'funnel_live' => $has_live_funnel,
399 );
400
401 foreach ($steps_status as $status) {
402 if ($status) {
403 $completed_count++;
404 }
405 }
406
407 $total_steps = count($steps_status);
408 $progress_percent = $total_steps > 0 ? round(($completed_count / $total_steps) * 100) : 0;
409
410 return rest_ensure_response(array(
411 'success' => true,
412 'show_onboarding' => !$should_hide_onboarding,
413 'steps' => $steps_status,
414 'completed_count' => $completed_count,
415 'total_steps' => $total_steps,
416 'progress_percent' => $progress_percent,
417 ));
418 }
419
420 /**
421 * Check if there are any funnels
422 *
423 * @param \WP_REST_Request $request
424 * @return \WP_REST_Response
425 *
426 * @since 3.9.5
427 */
428 public function check_has_funnels( \WP_REST_Request $request ) {
429 global $wpdb;
430
431 // Count only non-store-checkout funnels
432 $funnel_count = $wpdb->get_var(
433 $wpdb->prepare(
434 "SELECT COUNT(*) FROM {$wpdb->posts} p
435 LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_wpfnl_funnel_type'
436 WHERE p.post_type = %s
437 AND p.post_status IN ('publish', 'draft')
438 AND (pm.meta_value IS NULL OR pm.meta_value != 'store_checkout')",
439 'wpfunnels'
440 )
441 );
442
443 return rest_ensure_response( array(
444 'success' => true,
445 'has_funnels' => (int) $funnel_count > 0,
446 'funnel_count' => (int) $funnel_count
447 ) );
448 }
449
450 /**
451 * Dismiss the onboarding checklist
452 *
453 * @param \WP_REST_Request $request
454 * @return \WP_REST_Response
455 *
456 * @since 3.9.5
457 */
458 public function dismiss_onboarding( \WP_REST_Request $request ) {
459 // Store dismissal in a transient for 30 days
460 set_transient( 'wpfnl_onboarding_dismissed', true, 30 * DAY_IN_SECONDS );
461
462 return rest_ensure_response( array(
463 'success' => true,
464 'message' => 'Onboarding dismissed successfully'
465 ) );
466 }
467 }
468