PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.12.13
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.12.13
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 / utils / class-wpfnl-functions.php

class-wpfnl-functions.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.12.13, at includes/utils/class-wpfnl-functions.php

6,563 lines 227.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This class is responsible for all the helper functions
4 *
5 * @package WPFunnels
6 */
7 namespace WPFunnels;
8
9 use ElementorPro\Modules\WpCli\Update;
10 use WPFunnels\Metas\Wpfnl_Step_Meta_keys;
11 use WPFunnels\Discount\WpfnlDiscount;
12
13 class Wpfnl_functions {
14
15 public static $installed_plugins;
16
17 /**
18 * Canvas layout used when nothing has been stored yet.
19 *
20 * Installs that predate this default are pinned to `horizontal` once, by
21 * Wpfnl_Activator::maybe_pin_legacy_builder_mode(), so flipping this value
22 * only affects sites that never ran on the old default.
23 *
24 * @since 3.12.13
25 */
26 const DEFAULT_FUNNEL_BUILDER_MODE = 'vertical';
27
28 /**
29 * User meta key holding a single admin's preferred canvas layout.
30 *
31 * Overrides the site-wide default for that user only, so switching views on
32 * the canvas survives a reload without changing anything for their teammates.
33 *
34 * @since 3.12.13
35 */
36 const CANVAS_VIEW_MODE_META_KEY = '_wpfnl_canvas_view_mode';
37
38 /**
39 * Post meta keys that must not travel between sites on funnel export/import.
40 *
41 * These hold per-site render caches or bookkeeping that is only meaningful on the site
42 * that produced them. Carrying them into a new site makes the imported step inherit the
43 * source site's cached state:
44 *
45 * - `_elementor_css` records that a `post-{ID}.css` file was already generated. Elementor
46 * regenerates only when the recorded status is empty (`Base::enqueue()`), and `Post_CSS`
47 * does not override `is_update_required()`, so a stale record makes it enqueue a CSS file
48 * that was never written here — the step renders with no per-post styles at all.
49 * - `_elementor_element_cache` holds pre-rendered element HTML from the source site.
50 * - `_elementor_page_assets` holds the cached widget style handles to enqueue.
51 * - `_edit_lock` / `_edit_last` / `_wp_old_slug` / `_eael_post_view_count` reference users,
52 * locks, and slugs that do not exist on the destination.
53 *
54 * Elementor's own `files_manager->clear_cache()` clears the first three, but only when the
55 * builder post-import pass runs. Excluding them on both sides keeps an import correct even
56 * when Elementor is absent or `allow_url_fopen` is disabled.
57 *
58 * @since 2.5.15
59 */
60 const VOLATILE_TRANSFER_META_KEYS = [
61 '_elementor_css',
62 '_elementor_element_cache',
63 '_elementor_page_assets',
64 '_edit_lock',
65 '_edit_last',
66 '_wp_old_slug',
67 '_eael_post_view_count',
68 ];
69
70 /**
71 * Get all the steps of the funnel
72 *
73 * @param $funnel_id
74 *
75 * @return array|mixed
76 */
77 public static function get_steps($funnel_id) {
78 if (!is_int($funnel_id) && !is_string($funnel_id)) {
79 // Throw an error or handle this case as appropriate for your application
80 throw new \InvalidArgumentException('Funnel ID must be an integer or a string');
81 }
82
83 $steps = get_post_meta($funnel_id, '_steps_order', true);
84 if (!is_array($steps)) {
85 $steps = array();
86 }
87 return $steps;
88 }
89
90
91 /**
92 * Check if the associated order is from a funnel.
93 *
94 * This function checks if the given order is associated with a funnel.
95 * It returns true if the order is from a funnel, and false otherwise.
96 * Previously 'get_funnel_id_from_order' function parameter was order id but from 2.8.0 version it changed to order object
97 *
98 * @param \WC_Order $order The WooCommerce order object.
99 * @return bool True if the order is from a funnel, false otherwise.
100 * @since 2.8.0
101 */
102 public static function check_if_funnel_order( $order ) {
103 if ( false === is_a( $order, 'WC_Order' ) ) {
104 return false;
105 }
106
107 $funnel_id = self::get_funnel_id_from_order( $order );
108 if ( ! $funnel_id ) {
109 return false;
110 }
111 return true;
112 }
113
114
115 /**
116 * Get the associated funnel ID from the order ID.
117 *
118 * This function retrieves the ID of the associated funnel for a given order.
119 * Previously data was retrieved by post meta table but from 2.8.0 version user get_meta function to retrieve meta
120 *
121 * @param \WC_Order|Int $order The WooCommerce order object or Order ID of WooCommerce order.
122 * @return int|false The ID of the associated funnel, or false if not found.
123 * @since 2.8.0
124 */
125 public static function get_funnel_id_from_order( $order ) {
126 if ( ! $order ) {
127 return false;
128 }
129
130 if ( false === is_a( $order, 'WC_Order' ) ) {
131 $order_id = intval( $order );
132 $order = wc_get_order( $order_id );
133 if ( false === is_a( $order, 'WC_Order' ) ) {
134 return false;
135 }
136 }
137
138 $funnel_id = $order->get_meta( '_wpfunnels_funnel_id' ) ? $order->get_meta( '_wpfunnels_funnel_id' ) : $order->get_meta( '_wpfunnels_parent_funnel_id' );
139 if ( ! $funnel_id ) {
140 return false;
141 }
142 return intval( $funnel_id );
143 }
144
145 /**
146 * Check if the given string is a date or not
147 *
148 * @param $date
149 *
150 * @return bool
151 * @since 1.0.0
152 */
153 public static function validate_date( $date ) {
154 return (bool) strtotime( $date );
155 }
156
157 /**
158 * Define constant if it is not set yet
159 *
160 * @param $name
161 * @param $value
162 *
163 * @since 2.0.3
164 */
165 public static function maybe_define_constant( $name, $value ) {
166 if ( ! defined( $name ) ) {
167 define( $name, $value );
168 }
169 }
170
171 /**
172 * Set do not cache constants
173 */
174 public static function do_not_cache() {
175 global $post;
176
177 if ( ! apply_filters( 'wpfunnels/do_not_cache', true, $post->ID ) ) {
178 return;
179 }
180 self::maybe_define_constant( 'DONOTCACHEPAGE', true );
181 self::maybe_define_constant( 'DONOTCACHEOBJECT', true );
182 self::maybe_define_constant( 'DONOTCACHEDB', true );
183 nocache_headers();
184 }
185
186 /**
187 * Return formatted date from the
188 * given date object
189 *
190 * @param $date
191 *
192 * @return false|string
193 * @since 1.0.0
194 */
195 public static function get_formatted_date( $date ) {
196 return date( 'Y-m-d h:i A', strtotime( $date ) );
197 }
198
199
200 /**
201 * Get checkout ID
202 *
203 * @return bool|int
204 */
205 public static function get_checkout_id_from_post_data() {
206 $checkout_id = ! empty( $_POST['_wpfunnels_checkout_id'] ) ? filter_var( wp_unslash( $_POST['_wpfunnels_checkout_id'] ), FILTER_SANITIZE_NUMBER_INT ) : false;
207 return $checkout_id ? intval( $checkout_id ) : false;
208 }
209
210
211 /**
212 * Get checkout id from post
213 *
214 * @return bool|int
215 */
216 public static function get_checkout_id_from_post( $post = null ) {
217 if ( is_array( $post ) && ! empty( $post['_wpfunnels_checkout_id'] ) ) {
218 $checkout_id = filter_var( wp_unslash( $post['_wpfunnels_checkout_id'] ), FILTER_SANITIZE_NUMBER_INT );
219 return $checkout_id ? intval( $checkout_id ) : false;
220 }
221
222 if ( is_array( $post ) && isset( $post['post_data'] ) ) {
223 $post_data = array();
224 wp_parse_str( wp_unslash( $post['post_data'] ), $post_data );
225 if ( ! empty( $post_data['_wpfunnels_checkout_id'] ) ) {
226 $checkout_id = filter_var( $post_data['_wpfunnels_checkout_id'], FILTER_SANITIZE_NUMBER_INT );
227 return $checkout_id ? intval( $checkout_id ) : false;
228 }
229 }
230 return false;
231 }
232
233
234 /**
235 * Check order bump clicked or not from post data
236 *
237 * @return bool True|False
238 * @since 2.8.0
239 */
240 public static function is_orderbump_clicked_from_post_data() {
241 if ( ! empty( $_POST['post_data'] ) ) {
242 wp_parse_str( $_POST['post_data'], $post_data );
243 return ! empty( $post_data['_wpfunnels_order_bump_clicked'] ) && 'yes' === $post_data['_wpfunnels_order_bump_clicked'];
244 }
245 return ! empty( $_POST['_wpfunnels_order_bump_clicked'] ) && 'yes' === $_POST['_wpfunnels_order_bump_clicked'];
246 }
247
248
249 /**
250 * Check checkoutify order bump clicked or not from post data
251 *
252 * @return bool True|False
253 * @since 2.8.0
254 */
255 public static function is_checkoutify_orderbump_clicked_from_post_data() {
256 if ( ! empty( $_POST['post_data'] ) ) {
257 wp_parse_str( $_POST['post_data'], $post_data );
258 return ! empty( $post_data['checkoutify_orderbump_clicked'] ) && 'yes' === $post_data['checkoutify_orderbump_clicked'];
259 }
260 return ! empty( $_POST['checkoutify_orderbump_clicked'] ) && 'yes' === $_POST['checkoutify_orderbump_clicked'];
261 }
262
263
264 /**
265 * Check qunatity select or not from post data
266 *
267 * @return bool True|False
268 * @since 2.8.0
269 */
270 public static function maybe_select_quantity_from_post_data() {
271 if ( ! empty( $_POST['post_data'] ) ) {
272 $post_data = array();
273 wp_parse_str( $_POST['post_data'], $post_data );
274 return ! empty( $post_data['_wpfunnels_select_quantity'] ) && 'yes' === $post_data['_wpfunnels_select_quantity'];
275 }
276 return ! empty( $_POST['_wpfunnels_select_quantity'] ) && 'yes' === $_POST['_wpfunnels_select_quantity'];
277 }
278
279
280
281 /**
282 * Check variation is selected or not from post data
283 *
284 * @return bool True|False
285 * @since 2.8.0
286 */
287 public static function is_variation_selected_from_post_data() {
288 if ( ! empty( $_POST['post_data'] ) ) {
289 wp_parse_str( $_POST['post_data'], $post_data );
290 return ! empty( $post_data['_wpfunnels_variable_product'] ) && 'selected' === $post_data['_wpfunnels_variable_product'];
291 }
292 return ! empty( $_POST['_wpfunnels_variable_product'] ) && 'selected' === $_POST['_wpfunnels_variable_product'];
293 }
294
295
296 /**
297 * Check product option is selected or not from post data
298 *
299 * @return bool True|False
300 * @since 2.8.0
301 */
302 public static function is_product_option_from_post_data() {
303 if ( ! empty( $_POST['post_data'] ) ) {
304 wp_parse_str( $_POST['post_data'], $post_data );
305 return ! empty( $post_data['_wpfunnels_product_option'] ) && 'selected' === $post_data['_wpfunnels_product_option'];
306 }
307 return ! empty( $_POST['_wpfunnels_product_option'] ) && 'selected' === $_POST['_wpfunnels_product_option'];
308 }
309
310
311 /**
312 * Check variation is selected or not from post data
313 *
314 * @return bool True|False
315 * @since 2.8.0
316 */
317 public static function is_coupon_applied_from_post_data() {
318 if ( ! empty( $_POST['post_data'] ) ) {
319 wp_parse_str( $_POST['post_data'], $post_data );
320 return ! empty( $post_data['_wpfunnels_auto_coupon'] ) && 'yes' === $post_data['_wpfunnels_auto_coupon'];
321 }
322 return ! empty( $_POST['_wpfunnels_auto_coupon'] ) && 'yes' === $_POST['_wpfunnels_auto_coupon'];
323 }
324
325
326 /**
327 * Get funnel id
328 *
329 * @param $step_id
330 *
331 * @return mixed
332 */
333 public static function get_funnel_id_from_step( $step_id ) {
334 $funnel_id = get_post_meta( $step_id, '_funnel_id', true );
335 return intval( $funnel_id );
336 }
337
338 /**
339 * Get order id from funnel post data
340 *
341 * @return false|int
342 * @since 2.8.2
343 */
344 public static function get_order_id_from_post_data() {
345 if ( ! empty( $_POST['_wpfunnels_order_unique_identifier'] ) ) {
346 global $wpdb;
347 $identifier = wp_unslash( $_POST['_wpfunnels_order_unique_identifier'] );
348 $prepare_guery = $wpdb->prepare( "SELECT `post_id` FROM {$wpdb->postmeta} WHERE meta_key ='_wpfunnels_order_unique_identifier' and meta_value like '%s'", $identifier );
349 $get_value = $wpdb->get_row( $prepare_guery );
350 return $get_value ? intval( $get_value->post_id ) : false;
351 }
352 return false;
353 }
354
355
356 /**
357 * Get checkout id from order
358 *
359 * @param $order_id
360 *
361 * @return int
362 */
363 public static function get_checkout_id_from_order( $order_id ) {
364 if ( !self::is_wc_active() ) {
365 return false;
366 }
367
368 $order = wc_get_order( $order_id );
369
370 if ( false === is_a( $order, 'WC_Order' ) ) {
371 return false;
372 }
373
374 $checkout_id = $order->get_meta( '_wpfunnels_checkout_id' );
375 return $checkout_id ? intval( $checkout_id ) : false;
376 }
377
378
379 /**
380 * Get funnel from post data
381 *
382 * @return bool|int
383 */
384 public static function get_funnel_id_from_post_data() {
385 if ( ! empty( $_POST['_wpfunnels_funnel_id'] ) ) {
386 $funnel_id = filter_var( wp_unslash( $_POST['_wpfunnels_funnel_id'] ), FILTER_SANITIZE_NUMBER_INT );
387 return $funnel_id ? intval( $funnel_id ) : false;
388 }
389 return false;
390 }
391
392
393 /**
394 * Get funnel id from step page
395 *
396 * @return false|mixed
397 */
398 public static function get_funnel_id() {
399 global $post;
400 if ( $post ) {
401 $funnel_id = get_post_meta( $post->ID, '_funnel_id', true );
402 return $funnel_id ? intval( $funnel_id ) : false;
403 }
404 return false;
405 }
406
407 /**
408 * Unserialize data
409 *
410 * @param $data
411 *
412 * @return mixed
413 * @since 1.0.0
414 */
415 public static function unserialize_array_data( $data ) {
416 return maybe_unserialize( $data );
417 }
418
419
420 /**
421 * Get formatted string with phrase
422 * e.g: 1 item if singular or
423 * 2 items if plural
424 *
425 * @param Int $number
426 * @param String $singular
427 * @param String $plural
428 *
429 * @return String
430 * @since 1.0.0
431 */
432 public static function get_formatted_data_with_phrase( $number, $singular = '', $plural = 's' ) {
433 return ( is_numeric( $number ) && 0 <= $number ) ? ( 1 < $number ? $plural : $singular ) : null;
434 }
435
436
437 /**
438 * Get formatted string from funnel status
439 *
440 * @param $status
441 *
442 * @return string
443 * @since 1.0.0
444 */
445 public static function get_formatted_status( $status ) {
446 switch ( $status ) {
447 case 'publish':
448 return 'Published';
449 case 'draft':
450 return 'Draft';
451 default:
452 return $status;
453 }
454 }
455
456
457 /**
458 * Generate active class for funnel menus
459 *
460 * @param $key
461 *
462 * @return bool
463 * @since 1.0.0
464 */
465 public static function define_active_class($key)
466 {
467 $page = filter_input(INPUT_GET, 'page', FILTER_SANITIZE_SPECIAL_CHARS);
468 if ($page === WPFNL_FUNNEL_PAGE_SLUG && $key === 'overview') {
469 return true;
470 }
471
472 if ( $page === 'create_funnel' && $key === 'create_funnel' ) {
473 return true;
474 }
475
476 if ( $page === 'wpfnl_automations' && $key === 'automations' ) {
477 return true;
478 }
479
480 if ( $page === 'wpfnl_settings' && $key === 'settings' ) {
481 return true;
482 }
483
484 if ( $page === WPFNL_ADDONS_SLUG && $key === 'addons' ) {
485 return true;
486 }
487
488 if ( $page === 'wpfunnels_integrations' && $key === 'integrations' ) {
489 return true;
490 }
491
492 if ( $page === WPFNL_TEMPLATE_PAGE_SLUG && $key === 'templates' ) {
493 return true;
494 }
495
496 if ( $page === WPFNL_STORE_CHECKOUT_SLUG && $key === 'store_checkout' ) {
497 return true;
498 }
499
500 return false;
501 }
502
503
504 /**
505 * Check If module exists or not
506 *
507 * @param $id
508 *
509 * @return bool
510 * @since 1.0.0
511 */
512 public static function check_if_module_exists( $id ) {
513 return 'publish' === get_post_status( $id ) || 'draft' === get_post_status( $id ) || 'trash' === get_post_status( $id );
514 }
515
516
517 /**
518 * Check if the cpt is step or not
519 *
520 * @param $step_id
521 * @param string $type
522 *
523 * @return bool
524 */
525 public static function check_if_this_is_step_type_by_id( $step_id, $type = 'landing' ) {
526 $post_type = get_post_type( $step_id );
527 if ( WPFNL_STEPS_POST_TYPE === $post_type ) {
528 if ( $type === get_post_meta( $step_id, '_step_type', true ) ) {
529 return true;
530 }
531 }
532 return false;
533 }
534
535
536 /**
537 * Get thankyou page id
538 *
539 * @param $funnel_id
540 *
541 * @return bool|mixed
542 *
543 * @since 2.2.6
544 */
545 public static function get_thankyou_page_id( $funnel_id ) {
546 $steps = self::get_steps( $funnel_id );
547 if ( is_array( $steps ) ) {
548 foreach ( $steps as $step ) {
549 if ( 'thankyou' === $step['step_type'] ) {
550 return $step['id'];
551 }
552 }
553 }
554 return false;
555 }
556
557
558 /**
559 * Check if the current post type is step or not
560 *
561 * @return bool
562 * @since 2.0.3
563 */
564 public static function is_funnel_step_page( $post_type = '' ) {
565 if ( self::get_current_post_type( $post_type ) === WPFNL_STEPS_POST_TYPE ) {
566 return true;
567 }
568
569 $post_id = null;
570 if ( wp_doing_ajax() || isset( $_GET['wc-ajax'] ) ) {
571 $post_id = self::get_checkout_id_from_post( $_POST );
572 }
573 global $post;
574
575 if ( ! $post_id ) {
576 $post_id = get_the_ID();
577 }
578 $post = $post_id ? get_post( $post_id ) : $post;
579
580 $post_type = $post->post_type ?? '';
581 if ( self::get_current_post_type( $post_type ) === WPFNL_STEPS_POST_TYPE ) {
582 return true;
583 }
584
585 return false;
586 }
587
588
589 /**
590 * Get current post type
591 *
592 * @param $post_type
593 *
594 * @return string
595 *
596 * @since 2.3.5
597 */
598 public static function get_current_post_type( $post_type ) {
599 global $post;
600 if ( '' === $post_type && is_object( $post ) ) {
601 $post_type = $post->post_type;
602 } else {
603 $post_id = '';
604 if ( wp_doing_ajax() || isset( $_GET['wc-ajax'] ) ) {
605 $post_id = self::get_checkout_id_from_post( $_POST );
606 }
607
608 if ( $post_id ) {
609 $post = get_post( $post_id );
610 $post_type = $post->post_type;
611 }
612 }
613 return $post_type;
614 }
615
616
617 /**
618 * Check if funnel checkout page
619 *
620 * @param $funnel_id
621 *
622 * @return bool
623 */
624 public static function is_funnel_checkout_page() {
625 if ( isset( $_POST['post_data'] ) ) {
626 parse_str( $_POST['post_data'], $post_data );
627 if ( isset( $post_data['_wpfunnels_checkout_id'] ) ) {
628 return array(
629 'status' => true,
630 'id' => $post_data['_wpfunnels_checkout_id'],
631 );
632 }
633 }
634 return array(
635 'status' => false,
636 'id' => '',
637 );
638 }
639
640
641 /**
642 * Check if funnel exists
643 *
644 * @param $funnel_id
645 *
646 * @return bool
647 */
648 public static function is_funnel_exists( $funnel_id ) {
649 if (!is_int($funnel_id) && !is_string($funnel_id)) {
650 // Throw an error or handle this case as appropriate for your application
651 throw new \InvalidArgumentException('Funnel ID must be an integer or a string');
652 }
653
654 if ( ! $funnel_id ) {
655 return false;
656 }
657
658 if ( false === get_post_status( $funnel_id ) ) {
659 return true;
660 }
661 return false;
662 }
663
664
665 /**
666 * Function to check if the current page is a post edit page
667 *
668 * @return bool
669 *
670 * @since 2.0.3
671 */
672 public static function is_step_edit_page() {
673
674 $step_id = -1;
675 if ( is_admin() && isset( $_REQUEST['action'] ) ) {
676 if ( 'edit' === $_REQUEST['action'] && isset( $_GET['post'] ) ) {
677 $step_id = isset( $_GET['post'] ) ? $_GET['post'] : - 1;
678 } elseif ( isset( $_REQUEST['wpfunnels_gb'] ) && isset( $_POST['post_id'] ) ){ //phpcs:ignore
679 $step_id = intval( $_POST['post_id'] ); //phpcs:ignore
680 }
681 if ( $step_id === - 1 ) {
682
683 return false;
684 }
685 $get_post_type = get_post_type( $step_id );
686 if ( WPFNL_STEPS_POST_TYPE === $get_post_type ) {
687 return true;
688 }
689 }
690 return false;
691 }
692
693
694 /**
695 * Check if the cpt is step or not
696 *
697 * @param string $type
698 *
699 * @return bool
700 * @since 1.0.0
701 */
702 public static function check_if_this_is_step_type( $type = 'landing' ) {
703 $post_type = get_post_type();
704
705 if ( WPFNL_STEPS_POST_TYPE === $post_type ) {
706 global $post;
707 if ( $type === get_post_meta( $post->ID, '_step_type', true ) ) {
708 return true;
709 }
710 }
711 return false;
712 }
713
714
715 /**
716 * Check if the cpt is step or not
717 *
718 * @param string $type
719 *
720 * @return bool
721 * @since 1.0.0
722 */
723 public static function check_if_this_is_lms_step() {
724 global $post;
725 if( ! $post ) {
726 return false;
727 }
728 $funnel_id = get_post_meta( $post->ID, '_funnel_id', true );
729
730 if (! $funnel_id ) {
731 return false;
732 }
733
734 $funnel_type = get_post_meta( $funnel_id, '_wpfnl_funnel_type', true );
735
736 return $funnel_type === 'lms';
737 }
738
739
740 /**
741 * Hooks for start and end the journey
742 * of a funnel
743 */
744 public static function start_journey() {
745 $post_type = get_post_type();
746 if ( WPFNL_STEPS_POST_TYPE === $post_type ) {
747 global $post;
748 $step_id = $post->ID;
749 $funnel_id = self::get_funnel_id_from_step( $step_id );
750 $steps = self::get_steps( $funnel_id );
751 $funnel_data = self::get_funnel_data( $funnel_id );
752 // start the journey
753 if ( $steps && is_array( $steps ) ) {
754 if ( $steps[0]['id'] === $step_id ) {
755 do_action( 'wpfunnels/funnel_journey_starts', $step_id, $funnel_id );
756 }
757 }
758
759 // end the journey
760 if ( $funnel_data && isset( $funnel_data['drawflow']['Home']['data'] ) ) {
761 $funnel_data = $funnel_data['drawflow']['Home']['data'];
762 foreach ( $funnel_data as $data ) {
763 $info = $data['data'];
764 $step_type = $info['step_type'];
765
766 if ( 'conditional' !== $step_type ) {
767 $_step_id = isset( $info['step_id'] ) ? $info['step_id'] : '';
768 $output_con = $data['outputs'];
769
770 if ( empty( $output_con ) ) {
771 if ( $_step_id === $step_id ) {
772 do_action( 'wpfunnels/funnel_journey_end', $step_id, $funnel_id );
773 delete_option( 'optin_data' );
774 break;
775 }
776 }
777 }
778 }
779 }
780 }
781 }
782
783
784 /**
785 * Conditional node logic check
786 *
787 * @param $funnel_id
788 * @param $order
789 * @param $condition_identifier
790 * @param $current_page_id
791 * @param $checker
792 *
793 * @return bool
794 *
795 * @since 2.0.2
796 */
797 public static function check_condition( $funnel_id, $order, $condition_identifier, $current_page_id, $checker = 'accept' ) {
798 $group_conditions = get_post_meta( $funnel_id, $condition_identifier, true );
799
800 if ( $group_conditions ) {
801 // Loop through group condition.
802 foreach ( $group_conditions as $group ) {
803 if ( empty( $group ) ) {
804 continue;
805 }
806
807 $match_group = true;
808 // Loop over rules and determine if all rules match.
809 foreach ( $group as $rule ) {
810 if ( ! self::match_rule( $funnel_id, $rule, $order, $current_page_id, $checker ) ) {
811 $match_group = false;
812 break;
813 }
814 }
815
816 // If this group matches, show the field group.
817 if ( $match_group ) {
818 return true;
819 } else {
820 return false;
821 }
822 }
823 }
824 if ( $checker == 'accept' ) {
825 return true;
826 }
827 // Return default.
828 return false;
829 }
830
831
832 /**
833 * Check if rule is matched
834 *
835 * @param $rule
836 * @param $order
837 * @param $current_page_id
838 * @param $checker
839 *
840 * @return mixed
841 *
842 * @since 2.0.2
843 */
844 public static function match_rule( $funnel_id, $rule, $order, $current_page_id, $checker ) {
845 if ( isset( $rule['field'] ) && $rule['field'] == 'downsell' ) {
846 $rule['field'] = 'upsell';
847 }
848 $condition = $rule['field'];
849 if ( strpos( $rule['field'], 'optin' ) ) {
850 $condition = 'optin';
851 }
852 $checker_function = $condition . '_condition_checker';
853 return self::$checker_function( $funnel_id, $rule, $order, $current_page_id, $checker );
854 }
855
856
857 /**
858 * Upsell condition checker for conditional step
859 *
860 * @param Integer $funnel_id
861 * @param Array $data
862 * @param Object $order
863 * @param Integer $current_page_id
864 * @param String $checker
865 *
866 * @return Boolean
867 *
868 * @since 2.5.7
869 */
870 public static function upsell_condition_checker( $funnel_id, $data, $order, $current_page_id, $checker ) {
871
872 if ( $data['value'] == 'yes' ) {
873 if ( $checker == 'accept' ) {
874 return true;
875 } else {
876 return false;
877 }
878 } elseif ( $data['value'] == 'no' ) {
879
880 if ( $checker == 'reject' ) {
881 return true;
882 } else {
883 return false;
884 }
885 }
886 return false;
887 }
888
889
890 /**
891 * Optin condition checker for conditional step
892 *
893 * @param Integer $funnel_id
894 * @param Array $data
895 * @param Object $order
896 * @param Integer $current_page_id
897 * @param String $checker
898 *
899 * @return Boolean
900 *
901 * @since 2.5.7
902 */
903 public function optin_condition_checker( $funnel_id, $rule, $data, $current_page_id, $checker = 'accept' ) {
904
905 if ( isset( $data['step_id'] ) ) {
906 $step_id = str_replace( 'optin_', '', $data['step_id'] );
907 $optin_steps = get_option( 'optin_data' );
908 if ( isset( $optin_steps['optin_data'] ) ) {
909 $key = array_search( $step_id, array_column( $optin_steps['optin_data'], 'step_id' ) );
910 if ( false !== $key ) {
911 unset( $optin_steps[ $key ] );
912 update_option( 'optin_data', $optin_steps );
913 return true;
914 }
915 }
916 } elseif ( ! empty( $data['cta'] ) ) {
917 return false;
918 }
919
920 if ( isset( $rule['field'] ) ) {
921 $step_id = str_replace( 'optin_', '', $rule['field'] );
922 $optin_steps = get_option( 'optin_data' );
923 if ( isset( $optin_steps['optin_data'] ) ) {
924 $key = array_search( $step_id, array_column( $optin_steps['optin_data'], 'step_id' ) );
925 if ( false !== $key ) {
926 unset( $optin_steps[ $key ] );
927 update_option( 'optin_data', $optin_steps );
928 return true;
929 }
930 }
931 }
932
933 return false;
934 }
935
936
937 /**
938 * Order bump condition checker
939 *
940 * @param $data
941 * @param $order
942 * @param $current_page_id
943 *
944 * @return bool
945 */
946 public static function orderbump_condition_checker( $funnel_id, $data, $order, $current_page_id, $checker = 'accept' ) {
947 $order_bump_accepted = '';
948 if ( self::is_wc_active() ) {
949 $order_bump_accepted = WC()->session->get( 'order_bump_accepted' );
950 }
951 return $data['value'] == $order_bump_accepted;
952 }
953
954
955 /**
956 * Check cart total condition in conditional node
957 *
958 * @param $data
959 * @param $order
960 * @param $current_page_id
961 *
962 * @return bool
963 */
964 public static function carttotal_condition_checker( $funnel_id, $data, $order, $current_page_id, $checker = 'accept' ) {
965 $cart_total = $order->get_total();
966
967 $checker = false;
968 if ( $data['condition'] == 'greater' ) {
969 if ( $cart_total > $data['value'] ) {
970 $checker = true;
971 }
972 } elseif ( $data['condition'] == 'equal' ) {
973 if ( $cart_total == $data['value'] ) {
974 $checker = true;
975 }
976 } elseif ( $data['condition'] == 'less' ) {
977 if ( $cart_total < $data['value'] ) {
978 $checker = true;
979 }
980 }
981 return $checker;
982 }
983
984
985 /**
986 * Billing country condition checker
987 *
988 * @param Array
989 * @param Object
990 * @param String
991 * @param String
992 *
993 * @return Bool
994 * @since 2.4.18
995 */
996 public static function billing_condition_checker( $funnel_id, $data, $order, $current_page_id, $checker ) {
997 if ( $order ) {
998 $billing_country = $order->get_billing_country();
999 if ( isset( $data['field'] ) && 'billing' === $data['field'] && isset( $data['value'] ) && $billing_country === $data['value'] ) {
1000 return true;
1001 }
1002 }
1003 return false;
1004 }
1005
1006
1007 /**
1008 * Shipping country condition checker
1009 *
1010 * @param Array
1011 * @param Object
1012 * @param String
1013 * @param String
1014 *
1015 * @return Bool
1016 * @since 2.4.18
1017 */
1018 public static function shipping_condition_checker( $funnel_id, $data, $order, $current_page_id, $checker ) {
1019 if ( $order ) {
1020 $shipping_country = $order->get_shipping_country();
1021 if ( isset( $data['field'] ) && 'shipping' === $data['field'] && isset( $data['value'] ) && $shipping_country === isset( $data['value'] ) ) {
1022 return true;
1023 }
1024 }
1025 return false;
1026 }
1027
1028
1029 /**
1030 * Get next step of the
1031 * funnel
1032 *
1033 * @param $funnel_id
1034 * @param $step_id
1035 * @param bool $condition
1036 *
1037 * @return Mix
1038 *
1039 * @since 1.0.0
1040 */
1041 public static function get_next_step( $funnel_id, $step_id, $condition = true ) {
1042 if ( ! $funnel_id || ! $step_id ) {
1043 return false;
1044 }
1045 $parent_step_id = get_post_meta( $step_id, '_parent_step_id', true );
1046 if ( $parent_step_id ) {
1047 $step_id = $parent_step_id;
1048 }
1049
1050 $funnel_data = self::get_funnel_data( $funnel_id );
1051
1052 if ( ! $funnel_data || ! isset( $funnel_data['drawflow']['Home']['data'] ) ) {
1053 return false;
1054 }
1055
1056 $node_id = self::get_node_id( $funnel_id, $step_id );
1057 $node_data = $funnel_data['drawflow']['Home']['data'];
1058
1059 if ( ! is_array( $node_data ) || empty( $node_data ) ) {
1060 return false;
1061 }
1062
1063 $next_node_id = '';
1064
1065 foreach ( $node_data as $node_key => $node_value ) {
1066 if ( $node_value['id'] != $node_id ) {
1067 continue;
1068 }
1069
1070 if ( $condition ) {
1071 $next_node_id = isset( $node_value['outputs']['output_1']['connections'][0]['node'] ) ? $node_value['outputs']['output_1']['connections'][0]['node'] : '';
1072 } else {
1073 $next_node_id = isset( $node_value['outputs']['output_2']['connections'][0]['node'] ) ? $node_value['outputs']['output_2']['connections'][0]['node'] : '';
1074 }
1075
1076 if ( ! $next_node_id ) {
1077 self::redirect_to_deafult_thankyou();
1078 }
1079
1080 $next_step_id = self::get_step_by_node( $funnel_id, $next_node_id );
1081 $next_step_type = self::get_node_type( $node_data, $next_node_id );
1082
1083 return apply_filters(
1084 'wpfunnels/next_step_data',
1085 array(
1086 'step_id' => $next_step_id,
1087 'step_type' => $next_step_type,
1088 )
1089 );
1090
1091 }
1092
1093 return false;
1094 }
1095
1096
1097 /**
1098 * Redirect deafult thank you page
1099 */
1100 public static function redirect_to_deafult_thankyou() {
1101
1102 if ( isset( $_POST['order_id'] ) && $_POST['order_id'] ) {
1103 $url = home_url() . '/checkout/order-received/' . $_POST['order_id'] . '/?key=' . $_POST['order_key'];
1104 return $url;
1105 } else {
1106 add_action(
1107 'template_redirect',
1108 function( $order_id ) {
1109 if ( isset( $_GET['order-received'] ) && $_GET['order-received'] ) {
1110 $url = '';
1111 $url = add_query_arg( 'id', $_GET['order-received'], $url );
1112 wp_safe_redirect( wp_sanitize_redirect( esc_url_raw( $url ) ) );
1113 exit;
1114 } elseif ( isset( $_POST['order_id'] ) && $_POST['order_id'] ) {
1115 $url = '';
1116 $url = add_query_arg( 'id', $_POST['order_id'], $url );
1117 wp_safe_redirect( wp_sanitize_redirect( esc_url_raw( $url ) ) );
1118 exit;
1119 }
1120 }
1121 );
1122 }
1123 }
1124
1125
1126 /**
1127 * Get previous step of the
1128 * funnel
1129 *
1130 * @param $funnel_id
1131 * @param $step_id
1132 * @param bool $condition
1133 *
1134 * @return Mix
1135 *
1136 * @since 1.0.0
1137 */
1138 public static function get_prev_step( $funnel_id, $step_id, $condition = true ) {
1139 if ( $funnel_id && ! $step_id ) {
1140 return false;
1141 }
1142 $funnel_data = self::get_funnel_data( $funnel_id );
1143 if ( $funnel_data ) {
1144 $node_id = self::get_node_id( $funnel_id, $step_id );
1145 $node_data = $funnel_data['drawflow']['Home']['data'];
1146
1147 foreach ( $node_data as $node_key => $node_value ) {
1148 if ( $node_value['id'] == $node_id ) {
1149 if ( $condition ) {
1150 if ( ! empty( $node_value['inputs'] ) ) {
1151 $prev_node_id = $node_value['inputs']['input_1']['connections'][0]['node'];
1152 } else {
1153 $prev_node_id = '';
1154 }
1155 } else {
1156 if ( ! empty( $node_value['inputs'] ) ) {
1157 $prev_node_id = $node_value['inputs']['input_2']['connections'][0]['node'];
1158 } else {
1159 $prev_node_id = '';
1160 }
1161 }
1162 $prev_step_id = self::get_step_by_node( $funnel_id, $prev_node_id );
1163 $prev_step_type = self::get_node_type( $node_data, $prev_node_id );
1164 if ( $prev_step_type == 'conditional' ) {
1165 return self::get_prev_step( $funnel_id, $prev_step_id );
1166
1167 } else {
1168
1169 return array(
1170 'step_id' => $prev_step_id,
1171 'step_type' => $prev_step_type,
1172 );
1173 }
1174 }
1175 }
1176 }
1177 return false;
1178 }
1179
1180 /**
1181 * Get node type
1182 */
1183 public static function get_node_type( $node_data, $node_id ) {
1184 foreach ( $node_data as $node_key => $node_value ) {
1185 if ( $node_value['id'] == $node_id ) {
1186 return $node_value['data']['step_type'];
1187 }
1188 }
1189 }
1190
1191 /**
1192 * Get step by node
1193 */
1194 public static function get_step_by_node( $funnel_id, $node_id ) {
1195 if ( ! $funnel_id && ! $node_id ) {
1196 return false;
1197 }
1198
1199 $funnel_data = self::get_funnel_data( $funnel_id );
1200
1201 if ( ! $funnel_data || ! isset( $funnel_data['drawflow']['Home']['data'] ) || ! is_array( $funnel_data['drawflow']['Home']['data'] ) ) {
1202 return false;
1203 }
1204
1205 foreach ( $funnel_data['drawflow']['Home']['data'] as $key => $data ) {
1206 if ( isset( $data['id'] ) && isset( $data['data']['step_id'] ) && $node_id == $data['id'] ) {
1207 return $data['data']['step_id'];
1208 }
1209 }
1210 return false;
1211 }
1212
1213
1214 /**
1215 * Get node by step
1216 *
1217 * @param $funnel_id
1218 * @param $step_id
1219 *
1220 * @return bool|int|string
1221 */
1222 public static function get_node_id( $funnel_id, $step_id ) {
1223 if ( ! $funnel_id && ! $step_id ) {
1224 return false;
1225 }
1226
1227 $funnel_data = self::get_funnel_data( $funnel_id );
1228
1229 if ( ! $funnel_data || ! isset( $funnel_data['drawflow']['Home']['data'] ) || ! is_array( $funnel_data['drawflow']['Home']['data'] ) ) {
1230 return false;
1231 }
1232
1233 foreach ( $funnel_data['drawflow']['Home']['data'] as $key => $data ) {
1234 if ( isset( $data['id'] ) && isset( $data['data']['step_id'] ) && $step_id == $data['data']['step_id'] ) {
1235 return $data['id'];
1236 }
1237 }
1238 return false;
1239 }
1240
1241
1242 /**
1243 * Get funnel data
1244 *
1245 * @param $funnel_id
1246 *
1247 * @return mixed
1248 *
1249 * @since 2.0.5
1250 */
1251 public static function get_funnel_data( $funnel_id ) {
1252 static $cache = [];
1253 if ( ! array_key_exists( $funnel_id, $cache ) ) {
1254 $cache[ $funnel_id ] = get_post_meta( $funnel_id, '_funnel_data', true );
1255 }
1256 return $cache[ $funnel_id ];
1257 }
1258
1259
1260 /**
1261 * Update settings option
1262 *
1263 * @param $key
1264 * @param $value
1265 * @param bool $network
1266 *
1267 * @since 1.0.0
1268 */
1269 public static function update_admin_settings( $key, $value, $network = false ) {
1270 if ( $network && is_multisite() ) {
1271 update_site_option( $key, $value );
1272 } else {
1273 update_option( $key, $value );
1274 }
1275 }
1276
1277
1278 /**
1279 * Get admin settings option
1280 * by key
1281 *
1282 * @param $key
1283 * @param bool $default
1284 * @param bool $network
1285 *
1286 * @return mixed|void
1287 * @since 1.0.0
1288 */
1289 public static function get_admin_settings( $key, $default = false, $network = false ) {
1290 if ( $network && is_multisite() ) {
1291 $value = get_site_option( $key, $default );
1292 } else {
1293 $value = get_option( $key, $default );
1294 }
1295 return $value;
1296 }
1297
1298
1299 /**
1300 * Get general settings data
1301 *
1302 * @return array
1303 * @since 1.0.0
1304 */
1305 public static function get_general_settings() {
1306 $default_settings = apply_filters(
1307 'wpfunnels_general_settings',
1308 array(
1309 'builder' => 'gutenberg',
1310 'paypal_reference' => 'off',
1311 'order_bump' => 'off',
1312 'ab_testing' => 'off',
1313 'allow_funnels' => array( 'administrator' => 'true' ),
1314 'funnel_type' => 'sales',
1315 'create_child_order' => 'off',
1316 'disable_theme_style' => 'off',
1317 'enable_log_status' => 'off',
1318 'enable_skip_cart' => 'off',
1319 'skip_cart_for' => 'whole',
1320 'uninstall_cleanup' => 'off',
1321 'funnel_builder_mode' => self::DEFAULT_FUNNEL_BUILDER_MODE,
1322 )
1323 );
1324 $saved_settings = self::get_admin_settings( '_wpfunnels_general_settings', $default_settings );
1325 return wp_parse_args( $saved_settings, $default_settings );
1326 }
1327
1328
1329 /**
1330 * Valid canvas layouts.
1331 *
1332 * @return array
1333 * @since 3.12.13
1334 */
1335 public static function get_funnel_builder_modes() {
1336 return array( 'horizontal', 'vertical' );
1337 }
1338
1339
1340 /**
1341 * Site-wide default canvas layout, as chosen in Settings.
1342 *
1343 * @return string 'horizontal' or 'vertical'.
1344 * @since 3.12.13
1345 */
1346 public static function get_default_funnel_builder_mode() {
1347 $general_settings = self::get_general_settings();
1348 $mode = isset( $general_settings['funnel_builder_mode'] ) ? $general_settings['funnel_builder_mode'] : self::DEFAULT_FUNNEL_BUILDER_MODE;
1349
1350 return in_array( $mode, self::get_funnel_builder_modes(), true ) ? $mode : self::DEFAULT_FUNNEL_BUILDER_MODE;
1351 }
1352
1353
1354 /**
1355 * Canvas layout to open the builder in for a given user.
1356 *
1357 * A user who has switched views on the canvas keeps that view on their next
1358 * visit; everyone else gets the site-wide default from Settings.
1359 *
1360 * @param int $user_id User to resolve for. Defaults to the current user.
1361 *
1362 * @return string 'horizontal' or 'vertical'.
1363 * @since 3.12.13
1364 */
1365 public static function get_canvas_view_mode( $user_id = 0 ) {
1366 $user_id = $user_id ? $user_id : get_current_user_id();
1367
1368 if ( $user_id ) {
1369 $preference = get_user_meta( $user_id, self::CANVAS_VIEW_MODE_META_KEY, true );
1370 if ( in_array( $preference, self::get_funnel_builder_modes(), true ) ) {
1371 return $preference;
1372 }
1373 }
1374
1375 return self::get_default_funnel_builder_mode();
1376 }
1377
1378
1379 /**
1380 * Get permalink settings data
1381 *
1382 * @return array
1383 * @since 1.0.0
1384 */
1385 public static function get_permalink_settings() {
1386 $default_settings = apply_filters(
1387 'wpfunnels_permalink_settings',
1388 array(
1389 'structure' => 'default',
1390 'step_base' => WPFNL_STEPS_POST_TYPE,
1391 'funnel_base' => WPFNL_FUNNELS_POST_TYPE,
1392 )
1393 );
1394 $saved_settings = self::get_admin_settings( '_wpfunnels_permalink_settings' );
1395 return wp_parse_args( $saved_settings, $default_settings );
1396 }
1397
1398 /**
1399 * Get optin settings data
1400 *
1401 * @return array|string
1402 * @since 1.0.0
1403 */
1404 public static function get_optin_settings( $handler = '' ) {
1405 $default_settings = apply_filters(
1406 'wpfunnels_optin_settings',
1407 array(
1408 'sender_name' => get_bloginfo('name'),
1409 'sender_email' => wp_get_current_user()->user_email,
1410 'email_subject' => 'New Form Submission',
1411 )
1412 );
1413 $saved_settings = self::get_admin_settings( '_wpfunnels_optin_settings' );
1414 $settings = wp_parse_args( $saved_settings, $default_settings );
1415 return $handler && isset($settings[$handler]) ? $settings[$handler] : $settings;
1416 }
1417
1418
1419 /**
1420 * Get offer settings data
1421 *
1422 * @return array
1423 * @since 1.0.0
1424 */
1425 public static function get_offer_settings() {
1426 $default_settings = apply_filters(
1427 'wpfunnels/get_offer_settings',
1428 array(
1429 'offer_orders' => 'main-order',
1430 'show_supported_payment_gateway' => 'off',
1431 'skip_offer_step' => 'off',
1432 'skip_offer_step_for_free' => 'off',
1433 'enable_global_thankyou' => 'off',
1434 )
1435 );
1436 $saved_settings = self::get_admin_settings( '_wpfunnels_offer_settings' );
1437 return wp_parse_args( $saved_settings, $default_settings );
1438 }
1439
1440 /**
1441 * Get GTM events
1442 *
1443 * @return array
1444 */
1445 public static function get_gtm_events() {
1446 $default_gtm_events = array(
1447 'add_to_cart' => 'Add to cart',
1448 'begin_checkout' => 'Begin checkout',
1449 'add_payment_info' => 'Add Payment Info',
1450 'add_shipping_info' => 'Add Shipping Info',
1451 'purchase' => 'Purchase',
1452 'orderbump_accept' => 'Order Bump',
1453 'upsell' => 'Upsell',
1454 'downsell' => 'Downsell',
1455 );
1456 return $default_gtm_events;
1457 }
1458
1459 public static function get_gtm_settings() {
1460 $default_enable_settings = array(
1461 'gtm_enable' => 'off',
1462 'gtm_container_id' => '',
1463 'gtm_events' => array(
1464 'add_to_cart' => 'true',
1465 'begin_checkout' => 'true',
1466 'add_payment_info' => 'true',
1467 'add_shipping_info' => 'true',
1468 'purchase' => 'true',
1469 'orderbump_accept' => 'true',
1470 'upsell' => 'true',
1471 'downsell' => 'true',
1472 ),
1473 );
1474 $gtm_settings = self::get_admin_settings( '_wpfunnels_gtm' );
1475 return wp_parse_args( $gtm_settings, $default_enable_settings );
1476 }
1477 /**
1478 * Get facebook pixel events
1479 *
1480 * @return array
1481 */
1482 public static function get_facebook_events() {
1483 $default_fb_events = array(
1484 'AddPaymentInfo' => 'Add payment info',
1485 'AddToCart' => 'Add to cart',
1486 'InitiateCheckout' => 'Initiate checkout',
1487 // 'Lead' => 'Lead',
1488 'Purchase' => 'Purchase',
1489 'ViewContent' => 'View content',
1490 );
1491 return $default_fb_events;
1492 }
1493
1494 public static function get_facebook_pixel_settings() {
1495 $default_enable_settings = array(
1496 'enable_fb_pixel' => 'off',
1497 'facebook_pixel_id' => '',
1498 'facebook_tracking_events' => array(
1499 'AddPaymentInfo' => 'true',
1500 'AddToCart' => 'true',
1501 'InitiateCheckout' => 'true',
1502 'Lead' => 'true',
1503 'Purchase' => 'true',
1504 'ViewContent' => 'true',
1505 ),
1506 );
1507 $facebook_pixel_setting = self::get_admin_settings( '_wpfunnels_facebook_pixel' );
1508 return wp_parse_args( $facebook_pixel_setting, $default_enable_settings );
1509 }
1510
1511
1512 public static function get_recaptcha_settings() {
1513 $default_enable_settings = array(
1514 'enable_recaptcha' => 'no',
1515 'recaptcha_site_key' => '',
1516 'recaptcha_site_secret' => '',
1517 );
1518 $recaptcha_setting = self::get_admin_settings( '_wpfunnels_recaptcha_setting' );
1519 return wp_parse_args( $recaptcha_setting, $default_enable_settings );
1520 }
1521
1522
1523 /**
1524 * Get user role management settings
1525 *
1526 * @return array|mixed|void
1527 *
1528 * @since 3.1.3
1529 */
1530 public static function get_user_role_settings() {
1531 global $wp_roles;
1532
1533 $user_role_names = array_keys( $wp_roles->get_names() );
1534 $user_role_names = array_diff( $user_role_names, array( 'administrator' ) );
1535
1536 $default_settings = array();
1537
1538 foreach ( $user_role_names as $user_role_name ) {
1539 if( 'subscriber' === $user_role_name || 'customer' === $user_role_name ) {
1540 continue;
1541 }
1542 $default_settings[ $user_role_name ] = 'no';
1543 }
1544
1545 $user_role_manager = self::get_admin_settings( '_wpfunnels_user_roles' );
1546 $user_role_manager = wp_parse_args( $user_role_manager, $default_settings );
1547 return $user_role_manager;
1548 }
1549
1550
1551 /*
1552 * Get google place API key
1553 *
1554 * @return mixed|void
1555 * @since 3.1.3
1556 */
1557 public static function get_google_map_api_key() {
1558 return self::get_admin_settings( '_wpfunnels_google_map_api_key', '' );
1559 }
1560
1561
1562 /**
1563 * Get notification settings
1564 *
1565 * @return array
1566 * @since 3.2.0
1567 */
1568 public static function get_notification_settings() {
1569 $default_notification_settings = array(
1570 'enable_revenue_report' => 'no',
1571 'revenue_report_frequency' => 'weekly',
1572 'revenue_report_recipient' => get_option('admin_email'),
1573 'revenue_report_subject' => 'Store Revenue Report - {period}',
1574 'send_time' => '10:00',
1575 );
1576
1577 $notification_settings = self::get_admin_settings( '_wpfunnels_notification_settings' );
1578 return wp_parse_args( $notification_settings, $default_notification_settings );
1579 }
1580
1581
1582 /**
1583 * Get advanced settings
1584 */
1585 public static function get_advanced_settings() {
1586 $default_enable_settings = array(
1587 'show_supported_payment_gateway' => 'off',
1588 );
1589
1590 $advanced_settings = self::get_admin_settings( '_wpfunnels_advanced_settings' );
1591 return wp_parse_args( $advanced_settings, $default_enable_settings );
1592 }
1593
1594 /**
1595 * Get UTM Parameters
1596 *
1597 * @return array
1598 */
1599 public static function get_utm_params() {
1600 $default_utm_params = array(
1601 'utm_source' => 'UTM Source',
1602 'utm_medium' => 'UTM Medium',
1603 'utm_campaign' => 'UTM Campaign',
1604 'utm_content' => 'UTM Content',
1605 );
1606 return $default_utm_params;
1607 }
1608
1609 public static function get_utm_settings() {
1610 $default_enable_settings = array(
1611 'utm_enable' => 'off',
1612 'utm_source' => '',
1613 'utm_medium' => '',
1614 'utm_campaign' => '',
1615 'utm_content' => '',
1616 );
1617 $utm_settings = self::get_admin_settings( '_wpfunnels_utm_params' );
1618 return wp_parse_args( $utm_settings, $default_enable_settings );
1619 }
1620
1621 /**
1622 * Get user roles
1623 *
1624 * @return array
1625 * @since 2.1.7
1626 */
1627 public static function get_user_roles() {
1628 global $wp_roles;
1629 $all_roles = $wp_roles->roles;
1630 return array_keys( $all_roles );
1631 }
1632
1633
1634 /**
1635 * Get the saved builder type
1636 *
1637 * @return mixed|void
1638 * @since 1.0.0
1639 */
1640 public static function get_builder_type() {
1641 $general_settings = self::get_general_settings();
1642 return $general_settings['builder'];
1643 }
1644
1645
1646 /**
1647 * Check if wc is installed
1648 *
1649 * @return bool
1650 * @since 1.0.0
1651 */
1652 public static function is_wc_active() {
1653 if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
1654 return true;
1655 } elseif ( function_exists( 'is_plugin_active' ) ) {
1656 if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
1657 return true;
1658 }
1659 }
1660 return false;
1661 }
1662
1663 /**
1664 * Check if woocommerce payment is activated or not
1665 *
1666 * @return bool
1667 * @since 1.9.0
1668 */
1669 public static function is_wc_payment_active() {
1670 if ( defined( 'WCPAY_PLUGIN_FILE' ) ) {
1671 return true;
1672 }
1673 return false;
1674 }
1675
1676
1677 /**
1678 * Check if elementor is installed
1679 *
1680 * @return bool
1681 * @since 1.0.0
1682 */
1683 public static function is_elementor_active() {
1684 if ( in_array( 'elementor/elementor.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
1685 return true;
1686 } elseif ( function_exists( 'is_plugin_active' ) ) {
1687 if ( is_plugin_active( 'elementor/elementor.php' ) ) {
1688 return true;
1689 }
1690 }
1691 return false;
1692 }
1693
1694
1695 /**
1696 * Check if funnel step or not
1697 *
1698 * @return bool
1699 * @since 3.1.0
1700 */
1701 public static function maybe_funnel_step( $step_id = null ) {
1702 if ( ! $step_id ) {
1703 $step_id = get_the_ID();
1704 }
1705 $post_type = get_post_type( $step_id );
1706 if ( WPFNL_STEPS_POST_TYPE === $post_type ) {
1707 return true;
1708 }
1709 return false;
1710 }
1711
1712
1713 /**
1714 * Check if saved builder is activated
1715 * or not
1716 *
1717 * @param $builder
1718 *
1719 * @return bool
1720 * @since 1.0.0
1721 */
1722 public static function is_builder_active( $builder ) {
1723 switch ( $builder ) {
1724 case 'elementor':
1725 return self::is_elementor_active();
1726 break;
1727 default:
1728 return false;
1729 break;
1730 }
1731 }
1732
1733
1734 /**
1735 * Check if the global funnel addon is activated or not
1736 *
1737 * @return bool
1738 * @since 2.0.4
1739 */
1740 public static function is_global_funnel_activated() {
1741 return apply_filters( 'wpfunnels/is_global_funnel_activated', false );
1742 }
1743
1744
1745 /**
1746 * Check if the funnel is global funnel
1747 *
1748 * @param $funnel_id
1749 *
1750 * @return bool
1751 */
1752 public static function is_global_funnel( $funnel_id ) {
1753 if ( ! $funnel_id ) {
1754 return false;
1755 }
1756 return apply_filters( 'wpfunnels/is_global_funnel', false, $funnel_id );
1757 }
1758
1759
1760 /**
1761 * Check if pro is activated/deactivated
1762 *
1763 * @return bool
1764 * @since 1.0.0
1765 */
1766 public static function is_wpfnl_pro_activated() {
1767 return apply_filters( 'wpfunnels/is_wpfnl_pro_active', false ) || apply_filters( 'is_wpfnl_pro_active', false );
1768 }
1769
1770
1771 /**
1772 * Check if Divi 5 is active.
1773 *
1774 * This function checks if Divi 5 is enabled using multiple detection methods
1775 * to ensure compatibility regardless of when the check is performed.
1776 *
1777 * @return bool
1778 * @since 2.9.1
1779 */
1780 public static function is_divi5_active(): bool {
1781 // Primary: Check if the Divi 5 function exists and call it
1782 if ( function_exists( 'et_builder_d5_enabled' ) ) {
1783 return et_builder_d5_enabled();
1784 }
1785
1786 // Fallback 1: Check if Divi 5 classes/interfaces exist
1787 if ( interface_exists( 'ET\Builder\Framework\DependencyManagement\Interfaces\DependencyInterface' ) ) {
1788 return true;
1789 }
1790
1791 // Fallback 2: Check if Divi theme is active and ET_BUILDER_VERSION is 5.x
1792 if ( get_template() === 'Divi' && defined( 'ET_BUILDER_VERSION' ) ) {
1793 return version_compare( ET_BUILDER_VERSION, '5.0', '>=' );
1794 }
1795
1796 // Fallback 3: Check theme version from style.css
1797 if ( get_template() === 'Divi' ) {
1798 $theme = wp_get_theme( 'Divi' );
1799 if ( $theme->exists() ) {
1800 $version = $theme->get( 'Version' );
1801 return version_compare( $version, '5.0', '>=' );
1802 }
1803 }
1804
1805 return false;
1806 }
1807
1808
1809 /**
1810 * Check if pro is activated/deactivated
1811 *
1812 * @return bool
1813 * @since 1.0.0
1814 */
1815 public static function is_pro_license_activated() {
1816 return apply_filters( 'wpfunnels/is_pro_license_activated', false );
1817 }
1818
1819
1820 /**
1821 * Count funnels that are NOT store checkout type.
1822 *
1823 * Used to enforce the free-plan 3-funnel limit without counting
1824 * store checkout funnels against that quota.
1825 *
1826 * @param array $statuses Post statuses to include. Defaults to publish + draft.
1827 * @return int
1828 * @since 3.6.0
1829 */
1830 public static function count_non_store_checkout_funnels( $statuses = array( 'publish', 'draft' ) ) {
1831 $args = array(
1832 'post_type' => WPFNL_FUNNELS_POST_TYPE,
1833 'post_status' => $statuses,
1834 'posts_per_page' => -1,
1835 'suppress_filters' => true,
1836 'fields' => 'ids',
1837 'meta_query' => array(
1838 'relation' => 'OR',
1839 array(
1840 'key' => '_wpfnl_funnel_type',
1841 'compare' => 'NOT EXISTS',
1842 ),
1843 array(
1844 'key' => '_wpfnl_funnel_type',
1845 'value' => 'store_checkout',
1846 'compare' => '!=',
1847 ),
1848 ),
1849 );
1850 return count( get_posts( $args ) );
1851 }
1852
1853
1854 /**
1855 * Count store checkout funnels.
1856 *
1857 * Used to enforce the free-plan 3-store-checkout limit.
1858 *
1859 * @param array $statuses Post statuses to include. Defaults to publish + draft.
1860 * @return int
1861 * @since 3.6.0
1862 */
1863 public static function count_store_checkout_funnels( $statuses = array( 'publish', 'draft' ) ) {
1864 $args = array(
1865 'post_type' => WPFNL_FUNNELS_POST_TYPE,
1866 'post_status' => $statuses,
1867 'posts_per_page' => -1,
1868 'suppress_filters' => true,
1869 'fields' => 'ids',
1870 'meta_query' => array(
1871 array(
1872 'key' => '_wpfnl_funnel_type',
1873 'value' => 'store_checkout',
1874 'compare' => '=',
1875 ),
1876 ),
1877 );
1878 return count( get_posts( $args ) );
1879 }
1880
1881 /**
1882 * Check if the module is pro or
1883 * not
1884 *
1885 * @param $module
1886 *
1887 * @return bool
1888 * @since 1.0.0
1889 */
1890 public static function is_pro_module( $module ) {
1891 $pro_modules = apply_filters( 'wpfnl_pro_modules', array() );
1892 return in_array( $module, $pro_modules );
1893 }
1894
1895
1896 /**
1897 * Check if the module is exists or not
1898 *
1899 * @param $module_name
1900 * @param string $type
1901 * @param bool $step
1902 * @param bool $pro
1903 *
1904 * @return bool
1905 * @since 1.0.0
1906 */
1907 public static function is_module_registered( $module_name, $type = 'admin', $step = false, $pro = false ) {
1908 if ( empty( $module_name ) ) {
1909 return false;
1910 }
1911 $class_name = str_replace( '-', ' ', $module_name );
1912 $class_name = str_replace( ' ', '', ucwords( $class_name ) );
1913 if ( $pro ) {
1914 if ( $type === 'steps' ) {
1915 $class_name = 'WPFunnelsPro\\Admin\\Modules\\Steps\\' . $class_name . '\Module';
1916 }
1917 } else {
1918 if ( $type === 'admin' ) {
1919 $class_name = 'WPFunnels\\Admin\\Modules\\' . $class_name . '\Module';
1920 } elseif ( $type === 'steps' ) {
1921 $class_name = 'WPFunnels\\Admin\\Modules\\Steps\\' . $class_name . '\Module';
1922 } else {
1923 $class_name = 'WPFunnels\\Modules\\Frontend\\' . $class_name . '\Module';
1924 }
1925 }
1926 return class_exists( $class_name );
1927 }
1928
1929
1930 /**
1931 * Check manager permissions on REST API.
1932 *
1933 * @param string $object Object.
1934 * @param string $context Request context.
1935 *
1936 * @return bool
1937 * @since 2.6.0
1938 */
1939 public static function wpfnl_rest_check_manager_permissions( $object, $context = 'read' ) {
1940
1941 $objects = array(
1942 'funnels' => 'wpf_manage_funnels',
1943 'settings' => 'wpf_manage_funnels',
1944 'templates' => 'wpf_manage_funnels',
1945 'steps' => 'wpf_manage_funnels',
1946 'products' => 'wpf_manage_funnels',
1947 );
1948 if ( ! isset( $objects[ $object ] ) ) {
1949 return false;
1950 }
1951 return current_user_can( $objects[ $object ] );
1952 }
1953
1954
1955 /**
1956 * Check if the provided plugin ($path) is installed or not
1957 *
1958 * @param $path
1959 *
1960 * @return bool
1961 * @since 2.0.0
1962 */
1963 public static function is_plugin_installed( $path ) {
1964 $plugins = get_plugins();
1965 return isset( $plugins[ $path ] );
1966 }
1967
1968
1969
1970 /**
1971 * Check if the provided plugin ($path) is installed or not
1972 *
1973 * @param $path
1974 *
1975 * @return bool
1976 * @since 2.0.0
1977 */
1978 public static function is_plugin_activated( $path ) {
1979 include_once ABSPATH . 'wp-admin/includes/plugin.php';
1980 if ( is_plugin_active( $path ) ) {
1981 return true;
1982 }
1983 return false;
1984 }
1985
1986
1987
1988 /**
1989 * Check plugin status by path
1990 *
1991 * @param $path
1992 * @param $slug
1993 *
1994 * @return string
1995 *
1996 * @since 2.0.1
1997 */
1998 public static function get_plugin_action( $path, $slug ) {
1999 if ( 'divi-builder' === $slug ) {
2000 $is_divi_theme_active = self::wpfnl_is_theme_active( 'Divi' );
2001 if ( $is_divi_theme_active ) {
2002 return 'nothing';
2003 }
2004 }
2005
2006 if ( 'bricks' === $slug ) {
2007 $maybe_bricks_theme = self::maybe_bricks_theme();
2008 if ( $maybe_bricks_theme ) {
2009 return 'nothing';
2010 } else {
2011 return 'install';
2012 }
2013 }
2014
2015 if ( null === self::$installed_plugins ) {
2016 self::$installed_plugins = get_plugins();
2017 }
2018
2019 if ( ! isset( self::$installed_plugins[ $path ] ) ) {
2020 return 'install';
2021 } elseif ( ! is_plugin_active( $path ) ) {
2022 return 'activate';
2023 } else {
2024 return 'nothing';
2025 }
2026 }
2027
2028 /**
2029 * Check theme is active or not
2030 *
2031 * @param string $theme_name
2032 *
2033 * @return Bool
2034 */
2035 public static function wpfnl_is_theme_active( $theme_name ) {
2036 $theme = wp_get_theme(); // gets the current theme
2037 if ( $theme_name === $theme->name || $theme_name === $theme->parent_theme ) {
2038 return true;
2039 }
2040 return false;
2041 }
2042
2043
2044 /**
2045 * Check plugin is installed or not
2046 *
2047 * @param $plugin_slug
2048 *
2049 * @return Bolean
2050 */
2051 public static function wpfnl_check_is_plugin_installed( $plugin ) {
2052 $installed_plugins = get_plugins();
2053 return array_key_exists( $plugin, $installed_plugins ) || in_array( $plugin, $installed_plugins, true );
2054 }
2055
2056
2057 /**
2058 * Get depenedency plugins status
2059 *
2060 * @return mixed|void
2061 *
2062 * @since 2.0.1
2063 */
2064 public static function get_dependency_plugins_status() {
2065 return apply_filters(
2066 'wpfunnels/dependency_plugin_list',
2067 array(
2068 'elementor' => array(
2069 'name' => 'Elementor',
2070 'plugin_file' => 'elementor/elementor.php',
2071 'slug' => 'elementor',
2072 'action' => self::get_plugin_action( 'elementor/elementor.php', 'elementor' ),
2073 ),
2074 'divi-builder' => array(
2075 'name' => 'Divi',
2076 'plugin_file' => 'divi-builder/divi-builder.php',
2077 'slug' => 'divi-builder',
2078 'action' => self::get_plugin_action( 'divi-builder/divi-builder.php', 'divi-builder' ),
2079 ),
2080 'oxygen' => array(
2081 'name' => 'Oxygen',
2082 'plugin_file' => 'oxygen/functions.php',
2083 'slug' => 'oxygen',
2084 'action' => self::get_plugin_action( 'oxygen/functions.php', 'oxygen' ),
2085 ),
2086 'bricks' => array(
2087 'name' => 'Bricks',
2088 'plugin_file' => 'bricks',
2089 'slug' => 'bricks',
2090 'action' => self::get_plugin_action( 'bricks', 'bricks' ),
2091 ),
2092 )
2093 );
2094 }
2095
2096
2097 /**
2098 * Is there any missing plugin for wpfunnels
2099 *
2100 * @return string
2101 *
2102 * @since 2.0.1
2103 */
2104 public static function is_any_plugin_missing() {
2105 if ( null === self::$installed_plugins ) {
2106 self::$installed_plugins = get_plugins();
2107 }
2108 $builder = self::get_builder_type();
2109 $dependency_plugins = self::get_dependency_plugins_status();
2110 $is_missing = 'no';
2111
2112 if ( isset( $dependency_plugins[ $builder ] ) ) {
2113 $plugin_data = $dependency_plugins[ $builder ];
2114 if ( 'activate' === $plugin_data['action'] || 'install' === $plugin_data['action'] ) {
2115 $is_missing = 'yes';
2116 }
2117 }
2118
2119 /**
2120 * For support extra theme with divi
2121 */
2122 $theme = wp_get_theme();
2123
2124 if ( 'divi-builder' === $builder && ( 'Extra' === $theme->name || 'Extra' === $theme->parent_theme ) ) {
2125 $is_missing = 'no';
2126 }
2127
2128 return $is_missing;
2129 }
2130
2131
2132 /**
2133 * Recursively traverses a multidimensional array in search of a specific value and returns the
2134 * array containing the value, or an
2135 * null on failure.
2136 *
2137 * @param $search_value
2138 * @param $array
2139 *
2140 * @return array
2141 * @since 2.0.0
2142 */
2143 public static function recursive_multidimensional_ob_array_search_by_value( $search_value, $array, $keys = array() ) {
2144 if ( is_array( $array ) && count( $array ) > 0 ) {
2145 foreach ( $array as $key => $value ) {
2146 $temp_keys = $keys;
2147
2148 // Adding current key to search path
2149 array_push( $temp_keys, $key );
2150
2151 // Check if this value is an array
2152 // with atleast one element
2153 if ( is_array( $value ) && count( $value ) > 0 ) {
2154 $widget_type = isset( $value['widgetType'] ) ? $value['widgetType'] : false;
2155 if ( $widget_type ) {
2156 if ( $widget_type === $search_value ) {
2157 $value['path'] = $temp_keys;
2158 return $value;
2159 } else {
2160 $res_path = self::recursive_multidimensional_ob_array_search_by_value(
2161 $search_value,
2162 $value['elements'],
2163 $temp_keys
2164 );
2165 }
2166 if ( $res_path != null ) {
2167 return $res_path;
2168 }
2169 } else {
2170 $res_path = self::recursive_multidimensional_ob_array_search_by_value(
2171 $search_value,
2172 $value['elements'],
2173 $temp_keys
2174 );
2175 }
2176 if ( $res_path != null ) {
2177 return $res_path;
2178 }
2179 }
2180 }
2181 }
2182
2183 return null;
2184 }
2185
2186
2187 /**
2188 * Check if checkout ajax or not
2189 *
2190 * @return bool
2191 */
2192 public static function is_checkout_ajax() {
2193 if ( wp_doing_ajax() || isset( $_GET['wc-ajax'] ) ) {
2194 if ( isset( $_GET['wc-ajax'] ) && //phpcs:ignore
2195 isset( $_POST['_wcf_checkout_id'] ) //phpcs:ignore
2196 ) {
2197 return true;
2198 }
2199 }
2200
2201 return false;
2202 }
2203
2204
2205 /**
2206 * Calculate discount price
2207 *
2208 * @param $discount_type
2209 * @param $discount_value
2210 * @param $product_price
2211 *
2212 * @deprecated deprecated since 2.7.6
2213 *
2214 * @return string
2215 */
2216 public static function calculate_discount_price( $discount_type, $discount_value, $product_price ) {
2217
2218 $custom_price = $product_price;
2219 if ( ! empty( $discount_type ) ) {
2220 if ( 'discount-percentage' === $discount_type ) {
2221 if ( $discount_value > 0 && $discount_value <= 100 ) {
2222 $custom_price = $product_price - ( ( $product_price * $discount_value ) / 100 );
2223 $custom_price = number_format( (float) $custom_price, 2, '.', '' );
2224 }
2225 } elseif ( 'discount-price' === $discount_type ) {
2226 if ( $discount_value > 0 ) {
2227 $custom_price = $product_price - $discount_value;
2228 }
2229 }
2230 }
2231 return $custom_price;
2232 }
2233
2234
2235 /**
2236 * Calculate discount price
2237 *
2238 * @param $discount_type
2239 * @param $discount_value
2240 * @param $product_price
2241 *
2242 * @return string
2243 */
2244 public static function calculate_discount_amount( $discount_type, $discount_value, $product_price ) {
2245
2246 if ( $discount_type !== 'original' && ! empty( $discount_type ) ) {
2247 $discount = '';
2248 if ( 'discount-percentage' === $discount_type ) {
2249 if ( $discount_value > 0 && $discount_value <= 100 ) {
2250 $discount = ( ( $product_price * $discount_value ) / 100 );
2251 }
2252 } elseif ( 'discount-price' === $discount_type ) {
2253 if ( $discount_value > 0 ) {
2254 $discount = $discount_value;
2255
2256 }
2257 }
2258 return number_format( $discount, 2 );
2259 }
2260
2261 return false;
2262 }
2263
2264
2265 /**
2266 * Get attributes for wpfunnels body wrapper
2267 *
2268 * @param string $template
2269 *
2270 * @return string
2271 */
2272 public static function get_template_container_atts( $template = '' ) {
2273 $attributes = apply_filters( 'wpfunnels/page_container_atts', array() );
2274 $atts_string = '';
2275 foreach ( $attributes as $key => $value ) {
2276 if ( ! $value ) {
2277 continue;
2278 }
2279 if ( true === $value ) {
2280 $atts_string .= esc_html( $key ) . ' ';
2281 } else {
2282 $atts_string .= sprintf( '%s="%s" ', esc_html( $key ), esc_attr( $value ) );
2283 }
2284 }
2285 return $atts_string;
2286 }
2287
2288
2289 /**
2290 * Get funnel link
2291 *
2292 * @param $funnel_id
2293 *
2294 * @return Mix
2295 */
2296 public static function get_funnel_link( $funnel_id ) {
2297 if ( ! $funnel_id ) {
2298 return;
2299 }
2300 $steps = self::get_steps( $funnel_id );
2301 if ( $steps && is_array( $steps ) ) {
2302 $first_step = reset( $steps );
2303 $first_step_id = $first_step['id'];
2304 return get_the_permalink( $first_step_id );
2305 }
2306 return home_url();
2307 }
2308
2309
2310 /**
2311 * Get wc fragment
2312 *
2313 * @param array $data
2314 *
2315 * @return array
2316 */
2317 public static function get_wc_fragments( $data = array() ) {
2318 ob_start();
2319 woocommerce_order_review();
2320 $woocommerce_order_review = ob_get_clean();
2321
2322 ob_start();
2323 woocommerce_checkout_payment();
2324 $woocommerce_checkout_payment = ob_get_clean();
2325
2326 $response = array(
2327 'cart_total' => WC()->cart->total,
2328 'cart_total_currency' => WC()->cart->get_cart_total(),
2329 'wc_custom_fragments' => 'yes',
2330 'fragments' => apply_filters(
2331 'woocommerce_update_order_review_fragments',
2332 array(
2333 '.woocommerce-checkout-review-order-table' => $woocommerce_order_review,
2334 /*'.woocommerce-checkout-payment' => $woocommerce_checkout_payment,*/
2335 )
2336 ),
2337 );
2338
2339 if ( ! empty( $data ) ) {
2340 $response['wpfunnels_data'] = $data;
2341 }
2342
2343 return $response;
2344 }
2345
2346
2347 /**
2348 * Get supported builders
2349 *
2350 * @return array
2351 */
2352 public static function get_supported_builders() {
2353 $builders = array(
2354 'gutenberg' => 'Gutenberg',
2355 'elementor' => 'Elementor',
2356 'divi-builder' => 'Divi builder',
2357 'oxygen' => 'Oxygen',
2358 'bricks' => 'Bricks',
2359 'other' => 'Other',
2360 );
2361 return apply_filters( 'wpfunnels/supported_builders', $builders );
2362 }
2363
2364 /**
2365 * Get custom url instead of deafult tahnkyou page
2366 *
2367 * @param mixed $step_id
2368 *
2369 * @return [type]
2370 */
2371 public static function custom_url_for_thankyou_page( $step_id ) {
2372
2373 $isThankyou = self::check_if_this_is_step_type_by_id( $step_id, 'thankyou' );
2374 $isCustomUrl = get_post_meta( $step_id, '_wpfnl_thankyou_is_custom_redirect', true );
2375 $isDirect = get_post_meta( $step_id, '_wpfnl_thankyou_is_direct_redirect', true );
2376
2377 if ( $isThankyou && $isCustomUrl === 'on' ) {
2378 $url = get_post_meta( $step_id, '_wpfnl_thankyou_custom_redirect_url', true );
2379 if ( $url ) {
2380
2381 if ( $isDirect === 'off' ) {
2382 $redirectAfterSec = get_post_meta( $step_id, '_wpfnl_thankyou_set_time', true ) ? get_post_meta( $step_id, '_wpfnl_thankyou_set_time', true ) : 5;
2383 header( 'refresh:' . $redirectAfterSec . ';url=' . $url );
2384 } else {
2385 // unsell 'wpfunnels_automation_data' cookie and trigger 'wpfunnels/trigger_automation' hook
2386 self::unset_site_cookie( $step_id, 'wpfunnels_automation_data', 'wpfunnels/trigger_automation' );
2387 return $url;
2388 }
2389 }
2390 return false;
2391 }
2392 return false;
2393 }
2394
2395 /**
2396 * Check product has perfect variation or not
2397 *
2398 * @param $variation_id
2399 *
2400 * @return Boolean
2401 */
2402 public static function is_perfect_variations( $variation_id ) {
2403
2404 $blank_attr = array();
2405 $response = array(
2406 'status' => true,
2407 'data' => array(),
2408 );
2409 $product = wc_get_product( $variation_id );
2410 if ( $product ) {
2411 $parent_id = $product->get_parent_id();
2412 $_product = wc_get_product( $parent_id );
2413 if ( $_product ) {
2414 $attrs = self::get_product_attr( $_product );
2415 $attributes = $product->get_attributes();
2416 if ( ! empty( $attributes ) ) {
2417 foreach ( $attributes as $attribute_key => $attribute_value ) {
2418
2419 if ( ! $attribute_value ) {
2420 $blank_attr[ $attribute_key ] = $attrs[ $attribute_key ];
2421 $response['status'] = false;
2422 }
2423 }
2424 }
2425 }
2426 }
2427 $response['data'] = $blank_attr;
2428 return $response;
2429 }
2430
2431
2432 /**
2433 * Get attributes of product
2434 *
2435 * @param Object $produt
2436 */
2437 public static function get_product_attr( $product ) {
2438 $attributes = $product->get_attributes();
2439 $attr_array = array();
2440 foreach ( $attributes as $attribute_key => $attribute_value ) {
2441
2442 $attribute_name = str_replace( 'attribute_', '', $attribute_key );
2443 $attr_value = $product->get_attribute( $attribute_name );
2444 $attr_value = strtolower( $attr_value );
2445
2446 if ( strpos( $attr_value, '|' ) ) {
2447 $attr_array[ $attribute_key ] = explode( '|', $attr_value );
2448 } else {
2449 $attr_array[ $attribute_key ] = explode( ',', $attr_value );
2450 }
2451 }
2452 return $attr_array;
2453
2454 }
2455
2456
2457
2458 /**
2459 * Remove site cookie
2460 *
2461 * @param $step_id, $cookie_name, $trigger_hook, $funnel_id
2462 */
2463 public static function unset_site_cookie( $step_id, $cookie_name, $trigger_hook = '', $funnel_id = '' ) {
2464
2465 if ( ! $funnel_id ) {
2466 $funnel_id = self::get_funnel_id_from_step( $step_id );
2467 }
2468
2469 if ( ! $funnel_id ) {
2470 return false;
2471 }
2472
2473 $cookie = isset( $_COOKIE[ $cookie_name ] ) ? json_decode( wp_unslash( $_COOKIE[ $cookie_name ] ), true ) : array();
2474 if ( ! isset( $cookie['funnel_id'] ) ) {
2475 $cookie['funnel_id'] = $funnel_id;
2476 }
2477 $cookie['funnel_status'] = 'successful';
2478
2479 if ( isset( $_COOKIE[ $cookie_name ] ) ) {
2480 if ( $trigger_hook ) {
2481 do_action( $trigger_hook, $cookie );
2482 }
2483 }
2484 // unset cookie
2485 ob_start();
2486 if ( isset( $_COOKIE[ $cookie_name ] ) ) {
2487 setcookie( $cookie_name, null, strtotime( '-1 days' ), '/', COOKIE_DOMAIN );
2488 }
2489 ob_end_flush();
2490 }
2491
2492
2493 /**
2494 * Get formated product name
2495 *
2496 * @param Object $product
2497 *
2498 * @return String
2499 */
2500 public static function get_formated_product_name( $product, $formatted_attr = array() ) {
2501 $_product = wc_get_product( $product );
2502 $each_child_attr = array();
2503 $_title = '';
2504 if ( $_product ) {
2505 if ( ! $formatted_attr ) {
2506 // get_attribute_summary() only exists on variation products, not variable products
2507 if ( 'variation' === $_product->get_type() || 'subscription_variation' === $_product->get_type() ) {
2508 $attr_summary = $_product->get_attribute_summary();
2509 $attr_array = explode( ',', $attr_summary );
2510
2511 foreach ( $attr_array as $ata ) {
2512 $attr = strpbrk( $ata, ':' );
2513 $each_child_attr[] = $attr;
2514 }
2515 }
2516 } else {
2517 foreach ( $formatted_attr as $attr ) {
2518 $each_child_attr[] = ucfirst( $attr );
2519 }
2520 }
2521 if ( $each_child_attr ) {
2522 $each_child_attr_two = array();
2523 foreach ( $each_child_attr as $eca ) {
2524 $each_child_attr_two[] = str_replace( ': ', ' ', $eca );
2525 }
2526 $_title = $_product->get_title() . ' - ';
2527 $_title = $_title . implode( ', ', $each_child_attr_two );
2528 } else {
2529 $_title = $_product->get_title();
2530 }
2531 }
2532
2533 return $_title;
2534 }
2535
2536
2537 /**
2538 * Get page builder of a specific funnel by step Id from postmeta
2539 *
2540 * @param $funnel_id
2541 *
2542 * @return String $builder_name
2543 *
2544 * @since 2.0.5
2545 */
2546 public static function get_page_builder_by_step_id( $funnel_id ) {
2547 $steps = self::get_steps( $funnel_id );
2548 $builder_name = '';
2549 if ( isset( $steps[0] ) ) {
2550 $first_step_id = $steps[0]['id'];
2551 // check builder is elementor or not
2552 $elementor_page = get_post_meta( $first_step_id, '_elementor_edit_mode', true );
2553
2554 // check builder is divi or not
2555 $divi_page = get_post_meta( $first_step_id, '_et_pb_use_builder', true );
2556
2557 // check Oxygen builder is not
2558 $oxygen_page = get_post_meta( $first_step_id, 'ct_builder_shortcodes', true );
2559
2560 // check builder is bricks or not
2561 $bricks_page = get_post_meta( $first_step_id, '_bricks_editor_mode', true );
2562
2563 if ( $elementor_page ) {
2564 $builder_name = 'elementor';
2565 } elseif ( 'on' === $divi_page ) {
2566 $builder_name = 'divi-builder';
2567 } elseif ( 'bricks' === $bricks_page ) {
2568 $builder_name = 'bricks';
2569 } elseif ( ! empty( $oxygen_page ) ) {
2570 $builder_name = 'oxygen';
2571 } else {
2572 $builder_name = 'gutenberg';
2573 }
2574
2575 if ( $builder_name ) {
2576 return $builder_name;
2577 }
2578 }
2579 return $builder_name;
2580 }
2581
2582
2583 /**
2584 * Get checkout section heading settings
2585 */
2586 public static function get_checkout_section_heading_settings( $type = '', $step_id = '' ) {
2587
2588 if ( self::is_pro_license_activated() ) {
2589 if ( $step_id ) {
2590 $get_settings = get_post_meta( $step_id, '_wpfunnels_edit_field_additional_settings', true );
2591 if ( ! empty( $get_settings ) ) {
2592 if ( $type === 'billing' ) {
2593
2594 $settings = array(
2595 'custom_heading' => $get_settings['custom_billing_heading'],
2596 );
2597 return $settings;
2598 }
2599 if ( $type === 'shipping' ) {
2600 $settings = array(
2601 'custom_heading' => $get_settings['custom_shipping_heading'],
2602 );
2603 return $settings;
2604 }
2605
2606 if ( $type === 'order' ) {
2607 $settings = array(
2608 'custom_heading' => $get_settings['custom_order_detail_heading'],
2609 );
2610 return $settings;
2611 }
2612 }
2613 }
2614 }
2615 return false;
2616 }
2617
2618 /**
2619 * Check webhook addon activated or not
2620 *
2621 * @return Boolean
2622 */
2623 public static function is_webhook_activated() {
2624 if ( is_plugin_active( 'wpfunnels-pro-webhook/wpfunnels-pro-webhook.php' ) ) {
2625 return true;
2626 }
2627 return false;
2628 }
2629
2630
2631 /**
2632 * Check webhook addon license activated or not
2633 *
2634 * @return Boolean
2635 */
2636 public static function is_webhook_license_activated() {
2637
2638 if ( is_plugin_active( 'wpfunnels-pro/wpfnl-pro.php' ) ) {
2639 return true;
2640 }
2641 return false;
2642 }
2643
2644
2645 /**
2646 * Get gbf supported addons
2647 */
2648 public static function get_supported_addons() {
2649
2650 $addons = array(
2651 'global_funnel' => array(
2652 'features' => array(
2653 'trigger_options' => array(
2654 '' => __( 'Please select type..', 'wpfnl' ),
2655 'category' => __( 'Product category is ..', 'wpfnl' ),
2656 'product' => __( 'Product is ..', 'wpfnl' ),
2657 ),
2658 ),
2659 ),
2660 );
2661
2662 // 'all_product' trigger is available when GBF is active (built into Pro) or via legacy addon >= 1.0.7.
2663 if ( self::is_global_funnel_activated() || ( defined( 'WPFNL_PRO_GB_VERSION' ) && version_compare( WPFNL_PRO_GB_VERSION, '1.0.7', '>=' ) ) ) {
2664 $addons['global_funnel']['features']['trigger_options']['all_product'] = __( 'Any product is selected', 'wpfnl' );
2665 }
2666 return apply_filters( 'wpfunnels/supported-addons', $addons );
2667 }
2668
2669 /**
2670 * Get supported GBF offer condition
2671 */
2672 public static function get_supported_gbf_offer_condition() {
2673 $conditions = array(
2674 'specificProduct' => 'Specific Product',
2675 );
2676 return apply_filters( 'wpfunnels/supported-gbf-offer-condition', $conditions );
2677 }
2678
2679
2680 /**
2681 * Get product categories for GBF offer condition
2682 */
2683 public static function get_categories_gbf_offer_condition() {
2684 $categories = array();
2685 if ( self::is_wc_active() ) {
2686 $orderby = 'name';
2687 $order = 'asc';
2688 $hide_empty = false;
2689 $cat_args = array(
2690 'orderby' => $orderby,
2691 'order' => $order,
2692 'hide_empty' => $hide_empty,
2693 );
2694 $product_categories = get_terms( 'product_cat', $cat_args );
2695 if ( ! empty( $product_categories ) ) {
2696 foreach ( $product_categories as $category ) {
2697
2698 $categories[ $category->slug ] = $category->name;
2699 }
2700 }
2701 }
2702
2703 return $categories;
2704 }
2705
2706
2707 public static function oxygen_builder_version_capability() {
2708 if ( defined( 'CT_VERSION' ) && version_compare( CT_VERSION, '3.2', '>=' ) ) {
2709 return true;
2710 }
2711 return false;
2712 }
2713
2714 public static function get_funnel_utm_settings( $funnel_id = null ) {
2715 if ( $funnel_id ) {
2716 $default_enable_settings = array(
2717 'utm_enable' => 'off',
2718 'utm_source' => '',
2719 'utm_medium' => '',
2720 'utm_campaign' => '',
2721 'utm_content' => '',
2722 );
2723
2724 $utm_settings = get_post_meta( $funnel_id, '_wpfunnels_utm_params', true );
2725 return wp_parse_args( $utm_settings, $default_enable_settings );
2726 }
2727 return false;
2728 }
2729
2730 public static function get_funnel_settings( $funnel_id = null ) {
2731
2732 if ( $funnel_id ) {
2733
2734 $utm_settings = self::get_funnel_utm_settings( $funnel_id );
2735 $is_fb_pixel = get_post_meta( $funnel_id, '_wpfunnels_disabled_fb_pixel', true ) ? get_post_meta( $funnel_id, '_wpfunnels_disabled_fb_pixel', true ) : 'no';
2736 $is_gtm = get_post_meta( $funnel_id, '_wpfunnels_disabled_gtm', true ) ? get_post_meta( $funnel_id, '_wpfunnels_disabled_gtm', true ) : 'no';
2737
2738 return array(
2739 'utm_settings' => $utm_settings,
2740 'is_fb_pixel' => $is_fb_pixel,
2741 'is_gtm' => $is_gtm,
2742 );
2743 }
2744 return false;
2745
2746 }
2747
2748
2749 /**
2750 * Check discount is applicable or not in checkout page
2751 */
2752 public static function discount_on_checkout( $step_id = null ) {
2753 if ( $step_id ) {
2754 $discount = get_post_meta( $step_id, '_wpfnl_checkout_discount_main_product', true );
2755 if ( $discount ) {
2756 return $discount;
2757 }
2758 }
2759 return false;
2760 }
2761
2762
2763 /**
2764 * Calculate main product total amount in checkout page
2765 *
2766 * @param Array $checkout_product
2767 * @param Object $cart_content
2768 * @param String $step_id
2769 *
2770 * @return Mix
2771 */
2772 public static function calculate_main_product_total( $checkout_product = null, $cart_content = null, $step_id = '' ) {
2773
2774 if ( $checkout_product && $cart_content ) {
2775 $total = 0;
2776
2777 foreach ( $cart_content as $key => $content ) {
2778 if ( isset( $content['wpfnl_order_bump'] ) && $content['wpfnl_order_bump'] ) {
2779 continue;
2780 }
2781 $product_id = '';
2782 if ( ! empty( $content['variation_id'] ) ) {
2783 $product_id = $content['variation_id'];
2784 }
2785
2786 if ( ! $product_id ) {
2787 $product_id = isset( $content['product_id'] ) ? $content['product_id'] : '';
2788 }
2789
2790 $checkout_product = maybe_unserialize( $checkout_product );
2791 $key = array_search( $product_id, array_column( $checkout_product, 'id' ) );
2792 if ( $key !== false ) {
2793 if ( wc_tax_enabled() ) {
2794 if ( ! wc_prices_include_tax() ) {
2795 $content['line_total'] = number_format( $content['line_total'], 2 );
2796 $content['line_total'] = filter_var( $content['line_total'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
2797 $total = $total + $content['line_total'];
2798 } else {
2799
2800 $content['line_total'] = number_format( $content['line_total'], 2 );
2801 $content['line_total'] = filter_var( $content['line_total'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
2802 $total = $total + $content['line_total'] + $content['line_tax'];
2803
2804 }
2805 } else {
2806 $content['line_total'] = number_format( $content['line_total'], 2 );
2807 $content['line_total'] = filter_var( $content['line_total'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
2808 $total = $total + $content['line_total'] + $content['line_tax'];
2809 }
2810 }
2811 }
2812
2813 $total = number_format( $total, 2 );
2814 $total = filter_var( $total, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
2815 return $total;
2816 }
2817 return false;
2818 }
2819
2820
2821
2822 /**
2823 * Calculate cart total amount in checkout page
2824 *
2825 * @param Array $checkout_product
2826 * @param Object $cart_content
2827 * @param String $step_id
2828 *
2829 * @return bool|int|float
2830 */
2831 public static function calculate_cart_total( $checkout_product = null, $cart_content = null, $step_id = '' ) {
2832
2833 if ( $checkout_product && $cart_content ) {
2834 $total = 0;
2835
2836 foreach ( $cart_content as $key => $content ) {
2837 $product_id = $content['variation_id'] ? $content['variation_id'] : $content['product_id'];
2838
2839 if ( wc_tax_enabled() ) {
2840 if ( ! wc_prices_include_tax() ) {
2841 $content['line_total'] = number_format( $content['line_total'], 2 );
2842 $content['line_total'] = filter_var( $content['line_total'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
2843 $total = $total + $content['line_total'];
2844 } else {
2845 $content['line_total'] = number_format( $content['line_total'], 2 );
2846 $content['line_total'] = filter_var( $content['line_total'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
2847 $total = $total + $content['line_total'] + $content['line_tax'];
2848
2849 }
2850 } else {
2851 $content['line_total'] = number_format( $content['line_total'], 2 );
2852 $content['line_total'] = filter_var( $content['line_total'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
2853 $total = $total + $content['line_total'] + $content['line_tax'];
2854
2855 }
2856 }
2857 $total = number_format( $total, 2 );
2858 $total = filter_var( $total, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
2859 return $total;
2860 }
2861 return false;
2862 }
2863
2864
2865
2866 /**
2867 * Check array type ( multi-dimentional or one dimentional )
2868 *
2869 * @param Array
2870 *
2871 * @return Bool
2872 */
2873 public static function check_array_is_multidimentional( $multi_array = null ) {
2874
2875 if ( $multi_array ) {
2876 foreach ( $multi_array as $array ) {
2877 if ( is_array( $array ) ) {
2878 return true;
2879 } else {
2880 return false;
2881 }
2882 }
2883 return true;
2884 }
2885 return false;
2886 }
2887
2888
2889 /**
2890 * Migrate old Order bump settings with multiple order bump
2891 */
2892 public static function migrate_order_bump( $prev_settings = null, $step_id = null ) {
2893 if ( $prev_settings && $step_id ) {
2894 $multi_settings = array();
2895 if ( ! self::check_array_is_multidimentional( $prev_settings ) ) {
2896 $multi_settings = array(
2897 $prev_settings,
2898 );
2899 update_post_meta( $step_id, 'order-bump-settings', $multi_settings );
2900 return $multi_settings;
2901 }
2902 }
2903 return false;
2904 }
2905
2906
2907 /**
2908 * N:B test case should be implemented
2909 * Get order bump settings by step id
2910 */
2911 public static function get_ob_settings( $step_id ) {
2912
2913 $all_settings = get_post_meta( $step_id, 'order-bump-settings', true ) ? get_post_meta( $step_id, 'order-bump-settings', true ) : array();
2914 $is_multiple = self::check_array_is_multidimentional( $all_settings );
2915
2916 if ( ! $is_multiple && $all_settings ) {
2917 $all_settings['name'] = 'Order bump';
2918 $all_settings = self::migrate_order_bump( $all_settings, $step_id );
2919 }
2920
2921 if ( is_array( $all_settings ) && count( $all_settings ) > 0 ) {
2922 foreach ( $all_settings as $key => $settings ) {
2923 if ( ! isset( $all_settings[ $key ]['discountOption'] ) && 'original' !== $all_settings[ $key ]['discountOption'] ) {
2924 $product = wc_get_product( $all_settings[ $key ]['product'] );
2925 if ( $product ) {
2926 if ( $product->is_on_sale() ) {
2927 $price = wc_format_sale_price( $product->get_regular_price() * $all_settings[ $key ]['quantity'], $product->get_sale_price() ? $product->get_sale_price() * $all_settings[ $key ]['quantity'] : $product->get_regular_price() * $all_settings[ $key ]['quantity'] );
2928 } else {
2929 $price = $product->get_regular_price();
2930 if ( is_plugin_active( 'woocommerce-subscriptions/woocommerce-subscriptions.php' ) ) {
2931 $signUpFee = \WC_Subscriptions_Product::get_sign_up_fee( $product );
2932 $price = $price + $signUpFee;
2933 }
2934 $price = wc_price( $price * $all_settings[ $key ]['quantity'] );
2935 }
2936 $all_settings[ $key ]['htmlPrice'] = $price ? $price : $all_settings[ $key ]['htmlPrice'];
2937 }
2938 }
2939 }
2940 return $all_settings;
2941 }
2942
2943 return array();
2944 }
2945
2946
2947 /**
2948 * Check if funnel canvas or not
2949 *
2950 * @return bool
2951 * @since 2.3.8
2952 */
2953 public static function is_funnel_canvas_window() {
2954 if ( is_admin() && isset( $_GET['page'] ) && 'edit_funnel' === $_GET['page'] ) {
2955 return true;
2956 }
2957 return false;
2958 }
2959
2960
2961 /**
2962 * Get all funnel from post
2963 *
2964 * @return $funnels
2965 * @since 2.4.4
2966 */
2967 public static function get_all_funnels( $status = 'publish' ) {
2968
2969 if ( 'all' == $status ) {
2970 $arg = array(
2971 'post_type' => WPFNL_FUNNELS_POST_TYPE,
2972 'fields' => 'ID',
2973 'orderby' => 'date',
2974 'order' => 'ASC',
2975 'numberposts' => -1,
2976 );
2977 } else {
2978 $arg = array(
2979 'post_type' => WPFNL_FUNNELS_POST_TYPE,
2980 'post_status' => $status,
2981 'fields' => 'ID',
2982 'orderby' => 'date',
2983 'order' => 'ASC',
2984 'numberposts' => -1,
2985 );
2986 }
2987
2988 $funnels = get_posts( $arg );
2989 return $funnels;
2990 }
2991
2992
2993 /**
2994 * Get all funnel from post
2995 *
2996 * @return $funnels
2997 * @since 2.4.4
2998 */
2999 public static function get_all_steps() {
3000
3001 $steps = get_posts(
3002 array(
3003 'post_type' => 'wpfunnel_steps',
3004 'post_status' => 'publish',
3005 'fields' => 'ID',
3006 'orderby' => 'date',
3007 'order' => 'ASC',
3008 )
3009 );
3010 return $steps;
3011 }
3012
3013
3014 /**
3015 * Add type meta for each funnel
3016 *
3017 * @since 2.4.4
3018 */
3019 public static function add_type_meta() {
3020
3021 $is_added = get_option( '_wpfnl_added_type_meta' );
3022 if ( ! $is_added ) {
3023 update_option( '_wpfnl_added_type_meta', 'yes' );
3024 $funnels = self::get_all_funnels();
3025 foreach ( $funnels as $funnel ) {
3026 $type = get_post_meta( $funnel->ID, '_wpfnl_funnel_type', true );
3027 if ( ! $type ) {
3028 update_post_meta( $funnel->ID, '_wpfnl_funnel_type', 'wc' );
3029 }
3030 }
3031 }
3032
3033 }
3034
3035
3036 /**
3037 * Enable lms settings
3038 *
3039 * @return Bool
3040 * @since 2.4.5
3041 */
3042 public static function is_enable_lms_settings() {
3043 $status = get_option( 'wpfunnels_pro_lms_license_status' );
3044 if ( $status === 'active' ) {
3045 return apply_filters( 'wpfunnels/is_enable_lms_settings', false );
3046 }
3047 return false;
3048 }
3049
3050 /**
3051 * Check if lms addon activated or not
3052 *
3053 * @return Bool
3054 * @since 2.4.5
3055 */
3056 public static function is_lms_addon_active() {
3057
3058 if ( in_array( 'wpfunnels-pro-lms/wpfunnels-pro-lms.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
3059 return true;
3060 } elseif ( function_exists( 'is_plugin_active' ) ) {
3061 if ( is_plugin_active( 'wpfunnels-pro-lms/wpfunnels-pro-lms.php' ) ) {
3062 return true;
3063 }
3064 }
3065 return false;
3066 }
3067
3068 /**
3069 * Check if any LMS plugin is active (LearnDash or CreatorLMS)
3070 *
3071 * @return Bool
3072 * @since 2.0.0
3073 */
3074 public static function is_any_lms_plugin_active() {
3075 // Check LearnDash
3076 $is_learndash = in_array( 'sfwd-lms/sfwd_lms.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) );
3077
3078 // Check CreatorLMS
3079 $is_creatorlms = in_array( 'creatorlms/creatorlms.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) );
3080
3081 // For multisite
3082 if ( function_exists( 'is_plugin_active' ) && ! $is_learndash && ! $is_creatorlms ) {
3083 $is_learndash = is_plugin_active( 'sfwd-lms/sfwd_lms.php' );
3084 $is_creatorlms = is_plugin_active( 'creatorlms/creatorlms.php' );
3085 }
3086
3087 return $is_learndash || $is_creatorlms;
3088 }
3089
3090
3091 /**
3092 * Get checkout tabs column name
3093 *
3094 * @return Array
3095 * @since 2.4.5
3096 */
3097 public static function get_checkout_columns( $step_id ) {
3098
3099 $funnel_id = get_post_meta( $step_id, '_funnel_id', true );
3100 $columns = array(
3101 'product-name' => 'Product',
3102 'product-price' => 'Price',
3103 'product-quantity' => 'Quantity',
3104 'total-price' => 'Total Price',
3105 'product-action' => 'Actions',
3106 );
3107
3108 return apply_filters( 'wpfunnels/checkout_columns', $columns, $funnel_id );
3109 }
3110
3111
3112 /**
3113 * Supported order bump position
3114 *
3115 * @return Array
3116 * @since 2.4.5
3117 */
3118 public static function supported_orderbump_position( $step_id ) {
3119 $funnel_id = get_post_meta( $step_id, '_funnel_id', true );
3120
3121 $positions = array(
3122 array(
3123 'name' => 'Before Order Details',
3124 'value' => 'before-order',
3125 ),
3126 array(
3127 'name' => 'After Order Details',
3128 'value' => 'after-order',
3129 ),
3130 array(
3131 'name' => 'Before Checkout Details',
3132 'value' => 'before-checkout',
3133 ),
3134 array(
3135 'name' => 'After Customer Details',
3136 'value' => 'after-customer-details',
3137 ),
3138 array(
3139 'name' => 'Before Payment Options',
3140 'value' => 'before-payment',
3141 ),
3142 array(
3143 'name' => 'After Payment Options',
3144 'value' => 'after-payment',
3145 ),
3146 array(
3147 'name' => 'Pop-up offer',
3148 'value' => 'popup',
3149 ),
3150 );
3151 if ( $funnel_id ) {
3152 return apply_filters( 'wpfunnels/ob_positions', $positions, $funnel_id );
3153 }
3154
3155 return $positions;
3156
3157 }
3158
3159 /**
3160 * Get time zone in string
3161 *
3162 * @return String
3163 * @since 2.4.6
3164 */
3165 public static function wpfnl_timezone_string() {
3166
3167 // If site timezone string exists, return it.
3168 $timezone = get_option( 'timezone_string' );
3169 if ( $timezone ) {
3170 return $timezone;
3171 }
3172
3173 // Get UTC offset, if it isn't set then return UTC.
3174 $utc_offset = floatval( get_option( 'gmt_offset', 0 ) );
3175 if ( ! is_numeric( $utc_offset ) || 0.0 === $utc_offset ) {
3176 return 'UTC';
3177 }
3178
3179 // Adjust UTC offset from hours to seconds.
3180 $utc_offset = (int) ( $utc_offset * 3600 );
3181
3182 // Attempt to guess the timezone string from the UTC offset.
3183 $timezone = timezone_name_from_abbr( '', $utc_offset );
3184 if ( $timezone ) {
3185 return $timezone;
3186 }
3187
3188 // Last try, guess timezone string manually.
3189 foreach ( timezone_abbreviations_list() as $abbr ) {
3190 foreach ( $abbr as $city ) {
3191 // WordPress restrict the use of date(), since it's affected by timezone settings, but in this case is just what we need to guess the correct timezone.
3192 if ( (bool) date( 'I' ) === (bool) $city['dst'] && $city['timezone_id'] && intval( $city['offset'] ) === $utc_offset ) { // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
3193 return $city['timezone_id'];
3194 }
3195 }
3196 }
3197
3198 // Fallback to UTC.
3199 return 'UTC';
3200 }
3201
3202
3203
3204 /**
3205 * Get timezone offset in seconds.
3206 *
3207 * @since 2.4.6
3208 * @return float
3209 */
3210 public static function wpfnl_timezone_offset() {
3211 $timezone = get_option( 'timezone_string' );
3212 if ( $timezone ) {
3213 $timezone_object = new DateTimeZone( $timezone );
3214 return $timezone_object->getOffset( new DateTime( 'now' ) );
3215 } else {
3216 return floatval( get_option( 'gmt_offset', 0 ) ) * HOUR_IN_SECONDS;
3217 }
3218 }
3219
3220
3221 /**
3222 * Check if integrations addon activated or not
3223 *
3224 * @return Bool
3225 * @since 2.4.7
3226 */
3227 public static function is_integrations_addon_active() {
3228 if ( in_array( 'wpfunnels-pro-integrations/wpfunnels-pro-integrations.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
3229 return true;
3230 } elseif ( function_exists( 'is_plugin_active' ) ) {
3231 if ( is_plugin_active( 'wpfunnels-pro-integrations/wpfunnels-pro-integrations.php' ) ) {
3232 return true;
3233 }
3234 }
3235 return false;
3236 }
3237
3238
3239 /**
3240 * Get template types
3241 *
3242 * @return array
3243 * @since 2.4.11
3244 */
3245 public static function get_template_types() {
3246 $general_settings = self::get_general_settings();
3247 $types = array();
3248
3249 // Add WooCommerce templates if WooCommerce is active
3250 $types[] = array(
3251 'slug' => 'wc',
3252 'label' => 'Sales Templates',
3253 );
3254
3255 // Add LMS templates if LMS add-on and at least one LMS plugin (LearnDash or CreatorLMS) is active
3256 if ( self::is_lms_addon_active() && self::is_any_lms_plugin_active() ) {
3257 $types[] = array(
3258 'slug' => 'lms',
3259 'label' => 'LMS Templates',
3260 );
3261 }
3262
3263 // Lead generation templates are always available
3264 $types[] = array(
3265 'slug' => 'lead',
3266 'label' => 'Lead Gen Templates',
3267 );
3268
3269 // If funnel type is set to lead only, show only lead templates
3270 // if ( isset( $general_settings['funnel_type'] ) && 'lead' === $general_settings['funnel_type'] ) {
3271 // $types = array(
3272 // array(
3273 // 'slug' => 'lead',
3274 // 'label' => 'Lead Gen Templates',
3275 // ),
3276 // );
3277 // }
3278
3279 return apply_filters( 'wpfunnels/modify_template_type', $types );
3280
3281 }
3282
3283
3284 /**
3285 * Get global funnel type
3286 *
3287 * @return Mix
3288 */
3289 public static function get_global_funnel_type() {
3290 $general_settings = self::get_general_settings();
3291 if ( isset( $general_settings['funnel_type'] ) ) {
3292 return $general_settings['funnel_type'];
3293 }
3294 return false;
3295 }
3296
3297
3298 /**
3299 * May be allow to create sales funnel
3300 *
3301 * @return Bool
3302 */
3303 public static function maybe_allow_sales_funnel() {
3304 if ( self::is_wc_active() || self::is_lms_addon_active() ) {
3305 return true;
3306 }
3307 return false;
3308 }
3309
3310 /**
3311 * Retrieve all user role
3312 *
3313 * @return Array
3314 */
3315 public static function get_all_user_roles() {
3316 global $wp_roles;
3317
3318 $all_roles = isset( $wp_roles->roles ) ? $wp_roles->roles : array();
3319 $editable_roles = apply_filters( 'editable_roles', $all_roles );
3320
3321 return $editable_roles;
3322 }
3323
3324
3325 /**
3326 * Retrive all the product tags
3327 *
3328 * @return array
3329 * @since 2.4.11
3330 */
3331 public static function get_all_tags() {
3332 $term_array = array();
3333 if ( self::is_wc_active() ) {
3334 $terms = get_terms( 'product_tag' );
3335 if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
3336 foreach ( $terms as $term ) {
3337
3338 $term_array[] = array(
3339 'id' => isset( $term->term_id ) ? $term->term_id : '',
3340 'name' => isset( $term->name ) ? $term->name : '',
3341 );
3342 }
3343 }
3344 }
3345 return $term_array;
3346 }
3347
3348 /**
3349 * Get all shiiping classes
3350 *
3351 * @return array
3352 * @since 2.4.10
3353 */
3354 public static function get_shipping_classes() {
3355
3356 if ( self::is_wc_active() ) {
3357 $shipping_classes = WC()->shipping()->get_shipping_classes();
3358 if ( $shipping_classes ) {
3359 return $shipping_classes;
3360 }
3361 }
3362 return array();
3363 }
3364
3365
3366 /**
3367 * Maybe global funnel
3368 *
3369 * @param String
3370 *
3371 * @return String
3372 *
3373 * @since 2.4.14
3374 */
3375 public static function maybe_global_funnel( $step_id = '' ) {
3376 if ( $step_id ) {
3377 $funnel_id = get_post_meta( $step_id, '_funnel_id', true );
3378 $is_gbf = get_post_meta( $funnel_id, 'is_global_funnel', true );
3379 if ( $is_gbf ) {
3380 return $is_gbf;
3381 }
3382 }
3383 return 'no';
3384 }
3385
3386
3387 /**
3388 * Get Woocommerce billing/shipping countries
3389 *
3390 * @return array
3391 * @since 2.4.18
3392 */
3393 public static function get_countries() {
3394 if ( self::is_wc_active() ) {
3395 $wc_countries = new \WC_Countries();
3396 $countries = $wc_countries->get_countries();
3397 if ( $countries ) {
3398 return $countries;
3399 }
3400 }
3401 return array();
3402 }
3403
3404
3405 /**
3406 * Check the checkout layout is express or not
3407 *
3408 * @return Bool
3409 * @since 2.4.18
3410 */
3411 public static function maybe_express_checkout( $checkout_id = '' ) {
3412 if ( ! $checkout_id ) {
3413 global $post;
3414 if ( ! $post ) {
3415 return false;
3416 }
3417 if ( wp_doing_ajax() || isset( $_GET['wc-ajax'] ) ) {
3418 $checkout_id = self::get_checkout_id_from_post( $_POST );
3419 } else {
3420 $checkout_id = $post->ID;
3421 }
3422 if ( ! $checkout_id ) {
3423 $checkout_id = get_the_ID();
3424 }
3425 }
3426
3427 $layout = get_post_meta( $checkout_id, '_wpfnl_checkout_layout', true );
3428
3429 return 'wpfnl-express-checkout' === $layout;
3430 }
3431
3432
3433
3434 /**
3435 * Sanitize request data
3436 *
3437 * @param $data
3438 *
3439 * @return Mix
3440 */
3441 public static function get_sanitized_get_post( $data = array() ) {
3442 if ( is_array( $data ) && ! empty( $data ) ) {
3443 return filter_var_array( $data, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
3444 }
3445 return array(
3446 'get' => filter_input_array( INPUT_GET, FILTER_SANITIZE_FULL_SPECIAL_CHARS ),
3447 'post' => filter_input_array( INPUT_POST, FILTER_SANITIZE_FULL_SPECIAL_CHARS ),
3448 'request' => filter_var_array( $_REQUEST, FILTER_SANITIZE_FULL_SPECIAL_CHARS ),
3449 );
3450 }
3451
3452
3453
3454 /**
3455 * Update funnel type to lead
3456 *
3457 * @since 2.5.1
3458 */
3459 public static function update_funnel_type_to_lead() {
3460
3461 $saved_settings = self::get_admin_settings( '_wpfunnels_general_settings' );
3462 if ( isset( $saved_settings['funnel_type'] ) ) {
3463 $saved_settings['funnel_type'] = 'lead';
3464 update_option( '_wpfunnels_general_settings', $saved_settings );
3465 }
3466 }
3467
3468
3469 /**
3470 * Delete template transients
3471 *
3472 * @since 2.5.1
3473 */
3474 public static function wpfnl_delete_transient() {
3475 delete_option( WPFNL_TEMPLATES_OPTION_KEY . '_wc' );
3476 delete_option( WPFNL_TEMPLATES_OPTION_KEY . '_lms' );
3477 delete_option( WPFNL_TEMPLATES_OPTION_KEY . '_lead' );
3478
3479 delete_transient( 'wpfunnels_remote_template_data_wc_' . WPFNL_VERSION );
3480 delete_transient( 'wpfunnels_remote_template_data_lms_' . WPFNL_VERSION );
3481 delete_transient( 'wpfunnels_remote_template_data_lead_' . WPFNL_VERSION );
3482 delete_transient( 'wpfunnels_remote_template_data_store_checkout_' . WPFNL_VERSION );
3483 }
3484
3485
3486 /**
3487 * Get all the published funnel lists
3488 *
3489 * @return Array $funnels
3490 * @since 2.5.6
3491 */
3492 public static function get_funnel_list() {
3493 $funnels = get_posts(
3494 array(
3495 'post_type' => WPFNL_FUNNELS_POST_TYPE,
3496 'post_status' => 'publish',
3497 'numberposts' => -1,
3498 'fields' => 'ID',
3499 'orderby' => 'date',
3500 'order' => 'ASC',
3501 )
3502 );
3503 $formatted_funnel_data[' '] = __( 'Select funnel', 'wpfnl' );
3504 if ( is_array( $funnels ) ) {
3505 foreach ( $funnels as $funnel ) {
3506 $is_gbf = get_post_meta( $funnel->ID, 'is_global_funnel', true );
3507 if ( ! $is_gbf || 'no' === $is_gbf ) {
3508 $formatted_funnel_data[ self::get_funnel_link( $funnel->ID ) ] = $funnel->post_title;
3509 }
3510 }
3511 }
3512 return $formatted_funnel_data;
3513 }
3514
3515 /**
3516 * Check the log status is enabled or not
3517 *
3518 * @return Bool
3519 * @since 2.5.9
3520 */
3521 public static function maybe_logger_enabled() {
3522 $general_settings = get_option( '_wpfunnels_general_settings' );
3523 if ( $general_settings && isset( $general_settings['enable_log_status'] ) ) {
3524 return 'on' === $general_settings['enable_log_status'];
3525 }
3526 return false;
3527 }
3528
3529
3530
3531 /**
3532 * Check user permission to allow WPFunnels
3533 *
3534 * @param Array $permitted_role
3535 *
3536 * @return String $role
3537 * @since 2.6.2
3538 */
3539 public static function role_permission_to_allow_wpfunnel( $permitted_role = array() ) {
3540 return 'wpf_manage_funnels';
3541 }
3542
3543
3544 /**
3545 * Get supported step type
3546 * Landing, Checkout, Thankyou steps are supported for free
3547 *
3548 * @return Array $types
3549 * @since 2.5.7
3550 */
3551 public static function get_supported_step_type() {
3552 $types = array(
3553 array(
3554 'type' => 'landing',
3555 'name' => 'Landing',
3556 ),
3557 array(
3558 'type' => 'checkout',
3559 'name' => 'Checkout',
3560 ),
3561 array(
3562 'type' => 'upsell',
3563 'name' => 'Upsell',
3564 ),
3565 array(
3566 'type' => 'thankyou',
3567 'name' => 'Thankyou',
3568 ),
3569 );
3570
3571 return apply_filters( 'wpfunnels/supported_step_type', $types );
3572 }
3573
3574
3575 /**
3576 * Check if mint crm is installed
3577 *
3578 * @return bool
3579 * @since 1.0.0
3580 */
3581 public static function is_mint_mrm_active() {
3582 return apply_filters( 'is_mail_mint_pro_active', false );
3583 }
3584
3585 public static function is_mail_mint_free_active() {
3586 if ( is_plugin_active( 'mail-mint/mail-mint.php' ) ) {
3587 return true;
3588 }
3589 return false;
3590 }
3591
3592 public static function is_mail_mint_pro_license_active() {
3593 return apply_filters( 'is_mail_mint_pro_license_active', false );
3594 }
3595
3596 /**
3597 * Check if mint automation exist for a funnel by funnel id.
3598 *
3599 * @param int $funnel_id
3600 * @return bool
3601 * @since 1.0.0
3602 */
3603 public static function maybe_automation_exist_for_a_funnel( $funnel_id ) {
3604 return apply_filters( 'wpfunnels/maybe_automation_exist_for_a_funnel', false, $funnel_id );
3605 }
3606
3607 /**
3608 * Retrieve all automations
3609 *
3610 * @param int $page Current page number
3611 * @param int $per_page Number of items per page
3612 * @return array Array of automation data with pagination info
3613 * @since 3.7.0
3614 */
3615 public static function retrieve_all_automations( $page = 1, $per_page = 10 ){
3616 return apply_filters( 'wpfunnels/retrieve_all_automations', array(
3617 'data' => array(),
3618 'total' => 0,
3619 'total_pages' => 0,
3620 'current_page' => $page,
3621 'per_page' => $per_page,
3622 ), $page, $per_page );
3623 }
3624
3625 /**
3626 * Supported steps for allow settings
3627 *
3628 * @return array
3629 * @since 2.6.6
3630 */
3631 public static function get_settings_steps() {
3632 $settings_steps = array(
3633 'upsell',
3634 'downsell',
3635 'checkout',
3636 'thankyou',
3637 'delay',
3638 'sendMail',
3639 'createUser',
3640 'addTag',
3641 'addList',
3642 'removeTag',
3643 'removeList',
3644 'twilioSendMessage',
3645 'wpf_optin_submit',
3646 'wpf_order_placed',
3647 'wpf_cta_triggered',
3648 'wpf_order_bump_accepted',
3649 'wpf_offer_trigger',
3650 );
3651
3652 return apply_filters( 'wpfunnels/supported_settings_steps', $settings_steps );
3653 }
3654
3655
3656 /**
3657 * Supported steps for allow only settings
3658 *
3659 * @return array
3660 * @since 2.6.6
3661 */
3662 public static function get_only_settings_steps() {
3663 $settings_steps = array(
3664 'delay',
3665 'sendMail',
3666 'createUser',
3667 'addTag',
3668 'addList',
3669 'removeTag',
3670 'removeList',
3671 'twilioSendMessage',
3672 'wpf_optin_submit',
3673 'wpf_order_placed',
3674 'wpf_cta_triggered',
3675 'wpf_order_bump_accepted',
3676 'wpf_offer_trigger',
3677 );
3678
3679 return apply_filters( 'wpfunnels/supported_only_settings_steps', $settings_steps );
3680 }
3681
3682
3683 /**
3684 *
3685 */
3686 public static function get_default_automation() {
3687 $automation_data = array(
3688 'name' => 'automation-1',
3689 'status' => 'active',
3690 'author' => 1,
3691 'trigger_name' => 'wpf-offer-trigger',
3692 'steps' => array(
3693 array(
3694 'step_id' => 'mg3e1i',
3695 'key' => 'wp_user_login',
3696 'type' => 'trigger',
3697 'settings' => array(),
3698 'next_step_id' => 'zytv4',
3699 ),
3700 array(
3701 'step_id' => 'zytv4',
3702 'key' => 'createUser',
3703 'type' => 'action',
3704 'settings' => array(),
3705 'next_step_id' => '9w8vjh',
3706 ),
3707 array(
3708 'step_id' => '9w8vjh',
3709 'key' => 'addTag',
3710 'type' => 'action',
3711 'settings' => array(
3712 'tag_settings' => array(
3713 'tags' => array(
3714 array(
3715 'id' => 3,
3716 'title' => 'demo',
3717 ),
3718 ),
3719 ),
3720 ),
3721 'next_step_id' => '',
3722 ),
3723 ),
3724 'created_ago' => '3 seconds',
3725 );
3726
3727 return $automation_data;
3728 }
3729
3730
3731 /**
3732 * Get mint triggers
3733 *
3734 * @return array
3735 */
3736 public static function get_mint_triggers() {
3737 $triggers = array(
3738 'wpf_optin_submit',
3739 'wpf_order_placed',
3740 'wpf_cta_triggered',
3741 'wpf_order_bump_accepted',
3742 'wpf_offer_trigger',
3743 );
3744
3745 return apply_filters( 'wpfunnels/mint_triggers', $triggers );
3746 }
3747
3748
3749 /**
3750 * Get mint actions
3751 *
3752 * @return array
3753 */
3754 public static function get_mint_actions() {
3755 $actions = array(
3756 'delay',
3757 'sendMail',
3758 'createUser',
3759 'addTag',
3760 'addList',
3761 'removeTag',
3762 'removeList',
3763 'twilioSendMessage',
3764 );
3765
3766 return apply_filters( 'wpfunnels/mint_actions', $actions );
3767 }
3768
3769
3770 /**
3771 * Get all tags/list from mint
3772 *
3773 * @return array
3774 */
3775 public static function get_mint_contact_groups( $type = 'tags' ) {
3776 $class_name = 'Mint\\MRM\\DataBase\\Models\\ContactGroupModel';
3777 if ( class_exists( $class_name ) ) {
3778
3779 $groups = $class_name::get_all_lists_or_tags( $type );
3780 if ( $groups && is_array( $groups ) ) {
3781 $formatted_groups = array();
3782 $i = 1;
3783 foreach ( $groups as $group ) {
3784 $data = array(
3785 'index' => $i,
3786 'title' => $group['title'],
3787 'id' => $group['id'],
3788 'checked' => false,
3789 );
3790 array_push( $formatted_groups, $data );
3791 $i++;
3792 }
3793
3794 return $formatted_groups;
3795 }
3796 }
3797 return array();
3798 }
3799
3800
3801 /**
3802 * Get selected steps
3803 *
3804 * @param string $type
3805 * @param int $funnel_id
3806 *
3807 * @return array
3808 */
3809 public static function get_selected_steps( $type, $funnel_id ) {
3810 if ( $funnel_id ) {
3811 $steps = get_post_meta( $funnel_id, '_steps', true );
3812 if ( $steps && is_array( $steps ) ) {
3813 $i = 1;
3814 $formatted_steps = array();
3815 $step_types = array( $type );
3816 if ( 'landing' == $type ) {
3817 $step_types = array( $type, 'custom' );
3818 }
3819 foreach ( $steps as $step ) {
3820 if ( in_array( $step['step_type'], $step_types ) ) {
3821 $data = array(
3822 'id' => $i,
3823 'title' => $step['name'],
3824 'value' => $step['id'],
3825 );
3826 array_push( $formatted_steps, $data );
3827 $i++;
3828 }
3829 }
3830
3831 return $formatted_steps;
3832 }
3833 }
3834 return array();
3835 }
3836
3837
3838 /**
3839 * Get all order bump of a funnel
3840 *
3841 * @param int $funnel_id
3842 *
3843 * @return array
3844 */
3845 public static function get_all_ob( $funnel_id ) {
3846 if ( $funnel_id ) {
3847 $steps = self::get_steps( $funnel_id );
3848 if ( $steps && is_array( $steps ) ) {
3849 $key = array_search( 'checkout', array_column( $steps, 'step_type' ) );
3850 if ( false !== $key ) {
3851 if ( isset( $steps[ $key ]['id'] ) ) {
3852 $checkout_step_id = $steps[ $key ]['id'];
3853 $order_bumps = get_post_meta( $checkout_step_id, 'order-bump-settings', true );
3854 if ( $order_bumps && is_array( $order_bumps ) ) {
3855 $i = 1;
3856 $formatted_obs = array();
3857 foreach ( $order_bumps as $index => $ob ) {
3858 if ( is_array( $ob ) && isset( $ob['name'] ) ) {
3859 $data = array(
3860 'id' => $i,
3861 'title' => $ob['name'],
3862 'value' => $index,
3863 );
3864 array_push( $formatted_obs, $data );
3865 $i++;
3866 }
3867 }
3868 return $formatted_obs;
3869 }
3870 }
3871 }
3872 }
3873 }
3874 return array();
3875 }
3876
3877
3878
3879 /**
3880 * Get sequences from campaign table
3881 *
3882 * @return array
3883 * @since 1.0.0
3884 */
3885 public static function get_sequences() {
3886 if ( self::is_mint_mrm_active() && class_exists( 'Mint\\MRM\\DataBase\\Tables\\CampaignSchema' ) ) {
3887
3888 global $wpdb;
3889 $class_name = 'Mint\\MRM\\DataBase\\Tables\\CampaignSchema';
3890 $campaign_table = $wpdb->prefix . $class_name::$campaign_table;
3891 $results = $wpdb->get_results( $wpdb->prepare( "SELECT id as value, title as label FROM $campaign_table WHERE type = %s AND status = %s ", 'automation', 'created' ), ARRAY_A ); // phpcs:ignore.
3892 $default = array(
3893 array(
3894 'value' => '',
3895 'label' => 'Select Sequence',
3896 ),
3897 );
3898 if ( ! empty( $results ) ) {
3899 return array_merge( $default, $results );
3900 } else {
3901 return $default;
3902 }
3903 }
3904 return array();
3905 }
3906
3907
3908 /**
3909 * Get CFE deafult fields
3910 *
3911 * @return array
3912 * @since 2.7.0
3913 */
3914 public static function get_cfe_default_fields() {
3915 if ( self::is_wc_active() ) {
3916 $fields = array();
3917 $billing_default_field = WC()->countries->get_address_fields();
3918 foreach ( $billing_default_field as $key => $sf ) {
3919 $billing_default_field[ $key ]['enable'] = 1;
3920 $billing_default_field[ $key ]['show'] = 1;
3921 $billing_default_field[ $key ]['delete'] = 0;
3922 if ( ! ( isset( $sf['type'] ) ) ) {
3923 $billing_default_field[ $key ]['type'] = null;
3924 }
3925 if ( ! ( isset( $sf['placeholder'] ) ) ) {
3926 $billing_default_field[ $key ]['placeholder'] = null;
3927 }
3928 if ( ! ( isset( $sf['label'] ) ) ) {
3929 $billing_default_field[ $key ]['label'] = null;
3930 }
3931 if ( ! ( isset( $sf['validate'] ) ) ) {
3932 $billing_default_field[ $key ]['validate'] = null;
3933 }
3934 if ( ! ( isset( $sf['default'] ) ) ) {
3935 $billing_default_field[ $key ]['default'] = null;
3936 }
3937 }
3938
3939 $countries = new \WC_Countries();
3940 $shipping_default_fields = $countries->get_address_fields( $countries->get_base_country(), 'shipping_' );
3941 foreach ( $shipping_default_fields as $key => $sf ) {
3942 $shipping_default_fields[ $key ]['enable'] = 1;
3943 $shipping_default_fields[ $key ]['show'] = 1;
3944 $shipping_default_fields[ $key ]['delete'] = 0;
3945 if ( ! ( isset( $sf['type'] ) ) ) {
3946 $shipping_default_fields[ $key ]['type'] = null;
3947 }
3948 if ( ! ( isset( $sf['placeholder'] ) ) ) {
3949 $shipping_default_fields[ $key ]['placeholder'] = null;
3950 }
3951 if ( ! ( isset( $sf['label'] ) ) ) {
3952 $shipping_default_fields[ $key ]['label'] = null;
3953 }
3954 if ( ! ( isset( $sf['validate'] ) ) ) {
3955 $shipping_default_fields[ $key ]['validate'] = null;
3956 }
3957 if ( ! ( isset( $sf['default'] ) ) ) {
3958 $shipping_default_fields[ $key ]['default'] = null;
3959 }
3960 }
3961
3962 $order_comment = array(
3963 'type' => 'textarea',
3964 'class' => array( 'notes' ),
3965 'label' => __( 'Order notes', 'woocommerce' ),
3966 'placeholder' => esc_attr__(
3967 'Notes about your order, e.g. special notes for delivery.',
3968 'woocommerce'
3969 ),
3970 'name' => 'order_comments',
3971 'required' => false,
3972 'enable' => 1,
3973 'show' => 1,
3974 'default' => null,
3975 'validate' => null,
3976 'delete' => 0,
3977 );
3978
3979 $additional_default_fields = array(
3980 'order_comments' => $order_comment,
3981 );
3982
3983 $fields = array(
3984 'billing' => $billing_default_field,
3985 'shipping' => $shipping_default_fields,
3986 'additional' => $additional_default_fields,
3987 );
3988 return $fields;
3989 }
3990 }
3991
3992 /**
3993 * Generates and updates the first step id of the funnel
3994 *
3995 * @param int|string $funnel_id Funnel id.
3996 * @since 2.8.0
3997 */
3998 public static function generate_first_step( $funnel_id, $steps = array() ) {
3999 if ( $funnel_id ) {
4000 $steps = ! empty( $steps ) ? $steps : get_post_meta( $funnel_id, '_steps', true ); // Get step order
4001
4002 if ( empty( $steps ) ) {
4003 $steps = get_post_meta( $funnel_id, '_steps_order', true ); // Get step order
4004 }
4005 $priority_order = array( 'landing', 'custom', 'checkout', 'upsell', 'downsell', 'thankyou' );
4006 $first_step = array(
4007 'id' => null,
4008 'step_type' => '',
4009 );
4010
4011 if ( is_array( $steps ) && ! empty( $steps ) ) {
4012 foreach ( $steps as $step ) {
4013 if (
4014 isset( $step['step_type'], $step['id'] )
4015 && ( ! $first_step['id'] || ( array_search( $step['step_type'], $priority_order ) < array_search( $first_step['step_type'], $priority_order ) ) )
4016 ) {
4017 $first_step['id'] = $step['id'];
4018 $first_step['step_type'] = $step['step_type'];
4019 }
4020 }
4021 self::update_funnel_first_step( $funnel_id, $first_step['id'] );
4022 }
4023 }
4024 }
4025
4026 /**
4027 * Get the first step of a funnel
4028 *
4029 * @param int|string $funnel_id Funnel id.
4030 * @return string|int
4031 * @since 2.8.0
4032 */
4033 public static function get_first_step( $funnel_id ) {
4034 return get_post_meta( $funnel_id, '_first_step', true );
4035 }
4036
4037 /**
4038 * Updates the first step id of the funnel
4039 *
4040 * @param int|string $funnel_id Funnel id.
4041 * @param int|string $first_step_id First step id of the funnel.
4042 * @return void
4043 */
4044 public static function update_funnel_first_step( $funnel_id, $first_step_id ) {
4045 update_post_meta( $funnel_id, '_first_step', $first_step_id );
4046 }
4047
4048
4049 /**
4050 * Mail mint email
4051 *
4052 * @return mix
4053 */
4054 public static function get_mailmint_email() {
4055 if ( self::is_mint_mrm_active() ) {
4056 $email_settings = get_option( '_mrm_email_settings' );
4057 if ( isset( $email_settings['from_email'] ) ) {
4058 return $email_settings['from_email'];
4059 }
4060 }
4061 return false;
4062 }
4063
4064
4065 /**
4066 * Mail mint email
4067 *
4068 * @return mix
4069 */
4070 public static function get_mailmint_email_settings() {
4071 if ( self::is_mint_mrm_active() ) {
4072 return get_option( '_mrm_email_settings', \Mint\MRM\Utilites\Helper\Email::default_email_settings() );
4073 }
4074 return false;
4075 }
4076
4077
4078 /**
4079 * Mail mint email
4080 *
4081 * @return mix
4082 */
4083 public static function get_mailmint_twillo_settings() {
4084 if ( self::is_mint_mrm_active() ) {
4085 $settings = get_option( '_mint_integration_settings', array() );
4086 $twilio = isset( $settings['twilio'] ) ? $settings['twilio'] : array();
4087 return is_array( $twilio ) && ! empty( $twilio ) ? $twilio : array(
4088 'account_sid' => '',
4089 'auth_token' => '',
4090 'from_number' => '',
4091 'is_integrated' => false,
4092 );
4093 }
4094 return false;
4095 }
4096
4097
4098 /**
4099 * Maybe duplicate step
4100 *
4101 * @param $step_id Step ID
4102 *
4103 * @since 2.7.7
4104 * @return bool
4105 */
4106 public static function maybe_duplicate_step( $step_id ) {
4107
4108 if ( $step_id ) {
4109 $is_duplicate = get_post_meta( $step_id, '_is_duplicate', true );
4110 if ( 'yes' === $is_duplicate ) {
4111 return true;
4112 }
4113 }
4114 return false;
4115 }
4116
4117
4118 /**
4119 * Retrieves all created coupons from WooCommerce.
4120 * This function fetches all coupon posts of the 'shop_coupon' post type that are currently published in WooCommerce.
4121 *
4122 * @since 2.7.9
4123 * @return array An array of coupon codes and their corresponding names.
4124 */
4125 public static function get_all_coupons() {
4126 global $wpdb;
4127 $table_name = $wpdb->prefix . 'posts';
4128 $coupon_type = 'shop_coupon';
4129 $post_status = 'publish';
4130 $query = $wpdb->prepare(
4131 "SELECT post_title FROM $table_name WHERE post_type = %s AND post_status = %s ORDER BY post_name ASC LIMIT 100",
4132 $coupon_type,
4133 $post_status
4134 );
4135 $coupon_codes = array();
4136 $coupons = $wpdb->get_col( $query );
4137 if ( is_array( $coupons ) ) {
4138 foreach ( $coupons as $coupon ) {
4139 $coupon_codes[] = array(
4140 'name' => $coupon,
4141 );
4142 }
4143 }
4144 return $coupon_codes;
4145 }
4146
4147
4148 /**
4149 * Retrieves the applied coupon for a given step ID.
4150 * This function checks if a step ID is provided and retrieves the applied coupon associated with that step.
4151 * If no step ID is provided or the applied coupon is not found, it returns false.
4152 *
4153 * @param int $step_id The ID of the step for which to retrieve the applied coupon.
4154 *
4155 * @since 2.7.9
4156 * @return string|false The selected coupon code if found and auto-coupon feature is
4157 */
4158 public static function get_applied_coupon( $step_id ) {
4159 if ( ! $step_id ) {
4160 return false;
4161 }
4162 $auto_coupon = get_post_meta( $step_id, '_wpfnl_checkout_auto_coupon', true );
4163 if ( isset( $auto_coupon['enableAutoCoupon'], $auto_coupon['selectedCoupon'] ) && 'yes' === $auto_coupon['enableAutoCoupon'] && $auto_coupon['selectedCoupon'] ) {
4164 return $auto_coupon['selectedCoupon'];
4165 }
4166 return false;
4167 }
4168
4169 /**
4170 * Get step IDs for a given funnel.
4171 *
4172 * Retrieves an array of step IDs associated with a specific funnel, including A/B testing variations.
4173 *
4174 * @param int $funnel_id The ID of the funnel.
4175 * @return array An array of step IDs.
4176 * @since 2.7.10
4177 */
4178 public static function get_step_ids( $funnel_id ) {
4179 $steps = self::get_steps( $funnel_id );
4180
4181 $all_steps_including_ab = array();
4182 $all_steps_including_ab_ids = array();
4183 if ( is_array( $steps ) ) {
4184 foreach ( $steps as $step ) {
4185 if ( isset( $step['id'] ) ) {
4186 $all_steps_including_ab = get_post_meta( $step['id'], '_wpfnl_ab_testing_start_settings', true );
4187 if ( isset( $all_steps_including_ab['variations'] ) ) {
4188 $ids = array_column( $all_steps_including_ab['variations'], 'id' );
4189 $all_steps_including_ab_ids = array_merge( $all_steps_including_ab_ids, $ids );
4190 }
4191 }
4192 }
4193 }
4194
4195 return $all_steps_including_ab_ids;
4196 }
4197
4198 /**
4199 * Validates the order owner's authorization.
4200 *
4201 * @param WC_Order $order The order object to validate.
4202 * @return bool
4203 * @since 2.7.13
4204 */
4205 public static function is_valid_order_owner( $order, $order_key = '' ) {
4206
4207 if ( ! self::is_wc_active() || false === is_a( $order, 'WC_Order' ) ) {
4208 return false;
4209 }
4210
4211 // Validate via order_key when provided. The key is cryptographically random
4212 // and unpredictable, so possession of it proves the requester is the legitimate
4213 // order owner. This covers guest checkout and Express Checkout (Google Pay /
4214 // Apple Pay) in incognito mode where no WP user session or WC session exists.
4215 if ( ! empty( $order_key ) && $order->key_is_valid( $order_key ) ) {
4216 return true;
4217 }
4218
4219 // If it's not a guest order, current user can pay but only if it's their own order,
4220 // or they are an admin or shop manager or they are in a same session(for guest).
4221
4222 $order_has_customer = ! empty( $order->get_customer_id() );
4223 $is_authorized_owner = false;
4224
4225 $has_owner = ! empty( WC()->session->get( 'wpfnl_order_owner' ) );
4226
4227 if ( $order_has_customer ) {
4228 $is_order_owned_by_current_user = $order->get_customer_id() === get_current_user_id();
4229
4230 $is_authorized_owner = $is_order_owned_by_current_user || current_user_can( 'manage_woocommerce' );
4231 }
4232
4233 if ( ! $is_authorized_owner && ! $has_owner ) {
4234 return false;
4235 }
4236 return true;
4237 }
4238
4239
4240 /**
4241 * Capture a screenshot of a webpage given its URL and save it locally.
4242 *
4243 * @param string $url The URL of the webpage to capture the screenshot.
4244 * @param int $step_id The ID of the step associated with the screenshot.
4245 *
4246 * @return string The full URL of the captured screenshot if successful, an empty string otherwise.
4247 *
4248 * @since 2.9.0
4249 */
4250 public static function get_screenshot_from_url( $url, $step_id ) {
4251 try {
4252 $file_name = uniqid();
4253 $path = wp_upload_dir();
4254 $baseurl = $path['baseurl'] . '/wpfunnels';
4255 $path = $path['basedir'] . '/wpfunnels';
4256 $default_file = $path . '/index.php';
4257 if ( ! file_exists( $path ) ) {
4258 wp_mkdir_p( $path );
4259 }
4260
4261 if ( ! file_exists( $default_file ) ) {
4262 $file_handle = @fopen( trailingslashit( $path ) . 'index.php', 'wb' );
4263 if ( $file_handle ) {
4264 fwrite( $file_handle, $file['content'] );
4265 fclose( $file_handle );
4266 }
4267 }
4268
4269 \Spatie\Browsershot\Browsershot::url( $url )
4270 ->waitUntilNetworkIdle()
4271 ->windowSize( 1280, 800 )
4272 ->ignoreHttpsErrors()
4273 ->save( $screenshotPath );
4274
4275 // Update the post meta with the screenshot's base URL.
4276 update_post_meta( $step_id, 'wpfnl_step_screenshot_baseurl', $path . '/' . $file_name . '.png' );
4277
4278 // Return the full URL of the captured screenshot.
4279 return $baseurl . '/' . $file_name . '.png';
4280 } catch ( Exception $e ) {
4281 return '';
4282 }
4283 }
4284
4285
4286 /**
4287 * Update the screenshot URL for a step by its ID.
4288 *
4289 * @param int $step_id The ID of the step.
4290 * @param string $url The new screenshot URL to be set.
4291 *
4292 * @return bool True if the update is successful, false otherwise.
4293 *
4294 * @since 2.9.0
4295 */
4296 public static function maybe_update_screenshot_url_by_step_id( $step_id, $url ) {
4297 if ( ! $step_id || ! $url ) {
4298 return false;
4299 }
4300
4301 // Update the post meta for the step with the new screenshot URL.
4302 update_post_meta( $step_id, 'wpfnl_step_screenshot_url', $url );
4303
4304 return true;
4305 }
4306
4307
4308 /**
4309 * Update the modified date of a step's screenshot identified by its ID.
4310 *
4311 * @param int $step_id The ID of the step.
4312 *
4313 * @return bool True on success, false on failure.
4314 *
4315 * @since 2.9.0
4316 */
4317 public static function maybe_update_screenshot_modified_date_by_step_id( $step_id ) {
4318 if ( ! $step_id ) {
4319 return false;
4320 }
4321 if ( ! self::get_screenshot_url_by_step_id( $step_id ) ) {
4322 return false;
4323 }
4324 // Update the post meta for the step with the new screenshot modified date.
4325 update_post_meta( $step_id, 'wpfnl_step_screenshot_modified_date', date( 'Y-m-d H:i:s' ) );
4326
4327 return true;
4328 }
4329
4330
4331 /**
4332 * Get the screenshot URL for a step by its ID.
4333 *
4334 * @param int $step_id The ID of the step.
4335 *
4336 * @return string|false The screenshot URL if found, false if not found.
4337 *
4338 * @since 2.9.0
4339 */
4340 public static function get_screenshot_url_by_step_id( $step_id ) {
4341 if ( ! $step_id ) {
4342 return false;
4343 }
4344
4345 // Get the post meta for the step's screenshot URL.
4346 $url = get_post_meta( $step_id, 'wpfnl_step_screenshot_url', true );
4347
4348 if ( ! $url ) {
4349 return false;
4350 }
4351
4352 return $url;
4353 }
4354
4355
4356 /**
4357 * Get the modified date of a step identified by its ID.
4358 *
4359 * @param int $step_id The ID of the step.
4360 *
4361 * @return string|bool The modified date in 'Y-m-d H:i:s' format on success, false on failure.
4362 *
4363 * @since 2.9.0
4364 */
4365 public static function get_screenshot_modified_date_by_step_id( $step_id ) {
4366 if ( ! $step_id ) {
4367 return false;
4368 }
4369
4370 // Get the post meta for the step's screenshot modified date.
4371 $date = get_post_meta( $step_id, 'wpfnl_step_screenshot_modified_date', true );
4372
4373 if ( ! $date ) {
4374 return false;
4375 }
4376
4377 return $date;
4378 }
4379
4380
4381 /**
4382 * Get the modified date of a step by its ID.
4383 *
4384 * @param int $step_id The ID of the step.
4385 *
4386 * @return string|false The modified date if found, false if not found.
4387 *
4388 * @since 2.9.0
4389 */
4390 public static function get_the_modified_date_of_step( $step_id ) {
4391 if ( ! $step_id ) {
4392 return false;
4393 }
4394
4395 // Get the modified date of the step.
4396 $date = get_the_modified_date( 'Y-m-d H:i:s', $step_id );
4397
4398 if ( ! $date ) {
4399 return false;
4400 }
4401
4402 return $date;
4403 }
4404
4405
4406 /**
4407 * Delete the screenshot file associated with a step identified by its ID.
4408 *
4409 * @param int $step_id The ID of the step.
4410 *
4411 * @return bool True if the screenshot file is deleted successfully, false otherwise.
4412 */
4413 public static function maybe_delete_screenshot_by_step_id( $step_id ) {
4414
4415 if ( ! $step_id ) {
4416 return false;
4417 }
4418
4419 // Update the screenshot URL to get the file path.
4420 $path = get_post_meta( $step_id, 'wpfnl_step_screenshot_baseurl', true );
4421
4422 // Check if the file exists and is readable.
4423 if ( ! file_exists( $path ) ) {
4424 return false;
4425 }
4426
4427 // Try to delete the file.
4428 unlink( $path );
4429 return true;
4430 }
4431
4432
4433 /**
4434 * Get the URL of a Step with Optional UTM Parameters.
4435 *
4436 * This function retrieves the URL of a specified step within a funnel. It takes a funnel ID
4437 * and a step ID as input and returns the URL of the step. If UTM tracking is enabled for the
4438 * funnel, this function appends the UTM parameters to the URL as query parameters.
4439 *
4440 * @since 1.6.0
4441 *
4442 * @param int $funnel_id The ID of the funnel.
4443 * @param int $step_id The ID of the step whose URL needs to be retrieved.
4444 *
4445 * @return string|false The URL of the step with optional UTM parameters or false if the URL is not found.
4446 */
4447 public static function get_step_url( $funnel_id, $step_id ) {
4448 if ( ! $funnel_id || ! $step_id ) {
4449 return false;
4450 }
4451
4452 $url = get_the_permalink( $step_id );
4453
4454 if ( ! $url ) {
4455 return false;
4456 }
4457
4458 // Get UTM settings for the funnel.
4459 $utm_settings = self::get_funnel_utm_settings( $funnel_id );
4460
4461 // Check if UTM parameters are enabled.
4462 if ( ! is_array( $utm_settings ) || ! isset( $utm_settings['utm_enable'] ) || 'on' !== $utm_settings['utm_enable'] ) {
4463 return $url;
4464 }
4465
4466 unset( $utm_settings['utm_enable'] );
4467 $url = add_query_arg( $utm_settings, $url );
4468 $url = strtolower( $url );
4469
4470 return $url;
4471 }
4472
4473
4474 /**
4475 * Check if conditional logic is enabled for a specific step.
4476 *
4477 * This function checks whether the conditional logic feature is enabled for a given step
4478 * by checking the '_wpfnl_maybe_enable_condition' post meta value associated with the step.
4479 * If the value is 'yes', then the conditional logic is considered enabled; otherwise, it is not.
4480 *
4481 * @param int $step_id The ID of the step to check for conditional logic.
4482 * @return bool True if conditional logic is enabled, false otherwise.
4483 *
4484 * @since 2.9.0
4485 */
4486 public static function maybe_enable_condition( $step_id ) {
4487 if ( ! $step_id ) {
4488 return false;
4489 }
4490
4491 $is_enabled = get_post_meta( $step_id, '_wpfnl_maybe_enable_condition', true );
4492 if ( 'yes' !== $is_enabled ) {
4493 return false;
4494 }
4495
4496 return true;
4497 }
4498
4499
4500 /**
4501 * Get the conditions associated with a specific step.
4502 *
4503 * This function retrieves the conditions linked to a given step by querying the '_wpfnl_step_conditions'
4504 * post meta associated with that step. If conditions are found and are in an array format, they are returned;
4505 * otherwise, false is returned to indicate that no conditions or invalid conditions are available.
4506 *
4507 * @param int $step_id The ID of the step to retrieve conditions for.
4508 * @return array|false An array of conditions if available and valid, false otherwise.
4509 *
4510 * @since 2.9.0
4511 */
4512 public static function get_conditions( $step_id ) {
4513 if ( ! $step_id ) {
4514 return false;
4515 }
4516
4517 $conditions = get_post_meta( $step_id, '_wpfnl_step_conditions', true );
4518 if ( empty( $conditions ) || ! is_array( $conditions ) ) {
4519 return false;
4520 }
4521
4522 return $conditions;
4523
4524 }
4525
4526
4527 /**
4528 * Get the next step to be executed conditionally after the current step.
4529 *
4530 * This function retrieves the next step that should be executed after the current step
4531 * if certain conditions are met. The next step is retrieved by querying the '_wpfnl_next_step_after_condition'
4532 * post meta associated with the given step. If a valid array containing the next step information is found,
4533 * it is returned; otherwise, false is returned to indicate that no next step or invalid data is available.
4534 *
4535 * @param int $step_id The ID of the step to retrieve the conditional next step for.
4536 * @return array|false An array containing information about the next step if available and valid, false otherwise.
4537 *
4538 * @since 2.9.0
4539 */
4540 public static function get_conditional_next_step( $step_id ) {
4541 if ( ! $step_id ) {
4542 return false;
4543 }
4544
4545 $next_step = get_post_meta( $step_id, '_wpfnl_next_step_after_condition', true );
4546 if ( empty( $next_step ) || ! is_array( $next_step ) ) {
4547 return false;
4548 }
4549
4550 return $next_step;
4551
4552 }
4553
4554
4555 /**
4556 * Get the next step to be executed based on conditional criteria.
4557 *
4558 * This function determines the next step to be executed in a funnel based on conditional criteria.
4559 * If conditional criteria are not enabled for the step, it retrieves the next step using the
4560 * Wpfnl_functions::get_next_step method. If conditional criteria are enabled, it checks the conditions
4561 * using the WPFunnels\Conditions\Wpfnl_Condition_Checker class. If the conditions match, the true branch's
4562 * next step is returned; otherwise, the false branch's next step is returned.
4563 *
4564 * @param int $funnel_id The ID of the funnel to which the step belongs.
4565 * @param int $step_id The ID of the current step.
4566 * @param string $order Optional. The order in which the step is executed. Default is empty.
4567 * @param string $checker Optional. The checker to be used for condition checking. Default is 'accept'.
4568 * @return bool|array An array containing information about the next step and its type. if funnel id or step id is missing then it return false;
4569 *
4570 * @since 2.9.0
4571 */
4572 public static function get_next_conditional_step( $funnel_id, $step_id, $order = '', $checker = 'accept' ) {
4573
4574 if ( ! $funnel_id || ! $step_id ) {
4575 return false;
4576 }
4577
4578 $is_condition_enable = self::maybe_enable_condition( $step_id );
4579 if ( ! $is_condition_enable ) {
4580 return self::get_next_step( $funnel_id, $step_id );
4581 }
4582 $condition = \WPFunnels\Conditions\Wpfnl_Condition_Checker::getInstance();
4583 $condition_matched = $condition->check_condition( $funnel_id, $order, $step_id, $step_id, $checker );
4584 $conditional_next_step = self::get_conditional_next_step( $step_id );
4585
4586 if ( ! is_array( $conditional_next_step ) || empty( $conditional_next_step ) ) {
4587 return self::get_next_step( $funnel_id, $step_id, $condition_matched );
4588 }
4589
4590 if ( $condition_matched && empty( $conditional_next_step['true'] ) ) {
4591 return self::get_next_step( $funnel_id, $step_id, $condition_matched );
4592 } elseif ( ! $condition_matched && empty( $conditional_next_step['false'] ) ) {
4593 return self::get_next_step( $funnel_id, $step_id, $condition_matched );
4594 }
4595
4596 $next_node = array(
4597 'step_id' => $condition_matched ? $conditional_next_step['true'] : $conditional_next_step['false'],
4598 'step_type' => $condition_matched ? get_post_meta( $conditional_next_step['true'], '_step_type', true ) : get_post_meta( $conditional_next_step['false'], '_step_type', true ),
4599 );
4600
4601 return $next_node;
4602 }
4603
4604
4605
4606 /**
4607 * Update a meta key with a new value for a given step.
4608 *
4609 * @param int $step_id The ID of the step for which to update the meta.
4610 * @param string $meta_key The key of the meta data to update.
4611 * @param mixed $meta_value The new value to set for the meta key.
4612 *
4613 * @return bool True on success, false on failure.
4614 *
4615 * @since 3.0.0
4616 */
4617 public static function update_meta( $step_id, $meta_key, $meta_value ) {
4618 if ( ! $step_id || ! $meta_key ) {
4619 return false;
4620 }
4621 update_post_meta( $step_id, $meta_key, $meta_value );
4622 return true;
4623 }
4624
4625
4626 /**
4627 * Delete a meta key and its associated value for a given step.
4628 *
4629 * @param int $step_id The ID of the step from which to delete the meta.
4630 * @param string $meta_key The key of the meta data to delete.
4631 *
4632 * @return bool True on success, false on failure.
4633 * @since 3.0.0
4634 */
4635 public static function delete_meta( $step_id, $meta_key ) {
4636 if ( ! $step_id || ! $meta_key ) {
4637 return false;
4638 }
4639 delete_post_meta( $step_id, $meta_key );
4640 return true;
4641 }
4642 /**
4643 * Check if the current page is using the Gutenberg editor.
4644 *
4645 * This function checks whether the current page in the WordPress admin area is using the Gutenberg editor. It does so by checking the availability of a specific function associated with Gutenberg. If this function exists, it is likely that the Gutenberg editor is in use.
4646 *
4647 * @return bool True if the current page is using the Gutenberg editor, false otherwise.
4648 * @since 2.8.12
4649 */
4650 private static function is_gutenberg_edit_page() {
4651 if ( function_exists( 'wp_enqueue_script' ) && function_exists( 'wp_enqueue_style' ) ) {
4652 // Check if Gutenberg scripts and styles are enqueued
4653 return wp_script_is( 'wp-blocks' ) && wp_script_is( 'wp-element' ) && wp_style_is( 'wp-edit-blocks' );
4654 }
4655 return false;
4656 }
4657
4658 /**
4659 * Check if the current page is an Elementor edit page.
4660 *
4661 * This function checks whether the current page in the WordPress admin area is an edit page created with the Elementor page builder plugin. It does so by inspecting the 'action' query parameter in the URL. If the 'action' is set to 'elementor', it is likely an edit page created with Elementor.
4662 *
4663 * @return bool True if the current page is an Elementor edit page, false otherwise.
4664 * @since 2.8.12
4665 */
4666 private static function is_elementor_edit_page() {
4667 // Check if the 'action' query parameter is set to 'elementor'.
4668 if ( defined( 'ELEMENTOR_VERSION' ) && \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
4669 return true;
4670 }
4671 return false;
4672 }
4673
4674 /**
4675 * Check if the current page is an edit page created with the Divi Builder.
4676 *
4677 * This function checks whether the current page in the WordPress admin area is an edit page created with the Divi Builder plugin. It does so by checking the availability of specific functions associated with Divi Builder. If these functions exist, it is likely an edit page created with Divi Builder.
4678 *
4679 * @return bool True if the current page is an edit page created with Divi Builder, false otherwise.
4680 * @since 2.8.12
4681 */
4682 private static function is_divi_edit_page() {
4683 // Check if the Divi Builder's assets are enqueued by verifying the presence of specific functions.
4684 return is_callable( 'et_pb_enqueue_main_scripts' ) && is_callable( 'et_theme_builder_overrides' );
4685 }
4686
4687 /**
4688 * Check if the current page is an Oxygen Builder edit page.
4689 *
4690 * This function checks whether the current page in the WordPress admin area is an Oxygen Builder edit page. It does so by verifying if specific Oxygen assets (scripts and styles) are enqueued. If the assets are enqueued, it is likely that the current page is an Oxygen edit page.
4691 *
4692 * @return bool True if the current page is an Oxygen edit page, false otherwise.
4693 * @since 2.8.12
4694 */
4695 private static function is_oxygen_edit_page() {
4696 // Check if Oxygen's assets are enqueued
4697 return wp_script_is( 'ct-automation' ) || wp_style_is( 'ct-select2' ) || 'ct_render_shortcode' === filter_input( INPUT_GET, 'action', FILTER_SANITIZE_SPECIAL_CHARS ) || filter_input( INPUT_GET, 'ct_builder', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) || filter_input( INPUT_GET, 'ct_inner', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
4698 }
4699
4700 /**
4701 * Check if the current page is being edited using a specific page builder or any builder if not specified.
4702 *
4703 * This function checks whether the current page in the WordPress admin area is being edited using a specific page builder, such as Elementor, Divi, Oxygen, or Gutenberg. If no specific builder is provided, it checks for any of the supported page builders.
4704 *
4705 * @param string|null $builder (Optional) The name of the page builder to check (e.g., 'elementor', 'divi', 'oxygen', 'gutenberg'). If not specified, checks for any supported page builder.
4706 * @return bool True if the current page is being edited with the specified page builder or any supported builder, false otherwise.
4707 * @since 2.8.12
4708 */
4709 public static function is_builder_edit_page( $builder = null ) {
4710 switch ( $builder ) {
4711 case 'elementor':
4712 return self::is_elementor_edit_page();
4713 case 'divi':
4714 return self::is_divi_edit_page();
4715 case 'oxygen':
4716 return self::is_oxygen_edit_page();
4717 case 'gutenberg':
4718 return self::is_gutenberg_edit_page();
4719 default:
4720 // Check for any supported page builder.
4721 return self::is_elementor_edit_page() || self::is_gutenberg_edit_page() || self::is_divi_edit_page() || self::is_oxygen_edit_page() || 'edit' === filter_input( INPUT_GET, 'action', FILTER_SANITIZE_SPECIAL_CHARS );
4722 }
4723 }
4724
4725
4726 /**
4727 * Maybe remote funnel
4728 *
4729 * @return bool
4730 * @since 3.0.6
4731 */
4732 public static function maybe_remote_funnel() {
4733 if ( defined( 'REMOTE_FUNNEL_VERSION' ) ) {
4734 return 'yes';
4735 }
4736 return 'no';
4737 }
4738
4739
4740 /**
4741 * Removes a disconnected addstep node from the funnel data.
4742 *
4743 * @param array $funnel_data The funnel data.
4744 * @param int $funnel_id The ID of the funnel.
4745 * @return array The updated funnel data.
4746 */
4747 public static function remove_disconnected_addstep_node( $funnel_data, $funnel_id ) {
4748 if ( ! $funnel_id || ! isset( $funnel_data['drawflow']['Home']['data'] ) || empty( $funnel_data['drawflow']['Home']['data'] ) ) {
4749 return $funnel_data;
4750 }
4751
4752 $count = count( $funnel_data['drawflow']['Home']['data'] );
4753
4754 if ( $count <= 1 ) {
4755 return $funnel_data;
4756 }
4757
4758 // Loop through each step in funnel
4759 foreach ( $funnel_data['drawflow']['Home']['data'] as $key => &$step ) {
4760 // Find corresponding step in step id data
4761 if ( 'addstep' === $step['data']['step_type'] ) {
4762 $maybeEmptyInput = false;
4763 $maybeEmptyOutput = false;
4764 if ( empty( $step['inputs']['input_1']['connections'] ) ) {
4765 $maybeEmptyInput = true;
4766 }
4767
4768 if ( empty( $step['inputs'] ) ) {
4769 $maybeEmptyInput = true;
4770 }
4771
4772 if ( empty( $step['outputs'] ) ) {
4773 $maybeEmptyOutput = true;
4774 }
4775
4776 if ( empty( $step['outputs']['output_1']['connections'] ) ) {
4777 $maybeEmptyOutput = true;
4778 }
4779 if ( $maybeEmptyInput && $maybeEmptyOutput ) {
4780 unset( $funnel_data['drawflow']['Home']['data'][ $key ] );
4781 update_post_meta( $funnel_id, '_funnel_data', $funnel_data );
4782 }
4783 }
4784 }
4785
4786 return $funnel_data;
4787 }
4788
4789
4790 /**
4791 * Get all static text
4792 *
4793 * @return array
4794 * @since 3.0.13
4795 */
4796 public static function get_text() {
4797 return array(
4798 'error_occurred' => __( 'Error Occurred!', 'wpfnl' ),
4799 'stats' => __( 'Stats', 'wpfnl' ),
4800 'stats_tooltip_txt' => __( 'Toggle to get a quick view of visits & conversion of steps right on the canvas.', 'wpfnl' ),
4801 'find_your_templates' => __( 'Find your templates', 'wpfnl' ),
4802 'enable_global_funnel' => __( 'Enable Global Funnel', 'wpfnl' ),
4803 'enable_gbf_tooltip_txt' => __( 'Enabling this will convert this funnel into a conditional global funnel and you will have define conditions to trigger the funnel right from your WooCommerce store.', 'wpfnl' ),
4804 'save' => __( 'Save', 'wpfnl' ),
4805 'webhook' => __( 'Webhook', 'wpfnl' ),
4806 'analytics' => __( 'Analytics', 'wpfnl' ),
4807 'integrations' => __( 'Integrations', 'wpfnl' ),
4808 'publish' => __( 'Publish', 'wpfnl' ),
4809 'draft' => __( 'Draft', 'wpfnl' ),
4810 'enable' => __( 'Enable', 'wpfnl' ),
4811 'disable' => __( 'Disable', 'wpfnl' ),
4812 'delete' => __( 'Delete', 'wpfnl' ),
4813 'trash' => __( 'Trash', 'wpfnl' ),
4814 'deleting' => __( 'Deleting...', 'wpfnl' ),
4815 'settings' => __( 'Settings', 'wpfnl' ),
4816 'youtube_video' => __( 'Videos', 'wpfnl' ),
4817 'documentation' => __( 'Documentation', 'wpfnl' ),
4818 'blog' => __( 'Blog', 'wpfnl' ),
4819 'select_template_industries' => __( 'Select Template Industries', 'wpfnl' ),
4820 'select_template_builder' => __( 'Select Template Builder', 'wpfnl' ),
4821 'upload_image' => __( 'Upload image', 'wpfnl' ),
4822 'upload_image_tooltip' => __( 'Uploade Funnel Featured Image', 'wpfnl' ),
4823 'remove_image' => __( 'Remove image', 'wpfnl' ),
4824 'enter_seo_title' => __( 'Enter seo title', 'wpfnl' ),
4825 'enter_seo_desc' => __( 'Enter seo description', 'wpfnl' ),
4826 'save_data' => __( 'Save Data', 'wpfnl' ),
4827 'want_add_new_step' => __( 'Do you want to add a new step?', 'wpfnl' ),
4828 'what_change_you_need' => __( 'What Changes Do You Need in this Position?', 'wpfnl' ),
4829 'step_has_dependency' => __( 'The step you are deleting has a dependency. You can delete the step and connect the condition to the previous step or you can add a new step here.', 'wpfnl' ),
4830 'are_you_sure_to_delete_step' => __( 'Are you sure to delete this step?', 'wpfnl' ),
4831 'cancel' => __( 'Cancel', 'wpfnl' ),
4832 'no' => __( 'No', 'wpfnl' ),
4833 'connect_to_previous_step' => __( 'Connect to previous step', 'wpfnl' ),
4834 'add_new_step' => __( 'Add a new step', 'wpfnl' ),
4835 'yes' => __( 'Yes', 'wpfnl' ),
4836 'are_you_sure_delete_product' => __( 'Are you sure you want to delete this product?', 'wpfnl' ),
4837 'thankyou' => __( 'Thank you', 'wpfnl' ),
4838 'add_condition' => __( 'Set Entry Rules', 'wpfnl' ),
4839 'conditional_drawer_header' => __( 'Conditions', 'wpfnl' ),
4840 'add_featured_image' => __( 'Add Featured Image', 'wpfnl' ),
4841 'title' => __( 'Title', 'wpfnl' ),
4842 'url_slug' => __( 'URL Slug', 'wpfnl' ),
4843 'click_to_copy' => __( 'Click to Copy', 'wpfnl' ),
4844 'copied' => __( 'Copied!', 'wpfnl' ),
4845 'update' => __( 'Update', 'wpfnl' ),
4846 'enable_condition' => __( 'Enable Condition', 'wpfnl' ),
4847 'successfully_saved' => __( 'Succesfully saved', 'wpfnl' ),
4848 'no_condition_found' => __( 'No condition found', 'wpfnl' ),
4849 'add_new_condition' => __( 'Add New Condition', 'wpfnl' ),
4850 'and' => __( 'And', 'wpfnl' ),
4851 'or' => __( 'Or', 'wpfnl' ),
4852 'choose_next_steps' => __( 'Choose next steps', 'wpfnl' ),
4853 'false' => __( 'False', 'wpfnl' ),
4854 'please_select' => __( 'Please select', 'wpfnl' ),
4855 'select_steps' => __( 'Select Steps', 'wpfnl' ),
4856 'select_step' => __( 'Select Step', 'wpfnl' ),
4857 'add_step' => __( 'Add Step', 'wpfnl' ),
4858 'no_steps_found' => __( 'No steps found', 'wpfnl' ),
4859 'reconfigure_conditions' => __( 'Reconfigure Conditions', 'wpfnl' ),
4860 'reconfigure_conditions_helper' => __( 'Seems like you\'ve set a condition for the step earlier. Would you like to reconfigure?', 'wpfnl' ),
4861 'no_keep_the_same' => __( 'No, keep the same', 'wpfnl' ),
4862 'yes_reconfigure' => __( 'Yes, reconfigure', 'wpfnl' ),
4863 'zoom_in' => __( 'Zoom In', 'wpfnl' ),
4864 'zoom_level' => __( 'Zoom Level', 'wpfnl' ),
4865 'zoom_out' => __( 'Zoom Out', 'wpfnl' ),
4866
4867 // ------analytics------
4868 'weekly' => __( 'Weekly', 'wpfnl' ),
4869 'monthly' => __( 'Monthly', 'wpfnl' ),
4870 'yearly' => __( 'Yearly', 'wpfnl' ),
4871 'custom' => __( 'Custom', 'wpfnl' ),
4872 'start_date' => __( 'Start Date', 'wpfnl' ),
4873 'end_date' => __( 'End Date', 'wpfnl' ),
4874 'filter' => __( 'Filter', 'wpfnl' ),
4875 'conversion_overview' => __( 'Conversion Overview', 'wpfnl' ),
4876 'previous' => __( 'Previous', 'wpfnl' ),
4877 'next' => __( 'Next', 'wpfnl' ),
4878 'total_visitors' => __( 'Total visitors:', 'wpfnl' ),
4879 'unique_visitors' => __( 'unique visitors:', 'wpfnl' ),
4880 'conversion_rates' => __( 'conversion rates:', 'wpfnl' ),
4881 'bounce_rates' => __( 'Bounce Rates:', 'wpfnl' ),
4882 'no_data_found' => __( 'No data Found', 'wpfnl' ),
4883 'from' => __( 'From', 'wpfnl' ),
4884 'to' => __( 'To', 'wpfnl' ),
4885 'revenue' => __( 'Revenue', 'wpfnl' ),
4886 'are_you_sure_delete_analytics' => __( 'Are you sure to delete all analytics data? This won\'t affect other funnels.', 'wpfnl' ),
4887
4888 // ---webhook---
4889 'webhooks' => __( 'Webhooks', 'wpfnl' ),
4890 'search_by_name' => __( 'Search by Name', 'wpfnl' ),
4891 'add_webhook' => __( 'Add Webhook', 'wpfnl' ),
4892 'name' => __( 'Name', 'wpfnl' ),
4893 'request_url' => __( 'Request URL', 'wpfnl' ),
4894 'status' => __( 'Status', 'wpfnl' ),
4895 'actions' => __( 'Actions', 'wpfnl' ),
4896 'field_required' => __( 'Field Required', 'wpfnl' ),
4897 'key' => __( 'Key', 'wpfnl' ),
4898 'value' => __( 'Value', 'wpfnl' ),
4899 'select_value' => __( 'Select value', 'wpfnl' ),
4900 'select_name' => __( 'Select name', 'wpfnl' ),
4901 'add' => __( 'Add', 'wpfnl' ),
4902 'remove' => __( 'Remove', 'wpfnl' ),
4903 'delete_webhook' => __( 'Delete Webhook', 'wpfnl' ),
4904 'want_to_delete_webhook_list' => __( 'Are you sure you want to delete this List?', 'wpfnl' ),
4905 'edit_field' => __( 'Edit Field', 'wpfnl' ),
4906 'delete_field' => __( 'Delete Field', 'wpfnl' ),
4907
4908 // ----webhook settings------
4909 'give_name_or_id' => __( 'Give a name or ID to this webhook.', 'wpfnl' ),
4910 'allow_connect_3rd_party_services' => __( 'Allow to connect with 3rd party services.', 'wpfnl' ),
4911 'request_method' => __( 'Request Method', 'wpfnl' ),
4912 'select_http_request_method' => __( 'Select HTTP request sending method.', 'wpfnl' ),
4913 'select_method' => __( 'Select method', 'wpfnl' ),
4914 'request_format' => __( 'Request Format', 'wpfnl' ),
4915 'select_http_request_formate' => __( 'Select HTTP request sending format.', 'wpfnl' ),
4916 'select_format' => __( 'Select format', 'wpfnl' ),
4917 'webhook_events' => __( 'Webhook Events', 'wpfnl' ),
4918 'webhook_events_tooltip' => __( 'Select the WPFunnels event when the request would be sent.', 'wpfnl' ),
4919 'select_an_event' => __( 'Select an event', 'wpfnl' ),
4920 'request_body' => __( 'Request Body', 'wpfnl' ),
4921 'request_body_tooltip' => __( 'Select which values would be sent.', 'wpfnl' ),
4922 'all_fields' => __( 'All Fields', 'wpfnl' ),
4923 'select_fields' => __( 'Select Fields', 'wpfnl' ),
4924 'select_fields' => __( 'Add New Condition', 'wpfnl' ),
4925 'new_condition' => __( 'New Condition', 'wpfnl' ),
4926 'saved_successfully' => __( 'Saved Successfully', 'wpfnl' ),
4927 'please_select_an_event_first' => __( 'Please select an event first', 'wpfnl' ),
4928
4929 // ----funnels settings drawer.vue------
4930 'funnel_settings' => __( 'Funnel Settings', 'wpfnl' ),
4931 'utm_settings' => __( 'UTM Settings', 'wpfnl' ),
4932 'guide_on_utm_integration' => __( 'Guide On UTM Integration', 'wpfnl' ),
4933 'enable_utm_parameters_for_this_funnel' => __( 'Enable UTM Parameters For This Funnel', 'wpfnl' ),
4934 'pro' => __( 'pro', 'wpfnl' ),
4935 'referrer_utm_source' => __( 'Referrer (utm_source)', 'wpfnl' ),
4936 'medium_utm_medium' => __( 'Medium (utm_medium)', 'wpfnl' ),
4937 'campaign_utm_campaign' => __( 'Campaign (utm_campaign)', 'wpfnl' ),
4938 'content_utm_content' => __( 'Content (utm_content)', 'wpfnl' ),
4939 'event_tracking' => __( 'Event Tracking', 'wpfnl' ),
4940 'exclude_this_funnel_from_fb_pixel_tracking' => __( 'Exclude this funnel from FB Pixel tracking', 'wpfnl' ),
4941 'fb_pixel_tracking_hints' => __( 'Please connect from WPFunnels > Settings > Event Tracking to use it in this funnel.', 'wpfnl' ),
4942 'exclude_this_funnel_from_gtm_tracking' => __( 'Exclude this funnel from GTM tracking', 'wpfnl' ),
4943 'gtm_tracking_hints' => __( 'Please connect from WPFunnels > Settings > Event Tracking to use it in this funnel.', 'wpfnl' ),
4944 'more_settings' => __( 'More Settings', 'wpfnl' ),
4945 'skip_offers_for_identical_products_in_the_cart' => __( 'Skip offers for identical products in the cart', 'wpfnl' ),
4946 'skip_offers_cart_tooltip' => __( 'Enable this if you want to skip upsell or downsell offers (for Global Funnels only) when the offer product is the same as main product in the cart.', 'wpfnl' ),
4947 'dont_skip_offer_for_higher_quantity' => __( 'Don\'t skip if the offer is for a higher quantity', 'wpfnl' ),
4948 'dont_skip_offer_for_higher_quantity_tooltip' => __( 'Enabling this will mean that if the offer (for Global Funnels only) product is same as the product in the cart but with higher quantity, then it will not be skipped.', 'wpfnl' ),
4949 'skip_upsell_downsell_for_recurring_buyers' => __( 'Skip upsell/downsell for recurring buyers', 'wpfnl' ),
4950 'skip_upsell_downsell_for_recurring_buyers_tooltip' => __( 'Enabling this will mean if the upsell/downsell offer product was purchased by a buyer in the last 365 days, the offer will be skipped.', 'wpfnl' ),
4951 'threshold_max_365' => __( 'Threshold (max 365)', 'wpfnl' ),
4952 'threshold_max_365_tooltip' => __( 'You may specify the number of days (less than 365 days) within which the product has to be purchased for the offer to be skipped. For example, skip the offer if the product was purchased within the last 100 days.', 'wpfnl' ),
4953 'lms_sales_sunnel_support' => __( 'LMS Sales Funnel Support', 'wpfnl' ),
4954 'use_this_funnel_for_lms_only' => __( 'Use this funnel for LMS only', 'wpfnl' ),
4955 'use_this_funnel_for_lms_only_tooltip' => __( 'Enable this option to turn this funnel into a sales funnel for LMS. You can use it when you have a supported LMS activated such as LearnDash.', 'wpfnl' ),
4956 'save_changes' => __( 'Save Changes', 'wpfnl' ),
4957 'custom_css_desc' => __( 'Add custom CSS code here and it will be automatically added to the head section of this page. Do not include &lt;style&gt; tags.', 'wpfnl' ),
4958 'field_validation' => __( 'Field Validation', 'wpfnl' ),
4959 'enable_field_validation' => __( 'Enable Field validation error message', 'wpfnl' ),
4960 'enable_field_validation_desc' => __( 'Show custom validation error messages for required checkout fields.', 'wpfnl' ),
4961 'validation_message_label' => __( 'Validation error message', 'wpfnl' ),
4962
4963 // ----Global Funnel Offer Mode------
4964 'offer_selection_mode' => __( 'How Should the Offer be Selected?', 'wpfnl' ),
4965 'manual' => __( 'Specific Product', 'wpfnl' ),
4966 'auto_match' => __( 'Auto-match from Store', 'wpfnl' ),
4967 'recommended' => __( 'Recommended', 'wpfnl' ),
4968 'add_existing_product' => __( 'Add Existing Product', 'wpfnl' ),
4969 'create_product' => __( 'Create Product', 'wpfnl' ),
4970 'product' => __( 'PRODUCT', 'wpfnl' ),
4971 'total_price' => __( 'TOTAL PRICE', 'wpfnl' ),
4972 'no_product_yet' => __( 'No Product Yet', 'wpfnl' ),
4973 'fallback_behavior' => __( 'If No product Matches', 'wpfnl' ),
4974 'fallback_description' => __( 'What should happen when no rules match the main product?', 'wpfnl' ),
4975 'dont_show_upsell' => __( "Don’t show this offer", 'wpfnl' ),
4976 'skip_upsell_description' => __( 'Skip the offer if no rules match.', 'wpfnl' ),
4977 'show_default_product' => __( 'Show a default product', 'wpfnl' ),
4978 'default_product_description' => __( 'Offer a specific product when no rules match.', 'wpfnl' ),
4979
4980 // ----AB testing------
4981 'running' => __( 'Running', 'wpfnl' ),
4982 'draft' => __( 'Draft', 'wpfnl' ),
4983 'archived_variant' => __( 'Archived Variant', 'wpfnl' ),
4984 'ab_test' => __( 'A/B Testing', 'wpfnl' ),
4985 'click_to_copy_ab_url' => __( 'Click to Copy A/B URL', 'wpfnl' ),
4986 'copy_link' => __( 'Copy Link', 'wpfnl' ),
4987 'stats' => __( 'Stats', 'wpfnl' ),
4988 'traffic_distribution' => __( 'Traffic Distribution', 'wpfnl' ),
4989 'start' => __( 'Start', 'wpfnl' ),
4990 'pause' => __( 'Pause', 'wpfnl' ),
4991 'archive_list' => __( 'Archive List', 'wpfnl' ),
4992 'templates' => __( 'Templates', 'wpfnl' ),
4993 'duplicate' => __( 'Duplicate', 'wpfnl' ),
4994 'duplicating' => __( 'Duplicating..', 'wpfnl' ),
4995 'delete_variant' => __( 'Delete Variant', 'wpfnl' ),
4996 'are_you_sure_to_delete_this_variant' => __( 'Are you sure to delete this Variant?', 'wpfnl' ),
4997 'visits' => __( 'Visits', 'wpfnl' ),
4998 'conversion_rate' => __( 'Conversion Rate', 'wpfnl' ),
4999 'action' => __( 'Action', 'wpfnl' ),
5000 'retrieve' => __( 'Retrieve', 'wpfnl' ),
5001 'no_variation_found' => __( 'No Variation Found', 'wpfnl' ),
5002 'confirm_retrieve' => __( 'Confirm Retrieve', 'wpfnl' ),
5003 'are_you_sure_to_retrieve' => __( 'Are you sure to retrieve?', 'wpfnl' ),
5004 'confirm' => __( 'Confirm', 'wpfnl' ),
5005 'retrieving' => __( 'Retrieving...', 'wpfnl' ),
5006 'delete_this_variant' => __( 'Delete this Variant', 'wpfnl' ),
5007 'landing' => __( 'Landing', 'wpfnl' ),
5008 'checkout' => __( 'Checkout', 'wpfnl' ),
5009 'buy_now' => __( 'Buy Now', 'wpfnl' ),
5010 'upsell' => __( 'Upsell', 'wpfnl' ),
5011 'downsell' => __( 'Downsell', 'wpfnl' ),
5012 'conversion' => __( 'Conversion', 'wpfnl' ),
5013 'edit' => __( 'Edit', 'wpfnl' ),
5014 'view_step' => __( 'View Step', 'wpfnl' ),
5015 'more_option' => __( 'More Option', 'wpfnl' ),
5016 'declare_as_winner' => __( 'Declare as Winner', 'wpfnl' ),
5017 'make_archive' => __( 'Make Archive', 'wpfnl' ),
5018 'archiving' => __( 'Archiving...', 'wpfnl' ),
5019 'make_archive' => __( 'Make Archive', 'wpfnl' ),
5020 'traffic_distribution_tooltip' => __( 'Traffic distribution should be 100%', 'wpfnl' ),
5021 'close' => __( 'Close', 'wpfnl' ),
5022 'unlock_to_enter_value' => __( 'Unlock to Enter Value', 'wpfnl' ),
5023 'want_to_set_original' => __( 'Want to set “Original', 'wpfnl' ),
5024 'page_as_winner' => __( 'page” as a winner', 'wpfnl' ),
5025 'want_to_set' => __( 'Want to set', 'wpfnl' ),
5026 'as_a_winner' => __( 'as a winner', 'wpfnl' ),
5027 'set_variation_as_winner' => __( 'If you set this variation as a winner', 'wpfnl' ),
5028 'winner_check_list1' => __( 'Winner variant will be live on your site', 'wpfnl' ),
5029 'winner_check_list2' => __( 'Other variants will no longer available for the site visitors', 'wpfnl' ),
5030 'winner_check_list3' => __( 'Other variants will be on draft', 'wpfnl' ),
5031 'archive_all_except_winner' => __( 'Archive All Variants Except Winner', 'wpfnl' ),
5032 'declare_a_winner' => __( 'Declare a Winner', 'wpfnl' ),
5033
5034 // -----mail mint automation-----
5035 'automation' => __( 'Automation', 'wpfnl' ),
5036 'select_start_point' => __( 'Select A Start Point', 'wpfnl' ),
5037 'remove_this_trigger' => __( 'Remove this Trigger', 'wpfnl' ),
5038 'send_an_email' => __( 'Send An Email', 'wpfnl' ),
5039 'email_sequence' => __( 'Email Sequence', 'wpfnl' ),
5040 'assign_tags' => __( 'Assign Tag(s)', 'wpfnl' ),
5041 'add_to_lists' => __( 'Add To List(s)', 'wpfnl' ),
5042 'remove_tags' => __( 'Remove Tag(s)', 'wpfnl' ),
5043 'remove_lists' => __( 'Remove List(s)', 'wpfnl' ),
5044 'create_contact' => __( 'Create Contact', 'wpfnl' ),
5045 'outgoing_webhook' => __( 'Outgoing Webhook', 'wpfnl' ),
5046 'stop_automation' => __( 'Stop Automation', 'wpfnl' ),
5047 'delay' => __( 'Delay', 'wpfnl' ),
5048 'twilioSendMessage' => __( 'Send Message', 'wpfnl' ),
5049 'if_else' => __( 'If/Else', 'wpfnl' ),
5050 'set_hours_or_days' => __( 'Set a number of hours or days to wait before the next step of the journey is triggered.', 'wpfnl' ),
5051 'prepare_custom_email_template' => __( 'Prepare a custom email template to send to contacts that enter this automation flow.', 'wpfnl' ),
5052 'choose_an_email_sequence' => __( 'Choose an email sequence to send at this point of your automation flow.', 'wpfnl' ),
5053 'choose_tags_to_assign' => __( 'Choose Tag(s) to assign to contacts.', 'wpfnl' ),
5054 'choose_list_to_assign' => __( 'Choose List(s) to add contacts on.', 'wpfnl' ),
5055 'choose_tag_to_remove' => __( 'Choose certain tag(s) to remove from your contacts.', 'wpfnl' ),
5056 'choose_list_to_remove' => __( 'Remove contacts from selected lists(s).', 'wpfnl' ),
5057 'contact_meets_if_else' => __( 'Contact meets If/Else conditions.', 'wpfnl' ),
5058 'create_user_hints' => __( 'If the contact is not on the contacts list already, a new contact will be created.', 'wpfnl' ),
5059 'stop_automation_hints' => __( 'Use this action to stop your automation workflow at this point.', 'wpfnl' ),
5060 'webhook_outgoing_hints' => __( 'Send Data to any external server via GET or POST Methods at any point of the Automation workflow', 'wpfnl' ),
5061 'twillo_hints' => __( 'Send a message to the contact.', 'wpfnl' ),
5062 'not_set_up_yet' => __( 'Not set up yet', 'wpfnl' ),
5063 'assigned_tags' => __( 'Assigned Tags', 'wpfnl' ),
5064 'removed_tags' => __( 'Removed Tags', 'wpfnl' ),
5065 'assigned_lists' => __( 'Assigned Lists', 'wpfnl' ),
5066 'removed_lists' => __( 'Removed Lists', 'wpfnl' ),
5067 'select_status' => __( 'Select Status :', 'wpfnl' ),
5068 'welcome_email_from_wpfunnels' => __( 'Welcome email from WPFunnels', 'wpfnl' ),
5069 'stop_this_automation_workflow' => __( 'Stop This Automation Workflow', 'wpfnl' ),
5070 'send_data_via_get_post_method' => __( 'Send Data to external server via GET or POST Method', 'wpfnl' ),
5071 'delay_not_set_yet' => __( 'Delay Not Set Yet', 'wpfnl' ),
5072 'twilloBodyText' => __( 'Dispatch text message through Twilio SMS marketing.', 'wpfnl' ),
5073 'wait_for' => __( 'Wait for', 'wpfnl' ),
5074 'delete_this_step' => __( 'Delete this Step', 'wpfnl' ),
5075 'exit' => __( 'Exit', 'wpfnl' ),
5076 'choose_templates' => __( 'Choose Templates', 'wpfnl' ),
5077 'ready_email_templates' => __( 'Ready Email Templates', 'wpfnl' ),
5078 'my_saved_templates' => __( 'My Saved Templates', 'wpfnl' ),
5079 'edit_template' => __( 'Edit Template', 'wpfnl' ),
5080 'import' => __( 'Import', 'wpfnl' ),
5081 'do_you_want_to_save_automation' => __( 'Do you want to save the automation?', 'wpfnl' ),
5082 'no_back' => __( 'No & Back', 'wpfnl' ),
5083 'yes_back' => __( 'Yes & Back', 'wpfnl' ),
5084 'saving' => __( 'Saving...', 'wpfnl' ),
5085
5086 'choose_list' => __( 'Choose List', 'wpfnl' ),
5087 'select_lists' => __( 'Select Lists', 'wpfnl' ),
5088 'search_list' => __( 'Search List', 'wpfnl' ),
5089 'add_list' => __( 'Add List', 'wpfnl' ),
5090 'choose_tags' => __( 'Choose Tags', 'wpfnl' ),
5091 'select_tags' => __( 'Select Tags', 'wpfnl' ),
5092 'search_tag' => __( 'Search Tag', 'wpfnl' ),
5093 'add_tag' => __( 'Add Tag', 'wpfnl' ),
5094 'contact_status' => __( 'Contact Status', 'wpfnl' ),
5095 'select_status' => __( 'Select Status', 'wpfnl' ),
5096 'set_waiting_time' => __( 'Set waiting time', 'wpfnl' ),
5097 'select_time' => __( 'Select Time', 'wpfnl' ),
5098 'minutes' => __( 'Minutes', 'wpfnl' ),
5099 'hours' => __( 'Hours', 'wpfnl' ),
5100 'days' => __( 'Days', 'wpfnl' ),
5101 'weeks' => __( 'Weeks', 'wpfnl' ),
5102 'month' => __( 'Month', 'wpfnl' ),
5103 'year' => __( 'Year', 'wpfnl' ),
5104 'sender_email' => __( 'Sender Email', 'wpfnl' ),
5105 'sender_name' => __( 'Sender Name', 'wpfnl' ),
5106 'reply_email' => __( 'Reply Email', 'wpfnl' ),
5107 'reply_name' => __( 'Reply Name', 'wpfnl' ),
5108 'email_subject_line' => __( 'Email Subject Line', 'wpfnl' ),
5109 'email_preview_text' => __( 'Email Preview Text', 'wpfnl' ),
5110 'email_preview_placeholder' => __( 'Be Specific and concise to spark interest', 'wpfnl' ),
5111 'design' => __( 'Design', 'wpfnl' ),
5112 'select_a_template' => __( 'Select a Template', 'wpfnl' ),
5113 'choose_a_sequence' => __( 'Choose a sequence', 'wpfnl' ),
5114 'data_send_method' => __( 'Data Send Method', 'wpfnl' ),
5115 'remote_url' => __( 'Remote URL', 'wpfnl' ),
5116 'request_format' => __( 'Request Format', 'wpfnl' ),
5117 'request_body_data' => __( 'Request Body Data', 'wpfnl' ),
5118 'body_key' => __( 'Body Key', 'wpfnl' ),
5119 'body_value' => __( 'Body Value', 'wpfnl' ),
5120 'enter_key' => __( 'Enter key', 'wpfnl' ),
5121 'add_new_item' => __( 'Add new item', 'wpfnl' ),
5122 'add_new' => __( 'Add New', 'wpfnl' ),
5123 'request_header' => __( 'Request Header', 'wpfnl' ),
5124 'header_key' => __( 'Header Key', 'wpfnl' ),
5125 'header_value' => __( 'Header Value', 'wpfnl' ),
5126 'new_rule' => __( 'New Rule', 'wpfnl' ),
5127 'enter_a_value' => __( 'Enter a value', 'wpfnl' ),
5128 'select_date' => __( 'Select Date', 'wpfnl' ),
5129 'mark_this_email_as_transactional' => __( 'Mark this email as transactional', 'wpfnl' ),
5130 'mark_this_email_as_transactional_tooltip' => __( 'Unsubscribed contacts will still receive transactional emails like Order Summaries, Renewal Reminders, and Admin notifications.', 'wpfnl' ),
5131
5132 // -----step importer------
5133 'enter_step_name' => __( 'Enter A Step Name', 'wpfnl' ),
5134 'find_your_templates' => __( 'Find your templates', 'wpfnl' ),
5135 'all' => __( 'all', 'wpfnl' ),
5136 'free' => __( 'free', 'wpfnl' ),
5137 'premium' => __( 'Premium', 'wpfnl' ),
5138 'freemium' => __( 'Freemium', 'wpfnl' ),
5139 'custom' => __( 'Custom', 'wpfnl' ),
5140 'pages' => __( 'Pages', 'wpfnl' ),
5141 'start_from_scratch' => __( 'Start From Scratch', 'wpfnl' ),
5142 'oops_it_looks_like' => __( 'Oops! It looks like', 'wpfnl' ),
5143 'is_inactive' => __( 'is inactive.', 'wpfnl' ),
5144 'it_seems_like_you_have_selected' => __( 'It seems like you have selected', 'wpfnl' ),
5145 'preferred_page_builder' => __( 'as your preferred page builder, but you do not have', 'wpfnl' ),
5146 'activated_your_site' => __( 'activated on your site.', 'wpfnl' ),
5147 'you_see_we_create_funnel_templates_for_gutenberg' => __( 'You see, we create funnel templates for Gutenberg using', 'wpfnl' ),
5148 'please_install_and_activate' => __( 'Please install and activate', 'wpfnl' ),
5149 'to_import_funnel_page_templates_for_gutenberg' => __( 'to import funnel page templates for Gutenberg.', 'wpfnl' ),
5150 'click_here_to_install_and_activate' => __( 'Click here to install & activate', 'wpfnl' ),
5151 'if_you_want_to_create_funnel_steps_without' => __( 'If you want to create funnel steps without using', 'wpfnl' ),
5152 'then_dont_worry' => __( 'then don"t worry.', 'wpfnl' ),
5153 'create_from_scratch' => __( 'You can go ahead and create this step from scratch and design it with any page builder/editor. It will work just fine.', 'wpfnl' ),
5154 'selected_builder_inactive' => __( 'Oops! It looks like the page builder you selected is inactive.', 'wpfnl' ),
5155 'elementor_didnot_active' => __( 'as your preferred page builder, but you do not have the plugin activated on your site.', 'wpfnl' ),
5156 'elementor_template_import' => __( 'to import funnel templates.', 'wpfnl' ),
5157 'if_you_want_to_design_funnel_steps_without' => __( 'If you want to design the funnel steps without using', 'wpfnl' ),
5158 'divi_notice_body1' => __( 'It seems like you have selected Divi builder as your preferred page builder, but you do not have the plugin activated on your site. Please install and activate Divi builder to import funnel templates.', 'wpfnl' ),
5159 'divi_notice_body2' => __( 'If you want to design the funnel steps without using Divi builder, then don"t worry.', 'wpfnl' ),
5160
5161 'oxygen_notice_body1' => __( 'It seems like you have selected Oxygen builder as your preferred page builder, but you do not have the plugin Oxygen builder activated on your site.', 'wpfnl' ),
5162 'oxygen_notice_body2' => __( 'Please install and activate Oxygen builder to import ready funnel templates.', 'wpfnl' ),
5163 'oxygen_notice_body3' => __( 'If you want to create & design funnel pages without using Oxygen builder, then don"t worry.', 'wpfnl' ),
5164 'oxygen_notice_body4' => __( 'You can go ahead and create funnels from scratch using any page builder/editor. It will work just fine.', 'wpfnl' ),
5165
5166 'importing_step' => __( 'Importing Step', 'wpfnl' ),
5167 'preview' => __( 'Preview', 'wpfnl' ),
5168
5169 // -----integration-------
5170 'funnel_integration' => __( 'Funnel Integration', 'wpfnl' ),
5171 'add_new_integration' => __( 'Add New Integration', 'wpfnl' ),
5172 'delete_selected' => __( 'Delete Selected', 'wpfnl' ),
5173 'crm' => __( 'CRM', 'wpfnl' ),
5174 'trigger' => __( 'Trigger', 'wpfnl' ),
5175 'options' => __( 'Options', 'wpfnl' ),
5176 'are_you_sure_delete_automation' => __( 'Are you sure you want to delete this Automation?', 'wpfnl' ),
5177
5178 'integration_name' => __( 'Integration Name:', 'wpfnl' ),
5179 'edit_name' => __( 'Edit Name', 'wpfnl' ),
5180 'save_name' => __( 'Save Name', 'wpfnl' ),
5181 'cancel_changes' => __( 'Cancel Changes', 'wpfnl' ),
5182 'connect_crm' => __( 'Connect CRM', 'wpfnl' ),
5183 'select_crm' => __( 'Select CRM', 'wpfnl' ),
5184 'see_documantation' => __( 'See Documantation', 'wpfnl' ),
5185 'test_zapier_trigger' => __( 'Test Zapier Trigger', 'wpfnl' ),
5186 'choose_crm' => __( 'Choose A CRM To Start Setting Up Your Integration', 'wpfnl' ),
5187 'no_crm_notice' => __( 'It appears no CRMs or automation tools are connected or activated. Please activate or connect a supported automation tool to use this feature.', 'wpfnl' ),
5188 'are_you_sure_dummy_data' => __( 'Are you sure want to send dummy data?', 'wpfnl' ),
5189 'field_sent_successfully' => __( 'Field Sent Successfully', 'wpfnl' ),
5190 'automation_name_required' => __( 'Automation name is required', 'wpfnl' ),
5191 'when' => __( 'When', 'wpfnl' ),
5192 'user_event' => __( 'User Event', 'wpfnl' ),
5193 'select_event' => __( 'Select event', 'wpfnl' ),
5194 'then' => __( 'Then', 'wpfnl' ),
5195 'add_to_audience' => __( 'Add To Audience', 'wpfnl' ),
5196 'add_to_group' => __( 'Add To Group', 'wpfnl' ),
5197 'add_to_static_list' => __( 'Add To Static List', 'wpfnl' ),
5198 'add_to_list' => __( 'Add To List', 'wpfnl' ),
5199 'assign_tag' => __( 'Assign Tag', 'wpfnl' ),
5200 'enter_zapier_webhook_url' => __( 'Enter Zapier Webhook URL', 'wpfnl' ),
5201 'enter_pabbly_webhook_url' => __( 'Enter Pabbly Webhook URL', 'wpfnl' ),
5202 'select_tag' => __( 'Please select', 'wpfnl' ),
5203
5204 // -----coupon.vue----
5205 'select_coupon' => __( 'Select Coupon', 'wpfnl' ),
5206 'search_for_coupon' => __( 'Search for coupon', 'wpfnl' ),
5207
5208 // -----discount.vue----
5209 'discount_type' => __( 'Discount Type', 'wpfnl' ),
5210 'discount_type_tooltip' => __( 'Choose if you want to give a unique percentage/flat discount on your', 'wpfnl' ),
5211 'discount_type_tooltip2' => __( 'Choose if you want to give a unique percentage/flat discount on your offer product in the funnel.', 'wpfnl' ),
5212 'discount_type_tooltip3' => __( 'Choose if you want to give a unique percentage/flat discount on your main product.', 'wpfnl' ),
5213 'offer' => __( 'offer.', 'wpfnl' ),
5214 'discount_apply_to' => __( 'Discount Apply To', 'wpfnl' ),
5215 'discount_apply_to_tooltip' => __( 'Choose if you want the discount to be applied on the Regular Price or the Sale Price of the', 'wpfnl' ),
5216 'discount_apply_to_tooltip2' => __( 'Chose if you want the discount to be applied on the Regular Price or the Sale Price of the offer product.', 'wpfnl' ),
5217 'discount_apply_to_tooltip3' => __( 'Choose if you want the discount to be applied on the Regular Price or the Sale Price of the main product.', 'wpfnl' ),
5218 'offer_product' => __( 'offer product.', 'wpfnl' ),
5219 'discount_value' => __( 'Discount Value', 'wpfnl' ),
5220 'discount_value_tooltip' => __( '(when discount type is percentage) Assign a suitable percentage discount.', 'wpfnl' ),
5221 'discount_value_tooltip2' => __( '(when discount type is flat amount) Choose a certain amount to waive off.', 'wpfnl' ),
5222 'original_price' => __( 'Original Price', 'wpfnl' ),
5223 'sale_price' => __( 'Sale Price', 'wpfnl' ),
5224 'discounted_offer_price' => __( 'Discounted Offer Price', 'wpfnl' ),
5225 'discounted_offer_price_tooltip1' => __( 'This will be the', 'wpfnl' ),
5226 'discounted_offer_price_tooltip2' => __( 'offer price your buyer will pay.', 'wpfnl' ),
5227 'discounted_offer_price_tooltip3' => __( 'This will be the offer price your buyer will pay.', 'wpfnl' ),
5228 'discounted_offer_price_tooltip4' => __( 'This will be the main product price your buyer will pay.', 'wpfnl' ),
5229 'replace_first_product' => __( 'Replace First Product', 'wpfnl' ),
5230 'replace_first_product_hints' => __( 'It will replace the first selected product (from checkout products) with the order bump product.', 'wpfnl' ),
5231 'replace_product' => __( 'Replace Product', 'wpfnl' ),
5232 'pre_purchase_upsell_text' => __( 'Set as pre-purchase upsell', 'wpfnl' ),
5233 'pre_purchase_upsell_tooltip_text' => __( 'Enable this option to display a special offer to customers before they complete their purchase. The upsell will appear after they click the Place Order button, giving them a final opportunity to add the promoted product to their order.', 'wpfnl' ),
5234 'replace_product_tooltip' => __( 'Enabling this will mean that if a buyer accepts the order bump offer, then the order bump product will replace the main products in the checkout and the buyer will just have to pay for the order bump product.', 'wpfnl' ),
5235 'replace_product_hints' => __( 'It will replace all products (from checkout products) with the order bump product.', 'wpfnl' ),
5236
5237 'search_for_categories' => __( 'Search for categories', 'wpfnl' ),
5238 'search_for_tag' => __( 'Search for tag', 'wpfnl' ),
5239 'product' => __( 'Product', 'wpfnl' ),
5240
5241 'enter_funnel_if' => __( 'Enter Funnel If', 'wpfnl' ),
5242 'add_category' => __( 'Add Category', 'wpfnl' ),
5243 'add_product' => __( 'Add Existing Product', 'wpfnl' ),
5244 'amount' => __( 'Amount', 'wpfnl' ),
5245 'ammount_must_be_greater_than_1' => __( 'Amount must be greater than 1', 'wpfnl' ),
5246 'ammount_must_be_greater_than_0' => __( 'Amount must be greater than 0', 'wpfnl' ),
5247 'quantity' => __( 'Quantity', 'wpfnl' ),
5248 'quantity_must_be_greater_than_1' => __( 'Quantity must be greater than 1', 'wpfnl' ),
5249 'day_must_be_greater_than_or_euqal_1' => __( 'Day must be greater than or euqal 1', 'wpfnl' ),
5250 'role' => __( 'Role', 'wpfnl' ),
5251 'select_role' => __( 'Select Role', 'wpfnl' ),
5252 'class' => __( 'Class', 'wpfnl' ),
5253 'slect_shippin_class' => __( 'Select Shipping Class', 'wpfnl' ),
5254 'date' => __( 'Date :', 'wpfnl' ),
5255
5256 // -----image upload.vue------
5257 'product_image' => __( 'Product Image', 'wpfnl' ),
5258 'course_image' => __( 'Course Image', 'wpfnl' ),
5259 'product_image_tooltip' => __( 'You may change this image and use a custom image just for this order bump.', 'wpfnl' ),
5260 'click_to_change_image' => __( 'Click to Change Image', 'wpfnl' ),
5261 'click_to_update_image' => __( 'Drop an Image or ', 'wpfnl' ),
5262 'recommended_image_size' => __( 'Recommended image size 160px x 160px', 'wpfnl' ),
5263
5264 // -----orderbump.vue------
5265 'order_bump' => __( 'Order Bump', 'wpfnl' ),
5266 'add_order_bump' => __( 'Add Order Bump', 'wpfnl' ),
5267 'no_order_bump' => __( 'No Order Bump', 'wpfnl' ),
5268 'order_bump_hints1' => __( 'You can only add 1 order bump offer in the Free version.', 'wpfnl' ),
5269 'order_bump_hints2' => __( 'Upgrade to Pro now to create multiple order bump offers.', 'wpfnl' ),
5270 'upgrade_to_wpfunnels_pro' => __( 'Upgrade to WPFunnels Pro', 'wpfnl' ),
5271 'product_must_be_added' => __( 'Product must be added', 'wpfnl' ),
5272 'offer_must_be_selected' => __( 'Offer must be selected', 'wpfnl' ),
5273 'ob_default_product_name' => __( '6D Screen Protector (20% OFF)', 'wpfnl' ),
5274 'ob_highLight' => __( 'Special one time offer', 'wpfnl' ),
5275 'ob_checkbox_label' => __( 'Grab this offer with one click!', 'wpfnl' ),
5276 'ob_product_details' => __( 'Get this scratch proof 6D Tempered Glass Screen Protector for your iPhone. Keep your phone safe and sound just like a new one.', 'wpfnl' ),
5277 'ob_preview_tooltip' => __( 'The width of the order bump may vary depending on the page/container size on the Checkout Page or the device screen size.', 'wpfnl' ),
5278 'choose_offers' => __( 'Which Products to Pick from', 'wpfnl' ),
5279 'choose_offers_condition' => __( 'Select the rule that determines which product(s) will be automatically recommended as the upsell.', 'wpfnl' ),
5280 'select_product' => __( 'Select Product', 'wpfnl' ),
5281 'select_product_tooltip' => __( 'Choose the product that you want to offer as order bump at the checkout.', 'wpfnl' ),
5282 'search_for_product' => __( 'Search for product', 'wpfnl' ),
5283 'search_for_course' => __( 'Search for course', 'wpfnl' ),
5284 'select_course' => __( 'Select Course', 'wpfnl' ),
5285 'choose_the' => __( 'Choose the', 'wpfnl' ),
5286 'course' => __( 'course', 'wpfnl' ),
5287 'select_course_tooltip' => __( 'that you want to offer as order bump at the checkout.', 'wpfnl' ),
5288 'course_title' => __( 'Course Title', 'wpfnl' ),
5289 'product_title' => __( 'Product Title', 'wpfnl' ),
5290 'select_color_for_title' => __( 'Select Color for Title', 'wpfnl' ),
5291 'course_quantity' => __( 'Course Quantity', 'wpfnl' ),
5292 'product_quantity' => __( 'Product Quantity', 'wpfnl' ),
5293 'highlight_text' => __( 'Highlight Text', 'wpfnl' ),
5294 'hilight_text_color' => __( 'Select Color for Highlight Text', 'wpfnl' ),
5295 'checkbox_label' => __( 'Checkbox Label', 'wpfnl' ),
5296 'checkbox_title_color' => __( 'Select Color for Checkbox Title', 'wpfnl' ),
5297 'course_description' => __( 'Course Description', 'wpfnl' ),
5298 'product_description' => __( 'Product Description', 'wpfnl' ),
5299 'product_description_color' => __( 'Select Color for Product Description', 'wpfnl' ),
5300 'ob_delete_confirmation' => __( 'Are you sure you want to Delete the', 'wpfnl' ),
5301 'enable_order_bump' => __( 'Enable Order Bump', 'wpfnl' ),
5302 'delete_order_bump' => __( 'Delete Order Bump', 'wpfnl' ),
5303 'ob_name' => __( 'Order Bump Name', 'wpfnl' ),
5304 'select_template' => __( 'Template Style', 'wpfnl' ),
5305 'select_template_tooltip' => __( 'Choose a suitable template for your Order bump design.', 'wpfnl' ),
5306 'ob_position' => __( 'Display Position', 'wpfnl' ),
5307 'ob_position_tooltip' => __( 'Decide where the order bump offer should be placed in the checkout page.', 'wpfnl' ),
5308 'ob_arrow_color' => __( 'Arrow Color', 'wpfnl' ),
5309 'ob_remove_arrow_color' => __( 'Remove arrow color', 'wpfnl' ),
5310 'ob_bg_color' => __( 'Background Color', 'wpfnl' ),
5311 'ob_bg_color_tooltip' => __( 'Change the color of the Checkbox bar if you want to.', 'wpfnl' ),
5312 'ob_remove_bg_color' => __( 'Remove background color', 'wpfnl' ),
5313 'ob_main_color' => __( 'Main Color', 'wpfnl' ),
5314 'ob_main_color_tooltip' => __( 'Change the color of the Checkbox bar if you want to.', 'wpfnl' ),
5315 'ob_price_color' => __( 'Price Color', 'wpfnl' ),
5316 'ob_choose_variant_text' => __( 'Order Bump Choose Variant Text', 'wpfnl' ),
5317 'select_color_for_variant_text' => __( 'Select Color for Variant Text', 'wpfnl' ),
5318 'price' => __( 'Price', 'wpfnl' ),
5319 'regular_price' => __( 'Regular Price', 'wpfnl' ),
5320 'ob_template10_title' => __( 'to your order for only', 'wpfnl' ),
5321 'offer_price' => __( 'Offer Price', 'wpfnl' ),
5322
5323 // -----mint automation------
5324 'select_offer' => __( 'Select Offer', 'wpfnl' ),
5325 'select_order_bump' => __( 'Select Order Bump', 'wpfnl' ),
5326
5327 // -----step body------
5328 'new_step' => __( 'New Step', 'wpfnl' ),
5329 'new_step_hints' => __( 'Use this button to create the first step of the funnel.', 'wpfnl' ),
5330 'delete_this_step' => __( 'Delete this step', 'wpfnl' ),
5331 'ab_testing_running' => __( 'A/B Test Running', 'wpfnl' ),
5332 'view_automation' => __( 'View Automation', 'wpfnl' ),
5333 'add_automation_action' => __( 'Add Automation Action', 'wpfnl' ),
5334 'automation_running' => __( 'Automation running', 'wpfnl' ),
5335 'add_automation' => __( 'Add Automation', 'wpfnl' ),
5336 'upload_funnel_feature_image' => __( 'Uploade Funnel Featured Image', 'wpfnl' ),
5337 'oxygen_step_url' => __( 'Oxygen Builder Step URL', 'wpfnl' ),
5338
5339 'no_product_added' => __( 'No Product Added', 'wpfnl' ),
5340 'no_course_added' => __( 'No Course Added', 'wpfnl' ),
5341 'run_ab_split_test' => __( 'Run A/B Split Test', 'wpfnl' ),
5342 'to_funnel_update_msg' => __( 'Please update to WPFunnels Pro 1.7.3 or higher to use this feature.', 'wpfnl' ),
5343 'conditions' => __( 'Conditions', 'wpfnl' ),
5344
5345 // -----vertical flow canvas------
5346 'order_bumps' => __( 'Order Bumps', 'wpfnl' ),
5347 'checkout_products' => __( 'Products', 'wpfnl' ),
5348 'store_conditions' => __( 'Store Conditions', 'wpfnl' ),
5349 'add_featured_image' => __( 'Add Featured Image', 'wpfnl' ),
5350 'automation' => __( 'Automation', 'wpfnl' ),
5351 'add_conditional_next_step' => __( 'Add Conditional Next Step', 'wpfnl' ),
5352 'add_step_here' => __( 'Add step here', 'wpfnl' ),
5353 'paste_here' => __( 'Paste here', 'wpfnl' ),
5354 'conditional' => __( 'Conditional', 'wpfnl' ),
5355 'click_to_configure' => __( 'Click to configure', 'wpfnl' ),
5356 'journey_ends_here' => __( 'Journey ends here', 'wpfnl' ),
5357 'activated_conditional_flow' => __( 'Activated Conditional Flow', 'wpfnl' ),
5358 'need_reconfigure_condition' => __( 'Need reconfigure condition', 'wpfnl' ),
5359
5360 // -----step settings drawer------
5361 'assign_the_main_funnel_product' => __( 'Assign the main funnel product here.', 'wpfnl' ),
5362 'courses' => __( 'Courses', 'wpfnl' ),
5363 'products' => __( 'Products', 'wpfnl' ),
5364 'set_ob_tooltip' => __( 'Set up an order bump offer at the checkout.', 'wpfnl' ),
5365 'edit_field_tooltip' => __( 'Checkout Field Editor for this funnel.', 'wpfnl' ),
5366 'edit_fields' => __( 'Edit Fields', 'wpfnl' ),
5367
5368 'add_new_field' => __( 'Add New Field', 'wpfnl' ),
5369 'select_section' => __( 'Select Section', 'wpfnl' ),
5370 'billing' => __( 'Billing', 'wpfnl' ),
5371 'shipping' => __( 'Shipping', 'wpfnl' ),
5372 'additional' => __( 'Additional', 'wpfnl' ),
5373 'type' => __( 'Type', 'wpfnl' ),
5374 'add_more' => __( 'Add More', 'wpfnl' ),
5375 'option_text' => __( 'Option text', 'wpfnl' ),
5376 'option_value' => __( 'Option value', 'wpfnl' ),
5377 'label' => __( 'Label', 'wpfnl' ),
5378 'placeholder' => __( 'Placeholder', 'wpfnl' ),
5379 'default_value' => __( 'Default Value', 'wpfnl' ),
5380 'validation' => __( 'Validation', 'wpfnl' ),
5381 'validations' => __( 'Validations', 'wpfnl' ),
5382 'required' => __( 'Required', 'wpfnl' ),
5383 'enabled' => __( 'Enabled', 'wpfnl' ),
5384 'display_in_order_detail' => __( 'Display in Order Detail Pages', 'wpfnl' ),
5385 'select_section_type' => __( 'Please select section type', 'wpfnl' ),
5386 'select_field_type' => __( 'Please select field type', 'wpfnl' ),
5387 'name_should_not_be_empty' => __( 'Name should not be empty', 'wpfnl' ),
5388 'label_should_not_be_empty' => __( 'Label should not be empty', 'wpfnl' ),
5389 'are_you_sure_delete_this_field' => __( 'Are you sure you want to delete this field?', 'wpfnl' ),
5390
5391 'billing_fields' => __( 'Billing Fields', 'wpfnl' ),
5392 'shipping_fields' => __( 'Shipping Fields', 'wpfnl' ),
5393 'additional_fields' => __( 'Additional Fields', 'wpfnl' ),
5394 'restore_to_default' => __( 'Restore to default', 'wpfnl' ),
5395 'add_field' => __( 'Add Field', 'wpfnl' ),
5396 'additional_options' => __( 'Additional Options', 'wpfnl' ),
5397 'custom_order_button_text' => __( 'Custom order button text', 'wpfnl' ),
5398 'custom_order_button_text_tooltip' => __( 'Set a custom text for the place order button.', 'wpfnl' ),
5399 'custom_billing_section_heading' => __( 'Custom Billing Section Heading', 'wpfnl' ),
5400 'custom_billing_section_heading_tooltip' => __( 'Set a custom heading text for the Billing Section.', 'wpfnl' ),
5401 'custom_shipping_section_heading' => __( 'Custom Shipping Section Heading', 'wpfnl' ),
5402 'custom_shipping_section_heading_tooltip' => __( 'Set a custom heading text for the Shipping Section.', 'wpfnl' ),
5403 'custom_order_detail_section_heading' => __( 'Custom Order Detail Section Heading', 'wpfnl' ),
5404 'custom_order_detail_section_heading_tooltip' => __( 'Set a custom heading text for the Order Detail Section.', 'wpfnl' ),
5405 'save_additional_changes' => __( 'Save Addtional Changes', 'wpfnl' ),
5406 'billint_details' => __( 'Billing details', 'wpfnl' ),
5407 'shipto_different_address' => __( 'Ship to a different address?', 'wpfnl' ),
5408 'your_order' => __( 'Your order', 'wpfnl' ),
5409 'place_order' => __( 'Place Order', 'wpfnl' ),
5410
5411 'this_product_in_trash' => __( 'This product is in the Trash. Please restore it', 'wpfnl' ),
5412 'recurring' => __( 'Recurring', 'wpfnl' ),
5413 'subtext' => __( 'Subtext', 'wpfnl' ),
5414 'enable_highlight' => __( 'Enable Highlight', 'wpfnl' ),
5415 'permanently_deleted' => __( 'This Product is permanently deleted', 'wpfnl' ),
5416 'edit_product' => __( 'Edit product', 'wpfnl' ),
5417 'delete_product' => __( 'Delete product', 'wpfnl' ),
5418
5419 // -------product tab content----
5420 'funnel_products' => __( 'Funnel Products', 'wpfnl' ),
5421 'funnel_products_tooltip' => __( 'This will be the main offer product of the funnel. Search for a product, select it, and then click on the "+ Add Product" button.', 'wpfnl' ),
5422 'add_course' => __( 'Add course', 'wpfnl' ),
5423 'total' => __( 'Total', 'wpfnl' ),
5424 'allow_use_of_coupon' => __( 'Allow Use Of Coupon', 'wpfnl' ),
5425 'allow_use_of_coupon_tooltip' => __( 'Enable this if you want to allow buyers to use coupons during checkout in the funnel.', 'wpfnl' ),
5426 'collapsible_coupon_field' => __( 'Collapsible Coupon Field', 'wpfnl' ),
5427 'collapsible_coupon_field_tooltip' => __( 'Enable this to show the coupon field in a collapsible section on the checkout page.', 'wpfnl' ),
5428 'collapsible' => __( 'Collapsible', 'wpfnl' ),
5429 'coupon_auto_applied' => __( 'Coupon (auto-applied)', 'wpfnl' ),
5430 'coupon_auto_applied_tooltip' => __( 'Enable this to allow Auto-apply Coupon during the checkout on this funnel.', 'wpfnl' ),
5431 'quantity_limit' => __( 'Enable Quantity Limit', 'wpfnl' ),
5432 'quantity_limit_tooltip' => __( 'Enable this settings to set the quantity limit for a single user', 'wpfnl' ),
5433 'set_quantity_limit' => __( 'Set the quantity limit for a single user', 'wpfnl' ),
5434 'set_quantity_limit_tooltip' => __( 'Set the quantity limit for a single user', 'wpfnl' ),
5435 'choose_coupon' => __( 'Choose coupon', 'wpfnl' ),
5436 'choose_coupon_tooltip' => __( 'Choose any existing WooCommerce coupon from the dropdown.', 'wpfnl' ),
5437 'enable_quantity_selection' => __( 'Enable Quantity Selection', 'wpfnl' ),
5438 'enable_quantity_selection_tooltip' => __( 'Enable this if you want to allow buyers to change the quantity of the product they want to buy.', 'wpfnl' ),
5439
5440 // ------downsell-------
5441 'upsell_course' => __( 'Upsell Course', 'wpfnl' ),
5442 'downsell_course' => __( 'Downsell Course', 'wpfnl' ),
5443 'downsell_product' => __( 'Downsell Product', 'wpfnl' ),
5444 'select_an_offer' => __( 'Please select an offer', 'wpfnl' ),
5445 'select_category' => __( 'Select Category', 'wpfnl' ),
5446 'choose_offer_category' => __( 'Choose offer category', 'wpfnl' ),
5447 'choose_offer_tag' => __( 'Choose offer tag', 'wpfnl' ),
5448 'selected_price_is_less_than1' => __( 'Selected price is less than 1', 'wpfnl' ),
5449 'gbf_product_quantity_tooltip' => __( 'The number of this upsell product to add to the cart when the customer accepts the offer.', 'wpfnl' ),
5450 'selected_quantity_is_less_than1' => __( 'Selected quantity is less than 1', 'wpfnl' ),
5451 'enable_replace_settings' => __( 'Enable Replace Settings', 'wpfnl' ),
5452 'enable_replace_settings_tooltip' => __( 'Enabling this will mean that if a buyer accepts the downsell offer, then the downsell offer product will replace the main product in the checkout and the buyer will just have to pay for the downsell offer product.', 'wpfnl' ),
5453 'replace_main_order_including_order_bump' => __( 'Replace Main Order Including Order Bump', 'wpfnl' ),
5454 'replace_main_order_excluding_order_bump' => __( 'Replace Main Order Excluding Order bump', 'wpfnl' ),
5455 'replace_all_prior_orders' => __( 'Replace All Prior Orders', 'wpfnl' ),
5456 'replace_order_in_previous_step' => __( 'Replace Order in Previous Step', 'wpfnl' ),
5457 'enable_replace_with_main_product' => __( 'Enable replace with the main product.', 'wpfnl' ),
5458 'enable_replace_with_main_product_tooltip1' => __( 'You must enable child orders for offers to use this feature.', 'wpfnl' ),
5459 'enable_replace_with_main_product_tooltip2' => __( 'Go to WPFunnels > Settings > Offer Settings and enable "Create a new child order."', 'wpfnl' ),
5460 'product_must_be_added' => __( 'Product must be added', 'wpfnl' ),
5461 'offer_must_be_selected' => __( 'Offer must be selected', 'wpfnl' ),
5462
5463 // ------thank you settings-------
5464 'show_order_overview' => __( 'Show Order Overview', 'wpfnl' ),
5465 'show_order_details' => __( 'Show Order Details', 'wpfnl' ),
5466 'show_billing_details' => __( 'Show Billing Details', 'wpfnl' ),
5467 'show_shipping_details' => __( 'Show Shipping Details', 'wpfnl' ),
5468 'thankyou_redicrection_page' => __( 'Thank You Page Redirection', 'wpfnl' ),
5469 'thankyou_redicrection_page_tooltip' => __( 'Redirect buyers to an external link after viewing the thank you page for a few seconds or take the buyer to another page instead of the thank you page.', 'wpfnl' ),
5470 'redirect_url' => __( 'Redirect URL', 'wpfnl' ),
5471 'redirect_without_viewing_thankyou' => __( 'Redirect without viewing Thank You Page', 'wpfnl' ),
5472 'redirect_without_viewing_thankyou_tooltip' => __( 'The buyer will be taken to the redirect URL directly instead of the thank you page.', 'wpfnl' ),
5473 'redirect_after_viewing_thankyou' => __( 'Redirect after viewing Thank You Page', 'wpfnl' ),
5474 'redirect_after_viewing_thankyou_tooltip' => __( 'The buyer be taken to thank you page, and after the set time, will be redirected to the given redirect URL.', 'wpfnl' ),
5475 'redirect_after_in_second' => __( 'Redirect after [ in second ]', 'wpfnl' ),
5476
5477 // -----upsell settings-----
5478 'upsell_product' => __( 'Upsell Product', 'wpfnl' ),
5479 'please_select_a_category' => __( 'Please select a category', 'wpfnl' ),
5480 'please_select_a_tag' => __( 'Please select a tag', 'wpfnl' ),
5481 'upsell_product_quantity_tooltip' => __( 'The value you input here will be added to the quantity of the product that triggered the funnel, which will be offered to the buyer as the upsell offer. For example, if someone purchases 2 t-shirts, and you input 3 here, the buyer will be offered 2+3 = 5 t-shirts as the upsell offer.', 'wpfnl' ),
5482 'upsell_enable_replace_settings_tooltip' => __( 'Enabling this will mean that if a buyer accepts the upsell offer, then the upsell offer product will replace the main product in the checkout and the buyer will just have to pay for the upsell offer product.', 'wpfnl' ),
5483
5484 // -------funnels template-------
5485 'steps' => __( 'Steps', 'wpfnl' ),
5486 'step' => __( 'Step', 'wpfnl' ),
5487 'getting_ready_to_import' => __( 'Getting ready to import', 'wpfnl' ),
5488 'step_creating' => __( 'Creating Step...', 'wpfnl' ),
5489 'click_to_upgrade' => __( 'Click to Upgrade Pro', 'wpfnl' ),
5490 'template_builder_tooltip1' => __( 'as your preferred page builder, but you do not have the plugin activated on your site. Please install and activate', 'wpfnl' ),
5491 'if_you_want_to_create_funnels_without_using' => __( 'If you want to create funnels without using', 'wpfnl' ),
5492 'create_funnel_from_scratch' => __( 'You can go ahead and create funnels from scratch with any page builder/editor. It will work just fine.', 'wpfnl' ),
5493 'we_have_ready_templates_for' => __( 'As of now, we have ready templates for', 'wpfnl' ),
5494 'more_template_coming_soon' => __( 'only, but we are planning to create templates for more builders soon.', 'wpfnl' ),
5495 'create_a_funnel' => __( 'Create A Funnel', 'wpfnl' ),
5496 'how_to_use_this_funnel' => __( 'How to use this funnel', 'wpfnl' ),
5497 'creating_funnel' => __( 'Creating Funnel...', 'wpfnl' ),
5498 'creating_store_checkout' => __( 'Creating Store Checkout...', 'wpfnl' ),
5499 'write_funnel_name' => __( 'Write Funnel Name', 'wpfnl' ),
5500 'templates' => __( 'Templates', 'wpfnl' ),
5501 'template' => __( 'Template', 'wpfnl' ),
5502 'please_wait' => __( 'Please Wait...', 'wpfnl' ),
5503
5504 // -------Available payment gateways-------
5505 'disable_payment_gateways' => __( 'Disable payment gateways for this checkout', 'wpfnl' ),
5506 'disable_payment_gateways_tooltip' => __( 'Disable payment gateways for this checkout to restrict payment options. This setting is useful for special cases where you want to manage payments manually or through alternative methods.', 'wpfnl' ),
5507
5508 // -------Time bound discount-------
5509 'startDateText' => __( 'Start Date', 'wpfnl' ),
5510 'endDateText' => __( 'End Date', 'wpfnl' ),
5511 'enableTimeBoundDiscountText' => __( 'Enable time bound discount', 'wpfnl' ),
5512 'enableTimeBoundDiscountTooltipText' => __( 'This allows you to set a date range for offering discounts. Simply select the start and end dates, and the discount will automatically apply to purchases made during this period.', 'wpfnl' ),
5513 'undo' => __('Undo', 'wpfnl'),
5514 'stepCopied' => __('Step copied', 'wpfnl'),
5515 'copy' => __('Copy', 'wpfnl'),
5516
5517 // -------Automation Analytics-------
5518 'report' => __('Report', 'wpfnl'),
5519 'back' => __('Back', 'wpfnl'),
5520 'automation' => __('Automation', 'wpfnl'),
5521 'performance' => __('Performance', 'wpfnl'),
5522 'open_rate' => __('Open Rate', 'wpfnl'),
5523 'click_rate' => __('Click Rate', 'wpfnl'),
5524 'email_sent' => __('Email Sent', 'wpfnl'),
5525 'open' => __('Open', 'wpfnl'),
5526 'click' => __('Click', 'wpfnl'),
5527 'overall_report' => __('Overall Report', 'wpfnl'),
5528 'entrance' => __('Entrance', 'wpfnl'),
5529 'subscribers_completed' => __('Subscribers Completed', 'wpfnl'),
5530 'welcome_to_wpfunnels' => __('Welcome to WPFunnels', 'wpfnl'),
5531 'setup_wizard' => __('Setup Wizard', 'wpfnl'),
5532 'support' => __('Support', 'wpfnl'),
5533 'video' => __('Video', 'wpfnl'),
5534
5535 'total_customers' => __('Total Customers', 'wpfnl'),
5536 'total_orders' => __('Total Orders', 'wpfnl'),
5537 'total_revenue' => __('Total Revenue', 'wpfnl'),
5538 'order_bump_revenue' => __('Order Bump Revenue', 'wpfnl'),
5539 'upsell_downsell_evenue' => __('Upsell/Downsell Revenue', 'wpfnl'),
5540 'growth_comparison' => __('Growth Comparison', 'wpfnl'),
5541 'previous_year' => __('Previous Year', 'wpfnl'),
5542 'current_year' => __('Current Year', 'wpfnl'),
5543 'top_performing_funnels' => __('Top Performing Funnels', 'wpfnl'),
5544 'funnel_name' => __('Funnel Name', 'wpfnl'),
5545 'views' => __('Views', 'wpfnl'),
5546 'conversion' => __('Conversion', 'wpfnl'),
5547 'conversion_rate' => __('Conversion Rate', 'wpfnl'),
5548 'no_top_performing_funnels' => __('You have no top performing funnels', 'wpfnl'),
5549 'today' => __('Today', 'wpfnl'),
5550 'week_to_date' => __('Week to date', 'wpfnl'),
5551 'month_to_date' => __('Month to date', 'wpfnl'),
5552 'year_to_date' => __('Year to date', 'wpfnl'),
5553 'previous_period' => __('Previous period', 'wpfnl'),
5554 'previous_year' => __('Previous year', 'wpfnl'),
5555
5556 // -------Funnel dashboard-------
5557
5558 // tour text
5559 'add_product_on_canvas' => __('Add Product', 'wpfnl'),
5560 'add_product_on_canvas_des' => __('To add a product to this funnel, click the cart icon. You can select an existing product from your store or create a new one. This will add the product to your funnel and allow you to adjust its quantity, price, and other details.', 'wpfnl'),
5561 'update_funnel_name' => __('Update Funnel Name', 'wpfnl'),
5562 'update_funnel_name_des' => __('To give your funnel a unique and recognizable name, simply click the Funnel Name field at the top. Enter your new name and make sure to Save your changes to update the funnel details.', 'wpfnl'),
5563 'view_funnel_stats' => __('View Funnel Stats', 'wpfnl'),
5564 'view_funnel_stats_des' => __("To monitor the performance of your funnel, click on the Stats button or navigate to the Analytics section. Here, you'll see key metrics like total leads, conversions, and revenue, helping you track and optimize your funnel's success.", 'wpfnl'),
5565 'preview_funnel' => __("Preview Funnel", 'wpfnl'),
5566 'preview_funnel_des' => __("Click the Preview button to see how your funnel looks in real-time. This allows you to review each step and ensure everything is set up as you want before going live.", 'wpfnl'),
5567 'ab_testing_on_canvas' => __("Enable A/B Testing", 'wpfnl'),
5568 'ab_testing_on_canvas_des' => __("Activate A/B Testing to compare different versions of your funnel steps. This feature allows you to test variations in content, design, and calls to action to determine which version performs better. Use the insights gained to optimize your funnel for maximum conversions.", 'wpfnl'),
5569 'condition_on_canvas' => __("Enable Conditional Funnel", 'wpfnl'),
5570 'condition_on_canvas_des' => __("To create a tailored experience for your users, enable the Conditional Funnel feature. This allows you to set specific conditions that determine which steps users will see based on their actions or selections. Customize your funnel flow to increase engagement and conversion rates.", 'wpfnl'),
5571 'add_new_step' => __("Add New Step", 'wpfnl'),
5572 'add_new_step_des' => __("To enhance your funnel, click the + Add New Step button. You can create various types of steps, such as landing pages, product pages, or thank-you pages. Each step allows you to customize content and settings to guide your customers effectively through the funnel.", 'wpfnl'),
5573 'search_ob' => __("Search and Select Order Bump Product", 'wpfnl'),
5574 'search_ob_des' => __("Use the search bar to find an existing product to offer as an order bump. Once you've found the product, select it to include it as an optional add-on during checkout. This will help increase your average order value.", 'wpfnl'),
5575 'modify_ob_title' => __("Modify Product Title", 'wpfnl'),
5576 'modify_ob_title_des' => __("To edit the product title, click on the product name field and enter a new title that accurately describes the product. A clear and concise title helps customers quickly understand what you're offering.", 'wpfnl'),
5577 'choose_ob_style' => __("Choose Order Bump Style", 'wpfnl'),
5578 'choose_ob_style_des' => __("Customize the appearance of your order bump by selecting a style that fits your funnel’s design. You can choose from various layout options and adjust colors, fonts, and other visual elements to make your order bump stand out during checkout.", 'wpfnl'),
5579 'select_ob_position' => __("Select Order Bump Position", 'wpfnl'),
5580 'select_ob_position_des' => __("Select where you want the order bump to appear during checkout. You can position it above or below the payment details to ensure maximum visibility and encourage customers to add the extra product.", 'wpfnl'),
5581 'save_ob' => __("Save Your Order Bump", 'wpfnl'),
5582 'save_ob_des' => __("Make sure to click the Save button to keep your order bump settings. This ensures that your changes are applied and your order bump is active for your funnel. Don’t forget to review all details before saving!", 'wpfnl'),
5583 );
5584 }
5585
5586 /**
5587 * Maybe current theme is bricks
5588 *
5589 * @return bool
5590 * @since 3.1.0
5591 */
5592 public static function maybe_bricks_theme() {
5593 $current_theme = wp_get_theme();
5594 $parent_theme = $current_theme->parent();
5595
5596 if ('Bricks' === $current_theme->get('Name')) {
5597 return true;
5598 }
5599
5600 if ($parent_theme && 'Bricks' === $parent_theme->get('Name')) {
5601 return true;
5602 }
5603
5604 return false;
5605 }
5606
5607
5608 /**
5609 * Maybe current theme is WoodMart
5610 *
5611 * @return bool
5612 * @since 3.1.0
5613 */
5614 public static function maybe_woodmart_theme() {
5615 $current_theme = wp_get_theme();
5616 $parent_theme = $current_theme->parent();
5617
5618 if ('Woodmart' === $current_theme->get('Name')) {
5619 return true;
5620 }
5621
5622 if ($parent_theme && 'Woodmart' === $parent_theme->get('Name')) {
5623 return true;
5624 }
5625
5626 return false;
5627 }
5628
5629
5630 /**
5631 * Get WooCommerce price & currency configuration
5632 *
5633 * @return array
5634 * @since 3.0.13
5635 */
5636 public static function get_wc_price_config() {
5637 $config = array(
5638 'currency_symbol' => '$',
5639 'currency_position' => 'left',
5640 'thousand_separator' => ',',
5641 'decimal_separator' => '.',
5642 'decimal_places' => 2,
5643 'price_format' => '%1$s%2$s',
5644 );
5645
5646 if ( self::is_wc_active() ) {
5647 $config = array(
5648 'currency_symbol' => get_woocommerce_currency_symbol(),
5649 'currency_position' => get_option( 'woocommerce_currency_pos' ),
5650 'thousand_separator' => wc_get_price_thousand_separator(),
5651 'decimal_separator' => wc_get_price_decimal_separator(),
5652 'decimal_places' => wc_get_price_decimals(),
5653 'price_format' => get_woocommerce_price_format(),
5654 );
5655 }
5656 return $config;
5657 }
5658
5659
5660 /**
5661 * Get default meta for each steps
5662 *
5663 * @param $step_type
5664 * @return array|array[]
5665 *
5666 * @since 3.1.0
5667 */
5668 public static function get_step_default_meta( $step_type ) {
5669 $default_meta = array();
5670 switch ( $step_type ) {
5671 case 'landing':
5672 $default_meta = Wpfnl_Step_Meta_keys::get_landing_meta();
5673 break;
5674 case 'checkout':
5675 $default_meta = Wpfnl_Step_Meta_keys::get_checkout_meta();
5676 break;
5677 case 'thankyou':
5678 $default_meta = Wpfnl_Step_Meta_keys::get_thankyou_meta();
5679 break;
5680 case 'upsell':
5681 case 'downsell':
5682 $default_meta = Wpfnl_Step_Meta_keys::get_offer_meta();
5683 break;
5684 case 'custom':
5685 $default_meta = Wpfnl_Step_Meta_keys::get_custom_meta();
5686 break;
5687 default:
5688 break;
5689 }
5690 return $default_meta;
5691 }
5692
5693
5694 /**
5695 * Get step type from step id
5696 *
5697 * @param $step_id
5698 * @return mixed
5699 * @since 3.1.0
5700 */
5701 public static function get_step_type( $step_id ) {
5702 return get_post_meta( $step_id, '_step_type', true );
5703 }
5704
5705 /**
5706 * Check the current page is funnel page
5707 *
5708 * @return bool
5709 * @since 3.1.4
5710 */
5711 public static function maybe_funnel_page() {
5712 $supported_pages = array( 'wpfnl_settings', 'wpf-license', 'edit_funnel', 'wp_funnels' );
5713 if ( isset( $_GET['page'] ) && in_array( $_GET['page'], $supported_pages ) ) {
5714 return true;
5715 }
5716 return false;
5717 }
5718
5719
5720
5721 /**
5722 * Retrieves the list of enabled payment gateways.
5723 *
5724 * @return array The list of enabled payment gateways.
5725 *
5726 * @since 3.1.8
5727 */
5728 public static function get_enabled_payment_gateways() {
5729 if ( ! self::is_wc_active() ) {
5730 return array();
5731 }
5732 $gateways = WC()->payment_gateways->get_available_payment_gateways();
5733 $enabled_gateways = array();
5734 if ( is_array( $gateways ) && ! empty( $gateways ) ) {
5735 foreach ( $gateways as $gateway ) {
5736 if ( $gateway->enabled == 'yes' ) {
5737 $enabled_gateways[ $gateway->id ] = $gateway->title;
5738 }
5739 }
5740 }
5741 return $enabled_gateways;
5742 }
5743
5744
5745 /**
5746 * Check the slug is already available or not
5747 *
5748 * @param string $slug
5749 * @param int $post_id
5750 * @param string $title
5751 * @return bool
5752 *
5753 * @since 3.1.8
5754 */
5755 public static function is_slug_available( $slug, $post_id = 0, $title = '' ) {
5756 global $wpdb;
5757
5758 // If no title is provided, use the slug itself
5759 if ( empty( $title ) ) {
5760 $title = $slug;
5761 }
5762
5763 // Check if the generated slug exists in the database
5764 $query = $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d", $slug, $post_id );
5765 $result = $wpdb->get_var( $query );
5766
5767 // If the result is null, the slug is available
5768 if ( is_null( $result ) ) {
5769 return true;
5770 }
5771
5772 return false;
5773 }
5774
5775
5776 // Function to get the total price of a composite product
5777 public static function get_composite_product_price( $composite_product_id, $use_regular_price = false ) {
5778 // Get the composite product object
5779 $composite_product = wc_get_product( $composite_product_id );
5780
5781 // Check if the product is a composite product
5782 if ( ! $composite_product || 'composite' !== $composite_product->get_type() ) {
5783 return 'The product is not a composite product.';
5784 }
5785
5786 // Initialize total price
5787 $total_price = $use_regular_price ? $composite_product->get_regular_price() : $composite_product->get_price();
5788
5789 // Get components
5790 $components = $composite_product->get_components();
5791
5792 // Loop through each component
5793 foreach ( $components as $component_id => $component ) {
5794 // Get the selected option (or default option if no selection)
5795 $selected_option = $component->get_default_option();
5796
5797 // Check if component is optional and no selection made
5798 if ( $component->is_optional() && ! $selected_option ) {
5799 continue;
5800 }
5801
5802 // Get the product object for the selected option
5803 $product = wc_get_product( $selected_option );
5804 $configuration = array();
5805 // Calculate price based on selected option and quantity
5806 if ( $product ) {
5807 $component_quantity = $component->get_quantity( $configuration );
5808 $product_price = $use_regular_price ? $product->get_regular_price() : $product->get_price();
5809
5810 // Add the price of the selected option multiplied by the quantity
5811 $total_price += $product_price * $component_quantity;
5812 }
5813 }
5814
5815 // Apply any composite product specific pricing rules or discounts
5816 // Note: This step may vary based on how your site applies discounts or custom pricing rules
5817 // $total_price = apply_composite_product_discounts($composite_product, $total_price);
5818
5819 // Format and return the total price
5820 return $total_price;
5821 }
5822
5823
5824 public static function add_composite_product_to_cart_with_defaults( $composite_product_id, $quantity, $use_regular_price = false ) {
5825
5826 // Get the composite product object
5827 $composite_product = wc_get_product( $composite_product_id );
5828
5829 if ( ! defined( 'WC_CP_VERSION' ) ) {
5830 return $use_regular_price ? $composite_product->get_regular_price() : $composite_product->get_price();
5831 }
5832
5833 // Check if the product is a composite product
5834 if ( ! $composite_product || 'composite' !== $composite_product->get_type() ) {
5835 return 'The product is not a composite product.';
5836 }
5837
5838 // Prepare the components array for the cart
5839 $cart_item_data = array(
5840 'composite_data' => array(),
5841 );
5842
5843 $components = $composite_product->get_components();
5844
5845 foreach ( $components as $component_id => $component ) {
5846 // Get the default option for the component
5847 $selected_option_id = $component->get_default_option();
5848
5849 if ( ! $selected_option_id && ! $component->is_optional() ) {
5850 return 'Missing required component option for component ID ' . $component_id;
5851 }
5852
5853 // Get the product object for the selected option
5854 $product = wc_get_product( $selected_option_id );
5855
5856 // Assuming configuration array as an empty array for this example
5857 // Ideally, this should contain the current state of the component configuration
5858 $configuration = array(); // You should replace this with actual configuration if available
5859
5860 // Calculate quantity
5861 $component_quantity = $component->get_quantity( $configuration );
5862
5863 // Add selected option and quantity to cart item data if the option is not null
5864 if ( $selected_option_id ) {
5865 $cart_item_data['composite_data'][ $component_id ] = array(
5866 'product_id' => $selected_option_id,
5867 'quantity' => $component_quantity,
5868 );
5869 }
5870 }
5871
5872 // Add the composite product to the cart
5873 $added = WC()->cart->add_to_cart( $composite_product_id, $quantity, 0, array(), $cart_item_data );
5874
5875 if ( $added ) {
5876 return 'Composite product added to cart successfully.';
5877 } else {
5878 return 'Failed to add composite product to cart.';
5879 }
5880 }
5881
5882
5883
5884 /**
5885 * Checks if a theme is installed.
5886 *
5887 * @param string $theme_slug The slug of the theme to check.
5888 * @return bool True if the theme is installed, false otherwise.
5889 *
5890 * @since 3.3.1
5891 */
5892 public static function maybe_theme_installed( $theme_slug ) {
5893 $theme = wp_get_theme( $theme_slug );
5894 // Check if the theme exists and is not an empty theme
5895 if ( $theme->exists() ) {
5896 return true;
5897 } else {
5898 return false;
5899 }
5900 }
5901
5902
5903 /**
5904 * Validate google autocomplete API key
5905 *
5906 * @param string $api_key
5907 * @return bool
5908 * @since 3.4.8
5909 */
5910 public static function validate_google_places_api_key($api_key) {
5911 if (!is_string($api_key)) {
5912 // Throw an error or handle this case as appropriate for your application
5913 throw new InvalidArgumentException('API key must be a string');
5914 }
5915
5916 $url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?key=' . $api_key;
5917
5918 $response = wp_remote_get($url);
5919
5920 if (is_wp_error($response)) {
5921 return false;
5922 }
5923
5924 $body = wp_remote_retrieve_body($response);
5925 $data = json_decode($body, true);
5926
5927 if (isset($data['status']) && 'REQUEST_DENIED' === $data['status'] ) {
5928 return false;
5929 }
5930
5931 return true;
5932 }
5933
5934
5935 /**
5936 * Get Guided tour
5937 *
5938 * @return false|mixed|null
5939 * @since 3.4.16
5940 */
5941
5942 public static function get_guided_tour() {
5943 $guided_tour_default = apply_filters('wpfnl_guided_tour_default', [
5944 'checkout',
5945 'upsell',
5946 'downsell',
5947 'orderbump',
5948 'condition',
5949 'add-step',
5950 'a/b-testing',
5951 'mm-automation',
5952 'canvas-step',
5953 'view-step',
5954 'edit-step',
5955 ]);
5956 $guided_tour = get_option('wpfnl_guided_tour');
5957 // Check if the option is not found and return the default
5958 if (false === $guided_tour) {
5959 update_option('wpfnl_guided_tour',$guided_tour_default);
5960 return $guided_tour_default;
5961 }
5962 return $guided_tour;
5963 }
5964
5965 /**
5966 * Check if the Global Thank You Page feature is enabled.
5967 *
5968 * This method retrieves the `_wpfunnels_offer_settings` option from the database
5969 * and checks whether the `enable_global_thankyou` setting is set to `'on'`.
5970 *
5971 * @since 3.5.31
5972 *
5973 * @return bool True if the Global Thank You Page is enabled, false otherwise.
5974 */
5975 public static function is_global_thank_you_page_enabled() {
5976 $offer_settings = get_option('_wpfunnels_offer_settings');
5977 if( isset($offer_settings['enable_global_thankyou']) && $offer_settings['enable_global_thankyou'] ){
5978 return 'on' == $offer_settings['enable_global_thankyou'] ? true : false;
5979 }
5980 return false;
5981 }
5982
5983
5984 /**
5985 * Retrieves the product type of an offer.
5986 *
5987 * @param string $step_id The ID of the step.
5988 * @return string The product type of the offer.
5989 */
5990 public static function get_offer_product_type( $step_id = '' ) {
5991
5992 if ( $step_id ) {
5993 $step_type = get_post_meta( $step_id, '_step_type', true );
5994 if ( $step_type == 'upsell' || $step_type == 'downsell' ) {
5995 $offer_product = self::get_offer_product( $step_id, $step_type );
5996 if ( is_array( $offer_product ) ) {
5997 $product_type = '';
5998 foreach ( $offer_product as $pr_index => $pr_data ) {
5999 $product_id = $pr_data['id'];
6000 $product = wc_get_product( $product_id );
6001 if ( $product ) {
6002 $product_type = $product->get_type();
6003 break;
6004 }
6005 }
6006 return $product_type;
6007 }
6008 }
6009 }
6010 return false;
6011 }
6012
6013 /**
6014 * Retrieves the offer product for a given step ID.
6015 *
6016 * @param int $step_id The ID of the funnel step.
6017 * @param string $type The type of offer product (default is 'upsell').
6018 * @param mixed $default The default value to return if no offer product is found.
6019 *
6020 * @return mixed The offer product data or the default value if not found.
6021 *
6022 * @since 1.0.0
6023 */
6024 public static function get_offer_product( $step_id, $type = 'upsell', $default = false ) {
6025 if ( $default ) {
6026 return $default;
6027 }
6028 $value = get_post_meta( $step_id, "_wpfnl_{$type}_products", true );
6029 if ( ! $value ) {
6030 return array();
6031 }
6032 return $value;
6033 }
6034
6035
6036 /**
6037 * Get product data for widget
6038 *
6039 * @param String
6040 *
6041 * @return Array
6042 * @since 1.6.8
6043 */
6044 public static function get_product_data_for_widget( $step_id = '' ) {
6045
6046 if ( $step_id && Wpfnl_functions::is_wc_active() ) {
6047 $funnel_id = get_post_meta( $step_id, '_funnel_id', true );
6048 $step_type = get_post_meta( $step_id, '_step_type', true );
6049 $is_gbf = get_post_meta( $funnel_id, 'is_global_funnel', true );
6050 $offer_product = '';
6051 if ( 'yes' === $is_gbf && ( 'upsell' === $step_type || 'downsell' === $step_type ) ) {
6052 if ( self::is_global_funnel_activated() ) {
6053 $instance = new \WPFunnelsPro\GBF\Wpfnl_Pro_GBF_Functions();
6054 $offer_rules = get_post_meta( $step_id, 'global_funnel_' . $step_type . '_rules', true );
6055 $quantity = isset( $offer_rules['quantity'] ) ? $offer_rules['quantity'] : 1;
6056 $rules_type = isset( $offer_rules['type'] ) ? $offer_rules['type'] : '';
6057 if ( 'moreQuantity' == $rules_type ) {
6058 $rand_product_id = self::get_random_product();
6059 $offer_product = wc_get_product( $rand_product_id );
6060 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6061 $quantity = isset( $gbf_product[0]['quantity'] ) ? $quantity + $gbf_product[0]['quantity'] : $quantity;
6062
6063 } elseif ( 'specificProduct' == $rules_type ) {
6064 $rules = get_post_meta( $step_id, 'global_funnel_' . $step_type . '_rules', true );
6065 $offer_product = '';
6066 if ( is_array( $rules ) ) {
6067 if ( isset( $rules['show'] ) && $rules['show'] ) {
6068 $offer_product = wc_get_product( $rules['show'] );
6069 }
6070 }
6071 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6072
6073 } elseif ( 'randomProduct' == $rules_type ) {
6074 $offer_mappings = get_post_meta( $step_id, 'global_funnel_' . $step_type . '_rules', true );
6075 if ( $offer_mappings ) {
6076 $category = isset( $offer_mappings['category'] ) ? $offer_mappings['category'] : null;
6077 $function_exist = is_callable( array( $instance, 'get_random_product_in_category_for_offer' ) );
6078 if ( $function_exist ) {
6079 $id = '';
6080 if ( is_admin() ) {
6081 $dynamic_product = Wpfnl_Pro_GBF_Functions::get_random_product_in_shop_for_widget( 1 );
6082 if ( isset( $dynamic_product[0]->ID ) ) {
6083 $id = $dynamic_product[0]->ID;
6084 }
6085 } else {
6086 $dynamic_product = json_decode( wp_unslash( get_option( 'wpfunnels_dynamic_offer_data' ) ), true );
6087 if ( is_array( $dynamic_product ) && isset( $dynamic_product[0]['ID'] ) ) {
6088 $id = $dynamic_product[0]['ID'];
6089 }
6090 }
6091
6092 if ( $id ) {
6093 $offer_product = wc_get_product( $id );
6094 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6095 }
6096 }
6097 }
6098 } elseif ( 'randomProductInsideCategoryWithinPrice' == $rules_type ) {
6099 $offer_mappings = get_post_meta( $step_id, 'global_funnel_' . $step_type . '_rules', true );
6100 if ( $offer_mappings ) {
6101 $category = isset( $offer_mappings['category'] ) ? $offer_mappings['category'] : null;
6102 $function_exist = is_callable( array( $instance, 'get_random_product_in_category_within_price_for_offer_for_widget' ) );
6103
6104 if ( $function_exist ) {
6105 $id = '';
6106 if ( is_admin() ) {
6107 $dynamic_product = Wpfnl_Pro_GBF_Functions::get_random_product_in_category_within_price_for_offer_for_widget( 1 );
6108 if ( isset( $dynamic_product[0]->ID ) ) {
6109 $id = $dynamic_product[0]->ID;
6110 }
6111 } else {
6112 $dynamic_product = json_decode( wp_unslash( get_option( 'wpfunnels_dynamic_offer_data' ) ), true );
6113 if ( is_array( $dynamic_product ) && isset( $dynamic_product[0]['ID'] ) ) {
6114 $id = $dynamic_product[0]['ID'];
6115 }
6116 }
6117
6118 if ( $id ) {
6119 $offer_product = wc_get_product( $id );
6120 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6121 }
6122 }
6123 }
6124 } elseif ( 'highestSoldInsideCategory' == $rules_type ) {
6125 $offer_mappings = get_post_meta( $step_id, 'global_funnel_' . $step_type . '_rules', true );
6126 if ( $offer_mappings ) {
6127 $category = isset( $offer_mappings['category'] ) ? $offer_mappings['category'] : null;
6128 $function_exist = is_callable( array( $instance, 'get_highest_sold_product_in_category_for_offer' ) );
6129 if ( $function_exist ) {
6130 $highestSoldInsideCategory = Wpfnl_Pro_GBF_Functions::get_highest_sold_product_in_category_for_offer( 1, $category );
6131 if ( is_array( $highestSoldInsideCategory ) && isset( $highestSoldInsideCategory['posts'][0]->ID ) ) {
6132 $id = $highestSoldInsideCategory['posts'][0]->ID;
6133 $offer_product = wc_get_product( $id );
6134 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6135 }
6136 }
6137 }
6138 } elseif ( 'highestSoldInsideTag' == $rules_type ) {
6139 $offer_mappings = get_post_meta( $step_id, 'global_funnel_' . $step_type . '_rules', true );
6140 if ( $offer_mappings ) {
6141 $tag = isset( $offer_mappings['tag'] ) ? $offer_mappings['tag'] : null;
6142 $function_exist = is_callable( array( $instance, 'get_highest_sold_product_in_tag_for_offer' ) );
6143 if ( $function_exist ) {
6144 $highestSoldInsideTag = Wpfnl_Pro_GBF_Functions::get_highest_sold_product_in_tag_for_offer( $tag, 1 );
6145 if ( is_array( $highestSoldInsideTag ) && isset( $highestSoldInsideTag['posts'][0]->ID ) ) {
6146 $id = $highestSoldInsideTag['posts'][0]->ID;
6147 $offer_product = wc_get_product( $id );
6148 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6149 }
6150 }
6151 }
6152 } elseif ( 'highestSold' == $rules_type ) {
6153 $function_exist = is_callable( array( $instance, 'get_highest_sold_product_for_offer' ) );
6154 if ( $function_exist ) {
6155 $highestSold = Wpfnl_Pro_GBF_Functions::get_highest_sold_product_for_offer( 1 );
6156 if ( is_array( $highestSold ) && isset( $highestSold['posts'][0]->ID ) ) {
6157 $id = $highestSold['posts'][0]->ID;
6158 $offer_product = wc_get_product( $id );
6159 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6160 }
6161 }
6162 } elseif ( 'randomProductInTag' == $rules_type ) {
6163 $function_exist = is_callable( array( $instance, 'get_random_product_in_tag_for_offer_for_widget' ) );
6164 if ( $function_exist ) {
6165 $id = '';
6166 if ( is_admin() ) {
6167 $dynamic_product = Wpfnl_Pro_GBF_Functions::get_random_product_in_tag_for_offer_for_widget( 1 );
6168 if ( isset( $dynamic_product[0]->ID ) ) {
6169 $id = $dynamic_product[0]->ID;
6170 }
6171 } else {
6172 $dynamic_product = json_decode( wp_unslash( get_option( 'wpfunnels_dynamic_offer_data' ) ), true );
6173 if ( is_array( $dynamic_product ) && isset( $dynamic_product[0]['ID'] ) ) {
6174 $id = $dynamic_product[0]['ID'];
6175 }
6176 }
6177 if ( $id ) {
6178 $offer_product = wc_get_product( $id );
6179 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6180 }
6181 }
6182 } elseif ( 'randomProductInsideTagWithinPrice' == $rules_type ) {
6183 $function_exist = is_callable( array( $instance, 'get_random_product_in_tag_within_price_for_offer_for_widget' ) );
6184 if ( $function_exist ) {
6185 $id = '';
6186 if ( is_admin() ) {
6187 $dynamic_product = Wpfnl_Pro_GBF_Functions::get_random_product_in_tag_within_price_for_offer_for_widget( 1 );
6188
6189 if ( isset( $dynamic_product[0]->ID ) ) {
6190 $id = $dynamic_product[0]->ID;
6191 }
6192 } else {
6193 $dynamic_product = json_decode( wp_unslash( get_option( 'wpfunnels_dynamic_offer_data' ) ), true );
6194 if ( is_array( $dynamic_product ) && isset( $dynamic_product[0]['ID'] ) ) {
6195 $id = $dynamic_product[0]['ID'];
6196 }
6197 }
6198 if ( $id ) {
6199 $offer_product = wc_get_product( $id );
6200 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6201 }
6202 }
6203 } elseif ( 'randomInShop' == $rules_type ) {
6204 $id = '';
6205 if ( is_admin() ) {
6206 $dynamic_product = Wpfnl_Pro_GBF_Functions::get_random_product_in_shop_for_widget( 1 );
6207 if ( isset( $dynamic_product[0]->ID ) ) {
6208 $id = $dynamic_product[0]->ID;
6209 }
6210 } else {
6211 $dynamic_product = json_decode( wp_unslash( get_option( 'wpfunnels_dynamic_offer_data' ) ), true );
6212 if ( is_array( $dynamic_product ) && isset( $dynamic_product[0]['ID'] ) ) {
6213 $id = $dynamic_product[0]['ID'];
6214 }
6215 }
6216 if ( $id ) {
6217 $offer_product = wc_get_product( $id );
6218 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6219 }
6220 }
6221 }
6222 } else {
6223 $offer_product_data = self::get_offer_product( $step_id, $step_type );
6224 $quantity = isset( $offer_product_data['quantity'] ) ? $offer_product_data['quantity'] : 1;
6225 $product = null;
6226 if ( is_array( $offer_product_data ) ) {
6227 foreach ( $offer_product_data as $pr_index => $pr_data ) {
6228 $product_id = $pr_data['id'];
6229 $product = wc_get_product( $product_id );
6230 break;
6231 }
6232 }
6233
6234 $offer_product = $product;
6235 $get_product_type = $offer_product ? $offer_product->get_type() : '';
6236 }
6237
6238 if ( $offer_product && $get_product_type ) {
6239
6240 return array(
6241 'offer_product' => $offer_product,
6242 'get_product_type' => $get_product_type,
6243 'is_gbf' => $is_gbf,
6244 'quantity' => $quantity,
6245 );
6246 }
6247 }
6248
6249 return array(
6250 'offer_product' => '',
6251 'get_product_type' => '',
6252 'is_gbf' => 'no',
6253 'quantity' => 1,
6254 );
6255 }
6256
6257
6258 /**
6259 * Checks if a payment gateway is potentially unsupported.
6260 *
6261 * @return bool
6262 * @since 2.0.5
6263 */
6264 public static function maybe_unsupported_payment_gateway() {
6265
6266 if ( ! Wpfnl_functions::is_wc_active() || ! isset( $_GET['wpfnl-order'] ) ) {
6267 return false;
6268 }
6269
6270 $order_id = ( isset( $_GET['wpfnl-order'] ) ) ? intval( $_GET['wpfnl-order'] ) : 0;
6271 $order = wc_get_order( $order_id );
6272 if ( false === is_a( $order, 'WC_Order' ) ) {
6273 return false;
6274 }
6275 $payment_method = $order->get_payment_method();
6276
6277 if ( $payment_method ) {
6278 // First check if Pro version's factory exists (for backward compatibility)
6279 if ( class_exists( '\WPFunnelsPro\Frontend\Modules\Gateways\Payment_Gateways_Factory' ) ) {
6280 $payment_gateways = \WPFunnelsPro\Frontend\Modules\Gateways\Payment_Gateways_Factory::getInstance()->get_supported_payment_gateways();
6281 }
6282 // Otherwise use the free version's factory
6283 elseif ( class_exists( '\WPFunnels\Gateway\Payment_Gateways_Factory' ) ) {
6284 $payment_gateways = \WPFunnels\Gateway\Payment_Gateways_Factory::getInstance()->get_supported_payment_gateways();
6285 } else {
6286 // If neither exists, assume unsupported
6287 return true;
6288 }
6289
6290 if ( ! isset( $payment_gateways[ $payment_method ] ) ) {
6291 return true;
6292 } else {
6293 return false;
6294 }
6295 }
6296 return true;
6297 }
6298
6299
6300 /**
6301 * Checks if an offer exists in a funnel.
6302 *
6303 * @param int $funnel_id The ID of the funnel to check.
6304 *
6305 * @return bool True if an offer exists, false otherwise.
6306 *
6307 * @since 2.0.5
6308 */
6309 public static function is_offer_exists_in_funnel( $funnel_id ) {
6310 $is_offer_exists = false;
6311 $steps = Wpfnl_functions::get_steps( $funnel_id );
6312 if ( is_array( $steps ) ) {
6313 foreach ( $steps as $index => $step ) {
6314 $step_type = $step['step_type'];
6315 if ( in_array( $step_type, array( 'upsell', 'downsell' ) ) ) {
6316 $is_offer_exists = true;
6317 break;
6318 }
6319 }
6320 }
6321 return $is_offer_exists;
6322 }
6323
6324
6325 /**
6326 * Get upsell/downsell offer product information
6327 *
6328 * @param $step_id
6329 * @param string $selected_product_id
6330 * @param string $input_qty
6331 * @param int $order_id
6332 * @return array
6333 *
6334 * @since 1.0.0
6335 */
6336 public static function get_offer_product_data( $step_id, $selected_product_id = '', $input_qty = '', $order_id = 0 ) {
6337 $funnel_id = get_post_meta( $step_id, '_funnel_id', true );
6338
6339 return apply_filters( 'wpfunnels/offer_product_data', self::get_offer_data( $step_id, $selected_product_id, $input_qty, $order_id = 0 ), $funnel_id, $step_id );
6340 }
6341
6342
6343 /**
6344 * Retrieves offer data based on the provided parameters.
6345 *
6346 * @param int $step_id The ID of the step.
6347 * @param string $selected_product_id The ID of the selected product.
6348 * @param string $input_qty The quantity input.
6349 * @param int $order_id The ID of the order.
6350 *
6351 * @return mixed The offer data.
6352 *
6353 * @since 1.0.0
6354 */
6355 public static function get_offer_data( $step_id, $selected_product_id = '', $input_qty = '', $order_id = 0 ) {
6356 $data = array();
6357 $amount_diff = 0;
6358 $cancel_main_order = false;
6359 $product_id = 0;
6360 $product_qty = 0;
6361
6362 $step_type = get_post_meta( $step_id, '_step_type', true );
6363 $offer_product = get_post_meta( $step_id, '_wpfnl_' . $step_type . '_products', true );
6364 $discount = get_post_meta( $step_id, '_wpfnl_' . $step_type . '_discount', true );
6365 if ( isset( $offer_product[0] ) && ! $selected_product_id ) {
6366 $product_id = $offer_product[0]['id'];
6367 $product_qty = $offer_product[0]['quantity'];
6368 } else {
6369 $product_id = $selected_product_id;
6370 $product_qty = ! empty( $input_qty ) ? intval( $input_qty ) : ( isset( $offer_product[0]['quantity'] ) ? $offer_product[0]['quantity'] : 1 );
6371 }
6372
6373 if ( $product_id ) {
6374
6375 $product = wc_get_product( $product_id );
6376 if ( $product ) {
6377 $main_qty = $product->get_stock_quantity();
6378 if ( $main_qty <= 0 && $product->get_manage_stock() == true ) {
6379 $product_qty = 0;
6380 }
6381 $product_qty = intval( $product_qty );
6382 $order = wc_get_order( $order_id );
6383
6384 $product_type = $product->get_type();
6385 $original_price = $product->get_price( 'edit' );
6386 $custom_price = $original_price;
6387
6388 if ( is_a( $product, 'WC_Product_Bundle' ) ) {
6389 $custom_price = $product->get_bundle_price( 'min' );
6390 }
6391 if ( 'composite' === $product->get_type() ) {
6392 $custom_price = Wpfnl_functions::get_composite_product_price( $product->get_id(), false );
6393 }
6394
6395 $custom_price = apply_filters( 'wpfunnels/modify_offer_product_price_data_without_discount', $custom_price );
6396
6397 if ( is_plugin_active( 'woocommerce-subscriptions/woocommerce-subscriptions.php' ) ) {
6398 if ( 'subscription_variation' === $product->get_type() || 'subscription' === $product->get_type() ) {
6399 $signUpFee = \WC_Subscriptions_Product::get_sign_up_fee( $product );
6400 $custom_price = $custom_price + $signUpFee;
6401 }
6402 }
6403
6404 $unit_price = $custom_price;
6405 $unit_price_tax = $custom_price;
6406 $product_price = floatval( $custom_price ) * intval( $product_qty );
6407
6408 /** tax calculation */
6409 $tax_enabled = get_option( 'woocommerce_calc_taxes' );
6410 $shipping_fee = 0;
6411 $shipping_incl_tax = 0;
6412 $shipping_excl_tax = 0;
6413 $shipping_method_name = 0;
6414 if ( $order ) {
6415 $shipping_fee = $order->get_shipping_total();
6416 $shipping_method_name = $order->get_shipping_method();
6417 }
6418
6419 if ( 'yes' === $tax_enabled ) {
6420 if ( ! wc_prices_include_tax() ) {
6421 $product_price = wc_get_price_including_tax( $product, array( 'price' => $custom_price ) ) * floatval( $product_qty );
6422 $shipping_excl_tax = wc_get_price_including_tax( $product, array( 'price' => $shipping_fee ) );
6423 } else {
6424 $custom_price = wc_get_price_excluding_tax( $product, array( 'price' => $custom_price ) ) * floatval( $product_qty );
6425 $shipping_incl_tax = wc_get_price_excluding_tax( $product, array( 'price' => $shipping_fee ) );
6426 }
6427 $unit_price_tax = $custom_price;
6428 }
6429
6430 $shipping_incl_tax = $shipping_incl_tax ? $shipping_incl_tax : $shipping_fee;
6431 $shipping_excl_tax = $shipping_excl_tax ? $shipping_excl_tax : $shipping_fee;
6432
6433 /** if offer product has discount */
6434 if ( is_array( $discount ) ) {
6435 $discount_instance = new WpfnlDiscount();
6436
6437 if ( ! $discount_instance->maybe_time_bound_discount( $step_id ) || ( $discount_instance->maybe_time_bound_discount( $step_id ) && $discount_instance->maybe_validate_discount_time( $step_id ) ) ) {
6438
6439 $discount_type = $discount['discountType'];
6440 $discount_apply_to = $discount['discountApplyTo'];
6441 $discount_value = $discount['discountValue'];
6442 if ( 'discount-percentage' === $discount_type || 'discount-price' === $discount_type ) {
6443 $regular_price = $product->get_regular_price();
6444 if ( is_plugin_active( 'woocommerce-subscriptions/woocommerce-subscriptions.php' ) ) {
6445 if ( 'subscription_variation' === $product->get_type() || 'subscription' === $product->get_type() ) {
6446 $signUpFee = \WC_Subscriptions_Product::get_sign_up_fee( $product );
6447 $regular_price = $regular_price + $signUpFee;
6448 }
6449 }
6450
6451 $product_price = $discount_apply_to === 'sale' ? $product->get_sale_price() : $regular_price;
6452 $product_price = $product_price ? $product_price : $product->get_price();
6453
6454 if ( is_a( $product, 'WC_Product_Bundle' ) ) {
6455 $custom_price = $discount_apply_to === 'sale' ? $product->get_bundle_price( 'min' ) : $product->get_bundle_regular_price( 'max' );
6456 }
6457 if ( 'composite' === $product->get_type() ) {
6458 $custom_price = Wpfnl_functions::get_composite_product_price( $product->get_id(), false );
6459 }
6460
6461 $product_price = self::calculate_discount_price_for_widget( $discount_type, $discount_value, floatval( $product_price ) * floatval( $product_qty ) );
6462 $product_price = apply_filters( 'wpfunnels/modify_offer_product_price_data_with_discount', $product_price );
6463
6464 $custom_price = $product_price;
6465 $unit_price = $product_price;
6466 $unit_price_tax = $product_price;
6467 }
6468 }
6469 }
6470
6471 $data = array(
6472 'step_id' => $step_id,
6473 'id' => $product_id,
6474 'name' => $product->get_title(),
6475 'desc' => self::get_item_description( $product ),
6476 'qty' => $product_qty,
6477 'original_price' => $original_price,
6478 'regular_price' => $product->get_regular_price(),
6479 'sale_price' => $product->get_sale_price(),
6480 'unit_price' => $unit_price,
6481 'unit_price_tax' => $unit_price_tax,
6482 'args' => array(
6483 'subtotal' => $custom_price,
6484 'total' => $custom_price,
6485 ),
6486 'shipping_fee' => $shipping_excl_tax,
6487 'shipping_fee_incl_tax' => $shipping_incl_tax,
6488 'shipping_method_name' => $shipping_method_name,
6489 'price' => wc_prices_include_tax() ? $custom_price : $product_price,
6490 'url' => $product->get_permalink(),
6491 'total_unit_price_amount' => floatval( preg_replace( '/[^\d.]/', '', $unit_price_tax ) ) * floatval( $product_qty ),
6492 'total' => wc_prices_include_tax() ? $custom_price : $product_price,
6493 'cancel_main_order' => $cancel_main_order,
6494 'amount_diff' => $amount_diff,
6495 'discount' => $discount ? true : false,
6496 'discount_type' => isset( $discount['discountType'] ) ? $discount['discountType'] : '',
6497 'discount_apply_to' => isset( $discount['discountApplyTo'] ) ? $discount['discountApplyTo'] : '',
6498 'discount_value' => isset( $discount['discountValue'] ) ? $discount['discountValue'] : '',
6499 );
6500 }
6501 }
6502 return $data;
6503 }
6504
6505
6506 /**
6507 * Calculate discount price
6508 *
6509 * @param $discount_type
6510 * @param $discount_value
6511 * @param $product_price
6512 *
6513 * @return string
6514 */
6515 public static function calculate_discount_price_for_widget( $discount_type, $discount_value, $product_price ) {
6516
6517 $custom_price = $product_price;
6518 if ( ! empty( $discount_type ) ) {
6519 if ( 'discount-percentage' === $discount_type ) {
6520 if ( $discount_value > 0 && $discount_value <= 100 ) {
6521 $custom_price = (float) $product_price - ( (float) ( $product_price * $discount_value ) / 100 );
6522 }
6523 } elseif ( 'discount-price' === $discount_type ) {
6524 if ( $discount_value > 0 ) {
6525 $custom_price = $product_price - $discount_value;
6526 }
6527 }
6528 }
6529 return $custom_price;
6530 }
6531
6532 /**
6533 * Helper method to return the item description, which is composed of item
6534 * meta flattened into a comma-separated string, if available. Otherwise the
6535 * product SKU is included.
6536 *
6537 * The description is automatically truncated to the 127 char limit.
6538 *
6539 * @param array $item cart or order item
6540 * @param \WC_Product $product product data
6541 *
6542 * @return string
6543 * @since 1.0.0
6544 */
6545 public static function get_item_description( $product ) {
6546
6547 if ( is_string( $product ) ) {
6548 $str = $product;
6549 } else {
6550 $str = $product->get_short_description();
6551 }
6552 $item_desc = wp_strip_all_tags( wp_specialchars_decode( wp_staticize_emoji( $str ) ) );
6553 $item_desc = preg_replace( '/[\x00-\x1F\x80-\xFF]/', '', $item_desc );
6554 $item_desc = str_replace( "\n", ', ', rtrim( $item_desc ) );
6555 if ( strlen( $item_desc ) > 127 ) {
6556 $item_desc = substr( $item_desc, 0, 124 ) . '...';
6557 }
6558
6559 return html_entity_decode( $item_desc, ENT_NOQUOTES, 'UTF-8' );
6560 }
6561 }
6562
6563