PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.3
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.3
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
← All changes | classes/class-wpfnl-order-bump-actions.php +103 -1 3.12.13 → 3.13.3 View file →
@@ -20,8 +20,46 @@
20 20 protected $current_ob_settings;
21 21 protected $selected_ob;
22 22 protected $checkout_id;
23 23
24 + /**
25 + * Counts how many times each (checkout_id, bump key) pair has been
26 + * rendered on this request. Some checkout layouts (e.g. the express
27 + * checkout's two-column form) call `woocommerce_order_review()` more than
28 + * once, which fires the order bump position hooks more than once too, so
29 + * the same bump's markup ends up on the page more than once. Every
30 + * occurrence still needs to work (it may be the one the customer actually
31 + * sees), so instead of suppressing the repeats we give each render its
32 + * own instance number and use it to keep the checkbox's `id`/`for` pair
33 + * unique per occurrence -- see $render_instance below.
34 + *
35 + * @var array
36 + */
37 + protected static $render_counts = array();
38 +
39 + /**
40 + * The 0-based occurrence number of the bump currently being rendered,
41 + * for the (checkout_id, key) pair being processed. Templates use this to
42 + * suffix the checkbox's id/for attributes so duplicate renders on the
43 + * same page never collide -- `data-key` (still just $key) is what JS
44 + * uses to keep every occurrence of the same bump in sync.
45 + *
46 + * @var int
47 + */
48 + public $render_instance = 0;
49 +
50 + /**
51 + * @param int|string $checkout_id
52 + * @param int|string $key
53 + *
54 + * @return void
55 + */
56 + private function mark_bump_render_instance( $checkout_id, $key ) {
57 + $cache_key = $checkout_id . ':' . $key;
58 + $this->render_instance = isset( self::$render_counts[ $cache_key ] ) ? self::$render_counts[ $cache_key ] : 0;
59 + self::$render_counts[ $cache_key ] = $this->render_instance + 1;
60 + }
61 +
24 62 public function __construct() {
25 63
26 64 add_action( 'woocommerce_checkout_after_order_review', array( $this, 'add_order_bump_hidden_fields' ), 99 );
27 65 add_action( 'wpfunnels/elementor_render_order_bump', array( $this, 'load_elementor_actions' ), 9999, 2 );
@@ -27,8 +65,9 @@
27 65 add_action( 'wpfunnels/elementor_render_order_bump', array( $this, 'load_elementor_actions' ), 9999, 2 );
28 66 add_action( 'wpfunnels/bricks_render_order_bump', array( $this, 'load_elementor_actions' ), 9999, 2 );
29 67 add_action( 'wpfunnels/gb_render_order_bump_ajax', array( $this, 'load_gb_actions' ), 10, 2 );
30 68 add_action( 'wpfunnels/before_checkout_form', array( $this, 'load_actions' ), 10, 2 );
69 + add_action( 'woocommerce_checkout_update_order_review', array( $this, 'load_ajax_actions' ), 5 );
31 70
32 71 }
33 72
34 73
@@ -40,9 +79,10 @@
40 79
41 80 $this->ob_settings = $checkout_meta->get_checkout_meta_value(get_the_ID(), 'order-bump-settings', wpfnl()->meta->get_default_order_bump_meta());
42 81 foreach( $this->ob_settings as $key=>$settings ){
43 82 if ( $settings['isEnabled'] ) {
44 - echo '<input type="hidden" name="_wpfunnels_order_bump_product_'.$key.'" value="">';
83 + $in_cart = isset( $settings['product'] ) && $this->is_order_bump_in_cart( $settings['product'] );
84 + echo '<input type="hidden" name="_wpfunnels_order_bump_product_'.$key.'" value="'.( $in_cart ? esc_attr( $settings['product'] ) : '' ).'">';
45 85 echo '<input type="hidden" name="_wpfunnels_order_bump_clicked" value="">';
46 86 }
47 87 }
48 88 }
@@ -47,8 +87,33 @@
47 87 }
48 88 }
49 89
50 90
91 + /**
92 + * Load action hooks during the update_order_review AJAX request.
93 + *
94 + * `wpfunnels/before_checkout_form` only fires while the full checkout form is
95 + * rendered, so on an AJAX refresh nothing would be listening on
96 + * `wpfunnels/after_order_total`. An `after-order` bump would then be dropped from
97 + * the refreshed `.wpfnl-after-order-total` fragment and disappear from the page.
98 + *
99 + * @return void
100 + */
101 + public function load_ajax_actions() {
102 + $checkout_id = Wpfnl_functions::get_checkout_id_from_post( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
103 +
104 + if ( ! $checkout_id ) {
105 + return;
106 + }
107 +
108 + // render_order_bump_after_order() falls back to this property when $post is
109 + // unavailable, which is always the case during this AJAX request.
110 + $this->checkout_id = (int) $checkout_id;
111 +
112 + $this->load_actions( $checkout_id );
113 + }
114 +
115 +
51 116 public function load_actions( $checkout_id, $settings = array() ) {
52 117 if( !function_exists( 'is_plugin_active' ) ) {
53 118 require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
54 119 if( is_plugin_active( 'elementor/elementor.php' ) ) {
@@ -209,8 +274,35 @@
209 274 }
210 275
211 276
212 277 /**
278 + * Check if an order bump product is already present in the cart so the
279 + * checkbox can be rendered as checked after a page reload.
280 + *
281 + * @param int|string $product_id product/variation id stored in the order bump settings
282 + *
283 + * @return bool
284 + */
285 + public function is_order_bump_in_cart( $product_id ) {
286 + if ( ! Wpfnl_functions::is_wc_active() || ! WC()->cart ) {
287 + return false;
288 + }
289 +
290 + foreach ( WC()->cart->get_cart() as $cart_item ) {
291 + if ( empty( $cart_item['wpfnl_order_bump'] ) ) {
292 + continue;
293 + }
294 +
295 + if ( $cart_item['product_id'] == $product_id || ( ! empty( $cart_item['variation_id'] ) && $cart_item['variation_id'] == $product_id ) ) {
296 + return true;
297 + }
298 + }
299 +
300 + return false;
301 + }
302 +
303 +
304 + /**
213 305 * Render popup style for order bump in elementor
214 306 * builder preview
215 307 *
216 308 * @since 2.0.3
@@ -260,8 +352,10 @@
260 352 foreach( $this->ob_settings as $key=> $settings ){
261 353
262 354 if( $settings['isEnabled'] && $settings['position'] == 'popup'){
263 355
356 + $this->mark_bump_render_instance( $checkout_id, $key );
357 +
264 358 if( $settings['isEnabled'] === 'no' ){
265 359 continue;
266 360 }
267 361
@@ -341,8 +435,9 @@
341 435
342 436 foreach( $this->ob_settings as $key=> $settings ){
343 437
344 438 if( $settings['isEnabled'] && $settings['position'] == 'before-checkout'){
439 + $this->mark_bump_render_instance( $checkout_id, $key );
345 440 if( $settings['isEnabled'] === 'no' ){
346 441 continue;
347 442 }
348 443
@@ -421,8 +516,9 @@
421 516 $type = get_post_meta($funnel_id,'_wpfnl_funnel_type',true);
422 517 foreach( $this->ob_settings as $key=> $settings ){
423 518
424 519 if( $settings['isEnabled'] && $settings['position'] == 'before-lms-order'){
520 + $this->mark_bump_render_instance( $checkout_id, $key );
425 521 if( $settings['isEnabled'] === 'no' ){
426 522 continue;
427 523 }
428 524
@@ -501,8 +597,9 @@
501 597 $type = get_post_meta($funnel_id,'_wpfnl_funnel_type',true);
502 598 foreach( $this->ob_settings as $key=> $settings ){
503 599
504 600 if( $settings['isEnabled'] && $settings['position'] == 'after-lms-order'){
601 + $this->mark_bump_render_instance( $checkout_id, $key );
505 602 if( $settings['isEnabled'] === 'no' ){
506 603 continue;
507 604 }
508 605
@@ -581,8 +678,9 @@
581 678
582 679 foreach( $this->ob_settings as $key=> $settings ){
583 680
584 681 if( $settings['isEnabled'] && $settings['position'] == 'before-payment'){
682 + $this->mark_bump_render_instance( $checkout_id, $key );
585 683 if( $settings['isEnabled'] === 'no' ){
586 684 continue;
587 685 }
588 686
@@ -662,8 +760,9 @@
662 760
663 761 foreach( $this->ob_settings as $key=> $settings ){
664 762
665 763 if( $settings['isEnabled'] && $settings['position'] == 'after-payment'){
764 + $this->mark_bump_render_instance( $checkout_id, $key );
666 765 if( $settings['isEnabled'] === 'no' ){
667 766 continue;
668 767 }
669 768
@@ -743,8 +842,9 @@
743 842 $type = get_post_meta( $funnel_id, '_wpfnl_funnel_type', true );
744 843
745 844 foreach( $this->ob_settings as $key=> $settings ){
746 845 if( $settings['isEnabled'] && $settings['position'] == 'after-customer-details'){
846 + $this->mark_bump_render_instance( $checkout_id, $key );
747 847 if( $settings['isEnabled'] === 'no' ){
748 848 continue;
749 849 }
750 850 // Check conditional rules before displaying
@@ -824,8 +924,9 @@
824 924 foreach( $this->ob_settings as $key=> $settings ){
825 925
826 926 if( $settings['isEnabled'] && $settings['position'] == 'after-order'){
827 927
928 + $this->mark_bump_render_instance( $checkout_id, $key );
828 929
829 930 if( $settings['isEnabled'] === 'no' ){
830 931 continue;
831 932 }
@@ -909,8 +1010,9 @@
909 1010
910 1011 foreach( $this->ob_settings as $key=> $settings ){
911 1012
912 1013 if( $settings['isEnabled'] && $settings['position'] == 'before-order'){
1014 + $this->mark_bump_render_instance( $checkout_id, $key );
913 1015 if( $settings['isEnabled'] === 'no' ){
914 1016 continue;
915 1017 }
916 1018