From 1e004dc04e076b44754ddd407717852b077d0e16 Mon Sep 17 00:00:00 2001 From: vendidero Date: Mon, 27 May 2024 14:57:25 +0200 Subject: [PATCH] Composer update. Added payment method binding for checkboxes. --- .../checkout/checkout-checkboxes/block.js | 95 ++++++++++++++----- .../class-wc-gzd-legal-checkbox-manager.php | 54 +++++++---- includes/class-wc-gzd-legal-checkbox.php | 66 ++++++++----- src/Blocks/Checkout.php | 63 +++++++----- woocommerce-germanized.php | 6 +- 5 files changed, 192 insertions(+), 92 deletions(-) diff --git a/assets/js/blocks/checkout/checkout-checkboxes/block.js b/assets/js/blocks/checkout/checkout-checkboxes/block.js index 05bf659f3..d868b297b 100644 --- a/assets/js/blocks/checkout/checkout-checkboxes/block.js +++ b/assets/js/blocks/checkout/checkout-checkboxes/block.js @@ -1,14 +1,17 @@ /** * External dependencies */ -import { useEffect, useState, useCallback } from '@wordpress/element'; +import { useEffect, useState, useCallback, useRef } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; import { extensionCartUpdate } from '@woocommerce/blocks-checkout'; import _ from 'lodash'; +import { PAYMENT_STORE_KEY } from '@woocommerce/block-data'; import Modal from './modal'; import LegalCheckbox from "./checkboxes/legal-checkbox"; import PrivacyCheckbox from "./checkboxes/privacy-checkbox"; import SepaCheckbox from "./checkboxes/sepa-checkbox"; + const Block = ({ children, checkoutExtensionData, @@ -19,11 +22,25 @@ const Block = ({ const { setExtensionData } = checkoutExtensionData; const gzdCartData = extensions.hasOwnProperty( 'woocommerce-germanized' ) ? extensions['woocommerce-germanized'] : {}; const availableCheckboxes = gzdCartData.hasOwnProperty( 'checkboxes' ) ? gzdCartData['checkboxes'] : []; + /** + * Default state + */ const cartCheckboxes = availableCheckboxes.reduce(( acc, cur ) => ( - { ...acc, [cur.id]: cur } + { ...acc, [ cur.id ]: { ...cur, 'hidden': cur.default_hidden, 'checked': cur.default_checked } } ), {} ); const [ checkboxes, setCheckboxes ] = useState( cartCheckboxes ); const [ modalUrl, setModalUrl ] = useState( '' ); + const hasRendered = useRef( false ); + + const { + currentPaymentMethod + } = useSelect( ( select ) => { + const paymentStore = select( PAYMENT_STORE_KEY ); + + return { + currentPaymentMethod: paymentStore.getActivePaymentMethod(), + } + } ); const getExtensionDataFromCheckboxes = ( checkboxes ) => { return Object.values( checkboxes ).filter( ( checkbox ) => { if ( checkbox.checked || ( ! checkbox.has_checkbox && ! checkbox.hidden ) ) { @@ -34,24 +51,6 @@ const Block = ({ } ); }; - // Check for new/adjusted cart data, e.g. retrieved via cart updates - useEffect( () => { - let newCheckboxes = {}; - - Object.keys( cartCheckboxes ).map( ( checkboxId ) => { - const currentCheckbox = checkboxes.hasOwnProperty( checkboxId ) ? { 'checked': checkboxes[ checkboxId ].checked, 'hidden': checkboxes[ checkboxId ].hidden } : {}; - - newCheckboxes[ checkboxId ] = { ...cartCheckboxes[ checkboxId ], ...currentCheckbox } - }); - - if( !_.isEqual( newCheckboxes, checkboxes ) ) { - setCheckboxes( newCheckboxes ); - } - }, [ - cartCheckboxes, - setCheckboxes - ] ); - // Update extension data useEffect( () => { setExtensionData( @@ -63,18 +62,43 @@ const Block = ({ checkboxes ] ); + useEffect( () => { + Object.keys( checkboxes ).map( ( checkboxId ) => { + if ( checkboxes[ checkboxId ].show_for_payment_methods.length > 0 ) { + onChangeCheckbox( checkboxes[ checkboxId ] ); + } + }); + }, [ + currentPaymentMethod + ] ); + const onChangeCheckbox = useCallback( ( checkbox ) => { - setCheckboxes( ( currentCheckboxes ) => { const needsUpdate = currentCheckboxes[ checkbox.id ].checked !== checkbox.checked; - const updatedCheckboxes = { ...currentCheckboxes, [checkbox.id]: { ...checkbox } }; + + /** + * This is a tweak that overrides current checkbox hidden state + * in case the checkbox is conditionally shown for certain payment methods only + * as current payment method is only available client-side. + */ + if ( checkbox.show_for_payment_methods.length > 0 ) { + let isHidden = checkbox.default_hidden; + + if ( ! isHidden ) { + checkbox.hidden = ! _.includes( checkbox.show_for_payment_methods, currentPaymentMethod ); + } else { + checkbox.hidden = isHidden; + } + } + + const updatedCheckboxes = { ...currentCheckboxes, [ checkbox.id ]: { ...checkbox } }; if ( needsUpdate ) { extensionCartUpdate( { namespace: 'woocommerce-germanized-checkboxes', data: { - 'checkboxes': getExtensionDataFromCheckboxes( updatedCheckboxes ), + 'checkboxes': getExtensionDataFromCheckboxes( updatedCheckboxes ) }, } ); } @@ -86,10 +110,33 @@ const Block = ({ setExtensionData, checkboxes, setCheckboxes, - extensionCartUpdate + extensionCartUpdate, + currentPaymentMethod ] ); + // Check for new/adjusted cart data, e.g. retrieved via cart updates + useEffect( () => { + if ( hasRendered.current ) { + let newCheckboxes = {}; + + Object.keys( cartCheckboxes ).map( ( checkboxId ) => { + const currentCheckbox = checkboxes.hasOwnProperty( checkboxId ) ? checkboxes[ checkboxId ] : {}; + const newCheckbox = checkboxes.hasOwnProperty( checkboxId ) ? { 'checked': checkboxes[ checkboxId ].checked, 'hidden': checkboxes[ checkboxId ].hidden } : {}; + + newCheckboxes[ checkboxId ] = { ...cartCheckboxes[ checkboxId ], ...newCheckbox }; + + if ( newCheckboxes[ checkboxId ] !== currentCheckbox ) { + onChangeCheckbox( newCheckboxes[ checkboxId ] ); + } + }); + } + + hasRendered.current = true; + }, [ + availableCheckboxes + ] ); + return (
false, 'order' => $order, 'needs_age_verification' => wc_gzd_order_has_age_verification( $order->get_id() ), + 'payment_method' => $order->get_payment_method(), ); foreach ( $items as $key => $item ) { @@ -547,6 +548,7 @@ public function show_conditionally_checkout() { 'company' => WC_GZD_Checkout::instance()->get_checkout_value( 'shipping_company' ) ? WC_GZD_Checkout::instance()->get_checkout_value( 'shipping_company' ) : WC_GZD_Checkout::instance()->get_checkout_value( 'billing_company' ), 'create_account' => WC_GZD_Checkout::instance()->get_checkout_value( 'createaccount' ) ? WC_GZD_Checkout::instance()->get_checkout_value( 'createaccount' ) : false, 'needs_age_verification' => WC()->cart && wc_gzd_cart_needs_age_verification(), + 'payment_method' => WC_GZD_Checkout::instance()->get_checkout_value( 'payment_method' ) ? WC_GZD_Checkout::instance()->get_checkout_value( 'payment_method' ) : '', ); $args = array_merge( $args, $this->get_cart_product_data() ); @@ -570,6 +572,7 @@ public function update_show_conditionally( $location, $args = array(), $context 'create_account' => false, 'order' => false, 'needs_age_verification' => false, + 'payment_method' => '', ) ); @@ -708,14 +711,26 @@ public function update_show_conditionally( $location, $args = array(), $context /** * Do only apply global hide/show logic in case the checkbox is visible by default */ - if ( $checkbox_args['is_shown'] && ( $checkbox->get_show_for_countries() || $checkbox->get_show_for_categories() ) ) { - $show_for_country_is_valid = $checkbox->get_show_for_countries() ? false : true; - $show_for_categories_is_valid = $checkbox->get_show_for_categories() ? false : true; + if ( $checkbox_args['is_shown'] && ( $checkbox->get_show_for_countries() || $checkbox->get_show_for_categories() || $checkbox->get_show_for_payment_methods() ) ) { + $show_for_country_is_valid = $checkbox->get_show_for_countries() ? false : true; + $show_for_categories_is_valid = $checkbox->get_show_for_categories() ? false : true; + $show_for_payment_methods_is_valid = $checkbox->get_show_for_payment_methods() ? false : true; if ( $checkbox->get_show_for_countries() && $checkbox->show_for_country( $args['country'] ) ) { $show_for_country_is_valid = true; } + if ( $checkbox->get_show_for_payment_methods() && $checkbox->show_for_payment_method( $args['payment_method'] ) ) { + $show_for_payment_methods_is_valid = true; + } + + /** + * Checkout block payment method validation is triggered client-side. + */ + if ( WC_germanized()->is_rest_api_request() ) { + $show_for_payment_methods_is_valid = true; + } + if ( $category_ids = $checkbox->get_show_for_categories() ) { $intersected = array_intersect( $category_ids, $args['product_category_ids'] ); @@ -724,7 +739,7 @@ public function update_show_conditionally( $location, $args = array(), $context } } - if ( $show_for_country_is_valid && $show_for_categories_is_valid ) { + if ( $show_for_country_is_valid && $show_for_categories_is_valid && $show_for_payment_methods_is_valid ) { $checkbox_args['is_shown'] = true; } else { $checkbox_args['is_shown'] = false; @@ -921,21 +936,22 @@ public function register( $id, $args ) { $args = wp_parse_args( $args, array( - 'html_name' => '', - 'html_id' => '', - 'is_mandatory' => false, - 'locations' => array(), - 'supporting_locations' => array(), - 'html_wrapper_classes' => array(), - 'html_classes' => array(), - 'label_args' => array(), - 'hide_input' => false, - 'error_message' => '', - 'admin_name' => '', - 'show_for_categories' => array(), - 'show_for_countries' => array(), - 'refresh_fragments' => true, - 'is_shown' => true, + 'html_name' => '', + 'html_id' => '', + 'is_mandatory' => false, + 'locations' => array(), + 'supporting_locations' => array(), + 'html_wrapper_classes' => array(), + 'html_classes' => array(), + 'label_args' => array(), + 'hide_input' => false, + 'error_message' => '', + 'admin_name' => '', + 'show_for_categories' => array(), + 'show_for_countries' => array(), + 'show_for_payment_methods' => array(), + 'refresh_fragments' => true, + 'is_shown' => true, ) ); diff --git a/includes/class-wc-gzd-legal-checkbox.php b/includes/class-wc-gzd-legal-checkbox.php index 8430c9e5e..fc21f42da 100644 --- a/includes/class-wc-gzd-legal-checkbox.php +++ b/includes/class-wc-gzd-legal-checkbox.php @@ -7,30 +7,31 @@ class WC_GZD_Legal_Checkbox { private $id = ''; private $settings = array( - 'admin_name' => '', - 'admin_desc' => '', - 'html_id' => '', - 'html_name' => '', - 'html_classes' => array(), - 'html_wrapper_classes' => array(), - 'html_style' => '', - 'hide_input' => 'no', - 'is_mandatory' => 'no', - 'is_shown' => 'yes', - 'is_enabled' => 'yes', - 'is_core' => 'no', - 'refresh_fragments' => 'no', - 'value' => '1', - 'label' => '', - 'label_args' => array(), - 'template_name' => 'checkboxes/default.php', - 'template_args' => array(), - 'error_message' => '', - 'priority' => 10, - 'locations' => array(), - 'supporting_locations' => array(), - 'show_for_categories' => array(), - 'show_for_countries' => array(), + 'admin_name' => '', + 'admin_desc' => '', + 'html_id' => '', + 'html_name' => '', + 'html_classes' => array(), + 'html_wrapper_classes' => array(), + 'html_style' => '', + 'hide_input' => 'no', + 'is_mandatory' => 'no', + 'is_shown' => 'yes', + 'is_enabled' => 'yes', + 'is_core' => 'no', + 'refresh_fragments' => 'no', + 'value' => '1', + 'label' => '', + 'label_args' => array(), + 'template_name' => 'checkboxes/default.php', + 'template_args' => array(), + 'error_message' => '', + 'priority' => 10, + 'locations' => array(), + 'supporting_locations' => array(), + 'show_for_categories' => array(), + 'show_for_countries' => array(), + 'show_for_payment_methods' => array(), ); public function __construct( $id, $args = array() ) { @@ -475,6 +476,23 @@ public function show_for_category( $category_id ) { return in_array( absint( $category_id ), $this->get_show_for_categories(), true ); } + /** + * Payment methods to show the checkbox for. + * + * @return array + */ + public function get_show_for_payment_methods() { + return $this->settings['show_for_payment_methods']; + } + + public function set_show_for_payment_methods( $payment_methods ) { + $this->settings['show_for_payment_methods'] = array_filter( (array) $payment_methods ); + } + + public function show_for_payment_method( $payment_method ) { + return in_array( trim( $payment_method ), $this->get_show_for_payment_methods(), true ); + } + /** * Countries show the checkbox for. * diff --git a/src/Blocks/Checkout.php b/src/Blocks/Checkout.php index 54431cfe7..02e7a605c 100644 --- a/src/Blocks/Checkout.php +++ b/src/Blocks/Checkout.php @@ -288,67 +288,73 @@ private function get_cart_schema() { 'items' => array( 'type' => 'object', 'properties' => array( - 'id' => array( + 'id' => array( 'description' => __( 'Unique identifier for the checkbox within the cart.', 'woocommerce-germanized' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'name' => array( + 'name' => array( 'description' => __( 'Checkbox name.', 'woocommerce-germanized' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'label' => array( + 'label' => array( 'description' => __( 'Checkbox label.', 'woocommerce-germanized' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'checked' => array( + 'default_checked' => array( 'description' => __( 'Checkbox checked status.', 'woocommerce-germanized' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'default' => false, ), - 'hidden' => array( - 'description' => __( 'Checkbox hidden.', 'woocommerce-germanized' ), + 'default_hidden' => array( + 'description' => __( 'Checkbox hidden by default.', 'woocommerce-germanized' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'default' => false, ), - 'error_message' => array( + 'error_message' => array( 'description' => __( 'Checkbox error message.', 'woocommerce-germanized' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'wrapper_classes' => array( + 'show_for_payment_methods' => array( + 'description' => __( 'Show for specific payment methods only.', 'woocommerce-germanized' ), + 'type' => 'array', + 'context' => array( 'view', 'edit' ), + 'readonly' => true, + ), + 'wrapper_classes' => array( 'description' => __( 'Wrapper classes.', 'woocommerce-germanized' ), 'type' => 'array', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'custom_styles' => array( + 'custom_styles' => array( 'description' => __( 'Custom styles.', 'woocommerce-germanized' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'html_id' => array( + 'html_id' => array( 'description' => __( 'HTML field id.', 'woocommerce-germanized' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'has_checkbox' => array( + 'has_checkbox' => array( 'description' => __( 'Whether to show a checkbox field or not.', 'woocommerce-germanized' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'is_required' => array( + 'is_required' => array( 'description' => __( 'Whether the checkbox is required or not.', 'woocommerce-germanized' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), @@ -366,22 +372,31 @@ private function get_cart_data() { $customer = wc()->customer; foreach ( $this->get_checkboxes() as $id => $checkbox ) { + /** + * Make sure to force render checkboxes conditionally bound to certain + * payment methods only as payment method data is only available client-side. + */ + if ( $checkbox->is_enabled() && ! empty( $checkbox->get_show_for_payment_methods() ) ) { + $checkboxes_force_print[] = $checkbox->get_id(); + } + if ( ! $checkbox->is_printable() && ! in_array( $checkbox->get_id(), $checkboxes_force_print, true ) ) { continue; } $checkboxes_for_api[] = array( - 'id' => $id, - 'name' => $checkbox->get_html_name(), - 'label' => $checkbox->get_label(), - 'wrapper_classes' => array_diff( $checkbox->get_html_wrapper_classes(), array( 'validate-required', 'form-row' ) ), - 'custom_styles' => $checkbox->get_html_style(), - 'error_message' => $checkbox->get_error_message( true ), - 'html_id' => $checkbox->get_html_id(), - 'has_checkbox' => ! $checkbox->hide_input(), - 'is_required' => $checkbox->is_mandatory(), - 'checked' => $checkbox->hide_input() ? true : false, - 'hidden' => $checkbox->is_hidden(), + 'id' => $id, + 'name' => $checkbox->get_html_name(), + 'label' => $checkbox->get_label(), + 'wrapper_classes' => array_diff( $checkbox->get_html_wrapper_classes(), array( 'validate-required', 'form-row' ) ), + 'custom_styles' => $checkbox->get_html_style(), + 'error_message' => $checkbox->get_error_message( true ), + 'html_id' => $checkbox->get_html_id(), + 'has_checkbox' => ! $checkbox->hide_input(), + 'show_for_payment_methods' => $checkbox->get_show_for_payment_methods(), + 'is_required' => $checkbox->is_mandatory(), + 'default_checked' => $checkbox->hide_input() ? true : false, + 'default_hidden' => $checkbox->is_hidden(), ); } @@ -420,6 +435,8 @@ private function get_checkout_schema() { } private function get_checkboxes() { + add_filter( 'woocommerce_gzd_is_rest_api_request', '__return_true' ); + add_filter( 'woocommerce_gzd_get_checkout_value', function( $value, $key ) { diff --git a/woocommerce-germanized.php b/woocommerce-germanized.php index 36a8c0183..c56d9af23 100644 --- a/woocommerce-germanized.php +++ b/woocommerce-germanized.php @@ -636,15 +636,17 @@ public function is_frontend() { } public function is_rest_api_request() { + $is_rest_api_request = false; + if ( function_exists( 'WC' ) ) { $wc = WC(); if ( is_callable( array( $wc, 'is_rest_api_request' ) ) ) { - return $wc->is_rest_api_request(); + $is_rest_api_request = $wc->is_rest_api_request(); } } - return false; + return apply_filters( 'woocommerce_gzd_is_rest_api_request', $is_rest_api_request ); } public function setup_compatibility() {