PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.1.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.1.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
cartflows / admin-core / inc / admin-helper.php

admin-helper.php in CartFlows – Funnel Builder & Checkout Plugin for WooCommerce 3.1.0, at admin-core/inc/admin-helper.php

1,309 lines 37.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CartFlows Admin Helper.
4 *
5 * @package CartFlows
6 */
7
8 namespace CartflowsAdmin\AdminCore\Inc;
9
10 use Automattic\WooCommerce\Utilities\OrderUtil;
11
12 // Exit if accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Class AdminHelper.
19 */
20 class AdminHelper {
21
22 /**
23 * Meta_options.
24 *
25 * @var object instance
26 */
27 public static $meta_options = array();
28 /**
29 * Common.
30 *
31 * @var object instance
32 */
33 public static $common = null;
34
35 /**
36 * Permalink setting.
37 *
38 * @var object instance
39 */
40 public static $permalink_setting = null;
41
42 /**
43 * Facebook.
44 *
45 * @var object instance
46 */
47 public static $facebook = null;
48
49 /**
50 * TikTok.
51 *
52 * @var object instance
53 */
54 public static $tiktok = null;
55
56 /**
57 * Snapchat.
58 *
59 * @var object instance
60 */
61 public static $snapchat = null;
62
63 /**
64 * Google_analytics_settings.
65 *
66 * @var object instance
67 */
68 public static $google_analytics_settings = null;
69
70 /**
71 * Google_analytics_settings.
72 *
73 * @var object instance
74 */
75 public static $pinterest = null;
76
77 /**
78 * Google_ads_settings.
79 *
80 * @since 2.1.0
81 * @var object instance
82 */
83 public static $google_ads_settings_data = null;
84
85 /**
86 * Options.
87 *
88 * @var object instance
89 */
90 public static $options = null;
91
92 /**
93 * Get flow meta options.
94 *
95 * @param int $post_id post id.
96 * @return array.
97 */
98 public static function get_flow_meta_options( $post_id ) {
99
100 if ( ! isset( self::$meta_options[ $post_id ] ) ) {
101
102 /**
103 * Set metabox options
104 */
105
106 $default_meta = wcf()->options->get_flow_fields( $post_id );
107 $stored_meta = get_post_meta( $post_id );
108
109 /**
110 * Get options
111 */
112 self::$meta_options[ $post_id ] = self::get_prepared_meta_options( $default_meta, $stored_meta );
113 }
114
115 return self::$meta_options[ $post_id ];
116 }
117
118 /**
119 * Get step meta options.
120 *
121 * @param int $step_id step id.
122 * @return array.
123 */
124 public static function get_step_meta_options( $step_id ) {
125
126 if ( ! isset( self::$meta_options[ $step_id ] ) ) {
127
128 $step_type = wcf_get_step_type( $step_id );
129 $step_fields = array();
130 $step_tabs = array();
131
132 $default_meta = self::get_step_default_meta( $step_type, $step_id );
133
134 $stored_meta = get_post_meta( $step_id );
135
136 $prepared_options = self::get_prepared_meta_options( $default_meta, $stored_meta );
137
138 $prepared_options = apply_filters( 'cartflows_admin_' . $step_type . '_step_meta_fields', $prepared_options, $step_id );
139
140 $step_tabs = apply_filters( 'cartflows_admin_' . $step_type . '_step_tabs', $step_tabs );
141
142 /**
143 * Get options
144 */
145 self::$meta_options[ $step_id ]['type'] = $step_type;
146 self::$meta_options[ $step_id ]['tabs'] = $step_tabs;
147 self::$meta_options[ $step_id ]['options'] = $prepared_options;
148 }
149
150 return self::$meta_options[ $step_id ];
151 }
152
153 /**
154 * Merge default and saved meta options.
155 *
156 * @param array $default_meta Default meta.
157 * @param array $stored_meta Saved meta.
158 * @return array.
159 */
160 public static function get_prepared_meta_options( $default_meta, $stored_meta ) {
161
162 $meta_options = array();
163
164 // Set stored and override defaults.
165 foreach ( $default_meta as $key => $value ) {
166
167 $meta_options[ $key ] = ( isset( $stored_meta[ $key ][0] ) ) ? maybe_unserialize( $stored_meta[ $key ][0] ) : $default_meta[ $key ]['default'];
168 }
169
170 return $meta_options;
171 }
172
173 /**
174 * Get Common settings.
175 *
176 * @return array
177 */
178 public static function get_common_settings() {
179
180 $options = array();
181
182 $common_default = apply_filters(
183 'cartflows_common_settings_default',
184 array(
185 'global_checkout' => '',
186 'override_global_checkout' => 'enable',
187 'disallow_indexing' => 'disable',
188 'default_page_builder' => 'elementor',
189 )
190 );
191
192 $common = self::get_admin_settings_option( '_cartflows_common', false, false );
193
194 $common = wp_parse_args( $common, $common_default );
195
196 foreach ( $common as $key => $data ) {
197 $options[ '_cartflows_common[' . $key . ']' ] = $data;
198 }
199
200 return $options;
201 }
202
203 /**
204 * Get admin settings.
205 *
206 * Note: Use this function to access any properties to backend-end of the website i:e in admin-core.
207 *
208 * @param string $key key.
209 * @param bool $default key.
210 * @param bool $network_override key.
211 *
212 * @return array.
213 */
214 public static function get_admin_settings_option( $key, $default = false, $network_override = false ) {
215
216 // Get the site-wide option if we're in the network admin.
217 if ( $network_override && is_multisite() ) {
218 $value = get_site_option( $key, $default );
219 } else {
220 $value = get_option( $key, $default );
221 }
222
223 return $value;
224 }
225
226 /**
227 * Update admin settings.
228 *
229 * Note: Use this function to access any properties to backend-end of the website i:e in admin-core.
230 *
231 * @param string $key key.
232 * @param mixed $value key.
233 * @param bool $network network.
234 * @return void
235 */
236 public static function update_admin_settings_option( $key, $value, $network = false ) {
237
238 // Update the site-wide option since we're in the network admin.
239 if ( $network && is_multisite() ) {
240 update_site_option( $key, $value );
241 } else {
242 update_option( $key, $value );
243 }
244 }
245
246 /**
247 * Get Common settings.
248 *
249 * @return array
250 */
251 public static function get_permalink_settings() {
252
253 $options = array();
254
255 $permalink_default = apply_filters(
256 'cartflows_permalink_settings_default',
257 array(
258 'permalink' => CARTFLOWS_STEP_PERMALINK_SLUG,
259 'permalink_flow_base' => CARTFLOWS_FLOW_PERMALINK_SLUG,
260 'permalink_structure' => '',
261
262 )
263 );
264
265 $permalink_data = self::get_admin_settings_option( '_cartflows_permalink', false, true );
266
267 $permalink_data = wp_parse_args( $permalink_data, $permalink_default );
268
269 foreach ( $permalink_data as $key => $data ) {
270 $options[ '_cartflows_permalink[' . $key . ']' ] = $data;
271 }
272
273 return $options;
274 }
275
276 /**
277 * Get Common settings.
278 *
279 * @return array
280 */
281 public static function get_facebook_settings() {
282
283 $options = array();
284
285 $facebook_default = array(
286 'facebook_pixel_id' => '',
287 'facebook_pixel_add_to_cart' => 'enable',
288 'facebook_pixel_view_content' => 'enable',
289 'facebook_pixel_initiate_checkout' => 'enable',
290 'facebook_pixel_add_payment_info' => 'enable',
291 'facebook_pixel_purchase_complete' => 'enable',
292 'facebook_pixel_optin_lead' => 'enable',
293 'facebook_pixel_tracking' => 'disable',
294 'facebook_pixel_tracking_for_site' => 'disable',
295 );
296
297 $facebook = self::get_admin_settings_option( '_cartflows_facebook', false, true );
298
299 $facebook = wp_parse_args( $facebook, $facebook_default );
300
301 $facebook = apply_filters( 'cartflows_facebook_settings_default', $facebook );
302
303 foreach ( $facebook as $key => $data ) {
304 $options[ '_cartflows_facebook[' . $key . ']' ] = $data;
305 }
306
307 return $options;
308 }
309
310 /**
311 * Get Common settings.
312 *
313 * @return array
314 */
315 public static function get_google_analytics_settings() {
316
317 $options = array();
318
319 $google_analytics_settings_default = apply_filters(
320 'cartflows_google_analytics_settings_default',
321 array(
322 'enable_google_analytics' => 'disable',
323 'enable_google_analytics_for_site' => 'disable',
324 'google_analytics_id' => '',
325 'enable_begin_checkout' => 'disable',
326 'enable_add_to_cart' => 'disable',
327 'enable_add_payment_info' => 'disable',
328 'enable_purchase_event' => 'disable',
329 )
330 );
331
332 $google_analytics_settings_data = self::get_admin_settings_option( '_cartflows_google_analytics', false, false );
333
334 $google_analytics_settings_data = wp_parse_args( $google_analytics_settings_data, $google_analytics_settings_default );
335
336 foreach ( $google_analytics_settings_data as $key => $data ) {
337 $options[ '_cartflows_google_analytics[' . $key . ']' ] = $data;
338 }
339
340 return $options;
341 }
342
343 /**
344 * Get Common settings.
345 *
346 * @return array
347 */
348 public static function get_tiktok_settings() {
349
350 $options = array();
351
352 $tiktok_default = array(
353 'tiktok_pixel_id' => '',
354 'enable_tiktok_begin_checkout' => 'disable',
355 'enable_tiktok_add_to_cart' => 'disable',
356 'enable_tiktok_view_content' => 'disable',
357 'enable_tiktok_add_payment_info' => 'disable',
358 'enable_tiktok_purchase_event' => 'disable',
359 'enable_tiktok_optin_lead' => 'disable',
360 'tiktok_pixel_tracking' => 'disable',
361 'tiktok_pixel_tracking_for_site' => 'disable',
362 );
363
364 $tiktok = self::get_admin_settings_option( '_cartflows_tiktok', false, false );
365
366 $tiktok = wp_parse_args( $tiktok, $tiktok_default );
367
368 $tiktok = apply_filters( 'cartflows_tiktok_settings_default', $tiktok );
369
370 foreach ( $tiktok as $key => $data ) {
371 $options[ '_cartflows_tiktok[' . $key . ']' ] = $data;
372 }
373
374 return $options;
375 }
376
377 /**
378 * Get Common settings of pinterest.
379 *
380 * @return array
381 */
382 public static function get_pinterest_settings() {
383
384 $options = array();
385
386 $pinterest_default = array(
387 'pinterest_tag_id' => '',
388 'enable_pinterest_consent' => 'disable',
389 'enable_pinterest_begin_checkout' => 'disable',
390 'enable_pinterest_add_to_cart' => 'disable',
391 'enable_pinterest_add_payment_info' => 'disable',
392 'enable_pinterest_purchase_event' => 'disable',
393 'enable_pinterest_optin_lead' => 'disable',
394 'enable_pinterest_signup' => 'disable',
395 'pinterest_tag_tracking' => 'disable',
396 'pinterest_tag_tracking_for_site' => 'disable',
397 );
398
399 $pinterest = self::get_admin_settings_option( '_cartflows_pinterest', false, false );
400
401 $pinterest = wp_parse_args( $pinterest, $pinterest_default );
402
403 $pinterest = apply_filters( 'cartflows_pinterest_settings_default', $pinterest );
404
405 foreach ( $pinterest as $key => $data ) {
406 $options[ '_cartflows_pinterest[' . $key . ']' ] = $data;
407 }
408
409 return $options;
410 }
411
412 /**
413 * Get Common settings.
414 *
415 * @since 2.1.0
416 * @return array
417 */
418 public static function get_google_ads_settings() {
419
420 $options = array();
421
422 $google_ads_settings_default = apply_filters(
423 'cartflows_google_ads_settings_default',
424 array(
425 'google_ads_id' => '',
426 'google_ads_label' => '',
427 'enable_google_ads_begin_checkout' => 'disable',
428 'enable_google_ads_add_to_cart' => 'disable',
429 'enable_google_ads_view_content' => 'disable',
430 'enable_google_ads_add_payment_info' => 'disable',
431 'enable_google_ads_purchase_event' => 'disable',
432 'enable_google_ads_optin_lead' => 'disable',
433 'google_ads_tracking' => 'disable',
434 'google_ads_for_site' => 'disable',
435 )
436 );
437
438 $google_ads_settings_data = self::get_admin_settings_option( '_cartflows_google_ads', false, false );
439
440 $google_ads_settings_data = wp_parse_args( $google_ads_settings_data, $google_ads_settings_default );
441
442 foreach ( $google_ads_settings_data as $key => $data ) {
443 $options[ '_cartflows_google_ads[' . $key . ']' ] = $data;
444 }
445
446 return $options;
447 }
448
449 /**
450 * Get Snapchat settings.
451 *
452 * @since 2.1.0
453 * @return array
454 */
455 public static function get_snapchat_settings() {
456
457 $options = array();
458
459 $snapchat_settings_default = apply_filters(
460 'cartflows_snapchat_settings_default',
461 array(
462 'snapchat_pixel_id' => '',
463 'enable_snapchat_begin_checkout' => 'disable',
464 'enable_snapchat_add_to_cart' => 'disable',
465 'enable_snapchat_view_content' => 'disable',
466 'enable_snapchat_purchase_event' => 'disable',
467 'enable_snapchat_optin_lead' => 'disable',
468 'enable_snapchat_subscribe_event' => 'disable',
469 'snapchat_pixel_tracking' => 'disable',
470 'snapchat_pixel_for_site' => 'disable',
471 )
472 );
473
474 $snapchat_settings_data = self::get_admin_settings_option( '_cartflows_snapchat', false, false );
475
476 $snapchat_settings_data = wp_parse_args( $snapchat_settings_data, $snapchat_settings_default );
477
478 foreach ( $snapchat_settings_data as $key => $data ) {
479 $options[ '_cartflows_snapchat[' . $key . ']' ] = $data;
480 }
481
482 return $options;
483 }
484
485 /**
486 * Get User role settings.
487 *
488 * @return array
489 */
490 public static function get_user_role_management_settings() {
491 global $wp_roles;
492
493 $options = array();
494
495 // Get all user roles.
496 $roles_names_array = array_keys( $wp_roles->get_names() );
497 $roles_names_array = array_diff( $roles_names_array, array( 'administrator' ) );
498 $user_role_management_default = array();
499
500 foreach ( $roles_names_array as $role_name ) {
501 $user_role_management_default[ $role_name ] = 'no_access';
502 }
503
504 $user_role_management = self::get_admin_settings_option( '_cartflows_roles', false, false );
505
506 $user_role_management = wp_parse_args( $user_role_management, $user_role_management_default );
507
508 $user_role_management = apply_filters( 'cartflows_user_role_default_settings', $user_role_management );
509
510 foreach ( $user_role_management as $key => $data ) {
511 $options[ '_cartflows_roles[' . $key . ']' ] = $data;
512 }
513
514 return $options;
515 }
516
517 /**
518 * Get Google Auto-Address Fields settings.
519 *
520 * @return array
521 */
522 public static function get_google_auto_fields_settings() {
523 $options = array();
524
525 $google_auto_fields_setting_default = apply_filters(
526 'cartflows_google_auto_fields_setting_default',
527 array(
528 'google_map_api_key' => '',
529 )
530 );
531
532 $google_auto_fields_settings_data = self::get_admin_settings_option( '_cartflows_google_auto_address', false, false );
533
534 $google_auto_fields_settings_data = wp_parse_args( $google_auto_fields_settings_data, $google_auto_fields_setting_default );
535
536 foreach ( $google_auto_fields_settings_data as $key => $data ) {
537 $options[ '_cartflows_google_auto_address[' . $key . ']' ] = $data;
538 }
539
540 return $options;
541 }
542
543 /**
544 * Get Global CSS & Scripts settings.
545 *
546 * @return array.
547 */
548 public static function get_global_scripts_settings() {
549 $options = array();
550
551 $default = array(
552 'global_css' => '',
553 'global_js' => '',
554 );
555
556 $global_scripts = self::get_admin_settings_option( '_cartflows_global_scripts', false, false );
557
558 $global_scripts = wp_parse_args( $global_scripts, $default );
559
560 foreach ( $global_scripts as $key => $data ) {
561 $options[ '_cartflows_global_scripts[' . $key . ']' ] = $data;
562 }
563
564 return $options;
565 }
566
567 /**
568 * Clear Page Builder Cache
569 */
570 public static function clear_cache() {
571
572 // Clear 'Elementor' file cache.
573 if ( class_exists( '\Elementor\Plugin' ) ) {
574 \Elementor\Plugin::$instance->files_manager->clear_cache();
575 }
576 }
577
578 /**
579 * Get Flows count.
580 */
581 public static function get_flows_count() {
582
583 $flow_posts = get_posts(
584 array(
585 'posts_per_page' => 4,
586 'post_type' => CARTFLOWS_FLOW_POST_TYPE,
587 'post_status' => array( 'publish', 'pending', 'draft', 'future', 'private' ),
588 )
589 );
590
591 return count( $flow_posts );
592 }
593
594 /**
595 * Font family field.
596 *
597 * @return array field.
598 */
599 public static function get_font_family() {
600
601 $font_family[0] = array(
602 'value' => '',
603 'label' => __( 'Default', 'cartflows' ),
604 );
605
606 $system_font_family = array();
607 $google_font_family = array();
608
609 foreach ( \CartFlows_Font_Families::get_system_fonts() as $name => $variants ) {
610 array_push(
611 $system_font_family,
612 array(
613 'value' => $name,
614 'label' => esc_attr( $name ),
615 )
616 );
617 }
618
619 $font_family[1] = array(
620 'label' => __( 'System Fonts', 'cartflows' ),
621 'options' => $system_font_family,
622 );
623
624 foreach ( \CartFlows_Font_Families::get_google_fonts() as $name => $single_font ) {
625 $variants = wcf_get_prop( $single_font, 'variants' );
626 $category = wcf_get_prop( $single_font, 'category' );
627 $font_value = '\'' . esc_attr( $name ) . '\', ' . esc_attr( $category );
628 array_push(
629 $google_font_family,
630 array(
631 'value' => $font_value,
632 'label' => esc_attr( $name ),
633 )
634 );
635 }
636
637 $font_family[2] = array(
638 'label' => __( 'Google Fonts', 'cartflows' ),
639 'options' => $google_font_family,
640 );
641
642 return $font_family;
643 }
644
645 /**
646 * Get step default meta.
647 *
648 * @param string $step_type type.
649 * @param int $step_id id.
650 */
651 public static function get_step_default_meta( $step_type, $step_id ) {
652
653 $step_default_fields = array();
654
655 switch ( $step_type ) {
656 case 'landing':
657 $step_default_fields = wcf()->options->get_landing_fields( $step_id );
658 break;
659
660 case 'checkout':
661 $step_default_fields = wcf()->options->get_checkout_fields( $step_id );
662 break;
663
664 case 'thankyou':
665 $step_default_fields = wcf()->options->get_thankyou_fields( $step_id );
666 break;
667
668 case 'optin':
669 $step_default_fields = wcf()->options->get_optin_fields( $step_id );
670 break;
671
672 default:
673 break;
674 }
675 $step_default_fields = apply_filters( 'cartflows_admin_' . $step_type . '_step_default_meta_fields', $step_default_fields, $step_id );
676 return $step_default_fields;
677 }
678
679 /**
680 * Get options.
681 */
682 public static function get_options() {
683
684 $general_settings = self::get_common_settings();
685 $permalink_settings = self::get_permalink_settings();
686 $fb_settings = self::get_facebook_settings();
687 $tik_settings = self::get_tiktok_settings();
688 $ga_settings = self::get_google_analytics_settings();
689 $pin_settings = self::get_pinterest_settings();
690 $gads_settings = self::get_google_ads_settings();
691 $snap_settings = self::get_snapchat_settings();
692 $urm_settings = self::get_user_role_management_settings();
693 $auto_fields = self::get_google_auto_fields_settings();
694 $global_scripts = self::get_global_scripts_settings();
695 $options = array_merge( $general_settings, $permalink_settings, $fb_settings, $tik_settings, $ga_settings, $gads_settings, $pin_settings, $snap_settings, $urm_settings, $auto_fields, $global_scripts );
696 $options = apply_filters( 'cartflows_admin_global_data_options', $options );
697
698 return $options;
699 }
700
701
702
703 /**
704 * Prepare step data.
705 *
706 * @param int $flow_id Flow id.
707 * @param array $meta_options Meta data.
708 *
709 * @return array
710 */
711 public static function prepare_step_data( $flow_id, $meta_options ) {
712
713 $steps = $meta_options['wcf-steps'];
714
715 if ( is_array( $steps ) && ! empty( $steps ) ) {
716
717 foreach ( $steps as $in => $step ) {
718 $step_id = $step['id'];
719 $steps[ $in ]['title'] = get_the_title( $step_id );
720 $steps[ $in ]['is_product_assigned'] = \Cartflows_Helper::has_product_assigned( $step_id );
721 $steps[ $in ]['step_disabled'] = get_post_meta( $step_id, 'wcf-disable-step', true ) === 'yes';
722
723 $steps[ $in ]['actions'] = self::get_step_actions( $flow_id, $step_id );
724 $steps[ $in ]['menu_actions'] = self::get_step_actions( $flow_id, $step_id, 'menu' );
725
726 $steps[ $in ]['page_builder_edit'] = \Cartflows_Helper::get_page_builder_edit_link( $step_id );
727
728 if ( 'checkout' === $step['type'] ) {
729 $order_bumps = get_post_meta( $step_id, 'wcf-order-bumps', true );
730 $order_bumps = is_array( $order_bumps ) ? array_values( $order_bumps ) : array();
731 $steps[ $in ]['order_bumps'] = $order_bumps;
732 $steps[ $in ]['order_bump_count'] = count( $order_bumps );
733 }
734
735 /* Add variation data */
736 if ( ! empty( $steps[ $in ]['ab-test-variations'] ) ) {
737
738 $ab_test_variations = $steps[ $in ]['ab-test-variations'];
739
740 foreach ( $ab_test_variations as $variation_in => $variation ) {
741
742 $ab_test_variations[ $variation_in ]['title'] = get_the_title( $variation['id'] );
743 $ab_test_variations[ $variation_in ]['actions'] = self::get_ab_test_step_actions( $flow_id, $variation['id'] );
744 $ab_test_variations[ $variation_in ]['menu_actions'] = self::get_ab_test_step_actions( $flow_id, $variation['id'], 'menu' );
745 $ab_test_variations[ $variation_in ]['is_product_assigned'] = \Cartflows_Helper::has_product_assigned( $variation['id'] );
746 $ab_test_variations[ $variation_in ]['step_disabled'] = get_post_meta( $variation['id'], 'wcf-disable-step', true ) === 'yes';
747 }
748
749 $steps[ $in ]['ab-test-variations'] = $ab_test_variations;
750 }
751
752 if ( ! empty( $steps[ $in ]['ab-test-archived-variations'] ) ) {
753
754 $ab_test_archived_variations = $steps[ $in ]['ab-test-archived-variations'];
755
756 foreach ( $ab_test_archived_variations as $variation_in => $variation ) {
757
758 // Don't add hidden archived steps.
759 if ( get_post_meta( $variation['id'], 'wcf-hide-step', true ) ) {
760 unset( $ab_test_archived_variations[ $variation_in ] );
761 continue;
762 }
763
764 $ab_test_archived_variations[ $variation_in ]['actions'] = self::get_ab_test_step_archived_actions( $flow_id, $variation['id'], $variation['deleted'] );
765 $ab_test_archived_variations[ $variation_in ]['hide'] = get_post_meta( $variation['id'], 'wcf-hide-step', true );
766 }
767
768 $steps[ $in ]['ab-test-archived-variations'] = array_values( $ab_test_archived_variations );
769 }
770 }
771
772 $steps = apply_filters( 'cartflows_admin_flows_step_data', $steps );
773 }
774
775 return $steps;
776 }
777
778 /**
779 * Get step actions.
780 *
781 * @param int $flow_id Flow id.
782 * @param int $step_id Step id.
783 * @param string $type type.
784 *
785 * @return array
786 */
787 public static function get_step_actions( $flow_id, $step_id, $type = 'inline' ) {
788
789 if ( 'menu' === $type ) {
790 $actions = array(
791 'clone' => array(
792 'slug' => 'clone',
793 'class' => 'wcf-step-clone',
794 'icon_class' => 'dashicons dashicons-admin-page',
795 'text' => __( 'Duplicate', 'cartflows' ),
796 'pro' => true,
797 'link' => '#',
798 'ajaxcall' => 'cartflows_clone_step',
799 ),
800 'abtest' => array(
801 'slug' => 'abtest',
802 'class' => 'wcf-step-abtest',
803 'icon_class' => 'dashicons dashicons-forms',
804 'text' => __( 'A/B Test', 'cartflows' ),
805 'pro' => true,
806 'link' => '#',
807 ),
808 'delete' => array(
809 'slug' => 'delete',
810 'class' => 'wcf-step-delete',
811 'icon_class' => 'dashicons dashicons-trash',
812 'text' => __( 'Delete', 'cartflows' ),
813 'link' => '#',
814 'ajaxcall' => 'cartflows_delete_step',
815 ),
816 );
817
818 if ( current_user_can( 'cartflows_manage_settings' ) ) {
819 // Show Automation action only if suretriggers is connected.
820 $is_suretriggers_connected = _is_suretriggers_connected();
821 $automation_link = $is_suretriggers_connected ? '#' : admin_url( 'admin.php?page=' . CARTFLOWS_SLUG . '&path=automations' );
822
823 $actions = array_merge(
824 array(
825 'automation' => array(
826 'slug' => 'automation',
827 'class' => 'wcf-step-automation',
828 'icon_class' => 'dashicons dashicons-editor-code',
829 'text' => __( 'Automation', 'cartflows' ),
830 'pro' => false,
831 'link' => $automation_link,
832 'tag' => ! $is_suretriggers_connected ? __( '(Connect)', 'cartflows' ) : '',
833 'ajaxcall' => 'cartflows_automation_step',
834 ),
835 ),
836 $actions
837 );
838 }
839 } else {
840 $actions = array(
841 'view' => array(
842 'slug' => 'view',
843 'class' => 'wcf-step-view',
844 'icon_class' => 'dashicons dashicons-visibility',
845 'target' => 'blank',
846 'link' => get_permalink( $step_id ),
847 ),
848 'edit' => array(
849 'slug' => 'edit',
850 'class' => 'wcf-step-edit',
851 'icon_class' => 'dashicons dashicons-edit',
852 'link' => \Cartflows_Helper::get_page_builder_edit_link( $step_id ),
853 ),
854 );
855 }
856 return $actions;
857 }
858
859 /**
860 * Get step actions.
861 *
862 * @param int $flow_id Flow id.
863 * @param int $step_id Step id.
864 * @param string $type type.
865 *
866 * @return array
867 */
868 public static function get_ab_test_step_actions( $flow_id, $step_id, $type = 'inline' ) {
869
870 if ( 'menu' === $type ) {
871
872 $actions = array(
873 'clone' => array(
874 'slug' => 'clone',
875 'class' => 'wcf-ab-test-step-clone',
876 'icon_class' => 'dashicons dashicons-admin-page',
877 'text' => __( 'Duplicate', 'cartflows' ),
878 'link' => '#',
879 'pro' => true,
880 'ajaxcall' => 'cartflows_clone_ab_test_step',
881 ),
882 'delete' => array(
883 'slug' => 'delete',
884 'class' => 'wcf-ab-test-step-delete',
885 'icon_class' => 'dashicons dashicons-trash',
886 'text' => __( 'Delete', 'cartflows' ),
887 'link' => '#',
888 'ajaxcall' => 'cartflows_delete_ab_test_step',
889 ),
890 'archived' => array(
891 'slug' => 'archived',
892 'class' => 'wcf-ab-test-step-archived',
893 'icon_class' => 'dashicons dashicons-archive',
894 'text' => __( 'Archive', 'cartflows' ),
895 'link' => '#',
896 ),
897 'winner' => array(
898 'slug' => 'winner',
899 'class' => 'wcf-declare-winner',
900 'icon_class' => 'dashicons dashicons-yes-alt',
901 'text' => __( 'Declare as Winner', 'cartflows' ),
902 'link' => '#',
903 ),
904 );
905
906 } else {
907 $action_slug = apply_filters( 'cartflows_admin_action_slug', 'wcf-edit-step', $flow_id );
908 $actions = array(
909 'view' => array(
910 'slug' => 'view',
911 'class' => 'wcf-step-view',
912 'icon_class' => 'dashicons dashicons-visibility',
913 'target' => 'blank',
914 'link' => get_permalink( $step_id ),
915 ),
916 'edit' => array(
917 'slug' => 'edit',
918 'class' => 'wcf-step-edit',
919 'icon_class' => 'dashicons dashicons-edit',
920 'link' => \Cartflows_Helper::get_page_builder_edit_link( $step_id ),
921 ),
922 );
923 }
924
925 return $actions;
926 }
927
928 /**
929 * Get ab test step action.
930 *
931 * @param int $flow_id Flow id.
932 * @param int $step_id Step id.
933 * @param bool $deleted Step deleted or archived.
934 * @return array
935 */
936 public static function get_ab_test_step_archived_actions( $flow_id, $step_id, $deleted ) {
937
938 if ( $deleted ) {
939 $actions = array(
940 'archive-hide' => array(
941 'slug' => 'hide',
942 'class' => 'wcf-step-archive-hide',
943 'icon_class' => '',
944 'target' => 'blank',
945 'before_text' => __( 'Deleted variation can\'t be restored.', 'cartflows' ),
946 'text' => __( 'Hide', 'cartflows' ),
947 'link' => '#',
948 ),
949 'archive-delete' => array(
950 'slug' => 'deleteArch',
951 'class' => 'wcf-step-archive-delete',
952 'icon_class' => '',
953 'target' => 'blank',
954 'text' => __( 'Delete', 'cartflows' ),
955 'link' => '#',
956 ),
957 );
958 } else {
959
960 $actions = array(
961 'archive-restore' => array(
962 'slug' => 'restore',
963 'class' => 'wcf-step-archive-restore',
964 'icon_class' => '',
965 'target' => 'blank',
966 'text' => __( 'Restore', 'cartflows' ),
967 'link' => '#',
968 ),
969 'archive-delete' => array(
970 'slug' => 'delete',
971 'class' => 'wcf-step-archive-delete',
972 'icon_class' => '',
973 'target' => 'blank',
974 'text' => __( 'Delete', 'cartflows' ),
975 'link' => '#',
976 ),
977 );
978 }
979
980 return $actions;
981 }
982
983 /**
984 * Calculate earning.
985 *
986 * @param string $start_date start date.
987 * @param string $end_date end date.
988 * @param string $flow_id flow id.
989 * @param string $screen_type screen_type.
990 * @param string $comparison_range_type comparison range type - 'previous-period' | 'previous-year'.
991 *
992 * @return array
993 */
994 public static function get_earnings( $start_date, $end_date, $flow_id = '', $screen_type = 'funnels', $comparison_range_type = '' ) {
995
996 $currency_symbol = function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '';
997
998 $start_date = $start_date ? $start_date : gmdate( 'Y-m-d' );
999 $end_date = $end_date ? $end_date : gmdate( 'Y-m-d' );
1000
1001 $start_date = gmdate( 'Y-m-d H:i:s', strtotime( $start_date . '00:00:00' ) );
1002 $end_date = gmdate( 'Y-m-d H:i:s', strtotime( $end_date . '23:59:59' ) );
1003
1004
1005 if ( _is_cartflows_pro() && is_wcf_pro_plan() ) {
1006 // Return All Stats.
1007 return apply_filters(
1008 'cartflows_home_page_analytics',
1009 array(
1010 'order_currency' => $currency_symbol,
1011 'total_orders' => '0',
1012 'total_revenue' => '0',
1013 'total_bump_revenue' => '0',
1014 'total_offers_revenue' => '0',
1015 'total_visits' => '0',
1016 ),
1017 $start_date,
1018 $end_date,
1019 $flow_id,
1020 $screen_type,
1021 $comparison_range_type
1022 );
1023 }
1024
1025 $orders = self::get_orders_by_flow( $start_date, $end_date, $flow_id );
1026 $gross_sale = 0;
1027 $order_count = 0;
1028
1029 if ( ! empty( $orders ) ) {
1030
1031 foreach ( $orders as $order ) {
1032
1033 $order_id = $order->ID;
1034 $order = wc_get_order( $order_id );
1035 $order_total = $order ? $order->get_total() : 0;
1036
1037 $order_count++;
1038
1039 if ( $order && ! $order->has_status( 'cancelled' ) ) {
1040 $gross_sale += (float) $order_total;
1041 }
1042 }
1043 }
1044
1045 // Return All Stats.
1046 return array(
1047 'order_currency' => $currency_symbol,
1048 'total_orders' => $order_count,
1049 'total_revenue_raw' => $gross_sale,
1050 'total_revenue' => str_replace( '&nbsp;', '', wc_price( (float) $gross_sale ) ),
1051 'total_bump_revenue' => '0',
1052 'total_offers_revenue' => '0',
1053 'total_visits' => '0',
1054 );
1055 }
1056
1057
1058
1059 /**
1060 * Get orders data for flow.
1061 *
1062 * @since 1.6.15
1063 *
1064 * @param string $start_date start date.
1065 * @param string $end_date end date.
1066 * @param string $flow_id flow id.
1067 * @return wc_order object.
1068 */
1069 public static function get_orders_by_flow( $start_date, $end_date, $flow_id = '' ) {
1070
1071 global $wpdb;
1072
1073 if ( class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) && OrderUtil::custom_orders_table_usage_is_enabled() ) {
1074 // HPOS usage is enabled.
1075 $conditions = array(
1076 'tb1.type' => 'shop_order',
1077 );
1078
1079 $order_date_key = 'date_created_gmt';
1080 $order_status_key = 'status';
1081 $order_id_key = 'order_id';
1082 $order_table = $wpdb->prefix . 'wc_orders';
1083 $order_meta_table = $wpdb->prefix . 'wc_orders_meta';
1084
1085 } else {
1086 // Traditional CPT-based orders are in use.
1087
1088 $conditions = array(
1089 'tb1.post_type' => 'shop_order',
1090 );
1091
1092 $order_date_key = 'post_date';
1093 $order_status_key = 'post_status';
1094 $order_id_key = 'post_id';
1095 $order_table = $wpdb->prefix . 'posts';
1096 $order_meta_table = $wpdb->prefix . 'postmeta';
1097 }
1098
1099 $where = self::get_items_query_where( $conditions );
1100
1101 if ( ! empty( $flow_id ) ) {
1102 $where .= $wpdb->prepare( ' AND tb2.meta_value = %s', $flow_id );
1103 }
1104
1105 // Security: Use $wpdb->prepare() for date parameters to prevent SQL injection.
1106 $where .= $wpdb->prepare(
1107 ' AND ( tb1.' . $order_date_key . " BETWEEN IF (tb2.meta_key='wcf-analytics-reset-date'>%s, tb2.meta_key, %s) AND %s )", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1108 $start_date,
1109 $start_date,
1110 $end_date
1111 );
1112 $where .= " AND ( ( tb2.meta_key = '_wcf_flow_id' ) OR ( tb2.meta_key = '_cartflows_parent_flow_id' ) )";
1113 $where .= ' AND tb1.' . $order_status_key . " IN ( 'wc-completed', 'wc-processing' )";
1114
1115 $query = 'SELECT tb1.ID, DATE( tb1.' . $order_date_key . ' ) date, tb2.meta_value FROM ' . $order_table . ' tb1
1116 INNER JOIN ' . $order_meta_table . ' tb2
1117 ON tb1.ID = tb2.' . $order_id_key . ' '
1118 . $where;
1119
1120 return $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery
1121 }
1122
1123 /**
1124 * Prepare where items for query.
1125 *
1126 * @param array $conditions conditions to prepare WHERE query.
1127 * @return string
1128 */
1129 public static function get_items_query_where( $conditions ) {
1130
1131 global $wpdb;
1132
1133 $where_conditions = array();
1134
1135 foreach ( $conditions as $key => $condition ) {
1136
1137 if ( false !== stripos( $key, 'IN' ) ) {
1138 $where_conditions[] = $key . $wpdb->prepare( '( %s )', $condition );
1139 } else {
1140 $where_conditions[] = $key . $wpdb->prepare( '= %s', $condition );
1141 }
1142 }
1143
1144 if ( ! empty( $where_conditions ) ) {
1145 return 'WHERE 1 = 1 AND ' . implode( ' AND ', $where_conditions );
1146 } else {
1147 return '';
1148 }
1149 }
1150
1151 /**
1152 * Check is error in the received response.
1153 *
1154 * @param object $response Received API Response.
1155 * @return array $result Error result.
1156 * @since 1.11.8
1157 */
1158 public static function has_api_error( $response ) {
1159
1160 $result = array(
1161 'error' => false,
1162 'error_message' => __( 'Ooops! Something went wrong. Please open a support ticket from the website.', 'cartflows' ),
1163 'call_to_action' => __( 'No error found.', 'cartflows' ),
1164 'error_code' => 0,
1165 );
1166
1167 if ( is_wp_error( $response ) ) {
1168
1169 $msg = $response->get_error_message();
1170 $error_code = $response->get_error_code();
1171
1172 if ( 'http_request_failed' === $error_code ) {
1173 /* translators: %1$s: HTML, %2$s: HTML */
1174 $msg = $msg . '<br>' . sprintf( __( 'Request timeout error. Please check if the firewall or any security plugin is blocking the outgoing HTTP/HTTPS requests to templates.cartflows.com or not. %1$1sTo resolve this issue, please check this %2$2sarticle%3$3s.', 'cartflows' ), '<br><br>', '<a target="_blank" href="https://cartflows.com/docs/request-timeout-error-while-importing-the-flow-step-templates/?utm_source=dashboard&utm_medium=free-cartflows&utm_campaign=docs">', '</a>' );
1175 }
1176
1177 $result['error'] = true;
1178 $result['call_to_action'] = $msg;
1179 $result['error_code'] = $error_code;
1180
1181 } elseif ( ! empty( wp_remote_retrieve_response_code( $response ) ) && ! in_array( wp_remote_retrieve_response_code( $response ), array( 200, 201, 204 ), true ) ) {
1182
1183 $error_message = ! empty( wp_remote_retrieve_response_message( $response ) ) ? wp_remote_retrieve_response_message( $response ) : '';
1184 $error_body = ! empty( wp_remote_retrieve_body( $response ) ) ? wp_kses( wp_remote_retrieve_body( $response ), '<p>' ) : '';
1185
1186 if ( false !== strpos( $error_body, 'MalCare' ) ) {
1187 /* translators: %1$s: HTML, %2$s: HTML, %3$s: HTML */
1188 $error_message = $error_message . '<br>' . sprintf( __( 'Sorry for the inconvenience, but your website seems to be having trouble connecting to our server. %1$s Please open a technical %2$ssupport ticket%3$s and share the server\'s outgoing IP address.', 'cartflows' ), '<br><br>', '<a href="https://cartflows.com/support?utm_source=dashboard&utm_medium=free-cartflows&utm_campaign=support" target="_blank">', '</a>' );
1189 $ip_address = self::get_valid_ip_address();
1190 $result['message'] = ! empty( $ip_address ) ? __( 'Server\'s outgoing IP address: ', 'cartflows' ) . $ip_address : '';
1191 }
1192
1193 $result['error'] = true;
1194 $result['call_to_action'] = $error_message;
1195 $result['error_code'] = wp_remote_retrieve_response_code( $response );
1196 } else {
1197 $result['response_code'] = wp_remote_retrieve_response_code( $response );
1198 }
1199
1200 return $result;
1201 }
1202
1203 /**
1204 * Convert the array format in the WooCommerce's expected format for display.
1205 * Received array format is array( 0=>key, 1=>Key_value ) expected is array( key=>key_value )
1206 * This is because of the core rules structure which we have created.
1207 *
1208 * @param array $options Array of key & value in the array format.
1209 * @return array $options
1210 */
1211 public static function sanitize_array_values( $options ) {
1212
1213 if ( is_array( $options ) ) {
1214
1215 $sanitized_options = array();
1216
1217 foreach ( $options as $option_key => $option_value ) {
1218 $sanitized_options[ trim( $option_value ) ] = trim( $option_value );
1219 }
1220
1221 $options = $sanitized_options;
1222 }
1223
1224 return $options;
1225 }
1226
1227 /**
1228 * Get the IP address of the user's server.
1229 * To check weather the IP is not blocked by any firewall.
1230 *
1231 * Note: This IP address is not stored any where in the database.
1232 *
1233 * @return string A valid IP address.
1234 */
1235 public static function get_valid_ip_address() {
1236
1237 if ( isset( $_SERVER['HTTP_X_REAL_IP'] ) ) { //phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders
1238 return self::validate_ip_address( sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_REAL_IP'] ) ) );
1239 } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) { //phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders
1240 return self::validate_ip_address( sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) ); //phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders, WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__REMOTE_ADDR__
1241 } elseif ( isset( $_SERVER['SERVER_ADDR'] ) ) { //phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders
1242 return self::validate_ip_address( sanitize_text_field( wp_unslash( $_SERVER['SERVER_ADDR'] ) ) );
1243 } else {
1244 return '';
1245 }
1246 }
1247
1248 /**
1249 * Validate the provided IP address safe to printing.
1250 *
1251 * @param string $ip_address IP address to validate.
1252 * @return string $ip_address Validated IP address for display/use.
1253 */
1254 public static function validate_ip_address( $ip_address ) {
1255
1256 $ip_address = filter_var( $ip_address, FILTER_VALIDATE_IP, array() );
1257
1258 return $ip_address ? $ip_address : '';
1259 }
1260
1261 /**
1262 * Track funnel creation method for analytics.
1263 *
1264 * @param string $creation_method The method used to create the funnel (e.g., 'scratch', 'ready_made_template').
1265 * @return void
1266 */
1267 public static function track_funnel_creation_method( $creation_method ) {
1268 $funnel_creation_stats = get_option(
1269 'cartflows_funnel_creation_method',
1270 array(
1271 'scratch' => 0,
1272 'ready_made_template' => 0,
1273 )
1274 );
1275
1276 if ( isset( $funnel_creation_stats[ $creation_method ] ) ) {
1277 $funnel_creation_stats[ $creation_method ]++;
1278 }
1279
1280 update_option( 'cartflows_funnel_creation_method', $funnel_creation_stats );
1281 }
1282
1283 /**
1284 * Determine whether to show the CodeMirror code editor fields.
1285 *
1286 * Returns true when the custom script migration status is 'completed',
1287 * meaning users should see the new separate JS and CSS code editors
1288 * instead of the old combined textarea.
1289 *
1290 * @since 2.2.2
1291 * @return bool
1292 */
1293 public static function should_show_code_editor() {
1294 return 'completed' === \CartFlows_Helper::get_script_migration_status();
1295 }
1296
1297 /**
1298 * Get the field type for custom script fields based on migration status.
1299 *
1300 * Returns 'code' after migration is completed, 'textarea' otherwise.
1301 *
1302 * @since 2.2.2
1303 * @return string 'code' or 'textarea'
1304 */
1305 public static function get_custom_script_field_type() {
1306 return self::should_show_code_editor() ? 'code' : 'textarea';
1307 }
1308 }
1309