PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.12.10
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.12.10
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / public / class-wpfnl-public.php

class-wpfnl-public.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.12.10, at public/class-wpfnl-public.php

1,377 lines 44.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * WPFunnels public module
5 *
6 * @package WPFunnels\Frontend
7 */
8
9 namespace WPFunnels\Frontend;
10
11 use PLL_Model;
12 use PLL_Translated_Post;
13 use WPFunnels\Ajax_Handler\Ajax_Handler;
14 use WPFunnels\Compatibility\Wpfnl_Theme_Compatibility;
15 use WPFunnels\Conditions\Wpfnl_Condition_Checker;
16 use WPFunnels\Traits\SingletonTrait;
17 use WPFunnels\Wpfnl_functions;
18 use WPFunnels\lms\helper\Wpfnl_lms_learndash_functions;
19
20 use Wpfnl_Logger;
21
22 /**
23 * The public-facing functionality of the plugin.
24 *
25 * @link https://rextheme.com
26 * @since 1.0.0
27 *
28 * @package Wpfnl
29 * @subpackage Wpfnl/public
30 */
31
32 /**
33 * The public-facing functionality of the plugin.
34 *
35 * Defines the plugin name, version, and two examples hooks for how to
36 * enqueue the public-facing stylesheet and JavaScript.
37 *
38 * @package Wpfnl
39 * @subpackage Wpfnl/public
40 * @author RexTheme <[email protected]>
41 */
42 class Wpfnl_Public
43 {
44
45 use SingletonTrait;
46
47 public $offer_metas;
48 public $step_id;
49
50 /**
51 * Initialize the class and set its properties.
52 *
53 * @param string $plugin_name The name of the plugin.
54 * @param string $version The version of this plugin.
55 *
56 * @since 1.0.0
57 */
58 public function __construct()
59 {
60 /**
61 * This will trigger when funnel first start
62 */
63 add_action('wp', array($this, 'init_funnel'), 1);
64 add_action('wp', array($this, 'init_wp_actions'), 55);
65
66 add_action('wp_enqueue_scripts', [$this, 'enqueue_styles']);
67 add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']);
68
69 add_action('init', array($this, 'init_function'));
70
71 add_filter('wpfnl_offer_meta', array($this, 'wpfnl_offer_meta'), 10, 1);
72
73 /**
74 * Modify the checkout order received url to next step
75 */
76 add_filter('woocommerce_get_checkout_order_received_url', [$this, 'redirect_to_funnel_thankyou_page'], 1000, 2);
77
78 /*
79 * This hook will be triggered once the elementor data is saved.
80 * Will be placed this hook on another class in future.
81 */
82 add_action('elementor/editor/after_save', array($this, 'elementor_data_after_save_action'), 10, 2);
83 add_filter('woocommerce_hidden_order_itemmeta', array($this, 'wpfnl_woocommerce_hidden_order_itemmeta'), 10, 1);
84
85 add_filter('woocommerce_order_item_display_meta_key', array($this, 'wpfnl_beautify_item_meta_on_order'), 10, 3);
86
87 add_filter('woocommerce_order_item_display_meta_value', array($this, 'wpfnl_update_order_item_display_meta_value'), 9999, 3);
88
89
90 /**
91 * Trigger ajax if any user abandoned a funnel
92 */
93 add_action('wp_ajax_nopriv_maybe_abandoned_funnel', array($this, 'maybe_abandoned_funnel'), 10);
94 add_action('wp_ajax_maybe_abandoned_funnel', array($this, 'maybe_abandoned_funnel'), 10);
95
96 add_action('wpfunnels/funnel_journey_end', array($this, 'end_journey'), 10, 2);
97
98 add_filter('woocommerce_add_cart_item_data', [$this, 'namespace_force_individual_cart_items'], 10, 2);
99 add_filter('woocommerce_cart_item_name', [$this, 'wpfnl_order_bump_cart_item_name'], 10, 3);
100 add_filter('woocommerce_checkout_cart_item_quantity', [$this, 'wpfnl_order_bump_cart_item_quantity'], 10, 3);
101 add_action('woocommerce_checkout_create_order_line_item', [$this, 'wpfnl_order_bump_order_line_item'], 10, 4);
102 add_filter('wpfunnels/funnel_order_placed', [$this, 'add_order_details_to_logger'], 10, 3);
103 add_action('wpfunnels/after_optin_submit', array($this, 'add_optin_data_to_logger'), 10, 4);
104 add_action('wpfunnels/after_optin_submit', array($this, 'get_optin_data'), 10, 4);
105 add_action('wpfunnels/after_optin_submit', [$this, 'register_optin_user'], 10, 5);
106 add_action('wpfunnels/optin_submission_response', [$this, 'regenerate_redirect_url'], 10, 5);
107
108 add_filter('posts_where', array($this, 'posts_where'), 99);
109
110 /**
111 * Add edit funnel menu at admin bar
112 */
113 add_action('admin_bar_menu', array($this, 'add_edit_funnel_menu'), 999);
114
115 /**
116 * Add custom script
117 */
118 add_action('wp_head', array($this, 'add_custom_script'));
119 add_action('wp_head', array($this, 'add_custom_css'));
120
121 // remove woodmart hook for funnel checkout
122 add_action('wp', [$this, 'remove_woodmart_hook'], 150);
123
124 add_action( 'wp_footer', array( $this, 'load_variable_product_modal' ) );
125
126
127 }
128
129 /**
130 * Removes Woodmart theme related hooks from the checkout page.
131 *
132 * This function is called when the checkout page is loaded in the admin or frontend.
133 * It checks if the Woodmart theme is active and if the current page is a checkout page.
134 * If both conditions are met, it removes the 'woodmart_enqueue_base_styles' action
135 * with a priority of 10000 and the 'woodmart_sticky_toolbar_template' action with a priority of 10000.
136 *
137 * @since 3.4.9
138 */
139 public function remove_woodmart_hook()
140 {
141 global $post;
142
143 // Check if we are in the admin or if the post object is not set
144 if (is_admin() || !$post) {
145 return;
146 }
147
148 $checkout_id = '';
149
150 // Check if we are doing an AJAX request
151 if (wp_doing_ajax()) {
152 // Get the checkout ID from the POST data
153 $checkout_id = Wpfnl_functions::get_checkout_id_from_post($_POST);
154 }
155
156 // If the checkout ID is not set, use the current post ID
157 $checkout_id = !$checkout_id ? $post->ID : $checkout_id;
158 $funnel_id = Wpfnl_functions::get_funnel_id_from_step( $checkout_id );
159 // Check if the Woodmart theme is active and if the current page is a checkout page
160 if (Wpfnl_functions::maybe_woodmart_theme() && $checkout_id && $funnel_id) {
161 // Remove the 'woodmart_enqueue_base_styles' action with a priority of 10000
162 remove_action('wp_enqueue_scripts', 'woodmart_enqueue_base_styles', 10000);
163
164 // Remove the 'woodmart_sticky_toolbar_template' action with a priority of 10000
165 remove_action('wp_footer', 'woodmart_sticky_toolbar_template', 10000);
166 }
167 }
168
169
170
171 /**
172 * Modify the SQL JOIN clause to exclude language-based filtering when querying posts.
173 *
174 * This function is a filter callback used in WordPress to customize the SQL query's JOIN clause
175 * when retrieving posts. It is specifically designed to work with the Polylang plugin to exclude
176 * language-based filtering from the JOIN clause.
177 *
178 * @param string $join The original SQL JOIN clause.
179 *
180 * @return string The modified SQL JOIN clause with language-based filtering excluded.
181 *
182 * @see https://polylang.pro/
183 * @since 2.8.6
184 */
185 public function posts_join($join)
186 {
187 if (class_exists('PLL_Model') && class_exists('PLL_Translated_Post')) {
188 $options = new \WP_Syntex\Polylang\Options\Options();
189 $model = new PLL_Model($options);
190 $post = new PLL_Translated_Post($model);
191 $custom_join = $post->join_clause();
192 return str_replace($custom_join, '', $join);
193 }
194 return $join;
195 }
196
197 /**
198 * Modify the SQL WHERE clause for post queries, optionally removing language filtering.
199 *
200 * This function is a filter callback used in WordPress to customize the SQL query's WHERE clause
201 * when retrieving posts. If the query appears to target 'wpfunnel_steps', it adjusts the WHERE
202 * clause, possibly removing language-based filtering. It also adds a filter to customize the SQL
203 * JOIN clause for further modifications.
204 *
205 * @param string $where The original SQL WHERE clause.
206 *
207 * @return string The modified SQL WHERE clause, possibly without language-based filtering.
208 *
209 * @see https://wordpress.org/
210 * @since 2.8.6
211 */
212 public function posts_where($where)
213 {
214 if (preg_match('/wpfunnel_steps/', $where)) {
215 add_filter('posts_join', array($this, 'posts_join'), 99);
216 $lang = $this->get_pll_term_id();
217 return str_replace("AND pll_tr.term_taxonomy_id = {$lang}", '', $where);
218 }
219 return $where;
220 }
221
222 /**
223 * Get the term ID associated with a language slug from Polylang plugin options.
224 *
225 * This function retrieves the term ID for a specific language slug by querying the WordPress
226 * database terms table. It uses the provided language slug and the Polylang plugin options.
227 *
228 * @return int|null The term ID of the language, or null if not found.
229 *
230 * @global object $wpdb WordPress database object.
231 *
232 * @see https://polylang.pro/
233 * @since 2.8.6
234 */
235 private function get_pll_term_id()
236 {
237 global $wpdb;
238 $pll_options = get_option('polylang', []);
239 return $wpdb->get_var($wpdb->prepare("SELECT `term_id` FROM %i WHERE `slug`=%s", [$wpdb->terms, $pll_options['default_lang'] ?? '']));
240 }
241
242 /**
243 * Add individual items in cart forcefully. It's implemented for indentifying orderbump items
244 *
245 * @param Array
246 * @param String
247 *
248 * @return Array
249 */
250 public function namespace_force_individual_cart_items($cart_item_data, $product_id)
251 {
252
253 $post_action = filter_input_array(INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS);
254 if (isset($post_action['action']) && ('wpfnl_order_bump_ajax' === $post_action['action'] || 'wpfnl_order_bump_ajax' === $post_action['action'])) {
255 $unique_cart_item_key = md5(microtime() . rand());
256 $cart_item_data['unique_key'] = $unique_cart_item_key;
257 }
258 return $cart_item_data;
259 }
260
261
262
263 /**
264 * Display custom product title for order bump items in cart
265 *
266 * @param string $product_name
267 * @param array $cart_item
268 * @param string $cart_item_key
269 * @return string
270 */
271 public function wpfnl_order_bump_cart_item_name($product_name, $cart_item, $cart_item_key)
272 {
273 if (isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] && isset($cart_item['wpfnl_ob_title']) && !empty($cart_item['wpfnl_ob_title'])) {
274 return $cart_item['wpfnl_ob_title'];
275 }
276 return $product_name;
277 }
278
279 /**
280 * Display custom quantity for order bump items in cart
281 *
282 * @param string $quantity
283 * @param array $cart_item
284 * @param string $cart_item_key
285 * @return string
286 */
287 public function wpfnl_order_bump_cart_item_quantity($quantity, $cart_item, $cart_item_key)
288 {
289 if (isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] && isset($cart_item['wpfnl_ob_quantity'])) {
290 return ' <strong class="product-quantity">' . $cart_item['quantity'] . '</strong>';
291 }
292 return $quantity;
293 }
294
295 /**
296 * Add custom product title to order line item meta
297 *
298 * @param \WC_Order_Item_Product $item
299 * @param string $cart_item_key
300 * @param array $values
301 * @param \WC_Order $order
302 */
303 public function wpfnl_order_bump_order_line_item($item, $cart_item_key, $values, $order)
304 {
305 if (isset($values['wpfnl_order_bump']) && $values['wpfnl_order_bump'] && isset($values['wpfnl_ob_title']) && !empty($values['wpfnl_ob_title'])) {
306 $item->set_name($values['wpfnl_ob_title']);
307 }
308 }
309
310 /**
311 * Initialize function
312 */
313 public function init_function()
314 {
315 $offer_meta = ['_wpfunnels_order_bump'];
316 return apply_filters('wpfnl_offer_meta', $offer_meta);
317 }
318
319 /**
320 * Init actions for a funnel
321 *
322 * @since 2.0.2
323 */
324 public function init_funnel()
325 {
326 if (Wpfnl_functions::is_funnel_step_page()) {
327 global $post;
328 do_action('wpfunnels/wp', $post->ID);
329 $this->start_funnel_journey();
330 }
331 }
332
333
334 /**
335 * Start session data when funnel starts
336 *
337 * @since 2.0.3
338 */
339 private function start_funnel_journey()
340 {
341 Wpfnl_functions::start_journey();
342 if (Wpfnl_functions::check_if_this_is_step_type('checkout')) {
343 Wpfnl_functions::do_not_cache();
344 }
345 }
346
347
348 public function init_wp_actions()
349 {
350 if (Wpfnl_functions::is_funnel_step_page('')) {
351 /**
352 * Enqueue public styles
353 */
354 add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
355 add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
356 add_action('wp_enqueue_scripts', array($this, 'remove_theme_styles'));
357 add_filter('woocommerce_enqueue_styles', array($this, 'enqueue_wc_styles'), 9999);
358
359 add_filter('woocommerce_locate_template', array($this, 'wpfunnels_woocommerce_locate_template'), 20, 3);
360 }
361 }
362
363
364 /**
365 * Register the stylesheets for the public-facing side of the site.
366 *
367 * @since 1.0.0
368 */
369 public function enqueue_styles()
370 {
371
372 /**
373 * This function is provided for demonstration purposes only.
374 *
375 * An instance of this class should be passed to the run() function
376 * defined in Wpfnl_Loader as all of the hooks are defined
377 * in that particular class.
378 *
379 * The Wpfnl_Loader will then create the relationship
380 * between the defined hooks and the functions defined in this
381 * class.
382 */
383
384 if (Wpfnl_functions::is_funnel_step_page()) {
385 add_filter('astra_theme_woocommerce_dynamic_css', '__return_empty_string');
386
387 wp_enqueue_style('wpfnl-public', plugin_dir_url(__FILE__) . 'assets/css/wpfnl-public.css', [], WPFNL_VERSION, 'all');
388 //$this->load_googlefonts();
389 }
390 }
391
392 /**
393 * Register the JavaScript for the public-facing side of the site.
394 *
395 * @since 1.0.0
396 */
397 public function enqueue_scripts()
398 {
399
400 /**
401 * This function is provided for demonstration purposes only.
402 *
403 * An instance of this class should be passed to the run() function
404 * defined in Wpfnl_Loader as all of the hooks are defined
405 * in that particular class.
406 *
407 * The Wpfnl_Loader will then create the relationship
408 * between the defined hooks and the functions defined in this
409 * class.
410 */
411 if (Wpfnl_functions::is_funnel_step_page()) {
412
413 $compatibility = Wpfnl_Theme_Compatibility::getInstance();
414
415 wp_enqueue_script('wpfnl-public', plugin_dir_url(__FILE__) . 'assets/js/wpfnl-public.js', ['jquery'], WPFNL_VERSION, false);
416 if ( Wpfnl_functions::is_funnel_step_page( 'checkout' ) ) {
417 $checkout_id = get_the_ID();
418 $order_bump_settings = get_post_meta( $checkout_id, 'order-bump-settings', true );
419
420 if ( ! empty( $order_bump_settings ) ) {
421 foreach ( $order_bump_settings as $key => $settings ) {
422 if ( isset( $settings['product'] ) ) {
423 $product = wc_get_product( $settings['product'] );
424 if ( $product && $product->is_type( 'variable' ) ) {
425 wp_enqueue_script( 'wc-single-product' );
426 break;
427 }
428 }
429 }
430 }
431 }
432 wp_enqueue_script('wpfnl-order-bump-variable', plugin_dir_url(__FILE__) . 'assets/js/order-bump-variable.js', array( 'jquery', 'wc-add-to-cart-variation' ), WPFNL_VERSION, true);
433 wp_localize_script(
434 'wpfnl-public',
435 'wpfnl_obj',
436 [
437 'ajaxurl' => admin_url('admin-ajax.php'),
438 'is_builder_preview' => $compatibility->is_builder_preview(),
439 'funnel_id' => get_post_meta(get_the_ID(), '_funnel_id', true),
440 'step_id' => get_the_ID(),
441 'ajax_nonce' => wp_create_nonce('wpfnl'),
442 'abandoned_ajax_nonce' => wp_create_nonce('abandoned_ajax_nonce'),
443 'optin_form_nonce' => wp_create_nonce('optin_form_nonce'),
444 'is_user_logged_in' => is_user_logged_in(),
445 'user_id' => get_current_user_id(),
446 'is_login_reminder' => get_option('woocommerce_enable_checkout_login_reminder'),
447 'express_checkout_enabled' => get_post_meta(get_the_ID(), '_wpfnl_express_checkout_enabled', true) ? get_post_meta(get_the_ID(), '_wpfnl_express_checkout_enabled', true) : 'no',
448 'field_validation_enabled' => get_post_meta(get_the_ID(), '_wpfnl_field_validation_enabled', true) ? get_post_meta(get_the_ID(), '_wpfnl_field_validation_enabled', true) : 'no',
449 'field_validation_message' => get_post_meta(get_the_ID(), '_wpfnl_field_validation_message', true) ? get_post_meta(get_the_ID(), '_wpfnl_field_validation_message', true) : '{field} is required',
450 'display_product_images' => get_post_meta(get_the_ID(), '_wpfnl_display_product_images', true) ? get_post_meta(get_the_ID(), '_wpfnl_display_product_images', true) : 'no',
451 'collapsible_order_summary_enabled' => get_post_meta(get_the_ID(), '_wpfnl_collapsible_order_summary_enabled', true) !== '' ? get_post_meta(get_the_ID(), '_wpfnl_collapsible_order_summary_enabled', true) : 'yes',
452 'enhanced_phone_field_enabled' => get_post_meta(get_the_ID(), '_wpfnl_enhanced_phone_field_enabled', true) ? get_post_meta(get_the_ID(), '_wpfnl_enhanced_phone_field_enabled', true) : 'no',
453 'validate_phone_number' => get_post_meta(get_the_ID(), '_wpfnl_validate_phone_number', true) ? get_post_meta(get_the_ID(), '_wpfnl_validate_phone_number', true) : 'no',
454 'save_phone_number_format' => get_post_meta(get_the_ID(), '_wpfnl_save_phone_number_format', true) ? get_post_meta(get_the_ID(), '_wpfnl_save_phone_number_format', true) : 'without_country_code',
455 'phone_help_text' => get_post_meta(get_the_ID(), '_wpfnl_phone_help_text', true) ? get_post_meta(get_the_ID(), '_wpfnl_phone_help_text', true) : '',
456 'phone_utils_url' => plugin_dir_url(__FILE__) . 'assets/phone/js/utils.js',
457 ]
458 );
459
460 if ('yes' === (get_post_meta(get_the_ID(), '_wpfnl_enhanced_phone_field_enabled', true) ? get_post_meta(get_the_ID(), '_wpfnl_enhanced_phone_field_enabled', true) : 'no')) {
461 wp_enqueue_style('intl-tel-input-style', plugin_dir_url(__FILE__) . 'assets/phone/css/intlTelInput.css', array(), WPFNL_VERSION);
462 wp_enqueue_script('intl-tel-input-script', plugin_dir_url(__FILE__) . 'assets/phone/js/intlTelInput.min.js', array('jquery'), WPFNL_VERSION, true);
463 wp_enqueue_script('intl-tel-input-utils', plugin_dir_url(__FILE__) . 'assets/phone/js/utils.js', array('intl-tel-input-script'), WPFNL_VERSION, true);
464 }
465
466 $post_id = get_the_ID();
467 $custom_css = get_post_meta($post_id, 'rex_gutenberg_css', true);
468
469 if ($custom_css) {
470 wp_register_style('rex-gutenberg-css', false);
471 wp_enqueue_style('rex-gutenberg-css');
472 wp_add_inline_style('rex-gutenberg-css', $custom_css);
473 }
474 }
475 }
476
477
478 /**
479 * This functions are from qubely.
480 */
481 private function load_googlefonts()
482 {
483 global $blocks;
484 $contains_wpfnl_blocks = false;
485 $block_fonts = [];
486 $load_google_fonts = 'yes';
487
488 if ($load_google_fonts == 'yes') {
489 $blocks = $this->parse_all_blocks();
490 $contains_wpfnl_blocks = $this->has_wpfnl_blocks($blocks);
491
492 if ($contains_wpfnl_blocks) {
493 $block_fonts = $this->gather_block_fonts($blocks, $block_fonts);
494 $global_settings = get_option($this->option_keyword);
495 $global_settings = $global_settings == false ? json_decode('{}') : json_decode($global_settings);
496 $global_settings = json_decode(json_encode($global_settings), true);
497 $gfonts = '';
498 $all_global_fonts = array();
499 if (isset($global_settings['presets']) && isset($global_settings['presets'][$global_settings['activePreset']]) && isset($global_settings['presets'][$global_settings['activePreset']]['typography'])) {
500 $all_global_fonts = $this->colsFromArray(array_column($global_settings['presets'][$global_settings['activePreset']]['typography'], 'value'), ['family', 'weight']);
501 }
502 if (count($all_global_fonts) > 0) {
503 $global_fonts = array_column($all_global_fonts, 'family');
504
505 $all_fonts = array_unique(array_merge($global_fonts, $block_fonts));
506
507 if (!empty($all_fonts)) {
508 $system = array(
509 'Arial',
510 'Tahoma',
511 'Verdana',
512 'Helvetica',
513 'Times New Roman',
514 'Trebuchet MS',
515 'Georgia',
516 );
517
518 $gfonts_attr = ':100,100italic,200,200italic,300,300italic,400,400italic,500,500italic,600,600italic,700,700italic,800,800italic,900,900italic';
519
520 foreach ($all_fonts as $font) {
521 if (!in_array($font, $system, true) && !empty($font)) {
522 $gfonts .= str_replace(' ', '+', trim($font)) . $gfonts_attr . '|';
523 }
524 }
525 }
526
527 if (!empty($gfonts)) {
528 $query_args = array(
529 'family' => $gfonts,
530 );
531
532 wp_register_style(
533 'qubely-google-fonts',
534 add_query_arg($query_args, '//fonts.googleapis.com/css'),
535 array(),
536 QUBELY_VERSION
537 );
538 wp_enqueue_style('qubely-google-fonts');
539 }
540 }
541 }
542 }
543 }
544
545
546 /**
547 * Parse all blocks
548 *
549 * @return array[]
550 */
551 public function parse_all_blocks()
552 {
553 $blocks;
554 if (is_single() || is_page() || is_404()) {
555 global $post;
556 if (is_object($post) && property_exists($post, 'post_content')) {
557 $blocks = parse_blocks($post->post_content);
558 }
559 } elseif (is_archive() || is_home() || is_search()) {
560 global $wp_query;
561 foreach ($wp_query as $post) {
562 if (is_object($post) && property_exists($post, 'post_content')) {
563 $blocks = parse_blocks($post->post_content);
564 }
565 }
566 }
567 return $blocks;
568 }
569
570
571 /**
572 * Check whether the contents has wpfnl blocks or not
573 *
574 * @param $blocks
575 *
576 * @return bool
577 */
578 public function has_wpfnl_blocks($blocks)
579 {
580 $is_wpfnl_block = false;
581 foreach ($blocks as $key => $block) {
582 if (strpos($block['blockName'], 'wpfnl') !== false) {
583 $is_wpfnl_block = true;
584 }
585 if (isset($block['innerBlocks']) && gettype($block['innerBlocks']) == 'array' && count($block['innerBlocks']) > 0) {
586 $is_wpfnl_block = $this->has_qubely_blocks($block['innerBlocks']);
587 }
588 }
589 return $is_wpfnl_block;
590 }
591
592
593 /**
594 * Gather block google fonts
595 *
596 * @param $blocks
597 * @param $block_fonts
598 *
599 * @return array
600 */
601 public function gather_block_fonts($blocks, $block_fonts)
602 {
603 $google_fonts = $block_fonts;
604 foreach ($blocks as $key => $block) {
605 if (strpos($block['blockName'], 'wpfnl') !== false) {
606 foreach ($block['attrs'] as $key => $att) {
607 if (gettype($att) == 'array' && isset($att['openTypography']) && isset($att['family'])) {
608 if (isset($block['attrs'][$key]['activeSource'])) {
609 if ($block['attrs'][$key]['activeSource'] == 'custom') {
610 array_push($google_fonts, $block['attrs'][$key]['family']);
611 }
612 } else {
613 array_push($google_fonts, $block['attrs'][$key]['family']);
614 }
615 }
616 }
617 }
618 if (isset($block['innerBlocks']) && gettype($block['innerBlocks']) == 'array' && count($block['innerBlocks']) > 0) {
619 $child_fonts = $this->gather_block_fonts($block['innerBlocks'], $google_fonts);
620 if (count($child_fonts) > 0) {
621 $google_fonts = array_merge($google_fonts, $child_fonts);
622 }
623 }
624 }
625 return array_unique($google_fonts);
626 }
627
628
629 /**
630 * Columns from array
631 *
632 * @param array $array
633 * @param $keys
634 *
635 * @return array
636 */
637 public function colsFromArray(array $array, $keys)
638 {
639 if (!is_array($keys)) $keys = [$keys];
640 return array_map(function ($el) use ($keys) {
641 $o = [];
642 foreach ($keys as $key) {
643 // if(isset($el[$key]))$o[$key] = $el[$key]; //you can do it this way if you don't want to set a default for missing keys.
644 $o[$key] = isset($el[$key]) ? $el[$key] : false;
645 }
646 return $o;
647 }, $array);
648 }
649
650
651 /**
652 * Remove theme style
653 *
654 * @return bool
655 */
656 public function remove_theme_styles()
657 {
658 if (Wpfnl_functions::is_funnel_step_page()) {
659
660 if (Wpfnl_Theme_Compatibility::getInstance()->is_compatible_theme_enabled()) {
661 return;
662 }
663
664 $general_settings = Wpfnl_functions::get_general_settings();
665
666 $wp_styles = wp_styles();
667 $themes_uri = get_theme_root_uri();
668
669 if ($general_settings['disable_theme_style'] == 'on') {
670 $dequeue_theme_style = apply_filters('wpfunnels/dequeue_theme_style', true);
671 } else {
672 $dequeue_theme_style = apply_filters('wpfunnels/dequeue_theme_style', false);
673 }
674
675 if ($dequeue_theme_style) {
676 foreach ($wp_styles->registered as $wp_style) {
677 if (strpos($wp_style->src, $themes_uri) !== false) {
678 wp_deregister_style($wp_style->handle);
679 wp_dequeue_style($wp_style->handle);
680 do_action('wpfunnels/enqueue_custom_scripts');
681 }
682 }
683 }
684 }
685 }
686
687
688 /**
689 * Enqueue woocommerce styles
690 *
691 * @return array
692 */
693 public function enqueue_wc_styles()
694 {
695 $wc_styles = array(
696 'woocommerce-layout' => array(
697 'src' => plugins_url('assets/css/woocommerce-layout.css', WC_PLUGIN_FILE),
698 'deps' => '',
699 'version' => WC_VERSION,
700 'media' => 'all',
701 'has_rtl' => true,
702 ),
703 'woocommerce-smallscreen' => array(
704 'src' => plugins_url('assets/css/woocommerce-smallscreen.css', WC_PLUGIN_FILE),
705 'deps' => 'woocommerce-layout',
706 'version' => WC_VERSION,
707 'media' => 'only screen and (max-width: ' . apply_filters('woocommerce_style_smallscreen_breakpoint', '768px') . ')',
708 'has_rtl' => true,
709 ),
710 'woocommerce-general' => array(
711 'src' => plugins_url('assets/css/woocommerce.css', WC_PLUGIN_FILE),
712 'deps' => '',
713 'version' => WC_VERSION,
714 'media' => 'all',
715 'has_rtl' => true,
716 ),
717 );
718 //unset( $wc_styles['woocommerce-layout'] );
719 //unset( $wc_styles['woocommerce-general'] );
720
721 return $wc_styles;
722 }
723
724
725 /**
726 * Save custom meta fields for order bump while saving
727 * checkout widget
728 *
729 * @param $post_id
730 * @param $editor_data
731 *
732 * @since 2.0.0
733 */
734 public function elementor_data_after_save_action($post_id, $editor_data)
735 {
736 if (Wpfnl_functions::check_if_this_is_step_type_by_id($post_id, 'checkout')) {
737
738 foreach ($editor_data as $key => $inner_element) {
739
740 $checkout_widget = Wpfnl_functions::recursive_multidimensional_ob_array_search_by_value('wpfnl-checkout', $inner_element['elements']);
741 $lms_checkout_widget = Wpfnl_functions::recursive_multidimensional_ob_array_search_by_value('wpfnl-lms-checkout', $inner_element['elements']);
742
743 if ($checkout_widget || $lms_checkout_widget) {
744 $widget_settings = isset($checkout_widget['settings']) ? $checkout_widget['settings'] : $lms_checkout_widget['settings'];
745 $layout = isset($widget_settings['checkout_layout']) ? $widget_settings['checkout_layout'] : 'wpfnl-col-2';
746 update_post_meta($post_id, '_wpfnl_checkout_layout', $layout);
747 $order_bump_status = get_post_meta($post_id, 'order-bump', true);
748 $order_bump_settings = get_post_meta($post_id, 'order-bump-settings', true);
749 $order_bump_enabled = 'no';
750 $default_order_bump_data = [];
751
752 if ($order_bump_status) {
753 $order_bump_enabled = $order_bump_status;
754 }
755
756 if ($order_bump_settings) {
757 $order_bump_data = $order_bump_settings;
758 } else {
759 $order_bump_data = $default_order_bump_data;
760 }
761
762 if (count($order_bump_data) > 0) {
763 foreach ($order_bump_data as $key => $ob) {
764 $order_bump_data[$key]['isEnabled'] = $this->set_order_bump_data('isEnabled', 'order_bump_' . $key, $widget_settings, $order_bump_data[$key], 'yes');
765 $order_bump_data[$key]['selectedStyle'] = $this->set_order_bump_data('selectedStyle', 'order_bump_layout_' . $key, $widget_settings, $order_bump_data[$key], 'style1');
766 $order_bump_data[$key]['position'] = $this->set_order_bump_data('position', 'order_bump_position_' . $key, $widget_settings, $order_bump_data[$key], 'after-order');
767 $order_bump_data[$key]['checkBoxLabel'] = $this->set_order_bump_data('checkBoxLabel', 'order_bump_checkbox_label_' . $key, $widget_settings, $order_bump_data[$key], 'Grab this offer with one click!');
768 $order_bump_data[$key]['highLightText'] = $this->set_order_bump_data('highLightText', 'order_bump_product_detail_header_' . $key, $widget_settings, $order_bump_data[$key], 'Special one time offer');
769 $order_bump_data[$key]['productDescriptionText'] = $this->set_order_bump_data('productDescriptionText', 'order_bump_product_detail_' . $key, $widget_settings, $order_bump_data[$key], 'Get this scratch proof 6D Tempered Glass Screen Protector for your iPhone. Keep your phone safe and sound just like a new one.');
770 $ob_image = $this->set_order_bump_data('productImage', 'order_bump_image_' . $key, $widget_settings, $order_bump_data[$key]);
771 $order_bump_data[$key]['productImage'] = array(
772 'id' => isset($ob_image['id']) ? $ob_image['id'] : 0,
773 'url' => isset($ob_image['url']) ? $ob_image['url'] : '',
774 );
775 }
776 }
777 update_post_meta($post_id, 'order-bump-settings', $order_bump_data);
778 // update_post_meta($post_id, 'order-bump', $order_bump_data['isEnabled']);
779 }
780 }
781 }
782 }
783
784
785 /**
786 * Redirect to next step
787 *
788 * @param $order_received_url
789 * @param $order
790 *
791 * @return string
792 *
793 * @since 1.0.0
794 */
795 public function redirect_to_funnel_thankyou_page($order_received_url, $order)
796 {
797 if ($order && Wpfnl_functions::check_if_funnel_order($order)) {
798 $order_key = $order->get_order_key();
799 $order_id = $order->get_id();
800 $link = '#';
801 $funnel_id = Wpfnl_functions::get_funnel_id_from_order($order);
802
803 if (!$funnel_id) {
804 return $link;
805 }
806
807 $current_page_id = apply_filters('wpfunnels/current_step_id', $order->get_meta('_wpfunnels_checkout_id'), $order);
808 $parent_step_id = get_post_meta($current_page_id, '_parent_step_id', true);
809 $current_page_id = $parent_step_id ? $parent_step_id : $current_page_id;
810 $next_node = Wpfnl_functions::get_next_conditional_step($funnel_id, $current_page_id, $order);
811 $next_node = apply_filters('wpfunnels/next_step_data', $next_node);
812 $next_node = apply_filters('wpfunnels/modify_next_step_based_on_order', $next_node, $order);
813
814 if (isset($next_node['step_type']) && 'thankyou' === $next_node['step_type']) {
815 if( Wpfnl_functions::is_global_thank_you_page_enabled() ){
816 return home_url() . '/checkout/order-received/' . $order_id . '/?key=' . $order_key;
817 }
818 $custom_url = Wpfnl_functions::custom_url_for_thankyou_page($next_node['step_id']);
819 if ($custom_url) {
820 return $custom_url;
821 }
822 }
823
824 $query_args = array(
825 'wpfnl-order' => $order_id,
826 'wpfnl-key' => $order_key,
827 );
828 $query_args = apply_filters('wpfunnels/order_meta', $query_args, $order);
829 $next_step_url = $this->get_thankyou_step_url($funnel_id, $next_node['step_id'], $order);
830 if ($next_step_url) {
831 return add_query_arg($query_args, $next_step_url);
832 }
833 }
834 return $order_received_url;
835 }
836
837
838 /**
839 * Get thankyou page url
840 *
841 * @param $funnel_id
842 * @param $step_id
843 * @param $order_received_url
844 *
845 * @return string
846 */
847 private function get_thankyou_step_url($funnel_id, $step_id, $order)
848 {
849 /**
850 * If pro plugin isn't installed, there is no need to redirect user to the
851 * offer page. So for the plugin we redirect the user to the funnel thankyou page
852 * forcefully. We have also placed a hook for next step url, which will be modified
853 * on the pro plugin
854 */
855
856 $next_step_url = get_permalink($step_id);
857 $step_type = get_post_meta($step_id, '_step_type', true);
858
859
860
861 $thankyou_step_url = '';
862 if (Wpfnl_functions::check_if_this_is_step_type_by_id($step_id, 'thankyou')) {
863 return apply_filters('wpfunnels/funnel_thankyou_page_url', $next_step_url, $order);
864 } else {
865 $thankyou_page_id = Wpfnl_functions::get_thankyou_page_id($funnel_id);
866 if ($thankyou_page_id) {
867 $thankyou_step_url = get_permalink($thankyou_page_id);
868 }
869 }
870 if( ! defined( 'WPFNL_PRO_DIR' ) ){
871 $thankyou_step_url = $next_step_url;
872 }
873
874 return apply_filters('wpfunnels/next_step_url', $thankyou_step_url, $next_step_url, $order);
875 }
876
877
878 /**
879 * Set order bump data
880 *
881 * @param $settings_key
882 * @param $key
883 * @param $widget_settings
884 * @param string $default_value
885 *
886 * @return mixed|string
887 *
888 * @since 2.0.0
889 */
890 private function set_order_bump_data($settings_key, $key, $widget_settings, $order_bump_data, $default_value = '')
891 {
892 $value = $default_value;
893 if (isset($widget_settings[$key])) {
894 $value = $widget_settings[$key];
895 } else {
896 $value = $order_bump_data[$settings_key];
897 }
898 return $value;
899 }
900
901
902 /**
903 * Locate wc templates from plugin folders
904 *
905 * @param $template
906 * @param $template_name
907 * @param $template_path
908 *
909 * @return string
910 *
911 * @since 2.0.3
912 */
913 public function wpfunnels_woocommerce_locate_template($template, $template_name, $template_path)
914 {
915 /***
916 * Fires when change the wc template
917 *
918 * @since 2.8.21
919 */
920 if (apply_filters('wpfunnels/maybe_locate_template', true)) {
921 $_template = $template;
922 $plugin_path = WPFNL_DIR . 'woocommerce/templates/';
923
924 if (file_exists($plugin_path . $template_name)) {
925 $template = $plugin_path . $template_name;
926 }
927
928 if (!$template) {
929 $template = $_template;
930 }
931 }
932 return $template;
933 }
934
935
936 /**
937 * Wpfnl offer meta
938 *
939 * Return offer meta
940 */
941 public function wpfnl_offer_meta($offer_meta)
942 {
943 $this->offer_metas = $offer_meta;
944 return $offer_meta;
945 }
946
947 /**
948 * Hidden order item meta
949 *
950 * @param $meta
951 *
952 * @return $meta
953 */
954 public function wpfnl_woocommerce_hidden_order_itemmeta($meta)
955 {
956 $meta = ['_wpfunnels_step_id', '_wpfnl_upsell', '_wpfnl_downsell', '_wpfnl_step_id', '_wpfunnels_offer_txn_id', '_reduced_stock', '_wpfunnels_offer_refunded'];
957 return $meta;
958 }
959
960 /**
961 * Beautify item meta on order
962 *
963 * @param $display_key, $meta, $item
964 *
965 * @return $display_key
966 */
967 public function wpfnl_beautify_item_meta_on_order($display_key, $meta, $item)
968 {
969 $offer_meta = '_wpfunnels_order_bump';
970 if (is_admin() && $item->get_type() === 'line_item' && ($meta->key === $offer_meta)) {
971 $display_key = __("Offer Type", "wpfnl");
972 }
973 return $display_key;
974 }
975
976 /**
977 * Display customize meta value
978 *
979 * @param $display_key, $meta, $item
980 *
981 * @return $meta
982 */
983 public function wpfnl_update_order_item_display_meta_value($display_key, $meta, $item)
984 {
985 if (isset($item['order_id']) && $item['order_id']) {
986 $order = wc_get_order($item['order_id']);
987 if ($order && Wpfnl_functions::check_if_funnel_order($order)) {
988 if (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_order_bump')) {
989 $meta = __("Order Bump", "wpfnl");
990 return $meta;
991 } elseif (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_upsell')) {
992 $meta = __("Upsell", "wpfnl");
993 return $meta;
994 } elseif (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_downsell')) {
995 $meta = __("Downsell", "wpfnl");
996 return $meta;
997 } elseif (is_admin() && $item->get_type() === 'shipping' && $meta->key === 'Items') {
998 $meta = $item->get_meta('Items');
999 return $meta;
1000 } else {
1001 $display_value = $meta->value;
1002 return $display_value;
1003 }
1004 } else {
1005 $display_value = $meta->value;
1006 return $display_value;
1007 }
1008 } else {
1009 $display_value = $meta->value;
1010 return $display_value;
1011 }
1012 }
1013
1014
1015 /**
1016 * May be user abandoned funnel
1017 */
1018 public function maybe_abandoned_funnel()
1019 {
1020 check_ajax_referer('abandoned_ajax_nonce', 'security');
1021 $step_id = $_POST['step_id'];
1022 $funnel_id = $_POST['funnel_id'];
1023 $cookie_name = 'wpfunnels_automation_data';
1024 $cookie = isset($_COOKIE[$cookie_name]) ? json_decode(wp_unslash($_COOKIE[$cookie_name]), true) : array();
1025 $cookie['funnel_status'] = 'abandoned';
1026 if (PHP_SESSION_DISABLED == session_status()) {
1027 session_start();
1028 if (isset($_SESSION) || $_SESSION) {
1029 if (isset($_SESSION['wpfnl_orders_' . get_current_user_id() . '_' . $funnel_id])) {
1030 unset($_SESSION['wpfnl_orders_' . get_current_user_id() . '_' . $funnel_id]);
1031 }
1032 }
1033 }
1034 $step_type = get_post_meta($step_id, '_step_type', true);
1035 if ('upsell' == $step_type || 'downsell' == $step_type || 'thankyou' == $step_type) {
1036 delete_option('wpfunnels_dynamic_offer_data');
1037 }
1038 delete_option('optin_data');
1039 do_action('wpfunnels/maybe_user_abandoned_funnel', $step_id, $funnel_id, $cookie);
1040 die();
1041 }
1042
1043
1044 /**
1045 * Redirect Thankyou page
1046 *
1047 * @param mixed $step_id
1048 * @param mixed $funnel_id
1049 *
1050 * @return [type]
1051 */
1052 public function end_journey($step_id, $funnel_id)
1053 {
1054
1055 if (!Wpfnl_Theme_Compatibility::getInstance()->is_elementor_preview()) {
1056 Wpfnl_functions::custom_url_for_thankyou_page($step_id);
1057 }
1058 }
1059 /**
1060 * Redirects from LearnDash offer step templates if accessed
1061 * without completing previous steps or user is already enrolled
1062 * in the course.
1063 */
1064 public function redirect_learndash_template()
1065 {
1066 global $post;
1067 $step_id = isset($post->ID) ? $post->ID : false;
1068
1069 if ($step_id) {
1070 $funnel_id = Wpfnl_functions::get_funnel_id_from_step($step_id);
1071 $lms_funnel = get_post_meta($funnel_id, 'is_lms', true);
1072
1073 if ($lms_funnel === 'yes') {
1074 $step_type = get_post_meta($step_id, '_step_type', true);
1075 $course = Wpfnl_lms_learndash_functions::get_course_details($step_id);
1076 $redirect_url = '';
1077 if ($step_type === 'upsell' || $step_type === 'downsell') {
1078 if (!isset($_GET['wpfnl_ld_payment'])) {
1079 $redirect_url = Wpfnl_functions::get_funnel_link($funnel_id);
1080 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url);
1081 }
1082 if ($course && !empty($course)) {
1083 $course_id = isset($course['id']) ? $course['id'] : '';
1084 $course_type = isset($course['type']) ? $course['type'] : '';
1085 $next_step = Wpfnl_functions::get_next_step($funnel_id, $step_id);
1086 $current_user_id = function_exists('get_current_user_id') ? get_current_user_id() : '';
1087
1088 if (isset($next_step['step_id'])) {
1089 if ($next_step['step_type'] == 'conditional') {
1090 $next_step = Wpfnl_functions::get_next_step($funnel_id, $next_step['step_id']);
1091 }
1092 $redirect_url = get_permalink($next_step['step_id']);
1093 }
1094
1095 if ($course_type === 'open') {
1096 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url, 'free');
1097 }
1098 if ($course_id !== '' && $current_user_id && $current_user_id !== '') {
1099 $course_access = function_exists('sfwd_lms_has_access') ? sfwd_lms_has_access($course_id, $current_user_id) : false;
1100
1101 if ($course_access) {
1102 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url, 'free');
1103 }
1104 }
1105 }
1106 }
1107 }
1108 }
1109 }
1110
1111
1112 private function create_lms_order($order_details = [])
1113 {
1114
1115 global $wpdb;
1116 $result = $wpdb->get_results("SELECT * FROM wp_wpfnl_lms_order WHERE funnel_id = " . $order_details['funnel_id'] . " AND step_id = " . $order_details['funnel_id'] . " AND user_id = " . $order_details['user_id'] . " ");
1117
1118 if (!empty($result)) {
1119 $wpdb->query($wpdb->prepare("UPDATE wp_wpfnl_lms_order SET meta='order' WHERE funnel_id = " . $order_details['funnel_id'] . " AND step_id = " . $order_details['funnel_id'] . " AND user_id = " . $order_details['user_id'] . ""));
1120 }
1121
1122
1123 $wpdb->insert('wp_wpfnl_lms_order', array(
1124 'funnel_id' => $order_details['funnel_id'],
1125 'step_id' => $order_details['step_id'],
1126 'user_id' => $order_details['user_id'],
1127 'meta' => 'new_order',
1128 'order_data' => serialize($order_details['course_details']),
1129 'date_created' => date("Y-m-d h:i:s")
1130 ));
1131 }
1132
1133 /**
1134 * After main order placed
1135 *
1136 * @param String $order_id
1137 * @param Number $funnel_id
1138 *
1139 * @since 2.5.9
1140 */
1141 public function add_order_details_to_logger($order_id, $funnel_id, $step_id)
1142 {
1143 if (Wpfnl_functions::maybe_logger_enabled() && Wpfnl_functions::is_wc_active()) {
1144 if ($order_id && $funnel_id) {
1145 $order = wc_get_order($order_id);
1146 $order_items = $order->get_items();
1147 $product_array = [];
1148 foreach ($order_items as $item_id => $item) {
1149 $product_array[] = [
1150 'name' => $item['name'],
1151 'price' => number_format($item['total'] + $item['total_tax'], 2),
1152 ];
1153 }
1154
1155 ob_start();
1156 print_r($product_array);
1157 $textual_representation = ob_get_contents();
1158 ob_end_clean();
1159
1160 Wpfnl_Logger::modify_log_file('event', $textual_representation, 'Main Order Deatils');
1161 }
1162 }
1163 }
1164
1165 /**
1166 * Add optin data to logger
1167 *
1168 * @param Integrer $step_id
1169 * @param String $post_action
1170 * @param String $action_type
1171 * @param Object $record
1172 *
1173 * @return void
1174 * @since 2.5.9
1175 */
1176 public function add_optin_data_to_logger($step_id, $post_action, $action_type, $record)
1177 {
1178 if (Wpfnl_functions::maybe_logger_enabled()) {
1179 $optin_data = (isset($record->form_data['email']) && $record->form_data['email']) ? $record->form_data : [];
1180 ob_start();
1181 print_r($optin_data);
1182 $textual_representation = ob_get_contents();
1183 ob_end_clean();
1184
1185 Wpfnl_Logger::modify_log_file('event', $textual_representation, 'Optin Submission Data');
1186 }
1187 }
1188
1189
1190 /**
1191 * Get optin data
1192 */
1193 public function get_optin_data($step_id, $post_action, $action_type, $record)
1194 {
1195
1196 $data = get_option('optin_data');
1197 $data['optin_data'][] = [
1198 'step_id' => $step_id,
1199 'step_type' => get_post_meta($step_id, '_step_type', true)
1200 ];
1201 update_option('optin_data', $data);
1202 }
1203
1204 /**
1205 * Registers an opt-in user and performs actions based on the submitted data.
1206 *
1207 * This function is used to register an opt-in user and perform additional actions
1208 * based on the provided data, such as creating a user account.
1209 *
1210 * @param int $step_id The ID of the opt-in step.
1211 * @param string $post_action The type of action to be performed after registration.
1212 * @param string $action_type The type of action being taken, such as 'redirect_to_url' or 'next_step'.
1213 * @param object $record An object containing the opt-in form data.
1214 * @param array $post_data An array of submitted post data.
1215 *
1216 * @return void
1217 * @since 2.8.2
1218 */
1219 public function register_optin_user($step_id, $post_action, $action_type, $record, $post_data)
1220 {
1221 // Extract user info from the record.
1222 $user_info = $record->form_data ?? [];
1223
1224 // Extract user's email for potential login and registration.
1225 $login = $user_info['email'] ? strstr($user_info['email'], '@', true) : '';
1226
1227 // Check WordPress site-wide registration setting FIRST.
1228 $can_register = get_option('users_can_register');
1229
1230 // Check if opt-in registration is allowed and perform related actions.
1231 if ( $can_register && !empty($post_data['optin_allow_registration']) && $post_data['optin_allow_registration'] == 'yes') {
1232 // Create user account based on the extracted login and post data.
1233 Ajax_Handler::create_user_optin_allow_registration($login, $post_data);
1234 }
1235 }
1236
1237 /**
1238 * Generates the redirect URL based on the provided data and conditions.
1239 *
1240 * This function generates a redirect URL based on the next step's data and additional
1241 * query parameters. If the next step's type is conditional, it matches the step
1242 * based on conditions and updates the redirect URL accordingly.
1243 *
1244 * @param array $response The response data containing the current redirect information.
1245 * @param int $funnel_id The ID of the funnel.
1246 * @param int $step_id The ID of the current step.
1247 * @param array $next_step The data of the next step.
1248 * @param array $query_params Additional query parameters to be added to the redirect URL.
1249 *
1250 * @return array The updated response data with the generated redirect URL.
1251 * @since 2.8.2
1252 */
1253 public function regenerate_redirect_url($response, $funnel_id, $step_id, $next_step, $query_params)
1254 {
1255 // Check if the next step type is conditional.
1256 if (!empty($next_step['step_type']) && 'conditional' === $next_step['step_type']) {
1257 // Match the conditional step and update the redirect URL.
1258 $next_step = Ajax_Handler::match_conditional_step($next_step, $funnel_id, $step_id);
1259 $response['redirect_url'] = add_query_arg($query_params, get_the_permalink($next_step['step_id']));
1260 }
1261
1262 return $response;
1263 }
1264
1265
1266 /**
1267 * Add Edit Funnel menu on admin bar for admin users
1268 *
1269 * @param \WP_Admin_Bar $admin_bar
1270 * @since 3.1.0
1271 */
1272 public function add_edit_funnel_menu(\WP_Admin_Bar $admin_bar)
1273 {
1274 if (Wpfnl_functions::is_funnel_step_page()) {
1275 $funnel_id = Wpfnl_functions::get_funnel_id();
1276
1277 if (!$funnel_id) {
1278 return;
1279 }
1280
1281 $admin_bar->add_node(
1282 array(
1283 'id' => 'edit-funnel',
1284 'title' => '<span class="ab-icon dashicons dashicons-edit"></span>' . esc_html__('Edit Funnel', 'wpfnl'),
1285 'href' => admin_url("admin.php?page=edit_funnel&id={$funnel_id}&step_id=0"),
1286 'meta' => array(
1287 'class' => 'wpf-admin-bar-edit-funnel-menu-item',
1288 ),
1289 )
1290 );
1291 }
1292 }
1293
1294
1295 /**
1296 * Add custom script to header of funnel step
1297 *
1298 * @return void
1299 * @since 3.1.0
1300 */
1301 public function add_custom_script()
1302 {
1303 global $post;
1304
1305 if (!is_object($post) && !isset($post->ID)) {
1306 return;
1307 }
1308
1309 $script = get_post_meta($post->ID, '_wpfnl_custom_script', true);
1310 if ('' !== $script) {
1311 if (false === strpos($script, htmlentities('<script'))) {
1312 $script = '<script>' . $script . '</script>';
1313 }
1314 echo '<!-- Custom WPFunnels Script -->';
1315 echo html_entity_decode($script); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1316 echo '<!-- End Custom WPFunnels Script -->';
1317 }
1318 }
1319
1320 /**
1321 * Add custom css to header of funnel step
1322 *
1323 * @return void
1324 * @since 3.1.0
1325 */
1326 public function add_custom_css()
1327 {
1328 global $post;
1329
1330 if (!is_object($post) && !isset($post->ID)) {
1331 return;
1332 }
1333
1334 $css = get_post_meta($post->ID, '_wpfnl_custom_css', true);
1335 if ('' !== $css) {
1336 if (false === strpos($css, htmlentities('<style'))) {
1337 $css = '<style>' . $css . '</style>';
1338 }
1339 echo "\n<!-- Custom WPFunnels CSS -->\n";
1340 echo html_entity_decode($css); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1341 echo "\n<!-- End Custom WPFunnels CSS -->\n";
1342 }
1343 }
1344
1345
1346
1347 /**
1348 * Load variable product modal
1349 *
1350 * @return void
1351 */
1352 public function load_variable_product_modal() {
1353 if ( Wpfnl_functions::is_funnel_step_page( 'checkout' ) ) {
1354 $checkout_id = get_the_ID();
1355 $order_bump_settings = get_post_meta( $checkout_id, 'order-bump-settings', true );
1356 if ( ! empty( $order_bump_settings ) ) {
1357 foreach ( $order_bump_settings as $key => $settings ) {
1358 if ( isset( $settings['product'] ) ) {
1359 $product = wc_get_product( $settings['product'] );
1360 if ( $product && $product->is_type( 'variable' ) ) {
1361 wc_get_template(
1362 'variable-product-modal.php',
1363 array(
1364 'product' => $product,
1365 'key' => $key,
1366 ),
1367 '',
1368 WPFNL_DIR . 'public/modules/checkout/templates/'
1369 );
1370 }
1371 }
1372 }
1373 }
1374 }
1375 }
1376 }
1377