PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.2
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.2
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.13.2, at public/class-wpfnl-public.php

1,389 lines 45.0 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 // When the checkout step has quantity selection enabled, let the bump
291 // item go through the same editable quantity control as every other
292 // cart item instead of forcing a fixed, non-editable badge.
293 $step_id = wp_doing_ajax() ? Wpfnl_functions::is_funnel_checkout_page()['id'] : get_the_ID();
294 if ( 'yes' === get_post_meta( $step_id, '_wpfnl_quantity_support', true ) ) {
295 return $quantity;
296 }
297 return ' <strong class="product-quantity">' . $cart_item['quantity'] . '</strong>';
298 }
299 return $quantity;
300 }
301
302 /**
303 * Add custom product title to order line item meta
304 *
305 * @param \WC_Order_Item_Product $item
306 * @param string $cart_item_key
307 * @param array $values
308 * @param \WC_Order $order
309 */
310 public function wpfnl_order_bump_order_line_item($item, $cart_item_key, $values, $order)
311 {
312 if (isset($values['wpfnl_order_bump']) && $values['wpfnl_order_bump'] && isset($values['wpfnl_ob_title']) && !empty($values['wpfnl_ob_title'])) {
313 $item->set_name($values['wpfnl_ob_title']);
314 }
315 }
316
317 /**
318 * Initialize function
319 */
320 public function init_function()
321 {
322 $offer_meta = ['_wpfunnels_order_bump'];
323 return apply_filters('wpfnl_offer_meta', $offer_meta);
324 }
325
326 /**
327 * Init actions for a funnel
328 *
329 * @since 2.0.2
330 */
331 public function init_funnel()
332 {
333 if (Wpfnl_functions::is_funnel_step_page()) {
334 global $post;
335 do_action('wpfunnels/wp', $post->ID);
336 $this->start_funnel_journey();
337 }
338 }
339
340
341 /**
342 * Start session data when funnel starts
343 *
344 * @since 2.0.3
345 */
346 private function start_funnel_journey()
347 {
348 Wpfnl_functions::start_journey();
349 if (Wpfnl_functions::check_if_this_is_step_type('checkout')) {
350 Wpfnl_functions::do_not_cache();
351 }
352 }
353
354
355 public function init_wp_actions()
356 {
357 $is_ajax = wp_doing_ajax() || isset( $_GET['wc-ajax'] );
358 if ( Wpfnl_functions::is_funnel_step_page('') || $is_ajax ) {
359 /**
360 * Enqueue public styles
361 */
362 if ( ! $is_ajax ) {
363 add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
364 add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
365 add_action('wp_enqueue_scripts', array($this, 'remove_theme_styles'));
366 add_filter('woocommerce_enqueue_styles', array($this, 'enqueue_wc_styles'), 9999);
367 }
368
369 add_filter('woocommerce_locate_template', array($this, 'wpfunnels_woocommerce_locate_template'), 20, 3);
370 }
371 }
372
373
374 /**
375 * Register the stylesheets for the public-facing side of the site.
376 *
377 * @since 1.0.0
378 */
379 public function enqueue_styles()
380 {
381
382 /**
383 * This function is provided for demonstration purposes only.
384 *
385 * An instance of this class should be passed to the run() function
386 * defined in Wpfnl_Loader as all of the hooks are defined
387 * in that particular class.
388 *
389 * The Wpfnl_Loader will then create the relationship
390 * between the defined hooks and the functions defined in this
391 * class.
392 */
393
394 if (Wpfnl_functions::is_funnel_step_page()) {
395 add_filter('astra_theme_woocommerce_dynamic_css', '__return_empty_string');
396
397 wp_enqueue_style('wpfnl-public', plugin_dir_url(__FILE__) . 'assets/css/wpfnl-public.css', [], WPFNL_VERSION, 'all');
398 //$this->load_googlefonts();
399 }
400 }
401
402 /**
403 * Register the JavaScript for the public-facing side of the site.
404 *
405 * @since 1.0.0
406 */
407 public function enqueue_scripts()
408 {
409
410 /**
411 * This function is provided for demonstration purposes only.
412 *
413 * An instance of this class should be passed to the run() function
414 * defined in Wpfnl_Loader as all of the hooks are defined
415 * in that particular class.
416 *
417 * The Wpfnl_Loader will then create the relationship
418 * between the defined hooks and the functions defined in this
419 * class.
420 */
421 if (Wpfnl_functions::is_funnel_step_page()) {
422
423 $compatibility = Wpfnl_Theme_Compatibility::getInstance();
424
425 wp_enqueue_script('wpfnl-public', plugin_dir_url(__FILE__) . 'assets/js/wpfnl-public.js', ['jquery'], WPFNL_VERSION, false);
426 if ( Wpfnl_functions::is_funnel_step_page( 'checkout' ) ) {
427 $checkout_id = get_the_ID();
428 $order_bump_settings = get_post_meta( $checkout_id, 'order-bump-settings', true );
429
430 if ( ! empty( $order_bump_settings ) ) {
431 foreach ( $order_bump_settings as $key => $settings ) {
432 if ( isset( $settings['product'] ) ) {
433 $product = wc_get_product( $settings['product'] );
434 if ( $product && $product->is_type( 'variable' ) ) {
435 wp_enqueue_script( 'wc-single-product' );
436 break;
437 }
438 }
439 }
440 }
441 }
442 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);
443 wp_localize_script(
444 'wpfnl-public',
445 'wpfnl_obj',
446 [
447 'ajaxurl' => admin_url('admin-ajax.php'),
448 'is_builder_preview' => $compatibility->is_builder_preview(),
449 'funnel_id' => get_post_meta(get_the_ID(), '_funnel_id', true),
450 'step_id' => get_the_ID(),
451 'ajax_nonce' => wp_create_nonce('wpfnl'),
452 'abandoned_ajax_nonce' => wp_create_nonce('abandoned_ajax_nonce'),
453 'optin_form_nonce' => wp_create_nonce('optin_form_nonce'),
454 'is_user_logged_in' => is_user_logged_in(),
455 'user_id' => get_current_user_id(),
456 'is_login_reminder' => get_option('woocommerce_enable_checkout_login_reminder'),
457 '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',
458 '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',
459 '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',
460 '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',
461 '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',
462 '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',
463 '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',
464 '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',
465 '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) : '',
466 'phone_utils_url' => plugin_dir_url(__FILE__) . 'assets/phone/js/utils.js',
467 ]
468 );
469
470 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')) {
471 wp_enqueue_style('intl-tel-input-style', plugin_dir_url(__FILE__) . 'assets/phone/css/intlTelInput.css', array(), WPFNL_VERSION);
472 wp_enqueue_script('intl-tel-input-script', plugin_dir_url(__FILE__) . 'assets/phone/js/intlTelInput.min.js', array('jquery'), WPFNL_VERSION, true);
473 wp_enqueue_script('intl-tel-input-utils', plugin_dir_url(__FILE__) . 'assets/phone/js/utils.js', array('intl-tel-input-script'), WPFNL_VERSION, true);
474 }
475
476 $post_id = get_the_ID();
477 $custom_css = get_post_meta($post_id, 'rex_gutenberg_css', true);
478
479 if ($custom_css) {
480 wp_register_style('rex-gutenberg-css', false);
481 wp_enqueue_style('rex-gutenberg-css');
482 wp_add_inline_style('rex-gutenberg-css', $custom_css);
483 }
484 }
485 }
486
487
488 /**
489 * This functions are from qubely.
490 */
491 private function load_googlefonts()
492 {
493 global $blocks;
494 $contains_wpfnl_blocks = false;
495 $block_fonts = [];
496 $load_google_fonts = 'yes';
497
498 if ($load_google_fonts == 'yes') {
499 $blocks = $this->parse_all_blocks();
500 $contains_wpfnl_blocks = $this->has_wpfnl_blocks($blocks);
501
502 if ($contains_wpfnl_blocks) {
503 $block_fonts = $this->gather_block_fonts($blocks, $block_fonts);
504 $global_settings = get_option($this->option_keyword);
505 $global_settings = $global_settings == false ? json_decode('{}') : json_decode($global_settings);
506 $global_settings = json_decode(json_encode($global_settings), true);
507 $gfonts = '';
508 $all_global_fonts = array();
509 if (isset($global_settings['presets']) && isset($global_settings['presets'][$global_settings['activePreset']]) && isset($global_settings['presets'][$global_settings['activePreset']]['typography'])) {
510 $all_global_fonts = $this->colsFromArray(array_column($global_settings['presets'][$global_settings['activePreset']]['typography'], 'value'), ['family', 'weight']);
511 }
512 if (count($all_global_fonts) > 0) {
513 $global_fonts = array_column($all_global_fonts, 'family');
514
515 $all_fonts = array_unique(array_merge($global_fonts, $block_fonts));
516
517 if (!empty($all_fonts)) {
518 $system = array(
519 'Arial',
520 'Tahoma',
521 'Verdana',
522 'Helvetica',
523 'Times New Roman',
524 'Trebuchet MS',
525 'Georgia',
526 );
527
528 $gfonts_attr = ':100,100italic,200,200italic,300,300italic,400,400italic,500,500italic,600,600italic,700,700italic,800,800italic,900,900italic';
529
530 foreach ($all_fonts as $font) {
531 if (!in_array($font, $system, true) && !empty($font)) {
532 $gfonts .= str_replace(' ', '+', trim($font)) . $gfonts_attr . '|';
533 }
534 }
535 }
536
537 if (!empty($gfonts)) {
538 $query_args = array(
539 'family' => $gfonts,
540 );
541
542 wp_register_style(
543 'qubely-google-fonts',
544 add_query_arg($query_args, '//fonts.googleapis.com/css'),
545 array(),
546 QUBELY_VERSION
547 );
548 wp_enqueue_style('qubely-google-fonts');
549 }
550 }
551 }
552 }
553 }
554
555
556 /**
557 * Parse all blocks
558 *
559 * @return array[]
560 */
561 public function parse_all_blocks()
562 {
563 $blocks;
564 if (is_single() || is_page() || is_404()) {
565 global $post;
566 if (is_object($post) && property_exists($post, 'post_content')) {
567 $blocks = parse_blocks($post->post_content);
568 }
569 } elseif (is_archive() || is_home() || is_search()) {
570 global $wp_query;
571 foreach ($wp_query as $post) {
572 if (is_object($post) && property_exists($post, 'post_content')) {
573 $blocks = parse_blocks($post->post_content);
574 }
575 }
576 }
577 return $blocks;
578 }
579
580
581 /**
582 * Check whether the contents has wpfnl blocks or not
583 *
584 * @param $blocks
585 *
586 * @return bool
587 */
588 public function has_wpfnl_blocks($blocks)
589 {
590 $is_wpfnl_block = false;
591 foreach ($blocks as $key => $block) {
592 if (strpos($block['blockName'], 'wpfnl') !== false) {
593 $is_wpfnl_block = true;
594 }
595 if (isset($block['innerBlocks']) && gettype($block['innerBlocks']) == 'array' && count($block['innerBlocks']) > 0) {
596 $is_wpfnl_block = $this->has_qubely_blocks($block['innerBlocks']);
597 }
598 }
599 return $is_wpfnl_block;
600 }
601
602
603 /**
604 * Gather block google fonts
605 *
606 * @param $blocks
607 * @param $block_fonts
608 *
609 * @return array
610 */
611 public function gather_block_fonts($blocks, $block_fonts)
612 {
613 $google_fonts = $block_fonts;
614 foreach ($blocks as $key => $block) {
615 if (strpos($block['blockName'], 'wpfnl') !== false) {
616 foreach ($block['attrs'] as $key => $att) {
617 if (gettype($att) == 'array' && isset($att['openTypography']) && isset($att['family'])) {
618 if (isset($block['attrs'][$key]['activeSource'])) {
619 if ($block['attrs'][$key]['activeSource'] == 'custom') {
620 array_push($google_fonts, $block['attrs'][$key]['family']);
621 }
622 } else {
623 array_push($google_fonts, $block['attrs'][$key]['family']);
624 }
625 }
626 }
627 }
628 if (isset($block['innerBlocks']) && gettype($block['innerBlocks']) == 'array' && count($block['innerBlocks']) > 0) {
629 $child_fonts = $this->gather_block_fonts($block['innerBlocks'], $google_fonts);
630 if (count($child_fonts) > 0) {
631 $google_fonts = array_merge($google_fonts, $child_fonts);
632 }
633 }
634 }
635 return array_unique($google_fonts);
636 }
637
638
639 /**
640 * Columns from array
641 *
642 * @param array $array
643 * @param $keys
644 *
645 * @return array
646 */
647 public function colsFromArray(array $array, $keys)
648 {
649 if (!is_array($keys)) $keys = [$keys];
650 return array_map(function ($el) use ($keys) {
651 $o = [];
652 foreach ($keys as $key) {
653 // 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.
654 $o[$key] = isset($el[$key]) ? $el[$key] : false;
655 }
656 return $o;
657 }, $array);
658 }
659
660
661 /**
662 * Remove theme style
663 *
664 * @return bool
665 */
666 public function remove_theme_styles()
667 {
668 if (Wpfnl_functions::is_funnel_step_page()) {
669
670 if (Wpfnl_Theme_Compatibility::getInstance()->is_compatible_theme_enabled()) {
671 return;
672 }
673
674 $general_settings = Wpfnl_functions::get_general_settings();
675
676 $wp_styles = wp_styles();
677 $themes_uri = get_theme_root_uri();
678
679 if ($general_settings['disable_theme_style'] == 'on') {
680 $dequeue_theme_style = apply_filters('wpfunnels/dequeue_theme_style', true);
681 } else {
682 $dequeue_theme_style = apply_filters('wpfunnels/dequeue_theme_style', false);
683 }
684
685 if ($dequeue_theme_style) {
686 foreach ($wp_styles->registered as $wp_style) {
687 if (strpos($wp_style->src, $themes_uri) !== false) {
688 wp_deregister_style($wp_style->handle);
689 wp_dequeue_style($wp_style->handle);
690 do_action('wpfunnels/enqueue_custom_scripts');
691 }
692 }
693 }
694 }
695 }
696
697
698 /**
699 * Enqueue woocommerce styles
700 *
701 * @return array
702 */
703 public function enqueue_wc_styles()
704 {
705 $wc_styles = array(
706 'woocommerce-layout' => array(
707 'src' => plugins_url('assets/css/woocommerce-layout.css', WC_PLUGIN_FILE),
708 'deps' => '',
709 'version' => WC_VERSION,
710 'media' => 'all',
711 'has_rtl' => true,
712 ),
713 'woocommerce-smallscreen' => array(
714 'src' => plugins_url('assets/css/woocommerce-smallscreen.css', WC_PLUGIN_FILE),
715 'deps' => 'woocommerce-layout',
716 'version' => WC_VERSION,
717 'media' => 'only screen and (max-width: ' . apply_filters('woocommerce_style_smallscreen_breakpoint', '768px') . ')',
718 'has_rtl' => true,
719 ),
720 'woocommerce-general' => array(
721 'src' => plugins_url('assets/css/woocommerce.css', WC_PLUGIN_FILE),
722 'deps' => '',
723 'version' => WC_VERSION,
724 'media' => 'all',
725 'has_rtl' => true,
726 ),
727 );
728 //unset( $wc_styles['woocommerce-layout'] );
729 //unset( $wc_styles['woocommerce-general'] );
730
731 return $wc_styles;
732 }
733
734
735 /**
736 * Save custom meta fields for order bump while saving
737 * checkout widget
738 *
739 * @param $post_id
740 * @param $editor_data
741 *
742 * @since 2.0.0
743 */
744 public function elementor_data_after_save_action($post_id, $editor_data)
745 {
746 if (Wpfnl_functions::check_if_this_is_step_type_by_id($post_id, 'checkout')) {
747
748 foreach ($editor_data as $key => $inner_element) {
749
750 $checkout_widget = Wpfnl_functions::recursive_multidimensional_ob_array_search_by_value('wpfnl-checkout', $inner_element['elements']);
751 $lms_checkout_widget = Wpfnl_functions::recursive_multidimensional_ob_array_search_by_value('wpfnl-lms-checkout', $inner_element['elements']);
752
753 if ($checkout_widget || $lms_checkout_widget) {
754 $widget_settings = isset($checkout_widget['settings']) ? $checkout_widget['settings'] : $lms_checkout_widget['settings'];
755 $layout = isset($widget_settings['checkout_layout']) ? $widget_settings['checkout_layout'] : 'wpfnl-col-2';
756 update_post_meta($post_id, '_wpfnl_checkout_layout', $layout);
757 $order_bump_status = get_post_meta($post_id, 'order-bump', true);
758 $order_bump_settings = get_post_meta($post_id, 'order-bump-settings', true);
759 $order_bump_enabled = 'no';
760 $default_order_bump_data = [];
761
762 if ($order_bump_status) {
763 $order_bump_enabled = $order_bump_status;
764 }
765
766 if ($order_bump_settings) {
767 $order_bump_data = $order_bump_settings;
768 } else {
769 $order_bump_data = $default_order_bump_data;
770 }
771
772 if (count($order_bump_data) > 0) {
773 foreach ($order_bump_data as $key => $ob) {
774 $order_bump_data[$key]['isEnabled'] = $this->set_order_bump_data('isEnabled', 'order_bump_' . $key, $widget_settings, $order_bump_data[$key], 'yes');
775 $order_bump_data[$key]['selectedStyle'] = $this->set_order_bump_data('selectedStyle', 'order_bump_layout_' . $key, $widget_settings, $order_bump_data[$key], 'style1');
776 $order_bump_data[$key]['position'] = $this->set_order_bump_data('position', 'order_bump_position_' . $key, $widget_settings, $order_bump_data[$key], 'after-order');
777 $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!');
778 $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');
779 $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.');
780 $ob_image = $this->set_order_bump_data('productImage', 'order_bump_image_' . $key, $widget_settings, $order_bump_data[$key]);
781 $order_bump_data[$key]['productImage'] = array(
782 'id' => isset($ob_image['id']) ? $ob_image['id'] : 0,
783 'url' => isset($ob_image['url']) ? $ob_image['url'] : '',
784 );
785 }
786 }
787 update_post_meta($post_id, 'order-bump-settings', $order_bump_data);
788 // update_post_meta($post_id, 'order-bump', $order_bump_data['isEnabled']);
789 }
790 }
791 }
792 }
793
794
795 /**
796 * Redirect to next step
797 *
798 * @param $order_received_url
799 * @param $order
800 *
801 * @return string
802 *
803 * @since 1.0.0
804 */
805 public function redirect_to_funnel_thankyou_page($order_received_url, $order)
806 {
807 if ($order && Wpfnl_functions::check_if_funnel_order($order)) {
808 $order_key = $order->get_order_key();
809 $order_id = $order->get_id();
810 $link = '#';
811 $funnel_id = Wpfnl_functions::get_funnel_id_from_order($order);
812
813 if (!$funnel_id) {
814 return $link;
815 }
816
817 $current_page_id = apply_filters('wpfunnels/current_step_id', $order->get_meta('_wpfunnels_checkout_id'), $order);
818 $parent_step_id = get_post_meta($current_page_id, '_parent_step_id', true);
819 $current_page_id = $parent_step_id ? $parent_step_id : $current_page_id;
820 $next_node = Wpfnl_functions::get_next_conditional_step($funnel_id, $current_page_id, $order);
821 $next_node = apply_filters('wpfunnels/next_step_data', $next_node);
822 $next_node = apply_filters('wpfunnels/modify_next_step_based_on_order', $next_node, $order);
823
824 if (isset($next_node['step_type']) && 'thankyou' === $next_node['step_type']) {
825 if( Wpfnl_functions::is_global_thank_you_page_enabled() ){
826 return home_url() . '/checkout/order-received/' . $order_id . '/?key=' . $order_key;
827 }
828 $custom_url = Wpfnl_functions::custom_url_for_thankyou_page($next_node['step_id']);
829 if ($custom_url) {
830 return $custom_url;
831 }
832 }
833
834 $query_args = array(
835 'wpfnl-order' => $order_id,
836 'wpfnl-key' => $order_key,
837 );
838 $query_args = apply_filters('wpfunnels/order_meta', $query_args, $order);
839 $next_step_url = $this->get_thankyou_step_url($funnel_id, $next_node['step_id'], $order);
840 if ($next_step_url) {
841 return add_query_arg($query_args, $next_step_url);
842 }
843 }
844 return $order_received_url;
845 }
846
847
848 /**
849 * Get thankyou page url
850 *
851 * @param $funnel_id
852 * @param $step_id
853 * @param $order_received_url
854 *
855 * @return string
856 */
857 private function get_thankyou_step_url($funnel_id, $step_id, $order)
858 {
859 /**
860 * If pro plugin isn't installed, there is no need to redirect user to the
861 * offer page. So for the plugin we redirect the user to the funnel thankyou page
862 * forcefully. We have also placed a hook for next step url, which will be modified
863 * on the pro plugin
864 */
865
866 $next_step_url = get_permalink($step_id);
867 $step_type = get_post_meta($step_id, '_step_type', true);
868
869
870
871 $thankyou_step_url = '';
872 if (Wpfnl_functions::check_if_this_is_step_type_by_id($step_id, 'thankyou')) {
873 return apply_filters('wpfunnels/funnel_thankyou_page_url', $next_step_url, $order);
874 } else {
875 $thankyou_page_id = Wpfnl_functions::get_thankyou_page_id($funnel_id);
876 if ($thankyou_page_id) {
877 $thankyou_step_url = get_permalink($thankyou_page_id);
878 }
879 }
880 if( ! defined( 'WPFNL_PRO_DIR' ) ){
881 $thankyou_step_url = $next_step_url;
882 }
883
884 return apply_filters('wpfunnels/next_step_url', $thankyou_step_url, $next_step_url, $order);
885 }
886
887
888 /**
889 * Set order bump data
890 *
891 * @param $settings_key
892 * @param $key
893 * @param $widget_settings
894 * @param string $default_value
895 *
896 * @return mixed|string
897 *
898 * @since 2.0.0
899 */
900 private function set_order_bump_data($settings_key, $key, $widget_settings, $order_bump_data, $default_value = '')
901 {
902 $value = $default_value;
903 if (isset($widget_settings[$key])) {
904 $value = $widget_settings[$key];
905 } else {
906 $value = $order_bump_data[$settings_key];
907 }
908 return $value;
909 }
910
911
912 /**
913 * Locate wc templates from plugin folders
914 *
915 * @param $template
916 * @param $template_name
917 * @param $template_path
918 *
919 * @return string
920 *
921 * @since 2.0.3
922 */
923 public function wpfunnels_woocommerce_locate_template($template, $template_name, $template_path)
924 {
925 /***
926 * Fires when change the wc template
927 *
928 * @since 2.8.21
929 */
930 if (apply_filters('wpfunnels/maybe_locate_template', true)) {
931 $_template = $template;
932 $plugin_path = WPFNL_DIR . 'woocommerce/templates/';
933
934 if (file_exists($plugin_path . $template_name)) {
935 $template = $plugin_path . $template_name;
936 }
937
938 if (!$template) {
939 $template = $_template;
940 }
941 }
942 return $template;
943 }
944
945
946 /**
947 * Wpfnl offer meta
948 *
949 * Return offer meta
950 */
951 public function wpfnl_offer_meta($offer_meta)
952 {
953 $this->offer_metas = $offer_meta;
954 return $offer_meta;
955 }
956
957 /**
958 * Hidden order item meta
959 *
960 * @param $meta
961 *
962 * @return $meta
963 */
964 public function wpfnl_woocommerce_hidden_order_itemmeta($meta)
965 {
966 $meta = ['_wpfunnels_step_id', '_wpfnl_upsell', '_wpfnl_downsell', '_wpfnl_step_id', '_wpfunnels_offer_txn_id', '_reduced_stock', '_wpfunnels_offer_refunded'];
967 return $meta;
968 }
969
970 /**
971 * Beautify item meta on order
972 *
973 * @param $display_key, $meta, $item
974 *
975 * @return $display_key
976 */
977 public function wpfnl_beautify_item_meta_on_order($display_key, $meta, $item)
978 {
979 $offer_meta = '_wpfunnels_order_bump';
980 if (is_admin() && $item->get_type() === 'line_item' && ($meta->key === $offer_meta)) {
981 $display_key = __("Offer Type", "wpfnl");
982 }
983 return $display_key;
984 }
985
986 /**
987 * Display customize meta value
988 *
989 * @param $display_key, $meta, $item
990 *
991 * @return $meta
992 */
993 public function wpfnl_update_order_item_display_meta_value($display_key, $meta, $item)
994 {
995 if (isset($item['order_id']) && $item['order_id']) {
996 $order = wc_get_order($item['order_id']);
997 if ($order && Wpfnl_functions::check_if_funnel_order($order)) {
998 if (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_order_bump')) {
999 $meta = __("Order Bump", "wpfnl");
1000 return $meta;
1001 } elseif (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_upsell')) {
1002 $meta = __("Upsell", "wpfnl");
1003 return $meta;
1004 } elseif (is_admin() && $item->get_type() === 'line_item' && ($meta->key === '_wpfunnels_downsell')) {
1005 $meta = __("Downsell", "wpfnl");
1006 return $meta;
1007 } elseif (is_admin() && $item->get_type() === 'shipping' && $meta->key === 'Items') {
1008 $meta = $item->get_meta('Items');
1009 return $meta;
1010 } else {
1011 $display_value = $meta->value;
1012 return $display_value;
1013 }
1014 } else {
1015 $display_value = $meta->value;
1016 return $display_value;
1017 }
1018 } else {
1019 $display_value = $meta->value;
1020 return $display_value;
1021 }
1022 }
1023
1024
1025 /**
1026 * May be user abandoned funnel
1027 */
1028 public function maybe_abandoned_funnel()
1029 {
1030 check_ajax_referer('abandoned_ajax_nonce', 'security');
1031 $step_id = $_POST['step_id'];
1032 $funnel_id = $_POST['funnel_id'];
1033 $cookie_name = 'wpfunnels_automation_data';
1034 $cookie = isset($_COOKIE[$cookie_name]) ? json_decode(wp_unslash($_COOKIE[$cookie_name]), true) : array();
1035 $cookie['funnel_status'] = 'abandoned';
1036 if (PHP_SESSION_DISABLED == session_status()) {
1037 session_start();
1038 if (isset($_SESSION) || $_SESSION) {
1039 if (isset($_SESSION['wpfnl_orders_' . get_current_user_id() . '_' . $funnel_id])) {
1040 unset($_SESSION['wpfnl_orders_' . get_current_user_id() . '_' . $funnel_id]);
1041 }
1042 }
1043 }
1044 $step_type = get_post_meta($step_id, '_step_type', true);
1045 if ('upsell' == $step_type || 'downsell' == $step_type || 'thankyou' == $step_type) {
1046 delete_option('wpfunnels_dynamic_offer_data');
1047 }
1048 delete_option('optin_data');
1049 do_action('wpfunnels/maybe_user_abandoned_funnel', $step_id, $funnel_id, $cookie);
1050 die();
1051 }
1052
1053
1054 /**
1055 * Redirect Thankyou page
1056 *
1057 * @param mixed $step_id
1058 * @param mixed $funnel_id
1059 *
1060 * @return [type]
1061 */
1062 public function end_journey($step_id, $funnel_id)
1063 {
1064
1065 if (!Wpfnl_Theme_Compatibility::getInstance()->is_elementor_preview()) {
1066 Wpfnl_functions::custom_url_for_thankyou_page($step_id);
1067 }
1068 }
1069 /**
1070 * Redirects from LearnDash offer step templates if accessed
1071 * without completing previous steps or user is already enrolled
1072 * in the course.
1073 */
1074 public function redirect_learndash_template()
1075 {
1076 global $post;
1077 $step_id = isset($post->ID) ? $post->ID : false;
1078
1079 if ($step_id) {
1080 $funnel_id = Wpfnl_functions::get_funnel_id_from_step($step_id);
1081 $lms_funnel = get_post_meta($funnel_id, 'is_lms', true);
1082
1083 if ($lms_funnel === 'yes') {
1084 $step_type = get_post_meta($step_id, '_step_type', true);
1085 $course = Wpfnl_lms_learndash_functions::get_course_details($step_id);
1086 $redirect_url = '';
1087 if ($step_type === 'upsell' || $step_type === 'downsell') {
1088 if (!isset($_GET['wpfnl_ld_payment'])) {
1089 $redirect_url = Wpfnl_functions::get_funnel_link($funnel_id);
1090 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url);
1091 }
1092 if ($course && !empty($course)) {
1093 $course_id = isset($course['id']) ? $course['id'] : '';
1094 $course_type = isset($course['type']) ? $course['type'] : '';
1095 $next_step = Wpfnl_functions::get_next_step($funnel_id, $step_id);
1096 $current_user_id = function_exists('get_current_user_id') ? get_current_user_id() : '';
1097
1098 if (isset($next_step['step_id'])) {
1099 if ($next_step['step_type'] == 'conditional') {
1100 $next_step = Wpfnl_functions::get_next_step($funnel_id, $next_step['step_id']);
1101 }
1102 $redirect_url = get_permalink($next_step['step_id']);
1103 }
1104
1105 if ($course_type === 'open') {
1106 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url, 'free');
1107 }
1108 if ($course_id !== '' && $current_user_id && $current_user_id !== '') {
1109 $course_access = function_exists('sfwd_lms_has_access') ? sfwd_lms_has_access($course_id, $current_user_id) : false;
1110
1111 if ($course_access) {
1112 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect($redirect_url, 'free');
1113 }
1114 }
1115 }
1116 }
1117 }
1118 }
1119 }
1120
1121
1122 private function create_lms_order($order_details = [])
1123 {
1124
1125 global $wpdb;
1126 $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'] . " ");
1127
1128 if (!empty($result)) {
1129 $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'] . ""));
1130 }
1131
1132
1133 $wpdb->insert('wp_wpfnl_lms_order', array(
1134 'funnel_id' => $order_details['funnel_id'],
1135 'step_id' => $order_details['step_id'],
1136 'user_id' => $order_details['user_id'],
1137 'meta' => 'new_order',
1138 'order_data' => serialize($order_details['course_details']),
1139 'date_created' => date("Y-m-d h:i:s")
1140 ));
1141 }
1142
1143 /**
1144 * After main order placed
1145 *
1146 * @param String $order_id
1147 * @param Number $funnel_id
1148 *
1149 * @since 2.5.9
1150 */
1151 public function add_order_details_to_logger($order_id, $funnel_id, $step_id)
1152 {
1153 if (Wpfnl_functions::maybe_logger_enabled() && Wpfnl_functions::is_wc_active()) {
1154 if ($order_id && $funnel_id) {
1155 $order = wc_get_order($order_id);
1156 $order_items = $order->get_items();
1157 $product_array = [];
1158 foreach ($order_items as $item_id => $item) {
1159 $product_array[] = [
1160 'name' => $item['name'],
1161 'price' => number_format($item['total'] + $item['total_tax'], 2),
1162 ];
1163 }
1164
1165 ob_start();
1166 print_r($product_array);
1167 $textual_representation = ob_get_contents();
1168 ob_end_clean();
1169
1170 Wpfnl_Logger::modify_log_file('event', $textual_representation, 'Main Order Deatils');
1171 }
1172 }
1173 }
1174
1175 /**
1176 * Add optin data to logger
1177 *
1178 * @param Integrer $step_id
1179 * @param String $post_action
1180 * @param String $action_type
1181 * @param Object $record
1182 *
1183 * @return void
1184 * @since 2.5.9
1185 */
1186 public function add_optin_data_to_logger($step_id, $post_action, $action_type, $record)
1187 {
1188 if (Wpfnl_functions::maybe_logger_enabled()) {
1189 $optin_data = (isset($record->form_data['email']) && $record->form_data['email']) ? $record->form_data : [];
1190 ob_start();
1191 print_r($optin_data);
1192 $textual_representation = ob_get_contents();
1193 ob_end_clean();
1194
1195 Wpfnl_Logger::modify_log_file('event', $textual_representation, 'Optin Submission Data');
1196 }
1197 }
1198
1199
1200 /**
1201 * Get optin data
1202 */
1203 public function get_optin_data($step_id, $post_action, $action_type, $record)
1204 {
1205
1206 $data = get_option('optin_data');
1207 $data['optin_data'][] = [
1208 'step_id' => $step_id,
1209 'step_type' => get_post_meta($step_id, '_step_type', true)
1210 ];
1211 update_option('optin_data', $data);
1212 }
1213
1214 /**
1215 * Registers an opt-in user and performs actions based on the submitted data.
1216 *
1217 * This function is used to register an opt-in user and perform additional actions
1218 * based on the provided data, such as creating a user account.
1219 *
1220 * @param int $step_id The ID of the opt-in step.
1221 * @param string $post_action The type of action to be performed after registration.
1222 * @param string $action_type The type of action being taken, such as 'redirect_to_url' or 'next_step'.
1223 * @param object $record An object containing the opt-in form data.
1224 * @param array $post_data An array of submitted post data.
1225 *
1226 * @return void
1227 * @since 2.8.2
1228 */
1229 public function register_optin_user($step_id, $post_action, $action_type, $record, $post_data)
1230 {
1231 // Extract user info from the record.
1232 $user_info = $record->form_data ?? [];
1233
1234 // Extract user's email for potential login and registration.
1235 $login = $user_info['email'] ? strstr($user_info['email'], '@', true) : '';
1236
1237 // Kept as an early-out next to the request-supplied flag; the
1238 // authoritative check lives in create_user_optin_allow_registration(),
1239 // which every registration path routes through.
1240 $can_register = get_option('users_can_register');
1241
1242 // Check if opt-in registration is allowed and perform related actions.
1243 if ( $can_register && !empty($post_data['optin_allow_registration']) && $post_data['optin_allow_registration'] == 'yes') {
1244 // Create user account based on the extracted login and post data.
1245 Ajax_Handler::create_user_optin_allow_registration($login, $post_data);
1246 }
1247 }
1248
1249 /**
1250 * Generates the redirect URL based on the provided data and conditions.
1251 *
1252 * This function generates a redirect URL based on the next step's data and additional
1253 * query parameters. If the next step's type is conditional, it matches the step
1254 * based on conditions and updates the redirect URL accordingly.
1255 *
1256 * @param array $response The response data containing the current redirect information.
1257 * @param int $funnel_id The ID of the funnel.
1258 * @param int $step_id The ID of the current step.
1259 * @param array $next_step The data of the next step.
1260 * @param array $query_params Additional query parameters to be added to the redirect URL.
1261 *
1262 * @return array The updated response data with the generated redirect URL.
1263 * @since 2.8.2
1264 */
1265 public function regenerate_redirect_url($response, $funnel_id, $step_id, $next_step, $query_params)
1266 {
1267 // Check if the next step type is conditional.
1268 if (!empty($next_step['step_type']) && 'conditional' === $next_step['step_type']) {
1269 // Match the conditional step and update the redirect URL.
1270 $next_step = Ajax_Handler::match_conditional_step($next_step, $funnel_id, $step_id);
1271 $response['redirect_url'] = add_query_arg($query_params, get_the_permalink($next_step['step_id']));
1272 }
1273
1274 return $response;
1275 }
1276
1277
1278 /**
1279 * Add Edit Funnel menu on admin bar for admin users
1280 *
1281 * @param \WP_Admin_Bar $admin_bar
1282 * @since 3.1.0
1283 */
1284 public function add_edit_funnel_menu(\WP_Admin_Bar $admin_bar)
1285 {
1286 if (Wpfnl_functions::is_funnel_step_page()) {
1287 $funnel_id = Wpfnl_functions::get_funnel_id();
1288
1289 if (!$funnel_id) {
1290 return;
1291 }
1292
1293 $admin_bar->add_node(
1294 array(
1295 'id' => 'edit-funnel',
1296 'title' => '<span class="ab-icon dashicons dashicons-edit"></span>' . esc_html__('Edit Funnel', 'wpfnl'),
1297 'href' => admin_url("admin.php?page=edit_funnel&id={$funnel_id}&step_id=0"),
1298 'meta' => array(
1299 'class' => 'wpf-admin-bar-edit-funnel-menu-item',
1300 ),
1301 )
1302 );
1303 }
1304 }
1305
1306
1307 /**
1308 * Add custom script to header of funnel step
1309 *
1310 * @return void
1311 * @since 3.1.0
1312 */
1313 public function add_custom_script()
1314 {
1315 global $post;
1316
1317 if (!is_object($post) && !isset($post->ID)) {
1318 return;
1319 }
1320
1321 $script = get_post_meta($post->ID, '_wpfnl_custom_script', true);
1322 if ('' !== $script) {
1323 if (false === strpos($script, htmlentities('<script'))) {
1324 $script = '<script>' . $script . '</script>';
1325 }
1326 echo '<!-- Custom WPFunnels Script -->';
1327 echo html_entity_decode($script); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1328 echo '<!-- End Custom WPFunnels Script -->';
1329 }
1330 }
1331
1332 /**
1333 * Add custom css to header of funnel step
1334 *
1335 * @return void
1336 * @since 3.1.0
1337 */
1338 public function add_custom_css()
1339 {
1340 global $post;
1341
1342 if (!is_object($post) && !isset($post->ID)) {
1343 return;
1344 }
1345
1346 $css = get_post_meta($post->ID, '_wpfnl_custom_css', true);
1347 if ('' !== $css) {
1348 if (false === strpos($css, htmlentities('<style'))) {
1349 $css = '<style>' . $css . '</style>';
1350 }
1351 echo "\n<!-- Custom WPFunnels CSS -->\n";
1352 echo html_entity_decode($css); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1353 echo "\n<!-- End Custom WPFunnels CSS -->\n";
1354 }
1355 }
1356
1357
1358
1359 /**
1360 * Load variable product modal
1361 *
1362 * @return void
1363 */
1364 public function load_variable_product_modal() {
1365 if ( Wpfnl_functions::is_funnel_step_page( 'checkout' ) ) {
1366 $checkout_id = get_the_ID();
1367 $order_bump_settings = get_post_meta( $checkout_id, 'order-bump-settings', true );
1368 if ( ! empty( $order_bump_settings ) ) {
1369 foreach ( $order_bump_settings as $key => $settings ) {
1370 if ( isset( $settings['product'] ) ) {
1371 $product = wc_get_product( $settings['product'] );
1372 if ( $product && $product->is_type( 'variable' ) ) {
1373 wc_get_template(
1374 'variable-product-modal.php',
1375 array(
1376 'product' => $product,
1377 'key' => $key,
1378 ),
1379 '',
1380 WPFNL_DIR . 'public/modules/checkout/templates/'
1381 );
1382 }
1383 }
1384 }
1385 }
1386 }
1387 }
1388 }
1389