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

1,098 lines 35.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPFunnels public module
4 *
5 * @package WPFunnels\Frontend
6 */
7 namespace WPFunnels\Frontend;
8
9 use PLL_Model;
10 use PLL_Translated_Post;
11 use WPFunnels\Ajax_Handler\Ajax_Handler;
12 use WPFunnels\Compatibility\Wpfnl_Theme_Compatibility;
13 use WPFunnels\Conditions\Wpfnl_Condition_Checker;
14 use WPFunnels\Traits\SingletonTrait;
15 use WPFunnels\Wpfnl_functions;
16 use WPFunnels\lms\helper\Wpfnl_lms_learndash_functions;
17
18 use Wpfnl_Logger;
19
20 /**
21 * The public-facing functionality of the plugin.
22 *
23 * @link https://rextheme.com
24 * @since 1.0.0
25 *
26 * @package Wpfnl
27 * @subpackage Wpfnl/public
28 */
29
30 /**
31 * The public-facing functionality of the plugin.
32 *
33 * Defines the plugin name, version, and two examples hooks for how to
34 * enqueue the public-facing stylesheet and JavaScript.
35 *
36 * @package Wpfnl
37 * @subpackage Wpfnl/public
38 * @author RexTheme <[email protected]>
39 */
40 class Wpfnl_Public
41 {
42
43 use SingletonTrait;
44
45 public $offer_metas;
46 public $step_id;
47
48 /**
49 * Initialize the class and set its properties.
50 *
51 * @param string $plugin_name The name of the plugin.
52 * @param string $version The version of this plugin.
53 *
54 * @since 1.0.0
55 */
56 public function __construct()
57 {
58 /**
59 * This will trigger when funnel first start
60 */
61 add_action( 'wp', array($this, 'init_funnel'), 1 );
62 add_action( 'wp', array($this, 'init_wp_actions'), 55 );
63
64 add_action( 'wp_enqueue_scripts', [$this, 'enqueue_styles'] );
65 add_action( 'wp_enqueue_scripts', [$this, 'enqueue_scripts'] );
66
67 add_action( 'init' , array($this, 'init_function') );
68
69 add_filter( 'wpfnl_offer_meta', array($this, 'wpfnl_offer_meta'), 10, 1 );
70
71 /**
72 * Modify the checkout order received url to next step
73 */
74 add_filter( 'woocommerce_get_checkout_order_received_url', [$this, 'redirect_to_funnel_thankyou_page'], 10, 2 );
75
76 /*
77 * This hook will be triggered once the elementor data is saved.
78 * Will be placed this hook on another class in future.
79 */
80 add_action( 'elementor/editor/after_save', array($this, 'elementor_data_after_save_action'), 10, 2 );
81 add_filter( 'woocommerce_hidden_order_itemmeta', array($this, 'wpfnl_woocommerce_hidden_order_itemmeta'), 10, 1 );
82
83 add_filter( 'woocommerce_order_item_display_meta_key', array($this, 'wpfnl_beautify_item_meta_on_order'), 10, 3 );
84
85 add_filter( 'woocommerce_order_item_display_meta_value', array($this, 'wpfnl_update_order_item_display_meta_value'), 9999, 3 );
86
87
88 /**
89 * Trigger ajax if any user abandoned a funnel
90 */
91 add_action( 'wp_ajax_nopriv_maybe_abandoned_funnel', array($this, 'maybe_abandoned_funnel'), 10 );
92 add_action( 'wp_ajax_maybe_abandoned_funnel', array($this, 'maybe_abandoned_funnel'), 10 );
93
94 add_action( 'wpfunnels/funnel_journey_end', array( $this, 'end_journey' ), 10, 2 );
95
96 add_filter( 'woocommerce_add_cart_item_data', [$this,'namespace_force_individual_cart_items'], 10, 2 );
97 add_filter( 'wpfunnels/funnel_order_placed', [$this,'add_order_details_to_logger'], 10, 3 );
98 add_action( 'wpfunnels/after_optin_submit', array( $this, 'add_optin_data_to_logger' ), 10, 4 );
99 add_action( 'wpfunnels/after_optin_submit', array( $this, 'get_optin_data' ), 10, 4 );
100 add_action( 'wpfunnels/after_optin_submit', [ $this, 'register_optin_user' ], 10, 5 );
101 add_action( 'wpfunnels/optin_submission_response', [ $this, 'regenerate_redirect_url' ], 10, 5 );
102
103 add_filter( 'posts_where', array( $this, 'posts_where' ), 99 );
104 }
105
106 /**
107 * Modify the SQL JOIN clause to exclude language-based filtering when querying posts.
108 *
109 * This function is a filter callback used in WordPress to customize the SQL query's JOIN clause
110 * when retrieving posts. It is specifically designed to work with the Polylang plugin to exclude
111 * language-based filtering from the JOIN clause.
112 *
113 * @param string $join The original SQL JOIN clause.
114 *
115 * @return string The modified SQL JOIN clause with language-based filtering excluded.
116 *
117 * @see https://polylang.pro/
118 * @since 2.8.6
119 */
120 public function posts_join( $join ) {
121 if( class_exists( 'PLL_Model' ) && class_exists( 'PLL_Translated_Post' ) ) {
122 $pll_options = get_option( 'polylang', [] );
123 $model = new PLL_Model( $pll_options );
124 $post = new PLL_Translated_Post( $model );
125 $custom_join = $post->join_clause();
126 return str_replace( $custom_join, '', $join );
127 }
128 return $join;
129 }
130
131 /**
132 * Modify the SQL WHERE clause for post queries, optionally removing language filtering.
133 *
134 * This function is a filter callback used in WordPress to customize the SQL query's WHERE clause
135 * when retrieving posts. If the query appears to target 'wpfunnel_steps', it adjusts the WHERE
136 * clause, possibly removing language-based filtering. It also adds a filter to customize the SQL
137 * JOIN clause for further modifications.
138 *
139 * @param string $where The original SQL WHERE clause.
140 *
141 * @return string The modified SQL WHERE clause, possibly without language-based filtering.
142 *
143 * @see https://wordpress.org/
144 * @since 2.8.6
145 */
146 public function posts_where( $where ) {
147 if( preg_match( '/wpfunnel_steps/', $where ) ) {
148 add_filter( 'posts_join', array( $this, 'posts_join' ), 99 );
149 $lang = $this->get_pll_term_id();
150 return str_replace( "AND pll_tr.term_taxonomy_id = {$lang}", '', $where );
151 }
152 return $where;
153 }
154
155 /**
156 * Get the term ID associated with a language slug from Polylang plugin options.
157 *
158 * This function retrieves the term ID for a specific language slug by querying the WordPress
159 * database terms table. It uses the provided language slug and the Polylang plugin options.
160 *
161 * @return int|null The term ID of the language, or null if not found.
162 *
163 * @global object $wpdb WordPress database object.
164 *
165 * @see https://polylang.pro/
166 * @since 2.8.6
167 */
168 private function get_pll_term_id() {
169 global $wpdb;
170 $pll_options = get_option( 'polylang', [] );
171 return $wpdb->get_var( $wpdb->prepare( "SELECT `term_id` FROM %i WHERE `slug`=%s", [ $wpdb->terms, $pll_options[ 'default_lang' ] ?? '' ] ) );
172 }
173
174 /**
175 * Add individual items in cart forcefully. It's implemented for indentifying orderbump items
176 *
177 * @param Array
178 * @param String
179 *
180 * @return Array
181 */
182 public function namespace_force_individual_cart_items( $cart_item_data, $product_id ) {
183
184 $post_action = filter_input_array(INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS);
185 if( isset( $post_action['action'] ) && ( 'wpfnl_order_bump_ajax' === $post_action['action'] || 'wpfnl_order_bump_ajax' === $post_action['action'] ) ){
186 $unique_cart_item_key = md5( microtime() . rand() );
187 $cart_item_data['unique_key'] = $unique_cart_item_key;
188
189 }
190 return $cart_item_data;
191 }
192
193
194
195 /**
196 * Initialize function
197 */
198 public function init_function(){
199 $offer_meta = ['_wpfunnels_order_bump'];
200 return apply_filters( 'wpfnl_offer_meta', $offer_meta );
201 }
202
203 /**
204 * Init actions for a funnel
205 *
206 * @since 2.0.2
207 */
208 public function init_funnel()
209 {
210 if (Wpfnl_functions::is_funnel_step_page()) {
211 global $post;
212 do_action('wpfunnels/wp', $post->ID);
213 $this->start_funnel_journey();
214 }
215 }
216
217
218 /**
219 * Start session data when funnel starts
220 *
221 * @since 2.0.3
222 */
223 private function start_funnel_journey()
224 {
225 Wpfnl_functions::start_journey();
226 if (Wpfnl_functions::check_if_this_is_step_type('checkout')) {
227 Wpfnl_functions::do_not_cache();
228 }
229 }
230
231
232 public function init_wp_actions() {
233 if (Wpfnl_functions::is_funnel_step_page('')) {
234 /**
235 * Enqueue public styles
236 */
237 add_action( 'wp_enqueue_scripts', array($this, 'enqueue_styles') );
238 add_action( 'wp_enqueue_scripts', array($this, 'enqueue_scripts') );
239 add_action( 'wp_enqueue_scripts', array($this, 'remove_theme_styles') );
240 add_filter( 'woocommerce_enqueue_styles', array( $this, 'enqueue_wc_styles' ), 9999 );
241
242 add_filter('woocommerce_locate_template', array($this, 'wpfunnels_woocommerce_locate_template'), 20, 3);
243 }
244 }
245
246
247 /**
248 * Register the stylesheets for the public-facing side of the site.
249 *
250 * @since 1.0.0
251 */
252 public function enqueue_styles()
253 {
254
255 /**
256 * This function is provided for demonstration purposes only.
257 *
258 * An instance of this class should be passed to the run() function
259 * defined in Wpfnl_Loader as all of the hooks are defined
260 * in that particular class.
261 *
262 * The Wpfnl_Loader will then create the relationship
263 * between the defined hooks and the functions defined in this
264 * class.
265 */
266
267 if(Wpfnl_functions::is_funnel_step_page()) {
268 add_filter( 'astra_theme_woocommerce_dynamic_css', '__return_empty_string' );
269
270 wp_enqueue_style('wpfnl-public', plugin_dir_url(__FILE__) . 'assets/css/wpfnl-public.css', [], WPFNL_VERSION, 'all');
271 //$this->load_googlefonts();
272 }
273 }
274
275 /**
276 * Register the JavaScript for the public-facing side of the site.
277 *
278 * @since 1.0.0
279 */
280 public function enqueue_scripts()
281 {
282
283 /**
284 * This function is provided for demonstration purposes only.
285 *
286 * An instance of this class should be passed to the run() function
287 * defined in Wpfnl_Loader as all of the hooks are defined
288 * in that particular class.
289 *
290 * The Wpfnl_Loader will then create the relationship
291 * between the defined hooks and the functions defined in this
292 * class.
293 */
294 if(Wpfnl_functions::is_funnel_step_page()) {
295
296 $compatibility = Wpfnl_Theme_Compatibility::getInstance();
297
298 wp_enqueue_script('wpfnl-public', plugin_dir_url(__FILE__) . 'assets/js/wpfnl-public.js', ['jquery'], WPFNL_VERSION, false);
299 wp_localize_script('wpfnl-public', 'wpfnl_obj', [
300 'ajaxurl' => admin_url('admin-ajax.php'),
301 'is_builder_preview' => $compatibility->is_builder_preview(),
302 'funnel_id' => get_post_meta( get_the_ID(), '_funnel_id', true ),
303 'step_id' => get_the_ID(),
304 'ajax_nonce' => wp_create_nonce('wpfnl'),
305 'abandoned_ajax_nonce' => wp_create_nonce('abandoned_ajax_nonce'),
306 'optin_form_nonce' => wp_create_nonce('optin_form_nonce'),
307 'is_user_logged_in' => is_user_logged_in(),
308 'user_id' => get_current_user_id(),
309 'is_login_reminder' => get_option( 'woocommerce_enable_checkout_login_reminder' ),
310 ]
311 );
312 }
313 }
314
315
316 /**
317 * This functions are from qubely.
318 */
319 private function load_googlefonts() {
320 global $blocks;
321 $contains_wpfnl_blocks = false;
322 $block_fonts = [];
323 $load_google_fonts ='yes';
324
325 if ($load_google_fonts == 'yes') {
326 $blocks=$this->parse_all_blocks();
327 $contains_wpfnl_blocks = $this->has_wpfnl_blocks($blocks);
328
329 if ($contains_wpfnl_blocks) {
330 $block_fonts = $this->gather_block_fonts($blocks, $block_fonts);
331 $global_settings = get_option($this->option_keyword);
332 $global_settings = $global_settings == false ? json_decode('{}') : json_decode($global_settings);
333 $global_settings = json_decode(json_encode($global_settings), true);
334 $gfonts = '';
335 $all_global_fonts = array();
336 if (isset($global_settings['presets']) && isset($global_settings['presets'][$global_settings['activePreset']]) && isset($global_settings['presets'][$global_settings['activePreset']]['typography'])) {
337 $all_global_fonts = $this->colsFromArray(array_column($global_settings['presets'][$global_settings['activePreset']]['typography'], 'value'), ['family', 'weight']);
338 }
339 if (count($all_global_fonts) > 0) {
340 $global_fonts = array_column($all_global_fonts, 'family');
341
342 $all_fonts = array_unique(array_merge($global_fonts, $block_fonts));
343
344 if (!empty($all_fonts)) {
345 $system = array(
346 'Arial',
347 'Tahoma',
348 'Verdana',
349 'Helvetica',
350 'Times New Roman',
351 'Trebuchet MS',
352 'Georgia',
353 );
354
355 $gfonts_attr = ':100,100italic,200,200italic,300,300italic,400,400italic,500,500italic,600,600italic,700,700italic,800,800italic,900,900italic';
356
357 foreach ($all_fonts as $font) {
358 if (!in_array($font, $system, true) && !empty($font)) {
359 $gfonts .= str_replace(' ', '+', trim($font)) . $gfonts_attr . '|';
360 }
361 }
362 }
363
364 if (!empty($gfonts)) {
365 $query_args = array(
366 'family' => $gfonts,
367 );
368
369 wp_register_style(
370 'qubely-google-fonts',
371 add_query_arg($query_args, '//fonts.googleapis.com/css'),
372 array(),
373 QUBELY_VERSION
374 );
375 wp_enqueue_style('qubely-google-fonts');
376 }
377 }
378 }
379 }
380 }
381
382
383 /**
384 * Parse all blocks
385 *
386 * @return array[]
387 */
388 public function parse_all_blocks(){
389 $blocks;
390 if (is_single() || is_page() || is_404()) {
391 global $post;
392 if (is_object($post) && property_exists($post, 'post_content')) {
393 $blocks = parse_blocks($post->post_content);
394 }
395 } elseif (is_archive() || is_home() || is_search()) {
396 global $wp_query;
397 foreach ($wp_query as $post) {
398 if (is_object($post) && property_exists($post, 'post_content')) {
399 $blocks = parse_blocks($post->post_content);
400 }
401 }
402 }
403 return $blocks;
404 }
405
406
407 /**
408 * Check whether the contents has wpfnl blocks or not
409 *
410 * @param $blocks
411 *
412 * @return bool
413 */
414 public function has_wpfnl_blocks($blocks){
415 $is_wpfnl_block = false;
416 foreach ($blocks as $key => $block) {
417 if (strpos($block['blockName'], 'wpfnl') !== false) {
418 $is_wpfnl_block = true;
419 }
420 if (isset($block['innerBlocks']) && gettype($block['innerBlocks']) == 'array' && count($block['innerBlocks']) > 0) {
421 $is_wpfnl_block = $this->has_qubely_blocks($block['innerBlocks']);
422 }
423 }
424 return $is_wpfnl_block;
425 }
426
427
428 /**
429 * Gather block google fonts
430 *
431 * @param $blocks
432 * @param $block_fonts
433 *
434 * @return array
435 */
436 public function gather_block_fonts($blocks,$block_fonts){
437 $google_fonts = $block_fonts;
438 foreach ($blocks as $key => $block) {
439 if (strpos($block['blockName'], 'wpfnl') !== false) {
440 foreach ($block['attrs'] as $key => $att) {
441 if (gettype($att) == 'array' && isset($att['openTypography']) && isset($att['family'])) {
442 if (isset($block['attrs'][$key]['activeSource'])) {
443 if ($block['attrs'][$key]['activeSource'] == 'custom') {
444 array_push($google_fonts,$block['attrs'][$key]['family']);
445 }
446 } else {
447 array_push($google_fonts,$block['attrs'][$key]['family']);
448 }
449 }
450 }
451 }
452 if(isset($block['innerBlocks']) && gettype($block['innerBlocks']) == 'array' && count($block['innerBlocks'])>0){
453 $child_fonts=$this->gather_block_fonts($block['innerBlocks'],$google_fonts);
454 if(count($child_fonts)>0){
455 $google_fonts= array_merge($google_fonts,$child_fonts);
456 }
457 }
458 }
459 return array_unique($google_fonts);
460 }
461
462
463 /**
464 * Columns from array
465 *
466 * @param array $array
467 * @param $keys
468 *
469 * @return array
470 */
471 public function colsFromArray(array $array, $keys) {
472 if (!is_array($keys)) $keys = [$keys];
473 return array_map(function ($el) use ($keys) {
474 $o = [];
475 foreach($keys as $key){
476 // 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.
477 $o[$key] = isset($el[$key])?$el[$key]:false;
478 }
479 return $o;
480 }, $array);
481 }
482
483
484 /**
485 * Remove theme style
486 *
487 * @return bool
488 */
489 public function remove_theme_styles() {
490 if( Wpfnl_functions::is_funnel_step_page() ) {
491
492 if ( Wpfnl_Theme_Compatibility::getInstance()->is_compatible_theme_enabled() ) {
493 return;
494 }
495
496 $general_settings = Wpfnl_functions::get_general_settings();
497
498 $wp_styles = wp_styles();
499 $themes_uri = get_theme_root_uri();
500
501 if( $general_settings['disable_theme_style'] == 'on' ){
502 $dequeue_theme_style = apply_filters('wpfunnels/dequeue_theme_style', true);
503
504 }else {
505 $dequeue_theme_style = apply_filters('wpfunnels/dequeue_theme_style', false);
506 }
507
508 if($dequeue_theme_style) {
509 foreach ( $wp_styles->registered as $wp_style ) {
510 if ( strpos( $wp_style->src, $themes_uri ) !== false ) {
511 wp_deregister_style( $wp_style->handle );
512 wp_dequeue_style( $wp_style->handle );
513 do_action('wpfunnels/enqueue_custom_scripts');
514 }
515 }
516 }
517 }
518 }
519
520
521 /**
522 * Enqueue woocommerce styles
523 *
524 * @return array
525 */
526 public function enqueue_wc_styles() {
527 $wc_styles = array(
528 'woocommerce-layout' => array(
529 'src' => plugins_url( 'assets/css/woocommerce-layout.css', WC_PLUGIN_FILE ),
530 'deps' => '',
531 'version' => WC_VERSION,
532 'media' => 'all',
533 'has_rtl' => true,
534 ),
535 'woocommerce-smallscreen' => array(
536 'src' => plugins_url( 'assets/css/woocommerce-smallscreen.css', WC_PLUGIN_FILE ),
537 'deps' => 'woocommerce-layout',
538 'version' => WC_VERSION,
539 'media' => 'only screen and (max-width: ' . apply_filters( 'woocommerce_style_smallscreen_breakpoint', '768px' ) . ')',
540 'has_rtl' => true,
541 ),
542 'woocommerce-general' => array(
543 'src' => plugins_url( 'assets/css/woocommerce.css', WC_PLUGIN_FILE ),
544 'deps' => '',
545 'version' => WC_VERSION,
546 'media' => 'all',
547 'has_rtl' => true,
548 ),
549 );
550 //unset( $wc_styles['woocommerce-layout'] );
551 //unset( $wc_styles['woocommerce-general'] );
552
553 return $wc_styles;
554 }
555
556
557 /**
558 * Save custom meta fields for order bump while saving
559 * checkout widget
560 *
561 * @param $post_id
562 * @param $editor_data
563 *
564 * @since 2.0.0
565 */
566 public function elementor_data_after_save_action($post_id, $editor_data)
567 {
568 if (Wpfnl_functions::check_if_this_is_step_type_by_id($post_id, 'checkout')) {
569
570 foreach ($editor_data as $key => $inner_element) {
571
572 $checkout_widget = Wpfnl_functions::recursive_multidimensional_ob_array_search_by_value('wpfnl-checkout', $inner_element['elements']);
573 $lms_checkout_widget = Wpfnl_functions::recursive_multidimensional_ob_array_search_by_value('wpfnl-lms-checkout', $inner_element['elements']);
574
575 if ($checkout_widget || $lms_checkout_widget) {
576 $widget_settings = isset($checkout_widget['settings']) ? $checkout_widget['settings'] : $lms_checkout_widget['settings'];
577 $layout = isset($widget_settings['checkout_layout']) ? $widget_settings['checkout_layout'] : 'wpfnl-col-2';
578 update_post_meta( $post_id, '_wpfnl_checkout_layout' , $layout);
579 $order_bump_status = get_post_meta($post_id, 'order-bump', true);
580 $order_bump_settings = get_post_meta($post_id, 'order-bump-settings', true);
581 $order_bump_enabled = 'no';
582 $default_order_bump_data = [
583
584 ];
585
586 if ($order_bump_status) {
587 $order_bump_enabled = $order_bump_status;
588 }
589
590 if ($order_bump_settings) {
591 $order_bump_data = $order_bump_settings;
592 } else {
593 $order_bump_data = $default_order_bump_data;
594 }
595
596 if( count($order_bump_data) > 0 ){
597 foreach( $order_bump_data as $key=>$ob ){
598 $order_bump_data[$key]['isEnabled'] = $this->set_order_bump_data('isEnabled', 'order_bump_'.$key, $widget_settings, $order_bump_data[$key], 'yes');
599 $order_bump_data[$key]['selectedStyle'] = $this->set_order_bump_data('selectedStyle', 'order_bump_layout_'.$key, $widget_settings, $order_bump_data[$key], 'style1');
600 $order_bump_data[$key]['position'] = $this->set_order_bump_data('position', 'order_bump_position_'.$key, $widget_settings, $order_bump_data[$key], 'after-order');
601 $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!');
602 $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');
603 $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.');
604 $ob_image = $this->set_order_bump_data('productImage', 'order_bump_image_'.$key, $widget_settings, $order_bump_data[$key]);
605 $order_bump_data[$key]['productImage'] = array(
606 'id' => isset($ob_image['id']) ? $ob_image['id'] : 0,
607 'url' => isset($ob_image['url']) ? $ob_image['url'] : '',
608 );
609 }
610 }
611 update_post_meta($post_id, 'order-bump-settings', $order_bump_data);
612 // update_post_meta($post_id, 'order-bump', $order_bump_data['isEnabled']);
613 }
614 }
615 }
616 }
617
618
619 /**
620 * Redirect to next step
621 *
622 * @param $order_received_url
623 * @param $order
624 *
625 * @return string
626 *
627 * @since 1.0.0
628 */
629 public function redirect_to_funnel_thankyou_page( $order_received_url, $order ) {
630
631 if ( $order && Wpfnl_functions::check_if_funnel_order( $order )) {
632
633 $order_key = $order->get_order_key();
634 $order_id = $order->get_id();
635 $link = '#';
636 $funnel_id = Wpfnl_functions::get_funnel_id_from_order( $order );
637
638 if ( !$funnel_id ) {
639 return $link;
640 }
641
642 $current_page_id = $order->get_meta('_wpfunnels_checkout_id');
643 $next_step = Wpfnl_functions::get_next_step( $funnel_id, $current_page_id );
644 $next_step = apply_filters( 'wpfunnels/next_step_data', $next_step );
645 $next_step = apply_filters( 'wpfunnels/modify_next_step_based_on_order', $next_step, $order );
646
647 if ($next_step && isset( $next_step['step_id'], $next_step['step_type'] )) {
648 if ( 'conditional' !== $next_step['step_type'] ) {
649 $next_step_id = $next_step['step_id'];
650
651 if( isset($next_step['step_type']) && 'thankyou' === $next_step['step_type'] ){
652 $custom_url = Wpfnl_functions::custom_url_for_thankyou_page( $next_step_id );
653 if( $custom_url ){
654 return $custom_url;
655 }
656 }
657
658 $query_args = array(
659 'wpfnl-order' => $order_id,
660 'wpfnl-key' => $order_key,
661 );
662
663 $next_step_url = $this->get_thankyou_step_url( $funnel_id, $next_step_id, $order );
664 if( $next_step_url ) {
665 return add_query_arg( $query_args, $next_step_url );
666 }
667 return $order_received_url;
668 } else {
669 $condition = Wpfnl_Condition_Checker::getInstance();
670 $condition_identifier = strval( $next_step['step_id'] );
671 $condition_matched = $condition->check_condition( $funnel_id, $order, $condition_identifier, $current_page_id );
672 $next_node = Wpfnl_functions::get_next_step( $funnel_id, $condition_identifier, $condition_matched );
673 $next_node = apply_filters( 'wpfunnels/next_step_data', $next_node );
674
675 if( isset($next_node['step_type']) && 'thankyou' === $next_node['step_type'] ){
676 $custom_url = Wpfnl_functions::custom_url_for_thankyou_page( $next_node['step_id'] );
677 if( $custom_url ){
678 return $custom_url;
679 }
680 }
681
682 $query_args = array(
683 'wpfnl-order' => $order_id,
684 'wpfnl-key' => $order_key,
685 );
686
687 $next_step_url = $this->get_thankyou_step_url( $funnel_id, $next_node['step_id'], $order );
688 if( $next_step_url ) {
689 return add_query_arg( $query_args, $next_step_url );
690 }
691 return $order_received_url;
692 }
693 }
694 }
695 return $order_received_url;
696 }
697
698
699 /**
700 * Get thankyou page url
701 *
702 * @param $funnel_id
703 * @param $step_id
704 * @param $order_received_url
705 *
706 * @return string
707 */
708 private function get_thankyou_step_url( $funnel_id, $step_id, $order ) {
709 /**
710 * If pro plugin isn't installed, there is no need to redirect user to the
711 * offer page. So for the plugin we redirect the user to the funnel thankyou page
712 * forcefully. We have also placed a hook for next step url, which will be modified
713 * on the pro plugin
714 */
715 $next_step_url = get_permalink( $step_id );
716 $thankyou_step_url = '';
717 if ( Wpfnl_functions::check_if_this_is_step_type_by_id( $step_id, 'thankyou' ) ) {
718 return apply_filters( 'wpfunnels/funnel_thankyou_page_url', $next_step_url, $order );
719 }
720 else {
721 $thankyou_page_id = Wpfnl_functions::get_thankyou_page_id( $funnel_id );
722 if( $thankyou_page_id ) {
723 $thankyou_step_url = get_permalink( $thankyou_page_id );
724 }
725 }
726 return apply_filters( 'wpfunnels/next_step_url', $thankyou_step_url, $next_step_url, $order );
727 }
728
729
730 /**
731 * Set order bump data
732 *
733 * @param $settings_key
734 * @param $key
735 * @param $widget_settings
736 * @param string $default_value
737 *
738 * @return mixed|string
739 *
740 * @since 2.0.0
741 */
742 private function set_order_bump_data($settings_key, $key, $widget_settings, $order_bump_data, $default_value = '')
743 {
744 $value = $default_value;
745 if (isset($widget_settings[$key])) {
746 $value = $widget_settings[$key];
747 } else {
748 $value = $order_bump_data[$settings_key];
749 }
750 return $value;
751 }
752
753
754 /**
755 * Locate wc templates from plugin folders
756 *
757 * @param $template
758 * @param $template_name
759 * @param $template_path
760 *
761 * @return string
762 *
763 * @since 2.0.3
764 */
765 public function wpfunnels_woocommerce_locate_template( $template, $template_name, $template_path ) {
766 $_template = $template;
767 $plugin_path = WPFNL_DIR . 'woocommerce/templates/';
768
769 if( file_exists( $plugin_path . $template_name ) ) {
770 $template = $plugin_path . $template_name;
771 }
772
773 if( !$template ) {
774 $template = $_template;
775 }
776 return $template;
777 }
778
779
780 /**
781 * Wpfnl offer meta
782 *
783 * Return offer meta
784 */
785 public function wpfnl_offer_meta($offer_meta){
786 $this->offer_metas = $offer_meta;
787 return $offer_meta;
788 }
789
790 /**
791 * Hidden order item meta
792 *
793 * @param $meta
794 *
795 * @return $meta
796 */
797 public function wpfnl_woocommerce_hidden_order_itemmeta($meta) {
798 $meta = ['_wpfunnels_step_id','_wpfnl_upsell','_wpfnl_downsell','_wpfnl_step_id','_wpfunnels_offer_txn_id','_reduced_stock','_wpfunnels_offer_refunded'];
799 return $meta;
800 }
801
802 /**
803 * Beautify item meta on order
804 *
805 * @param $display_key, $meta, $item
806 *
807 * @return $display_key
808 */
809 public function wpfnl_beautify_item_meta_on_order( $display_key, $meta, $item ){
810 $offer_meta = '_wpfunnels_order_bump';
811 if( is_admin() && $item->get_type() === 'line_item' && ( $meta->key === $offer_meta)) {
812 $display_key = __("Offer Type", "wpfnl" );
813 }
814 return $display_key;
815 }
816
817 /**
818 * Display customize meta value
819 *
820 * @param $display_key, $meta, $item
821 *
822 * @return $meta
823 */
824 public function wpfnl_update_order_item_display_meta_value($display_key, $meta, $item){
825 if( isset($item['order_id']) && $item['order_id'] ){
826 $order = wc_get_order( $item['order_id'] );
827 if ( $order && Wpfnl_functions::check_if_funnel_order($order) ) {
828 if( is_admin() && $item->get_type() === 'line_item' && ( $meta->key === '_wpfunnels_order_bump')) {
829 $meta = __("Order Bump", "wpfnl" );
830 return $meta;
831 }elseif( is_admin() && $item->get_type() === 'line_item' && ( $meta->key === '_wpfunnels_upsell')) {
832 $meta = __("Upsell", "wpfnl" );
833 return $meta;
834 }elseif( is_admin() && $item->get_type() === 'line_item' && ( $meta->key === '_wpfunnels_downsell')) {
835 $meta = __("Downsell", "wpfnl" );
836 return $meta;
837 }elseif( is_admin() && $item->get_type() === 'shipping' && $meta->key === 'Items') {
838 $meta = $item->get_meta('Items');
839 return $meta;
840 }else{
841 $display_value = $meta->value;
842 return $display_value;
843 }
844 }else{
845 $display_value = $meta->value;
846 return $display_value;
847
848 }
849 }else{
850 $display_value = $meta->value;
851 return $display_value;
852 }
853
854 }
855
856
857 /**
858 * May be user abandoned funnel
859 */
860 public function maybe_abandoned_funnel() {
861 check_ajax_referer( 'abandoned_ajax_nonce', 'security' );
862 $step_id = $_POST['step_id'];
863 $funnel_id = $_POST['funnel_id'];
864 $cookie_name = 'wpfunnels_automation_data';
865 $cookie = isset( $_COOKIE[$cookie_name] ) ? json_decode( wp_unslash( $_COOKIE[$cookie_name] ), true ) : array();
866 $cookie['funnel_status'] = 'abandoned';
867 if( PHP_SESSION_DISABLED == session_status() ) {
868 session_start();
869 if ( isset( $_SESSION ) || $_SESSION ) {
870 if( isset($_SESSION['wpfnl_orders_'.get_current_user_id().'_'.$funnel_id]) ){
871 unset($_SESSION['wpfnl_orders_'.get_current_user_id().'_'.$funnel_id]);
872 }
873 }
874 }
875 $step_type = get_post_meta( $step_id, '_step_type', true );
876 if( 'upsell' == $step_type || 'downsell' == $step_type || 'thankyou' == $step_type ){
877 delete_option( 'wpfunnels_dynamic_offer_data' );
878 }
879 delete_option( 'optin_data' );
880 do_action( 'wpfunnels/maybe_user_abandoned_funnel', $step_id, $funnel_id, $cookie );
881 die();
882 }
883
884
885 /**
886 * Redirect Thankyou page
887 *
888 * @param mixed $step_id
889 * @param mixed $funnel_id
890 *
891 * @return [type]
892 */
893 public function end_journey( $step_id, $funnel_id ){
894
895 if( !Wpfnl_Theme_Compatibility::getInstance()->is_elementor_preview() ){
896 Wpfnl_functions::custom_url_for_thankyou_page( $step_id );
897 }
898
899 }
900 /**
901 * Redirects from LearnDash offer step templates if accessed
902 * without completing previous steps or user is already enrolled
903 * in the course.
904 */
905 public function redirect_learndash_template()
906 {
907 global $post;
908 $step_id = isset( $post->ID ) ? $post->ID : false;
909
910 if( $step_id ) {
911 $funnel_id = Wpfnl_functions::get_funnel_id_from_step( $step_id );
912 $lms_funnel = get_post_meta( $funnel_id, 'is_lms', true );
913
914 if( $lms_funnel === 'yes' ) {
915 $step_type = get_post_meta( $step_id, '_step_type', true );
916 $course = Wpfnl_lms_learndash_functions::get_course_details( $step_id );
917 $redirect_url = '';
918 if( $step_type === 'upsell' || $step_type === 'downsell' ) {
919 if( !isset( $_GET[ 'wpfnl_ld_payment' ] ) ) {
920 $redirect_url = Wpfnl_functions::get_funnel_link( $funnel_id );
921 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect( $redirect_url );
922 }
923 if( $course && !empty( $course ) ) {
924 $course_id = isset( $course[ 'id' ] ) ? $course[ 'id' ] : '';
925 $course_type = isset( $course[ 'type' ] ) ? $course[ 'type' ] : '';
926 $next_step = Wpfnl_functions::get_next_step( $funnel_id, $step_id );
927 $current_user_id = function_exists( 'get_current_user_id' ) ? get_current_user_id() : '';
928
929 if( isset( $next_step[ 'step_id' ] ) ) {
930 if( $next_step[ 'step_type' ] == 'conditional' ) {
931 $next_step = Wpfnl_functions::get_next_step( $funnel_id, $next_step[ 'step_id' ] );
932 }
933 $redirect_url = get_permalink( $next_step[ 'step_id' ] );
934 }
935
936 if( $course_type === 'open' ) {
937 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect( $redirect_url, 'free' );
938 }
939 if( $course_id !== '' && $current_user_id && $current_user_id !== '' ) {
940 $course_access = function_exists( 'sfwd_lms_has_access' ) ? sfwd_lms_has_access( $course_id, $current_user_id ) : false;
941
942 if( $course_access ) {
943 Wpfnl_lms_learndash_functions::learndash_template_safe_redirect( $redirect_url, 'free' );
944 }
945 }
946 }
947 }
948 }
949 }
950 }
951
952
953 private function create_lms_order( $order_details = [] ){
954
955 global $wpdb;
956 $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']." ");
957
958 if( !empty($result) ){
959 $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'].""));
960 }
961
962
963 $wpdb->insert('wp_wpfnl_lms_order', array(
964 'funnel_id' => $order_details['funnel_id'],
965 'step_id' => $order_details['step_id'],
966 'user_id' => $order_details['user_id'],
967 'meta' => 'new_order',
968 'order_data' => serialize($order_details['course_details']),
969 'date_created' => date("Y-m-d h:i:s")
970 ));
971 }
972
973 /**
974 * After main order placed
975 *
976 * @param String $order_id
977 * @param Number $funnel_id
978 *
979 * @since 2.5.9
980 */
981 public function add_order_details_to_logger( $order_id, $funnel_id, $step_id ){
982 if( Wpfnl_functions::maybe_logger_enabled() && Wpfnl_functions::is_wc_active() ) {
983 if( $order_id && $funnel_id ){
984 $order = wc_get_order( $order_id );
985 $order_items = $order->get_items();
986 $product_array = [];
987 foreach ($order_items as $item_id => $item ) {
988 $product_array[] = [
989 'name' => $item['name'],
990 'price' => number_format($item['total'] + $item['total_tax'],2),
991 ];
992 }
993
994 ob_start();
995 print_r($product_array);
996 $textual_representation = ob_get_contents();
997 ob_end_clean();
998
999 Wpfnl_Logger::modify_log_file( 'event',$textual_representation,'Main Order Deatils' );
1000 }
1001 }
1002 }
1003
1004 /**
1005 * Add optin data to logger
1006 *
1007 * @param Integrer $step_id
1008 * @param String $post_action
1009 * @param String $action_type
1010 * @param Object $record
1011 *
1012 * @return void
1013 * @since 2.5.9
1014 */
1015 public function add_optin_data_to_logger( $step_id, $post_action, $action_type, $record ){
1016 if( Wpfnl_functions::maybe_logger_enabled() ) {
1017 $optin_data = ( isset($record->form_data['email']) && $record->form_data['email'] ) ? $record->form_data : [];
1018 ob_start();
1019 print_r($optin_data);
1020 $textual_representation = ob_get_contents();
1021 ob_end_clean();
1022
1023 Wpfnl_Logger::modify_log_file( 'event',$textual_representation,'Optin Submission Data' );
1024 }
1025 }
1026
1027
1028 /**
1029 * Get optin data
1030 */
1031 public function get_optin_data( $step_id, $post_action, $action_type, $record ){
1032
1033 $data = get_option('optin_data');
1034 $data['optin_data'][] = [
1035 'step_id' => $step_id,
1036 'step_type' => get_post_meta( $step_id, '_step_type', true )
1037 ];
1038 update_option('optin_data', $data );
1039
1040 }
1041
1042 /**
1043 * Registers an opt-in user and performs actions based on the submitted data.
1044 *
1045 * This function is used to register an opt-in user and perform additional actions
1046 * based on the provided data, such as creating a user account.
1047 *
1048 * @param int $step_id The ID of the opt-in step.
1049 * @param string $post_action The type of action to be performed after registration.
1050 * @param string $action_type The type of action being taken, such as 'redirect_to_url' or 'next_step'.
1051 * @param object $record An object containing the opt-in form data.
1052 * @param array $post_data An array of submitted post data.
1053 *
1054 * @return void
1055 * @since 2.8.2
1056 */
1057 public function register_optin_user( $step_id, $post_action, $action_type, $record, $post_data ) {
1058 // Extract user info from the record.
1059 $user_info = $record->form_data ?? [];
1060
1061 // Extract user's email for potential login and registration.
1062 $login = $user_info['email'] ? strstr($user_info['email'], '@', true) : '';
1063
1064 // Check if opt-in registration is allowed and perform related actions.
1065 if (!empty($post_data['optin_allow_registration']) && $post_data['optin_allow_registration'] == 'yes') {
1066 // Create user account based on the extracted login and post data.
1067 Ajax_Handler::create_user_optin_allow_registration($login, $post_data);
1068 }
1069 }
1070
1071 /**
1072 * Generates the redirect URL based on the provided data and conditions.
1073 *
1074 * This function generates a redirect URL based on the next step's data and additional
1075 * query parameters. If the next step's type is conditional, it matches the step
1076 * based on conditions and updates the redirect URL accordingly.
1077 *
1078 * @param array $response The response data containing the current redirect information.
1079 * @param int $funnel_id The ID of the funnel.
1080 * @param int $step_id The ID of the current step.
1081 * @param array $next_step The data of the next step.
1082 * @param array $query_params Additional query parameters to be added to the redirect URL.
1083 *
1084 * @return array The updated response data with the generated redirect URL.
1085 * @since 2.8.2
1086 */
1087 public function regenerate_redirect_url( $response, $funnel_id, $step_id, $next_step, $query_params ) {
1088 // Check if the next step type is conditional.
1089 if( !empty( $next_step[ 'step_type' ] ) && 'conditional' === $next_step[ 'step_type' ] ) {
1090 // Match the conditional step and update the redirect URL.
1091 $next_step = Ajax_Handler::match_conditional_step( $next_step, $funnel_id, $step_id );
1092 $response[ 'redirect_url' ] = add_query_arg( $query_params, get_the_permalink( $next_step[ 'step_id' ] ) );
1093 }
1094
1095 return $response;
1096 }
1097 }
1098