Updates to 8.3.0
This commit is contained in:
parent
bfe7fb609d
commit
56e03c0544
|
|
@ -77,6 +77,14 @@ a.close-subscriptions-search {
|
|||
display: block;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.woocommerce_options_panel ._subscription_downloads_field .description,
|
||||
.woocommerce_options_panel .subscription_linked_downloadable_products .description {
|
||||
clear: both;
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Variation Subscription Product Sync Settings */
|
||||
.variable_subscription_sync .subscription_sync_annual > .select2-container {
|
||||
max-width: 48%;
|
||||
|
|
@ -385,6 +393,12 @@ p._subscription_gifting_field_description.form-field .description {
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#woocommerce-product-data
|
||||
.form-field._subscription_downloads_field
|
||||
.select2-selection__choice {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Variation Pricing Fields with WooCommerce 3.0+ */
|
||||
.wc-metaboxes-wrapper .variable_subscription_trial label,
|
||||
.wc-metaboxes-wrapper .variable_subscription_pricing label,
|
||||
|
|
|
|||
|
|
@ -639,6 +639,53 @@ jQuery( function ( $ ) {
|
|||
},
|
||||
} );
|
||||
|
||||
/**
|
||||
* Relocates the linked downloadable product fields in the context of a variable subscription product, and
|
||||
* selectively shows/hides it, depending on whether the variation is downloadable or not.
|
||||
*/
|
||||
function initLinkedDownloadableProductFieldsForVariationProduct() {
|
||||
$( '#variable_product_options .variable_subscription_linked_downloadable_products' )
|
||||
.not( '.wcs_moved' )
|
||||
.each( function () {
|
||||
const $this = $( this );
|
||||
const $variablePricing = $( this ).siblings( '.variable_pricing' );
|
||||
|
||||
const $downloadableFiles = $( this ).siblings( '.form-row.downloadable_files' ).parent( 'div' );
|
||||
|
||||
$this.insertAfter( $downloadableFiles );
|
||||
$this.addClass( 'wcs_gifting_moved' );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the linked downloadable product fields to the correct location (which is underneath the downloadable files
|
||||
* section).
|
||||
*/
|
||||
function relocateLinkedDownloadableProductFields() {
|
||||
const $linkedDownloadableProducts = $( '.subscription_linked_downloadable_products_section' );
|
||||
const $productTypeSelector = $('select#product-type');
|
||||
|
||||
const showHide = () => {
|
||||
$productTypeSelector.val() === WCSubscriptions.productType
|
||||
? $linkedDownloadableProducts.show()
|
||||
: $linkedDownloadableProducts.hide();
|
||||
};
|
||||
|
||||
$linkedDownloadableProducts
|
||||
.not( '.wcs_moved' )
|
||||
.each( function () {
|
||||
const $downloadableFilesSection = $( '.form-field.downloadable_files' ).parent( 'div' );
|
||||
|
||||
if ( $downloadableFilesSection.length === 1 ) {
|
||||
$( this ).insertAfter( $downloadableFilesSection );
|
||||
$( this ).addClass( 'wcs_moved' );
|
||||
}
|
||||
} );
|
||||
|
||||
showHide();
|
||||
$productTypeSelector.on( 'change', showHide );
|
||||
}
|
||||
|
||||
$( '.options_group.pricing ._sale_price_field .description' ).prepend(
|
||||
'<span id="sale-price-period" style="display: none;"></span>'
|
||||
);
|
||||
|
|
@ -653,9 +700,9 @@ jQuery( function ( $ ) {
|
|||
$( '.options_group.subscription_pricing' )
|
||||
);
|
||||
|
||||
// Move the subscription variation pricing and gifting sections to a better location in the DOM on load
|
||||
// We do this because these sections are initially loaded at the end of the variable product options section,
|
||||
// which is not the best location for them, so we move to before the "Sale price" section.
|
||||
// Move the subscription variation pricing, gifting and linked downloadable product sections to a better location in
|
||||
// the DOM on load. We do this because these sections are initially loaded at the end of the variable product
|
||||
// options section, which is not the best location for them, so we move to before the "Sale price" section.
|
||||
if (
|
||||
$( '#variable_product_options .variable_subscription_pricing' ).length >
|
||||
0
|
||||
|
|
@ -668,6 +715,7 @@ jQuery( function ( $ ) {
|
|||
) {
|
||||
$.moveSubscriptionGiftingFields();
|
||||
}
|
||||
|
||||
// When a variation is added
|
||||
$( '#woocommerce-product-data' ).on(
|
||||
'woocommerce_variations_added woocommerce_variations_loaded',
|
||||
|
|
@ -677,6 +725,7 @@ jQuery( function ( $ ) {
|
|||
$.showHideVariableSubscriptionMeta();
|
||||
$.showHideSyncOptions();
|
||||
$.setSubscriptionLengths();
|
||||
initLinkedDownloadableProductFieldsForVariationProduct();
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -690,6 +739,7 @@ jQuery( function ( $ ) {
|
|||
$.showHideSyncOptions();
|
||||
$.disableEnableOneTimeShipping();
|
||||
$.showHideSubscriptionsPanels();
|
||||
relocateLinkedDownloadableProductFields();
|
||||
}
|
||||
|
||||
// Update subscription ranges when subscription period or interval is changed
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
/* global wc_subscription_downloads_product */
|
||||
jQuery( document ).ready( function ( $ ) {
|
||||
function subscriptionDownloadsChosen() {
|
||||
$( 'select.subscription-downloads-ids' ).ajaxChosen({
|
||||
method: 'GET',
|
||||
url: wc_subscription_downloads_product.ajax_url,
|
||||
dataType: 'json',
|
||||
afterTypeDelay: 100,
|
||||
minTermLength: 1,
|
||||
data: {
|
||||
action: 'wc_subscription_downloads_search',
|
||||
security: wc_subscription_downloads_product.security
|
||||
}
|
||||
}, function ( data ) {
|
||||
|
||||
var orders = {};
|
||||
|
||||
$.each( data, function ( i, val ) {
|
||||
orders[i] = val;
|
||||
});
|
||||
|
||||
return orders;
|
||||
});
|
||||
}
|
||||
|
||||
subscriptionDownloadsChosen();
|
||||
|
||||
$( 'body' ).on( 'woocommerce_variations_added', function () {
|
||||
subscriptionDownloadsChosen();
|
||||
});
|
||||
});
|
||||
|
|
@ -18,7 +18,8 @@ jQuery( document ).ready( function ( $ ) {
|
|||
.find( '.recipient_email' )
|
||||
.trigger( 'focus' );
|
||||
}
|
||||
} );
|
||||
} )
|
||||
.removeClass( 'hidden' );
|
||||
|
||||
const shipToDifferentAddressCheckbox = $( document ).find(
|
||||
'#ship-to-different-address-checkbox'
|
||||
|
|
@ -31,12 +32,14 @@ jQuery( document ).ready( function ( $ ) {
|
|||
$( this )
|
||||
.closest( '.wcsg_add_recipient_fields_container' )
|
||||
.find( '.wcsg_add_recipient_fields' )
|
||||
.slideUp( 250 );
|
||||
.slideUp( 250 )
|
||||
.addClass( 'hidden' );
|
||||
|
||||
const recipientEmailElement = $( this )
|
||||
.closest( '.wcsg_add_recipient_fields_container' )
|
||||
.find( '.recipient_email' );
|
||||
recipientEmailElement.val( '' );
|
||||
hideValidationErrorForEmailField( recipientEmailElement );
|
||||
setShippingAddressNoticeVisibility( true );
|
||||
|
||||
if ( $( 'form.checkout' ).length !== 0 ) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-i18n', 'wp-primitives'), 'version' => '8bad0ce18409676b15d1');
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
<?php return array('dependencies' => array('react', 'react-dom', 'wp-api-fetch', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives'), 'version' => 'b52cf873a54e347c8f9e');
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
<?php return array('dependencies' => array('react', 'wc-blocks-checkout', 'wc-blocks-data-store', 'wc-price-format', 'wc-settings', 'wp-data', 'wp-element', 'wp-i18n', 'wp-plugins'), 'version' => '841d6ab2fe0080f15a21');
|
||||
<?php return array('dependencies' => array('react', 'wc-blocks-checkout', 'wc-blocks-data-store', 'wc-price-format', 'wc-settings', 'wp-data', 'wp-element', 'wp-i18n', 'wp-plugins'), 'version' => '32bfa486d902830f29c7');
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
<?php return array('dependencies' => array('react', 'react-dom', 'wc-blocks-checkout', 'wp-components', 'wp-data', 'wp-i18n', 'wp-plugins', 'wp-url'), 'version' => 'e7869a0a766c0f65ff8a');
|
||||
<?php return array('dependencies' => array('react', 'react-dom', 'wc-blocks-checkout', 'wp-components', 'wp-data', 'wp-i18n', 'wp-plugins', 'wp-url'), 'version' => 'cf4dd71c0c7418b591fd');
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,11 +1,17 @@
|
|||
*** WooCommerce Subscriptions Changelog ***
|
||||
|
||||
2026-01-06 - version 8.3.0
|
||||
* Add: Support for ajax add-to-cart flows from the single product page, when upgrading or downgrading subscriptions.
|
||||
* Add: Enhanced functionality from the WooCommerce Subscription Downloads plugin has now been integrated directly into WooCommerce Subscriptions.
|
||||
* Fix: Ensured the subject line for the "Customer Renewal Invoice" email correctly updates.
|
||||
|
||||
2025-12-22 - version 8.2.1
|
||||
* Fix: Subscription pricing strings now correctly display singular and plural billing intervals like "$10 / month" and "$10 every 3 months" in cart and checkout totals.
|
||||
* Fix: Display all recurring coupon names in the totals section of classic cart and checkout when multiple recurring coupons are applied.
|
||||
* Fix: Payment method selector options on Subscriptions list page are now properly displaying the payment method name for gateways providing multiple payment methods.
|
||||
* Fix: PayPal Standard plugin IPN renewal orders not being created for failed payment retries.
|
||||
* Fix: Prevent fatal errors on shop pages when special characters like % are used in a product name.
|
||||
* Fix: Prevent an email validation error from briefly and inadvertently flashing on the screen when the "this is a gift" checkbox is used.
|
||||
|
||||
2025-12-10 - version 8.2.0
|
||||
* Fix: Various accessibility issues.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
/**
|
||||
* WCS_Admin_Assets Class
|
||||
*
|
||||
* Handles admin assets (scripts and styles) for WooCommerce Subscriptions.
|
||||
*
|
||||
* @package WooCommerce Subscriptions/Admin
|
||||
*/
|
||||
class WCS_Admin_Assets {
|
||||
|
||||
/**
|
||||
* Initialize the tour handler
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue required scripts and styles
|
||||
*/
|
||||
public static function enqueue_scripts() {
|
||||
$script_asset_path = \WC_Subscriptions_Plugin::instance()->get_plugin_directory( 'build/admin.asset.php' );
|
||||
$script_asset = file_exists( $script_asset_path )
|
||||
? require $script_asset_path
|
||||
: array(
|
||||
'dependencies' => array(
|
||||
'react',
|
||||
'wc-blocks-checkout',
|
||||
'wc-price-format',
|
||||
'wc-settings',
|
||||
'wp-element',
|
||||
'wp-i18n',
|
||||
'wp-plugins',
|
||||
),
|
||||
'version' => WC_Subscriptions::$version,
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wcs-admin',
|
||||
plugins_url( '/build/admin.js', WC_Subscriptions::$plugin_file ),
|
||||
$script_asset['dependencies'],
|
||||
$script_asset['version'],
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'wcs-admin',
|
||||
plugins_url( '/build/style-admin.css', WC_Subscriptions::$plugin_file ),
|
||||
array( 'wp-components' ),
|
||||
$script_asset['version']
|
||||
);
|
||||
|
||||
wp_set_script_translations(
|
||||
'wcs-admin',
|
||||
'woocommerce-subscriptions',
|
||||
plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'languages'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ class WC_REST_Subscriptions_Settings_Option_Controller extends WP_REST_Controlle
|
|||
*/
|
||||
private const ALLOWED_OPTIONS = [
|
||||
'woocommerce_subscriptions_gifting_is_welcome_announcement_dismissed',
|
||||
'woocommerce_subscriptions_downloads_is_welcome_announcement_dismissed',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class WC_Subscriptions_Plugin extends WC_Subscriptions_Core_Plugin {
|
|||
WCS_Call_To_Action_Button_Text_Manager::init();
|
||||
WCS_Subscriber_Role_Manager::init();
|
||||
WCS_Upgrade_Notice_Manager::init();
|
||||
WCS_Admin_Assets::init();
|
||||
|
||||
$tracks_events = new WC_Tracks_Events();
|
||||
$tracks_events->setup();
|
||||
|
|
@ -300,10 +301,16 @@ class WC_Subscriptions_Plugin extends WC_Subscriptions_Core_Plugin {
|
|||
*/
|
||||
public function init_downloads() {
|
||||
if (
|
||||
! defined( 'WCS_ALLOW_SUBSCRIPTION_DOWNLOADS' )
|
||||
|| $this->is_plugin_being_activated( 'woocommerce-subscription-downloads' )
|
||||
$this->is_plugin_being_activated( 'woocommerce-subscription-downloads' )
|
||||
|| class_exists( WC_Subscription_Downloads::class, false )
|
||||
) {
|
||||
if ( class_exists( WC_Subscription_Downloads::class, false ) ) {
|
||||
// Will show the welcome announcement if the standalone plugin is active and the welcome announcement has not been dismissed.
|
||||
if ( ! WC_Subscription_Downloads_Admin_Welcome_Announcement::is_welcome_announcement_dismissed() ) {
|
||||
WC_Subscription_Downloads_Admin_Welcome_Announcement::init();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ class WCS_Autoloader extends WCS_Core_Autoloader {
|
|||
'wcs_webhooks' => true,
|
||||
'wcs_auth' => true,
|
||||
'wcs_upgrade_notice_manager' => true,
|
||||
'wcs_admin_assets' => true,
|
||||
'wc_subscriptions_cli' => true,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,43 @@ class WCS_Limiter {
|
|||
do_action( 'woocommerce_subscriptions_product_options_advanced' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the session contains a renewal for a given product.
|
||||
* Used for the pay for order flow.
|
||||
*
|
||||
* @param WC_Product $product The product to check.
|
||||
* @return bool
|
||||
*/
|
||||
private static function session_contains_renewal( $product ) {
|
||||
if ( ! empty( WC()->session->cart ) ) {
|
||||
foreach ( WC()->session->cart as $cart_item_key => $cart_item ) {
|
||||
if ( (int) $product->get_id() === (int) $cart_item['product_id'] && isset( $cart_item['subscription_renewal'] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the session contains a resubscribe for a given product.
|
||||
* Used for the pay for order flow with limited subscriptions products.
|
||||
*
|
||||
* @param WC_Product $product The product to check.
|
||||
* @return bool
|
||||
*/
|
||||
private static function session_contains_resubscribe( $product ) {
|
||||
if ( ! empty( WC()->session->cart ) ) {
|
||||
foreach ( WC()->session->cart as $cart_item_key => $cart_item ) {
|
||||
if ( (int) $product->get_id() === (int) $cart_item['product_id'] && isset( $cart_item['subscription_resubscribe'] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical is_purchasable method to be called by product classes.
|
||||
*
|
||||
|
|
@ -68,73 +105,62 @@ class WCS_Limiter {
|
|||
*/
|
||||
public static function is_purchasable( $purchasable, $product ) {
|
||||
|
||||
// Prevents making a non purchasable product purchasable again.
|
||||
// This can happen if the product is disabled and limited and the customer is trying to renew the subscription for example.
|
||||
if ( ! $purchasable ) {
|
||||
return $purchasable;
|
||||
// Check if product is private (for variations, also check parent product)
|
||||
$is_private_product = 'private' === $product->get_status();
|
||||
if ( $product->get_parent_id() > 0 ) {
|
||||
$parent_product = wc_get_product( $product->get_parent_id() );
|
||||
if ( $parent_product ) {
|
||||
$is_private_product = 'private' === $parent_product->get_status();
|
||||
}
|
||||
}
|
||||
|
||||
switch ( $product->get_type() ) {
|
||||
case 'subscription':
|
||||
case 'variable-subscription':
|
||||
// Checks if the product is limited.
|
||||
if ( false === self::is_product_limited( $purchasable, $product ) ) {
|
||||
// Product is limited, so it is not purchasable.
|
||||
// Checks limits for variable subscription products.
|
||||
if ( $product->get_type() === 'subscription_variation' && isset( $parent_product ) ) {
|
||||
|
||||
if ( $is_private_product ) {
|
||||
$purchasable = false;
|
||||
|
||||
// Forces product to be available when processing a renewal order. Allowing people to renew private products.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce validation is not required for this context.
|
||||
if ( self::is_paying_for_failed_renewal_order( $parent_product ) || isset( $_GET['subscription_renewal'] ) || wcs_cart_contains_renewal() || self::session_contains_renewal( $parent_product ) ) {
|
||||
$purchasable = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'no' !== wcs_get_product_limitation( $parent_product ) && ( ! empty( WC()->cart->cart_contents ) || self::session_contains_resubscribe( $parent_product ) ) && ! wcs_is_order_received_page() && ! wcs_is_paypal_api_page() ) {
|
||||
// When mixed checkout is disabled, the variation is replaceable.
|
||||
if ( 'yes' === get_option( WC_Subscriptions_Admin::$option_prefix . '_multiple_purchase', 'no' ) ) {
|
||||
foreach ( WC()->cart->cart_contents as $cart_item ) {
|
||||
// If the variable product is limited, it can't be purchased if it is the same variation
|
||||
if ( $product->get_parent_id() === $cart_item['data']->get_parent_id() && $product->get_id() !== $cart_item['data']->get_id() ) {
|
||||
$purchasable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // Checks limits for simple subscription products.
|
||||
if ( $is_private_product ) {
|
||||
$purchasable = false;
|
||||
|
||||
// Forces product to be available when processing a renewal order. Allowing people to renew private products.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce validation is not required for this context.
|
||||
if ( self::is_paying_for_failed_renewal_order( $product ) || isset( $_GET['subscription_renewal'] ) || wcs_cart_contains_renewal() || self::session_contains_renewal( $product ) ) {
|
||||
$purchasable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This actually means the product is limited when returning false.
|
||||
if ( false === self::is_product_limited( $purchasable, $product ) ) {
|
||||
$resubscribe_cart_item = wcs_cart_contains_resubscribe();
|
||||
// Allows the product to be resubscribed but not purchased again.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce validation is not required for this context.
|
||||
if ( empty( $_GET['resubscribe'] ) && false === $resubscribe_cart_item && false === self::session_contains_resubscribe( $product ) ) {
|
||||
$purchasable = false;
|
||||
|
||||
// Unless it's resubscribing, renewing or restoring cart from session.
|
||||
$resubscribe_cart_item = wcs_cart_contains_resubscribe();
|
||||
|
||||
// Resubscribe logic
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( ! empty( $_GET['resubscribe'] ) || false !== $resubscribe_cart_item ) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$subscription_id = ( isset( $_GET['resubscribe'] ) ) ? absint( $_GET['resubscribe'] ) : $resubscribe_cart_item['subscription_resubscribe']['subscription_id'];
|
||||
$subscription = wcs_get_subscription( $subscription_id );
|
||||
|
||||
if ( $subscription && $subscription->has_product( $product->get_id() ) && wcs_can_user_resubscribe_to( $subscription ) ) {
|
||||
$purchasable = true;
|
||||
}
|
||||
|
||||
// Renewal logic
|
||||
} elseif (
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
isset( $_GET['subscription_renewal'] ) ||
|
||||
wcs_cart_contains_renewal()
|
||||
) {
|
||||
$purchasable = true;
|
||||
|
||||
// Restoring cart from session, so need to check the cart in the session (wcs_cart_contains_renewal() only checks the cart).
|
||||
} elseif ( ! empty( WC()->session->cart ) ) {
|
||||
foreach ( WC()->session->cart as $cart_item_key => $cart_item ) {
|
||||
if ( (int) $product->get_id() === (int) $cart_item['product_id'] && ( isset( $cart_item['subscription_renewal'] ) || isset( $cart_item['subscription_resubscribe'] ) ) ) {
|
||||
$purchasable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'subscription_variation':
|
||||
$variable_product = wc_get_product( $product->get_parent_id() );
|
||||
|
||||
if ( 'no' != wcs_get_product_limitation( $variable_product ) && ! empty( WC()->cart->cart_contents ) && ! wcs_is_order_received_page() && ! wcs_is_paypal_api_page() ) {
|
||||
|
||||
// When mixed checkout is disabled, the variation is replaceable
|
||||
if ( 'no' === get_option( WC_Subscriptions_Admin::$option_prefix . '_multiple_purchase', 'no' ) ) {
|
||||
$purchasable = true;
|
||||
} else { // When mixed checkout is enabled
|
||||
foreach ( WC()->cart->cart_contents as $cart_item ) {
|
||||
// If the variable product is limited, it can't be purchased if it is the same variation
|
||||
if ( $product->get_parent_id() === $cart_item['data']->get_parent_id() && $product->get_id() !== $cart_item['data']->get_id() ) {
|
||||
$purchasable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $purchasable;
|
||||
}
|
||||
|
||||
|
|
@ -302,12 +328,73 @@ class WCS_Limiter {
|
|||
return self::$order_awaiting_payment_for_product[ $product_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're currently paying for a failed renewal order containing the product.
|
||||
*
|
||||
* @since 8.3.0 - Migrated from WooCommerce Subscriptions v2.1.0
|
||||
* @param WC_Product $product The product to check.
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_paying_for_failed_renewal_order( $product ) {
|
||||
global $wp;
|
||||
|
||||
// Check if we're on the pay for order page
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( ! isset( $_GET['pay_for_order'] ) || ! isset( $_GET['key'] ) || ! isset( $wp->query_vars['order-pay'] ) ) {
|
||||
// Also check if cart contains a failed renewal order payment
|
||||
$failed_renewal_cart_item = wcs_cart_contains_failed_renewal_order_payment();
|
||||
if ( false !== $failed_renewal_cart_item ) {
|
||||
$cart_item_product_id = isset( $failed_renewal_cart_item['variation_id'] ) && $failed_renewal_cart_item['variation_id'] > 0
|
||||
? $failed_renewal_cart_item['variation_id']
|
||||
: $failed_renewal_cart_item['product_id'];
|
||||
// Check both the product ID and parent product ID (for variations)
|
||||
if ( (int) $product->get_id() === (int) $cart_item_product_id || (int) $product->get_id() === (int) $failed_renewal_cart_item['product_id'] ) {
|
||||
return true;
|
||||
}
|
||||
// Also check if product is a variation and matches the parent
|
||||
if ( $product->get_parent_id() > 0 && (int) $product->get_parent_id() === (int) $failed_renewal_cart_item['product_id'] ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$order_key = isset( $_GET['key'] ) ? wc_clean( wp_unslash( $_GET['key'] ) ) : '';
|
||||
$order_id = isset( $wp->query_vars['order-pay'] ) ? $wp->query_vars['order-pay'] : 0;
|
||||
$order = wc_get_order( absint( $order_id ) );
|
||||
|
||||
if ( ! $order || $order->get_order_key() !== $order_key ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if order is a failed renewal order
|
||||
if ( ! $order->has_status( 'failed' ) && ! wcs_order_contains_renewal( $order ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the order contains the product
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
$item_product_id = isset( $item['variation_id'] ) && $item['variation_id'] > 0 ? $item['variation_id'] : $item['product_id'];
|
||||
// Check both the product ID and parent product ID (for variations)
|
||||
if ( (int) $product->get_id() === (int) $item_product_id || (int) $product->get_id() === (int) $item['product_id'] ) {
|
||||
return true;
|
||||
}
|
||||
// Also check if product is a variation and matches the parent
|
||||
if ( $product->get_parent_id() > 0 && (int) $product->get_parent_id() === (int) $item['product_id'] ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the order statuses that enable the order again button and functionality.
|
||||
*
|
||||
* This function will return no statuses if the order contains non purchasable or limited products.
|
||||
*
|
||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v3.0.2
|
||||
* @since 8.3.0 - Migrated from WooCommerce Subscriptions v3.0.2
|
||||
*
|
||||
* @param array $statuses The order statuses that enable the order again button.
|
||||
* @return array $statuses An empty array if the order contains limited products, otherwise the default statuses are returned.
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ class WCS_Email_Customer_Renewal_Invoice extends WC_Email_Customer_Invoice {
|
|||
* @return string
|
||||
*/
|
||||
function get_subject() {
|
||||
return apply_filters( 'woocommerce_subscriptions_email_subject_new_renewal_order', parent::get_subject(), $this->object );
|
||||
return apply_filters( 'woocommerce_subscriptions_email_subject_new_renewal_order', $this->format_string( $this->get_option( 'subject', $this->get_default_subject() ) ), $this->object );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -134,7 +134,7 @@ class WCS_Email_Customer_Renewal_Invoice extends WC_Email_Customer_Invoice {
|
|||
* @return string
|
||||
*/
|
||||
function get_heading() {
|
||||
return apply_filters( 'woocommerce_email_heading_customer_renewal_order', parent::get_heading(), $this->object );
|
||||
return apply_filters( 'woocommerce_email_heading_customer_renewal_order', $this->format_string( $this->get_option( 'heading', $this->get_default_heading() ) ), $this->object );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -227,9 +227,8 @@ class WC_Subscriptions_Upgrader {
|
|||
WCS_Plugin_Upgrade_7_8_0::check_gifting_plugin_is_enabled();
|
||||
}
|
||||
|
||||
if ( false && version_compare( self::$stored_plugin_version, '8.2.0', '<' ) ) {
|
||||
// TODO: remove false from the above conditional, once we are ready to make subscription downloads functionality available.
|
||||
WCS_Plugin_Upgrade_8_1_0::check_downloads_plugin_is_enabled();
|
||||
if ( version_compare( self::$stored_plugin_version, '8.3.0', '<' ) ) {
|
||||
WCS_Plugin_Upgrade_8_3_0::check_downloads_plugin_is_enabled();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class WCS_Plugin_Upgrade_8_1_0 {
|
||||
class WCS_Plugin_Upgrade_8_3_0 {
|
||||
|
||||
/**
|
||||
* Check if the Gifting plugin is enabled and update the settings.
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
/**
|
||||
* Downloads Admin Announcement Handler Class
|
||||
*
|
||||
* @package WooCommerce Subscriptions
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class WC_Subscription_Downloads_Admin_Welcome_Announcement {
|
||||
|
||||
/**
|
||||
* Initialize the tour handler
|
||||
*/
|
||||
public static function init() {
|
||||
// Register scripts and styles
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
|
||||
|
||||
// Add the tour HTML to the admin footer
|
||||
add_action( 'admin_footer', array( __CLASS__, 'output_tour' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue required scripts and styles
|
||||
*/
|
||||
public static function enqueue_scripts() {
|
||||
if ( ! self::is_woocommerce_admin_or_subscriptions_listing() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$screen = get_current_screen();
|
||||
|
||||
wp_localize_script(
|
||||
'wcs-admin',
|
||||
'wcsDownloadsSettings',
|
||||
array(
|
||||
'imagesPath' => plugins_url( '/assets/images', WC_Subscriptions::$plugin_file ),
|
||||
'pluginsUrl' => admin_url( 'plugins.php' ),
|
||||
'subscriptionsUrl' => WC_Subscriptions_Admin::settings_tab_url() . '#woocommerce_subscriptions_downloads_enable',
|
||||
'isStandaloneDownloadsEnabled' => is_plugin_active( 'woocommerce-subscription-downloads/woocommerce-subscription-downloads.php' ),
|
||||
'isSubscriptionsListing' => 'woocommerce_page_wc-orders--shop_subscription' === $screen->id,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the tour HTML in the admin footer
|
||||
*/
|
||||
public static function output_tour() {
|
||||
if ( ! self::is_woocommerce_admin_or_subscriptions_listing() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a div for the tour to be rendered into
|
||||
echo '<div id="wcs-downloads-welcome-announcement-root" class="woocommerce-tour-kit"></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the welcome tour has been dismissed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_welcome_announcement_dismissed() {
|
||||
return '1' === get_option(
|
||||
'woocommerce_subscriptions_downloads_is_welcome_announcement_dismissed',
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current screen is WooCommerce Admin or subscriptions listing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_woocommerce_admin_or_subscriptions_listing() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( ! $screen ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$action_param = isset( $_GET['action'] ) ? wc_clean( wp_unslash( $_GET['action'] ) ) : '';
|
||||
|
||||
$is_woocommerce_admin = 'woocommerce_page_wc-admin' === $screen->id;
|
||||
$is_subscriptions_listing = 'woocommerce_page_wc-orders--shop_subscription' === $screen->id && empty( $action_param );
|
||||
|
||||
return $is_woocommerce_admin || $is_subscriptions_listing;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ class WC_Subscription_Downloads_Ajax {
|
|||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_wc_subscription_downloads_search', array( $this, 'search_subscriptions' ) );
|
||||
add_action( 'wp_ajax_wc_subscription_linked_downloadable_products_search', array( $this, 'search_downloadable_products' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,4 +73,39 @@ class WC_Subscription_Downloads_Ajax {
|
|||
|
||||
wp_send_json( $found_subscriptions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for downloadable products that are simple or variants.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function search_downloadable_products(): void {
|
||||
$results = array();
|
||||
|
||||
// Prevent error noise from leaking.
|
||||
ob_start();
|
||||
|
||||
if ( isset( $_GET['term'] ) && check_ajax_referer( 'search-products', 'security' ) ) {
|
||||
$term = wc_clean( wp_unslash( $_GET['term'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $term ) ) {
|
||||
$products = wc_get_products(
|
||||
array(
|
||||
'downloadable' => true,
|
||||
'limit' => 100,
|
||||
's' => $term,
|
||||
'type' => array( 'simple', 'variation' ),
|
||||
'status' => 'any',
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $products as $product ) {
|
||||
$results[ $product->get_id() ] = sanitize_text_field( $product->get_formatted_name() );
|
||||
}
|
||||
}
|
||||
|
||||
ob_clean();
|
||||
wp_send_json( $results );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,52 +7,202 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
* WooCommerce Subscription Downloads Products.
|
||||
*
|
||||
* @package WC_Subscription_Downloads_Products
|
||||
* @category Products
|
||||
* @author WooThemes
|
||||
*/
|
||||
class WC_Subscription_Downloads_Products {
|
||||
public const EDITOR_UPDATE = 'wcsubs_subscription_download_relationships';
|
||||
public const RELATIONSHIP_DOWNLOAD_TO_SUB = 'download-to-sub';
|
||||
public const RELATIONSHIP_VAR_DOWNLOAD_TO_SUB = 'var-download-to-sub';
|
||||
public const RELATIONSHIP_SUB_TO_DOWNLOAD = 'sub-to-download';
|
||||
public const RELATIONSHIP_VAR_SUB_TO_DOWNLOAD = 'var-sub-to-download';
|
||||
|
||||
/**
|
||||
* Products actions.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'woocommerce_product_options_downloads', array( $this, 'simple_write_panel_options' ), 10 );
|
||||
add_action( 'woocommerce_product_options_downloads', array( $this, 'simple_write_panel_options' ) );
|
||||
add_action( 'woocommerce_variation_options_download', array( $this, 'variable_write_panel_options' ), 10, 3 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
|
||||
add_action( 'woocommerce_process_product_meta_simple', array( $this, 'save_simple_product_data' ), 10 );
|
||||
add_action( 'woocommerce_save_product_variation', array( $this, 'save_variation_product_data' ), 10, 2 );
|
||||
add_action( 'woocommerce_product_options_pricing', array( $this, 'subscription_product_editor_ui' ) );
|
||||
add_action( 'woocommerce_variable_subscription_pricing', array( $this, 'variable_subscription_product_editor_ui' ), 10, 3 );
|
||||
|
||||
add_action( 'save_post_product', array( $this, 'handle_product_save' ) );
|
||||
add_action( 'save_post_product_variation', array( $this, 'handle_product_variation_save' ) );
|
||||
add_action( 'woocommerce_save_product_variation', array( $this, 'handle_product_variation_save' ) );
|
||||
add_action( 'woocommerce_update_product', array( $this, 'handle_product_save' ) );
|
||||
add_action( 'woocommerce_update_product_variation', array( $this, 'handle_product_variation_save' ) );
|
||||
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
}
|
||||
|
||||
public function init() {
|
||||
if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
|
||||
add_action( 'woocommerce_duplicate_product', array( $this, 'save_subscriptions_when_duplicating_product' ), 10, 2 );
|
||||
} else {
|
||||
add_action( 'woocommerce_product_duplicate', array( $this, 'save_subscriptions_when_duplicating_product' ), 10, 2 );
|
||||
add_action( 'woocommerce_product_duplicate', array( $this, 'save_subscriptions_when_duplicating_product' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle product save - generic handler for all product updates.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle_product_save( $post_id ) {
|
||||
// Bail if this is an autosave or revision.
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( wp_is_post_revision( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product_is_downloadable = $product->is_downloadable();
|
||||
$product_is_subscription = $product->is_type( array( 'subscription', 'variable-subscription' ) );
|
||||
|
||||
// We do not allow downloadable subscription products to be linked with other subscription products; this is
|
||||
// principally to avoid confusion (though it would be technically feasible).
|
||||
if ( $product_is_subscription && ! $product_is_downloadable ) {
|
||||
$this->handle_subscription_product_save( $post_id );
|
||||
} elseif ( ! $product_is_subscription && $product_is_downloadable ) {
|
||||
$this->handle_downloadable_product_save( $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Product screen scripts.
|
||||
* Handle product variation save - generic handler for all variation updates.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function scripts() {
|
||||
$screen = get_current_screen();
|
||||
public function handle_product_variation_save( $post_id ) {
|
||||
// Bail if this is an autosave or revision.
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'product' == $screen->id && version_compare( WC_VERSION, '2.3.0', '<' ) ) {
|
||||
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
|
||||
if ( wp_is_post_revision( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script( 'wc_subscription_downloads_writepanel', plugins_url( 'assets/js/admin/writepanel' . $suffix . '.js', plugin_dir_path( __FILE__ ) ), array( 'ajax-chosen', 'chosen' ), WC_Subscriptions::$version, true );
|
||||
$variation = wc_get_product( $post_id );
|
||||
if ( ! $variation || ! $variation->is_type( 'variation' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'wc_subscription_downloads_writepanel',
|
||||
'wc_subscription_downloads_product',
|
||||
array(
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'security' => wp_create_nonce( 'search-products' ),
|
||||
)
|
||||
);
|
||||
// Handle downloadable variations (they link TO subscriptions).
|
||||
if ( $variation->is_downloadable() ) {
|
||||
$this->handle_downloadable_product_save( $post_id );
|
||||
}
|
||||
|
||||
// Handle subscription variations (they link TO downloadable products).
|
||||
$parent = wc_get_product( $variation->get_parent_id() );
|
||||
|
||||
if ( $parent && $parent->is_type( 'variable-subscription' ) ) {
|
||||
$this->handle_subscription_product_save( $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle save for downloadable products (simple or variation).
|
||||
* These products link TO subscription products.
|
||||
*
|
||||
* @param int $product_id Product or variation ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handle_downloadable_product_save( $product_id ) {
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
if ( ! $product ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
if (
|
||||
isset( $_POST[ self::RELATIONSHIP_VAR_DOWNLOAD_TO_SUB . $product_id ] )
|
||||
&& wp_verify_nonce( $_POST[ self::RELATIONSHIP_VAR_DOWNLOAD_TO_SUB . $product_id ], self::EDITOR_UPDATE )
|
||||
) {
|
||||
$subscription_ids = wc_clean( wp_unslash( $_POST['_variable_subscription_downloads_ids'][ $product_id ] ?? array() ) );
|
||||
$subscription_ids = array_filter( (array) $subscription_ids );
|
||||
$this->update_subscription_downloads( $product_id, $subscription_ids );
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $_POST[ self::RELATIONSHIP_DOWNLOAD_TO_SUB ] )
|
||||
&& wp_verify_nonce( $_POST[ self::RELATIONSHIP_DOWNLOAD_TO_SUB ], self::EDITOR_UPDATE )
|
||||
) {
|
||||
$subscription_ids = wc_clean( wp_unslash( $_POST['_subscription_downloads_ids'] ?? array() ) );
|
||||
$subscription_ids = array_filter( (array) $subscription_ids );
|
||||
$this->update_subscription_downloads( $product_id, $subscription_ids );
|
||||
}
|
||||
|
||||
// Observe and act on product status changes (regardless of whether they were made from within the product
|
||||
// editor, therefore we don't care about nonce checks here).
|
||||
$this->assess_downloadable_product_status( $product_id );
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle save for subscription products (simple subscription or variation).
|
||||
* These products link TO downloadable products.
|
||||
*
|
||||
* @param int $product_id Subscription product or variation ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handle_subscription_product_save( $product_id ) {
|
||||
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
if (
|
||||
isset( $_POST[ self::RELATIONSHIP_VAR_SUB_TO_DOWNLOAD . $product_id ] )
|
||||
&& wp_verify_nonce( $_POST[ self::RELATIONSHIP_VAR_SUB_TO_DOWNLOAD . $product_id ], self::EDITOR_UPDATE )
|
||||
) {
|
||||
$product_ids = wc_clean( wp_unslash( $_POST[ '_subscription_linked_downloadable_products_' . $product_id ] ?? array() ) );
|
||||
$product_ids = array_filter( (array) $product_ids );
|
||||
$this->update_subscription_products( $product_id, $product_ids );
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isset( $_POST[ self::RELATIONSHIP_SUB_TO_DOWNLOAD ] )
|
||||
&& wp_verify_nonce( $_POST[ self::RELATIONSHIP_SUB_TO_DOWNLOAD ], self::EDITOR_UPDATE )
|
||||
) {
|
||||
$product_ids = wc_clean( wp_unslash( (array) $_POST['_subscription_linked_downloadable_products'] ?? array() ) );
|
||||
$product_ids = array_filter( (array) $product_ids );
|
||||
$this->update_subscription_products( $product_id, $product_ids );
|
||||
}
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Assess downloadable product status and adjust permissions accordingly.
|
||||
* Called when no form data is available (e.g., status change, REST API update, file changes).
|
||||
*
|
||||
* @param int $product_id Product ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function assess_downloadable_product_status( $product_id ) {
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
if ( ! $product || ! $product->is_downloadable() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$status_object = get_post_status_object( $product->get_status() );
|
||||
$is_public = $status_object && $status_object->public;
|
||||
|
||||
// Always revoke existing permissions first to ensure clean state.
|
||||
// This handles file changes and status transitions.
|
||||
$this->revoke_permissions_for_product( $product_id );
|
||||
|
||||
// Grant fresh permissions only if product is public.
|
||||
if ( $is_public ) {
|
||||
$this->grant_permissions_for_product( $product_id );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,8 +212,8 @@ class WC_Subscription_Downloads_Products {
|
|||
public function simple_write_panel_options() {
|
||||
global $post;
|
||||
?>
|
||||
<p class="form-field _subscription_downloads_field">
|
||||
<label for="subscription-downloads-ids"><?php esc_html_e( 'Included subscription products', 'woocommerce-subscriptions' ); ?></label>
|
||||
<p class="form-field _subscription_downloads_field hide_if_subscription">
|
||||
<label for="subscription-downloads-ids"><?php esc_html_e( 'Linked subscription products', 'woocommerce-subscriptions' ); ?></label>
|
||||
|
||||
<select id="subscription-downloads-ids" multiple="multiple" data-action="wc_subscription_downloads_search" data-placeholder="<?php esc_attr_e( 'Select subscriptions', 'woocommerce-subscriptions' ); ?>" class="subscription-downloads-ids wc-product-search" name="_subscription_downloads_ids[]" style="width: 50%;">
|
||||
<?php
|
||||
|
|
@ -71,17 +221,18 @@ class WC_Subscription_Downloads_Products {
|
|||
|
||||
if ( $subscriptions_ids ) {
|
||||
foreach ( $subscriptions_ids as $subscription_id ) {
|
||||
$_subscription = wc_get_product( $subscription_id );
|
||||
$subscription = wc_get_product( $subscription_id );
|
||||
|
||||
if ( $_subscription ) {
|
||||
echo '<option value="' . esc_attr( $subscription_id ) . '" selected="selected">' . esc_html( wp_strip_all_tags( $_subscription->get_formatted_name() ) ) . '</option>';
|
||||
if ( $subscription ) {
|
||||
echo '<option value="' . esc_attr( $subscription_id ) . '" selected="selected">' . esc_html( wp_strip_all_tags( $subscription->get_formatted_name() ) ) . '</option>';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
|
||||
<?php echo wc_help_tip( wc_sanitize_tooltip( __( 'Select subscription products that will include this downloadable product.', 'woocommerce-subscriptions' ) ) ); ?>
|
||||
<span class="description"><?php esc_html_e( 'Select subscription products that will include this downloadable product.', 'woocommerce-subscriptions' ); ?></span>
|
||||
<?php wp_nonce_field( self::EDITOR_UPDATE, self::RELATIONSHIP_DOWNLOAD_TO_SUB, false ); ?>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
|
|
@ -94,30 +245,145 @@ class WC_Subscription_Downloads_Products {
|
|||
?>
|
||||
<tr class="show_if_variation_downloadable">
|
||||
<td colspan="2">
|
||||
<p class="form-field form-row form-row-full">
|
||||
<label><?php esc_html_e( 'Included subscription products', 'woocommerce-subscriptions' ); ?>:</label>
|
||||
<p class="form-field _subscription_downloads_field form-row form-row-full hide_if_variable-subscription">
|
||||
<label><?php esc_html_e( 'Linked subscription products', 'woocommerce-subscriptions' ); ?>:</label>
|
||||
<?php echo wc_help_tip( wc_sanitize_tooltip( __( 'Select subscription products that will include this downloadable product.', 'woocommerce-subscriptions' ) ) ); ?>
|
||||
|
||||
<select multiple="multiple" data-placeholder="<?php esc_html_e( 'Select subscriptions', 'woocommerce-subscriptions' ); ?>" class="subscription-downloads-ids wc-product-search" name="_variable_subscription_downloads_ids[<?php echo esc_attr( $loop ); ?>][]" style="width: 100%">
|
||||
<select multiple="multiple" data-placeholder="<?php esc_html_e( 'Select subscriptions', 'woocommerce-subscriptions' ); ?>" class="subscription-downloads-ids wc-product-search" name="_variable_subscription_downloads_ids[<?php echo esc_attr( $variation->ID ); ?>][]" style="width: 100%">
|
||||
<?php
|
||||
$subscriptions_ids = WC_Subscription_Downloads::get_subscriptions( $variation->ID );
|
||||
if ( $subscriptions_ids ) {
|
||||
foreach ( $subscriptions_ids as $subscription_id ) {
|
||||
$_subscription = wc_get_product( $subscription_id );
|
||||
$subscription = wc_get_product( $subscription_id );
|
||||
|
||||
if ( $_subscription ) {
|
||||
echo '<option value="' . esc_attr( $subscription_id ) . '" selected="selected">' . esc_html( wp_strip_all_tags( $_subscription->get_formatted_name() ) ) . '</option>';
|
||||
if ( $subscription ) {
|
||||
echo '<option value="' . esc_attr( $subscription_id ) . '" selected="selected">' . esc_html( wp_strip_all_tags( $subscription->get_formatted_name() ) ) . '</option>';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php wp_nonce_field( self::EDITOR_UPDATE, self::RELATIONSHIP_VAR_DOWNLOAD_TO_SUB . $variation->ID, false ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field with which to link the subscription product (the product being edited) with zero-or-many
|
||||
* downloadable products.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function subscription_product_editor_ui(): void {
|
||||
global $post;
|
||||
|
||||
if ( ! $post instanceof WP_Post ) {
|
||||
wc_get_logger()->warning(
|
||||
'Unable to add the downloadable products selector to the product editor (global post object is unavailable).',
|
||||
array( 'backtrace' => true )
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$description = esc_html__( 'Select simple and variable downloadable products that will be included with this subscription product.', 'woocommerce-subscriptions' );
|
||||
$label = esc_html__( 'Linked downloadable products', 'woocommerce-subscriptions' );
|
||||
$linked_products = '';
|
||||
$nonce_field = wp_nonce_field( self::EDITOR_UPDATE, self::RELATIONSHIP_SUB_TO_DOWNLOAD, false, false );
|
||||
$placeholder = esc_attr__( 'Select products', 'woocommerce-subscriptions' );
|
||||
|
||||
foreach ( WC_Subscription_Downloads::get_downloadable_products( $post->ID ) as $product_id ) {
|
||||
$product_id = absint( $product_id );
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
if ( $product ) {
|
||||
$product_name = esc_html( wp_strip_all_tags( $product->get_formatted_name() ) );
|
||||
$linked_products .= "<option value='$product_id' selected='selected'>$product_name</option>";
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- variables are escaped above.
|
||||
echo "
|
||||
<div class='options_group subscription_linked_downloadable_products_section'>
|
||||
<p class='form-field subscription_linked_downloadable_products'>
|
||||
<label for='subscription-linked-downloadable-products'>$label</label>
|
||||
<select
|
||||
class='wc-product-search subscription-downloads-ids'
|
||||
data-action='wc_subscription_linked_downloadable_products_search'
|
||||
data-placeholder='$placeholder'
|
||||
id='subscription-linked-downloadable-products'
|
||||
multiple='multiple'
|
||||
name='_subscription_linked_downloadable_products[]'
|
||||
style='width: 50%;'
|
||||
>
|
||||
$linked_products
|
||||
</select>
|
||||
<span class='description'>$description</span>
|
||||
$nonce_field
|
||||
</p>
|
||||
</div>
|
||||
";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $loop
|
||||
* @param array $variation_data
|
||||
* @param WP_Post $variation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function variable_subscription_product_editor_ui( $loop, $variation_data, $variation ): void {
|
||||
if ( ! $variation instanceof WP_Post ) {
|
||||
wc_get_logger()->warning(
|
||||
'Unable to add the downloadable products selector to the variation section of the product editor (we do not have a valid post object).',
|
||||
array( 'backtrace' => true )
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$variation_id = (int) $variation->ID;
|
||||
$label = esc_html__( 'Linked downloadable products', 'woocommerce-subscriptions' );
|
||||
$linked_products = '';
|
||||
$nonce_field = wp_nonce_field( self::EDITOR_UPDATE, self::RELATIONSHIP_VAR_SUB_TO_DOWNLOAD . $variation_id, false, false );
|
||||
$placeholder = esc_attr__( 'Select products', 'woocommerce-subscriptions' );
|
||||
$tooltip = wc_help_tip( wc_sanitize_tooltip( __( 'Select simple and variable downloadable products that will be included with this subscription variation.', 'woocommerce-subscriptions' ) ) );
|
||||
|
||||
foreach ( WC_Subscription_Downloads::get_downloadable_products( $variation->ID ) as $product_id ) {
|
||||
$product_id = absint( $product_id );
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
if ( $product ) {
|
||||
$product_name = esc_html( wp_strip_all_tags( $product->get_formatted_name() ) );
|
||||
$linked_products .= "<option value='$product_id' selected='selected'>$product_name</option>";
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- variables are escaped above.
|
||||
echo "
|
||||
<div class='variable_subscription_linked_downloadable_products show_if_variable-subscription' style='display: none'>
|
||||
<p class='form-row form-field subscription_linked_downloadable_products'>
|
||||
<label for='subscription-linked-downloadable-products'>$label</label>
|
||||
$tooltip
|
||||
<select
|
||||
class='wc-product-search subscription-downloads-ids'
|
||||
data-action='wc_subscription_linked_downloadable_products_search'
|
||||
data-placeholder='$placeholder'
|
||||
id='subscription-linked-downloadable-products'
|
||||
multiple='multiple'
|
||||
name='_subscription_linked_downloadable_products_{$variation_id}[]'
|
||||
style='width: 100%;'
|
||||
>
|
||||
$linked_products
|
||||
</select>
|
||||
$nonce_field
|
||||
</p>
|
||||
</div>
|
||||
";
|
||||
}
|
||||
|
||||
/**
|
||||
* Search orders from subscription product ID.
|
||||
*
|
||||
|
|
@ -164,9 +430,7 @@ class WC_Subscription_Downloads_Products {
|
|||
$orders[] = $order->id;
|
||||
}
|
||||
|
||||
$orders = apply_filters( 'woocommerce_subscription_downloads_get_orders', $orders, $subscription_product_id );
|
||||
|
||||
return $orders;
|
||||
return apply_filters( 'woocommerce_subscription_downloads_get_orders', $orders, $subscription_product_id );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -181,37 +445,93 @@ class WC_Subscription_Downloads_Products {
|
|||
protected function revoke_access_to_download( $download_id, $product_id, $order_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query( $wpdb->prepare( "
|
||||
DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions
|
||||
WHERE order_id = %d AND product_id = %d AND download_id = %s;
|
||||
", $order_id, $product_id, $download_id ) );
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions
|
||||
WHERE order_id = %d AND product_id = %d AND download_id = %s;
|
||||
",
|
||||
$order_id,
|
||||
$product_id,
|
||||
$download_id
|
||||
)
|
||||
);
|
||||
|
||||
do_action( 'woocommerce_ajax_revoke_access_to_product_download', $download_id, $product_id, $order_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update subscription downloads table and orders.
|
||||
* Update subscription downloads table and orders according in respect to the described relationship between a
|
||||
* regular product and zero-to-many regular subscription products.
|
||||
*
|
||||
* @param int $product_id
|
||||
* @param array $subscriptions
|
||||
* @param int $product_id The downloadable product ID.
|
||||
* @param array $subscriptions Subscription product IDs.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function update_subscription_downloads( $product_id, $subscriptions ) {
|
||||
$current = array_map( 'intval', WC_Subscription_Downloads::get_subscriptions( $product_id ) );
|
||||
$subscriptions = array_map( 'intval', (array) $subscriptions );
|
||||
|
||||
sort( $current );
|
||||
sort( $subscriptions );
|
||||
|
||||
$to_delete = array_diff( $current, $subscriptions );
|
||||
$to_create = array_diff( $subscriptions, $current );
|
||||
|
||||
$this->delete_relationships( $to_delete, array( $product_id ) );
|
||||
$this->create_relationships( $to_create, array( $product_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update subscription downloads table and orders according in respect to the described relationship between a
|
||||
* subscription product and zero-to-many regular products.
|
||||
*
|
||||
* @param int $subscription_product_id Subscription product ID.
|
||||
* @param int[] $new_ids IDs for downloadable products that should be associated with the subscription product.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function update_subscription_products( int $subscription_product_id, array $new_ids ): void {
|
||||
$existing_ids = array_map( 'intval', WC_Subscription_Downloads::get_downloadable_products( $subscription_product_id ) );
|
||||
$new_ids = array_map( 'intval', $new_ids );
|
||||
|
||||
sort( $existing_ids );
|
||||
sort( $new_ids );
|
||||
|
||||
$to_delete = array_diff( $existing_ids, $new_ids );
|
||||
$to_create = array_diff( $new_ids, $existing_ids );
|
||||
|
||||
$this->delete_relationships( array( $subscription_product_id ), $to_delete );
|
||||
$this->create_relationships( array( $subscription_product_id ), $to_create );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes relationships that exist between any of the supplied subscription IDs and any of the supplied product
|
||||
* IDs.
|
||||
*
|
||||
* The most common use case will be to supply a single subscription ID and one-or-more product IDs, or else the
|
||||
* inverse.
|
||||
*
|
||||
* @param int[] $subscription_ids
|
||||
* @param int[] $product_ids
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function delete_relationships( array $subscription_ids, array $product_ids ): void {
|
||||
global $wpdb;
|
||||
|
||||
$subscriptions = (array) $subscriptions;
|
||||
$current = WC_Subscription_Downloads::get_subscriptions( $product_id );
|
||||
foreach ( $product_ids as $product_id ) {
|
||||
$product_id = (int) $product_id;
|
||||
|
||||
foreach ( $subscription_ids as $subscription_id ) {
|
||||
$subscription_id = (int) $subscription_id;
|
||||
|
||||
// Delete items.
|
||||
$delete_ids = array_diff( $current, $subscriptions );
|
||||
if ( $delete_ids ) {
|
||||
foreach ( $delete_ids as $delete ) {
|
||||
$wpdb->delete(
|
||||
$wpdb->prefix . 'woocommerce_subscription_downloads',
|
||||
array(
|
||||
'product_id' => $product_id,
|
||||
'subscription_id' => $delete,
|
||||
'subscription_id' => $subscription_id,
|
||||
),
|
||||
array(
|
||||
'%d',
|
||||
|
|
@ -219,10 +539,10 @@ class WC_Subscription_Downloads_Products {
|
|||
)
|
||||
);
|
||||
|
||||
$_orders = $this->get_orders( $delete );
|
||||
foreach ( $_orders as $order_id ) {
|
||||
$_product = wc_get_product( $product_id );
|
||||
$downloads = $_product->get_downloads();
|
||||
$orders = $this->get_orders( $subscription_id );
|
||||
foreach ( $orders as $order_id ) {
|
||||
$product = wc_get_product( $product_id );
|
||||
$downloads = $product->get_downloads();
|
||||
|
||||
// Adds the downloadable files to the order/subscription.
|
||||
foreach ( array_keys( $downloads ) as $download_id ) {
|
||||
|
|
@ -231,16 +551,35 @@ class WC_Subscription_Downloads_Products {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add items.
|
||||
$add_ids = array_diff( $subscriptions, $current );
|
||||
if ( $add_ids ) {
|
||||
foreach ( $add_ids as $add ) {
|
||||
$wpdb->insert(
|
||||
$wpdb->prefix . 'woocommerce_subscription_downloads',
|
||||
/**
|
||||
* Revoke download permissions for a product across all related subscriptions.
|
||||
*
|
||||
* @param int $product_id Product ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function revoke_permissions_for_product( $product_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$subscription_ids = WC_Subscription_Downloads::get_subscriptions( $product_id );
|
||||
|
||||
if ( empty( $subscription_ids ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $subscription_ids as $subscription_id ) {
|
||||
$orders = $this->get_orders( $subscription_id );
|
||||
|
||||
foreach ( $orders as $order_id ) {
|
||||
// Delete ALL permissions for this product+order combination.
|
||||
// This ensures that when files change, old permissions with different download_ids are removed.
|
||||
$wpdb->delete(
|
||||
$wpdb->prefix . 'woocommerce_downloadable_product_permissions',
|
||||
array(
|
||||
'product_id' => $product_id,
|
||||
'subscription_id' => $add,
|
||||
'order_id' => $order_id,
|
||||
'product_id' => $product_id,
|
||||
),
|
||||
array(
|
||||
'%d',
|
||||
|
|
@ -248,23 +587,105 @@ class WC_Subscription_Downloads_Products {
|
|||
)
|
||||
);
|
||||
|
||||
$_orders = $this->get_orders( $add );
|
||||
foreach ( $_orders as $order_id ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
do_action( 'woocommerce_revoke_access_to_product_download', $product_id, $order_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! is_a( $order, 'WC_Subscription' ) ) {
|
||||
// avoid adding permissions to orders and it's
|
||||
// subscription for the same user, causing duplicates
|
||||
// to show up
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Grant download permissions for a product across all related subscriptions.
|
||||
*
|
||||
* @param int $product_id Product ID.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function grant_permissions_for_product( $product_id ) {
|
||||
$subscription_product_ids = WC_Subscription_Downloads::get_subscriptions( $product_id );
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
$_product = wc_get_product( $product_id );
|
||||
$downloads = $_product->get_downloads();
|
||||
if ( empty( $subscription_product_ids ) || ! $product ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adds the downloadable files to the order/subscription.
|
||||
foreach ( array_keys( $downloads ) as $download_id ) {
|
||||
wc_downloadable_file_permission( $download_id, $product_id, $order );
|
||||
$downloads = $product->get_downloads();
|
||||
|
||||
foreach ( $subscription_product_ids as $subscription_id ) {
|
||||
$orders = $this->get_orders( $subscription_id );
|
||||
|
||||
foreach ( $orders as $order_id ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( ! is_a( $order, 'WC_Subscription' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( array_keys( $downloads ) as $download_id ) {
|
||||
wc_downloadable_file_permission( $download_id, $product_id, $order );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds relationships between the specified subscription and product IDs.
|
||||
*
|
||||
* The most common use case will be to supply a single subscription ID and one-or-more product IDs, or else the
|
||||
* inverse.
|
||||
*
|
||||
* @param int[] $subscription_ids
|
||||
* @param int[] $product_ids
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function create_relationships( array $subscription_ids, array $product_ids ): void {
|
||||
global $wpdb;
|
||||
|
||||
foreach ( $product_ids as $product_id ) {
|
||||
$product_id = (int) $product_id;
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
// Check if product has public status.
|
||||
$has_public_status = false;
|
||||
if ( $product ) {
|
||||
$status_object = get_post_status_object( $product->get_status() );
|
||||
$has_public_status = $status_object && $status_object->public;
|
||||
}
|
||||
|
||||
foreach ( $subscription_ids as $subscription_id ) {
|
||||
$subscription_id = (int) $subscription_id;
|
||||
|
||||
$wpdb->insert(
|
||||
$wpdb->prefix . 'woocommerce_subscription_downloads',
|
||||
array(
|
||||
'product_id' => $product_id,
|
||||
'subscription_id' => $subscription_id,
|
||||
),
|
||||
array(
|
||||
'%d',
|
||||
'%d',
|
||||
)
|
||||
);
|
||||
|
||||
// Only grant download permissions if product has public status.
|
||||
if ( $has_public_status ) {
|
||||
$orders = $this->get_orders( $subscription_id );
|
||||
foreach ( $orders as $order_id ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( ! is_a( $order, 'WC_Subscription' ) ) {
|
||||
// avoid adding permissions to orders and it's
|
||||
// subscription for the same user, causing duplicates
|
||||
// to show up
|
||||
continue;
|
||||
}
|
||||
|
||||
$product = wc_get_product( $product_id );
|
||||
$downloads = $product->get_downloads();
|
||||
|
||||
// Adds the downloadable files to the order/subscription.
|
||||
foreach ( array_keys( $downloads ) as $download_id ) {
|
||||
wc_downloadable_file_permission( $download_id, $product_id, $order );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -289,26 +710,6 @@ class WC_Subscription_Downloads_Products {
|
|||
$this->update_subscription_downloads( $product_id, $subscription_downloads_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save variable product data.
|
||||
*
|
||||
* @param int $variation_id
|
||||
* @param int $index
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save_variation_product_data( $variation_id, $index ) {
|
||||
if ( ! isset( $_POST['variable_is_downloadable'][ $index ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
$subscription_download_ids = isset( $_POST['_variable_subscription_downloads_ids'][ $index ] ) ? wc_clean( wp_unslash( $_POST['_variable_subscription_downloads_ids'][ $index ] ) ) : array();
|
||||
$subscription_download_ids = array_filter( $subscription_download_ids ); // nosemgrep: audit.php.lang.misc.array-filter-no-callback -- $subscription_download_ids are already passed through wc_clean() and wp_unslash().
|
||||
|
||||
$this->update_subscription_downloads( $variation_id, $subscription_download_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save subscriptions information when duplicating a product.
|
||||
*
|
||||
|
|
@ -370,4 +771,45 @@ class WC_Subscription_Downloads_Products {
|
|||
|
||||
return (string) wc_get_formatted_variation( $product_variation, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated, do not use. Previously took care of saving product data for variations.
|
||||
*
|
||||
* @deprecated 8.3.0
|
||||
*
|
||||
* @param int $variation_id
|
||||
* @param int $index
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
public function save_variation_product_data( $variation_id, $index ) {
|
||||
wc_deprecated_function( __METHOD__, '8.3.0', __CLASS__ . '::handle_product_variation_save' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated, do not use. Previously took care of saving product data.
|
||||
*
|
||||
* @deprecated 8.3.0
|
||||
*
|
||||
* @param int $subscription_product_id
|
||||
* @param int|null $index
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
public function save_subscription_product_data( int $subscription_product_id, ?int $index = null ) {
|
||||
wc_deprecated_function( __METHOD__, '8.3.0', __CLASS__ . '::handle_product_save' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated, do not use. Previously set up assets for the Subscription Downloads extension.
|
||||
*
|
||||
* @deprecated 8.3.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function scripts() {
|
||||
wc_deprecated_function( __METHOD__, '8.3.0' );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,6 @@ class WC_Subscription_Downloads_Settings {
|
|||
* @since 8.0.0
|
||||
*/
|
||||
public static function add_notice_about_bundled_feature() {
|
||||
if ( ! Constants::is_true( 'WCS_ALLOW_SUBSCRIPTION_DOWNLOADS' ) ) {
|
||||
// TODO: remove this conditional as soon as we are ready to make subscription downloads functionality live.
|
||||
return;
|
||||
}
|
||||
|
||||
$screen = get_current_screen();
|
||||
if ( ! $screen ) {
|
||||
return false;
|
||||
|
|
@ -65,8 +60,8 @@ class WC_Subscription_Downloads_Settings {
|
|||
'id' => WC_Subscriptions_Admin::$option_prefix . '_downloads_settings',
|
||||
),
|
||||
array(
|
||||
'name' => __( 'Enable product linking to subscriptions', 'woocommerce-subscriptions' ),
|
||||
'desc' => __( 'Allow simple and variable downloadable products to be included with subscription products.', 'woocommerce-subscriptions' ),
|
||||
'name' => __( 'Enable downloadable file sharing', 'woocommerce-subscriptions' ),
|
||||
'desc' => __( 'Allow downloadable files from simple and variable products to be shared with subscription products so they are available to active subscribers.', 'woocommerce-subscriptions' ),
|
||||
'id' => WC_Subscriptions_Admin::$option_prefix . '_enable_downloadable_file_linking',
|
||||
'default' => 'no',
|
||||
'type' => 'checkbox',
|
||||
|
|
|
|||
|
|
@ -33,32 +33,8 @@ class WCSG_Admin_Welcome_Announcement {
|
|||
|
||||
$screen = get_current_screen();
|
||||
|
||||
$script_asset_path = \WC_Subscriptions_Plugin::instance()->get_plugin_directory( 'build/gifting-welcome-announcement.asset.php' );
|
||||
$script_asset = file_exists( $script_asset_path )
|
||||
? require $script_asset_path
|
||||
: array(
|
||||
'dependencies' => array(
|
||||
'react',
|
||||
'wc-blocks-checkout',
|
||||
'wc-price-format',
|
||||
'wc-settings',
|
||||
'wp-element',
|
||||
'wp-i18n',
|
||||
'wp-plugins',
|
||||
),
|
||||
'version' => WC_Subscriptions::$version,
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wcs-gifting-welcome-announcement',
|
||||
plugins_url( '/build/gifting-welcome-announcement.js', WC_Subscriptions::$plugin_file ),
|
||||
$script_asset['dependencies'],
|
||||
$script_asset['version'],
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'wcs-gifting-welcome-announcement',
|
||||
'wcs-admin',
|
||||
'wcsGiftingSettings',
|
||||
array(
|
||||
'imagesPath' => plugins_url( '/assets/images', WC_Subscriptions::$plugin_file ),
|
||||
|
|
@ -68,19 +44,6 @@ class WCSG_Admin_Welcome_Announcement {
|
|||
'isSubscriptionsListing' => 'woocommerce_page_wc-orders--shop_subscription' === $screen->id,
|
||||
)
|
||||
);
|
||||
|
||||
wp_enqueue_style(
|
||||
'wcs-gifting-welcome-announcement',
|
||||
plugins_url( '/build/style-gifting-welcome-announcement.css', WC_Subscriptions::$plugin_file ),
|
||||
array( 'wp-components' ),
|
||||
$script_asset['version']
|
||||
);
|
||||
|
||||
wp_set_script_translations(
|
||||
'wcs-gifting-welcome-announcement',
|
||||
'woocommerce-subscriptions',
|
||||
plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'languages'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Automattic\WooCommerce_Subscriptions\Internal\Utilities\Request;
|
||||
|
||||
/**
|
||||
* A class to make it possible to switch between different subscriptions (i.e. upgrade/downgrade a subscription)
|
||||
*
|
||||
|
|
@ -44,6 +47,9 @@ class WC_Subscriptions_Switcher {
|
|||
// Add the "Switch" button to the View Subscription table
|
||||
add_action( 'woocommerce_order_item_meta_end', array( __CLASS__, 'print_switch_link' ), 10, 3 );
|
||||
|
||||
// Add hidden form inputs for AJAX add-to-cart compatibility during switches
|
||||
add_action( 'woocommerce_before_add_to_cart_button', array( __CLASS__, 'add_switch_hidden_inputs' ) );
|
||||
|
||||
// We need to create subscriptions on checkout and want to do it after almost all other extensions have added their products/items/fees
|
||||
add_action( 'woocommerce_checkout_order_processed', array( __CLASS__, 'process_checkout' ), 50, 2 );
|
||||
|
||||
|
|
@ -179,22 +185,26 @@ class WC_Subscriptions_Switcher {
|
|||
public static function subscription_switch_handler() {
|
||||
global $post;
|
||||
|
||||
// If the current user doesn't own the subscription, remove the query arg from the URL
|
||||
if ( isset( $_GET['switch-subscription'] ) && isset( $_GET['item'] ) ) {
|
||||
$switch_subscription_id = Request::get_var( 'switch-subscription' );
|
||||
$item_id = Request::get_var( 'item' );
|
||||
|
||||
$subscription = wcs_get_subscription( absint( $_GET['switch-subscription'] ) );
|
||||
$line_item = $subscription ? wcs_get_order_item( absint( $_GET['item'] ), $subscription ) : false;
|
||||
$nonce = ! empty( $_GET['_wcsnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wcsnonce'] ) ) : false;
|
||||
// If the current user doesn't own the subscription, remove the query arg from the URL
|
||||
if ( $switch_subscription_id && $item_id ) {
|
||||
|
||||
$subscription = wcs_get_subscription( absint( $switch_subscription_id ) );
|
||||
$line_item = $subscription ? wcs_get_order_item( absint( $item_id ), $subscription ) : false;
|
||||
$nonce = Request::get_var( '_wcsnonce' );
|
||||
$nonce = $nonce ? sanitize_text_field( wp_unslash( $nonce ) ) : false;
|
||||
|
||||
// Visiting a switch link for someone elses subscription or if the switch link doesn't contain a valid nonce
|
||||
if ( ! is_object( $subscription ) || empty( $nonce ) || ! wp_verify_nonce( $nonce, 'wcs_switch_request' ) || empty( $line_item ) || ! self::can_item_be_switched_by_user( $line_item, $subscription ) ) {
|
||||
|
||||
wp_safe_redirect( remove_query_arg( array( 'switch-subscription', 'auto-switch', 'item', '_wcsnonce' ) ) );
|
||||
exit();
|
||||
Request::redirect( remove_query_arg( array( 'switch-subscription', 'auto-switch', 'item', '_wcsnonce' ) ) );
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
if ( isset( $_GET['auto-switch'] ) ) {
|
||||
if ( Request::get_var( 'auto-switch' ) ) {
|
||||
$switch_message = __( 'You have a subscription to this product. Choosing a new subscription will replace your existing subscription.', 'woocommerce-subscriptions' );
|
||||
} else {
|
||||
$switch_message = __( 'Choose a new subscription.', 'woocommerce-subscriptions' );
|
||||
|
|
@ -239,8 +249,8 @@ class WC_Subscriptions_Switcher {
|
|||
if ( $removed_item_count > 0 ) {
|
||||
wc_add_notice( _n( 'Your cart contained an invalid subscription switch request. It has been removed.', 'Your cart contained invalid subscription switch requests. They have been removed.', $removed_item_count, 'woocommerce-subscriptions' ), 'error' );
|
||||
|
||||
wp_safe_redirect( wc_get_cart_url() );
|
||||
exit();
|
||||
Request::redirect( wc_get_cart_url() );
|
||||
return;
|
||||
}
|
||||
} elseif ( is_product() && $product = wc_get_product( $post ) ) { // Automatically initiate the switch process for limited variable subscriptions
|
||||
|
||||
|
|
@ -308,8 +318,8 @@ class WC_Subscriptions_Switcher {
|
|||
}
|
||||
|
||||
if ( apply_filters( 'wcs_initiate_auto_switch', self::can_item_be_switched_by_user( $item, $subscription ), $item, $subscription ) ) {
|
||||
wp_safe_redirect( add_query_arg( 'auto-switch', 'true', self::get_switch_url( $item_id, $item, $subscription ) ) );
|
||||
exit;
|
||||
Request::redirect( add_query_arg( 'auto-switch', 'true', self::get_switch_url( $item_id, $item, $subscription ) ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -560,6 +570,30 @@ class WC_Subscriptions_Switcher {
|
|||
echo wp_kses( apply_filters( 'woocommerce_subscriptions_switch_link', $switch_link, $item_id, $item, $subscription ), array( 'a' => array( 'href' => array(), 'title' => array(), 'class' => array() ) ) ); // phpcs:ignore WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
|
||||
}
|
||||
|
||||
/**
|
||||
* Add hidden form inputs for subscription switch parameters.
|
||||
*
|
||||
* When a customer is switching subscriptions, the switch parameters are passed via URL query arguments.
|
||||
* This method outputs them as hidden form inputs so they're included when AJAX add-to-cart plugins
|
||||
* serialize and submit the form via POST.
|
||||
*
|
||||
* @since 8.3.0
|
||||
*/
|
||||
public static function add_switch_hidden_inputs() {
|
||||
$switch_subscription_id = Request::get_var( 'switch-subscription' );
|
||||
$item_id = Request::get_var( 'item' );
|
||||
$nonce = Request::get_var( '_wcsnonce' );
|
||||
|
||||
// Only output if we're in a switch context with a valid nonce
|
||||
if ( ! $switch_subscription_id || ! $item_id || ! $nonce ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<input type="hidden" name="switch-subscription" value="' . esc_attr( $switch_subscription_id ) . '" />';
|
||||
echo '<input type="hidden" name="item" value="' . esc_attr( $item_id ) . '" />';
|
||||
echo '<input type="hidden" name="_wcsnonce" value="' . esc_attr( $nonce ) . '" />';
|
||||
}
|
||||
|
||||
/**
|
||||
* The link for switching a subscription - the product page for variable subscriptions, or grouped product page for grouped subscriptions.
|
||||
*
|
||||
|
|
@ -1382,21 +1416,25 @@ class WC_Subscriptions_Switcher {
|
|||
|
||||
try {
|
||||
|
||||
if ( ! isset( $_GET['switch-subscription'] ) ) {
|
||||
$switch_subscription_id = Request::get_var( 'switch-subscription' );
|
||||
|
||||
if ( ! $switch_subscription_id ) {
|
||||
return $is_valid;
|
||||
}
|
||||
|
||||
if ( empty( $_GET['_wcsnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wcsnonce'] ) ), 'wcs_switch_request' ) ) {
|
||||
$nonce = Request::get_var( '_wcsnonce' );
|
||||
|
||||
if ( empty( $nonce ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $nonce ) ), 'wcs_switch_request' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$subscription = wcs_get_subscription( absint( $_GET['switch-subscription'] ) );
|
||||
$subscription = wcs_get_subscription( absint( $switch_subscription_id ) );
|
||||
|
||||
if ( ! $subscription ) {
|
||||
throw new Exception( __( 'The subscription may have been deleted.', 'woocommerce-subscriptions' ) );
|
||||
}
|
||||
|
||||
$item_id = absint( $_GET['item'] );
|
||||
$item_id = absint( Request::get_var( 'item' ) );
|
||||
$item = wcs_get_order_item( $item_id, $subscription );
|
||||
|
||||
// Prevent switching to non-subscription product
|
||||
|
|
@ -1468,11 +1506,13 @@ class WC_Subscriptions_Switcher {
|
|||
public static function set_switch_details_in_cart( $cart_item_data, $product_id, $variation_id ) {
|
||||
|
||||
try {
|
||||
if ( ! isset( $_GET['switch-subscription'] ) ) {
|
||||
$switch_subscription_id = Request::get_var( 'switch-subscription' );
|
||||
|
||||
if ( ! $switch_subscription_id ) {
|
||||
return $cart_item_data;
|
||||
}
|
||||
|
||||
$subscription = wcs_get_subscription( absint( $_GET['switch-subscription'] ) );
|
||||
$subscription = wcs_get_subscription( absint( $switch_subscription_id ) );
|
||||
|
||||
if ( ! $subscription ) {
|
||||
throw new Exception( __( 'The subscription may have been deleted.', 'woocommerce-subscriptions' ) );
|
||||
|
|
@ -1482,11 +1522,11 @@ class WC_Subscriptions_Switcher {
|
|||
if ( ! current_user_can( 'switch_shop_subscription', $subscription->get_id() ) ) {
|
||||
wc_add_notice( __( 'You can not switch this subscription. It appears you do not own the subscription.', 'woocommerce-subscriptions' ), 'error' );
|
||||
WC()->cart->empty_cart( true );
|
||||
wp_safe_redirect( get_permalink( $product_id ) );
|
||||
exit();
|
||||
Request::redirect( get_permalink( $product_id ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$item = wcs_get_order_item( absint( $_GET['item'] ), $subscription );
|
||||
$item = wcs_get_order_item( absint( Request::get_var( 'item' ) ), $subscription );
|
||||
|
||||
// Else it's a valid switch
|
||||
$product = wc_get_product( $item['product_id'] );
|
||||
|
|
@ -1538,7 +1578,7 @@ class WC_Subscriptions_Switcher {
|
|||
|
||||
$cart_item_data['subscription_switch'] = array(
|
||||
'subscription_id' => $subscription->get_id(),
|
||||
'item_id' => absint( $_GET['item'] ),
|
||||
'item_id' => absint( Request::get_var( 'item' ) ),
|
||||
'next_payment_timestamp' => $next_payment_timestamp,
|
||||
'upgraded_or_downgraded' => '',
|
||||
);
|
||||
|
|
@ -1549,8 +1589,8 @@ class WC_Subscriptions_Switcher {
|
|||
|
||||
wc_add_notice( __( 'There was an error locating the switch details.', 'woocommerce-subscriptions' ), 'error' );
|
||||
WC()->cart->empty_cart( true );
|
||||
wp_safe_redirect( get_permalink( wc_get_page_id( 'cart' ) ) );
|
||||
exit();
|
||||
Request::redirect( get_permalink( wc_get_page_id( 'cart' ) ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
# Copyright (C) 2025 WooCommerce
|
||||
# Copyright (C) 2026 WooCommerce
|
||||
# This file is distributed under the GNU General Public License v3.0.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: WooCommerce Subscriptions 8.2.1\n"
|
||||
"Project-Id-Version: WooCommerce Subscriptions 8.3.0\n"
|
||||
"Report-Msgid-Bugs-To: https://woocommerce.com/contact-us\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"POT-Creation-Date: 2025-12-22T21:15:57+00:00\n"
|
||||
"POT-Creation-Date: 2026-01-06T17:54:09+00:00\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"X-Generator: WP-CLI 2.12.0\n"
|
||||
"X-Domain: woocommerce-subscriptions\n"
|
||||
|
|
@ -889,7 +889,7 @@ msgstr ""
|
|||
msgid "Order already has subscriptions associated with it."
|
||||
msgstr ""
|
||||
|
||||
#: includes/api/class-wc-rest-subscriptions-settings-option-controller.php:83
|
||||
#: includes/api/class-wc-rest-subscriptions-settings-option-controller.php:84
|
||||
msgid "Invalid value type; must be either boolean or array"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -1123,26 +1123,26 @@ msgid "%1$sWooCommerce Subscriptions is inactive.%2$s This version of Subscripti
|
|||
msgstr ""
|
||||
|
||||
#. translators: 1-2: opening/closing <b> tags, 3: Subscriptions version.
|
||||
#: includes/class-wc-subscriptions-plugin.php:70
|
||||
#: includes/class-wc-subscriptions-plugin.php:71
|
||||
#, php-format
|
||||
msgid "%1$sWarning!%2$s We can see the %1$sWooCommerce Subscriptions Early Renewal%2$s plugin is active. Version %3$s of %1$sWooCommerce Subscriptions%2$s comes with that plugin's functionality packaged into the core plugin. Please deactivate WooCommerce Subscriptions Early Renewal to avoid any conflicts."
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-subscriptions-plugin.php:74
|
||||
#: includes/class-wc-subscriptions-plugin.php:75
|
||||
msgid "Installed Plugins"
|
||||
msgstr ""
|
||||
|
||||
#. translators: $1-$2: opening and closing <strong> tags, $3-$4: opening and closing <em> tags.
|
||||
#: includes/class-wc-subscriptions-plugin.php:216
|
||||
#: includes/class-wc-subscriptions-plugin.php:217
|
||||
#, php-format
|
||||
msgid "%1$sWooCommerce Subscriptions Installed%2$s – %3$sYou're ready to start selling subscriptions!%4$s"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-subscriptions-plugin.php:234
|
||||
#: includes/class-wc-subscriptions-plugin.php:235
|
||||
msgid "Add a Subscription Product"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-wc-subscriptions-plugin.php:235
|
||||
#: includes/class-wc-subscriptions-plugin.php:236
|
||||
#: includes/core/class-wc-subscriptions-core-plugin.php:580
|
||||
#: includes/core/upgrades/templates/wcs-about-2-0.php:35
|
||||
#: includes/core/upgrades/templates/wcs-about.php:34
|
||||
|
|
@ -1407,7 +1407,7 @@ msgid "Virtual"
|
|||
msgstr ""
|
||||
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:296
|
||||
#: templates/admin/html-variation-price.php:27
|
||||
#: templates/admin/html-variation-price.php:29
|
||||
msgid "Choose the subscription price, billing interval and period."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -1421,7 +1421,7 @@ msgstr ""
|
|||
#. translators: %s: currency symbol.
|
||||
#. translators: placeholder is a currency symbol / code
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:313
|
||||
#: templates/admin/html-variation-price.php:23
|
||||
#: templates/admin/html-variation-price.php:25
|
||||
#, php-format
|
||||
msgid "Subscription price (%s)"
|
||||
msgstr ""
|
||||
|
|
@ -1430,7 +1430,7 @@ msgstr ""
|
|||
#. translators: %s the formatted example price value.
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:318
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:360
|
||||
#: templates/admin/html-variation-price.php:33
|
||||
#: templates/admin/html-variation-price.php:35
|
||||
#, php-format
|
||||
msgctxt "example price"
|
||||
msgid "e.g. %s"
|
||||
|
|
@ -1447,7 +1447,7 @@ msgstr ""
|
|||
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:344
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:516
|
||||
#: templates/admin/html-variation-price.php:53
|
||||
#: templates/admin/html-variation-price.php:55
|
||||
msgid "Stop renewing after"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -1457,19 +1457,19 @@ msgstr ""
|
|||
|
||||
#. translators: %s is a currency symbol / code
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:358
|
||||
#: templates/admin/html-variation-price.php:69
|
||||
#: templates/admin/html-variation-price.php:71
|
||||
#, php-format
|
||||
msgid "Sign-up fee (%s)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:361
|
||||
#: templates/admin/html-variation-price.php:72
|
||||
#: templates/admin/html-variation-price.php:74
|
||||
msgid "Optionally include an amount to be charged at the outset of the subscription. The sign-up fee will be charged immediately, even if the product has a free trial or the payment dates are synced."
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:375
|
||||
#: includes/core/class-wc-subscriptions-cart.php:2458
|
||||
#: templates/admin/html-variation-price.php:79
|
||||
#: templates/admin/html-variation-price.php:81
|
||||
msgid "Free trial"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -1480,22 +1480,22 @@ msgstr ""
|
|||
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:403
|
||||
#: includes/gifting/class-wcsg-admin.php:182
|
||||
#: templates/admin/html-variation-price.php:107
|
||||
#: templates/admin/html-variation-price.php:109
|
||||
msgid "Gifting"
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:407
|
||||
#: templates/admin/html-variation-price.php:117
|
||||
#: templates/admin/html-variation-price.php:119
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:408
|
||||
#: templates/admin/html-variation-price.php:118
|
||||
#: templates/admin/html-variation-price.php:120
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/admin/class-wc-subscriptions-admin.php:411
|
||||
#: templates/admin/html-variation-price.php:110
|
||||
#: templates/admin/html-variation-price.php:112
|
||||
msgid "Allow shoppers to purchase a subscription as a gift."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -3932,13 +3932,13 @@ msgid "Never (charge the full recurring amount at sign-up)"
|
|||
msgstr ""
|
||||
|
||||
#: includes/core/class-wc-subscriptions-synchroniser.php:226
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:442
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:452
|
||||
msgctxt "when to prorate first payment / subscription length"
|
||||
msgid "For Virtual Subscription Products Only"
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/class-wc-subscriptions-synchroniser.php:227
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:443
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:453
|
||||
msgctxt "when to prorate first payment / subscription length"
|
||||
msgid "For All Subscription Products"
|
||||
msgstr ""
|
||||
|
|
@ -5374,64 +5374,64 @@ msgstr ""
|
|||
msgid "Customers with a subscription are excluded from this setting."
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:307
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:306
|
||||
msgid "A database upgrade is required to use Subscriptions. Upgrades from the previously installed version is no longer supported. You will need to install an older version of WooCommerce Subscriptions or WooCommerce Payments to proceed with the upgrade before you can use a newer version."
|
||||
msgstr ""
|
||||
|
||||
#. translators: 1-2: opening/closing <strong> tags, 3: active version of Subscriptions, 4: current version of Subscriptions, 5-6: opening/closing tags linked to ticket form, 7-8: opening/closing tags linked to documentation.
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:369
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:368
|
||||
#, php-format
|
||||
msgid "%1$sWarning!%2$s It appears that you have downgraded %1$sWooCommerce Subscriptions%2$s from %3$s to %4$s. Downgrading the plugin in this way may cause issues. Please update to %3$s or higher, or %5$sopen a new support ticket%6$s for further assistance. %7$sLearn more »%8$s"
|
||||
msgstr ""
|
||||
|
||||
#. translators: placeholder is a list of version numbers (e.g. "1.3 & 1.4 & 1.5")
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:508
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:507
|
||||
#, php-format
|
||||
msgid "Database updated to version %s"
|
||||
msgstr ""
|
||||
|
||||
#. translators: placeholder is number of upgraded subscriptions
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:516
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:515
|
||||
#, php-format
|
||||
msgctxt "used in the subscriptions upgrader"
|
||||
msgid "Marked %s subscription products as \"sold individually\"."
|
||||
msgstr ""
|
||||
|
||||
#. translators: 1$: number of action scheduler hooks upgraded, 2$: "{execution_time}", will be replaced on front end with actual time
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:525
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:524
|
||||
#, php-format
|
||||
msgid "Migrated %1$s subscription related hooks to the new scheduler (in %2$s seconds)."
|
||||
msgstr ""
|
||||
|
||||
#. translators: 1$: number of subscriptions upgraded, 2$: "{execution_time}", will be replaced on front end with actual time it took
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:537
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:536
|
||||
#, php-format
|
||||
msgid "Migrated %1$s subscriptions to the new structure (in %2$s seconds)."
|
||||
msgstr ""
|
||||
|
||||
#. translators: placeholder is "{time_left}", will be replaced on front end with actual time
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:540
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:586
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:539
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:585
|
||||
#, php-format
|
||||
msgctxt "Message that gets sent to front end."
|
||||
msgid "Estimated time left (minutes:seconds): %s"
|
||||
msgstr ""
|
||||
|
||||
#. translators: 1$: error message, 2$: opening link tag, 3$: closing link tag, 4$: break tag
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:550
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:549
|
||||
#, php-format
|
||||
msgid "Unable to upgrade subscriptions.%4$sError: %1$s%4$sPlease refresh the page and try again. If problem persists, %2$scontact support%3$s."
|
||||
msgstr ""
|
||||
|
||||
#. translators: placeholder is the number of subscriptions repaired
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:565
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:564
|
||||
#, php-format
|
||||
msgctxt "Repair message that gets sent to front end."
|
||||
msgid "Repaired %d subscriptions with incorrect dates, line tax data or missing customer notes."
|
||||
msgstr ""
|
||||
|
||||
#. translators: placeholder is number of subscriptions that were checked and did not need repairs. There's a space at the beginning!
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:571
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:570
|
||||
#, php-format
|
||||
msgctxt "Repair message that gets sent to front end."
|
||||
msgid " %d other subscription was checked and did not need any repairs."
|
||||
|
|
@ -5440,36 +5440,36 @@ msgstr[0] ""
|
|||
msgstr[1] ""
|
||||
|
||||
#. translators: placeholder is "{execution_time}", which will be replaced on front end with actual time
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:575
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:574
|
||||
#, php-format
|
||||
msgctxt "Repair message that gets sent to front end."
|
||||
msgid "(in %s seconds)"
|
||||
msgstr ""
|
||||
|
||||
#. translators: $1: "Repaired x subs with incorrect dates...", $2: "X others were checked and no repair needed", $3: "(in X seconds)". Ordering for RTL languages.
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:578
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:577
|
||||
#, php-format
|
||||
msgctxt "The assembled repair message that gets sent to front end."
|
||||
msgid "%1$s%2$s %3$s"
|
||||
msgstr ""
|
||||
|
||||
#. translators: 1$: error message, 2$: opening link tag, 3$: closing link tag, 4$: break tag
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:597
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:596
|
||||
#, php-format
|
||||
msgctxt "Error message that gets sent to front end when upgrading Subscriptions"
|
||||
msgid "Unable to repair subscriptions.%4$sError: %1$s%4$sPlease refresh the page and try again. If problem persists, %2$scontact support%3$s."
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:805
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:804
|
||||
msgid "Welcome to WooCommerce Subscriptions 2.1"
|
||||
msgstr ""
|
||||
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:805
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:804
|
||||
msgid "About WooCommerce Subscriptions"
|
||||
msgstr ""
|
||||
|
||||
#. translators: 1-2: opening/closing <strong> tags, 3-4: opening/closing tags linked to ticket form.
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:1043
|
||||
#: includes/core/upgrades/class-wc-subscriptions-upgrader.php:1042
|
||||
#, php-format
|
||||
msgid "%1$sWarning!%2$s We discovered an issue in %1$sWooCommerce Subscriptions 2.3.0 - 2.3.2%2$s that may cause your subscription renewal order and customer subscription caches to contain invalid data. For information about how to update the cached data, please %3$sopen a new support ticket%4$s."
|
||||
msgstr ""
|
||||
|
|
@ -6457,35 +6457,53 @@ msgstr ""
|
|||
msgid "Available downloads"
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:66
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:98
|
||||
msgid "Included subscription products"
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:216
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:249
|
||||
msgid "Linked subscription products"
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:68
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:101
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:218
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:252
|
||||
msgid "Select subscriptions"
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:84
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:99
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:234
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:250
|
||||
msgid "Select subscription products that will include this downloadable product."
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-settings.php:41
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:291
|
||||
msgid "Select simple and variable downloadable products that will be included with this subscription product."
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:292
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:348
|
||||
msgid "Linked downloadable products"
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:295
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:351
|
||||
msgid "Select products"
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-products.php:352
|
||||
msgid "Select simple and variable downloadable products that will be included with this subscription variation."
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-settings.php:36
|
||||
msgid "WooCommerce Subscription Downloads is now part of WooCommerce Subscriptions — no extra plugin needed. You can deactivate and uninstall WooCommerce Subscription Downloads via the plugin admin screen."
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-settings.php:63
|
||||
#: includes/downloads/class-wc-subscription-downloads-settings.php:58
|
||||
msgid "Downloads"
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-settings.php:68
|
||||
msgid "Enable product linking to subscriptions"
|
||||
#: includes/downloads/class-wc-subscription-downloads-settings.php:63
|
||||
msgid "Enable downloadable file sharing"
|
||||
msgstr ""
|
||||
|
||||
#: includes/downloads/class-wc-subscription-downloads-settings.php:69
|
||||
msgid "Allow simple and variable downloadable products to be included with subscription products."
|
||||
#: includes/downloads/class-wc-subscription-downloads-settings.php:64
|
||||
msgid "Allow downloadable files from simple and variable products to be shared with subscription products so they are available to active subscribers."
|
||||
msgstr ""
|
||||
|
||||
#: includes/early-renewal/class-wcs-cart-early-renewal.php:74
|
||||
|
|
@ -7234,217 +7252,217 @@ msgstr ""
|
|||
msgid "Automatic renewal payment failed"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:198
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:208
|
||||
msgid "You have a subscription to this product. Choosing a new subscription will replace your existing subscription."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:200
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:210
|
||||
msgid "Choose a new subscription."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:240
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1231
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:250
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1265
|
||||
msgid "Your cart contained an invalid subscription switch request. It has been removed."
|
||||
msgid_plural "Your cart contained invalid subscription switch requests. They have been removed."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:282
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:292
|
||||
msgid "You have already subscribed to this product and it is limited to one per customer. You can not purchase the product again."
|
||||
msgstr ""
|
||||
|
||||
#. translators: 1$: is the "You have already subscribed to this product" notice, 2$-4$: opening/closing link tags, 3$: an order number
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:291
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:301
|
||||
#, php-format
|
||||
msgid "%1$s Complete payment on %2$sOrder %3$s%4$s to be able to change your subscription."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:387
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:397
|
||||
msgid "Switching"
|
||||
msgstr ""
|
||||
|
||||
#. translators: placeholders are opening and closing link tags
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:390
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:400
|
||||
#, php-format
|
||||
msgid "Allow subscribers to switch (upgrade or downgrade) between different subscriptions. %1$sLearn more%2$s."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:398
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:408
|
||||
msgid "Prorate Recurring Payment"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:399
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:409
|
||||
msgid "When switching to a subscription with a different recurring payment or billing period, should the price paid for the existing billing period be prorated when switching to the new subscription?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:407
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:441
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:417
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:451
|
||||
msgctxt "when to allow a setting"
|
||||
msgid "Never"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:408
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:418
|
||||
msgctxt "when to prorate recurring fee when switching"
|
||||
msgid "For Upgrades of Virtual Subscription Products Only"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:409
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:419
|
||||
msgctxt "when to prorate recurring fee when switching"
|
||||
msgid "For Upgrades of All Subscription Products"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:410
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:420
|
||||
msgctxt "when to prorate recurring fee when switching"
|
||||
msgid "For Upgrades & Downgrades of Virtual Subscription Products Only"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:411
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:421
|
||||
msgctxt "when to prorate recurring fee when switching"
|
||||
msgid "For Upgrades & Downgrades of All Subscription Products"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:416
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:426
|
||||
msgid "Prorate Sign up Fee"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:417
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:427
|
||||
msgid "When switching to a subscription with a sign up fee, you can require the customer pay only the gap between the existing subscription's sign up fee and the new subscription's sign up fee (if any)."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:425
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:435
|
||||
msgctxt "when to prorate signup fee when switching"
|
||||
msgid "Never (do not charge a sign up fee)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:426
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:436
|
||||
msgctxt "when to prorate signup fee when switching"
|
||||
msgid "Never (charge the full sign up fee)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:427
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:437
|
||||
msgctxt "when to prorate signup fee when switching"
|
||||
msgid "Always"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:432
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:442
|
||||
msgid "Prorate Subscription Length"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:433
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:443
|
||||
msgid "When switching to a subscription with a length, you can take into account the payments already completed by the customer when determining how many payments the subscriber needs to make for the new subscription."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:448
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:458
|
||||
msgid "Switch Button Text"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:449
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:459
|
||||
msgid "Customise the text displayed on the button next to the subscription on the subscriber's account page. The default is \"Switch Subscription\", but you may wish to change this to \"Upgrade\" or \"Change Subscription\"."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:453
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:555
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:463
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:565
|
||||
msgid "Upgrade or Downgrade"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:492
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:502
|
||||
msgid "Allow Switching"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:499
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:509
|
||||
msgctxt "when to allow switching"
|
||||
msgid "Between Subscription Variations"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:503
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:513
|
||||
msgctxt "when to allow switching"
|
||||
msgid "Between Grouped Subscriptions"
|
||||
msgstr ""
|
||||
|
||||
#. translators: %s: order number.
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1165
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1199
|
||||
#, php-format
|
||||
msgid "Switch order cancelled due to a new switch order being created #%s."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1396
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1478
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1434
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1518
|
||||
msgid "The subscription may have been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1404
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1442
|
||||
msgid "You can only switch to a subscription product."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1410
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1448
|
||||
msgid "We can not find your old subscription item."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1432
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1470
|
||||
msgid "You can not switch to the same subscription."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1483
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1523
|
||||
msgid "You can not switch this subscription. It appears you do not own the subscription."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1550
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1590
|
||||
msgid "There was an error locating the switch details."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1899
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1939
|
||||
msgctxt "a switch type"
|
||||
msgid "Downgrade"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1902
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1942
|
||||
msgctxt "a switch type"
|
||||
msgid "Upgrade"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1905
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1945
|
||||
msgctxt "a switch type"
|
||||
msgid "Crossgrade"
|
||||
msgstr ""
|
||||
|
||||
#. translators: %1: product subtotal, %2: HTML span tag, %3: direction (upgrade, downgrade, crossgrade), %4: closing HTML span tag
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1910
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:1950
|
||||
#, php-format
|
||||
msgctxt "product subtotal string"
|
||||
msgid "%1$s %2$s(%3$s)%4$s"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2026
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2066
|
||||
msgid "The original subscription item being switched cannot be found."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2028
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2068
|
||||
msgid "The item on the switch order cannot be found."
|
||||
msgstr ""
|
||||
|
||||
#. translators: 1$: old item, 2$: new item when switching
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2039
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2079
|
||||
#, php-format
|
||||
msgctxt "used in order notes"
|
||||
msgid "Customer switched from: %1$s to %2$s."
|
||||
msgstr ""
|
||||
|
||||
#. translators: %s: new item name.
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2042
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2082
|
||||
#, php-format
|
||||
msgctxt "used in order notes"
|
||||
msgid "Customer added %s."
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2454
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2494
|
||||
msgid "Switch Order"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2469
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2509
|
||||
msgid "Switched Subscription"
|
||||
msgstr ""
|
||||
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2656
|
||||
#: includes/switching/class-wc-subscriptions-switcher.php:2696
|
||||
msgctxt "add to cart button text while switching a subscription"
|
||||
msgid "Switch subscription"
|
||||
msgstr ""
|
||||
|
|
@ -7507,7 +7525,7 @@ msgstr ""
|
|||
|
||||
#. translators: placeholder is trial period validation message if passed an invalid value (e.g. "Trial period can not exceed 4 weeks")
|
||||
#: templates/admin/deprecated/html-variation-price.php:118
|
||||
#: templates/admin/html-variation-price.php:83
|
||||
#: templates/admin/html-variation-price.php:85
|
||||
#, php-format
|
||||
msgctxt "Trial period dropdown's description in pricing fields"
|
||||
msgid "An optional period of time to wait before charging the first recurring payment. Any sign up fee will still be charged at the outset of the subscription. %s"
|
||||
|
|
@ -7571,25 +7589,25 @@ msgstr[1] ""
|
|||
msgid "To see further details about these errors, view the %1$s log file from the %2$sWooCommerce logs screen.%2$s"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/html-variation-price.php:35
|
||||
#: templates/admin/html-variation-price.php:37
|
||||
msgid "Billing interval:"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/html-variation-price.php:42
|
||||
#: templates/admin/html-variation-price.php:44
|
||||
msgid "Billing Period:"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/html-variation-price.php:56
|
||||
#: templates/admin/html-variation-price.php:58
|
||||
msgctxt "Subscription Length dropdown's description in pricing fields"
|
||||
msgid "Automatically stop renewing the subscription after this length of time. This length is in addition to any free trial or amount of time provided before a synchronised first renewal date."
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/html-variation-price.php:75
|
||||
#: templates/admin/html-variation-price.php:77
|
||||
msgctxt "example price"
|
||||
msgid "e.g."
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/html-variation-price.php:90
|
||||
#: templates/admin/html-variation-price.php:92
|
||||
msgid "Subscription trial period:"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -8660,7 +8678,7 @@ msgstr ""
|
|||
msgid "Clear"
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: build/admin.js:1
|
||||
#: client/components/announcement/index.js:112
|
||||
#: client/components/announcement/index.js:163
|
||||
#: client/components/announcement/__tests__/index.test.js:118
|
||||
|
|
@ -8668,7 +8686,7 @@ msgstr ""
|
|||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: build/admin.js:1
|
||||
#: client/components/announcement/index.js:131
|
||||
#: client/components/announcement/__tests__/index.test.js:70
|
||||
#: client/components/announcement/__tests__/index.test.js:95
|
||||
|
|
@ -8677,52 +8695,69 @@ msgstr ""
|
|||
msgid "Step image"
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: build/admin.js:1
|
||||
#: client/components/announcement/index.js:175
|
||||
#: client/components/announcement/__tests__/index.test.js:115
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:8
|
||||
msgid "Gifting is now part of WooCommerce Subscriptions"
|
||||
#: build/admin.js:1
|
||||
#: client/downloads/data/welcome-announcement.js:7
|
||||
msgid "WooCommerce Subscription Downloads is now part of WooCommerce Subscriptions"
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:12
|
||||
msgid "Introducing subscription gifting"
|
||||
#: build/admin.js:1
|
||||
#: client/downloads/data/welcome-announcement.js:12
|
||||
msgid "No separate extension required! WooCommerce Subscriptions now allows you to include simple and variable products with your subscriptions. And to make it easier, you can now configure which simple and variable products to include when creating a subscription product."
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:15
|
||||
msgid "No separate extension needed! The built-in gifting feature is fully compatible with product, cart and checkout blocks, plus you can now choose which subscription products can be gifted."
|
||||
#: build/admin.js:1
|
||||
#: client/downloads/data/welcome-announcement.js:18
|
||||
msgid "The WooCommerce Subscription Downloads extension can now be disabled via Plugins."
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:21
|
||||
msgid "The Gifting for WooCommerce Subscriptions extension can now be disabled via Plugins."
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:26
|
||||
msgid "Let your shoppers purchase subscriptions as gifts using the new built-in gifting feature in WooCommerce Subscriptions. It works seamlessly with product, cart and checkout blocks, and can be enabled storewide or managed per product."
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: build/admin.js:1
|
||||
#: client/downloads/data/welcome-announcement.js:24
|
||||
#: client/gifting/data/welcome-announcement.js:33
|
||||
msgid "Go to plugins"
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:34
|
||||
msgid "Set up gifting"
|
||||
msgstr ""
|
||||
|
||||
#: build/gifting-welcome-announcement.js:1
|
||||
#: build/admin.js:1
|
||||
#: client/downloads/data/welcome-announcement.js:35
|
||||
#: client/gifting/data/welcome-announcement.js:42
|
||||
msgid "Maybe later"
|
||||
msgstr ""
|
||||
|
||||
#: build/admin.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:8
|
||||
msgid "Gifting is now part of WooCommerce Subscriptions"
|
||||
msgstr ""
|
||||
|
||||
#: build/admin.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:12
|
||||
msgid "Introducing subscription gifting"
|
||||
msgstr ""
|
||||
|
||||
#: build/admin.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:15
|
||||
msgid "No separate extension needed! The built-in gifting feature is fully compatible with product, cart and checkout blocks, plus you can now choose which subscription products can be gifted."
|
||||
msgstr ""
|
||||
|
||||
#: build/admin.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:21
|
||||
msgid "The Gifting for WooCommerce Subscriptions extension can now be disabled via Plugins."
|
||||
msgstr ""
|
||||
|
||||
#: build/admin.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:26
|
||||
msgid "Let your shoppers purchase subscriptions as gifts using the new built-in gifting feature in WooCommerce Subscriptions. It works seamlessly with product, cart and checkout blocks, and can be enabled storewide or managed per product."
|
||||
msgstr ""
|
||||
|
||||
#: build/admin.js:1
|
||||
#: client/gifting/data/welcome-announcement.js:34
|
||||
msgid "Set up gifting"
|
||||
msgstr ""
|
||||
|
||||
#: build/index.js:1
|
||||
#: client/core/utils/index.js:8
|
||||
msgctxt "Used in recurring totals section in Cart. 2+ will need plural, 1 will need singular."
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Automattic\WooCommerce_Subscriptions\Internal\Utilities;
|
||||
|
||||
/**
|
||||
* Utilities for handling request-related operations, including test-safe redirects and exits.
|
||||
*
|
||||
* @internal This class may be modified, moved or removed in future releases.
|
||||
*/
|
||||
class Request {
|
||||
/**
|
||||
* Cached POST input variables.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
private static ?array $post_vars = null;
|
||||
|
||||
/**
|
||||
* Cached GET input variables.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
private static ?array $url_vars = null;
|
||||
|
||||
/**
|
||||
* Exit unless running in test environment.
|
||||
*
|
||||
* This method allows tests to run without terminating the PHP process,
|
||||
* enabling proper code coverage collection and test assertions.
|
||||
* In production, this behaves exactly like exit().
|
||||
*
|
||||
* @since 8.3.0
|
||||
* @return void
|
||||
*/
|
||||
public static function exit() {
|
||||
if ( defined( 'WCS_ENVIRONMENT_TYPE' ) && 'tests' === WCS_ENVIRONMENT_TYPE ) {
|
||||
return;
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe redirect that works in test environment.
|
||||
*
|
||||
* In production, this performs a normal wp_safe_redirect() and exits by default.
|
||||
* In tests, it stores the redirect information in a global variable for assertions
|
||||
* instead of attempting to send headers (which would fail).
|
||||
*
|
||||
* @since 8.3.0
|
||||
* @param string $location The URL to redirect to.
|
||||
* @param int $status HTTP status code (default 302).
|
||||
* @param bool $should_exit Whether to exit after setting redirect headers (default true).
|
||||
* @return void
|
||||
*/
|
||||
public static function redirect( $location, $status = 302, $should_exit = true ) {
|
||||
if ( defined( 'WCS_ENVIRONMENT_TYPE' ) && 'tests' === WCS_ENVIRONMENT_TYPE ) {
|
||||
// Store redirect info for test assertions but don't actually redirect
|
||||
$GLOBALS['wcs_test_redirect'] = array(
|
||||
'location' => $location,
|
||||
'status' => $status,
|
||||
);
|
||||
return;
|
||||
}
|
||||
wp_safe_redirect( $location, $status );
|
||||
|
||||
if ( $should_exit ) {
|
||||
self::exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the value of the POST or URL query parameter matching $key, or else returns $default.
|
||||
*
|
||||
* Essentially, this is an alternative to inspecting the $_REQUEST super-global and is intended for cases where we
|
||||
* are interested in a key:value pair, regardless of whether it was sent as a post var or URL query var.
|
||||
*
|
||||
* Its advantages are that it only ever examines the POST and GET inputs (POST taking priority, if both contain the
|
||||
* same key): cookies are always ignored. It also looks directly at the inputs, instead of using the $_POST or $_GET
|
||||
* superglobals (which can be manipulated).
|
||||
*
|
||||
* @since 8.3.0
|
||||
*
|
||||
* @param string $key The key to look up.
|
||||
* @param mixed $default_value The value to return if the key is not found. Defaults to null.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get_var( string $key, $default_value = null ) {
|
||||
if ( null === self::$post_vars ) {
|
||||
self::$post_vars = filter_input_array( INPUT_POST ) ?? array();
|
||||
self::$url_vars = filter_input_array( INPUT_GET ) ?? array();
|
||||
}
|
||||
|
||||
if ( isset( self::$post_vars[ $key ] ) ) {
|
||||
return self::$post_vars[ $key ];
|
||||
} elseif ( isset( self::$url_vars[ $key ] ) ) {
|
||||
return self::$url_vars[ $key ];
|
||||
}
|
||||
|
||||
return $default_value;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
/**
|
||||
* Outputs a subscription variation's pricing fields for WooCommerce 2.3+
|
||||
*
|
||||
* You may also be interested in assets/js/admin/admin.js, which dynamically relocates some of these fields.
|
||||
*
|
||||
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.12
|
||||
*
|
||||
* @var int $loop
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ return array(
|
|||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Telemetry\\Orders' => $baseDir . '/src/Internal/Telemetry/Orders.php',
|
||||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Telemetry\\Products' => $baseDir . '/src/Internal/Telemetry/Products.php',
|
||||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Telemetry\\Subscriptions' => $baseDir . '/src/Internal/Telemetry/Subscriptions.php',
|
||||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Utilities\\Request' => $baseDir . '/src/Internal/Utilities/Request.php',
|
||||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Utilities\\Scheduled_Actions' => $baseDir . '/src/Internal/Utilities/Scheduled_Actions.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'Composer\\Installers\\AglInstaller' => $vendorDir . '/composer/installers/src/Composer/Installers/AglInstaller.php',
|
||||
|
|
@ -138,6 +139,7 @@ return array(
|
|||
'WCSG_Template_Loader' => $baseDir . '/includes/gifting/class-wcsg-template-loader.php',
|
||||
'WCS_Gifting' => $baseDir . '/includes/gifting/class-wcs-gifting.php',
|
||||
'WC_Subscription_Downloads' => $baseDir . '/includes/downloads/class-wc-subscription-downloads.php',
|
||||
'WC_Subscription_Downloads_Admin_Welcome_Announcement' => $baseDir . '/includes/downloads/class-wc-subscription-downloads-admin-welcome-announcement.php',
|
||||
'WC_Subscription_Downloads_Ajax' => $baseDir . '/includes/downloads/class-wc-subscription-downloads-ajax.php',
|
||||
'WC_Subscription_Downloads_Install' => $baseDir . '/includes/downloads/class-wc-subscription-downloads-install.php',
|
||||
'WC_Subscription_Downloads_Order' => $baseDir . '/includes/downloads/class-wc-subscription-downloads-order.php',
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class ComposerStaticInitcb45de1ca955f89ec737c442c7cf101c
|
|||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Telemetry\\Orders' => __DIR__ . '/../..' . '/src/Internal/Telemetry/Orders.php',
|
||||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Telemetry\\Products' => __DIR__ . '/../..' . '/src/Internal/Telemetry/Products.php',
|
||||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Telemetry\\Subscriptions' => __DIR__ . '/../..' . '/src/Internal/Telemetry/Subscriptions.php',
|
||||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Utilities\\Request' => __DIR__ . '/../..' . '/src/Internal/Utilities/Request.php',
|
||||
'Automattic\\WooCommerce_Subscriptions\\Internal\\Utilities\\Scheduled_Actions' => __DIR__ . '/../..' . '/src/Internal/Utilities/Scheduled_Actions.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'Composer\\Installers\\AglInstaller' => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers/AglInstaller.php',
|
||||
|
|
@ -161,6 +162,7 @@ class ComposerStaticInitcb45de1ca955f89ec737c442c7cf101c
|
|||
'WCSG_Template_Loader' => __DIR__ . '/../..' . '/includes/gifting/class-wcsg-template-loader.php',
|
||||
'WCS_Gifting' => __DIR__ . '/../..' . '/includes/gifting/class-wcs-gifting.php',
|
||||
'WC_Subscription_Downloads' => __DIR__ . '/../..' . '/includes/downloads/class-wc-subscription-downloads.php',
|
||||
'WC_Subscription_Downloads_Admin_Welcome_Announcement' => __DIR__ . '/../..' . '/includes/downloads/class-wc-subscription-downloads-admin-welcome-announcement.php',
|
||||
'WC_Subscription_Downloads_Ajax' => __DIR__ . '/../..' . '/includes/downloads/class-wc-subscription-downloads-ajax.php',
|
||||
'WC_Subscription_Downloads_Install' => __DIR__ . '/../..' . '/includes/downloads/class-wc-subscription-downloads-install.php',
|
||||
'WC_Subscription_Downloads_Order' => __DIR__ . '/../..' . '/includes/downloads/class-wc-subscription-downloads-order.php',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => 'woocommerce/woocommerce-subscriptions',
|
||||
'pretty_version' => 'dev-release/8.2.1',
|
||||
'version' => 'dev-release/8.2.1',
|
||||
'reference' => '8bb99631f731722b758f4702dc319ae43043ed67',
|
||||
'pretty_version' => 'dev-release/8.3.0',
|
||||
'version' => 'dev-release/8.3.0',
|
||||
'reference' => '38b39c285d0bae952d5746ba460ae914e4ac0f60',
|
||||
'type' => 'wordpress-plugin',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
@ -29,9 +29,9 @@
|
|||
'dev_requirement' => false,
|
||||
),
|
||||
'woocommerce/woocommerce-subscriptions' => array(
|
||||
'pretty_version' => 'dev-release/8.2.1',
|
||||
'version' => 'dev-release/8.2.1',
|
||||
'reference' => '8bb99631f731722b758f4702dc319ae43043ed67',
|
||||
'pretty_version' => 'dev-release/8.3.0',
|
||||
'version' => 'dev-release/8.3.0',
|
||||
'reference' => '38b39c285d0bae952d5746ba460ae914e4ac0f60',
|
||||
'type' => 'wordpress-plugin',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Description: Sell products and services with recurring payments in your WooCommerce Store.
|
||||
* Author: WooCommerce
|
||||
* Author URI: https://woocommerce.com/
|
||||
* Version: 8.2.1
|
||||
* Version: 8.3.0
|
||||
* Requires Plugins: woocommerce
|
||||
*
|
||||
* WC requires at least: 10.3.0
|
||||
|
|
@ -84,7 +84,7 @@ class WC_Subscriptions {
|
|||
public static $plugin_file = __FILE__;
|
||||
|
||||
/** @var string */
|
||||
public static $version = '8.2.1'; // WRCS: DEFINED_VERSION.
|
||||
public static $version = '8.3.0'; // WRCS: DEFINED_VERSION.
|
||||
|
||||
/** @var string */
|
||||
public static $wc_minimum_supported_version = '7.7';
|
||||
|
|
|
|||
Loading…
Reference in New Issue