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