From 44e62c35f184ff6b16e3d9c7cb51ad26bc421816 Mon Sep 17 00:00:00 2001 From: Laurel Fulford Date: Tue, 10 Jun 2025 11:17:38 -0700 Subject: [PATCH 1/3] feat: add manual option to mark subscriptions as giftable --- ...lass-woocommerce-subscriptions-gifting.php | 27 ++++++++++++ .../class-woocommerce-products.php | 16 ++++++- .../custom-product-options/index.js | 44 ++++++++++++++++++- 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/includes/plugins/woocommerce-subscriptions/class-woocommerce-subscriptions-gifting.php b/includes/plugins/woocommerce-subscriptions/class-woocommerce-subscriptions-gifting.php index 3b99ae0e58..f6bb249289 100644 --- a/includes/plugins/woocommerce-subscriptions/class-woocommerce-subscriptions-gifting.php +++ b/includes/plugins/woocommerce-subscriptions/class-woocommerce-subscriptions-gifting.php @@ -19,6 +19,7 @@ class WooCommerce_Subscriptions_Gifting { public static function init() { \add_filter( 'wcsg_new_recipient_account_details_fields', [ __CLASS__, 'new_recipient_fields' ] ); \add_filter( 'wcsg_require_shipping_address_for_virtual_products', '__return_false' ); + \add_filter( 'wcsg_is_giftable_product', [ __CLASS__, 'is_giftable_product' ], 10, 2 ); \add_filter( 'default_option_woocommerce_subscriptions_gifting_gifting_checkbox_text', [ __CLASS__, 'default_gifting_checkbox_text' ] ); \add_filter( 'newpack_reader_activation_reader_is_without_password', [ __CLASS__, 'is_reader_without_password' ], 10, 2 ); } @@ -80,5 +81,31 @@ public static function is_reader_without_password( $is_reader_without_password, } return $is_reader_without_password; } + + /** + * Filter to check if a product is giftable. + * + * @param bool $is_giftable Whether the product is giftable. + * @param \WC_Product $product The product object. + * + * @return bool + */ + public static function is_giftable_product( $is_giftable, $product ) { + // Check if gifting is enabled for this product. + $allow_gifting = get_post_meta( $product->get_id(), '_newspack_allow_gifting', true ); + if ( 'yes' !== $allow_gifting ) { + return false; + } + + // Check if product is a subscription type, just in case. + $product_type = $product->get_type(); + if ( ! in_array( $product_type, [ 'subscription', 'variable-subscription' ] ) ) { + return false; + } + + // Check if subscription limit is set to 'no' (no limit), just in case. + $subscription_limit = get_post_meta( $product->get_id(), '_subscription_limit', true ); + return 'no' === $subscription_limit; + } } WooCommerce_Subscriptions_Gifting::init(); diff --git a/includes/plugins/woocommerce/class-woocommerce-products.php b/includes/plugins/woocommerce/class-woocommerce-products.php index 0a88031760..ffdf39e89a 100644 --- a/includes/plugins/woocommerce/class-woocommerce-products.php +++ b/includes/plugins/woocommerce/class-woocommerce-products.php @@ -49,7 +49,8 @@ public static function admin_enqueue_scripts() { * @return array Keyed array of custom product options. */ public static function get_custom_options() { - return [ + + $custom_options = [ 'newspack_autocomplete_orders' => [ 'id' => '_newspack_autocomplete_orders', 'wrapper_class' => '', @@ -59,6 +60,19 @@ public static function get_custom_options() { 'product_types' => [ 'simple', 'variation', 'subscription', 'subscription_variation' ], ], ]; + + if ( \Newspack\WooCommerce_Subscriptions_Gifting::is_active() ) { + // Don't limit by product type so we can show/hide it based on product settings. + $custom_options['newspack_allow_gifting'] = [ + 'id' => '_newspack_allow_gifting', + 'wrapper_class' => '', + 'label' => __( 'Allow to be gifted', 'newspack-plugin' ), + 'description' => __( 'Allow this subscription product to be gifted to another reader.', 'newspack-plugin' ), + 'default' => 'no', + ]; + } + + return $custom_options; } /** diff --git a/src/other-scripts/custom-product-options/index.js b/src/other-scripts/custom-product-options/index.js index 4394355b70..04650c6a47 100644 --- a/src/other-scripts/custom-product-options/index.js +++ b/src/other-scripts/custom-product-options/index.js @@ -1,8 +1,12 @@ /* globals jQuery */ -( function ( $ ) { +( function( $ ) { if ( ! $ ) { return; } + + /** + * Hide the 'Virtual' checkbox for variable products. + */ $( '#variable_product_options' ).on( 'change', 'input.variable_is_virtual', function ( e ) { $( e.currentTarget ) .closest( '.woocommerce_variation' ) @@ -16,4 +20,42 @@ .show(); } } ); + + /** + * Handle 'Allow to be gifted' checkbox visibility and state. + */ + function handleGiftingCheckbox() { + const $giftingCheckbox = $( '#_newspack_allow_gifting' ); + const $subscriptionLimit = $( '#_subscription_limit' ).val(); + const $productType = $( '#product-type' ).val(); + const $giftingLabel = $giftingCheckbox.closest( 'label' ); + + // If there's no subscription limit and the product type is subscription-related, show the gifting checkbox. + if ( 'no' === $subscriptionLimit && ( $productType === 'subscription' || $productType === 'variable-subscription' ) ) { + $giftingLabel.show(); + + // Restore previous state of the checkboxif it exists + const previousState = $giftingCheckbox.data( 'previous-state' ); + if ( previousState !== undefined ) { + // If undefined, switch it to the default (unchecked) state. + $giftingCheckbox.prop( 'checked', previousState ); + $giftingCheckbox.removeData( 'previous-state' ); + } + } else { + // Store current state before hiding. + $giftingCheckbox.data( 'previous-state', $giftingCheckbox.prop( 'checked' ) ); + $giftingCheckbox.prop( 'checked', false ); + $giftingLabel.hide(); + } + } + + // Initialize when document is ready. + $( document ).ready( function() { + handleGiftingCheckbox(); + + // Update when subscription limit changes. + $( '#_subscription_limit' ).on( 'change', handleGiftingCheckbox ); + // Update when product type changes (switching to and away from subscription products). + $( '#product-type' ).on( 'change', handleGiftingCheckbox ); + } ); } )( jQuery ); From 6d80bc8e6bf384ecb8a08578743ca784e810ff1d Mon Sep 17 00:00:00 2001 From: Laurel Fulford Date: Tue, 10 Jun 2025 11:58:52 -0700 Subject: [PATCH 2/3] fix: restore some formatting --- src/other-scripts/custom-product-options/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/other-scripts/custom-product-options/index.js b/src/other-scripts/custom-product-options/index.js index 04650c6a47..c51b981a20 100644 --- a/src/other-scripts/custom-product-options/index.js +++ b/src/other-scripts/custom-product-options/index.js @@ -1,5 +1,5 @@ /* globals jQuery */ -( function( $ ) { +( function ( $ ) { if ( ! $ ) { return; } From 9ac4833e6322ff33a91b91039f12a150e40dd15b Mon Sep 17 00:00:00 2001 From: Laurel Fulford Date: Wed, 11 Jun 2025 08:13:19 -0700 Subject: [PATCH 3/3] fix: updating some code comments --- src/other-scripts/custom-product-options/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/other-scripts/custom-product-options/index.js b/src/other-scripts/custom-product-options/index.js index c51b981a20..1744e5207c 100644 --- a/src/other-scripts/custom-product-options/index.js +++ b/src/other-scripts/custom-product-options/index.js @@ -34,7 +34,7 @@ if ( 'no' === $subscriptionLimit && ( $productType === 'subscription' || $productType === 'variable-subscription' ) ) { $giftingLabel.show(); - // Restore previous state of the checkboxif it exists + // Restore previous state of the checkbox from this product edit session, if it exists. const previousState = $giftingCheckbox.data( 'previous-state' ); if ( previousState !== undefined ) { // If undefined, switch it to the default (unchecked) state. @@ -42,7 +42,7 @@ $giftingCheckbox.removeData( 'previous-state' ); } } else { - // Store current state before hiding. + // Store current state before unchecking the checkbox and hiding it. $giftingCheckbox.data( 'previous-state', $giftingCheckbox.prop( 'checked' ) ); $giftingCheckbox.prop( 'checked', false ); $giftingLabel.hide(); @@ -53,9 +53,9 @@ $( document ).ready( function() { handleGiftingCheckbox(); - // Update when subscription limit changes. + // Update when the subscription limit changes. $( '#_subscription_limit' ).on( 'change', handleGiftingCheckbox ); - // Update when product type changes (switching to and away from subscription products). + // Update when the product type changes (switching to and away from subscription products). $( '#product-type' ).on( 'change', handleGiftingCheckbox ); } ); } )( jQuery );